📄 quicktimefilesink.cpp
字号:
// Allow this data if it is more recent than the newest sync time: return timevalGE(presentationTime, s.fNewestSyncTime);}void SubsessionIOState::setHintTrack(SubsessionIOState* hintedTrack, SubsessionIOState* hintTrack) { if (hintedTrack != NULL) hintedTrack->fHintTrackForUs = hintTrack; if (hintTrack != NULL) hintTrack->fTrackHintedByUs = hintedTrack;}SyncFrame::SyncFrame(unsigned frameNum) : nextSyncFrame(NULL), sfFrameNum(frameNum) {} SyncFrame::~SyncFrame() { delete nextSyncFrame;}void Count64::operator+=(unsigned arg) { unsigned newLo = lo + arg; if (newLo < lo) { // lo has overflowed ++hi; } lo = newLo;}ChunkDescriptor::ChunkDescriptor(int64_t offsetInFile, unsigned size, unsigned frameSize, unsigned frameDuration, struct timeval presentationTime) : fNextChunk(NULL), fOffsetInFile(offsetInFile), fNumFrames(size/frameSize), fFrameSize(frameSize), fFrameDuration(frameDuration), fPresentationTime(presentationTime) {}ChunkDescriptor::~ChunkDescriptor() { delete fNextChunk;}ChunkDescriptor* ChunkDescriptor::extendChunk(int64_t newOffsetInFile, unsigned newSize, unsigned newFrameSize, unsigned newFrameDuration, struct timeval newPresentationTime) { // First, check whether the new space is just at the end of this // existing chunk: if (newOffsetInFile == fOffsetInFile + fNumFrames*fFrameSize) { // We can extend this existing chunk, provided that the frame size // and frame duration have not changed: if (newFrameSize == fFrameSize && newFrameDuration == fFrameDuration) { fNumFrames += newSize/fFrameSize; return this; } } // We'll allocate a new ChunkDescriptor, and link it to the end of us: ChunkDescriptor* newDescriptor = new ChunkDescriptor(newOffsetInFile, newSize, newFrameSize, newFrameDuration, newPresentationTime); fNextChunk = newDescriptor; return newDescriptor;}////////// QuickTime-specific implementation //////////unsigned QuickTimeFileSink::addWord64(u_int64_t word) { addByte((unsigned char)(word>>56)); addByte((unsigned char)(word>>48)); addByte((unsigned char)(word>>40)); addByte((unsigned char)(word>>32)); addByte((unsigned char)(word>>24)); addByte((unsigned char)(word>>16)); addByte((unsigned char)(word>>8)); addByte((unsigned char)(word)); return 8;}unsigned QuickTimeFileSink::addWord(unsigned word) { addByte(word>>24); addByte(word>>16); addByte(word>>8); addByte(word); return 4;}unsigned QuickTimeFileSink::addHalfWord(unsigned short halfWord) { addByte((unsigned char)(halfWord>>8)); addByte((unsigned char)halfWord); return 2;}unsigned QuickTimeFileSink::addZeroWords(unsigned numWords) { for (unsigned i = 0; i < numWords; ++i) { addWord(0); } return numWords*4;}unsigned QuickTimeFileSink::add4ByteString(char const* str) { addByte(str[0]); addByte(str[1]); addByte(str[2]); addByte(str[3]); return 4;}unsigned QuickTimeFileSink::addArbitraryString(char const* str, Boolean oneByteLength) { unsigned size = 0; if (oneByteLength) { // Begin with a byte containing the string length: unsigned strLength = strlen(str); if (strLength >= 256) { envir() << "QuickTimeFileSink::addArbitraryString(\"" << str << "\") saw string longer than we know how to handle (" << strLength << "\n"; } size += addByte((unsigned char)strLength); } while (*str != '\0') { size += addByte(*str++); } return size;}unsigned QuickTimeFileSink::addAtomHeader(char const* atomName) { // Output a placeholder for the 4-byte size: addWord(0); // Output the 4-byte atom name: add4ByteString(atomName); return 8;}unsigned QuickTimeFileSink::addAtomHeader64(char const* atomName) { // Output 64Bit size marker addWord(1); // Output the 4-byte atom name: add4ByteString(atomName); addWord64(0); return 16;}void QuickTimeFileSink::setWord(int64_t filePosn, unsigned size) { do { if (SeekFile64(fOutFid, filePosn, SEEK_SET) < 0) break; addWord(size); if (SeekFile64(fOutFid, 0, SEEK_END) < 0) break; // go back to where we were return; } while (0); // One of the fseek()s failed, probable because we're not a seekable file envir() << "QuickTimeFileSink::setWord(): fseek failed (err " << envir().getErrno() << ")\n";}void QuickTimeFileSink::setWord64(int64_t filePosn, u_int64_t size) { do { if (SeekFile64(fOutFid, filePosn, SEEK_SET) < 0) break; addWord64(size); if (SeekFile64(fOutFid, 0, SEEK_END) < 0) break; // go back to where we were return; } while (0); // One of the fseek()s failed, probable because we're not a seekable file envir() << "QuickTimeFileSink::setWord(): fseek failed (err " << envir().getErrno() << ")\n";}// Methods for writing particular atoms. Note the following macros:#define addAtom(name) \ unsigned QuickTimeFileSink::addAtom_##name() { \ int64_t initFilePosn = TellFile64(fOutFid); \ unsigned size = addAtomHeader("" #name "")#define addAtomEnd \ setWord(initFilePosn, size); \ return size; \}addAtom(ftyp); size += add4ByteString("mp42"); size += addWord(0x00000000); size += add4ByteString("mp42"); size += add4ByteString("isom");addAtomEnd;addAtom(moov); size += addAtom_mvhd(); if (fGenerateMP4Format) { size += addAtom_iods(); } // Add a 'trak' atom for each subsession: // (For some unknown reason, QuickTime Player (5.0 at least) // doesn't display the movie correctly unless the audio track // (if present) appears before the video track. So ensure this here.) MediaSubsessionIterator iter(fInputSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { fCurrentIOState = (SubsessionIOState*)(subsession->miscPtr); if (fCurrentIOState == NULL) continue; if (strcmp(subsession->mediumName(), "audio") != 0) continue; size += addAtom_trak(); if (fCurrentIOState->hasHintTrack()) { // This track has a hint track; output it also: fCurrentIOState = fCurrentIOState->fHintTrackForUs; size += addAtom_trak(); } } iter.reset(); while ((subsession = iter.next()) != NULL) { fCurrentIOState = (SubsessionIOState*)(subsession->miscPtr); if (fCurrentIOState == NULL) continue; if (strcmp(subsession->mediumName(), "audio") == 0) continue; size += addAtom_trak(); if (fCurrentIOState->hasHintTrack()) { // This track has a hint track; output it also: fCurrentIOState = fCurrentIOState->fHintTrackForUs; size += addAtom_trak(); } }addAtomEnd;addAtom(mvhd); size += addWord(0x00000000); // Version + Flags size += addWord(fAppleCreationTime); // Creation time size += addWord(fAppleCreationTime); // Modification time // For the "Time scale" field, use the largest RTP timestamp frequency // that we saw in any of the subsessions. size += addWord(movieTimeScale()); // Time scale unsigned const duration = fMaxTrackDurationM; fMVHD_durationPosn = TellFile64(fOutFid); size += addWord(duration); // Duration size += addWord(0x00010000); // Preferred rate size += addWord(0x01000000); // Preferred volume + Reserved[0] size += addZeroWords(2); // Reserved[1-2] size += addWord(0x00010000); // matrix top left corner size += addZeroWords(3); // matrix size += addWord(0x00010000); // matrix center size += addZeroWords(3); // matrix size += addWord(0x40000000); // matrix bottom right corner size += addZeroWords(6); // various time fields size += addWord(SubsessionIOState::fCurrentTrackNumber+1);// Next track IDaddAtomEnd;addAtom(iods); size += addWord(0x00000000); // Version + Flags size += addWord(0x10808080); size += addWord(0x07004FFF); size += addWord(0xFF0FFFFF);addAtomEnd;addAtom(trak); size += addAtom_tkhd(); // If we're synchronizing the media streams (or are a hint track), // add an edit list that helps do this: if (fCurrentIOState->fHeadChunk != NULL && (fSyncStreams || fCurrentIOState->isHintTrack())) { size += addAtom_edts(); } // If we're generating a hint track, add a 'tref' atom: if (fCurrentIOState->isHintTrack()) size += addAtom_tref(); size += addAtom_mdia(); // If we're generating a hint track, add a 'udta' atom: if (fCurrentIOState->isHintTrack()) size += addAtom_udta();addAtomEnd;addAtom(tkhd); if (fCurrentIOState->fQTEnableTrack) { size += addWord(0x0000000F); // Version + Flags } else { // Disable this track in the movie: size += addWord(0x00000000); // Version + Flags } size += addWord(fAppleCreationTime); // Creation time size += addWord(fAppleCreationTime); // Modification time size += addWord(fCurrentIOState->fTrackID); // Track ID size += addWord(0x00000000); // Reserved unsigned const duration = fCurrentIOState->fQTDurationM; // movie units fCurrentIOState->fTKHD_durationPosn = TellFile64(fOutFid); size += addWord(duration); // Duration size += addZeroWords(3); // Reserved+Layer+Alternate grp size += addWord(0x01000000); // Volume + Reserved size += addWord(0x00010000); // matrix top left corner size += addZeroWords(3); // matrix size += addWord(0x00010000); // matrix center size += addZeroWords(3); // matrix size += addWord(0x40000000); // matrix bottom right corner if (strcmp(fCurrentIOState->fOurSubsession.mediumName(), "video") == 0) { size += addWord(fMovieWidth<<16); // Track width size += addWord(fMovieHeight<<16); // Track height } else { size += addZeroWords(2); // not video: leave width and height fields zero }addAtomEnd;addAtom(edts); size += addAtom_elst();addAtomEnd;#define addEdit1(duration,trackPosition) do { \ unsigned trackDuration \ = (unsigned) ((2*(duration)*movieTimeScale()+1)/2); \ /* in movie time units */ \ size += addWord(trackDuration); /* Track duration */ \ totalDurationOfEdits += trackDuration; \ size += addWord(trackPosition); /* Media time */ \ size += addWord(0x00010000); /* Media rate (1x) */ \ ++numEdits; \} while (0)#define addEdit(duration) addEdit1((duration),editTrackPosition)#define addEmptyEdit(duration) addEdit1((duration),(~0))addAtom(elst); size += addWord(0x00000000); // Version + Flags // Add a dummy "Number of entries" field // (and remember its position). We'll fill this field in later: int64_t numEntriesPosition = TellFile64(fOutFid); size += addWord(0); // dummy for "Number of entries" unsigned numEdits = 0; unsigned totalDurationOfEdits = 0; // in movie time units // Run through our chunks, looking at their presentation times. // From these, figure out the edits that need to be made to keep // the track media data in sync with the presentation times. double const syncThreshold = 0.1; // 100 ms // don't allow the track to get out of sync by more than this struct timeval editStartTime = fFirstDataTime; unsigned editTrackPosition = 0; unsigned currentTrackPosition = 0; double trackDurationOfEdit = 0.0; unsigned chunkDuration = 0; ChunkDescriptor* chunk = fCurrentIOState->fHeadChunk; while (chunk != NULL) { struct timeval const& chunkStartTime = chunk->fPresentationTime; double movieDurationOfEdit = (chunkStartTime.tv_sec - editStartTime.tv_sec) + (chunkStartTime.tv_usec - editStartTime.tv_usec)/1000000.0; trackDurationOfEdit = (currentTrackPosition-editTrackPosition) / (double)(fCurrentIOState->fQTTimeScale); double outOfSync = movieDurationOfEdit - trackDurationOfEdit;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -