📄 quicktimefilesink.cpp
字号:
// 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::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;}void QuickTimeFileSink::setWord(unsigned filePosn, unsigned size) { do { if (fseek(fOutFid, filePosn, SEEK_SET) < 0) break; addWord(size); if (fseek(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() { \ unsigned initFilePosn = ftell(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 = ftell(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 = ftell(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: unsigned numEntriesPosition = ftell(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; if (outOfSync > syncThreshold) { // The track's data is too short, so end this edit, add a new // 'empty' edit after it, and start a new edit // (at the current track posn.): if (trackDurationOfEdit > 0.0) addEdit(trackDurationOfEdit); addEmptyEdit(outOfSync); editStartTime = chunkStartTime; editTrackPosition = currentTrackPosition; } else if (outOfSync < -syncThreshold) { // The track's data is too long, so end this edit, and start // a new edit (pointing at the current track posn.): if (movieDurationOfEdit > 0.0) addEdit(movieDurationOfEdit); editStartTime = chunkStartTime; editTrackPosition = currentTrackPosition; } // Note the duration of this chunk: unsigned numChannels = fCurrentIOState->fOurSubsession.numChannels(); chunkDuration = chunk->fNumFrames*chunk->fFrameDuration/numChannels; currentTrackPosition += chunkDuration; chunk = chunk->fNextChunk; } // Write out the final edit trackDurationOfEdit += (double)chunkDuration/fCurrentIOState->fQTTimeScale; if (trackDurationOfEdit > 0.0) addEdit(trackDurationOfEdit); // Now go back and fill in the "Number of entries" field: setWord(numEntriesPosition, numEdits); // Also, if the sum of all of the edit durations exceeds the // track duration that we already computed (from sample durations), // then reset the track duration to this new value: if (totalDurationOfEdits > fCurrentIOState->fQTDurationM) { fCurrentIOState->fQTDurationM = totalDurationOfEdits; setWord(fCurrentIOState->fTKHD_durationPosn, totalDurationOfEdits); // Also, check whether the overall movie duration needs to change: if (totalDurationOfEdits > fMaxTrackDurationM) { fMaxTrackDurationM = totalDurationOfEdits; setWord(fMVHD_durationPosn, totalDurationOfEdits); } // Also, convert to track time scale: double scaleFactor = fCurrentIOState->fQTTimeScale/(double)movieTimeScale(); fCurrentIOState->fQTDurationT = (unsigned)(totalDurationOfEdits*scaleFactor); }addAtomEnd;addAtom(tref); size += addAtom_hint();addAtomEnd;addAtom(hint); SubsessionIOState* hintedTrack = fCurrentIOState->fTrackHintedByUs; // Assert: hintedTrack != NULL size += addWord(hintedTrack->fTrackID);addAtomEnd;addAtom(mdia); size += addAtom_mdhd(); size += addAtom_hdlr(); size += addAtom_minf();addAtomEnd;addAtom(mdhd); size += addWord(0x00000000); // Version + Flags size += addWord(fAppleCreationTime); // Creation time size += addWord(fAppleCreationTime); // Modification time unsigned const timeScale = fCurrentIOState->fQTTimeScale; size += addWord(timeScale); // Time scale unsigned const duration = fCurrentIOState->fQTDurationT; // track units
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -