00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "AVIFileSink.hh"
00022 #include "OutputFile.hh"
00023 #include "GroupsockHelper.hh"
00024
00025 #define fourChar(x,y,z,w) ( ((w)<<24)|((z)<<16)|((y)<<8)|(x) )
00026
00028
00029
00030 class SubsessionBuffer {
00031 public:
00032 SubsessionBuffer(unsigned bufferSize)
00033 : fBufferSize(bufferSize) {
00034 reset();
00035 fData = new unsigned char[bufferSize];
00036 }
00037 virtual ~SubsessionBuffer() { delete[] fData; }
00038 void reset() { fBytesInUse = 0; }
00039 void addBytes(unsigned numBytes) { fBytesInUse += numBytes; }
00040
00041 unsigned char* dataStart() { return &fData[0]; }
00042 unsigned char* dataEnd() { return &fData[fBytesInUse]; }
00043 unsigned bytesInUse() const { return fBytesInUse; }
00044 unsigned bytesAvailable() const { return fBufferSize - fBytesInUse; }
00045
00046 void setPresentationTime(struct timeval const& presentationTime) {
00047 fPresentationTime = presentationTime;
00048 }
00049 struct timeval const& presentationTime() const {return fPresentationTime;}
00050
00051 private:
00052 unsigned fBufferSize;
00053 struct timeval fPresentationTime;
00054 unsigned char* fData;
00055 unsigned fBytesInUse;
00056 };
00057
00058 class AVISubsessionIOState {
00059 public:
00060 AVISubsessionIOState(AVIFileSink& sink, MediaSubsession& subsession);
00061 virtual ~AVISubsessionIOState();
00062
00063 void setAVIstate(unsigned subsessionIndex);
00064 void setFinalAVIstate();
00065
00066 void afterGettingFrame(unsigned packetDataSize,
00067 struct timeval presentationTime);
00068 void onSourceClosure();
00069
00070 UsageEnvironment& envir() const { return fOurSink.envir(); }
00071
00072 public:
00073 SubsessionBuffer *fBuffer, *fPrevBuffer;
00074 AVIFileSink& fOurSink;
00075 MediaSubsession& fOurSubsession;
00076
00077 unsigned short fLastPacketRTPSeqNum;
00078 Boolean fOurSourceIsActive;
00079 struct timeval fPrevPresentationTime;
00080 unsigned fMaxBytesPerSecond;
00081 Boolean fIsVideo, fIsAudio, fIsByteSwappedAudio;
00082 unsigned fAVISubsessionTag;
00083 unsigned fAVICodecHandlerType;
00084 unsigned fAVISamplingFrequency;
00085 u_int16_t fWAVCodecTag;
00086 unsigned fAVIScale;
00087 unsigned fAVIRate;
00088 unsigned fAVISize;
00089 unsigned fNumFrames;
00090 unsigned fSTRHFrameCountPosition;
00091
00092 private:
00093 void useFrame(SubsessionBuffer& buffer);
00094 };
00095
00096
00098
00099 AVIFileSink::AVIFileSink(UsageEnvironment& env,
00100 MediaSession& inputSession,
00101 FILE* outFid,
00102 unsigned bufferSize,
00103 unsigned short movieWidth, unsigned short movieHeight,
00104 unsigned movieFPS, Boolean packetLossCompensate)
00105 : Medium(env), fInputSession(inputSession), fOutFid(outFid),
00106 fBufferSize(bufferSize), fPacketLossCompensate(packetLossCompensate),
00107 fAreCurrentlyBeingPlayed(False), fNumSubsessions(0), fNumBytesWritten(0),
00108 fHaveCompletedOutputFile(False),
00109 fMovieWidth(movieWidth), fMovieHeight(movieHeight), fMovieFPS(movieFPS) {
00110
00111 MediaSubsessionIterator iter(fInputSession);
00112 MediaSubsession* subsession;
00113 while ((subsession = iter.next()) != NULL) {
00114
00115 FramedSource* subsessionSource = subsession->readSource();
00116 if (subsessionSource == NULL) continue;
00117
00118
00119
00120 if (subsession->videoWidth() != 0) {
00121 fMovieWidth = subsession->videoWidth();
00122 }
00123 if (subsession->videoHeight() != 0) {
00124 fMovieHeight = subsession->videoHeight();
00125 }
00126 if (subsession->videoFPS() != 0) {
00127 fMovieFPS = subsession->videoFPS();
00128 }
00129
00130 AVISubsessionIOState* ioState
00131 = new AVISubsessionIOState(*this, *subsession);
00132 subsession->miscPtr = (void*)ioState;
00133
00134
00135 if (subsession->rtcpInstance() != NULL) {
00136 subsession->rtcpInstance()->setByeHandler(onRTCPBye, ioState);
00137 }
00138
00139 ++fNumSubsessions;
00140 }
00141
00142
00143 addFileHeader_AVI();
00144 }
00145
00146 AVIFileSink::~AVIFileSink() {
00147 completeOutputFile();
00148
00149
00150 MediaSubsessionIterator iter(fInputSession);
00151 MediaSubsession* subsession;
00152 while ((subsession = iter.next()) != NULL) {
00153 AVISubsessionIOState* ioState
00154 = (AVISubsessionIOState*)(subsession->miscPtr);
00155 if (ioState == NULL) continue;
00156
00157 delete ioState;
00158 }
00159 }
00160
00161 AVIFileSink* AVIFileSink
00162 ::createNew(UsageEnvironment& env, MediaSession& inputSession,
00163 char const* outputFileName,
00164 unsigned bufferSize,
00165 unsigned short movieWidth, unsigned short movieHeight,
00166 unsigned movieFPS, Boolean packetLossCompensate) {
00167 do {
00168 FILE* fid = OpenOutputFile(env, outputFileName);
00169 if (fid == NULL) break;
00170
00171 return new AVIFileSink(env, inputSession, fid, bufferSize,
00172 movieWidth, movieHeight, movieFPS,
00173 packetLossCompensate);
00174 } while (0);
00175
00176 return NULL;
00177 }
00178
00179 Boolean AVIFileSink::startPlaying(afterPlayingFunc* afterFunc,
00180 void* afterClientData) {
00181
00182 if (fAreCurrentlyBeingPlayed) {
00183 envir().setResultMsg("This sink has already been played");
00184 return False;
00185 }
00186
00187 fAreCurrentlyBeingPlayed = True;
00188 fAfterFunc = afterFunc;
00189 fAfterClientData = afterClientData;
00190
00191 return continuePlaying();
00192 }
00193
00194 Boolean AVIFileSink::continuePlaying() {
00195
00196
00197 Boolean haveActiveSubsessions = False;
00198 MediaSubsessionIterator iter(fInputSession);
00199 MediaSubsession* subsession;
00200 while ((subsession = iter.next()) != NULL) {
00201 FramedSource* subsessionSource = subsession->readSource();
00202 if (subsessionSource == NULL) continue;
00203
00204 if (subsessionSource->isCurrentlyAwaitingData()) continue;
00205
00206 AVISubsessionIOState* ioState
00207 = (AVISubsessionIOState*)(subsession->miscPtr);
00208 if (ioState == NULL) continue;
00209
00210 haveActiveSubsessions = True;
00211 unsigned char* toPtr = ioState->fBuffer->dataEnd();
00212 unsigned toSize = ioState->fBuffer->bytesAvailable();
00213 subsessionSource->getNextFrame(toPtr, toSize,
00214 afterGettingFrame, ioState,
00215 onSourceClosure, ioState);
00216 }
00217 if (!haveActiveSubsessions) {
00218 envir().setResultMsg("No subsessions are currently active");
00219 return False;
00220 }
00221
00222 return True;
00223 }
00224
00225 void AVIFileSink
00226 ::afterGettingFrame(void* clientData, unsigned packetDataSize,
00227 unsigned ,
00228 struct timeval presentationTime,
00229 unsigned ) {
00230 AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00231 ioState->afterGettingFrame(packetDataSize, presentationTime);
00232 }
00233
00234 void AVIFileSink::onSourceClosure(void* clientData) {
00235 AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00236 ioState->onSourceClosure();
00237 }
00238
00239 void AVIFileSink::onSourceClosure1() {
00240
00241
00242 MediaSubsessionIterator iter(fInputSession);
00243 MediaSubsession* subsession;
00244 while ((subsession = iter.next()) != NULL) {
00245 AVISubsessionIOState* ioState
00246 = (AVISubsessionIOState*)(subsession->miscPtr);
00247 if (ioState == NULL) continue;
00248
00249 if (ioState->fOurSourceIsActive) return;
00250 }
00251
00252 completeOutputFile();
00253
00254
00255 if (fAfterFunc != NULL) {
00256 (*fAfterFunc)(fAfterClientData);
00257 }
00258 }
00259
00260 void AVIFileSink::onRTCPBye(void* clientData) {
00261 AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00262
00263 struct timeval timeNow;
00264 gettimeofday(&timeNow, NULL);
00265 unsigned secsDiff
00266 = timeNow.tv_sec - ioState->fOurSink.fStartTime.tv_sec;
00267
00268 MediaSubsession& subsession = ioState->fOurSubsession;
00269 ioState->envir() << "Received RTCP \"BYE\" on \""
00270 << subsession.mediumName()
00271 << "/" << subsession.codecName()
00272 << "\" subsession (after "
00273 << secsDiff << " seconds)\n";
00274
00275
00276 ioState->onSourceClosure();
00277 }
00278
00279 void AVIFileSink::completeOutputFile() {
00280 if (fHaveCompletedOutputFile || fOutFid == NULL) return;
00281
00282
00283
00284 unsigned maxBytesPerSecond = 0;
00285 unsigned numVideoFrames = 0;
00286 unsigned numAudioFrames = 0;
00287
00289 MediaSubsessionIterator iter(fInputSession);
00290 MediaSubsession* subsession;
00291 while ((subsession = iter.next()) != NULL) {
00292 AVISubsessionIOState* ioState
00293 = (AVISubsessionIOState*)(subsession->miscPtr);
00294 if (ioState == NULL) continue;
00295
00296 maxBytesPerSecond += ioState->fMaxBytesPerSecond;
00297
00298 setWord(ioState->fSTRHFrameCountPosition, ioState->fNumFrames);
00299 if (ioState->fIsVideo) numVideoFrames = ioState->fNumFrames;
00300 else if (ioState->fIsAudio) numAudioFrames = ioState->fNumFrames;
00301 }
00302
00304 fRIFFSizeValue += fNumBytesWritten;
00305 setWord(fRIFFSizePosition, fRIFFSizeValue);
00306
00307 setWord(fAVIHMaxBytesPerSecondPosition, maxBytesPerSecond);
00308 setWord(fAVIHFrameCountPosition,
00309 numVideoFrames > 0 ? numVideoFrames : numAudioFrames);
00310
00311 fMoviSizeValue += fNumBytesWritten;
00312 setWord(fMoviSizePosition, fMoviSizeValue);
00313
00314
00315 fHaveCompletedOutputFile = True;
00316 }
00317
00318
00320
00321 AVISubsessionIOState::AVISubsessionIOState(AVIFileSink& sink,
00322 MediaSubsession& subsession)
00323 : fOurSink(sink), fOurSubsession(subsession),
00324 fMaxBytesPerSecond(0), fNumFrames(0) {
00325 fBuffer = new SubsessionBuffer(fOurSink.fBufferSize);
00326 fPrevBuffer = sink.fPacketLossCompensate
00327 ? new SubsessionBuffer(fOurSink.fBufferSize) : NULL;
00328
00329 FramedSource* subsessionSource = subsession.readSource();
00330 fOurSourceIsActive = subsessionSource != NULL;
00331
00332 fPrevPresentationTime.tv_sec = 0;
00333 fPrevPresentationTime.tv_usec = 0;
00334 }
00335
00336 AVISubsessionIOState::~AVISubsessionIOState() {
00337 delete fBuffer; delete fPrevBuffer;
00338 }
00339
00340 void AVISubsessionIOState::setAVIstate(unsigned subsessionIndex) {
00341 fIsVideo = strcmp(fOurSubsession.mediumName(), "video") == 0;
00342 fIsAudio = strcmp(fOurSubsession.mediumName(), "audio") == 0;
00343
00344 if (fIsVideo) {
00345 fAVISubsessionTag
00346 = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'d','c');
00347 if (strcmp(fOurSubsession.codecName(), "JPEG") == 0) {
00348 fAVICodecHandlerType = fourChar('m','j','p','g');
00349 } else if (strcmp(fOurSubsession.codecName(), "MP4V-ES") == 0) {
00350 fAVICodecHandlerType = fourChar('D','I','V','X');
00351 } else if (strcmp(fOurSubsession.codecName(), "MPV") == 0) {
00352 fAVICodecHandlerType = fourChar('m','p','g','1');
00353 } else if (strcmp(fOurSubsession.codecName(), "H263-1998") == 0 ||
00354 strcmp(fOurSubsession.codecName(), "H263-2000") == 0) {
00355 fAVICodecHandlerType = fourChar('H','2','6','3');
00356 } else if (strcmp(fOurSubsession.codecName(), "H264") == 0) {
00357 fAVICodecHandlerType = fourChar('H','2','6','4');
00358 } else {
00359 fAVICodecHandlerType = fourChar('?','?','?','?');
00360 }
00361 fAVIScale = 1;
00362 fAVIRate = fOurSink.fMovieFPS;
00363 fAVISize = fOurSink.fMovieWidth*fOurSink.fMovieHeight*3;
00364 } else if (fIsAudio) {
00365 fIsByteSwappedAudio = False;
00366 fAVISubsessionTag
00367 = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'w','b');
00368 fAVICodecHandlerType = 1;
00369 unsigned numChannels = fOurSubsession.numChannels();
00370 fAVISamplingFrequency = fOurSubsession.rtpTimestampFrequency();
00371 if (strcmp(fOurSubsession.codecName(), "L16") == 0) {
00372 fIsByteSwappedAudio = True;
00373 fWAVCodecTag = 0x0001;
00374 fAVIScale = fAVISize = 2*numChannels;
00375 fAVIRate = fAVISize*fAVISamplingFrequency;
00376 } else if (strcmp(fOurSubsession.codecName(), "L8") == 0) {
00377 fWAVCodecTag = 0x0001;
00378 fAVIScale = fAVISize = numChannels;
00379 fAVIRate = fAVISize*fAVISamplingFrequency;
00380 } else if (strcmp(fOurSubsession.codecName(), "PCMA") == 0) {
00381 fWAVCodecTag = 0x0006;
00382 fAVIScale = fAVISize = numChannels;
00383 fAVIRate = fAVISize*fAVISamplingFrequency;
00384 } else if (strcmp(fOurSubsession.codecName(), "PCMU") == 0) {
00385 fWAVCodecTag = 0x0007;
00386 fAVIScale = fAVISize = numChannels;
00387 fAVIRate = fAVISize*fAVISamplingFrequency;
00388 } else if (strcmp(fOurSubsession.codecName(), "MPA") == 0) {
00389 fWAVCodecTag = 0x0050;
00390 fAVIScale = fAVISize = 1;
00391 fAVIRate = 0;
00392 } else {
00393 fWAVCodecTag = 0x0001;
00394 fAVIScale = fAVISize = 1;
00395 fAVIRate = 0;
00396 }
00397 } else {
00398 fAVISubsessionTag
00399 = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'?','?');
00400 fAVICodecHandlerType = 0;
00401 fAVIScale = fAVISize = 1;
00402 fAVIRate = 0;
00403 }
00404 }
00405
00406 void AVISubsessionIOState::afterGettingFrame(unsigned packetDataSize,
00407 struct timeval presentationTime) {
00408
00409
00410 unsigned short rtpSeqNum
00411 = fOurSubsession.rtpSource()->curPacketRTPSeqNum();
00412 if (fOurSink.fPacketLossCompensate && fPrevBuffer->bytesInUse() > 0) {
00413 short seqNumGap = rtpSeqNum - fLastPacketRTPSeqNum;
00414 for (short i = 1; i < seqNumGap; ++i) {
00415
00416 useFrame(*fPrevBuffer);
00417 }
00418 }
00419 fLastPacketRTPSeqNum = rtpSeqNum;
00420
00421
00422 if (fBuffer->bytesInUse() == 0) {
00423 fBuffer->setPresentationTime(presentationTime);
00424 }
00425 fBuffer->addBytes(packetDataSize);
00426
00427 useFrame(*fBuffer);
00428 if (fOurSink.fPacketLossCompensate) {
00429
00430 SubsessionBuffer* tmp = fPrevBuffer;
00431 fPrevBuffer = fBuffer;
00432 fBuffer = tmp;
00433 }
00434 fBuffer->reset();
00435
00436
00437 fOurSink.continuePlaying();
00438 }
00439
00440 void AVISubsessionIOState::useFrame(SubsessionBuffer& buffer) {
00441 unsigned char* const frameSource = buffer.dataStart();
00442 unsigned const frameSize = buffer.bytesInUse();
00443 struct timeval const& presentationTime = buffer.presentationTime();
00444 if (fPrevPresentationTime.tv_usec != 0||fPrevPresentationTime.tv_sec != 0) {
00445 int uSecondsDiff
00446 = (presentationTime.tv_sec - fPrevPresentationTime.tv_sec)*1000000
00447 + (presentationTime.tv_usec - fPrevPresentationTime.tv_usec);
00448 if (uSecondsDiff > 0) {
00449 unsigned bytesPerSecond = (unsigned)((frameSize*1000000.0)/uSecondsDiff);
00450 if (bytesPerSecond > fMaxBytesPerSecond) {
00451 fMaxBytesPerSecond = bytesPerSecond;
00452 }
00453 }
00454 }
00455 fPrevPresentationTime = presentationTime;
00456
00457 if (fIsByteSwappedAudio) {
00458
00459
00460 for (unsigned i = 0; i < frameSize; i += 2) {
00461 unsigned char tmp = frameSource[i];
00462 frameSource[i] = frameSource[i+1];
00463 frameSource[i+1] = tmp;
00464 }
00465 }
00466
00467
00468 fOurSink.fNumBytesWritten += fOurSink.addWord(fAVISubsessionTag);
00469 fOurSink.fNumBytesWritten += fOurSink.addWord(frameSize);
00470 fwrite(frameSource, 1, frameSize, fOurSink.fOutFid);
00471 fOurSink.fNumBytesWritten += frameSize;
00472
00473 if (frameSize%2 != 0) fOurSink.fNumBytesWritten += fOurSink.addByte(0);
00474
00475 ++fNumFrames;
00476 }
00477
00478 void AVISubsessionIOState::onSourceClosure() {
00479 fOurSourceIsActive = False;
00480 fOurSink.onSourceClosure1();
00481 }
00482
00483
00485
00486 unsigned AVIFileSink::addWord(unsigned word) {
00487
00488 addByte(word); addByte(word>>8);
00489 addByte(word>>16); addByte(word>>24);
00490
00491 return 4;
00492 }
00493
00494 unsigned AVIFileSink::addHalfWord(unsigned short halfWord) {
00495
00496 addByte((unsigned char)halfWord); addByte((unsigned char)(halfWord>>8));
00497
00498 return 2;
00499 }
00500
00501 unsigned AVIFileSink::addZeroWords(unsigned numWords) {
00502 for (unsigned i = 0; i < numWords; ++i) {
00503 addWord(0);
00504 }
00505
00506 return numWords*4;
00507 }
00508
00509 unsigned AVIFileSink::add4ByteString(char const* str) {
00510 addByte(str[0]); addByte(str[1]); addByte(str[2]);
00511 addByte(str[3] == '\0' ? ' ' : str[3]);
00512
00513 return 4;
00514 }
00515
00516 void AVIFileSink::setWord(unsigned filePosn, unsigned size) {
00517 do {
00518 if (fseek(fOutFid, filePosn, SEEK_SET) < 0) break;
00519 addWord(size);
00520 if (fseek(fOutFid, 0, SEEK_END) < 0) break;
00521
00522 return;
00523 } while (0);
00524
00525
00526 envir() << "AVIFileSink::setWord(): fseek failed (err "
00527 << envir().getErrno() << ")\n";
00528 }
00529
00530
00531
00532 #define addFileHeader(tag,name) \
00533 unsigned AVIFileSink::addFileHeader_##name() { \
00534 add4ByteString("" #tag ""); \
00535 unsigned headerSizePosn = ftell(fOutFid); addWord(0); \
00536 add4ByteString("" #name ""); \
00537 unsigned ignoredSize = 8; \
00538 unsigned size = 12
00539
00540 #define addFileHeader1(name) \
00541 unsigned AVIFileSink::addFileHeader_##name() { \
00542 add4ByteString("" #name ""); \
00543 unsigned headerSizePosn = ftell(fOutFid); addWord(0); \
00544 unsigned ignoredSize = 8; \
00545 unsigned size = 8
00546
00547 #define addFileHeaderEnd \
00548 setWord(headerSizePosn, size-ignoredSize); \
00549 return size; \
00550 }
00551
00552 addFileHeader(RIFF,AVI);
00553 size += addFileHeader_hdrl();
00554 size += addFileHeader_movi();
00555 fRIFFSizePosition = headerSizePosn;
00556 fRIFFSizeValue = size-ignoredSize;
00557 addFileHeaderEnd;
00558
00559 addFileHeader(LIST,hdrl);
00560 size += addFileHeader_avih();
00561
00562
00563
00564 unsigned subsessionCount = 0;
00565 MediaSubsessionIterator iter(fInputSession);
00566 MediaSubsession* subsession;
00567 while ((subsession = iter.next()) != NULL) {
00568 fCurrentIOState = (AVISubsessionIOState*)(subsession->miscPtr);
00569 if (fCurrentIOState == NULL) continue;
00570 if (strcmp(subsession->mediumName(), "video") != 0) continue;
00571
00572 fCurrentIOState->setAVIstate(subsessionCount++);
00573 size += addFileHeader_strl();
00574 }
00575 iter.reset();
00576 while ((subsession = iter.next()) != NULL) {
00577 fCurrentIOState = (AVISubsessionIOState*)(subsession->miscPtr);
00578 if (fCurrentIOState == NULL) continue;
00579 if (strcmp(subsession->mediumName(), "video") == 0) continue;
00580
00581 fCurrentIOState->setAVIstate(subsessionCount++);
00582 size += addFileHeader_strl();
00583 }
00584
00585
00586 ++fJunkNumber;
00587 size += addFileHeader_JUNK();
00588 addFileHeaderEnd;
00589
00590 #define AVIF_HASINDEX 0x00000010 // Index at end of file?
00591 #define AVIF_MUSTUSEINDEX 0x00000020
00592 #define AVIF_ISINTERLEAVED 0x00000100
00593 #define AVIF_TRUSTCKTYPE 0x00000800 // Use CKType to find key frames?
00594 #define AVIF_WASCAPTUREFILE 0x00010000
00595 #define AVIF_COPYRIGHTED 0x00020000
00596
00597 addFileHeader1(avih);
00598 unsigned usecPerFrame = fMovieFPS == 0 ? 0 : 1000000/fMovieFPS;
00599 size += addWord(usecPerFrame);
00600 fAVIHMaxBytesPerSecondPosition = ftell(fOutFid);
00601 size += addWord(0);
00602 size += addWord(0);
00603 size += addWord(AVIF_TRUSTCKTYPE|AVIF_HASINDEX|AVIF_ISINTERLEAVED);
00604 fAVIHFrameCountPosition = ftell(fOutFid);
00605 size += addWord(0);
00606 size += addWord(0);
00607 size += addWord(fNumSubsessions);
00608 size += addWord(fBufferSize);
00609 size += addWord(fMovieWidth);
00610 size += addWord(fMovieHeight);
00611 size += addZeroWords(4);
00612 addFileHeaderEnd;
00613
00614 addFileHeader(LIST,strl);
00615 size += addFileHeader_strh();
00616 size += addFileHeader_strf();
00617 fJunkNumber = 0;
00618 size += addFileHeader_JUNK();
00619 addFileHeaderEnd;
00620
00621 addFileHeader1(strh);
00622 size += add4ByteString(fCurrentIOState->fIsVideo ? "vids" :
00623 fCurrentIOState->fIsAudio ? "auds" :
00624 "????");
00625 size += addWord(fCurrentIOState->fAVICodecHandlerType);
00626 size += addWord(0);
00627 size += addWord(0);
00628 size += addWord(0);
00629 size += addWord(fCurrentIOState->fAVIScale);
00630 size += addWord(fCurrentIOState->fAVIRate);
00631 size += addWord(0);
00632 fCurrentIOState->fSTRHFrameCountPosition = ftell(fOutFid);
00633 size += addWord(0);
00634 size += addWord(fBufferSize);
00635 size += addWord((unsigned)-1);
00636 size += addWord(fCurrentIOState->fAVISize);
00637 size += addWord(0);
00638 if (fCurrentIOState->fIsVideo) {
00639 size += addHalfWord(fMovieWidth);
00640 size += addHalfWord(fMovieHeight);
00641 } else {
00642 size += addWord(0);
00643 }
00644 addFileHeaderEnd;
00645
00646 addFileHeader1(strf);
00647 if (fCurrentIOState->fIsVideo) {
00648
00649 unsigned extraDataSize = 0;
00650 size += addWord(10*4 + extraDataSize);
00651 size += addWord(fMovieWidth);
00652 size += addWord(fMovieHeight);
00653 size += addHalfWord(1);
00654 size += addHalfWord(24);
00655 size += addWord(fCurrentIOState->fAVICodecHandlerType);
00656 size += addWord(fCurrentIOState->fAVISize);
00657 size += addZeroWords(4);
00658
00659 } else if (fCurrentIOState->fIsAudio) {
00660
00661 size += addHalfWord(fCurrentIOState->fWAVCodecTag);
00662 unsigned numChannels = fCurrentIOState->fOurSubsession.numChannels();
00663 size += addHalfWord(numChannels);
00664 size += addWord(fCurrentIOState->fAVISamplingFrequency);
00665 size += addWord(fCurrentIOState->fAVIRate);
00666 size += addHalfWord(fCurrentIOState->fAVISize);
00667 unsigned bitsPerSample = (fCurrentIOState->fAVISize*8)/numChannels;
00668 size += addHalfWord(bitsPerSample);
00669 if (strcmp(fCurrentIOState->fOurSubsession.codecName(), "MPA") == 0) {
00670
00671 size += addHalfWord(22);
00672 size += addHalfWord(2);
00673 size += addWord(8*fCurrentIOState->fAVIRate);
00674 size += addHalfWord(numChannels == 2 ? 1: 8);
00675 size += addHalfWord(0);
00676 size += addHalfWord(1);
00677 size += addHalfWord(16);
00678 size += addWord(0);
00679 size += addWord(0);
00680 }
00681 }
00682 addFileHeaderEnd;
00683
00684 #define AVI_MASTER_INDEX_SIZE 256
00685
00686 addFileHeader1(JUNK);
00687 if (fJunkNumber == 0) {
00688 size += addHalfWord(4);
00689 size += addHalfWord(0);
00690 size += addWord(0);
00691 size += addWord(fCurrentIOState->fAVISubsessionTag);
00692 size += addZeroWords(2);
00693 size += addZeroWords(AVI_MASTER_INDEX_SIZE*4);
00694 } else {
00695 size += add4ByteString("odml");
00696 size += add4ByteString("dmlh");
00697 unsigned wtfCount = 248;
00698 size += addWord(wtfCount);
00699 size += addZeroWords(wtfCount/4);
00700 }
00701 addFileHeaderEnd;
00702
00703 addFileHeader(LIST,movi);
00704 fMoviSizePosition = headerSizePosn;
00705 fMoviSizeValue = size-ignoredSize;
00706 addFileHeaderEnd;