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