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