liveMedia/AVIFileSink.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2012 Live Networks, Inc.  All rights reserved.
00018 // A sink that generates an AVI file from a composite media session
00019 // Implementation
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) )/*little-endian*/
00027 
00029 // A structure used to represent the I/O state of each input 'subsession':
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; // for audio
00086   u_int16_t fWAVCodecTag; // for audio
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   // Set up I/O state for each input subsession:
00115   MediaSubsessionIterator iter(fInputSession);
00116   MediaSubsession* subsession;
00117   while ((subsession = iter.next()) != NULL) {
00118     // Ignore subsessions without a data source:
00119     FramedSource* subsessionSource = subsession->readSource();
00120     if (subsessionSource == NULL) continue;
00121 
00122     // If "subsession's" SDP description specified screen dimension
00123     // or frame rate parameters, then use these.
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     // Also set a 'BYE' handler for this subsession's RTCP instance:
00139     if (subsession->rtcpInstance() != NULL) {
00140       subsession->rtcpInstance()->setByeHandler(onRTCPBye, ioState);
00141     }
00142 
00143     ++fNumSubsessions;
00144   }
00145 
00146   // Begin by writing an AVI header:
00147   addFileHeader_AVI();
00148 }
00149 
00150 AVIFileSink::~AVIFileSink() {
00151   completeOutputFile();
00152 
00153   // Then, delete each active "AVISubsessionIOState":
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   // Finally, close our output file:
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   // Make sure we're not already being played:
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   // Run through each of our input session's 'subsessions',
00202   // asking for a frame from each one:
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 /*durationInMicroseconds*/) {
00236   AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00237   ioState->afterGettingFrame(packetDataSize, presentationTime);
00238 }
00239 
00240 void AVIFileSink::onSourceClosure(void* clientData) {
00241   AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00242   ioState->onSourceClosure();
00243 }
00244 
00245 void AVIFileSink::onSourceClosure1() {
00246   // Check whether *all* of the subsession sources have closed.
00247   // If not, do nothing for now:
00248   MediaSubsessionIterator iter(fInputSession);
00249   MediaSubsession* subsession;
00250   while ((subsession = iter.next()) != NULL) {
00251     AVISubsessionIOState* ioState
00252       = (AVISubsessionIOState*)(subsession->miscPtr);
00253     if (ioState == NULL) continue;
00254 
00255     if (ioState->fOurSourceIsActive) return; // this source hasn't closed
00256   }
00257 
00258   completeOutputFile();
00259 
00260   // Call our specified 'after' function:
00261   if (fAfterFunc != NULL) {
00262     (*fAfterFunc)(fAfterClientData);
00263   }
00264 }
00265 
00266 void AVIFileSink::onRTCPBye(void* clientData) {
00267   AVISubsessionIOState* ioState = (AVISubsessionIOState*)clientData;
00268 
00269   struct timeval timeNow;
00270   gettimeofday(&timeNow, NULL);
00271   unsigned secsDiff
00272     = timeNow.tv_sec - ioState->fOurSink.fStartTime.tv_sec;
00273 
00274   MediaSubsession& subsession = ioState->fOurSubsession;
00275   ioState->envir() << "Received RTCP \"BYE\" on \""
00276                    << subsession.mediumName()
00277                    << "/" << subsession.codecName()
00278                    << "\" subsession (after "
00279                    << secsDiff << " seconds)\n";
00280 
00281   // Handle the reception of a RTCP "BYE" as if the source had closed:
00282   ioState->onSourceClosure();
00283 }
00284 
00285 void AVIFileSink::completeOutputFile() {
00286   if (fHaveCompletedOutputFile || fOutFid == NULL) return;
00287 
00288   // Update various AVI 'size' fields to take account of the codec data that
00289   // we've now written to the file:
00290   unsigned maxBytesPerSecond = 0;
00291   unsigned numVideoFrames = 0;
00292   unsigned numAudioFrames = 0;
00293 
00295   MediaSubsessionIterator iter(fInputSession);
00296   MediaSubsession* subsession;
00297   while ((subsession = iter.next()) != NULL) {
00298     AVISubsessionIOState* ioState
00299       = (AVISubsessionIOState*)(subsession->miscPtr);
00300     if (ioState == NULL) continue;
00301 
00302     maxBytesPerSecond += ioState->fMaxBytesPerSecond;
00303 
00304     setWord(ioState->fSTRHFrameCountPosition, ioState->fNumFrames);
00305     if (ioState->fIsVideo) numVideoFrames = ioState->fNumFrames;
00306     else if (ioState->fIsAudio) numAudioFrames = ioState->fNumFrames;
00307   }
00308 
00310   fRIFFSizeValue += fNumBytesWritten;
00311   setWord(fRIFFSizePosition, fRIFFSizeValue);
00312 
00313   setWord(fAVIHMaxBytesPerSecondPosition, maxBytesPerSecond);
00314   setWord(fAVIHFrameCountPosition,
00315           numVideoFrames > 0 ? numVideoFrames : numAudioFrames);
00316 
00317   fMoviSizeValue += fNumBytesWritten;
00318   setWord(fMoviSizePosition, fMoviSizeValue);
00319 
00320   // We're done:
00321   fHaveCompletedOutputFile = True;
00322 }
00323 
00324 
00326 
00327 AVISubsessionIOState::AVISubsessionIOState(AVIFileSink& sink,
00328                                      MediaSubsession& subsession)
00329   : fOurSink(sink), fOurSubsession(subsession),
00330     fMaxBytesPerSecond(0), fIsVideo(False), fIsAudio(False), fIsByteSwappedAudio(False), fNumFrames(0) {
00331   fBuffer = new SubsessionBuffer(fOurSink.fBufferSize);
00332   fPrevBuffer = sink.fPacketLossCompensate
00333     ? new SubsessionBuffer(fOurSink.fBufferSize) : NULL;
00334 
00335   FramedSource* subsessionSource = subsession.readSource();
00336   fOurSourceIsActive = subsessionSource != NULL;
00337 
00338   fPrevPresentationTime.tv_sec = 0;
00339   fPrevPresentationTime.tv_usec = 0;
00340 }
00341 
00342 AVISubsessionIOState::~AVISubsessionIOState() {
00343   delete fBuffer; delete fPrevBuffer;
00344 }
00345 
00346 void AVISubsessionIOState::setAVIstate(unsigned subsessionIndex) {
00347   fIsVideo = strcmp(fOurSubsession.mediumName(), "video") == 0;
00348   fIsAudio = strcmp(fOurSubsession.mediumName(), "audio") == 0;
00349 
00350   if (fIsVideo) {
00351     fAVISubsessionTag
00352       = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'d','c');
00353     if (strcmp(fOurSubsession.codecName(), "JPEG") == 0) {
00354       fAVICodecHandlerType = fourChar('m','j','p','g');
00355     } else if (strcmp(fOurSubsession.codecName(), "MP4V-ES") == 0) {
00356       fAVICodecHandlerType = fourChar('D','I','V','X');
00357     } else if (strcmp(fOurSubsession.codecName(), "MPV") == 0) {
00358       fAVICodecHandlerType = fourChar('m','p','g','1'); // what about MPEG-2?
00359     } else if (strcmp(fOurSubsession.codecName(), "H263-1998") == 0 ||
00360                strcmp(fOurSubsession.codecName(), "H263-2000") == 0) {
00361       fAVICodecHandlerType = fourChar('H','2','6','3');
00362     } else if (strcmp(fOurSubsession.codecName(), "H264") == 0) {
00363       fAVICodecHandlerType = fourChar('H','2','6','4');
00364     } else {
00365       fAVICodecHandlerType = fourChar('?','?','?','?');
00366     }
00367     fAVIScale = 1; // ??? #####
00368     fAVIRate = fOurSink.fMovieFPS; // ??? #####
00369     fAVISize = fOurSink.fMovieWidth*fOurSink.fMovieHeight*3; // ??? #####
00370   } else if (fIsAudio) {
00371     fIsByteSwappedAudio = False; // by default
00372     fAVISubsessionTag
00373       = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'w','b');
00374     fAVICodecHandlerType = 1; // ??? ####
00375     unsigned numChannels = fOurSubsession.numChannels();
00376     fAVISamplingFrequency = fOurSubsession.rtpTimestampFrequency(); // default
00377     if (strcmp(fOurSubsession.codecName(), "L16") == 0) {
00378       fIsByteSwappedAudio = True; // need to byte-swap data before writing it
00379       fWAVCodecTag = 0x0001;
00380       fAVIScale = fAVISize = 2*numChannels; // 2 bytes/sample
00381       fAVIRate = fAVISize*fAVISamplingFrequency;
00382     } else if (strcmp(fOurSubsession.codecName(), "L8") == 0) {
00383       fWAVCodecTag = 0x0001;
00384       fAVIScale = fAVISize = numChannels; // 1 byte/sample
00385       fAVIRate = fAVISize*fAVISamplingFrequency;
00386     } else if (strcmp(fOurSubsession.codecName(), "PCMA") == 0) {
00387       fWAVCodecTag = 0x0006;
00388       fAVIScale = fAVISize = numChannels; // 1 byte/sample
00389       fAVIRate = fAVISize*fAVISamplingFrequency;
00390     } else if (strcmp(fOurSubsession.codecName(), "PCMU") == 0) {
00391       fWAVCodecTag = 0x0007;
00392       fAVIScale = fAVISize = numChannels; // 1 byte/sample
00393       fAVIRate = fAVISize*fAVISamplingFrequency;
00394     } else if (strcmp(fOurSubsession.codecName(), "MPA") == 0) {
00395       fWAVCodecTag = 0x0050;
00396       fAVIScale = fAVISize = 1;
00397       fAVIRate = 0; // ??? #####
00398     } else {
00399       fWAVCodecTag = 0x0001; // ??? #####
00400       fAVIScale = fAVISize = 1;
00401       fAVIRate = 0; // ??? #####
00402     }
00403   } else { // unknown medium
00404     fAVISubsessionTag
00405       = fourChar('0'+subsessionIndex/10,'0'+subsessionIndex%10,'?','?');
00406     fAVICodecHandlerType = 0;
00407     fAVIScale = fAVISize = 1;
00408     fAVIRate = 0; // ??? #####
00409   }
00410 }
00411 
00412 void AVISubsessionIOState::afterGettingFrame(unsigned packetDataSize,
00413                                           struct timeval presentationTime) {
00414   // Begin by checking whether there was a gap in the RTP stream.
00415   // If so, try to compensate for this (if desired):
00416   unsigned short rtpSeqNum
00417     = fOurSubsession.rtpSource()->curPacketRTPSeqNum();
00418   if (fOurSink.fPacketLossCompensate && fPrevBuffer->bytesInUse() > 0) {
00419     short seqNumGap = rtpSeqNum - fLastPacketRTPSeqNum;
00420     for (short i = 1; i < seqNumGap; ++i) {
00421       // Insert a copy of the previous frame, to compensate for the loss:
00422       useFrame(*fPrevBuffer);
00423     }
00424   }
00425   fLastPacketRTPSeqNum = rtpSeqNum;
00426 
00427   // Now, continue working with the frame that we just got
00428   if (fBuffer->bytesInUse() == 0) {
00429     fBuffer->setPresentationTime(presentationTime);
00430   }
00431   fBuffer->addBytes(packetDataSize);
00432 
00433   useFrame(*fBuffer);
00434   if (fOurSink.fPacketLossCompensate) {
00435     // Save this frame, in case we need it for recovery:
00436     SubsessionBuffer* tmp = fPrevBuffer; // assert: != NULL
00437     fPrevBuffer = fBuffer;
00438     fBuffer = tmp;
00439   }
00440   fBuffer->reset(); // for the next input
00441 
00442   // Now, try getting more frames:
00443   fOurSink.continuePlaying();
00444 }
00445 
00446 void AVISubsessionIOState::useFrame(SubsessionBuffer& buffer) {
00447   unsigned char* const frameSource = buffer.dataStart();
00448   unsigned const frameSize = buffer.bytesInUse();
00449   struct timeval const& presentationTime = buffer.presentationTime();
00450   if (fPrevPresentationTime.tv_usec != 0||fPrevPresentationTime.tv_sec != 0) {
00451     int uSecondsDiff
00452       = (presentationTime.tv_sec - fPrevPresentationTime.tv_sec)*1000000
00453       + (presentationTime.tv_usec - fPrevPresentationTime.tv_usec);
00454     if (uSecondsDiff > 0) {
00455       unsigned bytesPerSecond = (unsigned)((frameSize*1000000.0)/uSecondsDiff);
00456       if (bytesPerSecond > fMaxBytesPerSecond) {
00457         fMaxBytesPerSecond = bytesPerSecond;
00458       }
00459     }
00460   }
00461   fPrevPresentationTime = presentationTime;
00462 
00463   if (fIsByteSwappedAudio) {
00464     // We need to swap the 16-bit audio samples from big-endian
00465     // to little-endian order, before writing them to a file:
00466     for (unsigned i = 0; i < frameSize; i += 2) {
00467       unsigned char tmp = frameSource[i];
00468       frameSource[i] = frameSource[i+1];
00469       frameSource[i+1] = tmp;
00470     }
00471   }
00472 
00473   // Write the data into the file:
00474   fOurSink.fNumBytesWritten += fOurSink.addWord(fAVISubsessionTag);
00475   fOurSink.fNumBytesWritten += fOurSink.addWord(frameSize);
00476   fwrite(frameSource, 1, frameSize, fOurSink.fOutFid);
00477   fOurSink.fNumBytesWritten += frameSize;
00478   // Pad to an even length:
00479   if (frameSize%2 != 0) fOurSink.fNumBytesWritten += fOurSink.addByte(0);
00480 
00481   ++fNumFrames;
00482 }
00483 
00484 void AVISubsessionIOState::onSourceClosure() {
00485   fOurSourceIsActive = False;
00486   fOurSink.onSourceClosure1();
00487 }
00488 
00489 
00491 
00492 unsigned AVIFileSink::addWord(unsigned word) {
00493   // Add "word" to the file in little-endian order:
00494   addByte(word); addByte(word>>8);
00495   addByte(word>>16); addByte(word>>24);
00496 
00497   return 4;
00498 }
00499 
00500 unsigned AVIFileSink::addHalfWord(unsigned short halfWord) {
00501   // Add "halfWord" to the file in little-endian order:
00502   addByte((unsigned char)halfWord); addByte((unsigned char)(halfWord>>8));
00503 
00504   return 2;
00505 }
00506 
00507 unsigned AVIFileSink::addZeroWords(unsigned numWords) {
00508   for (unsigned i = 0; i < numWords; ++i) {
00509     addWord(0);
00510   }
00511 
00512   return numWords*4;
00513 }
00514 
00515 unsigned AVIFileSink::add4ByteString(char const* str) {
00516   addByte(str[0]); addByte(str[1]); addByte(str[2]);
00517   addByte(str[3] == '\0' ? ' ' : str[3]); // e.g., for "AVI "
00518 
00519   return 4;
00520 }
00521 
00522 void AVIFileSink::setWord(unsigned filePosn, unsigned size) {
00523   do {
00524     if (SeekFile64(fOutFid, filePosn, SEEK_SET) < 0) break;
00525     addWord(size);
00526     if (SeekFile64(fOutFid, 0, SEEK_END) < 0) break; // go back to where we were
00527 
00528     return;
00529   } while (0);
00530 
00531   // One of the SeekFile64()s failed, probable because we're not a seekable file
00532   envir() << "AVIFileSink::setWord(): SeekFile64 failed (err "
00533           << envir().getErrno() << ")\n";
00534 }
00535 
00536 // Methods for writing particular file headers.  Note the following macros:
00537 
00538 #define addFileHeader(tag,name) \
00539     unsigned AVIFileSink::addFileHeader_##name() { \
00540         add4ByteString("" #tag ""); \
00541         unsigned headerSizePosn = (unsigned)TellFile64(fOutFid); addWord(0); \
00542         add4ByteString("" #name ""); \
00543         unsigned ignoredSize = 8;/*don't include size of tag or size fields*/ \
00544         unsigned size = 12
00545 
00546 #define addFileHeader1(name) \
00547     unsigned AVIFileSink::addFileHeader_##name() { \
00548         add4ByteString("" #name ""); \
00549         unsigned headerSizePosn = (unsigned)TellFile64(fOutFid); addWord(0); \
00550         unsigned ignoredSize = 8;/*don't include size of name or size fields*/ \
00551         unsigned size = 8
00552 
00553 #define addFileHeaderEnd \
00554   setWord(headerSizePosn, size-ignoredSize); \
00555   return size; \
00556 }
00557 
00558 addFileHeader(RIFF,AVI);
00559     size += addFileHeader_hdrl();
00560     size += addFileHeader_movi();
00561     fRIFFSizePosition = headerSizePosn;
00562     fRIFFSizeValue = size-ignoredSize;
00563 addFileHeaderEnd;
00564 
00565 addFileHeader(LIST,hdrl);
00566     size += addFileHeader_avih();
00567 
00568     // Then, add a "strl" header for each subsession (stream):
00569     // (Make the video subsession (if any) come before the audio subsession.)
00570     unsigned subsessionCount = 0;
00571     MediaSubsessionIterator iter(fInputSession);
00572     MediaSubsession* subsession;
00573     while ((subsession = iter.next()) != NULL) {
00574       fCurrentIOState = (AVISubsessionIOState*)(subsession->miscPtr);
00575       if (fCurrentIOState == NULL) continue;
00576       if (strcmp(subsession->mediumName(), "video") != 0) continue;
00577 
00578       fCurrentIOState->setAVIstate(subsessionCount++);
00579       size += addFileHeader_strl();
00580     }
00581     iter.reset();
00582     while ((subsession = iter.next()) != NULL) {
00583       fCurrentIOState = (AVISubsessionIOState*)(subsession->miscPtr);
00584       if (fCurrentIOState == NULL) continue;
00585       if (strcmp(subsession->mediumName(), "video") == 0) continue;
00586 
00587       fCurrentIOState->setAVIstate(subsessionCount++);
00588       size += addFileHeader_strl();
00589     }
00590 
00591     // Then add another JUNK entry
00592     ++fJunkNumber;
00593     size += addFileHeader_JUNK();
00594 addFileHeaderEnd;
00595 
00596 #define AVIF_HASINDEX           0x00000010 // Index at end of file?
00597 #define AVIF_MUSTUSEINDEX       0x00000020
00598 #define AVIF_ISINTERLEAVED      0x00000100
00599 #define AVIF_TRUSTCKTYPE        0x00000800 // Use CKType to find key frames?
00600 #define AVIF_WASCAPTUREFILE     0x00010000
00601 #define AVIF_COPYRIGHTED        0x00020000
00602 
00603 addFileHeader1(avih);
00604     unsigned usecPerFrame = fMovieFPS == 0 ? 0 : 1000000/fMovieFPS;
00605     size += addWord(usecPerFrame); // dwMicroSecPerFrame
00606     fAVIHMaxBytesPerSecondPosition = (unsigned)TellFile64(fOutFid);
00607     size += addWord(0); // dwMaxBytesPerSec (fill in later)
00608     size += addWord(0); // dwPaddingGranularity
00609     size += addWord(AVIF_TRUSTCKTYPE|AVIF_HASINDEX|AVIF_ISINTERLEAVED); // dwFlags
00610     fAVIHFrameCountPosition = (unsigned)TellFile64(fOutFid);
00611     size += addWord(0); // dwTotalFrames (fill in later)
00612     size += addWord(0); // dwInitialFrame
00613     size += addWord(fNumSubsessions); // dwStreams
00614     size += addWord(fBufferSize); // dwSuggestedBufferSize
00615     size += addWord(fMovieWidth); // dwWidth
00616     size += addWord(fMovieHeight); // dwHeight
00617     size += addZeroWords(4); // dwReserved
00618 addFileHeaderEnd;
00619 
00620 addFileHeader(LIST,strl);
00621     size += addFileHeader_strh();
00622     size += addFileHeader_strf();
00623     fJunkNumber = 0;
00624     size += addFileHeader_JUNK();
00625 addFileHeaderEnd;
00626 
00627 addFileHeader1(strh);
00628     size += add4ByteString(fCurrentIOState->fIsVideo ? "vids" :
00629                            fCurrentIOState->fIsAudio ? "auds" :
00630                            "????"); // fccType
00631     size += addWord(fCurrentIOState->fAVICodecHandlerType); // fccHandler
00632     size += addWord(0); // dwFlags
00633     size += addWord(0); // wPriority + wLanguage
00634     size += addWord(0); // dwInitialFrames
00635     size += addWord(fCurrentIOState->fAVIScale); // dwScale
00636     size += addWord(fCurrentIOState->fAVIRate); // dwRate
00637     size += addWord(0); // dwStart
00638     fCurrentIOState->fSTRHFrameCountPosition = (unsigned)TellFile64(fOutFid);
00639     size += addWord(0); // dwLength (fill in later)
00640     size += addWord(fBufferSize); // dwSuggestedBufferSize
00641     size += addWord((unsigned)-1); // dwQuality
00642     size += addWord(fCurrentIOState->fAVISize); // dwSampleSize
00643     size += addWord(0); // rcFrame (start)
00644     if (fCurrentIOState->fIsVideo) {
00645         size += addHalfWord(fMovieWidth);
00646         size += addHalfWord(fMovieHeight);
00647     } else {
00648         size += addWord(0);
00649     }
00650 addFileHeaderEnd;
00651 
00652 addFileHeader1(strf);
00653     if (fCurrentIOState->fIsVideo) {
00654       // Add a BITMAPINFO header:
00655       unsigned extraDataSize = 0;
00656       size += addWord(10*4 + extraDataSize); // size
00657       size += addWord(fMovieWidth);
00658       size += addWord(fMovieHeight);
00659       size += addHalfWord(1); // planes
00660       size += addHalfWord(24); // bits-per-sample #####
00661       size += addWord(fCurrentIOState->fAVICodecHandlerType); // compr. type
00662       size += addWord(fCurrentIOState->fAVISize);
00663       size += addZeroWords(4); // ??? #####
00664       // Later, add extra data here (if any) #####
00665     } else if (fCurrentIOState->fIsAudio) {
00666       // Add a WAVFORMATEX header:
00667       size += addHalfWord(fCurrentIOState->fWAVCodecTag);
00668       unsigned numChannels = fCurrentIOState->fOurSubsession.numChannels();
00669       size += addHalfWord(numChannels);
00670       size += addWord(fCurrentIOState->fAVISamplingFrequency);
00671       size += addWord(fCurrentIOState->fAVIRate); // bytes per second
00672       size += addHalfWord(fCurrentIOState->fAVISize); // block alignment
00673       unsigned bitsPerSample = (fCurrentIOState->fAVISize*8)/numChannels;
00674       size += addHalfWord(bitsPerSample);
00675       if (strcmp(fCurrentIOState->fOurSubsession.codecName(), "MPA") == 0) {
00676         // Assume MPEG layer II audio (not MP3): #####
00677         size += addHalfWord(22); // wav_extra_size
00678         size += addHalfWord(2); // fwHeadLayer
00679         size += addWord(8*fCurrentIOState->fAVIRate); // dwHeadBitrate #####
00680         size += addHalfWord(numChannels == 2 ? 1: 8); // fwHeadMode
00681         size += addHalfWord(0); // fwHeadModeExt
00682         size += addHalfWord(1); // wHeadEmphasis
00683         size += addHalfWord(16); // fwHeadFlags
00684         size += addWord(0); // dwPTSLow
00685         size += addWord(0); // dwPTSHigh
00686       }
00687     }
00688 addFileHeaderEnd;
00689 
00690 #define AVI_MASTER_INDEX_SIZE   256
00691 
00692 addFileHeader1(JUNK);
00693     if (fJunkNumber == 0) {
00694       size += addHalfWord(4); // wLongsPerEntry
00695       size += addHalfWord(0); // bIndexSubType + bIndexType
00696       size += addWord(0); // nEntriesInUse #####
00697       size += addWord(fCurrentIOState->fAVISubsessionTag); // dwChunkId
00698       size += addZeroWords(2); // dwReserved
00699       size += addZeroWords(AVI_MASTER_INDEX_SIZE*4);
00700     } else {
00701       size += add4ByteString("odml");
00702       size += add4ByteString("dmlh");
00703       unsigned wtfCount = 248;
00704       size += addWord(wtfCount); // ??? #####
00705       size += addZeroWords(wtfCount/4);
00706     }
00707 addFileHeaderEnd;
00708 
00709 addFileHeader(LIST,movi);
00710     fMoviSizePosition = headerSizePosn;
00711     fMoviSizeValue = size-ignoredSize;
00712 addFileHeaderEnd;

Generated on Thu Feb 2 23:51:29 2012 for live by  doxygen 1.5.2