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