📄 quicktimefilesink.cpp
字号:
SubsessionBuffer* tmp = fPrevBuffer; // assert: != NULL fPrevBuffer = fBuffer; fBuffer = tmp; } fBuffer->reset(); // for the next input // Now, try getting more frames: fOurSink.continuePlaying();}void SubsessionIOState::useFrame(SubsessionBuffer& buffer) { unsigned char* const frameSource = buffer.dataStart(); unsigned const frameSize = buffer.bytesInUse(); struct timeval const& presentationTime = buffer.presentationTime(); unsigned const destFileOffset = ftell(fOurSink.fOutFid); unsigned sampleNumberOfFrameStart = fQTTotNumSamples + 1; // If we're not syncing streams, or this subsession is not video, then // just give this frame a fixed duration: if (!fOurSink.fSyncStreams || fQTcomponentSubtype != fourChar('v','i','d','e')) { unsigned const frameDuration = fQTTimeUnitsPerSample*fQTSamplesPerFrame; fQTTotNumSamples += useFrame1(frameSize, presentationTime, frameDuration, destFileOffset); } else { // For synced video streams, we use the difference between successive // frames' presentation times as the 'frame duration'. So, record // information about the *previous* frame: struct timeval const& ppt = fPrevFrameState.presentationTime; //abbrev if (ppt.tv_sec != 0 || ppt.tv_usec != 0) { // There has been a previous frame. double duration = (presentationTime.tv_sec - ppt.tv_sec) + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0; if (duration < 0.0) duration = 0.0; unsigned frameDuration = (unsigned)((2*duration*fQTTimeScale+1)/2); // round unsigned numSamples = useFrame1(fPrevFrameState.frameSize, ppt, frameDuration, fPrevFrameState.destFileOffset); fQTTotNumSamples += numSamples; sampleNumberOfFrameStart = fQTTotNumSamples + 1; } // Remember the current frame for next time: fPrevFrameState.frameSize = frameSize; fPrevFrameState.presentationTime = presentationTime; fPrevFrameState.destFileOffset = destFileOffset; } // Write the data into the file: fwrite(frameSource, 1, frameSize, fOurSink.fOutFid); // If we have a hint track, then write to it also: if (hasHintTrack()) { // Because presentation times are used for RTP packet timestamps, // we don't starting writing to the hint track until we've been synced: if (!fHaveBeenSynced) { fHaveBeenSynced = fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP(); } if (fHaveBeenSynced) { fHintTrackForUs->useFrameForHinting(frameSize, presentationTime, sampleNumberOfFrameStart); } }}void SubsessionIOState::useFrameForHinting(unsigned frameSize, struct timeval presentationTime, unsigned startSampleNumber) { // At this point, we have a single, combined frame - not individual packets. // For the hint track, we need to split the frame back up into separate packets. // However, for some RTP sources, then we also need to reuse the special // header bytes that were at the start of each of the RTP packets. Boolean hack263 = strcmp(fOurSubsession.codecName(), "H263-1998") == 0; Boolean hackm4a_generic = strcmp(fOurSubsession.mediumName(), "audio") == 0 && strcmp(fOurSubsession.codecName(), "MPEG4-GENERIC") == 0; Boolean hackm4a_latm = strcmp(fOurSubsession.mediumName(), "audio") == 0 && strcmp(fOurSubsession.codecName(), "MP4A-LATM") == 0; Boolean hackm4a = hackm4a_generic || hackm4a_latm; Boolean haveSpecialHeaders = (hack263 || hackm4a_generic); // If there has been a previous frame, then output a 'hint sample' for it. // (We use the current frame's presentation time to compute the previous // hint sample's duration.) RTPSource* const rs = fOurSubsession.rtpSource(); // abbrev struct timeval const& ppt = fPrevFrameState.presentationTime; //abbrev if (ppt.tv_sec != 0 || ppt.tv_usec != 0) { double duration = (presentationTime.tv_sec - ppt.tv_sec) + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0; if (duration < 0.0) duration = 0.0; unsigned msDuration = (unsigned)(duration*1000); // milliseconds if (msDuration > fHINF.dmax) fHINF.dmax = msDuration; unsigned hintSampleDuration = (unsigned)((2*duration*fQTTimeScale+1)/2); // round if (hackm4a) { // Because multiple AAC frames can appear in a RTP packet, the presentation // times of the second and subsequent frames will not be accurate. // So, use the known "hintSampleDuration" instead: hintSampleDuration = fTrackHintedByUs->fQTTimeUnitsPerSample; // Also, if the 'time scale' was different from the RTP timestamp frequency, // (as can happen with aacPlus), then we need to scale "hintSampleDuration" // accordingly: if (fTrackHintedByUs->fQTTimeScale != fOurSubsession.rtpTimestampFrequency()) { unsigned const scalingFactor = fOurSubsession.rtpTimestampFrequency()/fTrackHintedByUs->fQTTimeScale ; hintSampleDuration *= scalingFactor; } } unsigned const hintSampleDestFileOffset = ftell(fOurSink.fOutFid); unsigned const maxPacketSize = 1450; unsigned short numPTEntries = (fPrevFrameState.frameSize + (maxPacketSize-1))/maxPacketSize; // normal case unsigned char* immediateDataPtr = NULL; unsigned immediateDataBytesRemaining = 0; if (haveSpecialHeaders) { // special case numPTEntries = fPrevFrameState.numSpecialHeaders; immediateDataPtr = fPrevFrameState.specialHeaderBytes; immediateDataBytesRemaining = fPrevFrameState.specialHeaderBytesLength; } unsigned hintSampleSize = fOurSink.addHalfWord(numPTEntries);// Entry count hintSampleSize += fOurSink.addHalfWord(0x0000); // Reserved unsigned offsetWithinSample = 0; for (unsigned i = 0; i < numPTEntries; ++i) { // Output a Packet Table entry (representing a single RTP packet): unsigned short numDTEntries = 1; unsigned short seqNum = fPrevFrameState.seqNum++; // Note: This assumes that the input stream had no packets lost ##### unsigned rtpHeader = fPrevFrameState.rtpHeader; if (i+1 < numPTEntries) { // This is not the last RTP packet, so clear the marker bit: rtpHeader &=~ (1<<23); } unsigned dataFrameSize = (i+1 < numPTEntries) ? maxPacketSize : fPrevFrameState.frameSize - i*maxPacketSize; // normal case unsigned sampleNumber = fPrevFrameState.startSampleNumber; unsigned char immediateDataLen = 0; if (haveSpecialHeaders) { // special case ++numDTEntries; // to include a Data Table entry for the special hdr if (immediateDataBytesRemaining > 0) { if (hack263) { immediateDataLen = *immediateDataPtr++; --immediateDataBytesRemaining; if (immediateDataLen > immediateDataBytesRemaining) { // shouldn't happen (length byte was bad) immediateDataLen = immediateDataBytesRemaining; } } else { immediateDataLen = fPrevFrameState.specialHeaderBytesLength; } } dataFrameSize = fPrevFrameState.packetSizes[i] - immediateDataLen; if (hack263) { Boolean PbitSet = immediateDataLen >= 1 && (immediateDataPtr[0]&0x4) != 0; if (PbitSet) { offsetWithinSample += 2; // to omit the two leading 0 bytes } } } // Output the Packet Table: hintSampleSize += fOurSink.addWord(0); // Relative transmission time hintSampleSize += fOurSink.addWord(rtpHeader|seqNum); // RTP header info + RTP sequence number hintSampleSize += fOurSink.addHalfWord(0x0000); // Flags hintSampleSize += fOurSink.addHalfWord(numDTEntries); // Entry count unsigned totalPacketSize = 0; // Output the Data Table: if (haveSpecialHeaders) { // use the "Immediate Data" format (1): hintSampleSize += fOurSink.addByte(1); // Source unsigned char len = immediateDataLen > 14 ? 14 : immediateDataLen; hintSampleSize += fOurSink.addByte(len); // Length totalPacketSize += len; fHINF.dimm += len; unsigned char j; for (j = 0; j < len; ++j) { hintSampleSize += fOurSink.addByte(immediateDataPtr[j]); // Data } for (j = len; j < 14; ++j) { hintSampleSize += fOurSink.addByte(0); // Data (padding) } immediateDataPtr += immediateDataLen; immediateDataBytesRemaining -= immediateDataLen; } // use the "Sample Data" format (2): hintSampleSize += fOurSink.addByte(2); // Source hintSampleSize += fOurSink.addByte(0); // Track ref index hintSampleSize += fOurSink.addHalfWord(dataFrameSize); // Length totalPacketSize += dataFrameSize; fHINF.dmed += dataFrameSize; hintSampleSize += fOurSink.addWord(sampleNumber); // Sample number hintSampleSize += fOurSink.addWord(offsetWithinSample); // Offset // Get "bytes|samples per compression block" from the hinted track: unsigned short const bytesPerCompressionBlock = fTrackHintedByUs->fQTBytesPerFrame; unsigned short const samplesPerCompressionBlock = fTrackHintedByUs->fQTSamplesPerFrame; hintSampleSize += fOurSink.addHalfWord(bytesPerCompressionBlock); hintSampleSize += fOurSink.addHalfWord(samplesPerCompressionBlock); offsetWithinSample += dataFrameSize;// for the next iteration (if any) // Tally statistics for this packet: fHINF.nump += 1; fHINF.tpyl += totalPacketSize; totalPacketSize += 12; // add in the size of the RTP header fHINF.trpy += totalPacketSize; if (totalPacketSize > fHINF.pmax) fHINF.pmax = totalPacketSize; } // Make note of this completed hint sample frame: fQTTotNumSamples += useFrame1(hintSampleSize, ppt, hintSampleDuration, hintSampleDestFileOffset); } // Remember this frame for next time: fPrevFrameState.frameSize = frameSize; fPrevFrameState.presentationTime = presentationTime; fPrevFrameState.startSampleNumber = startSampleNumber; fPrevFrameState.rtpHeader = rs->curPacketMarkerBit()<<23 | (rs->rtpPayloadFormat()&0x7F)<<16; if (hack263) { H263plusVideoRTPSource* rs_263 = (H263plusVideoRTPSource*)rs; fPrevFrameState.numSpecialHeaders = rs_263->fNumSpecialHeaders; fPrevFrameState.specialHeaderBytesLength = rs_263->fSpecialHeaderBytesLength; unsigned i; for (i = 0; i < rs_263->fSpecialHeaderBytesLength; ++i) { fPrevFrameState.specialHeaderBytes[i] = rs_263->fSpecialHeaderBytes[i]; } for (i = 0; i < rs_263->fNumSpecialHeaders; ++i) { fPrevFrameState.packetSizes[i] = rs_263->fPacketSizes[i]; } } else if (hackm4a_generic) { // Synthesize a special header, so that this frame can be in its own RTP packet. unsigned const sizeLength = fOurSubsession.fmtp_sizelength(); unsigned const indexLength = fOurSubsession.fmtp_indexlength(); if (sizeLength + indexLength != 16) { envir() << "Warning: unexpected 'sizeLength' " << sizeLength << " and 'indexLength' " << indexLength << "seen when creating hint track\n"; } fPrevFrameState.numSpecialHeaders = 1; fPrevFrameState.specialHeaderBytesLength = 4; fPrevFrameState.specialHeaderBytes[0] = 0; // AU_headers_length (high byte) fPrevFrameState.specialHeaderBytes[1] = 16; // AU_headers_length (low byte) fPrevFrameState.specialHeaderBytes[2] = ((frameSize<<indexLength)&0xFF00)>>8; fPrevFrameState.specialHeaderBytes[3] = (frameSize<<indexLength); fPrevFrameState.packetSizes[0] = fPrevFrameState.specialHeaderBytesLength + frameSize; }}unsigned SubsessionIOState::useFrame1(unsigned sourceDataSize, struct timeval presentationTime, unsigned frameDuration, unsigned destFileOffset) { // Figure out the actual frame size for this data: unsigned frameSize = fQTBytesPerFrame; if (frameSize == 0) { // The entire packet data is assumed to be a frame: frameSize = sourceDataSize; } unsigned const numFrames = sourceDataSize/frameSize; unsigned const numSamples = numFrames*fQTSamplesPerFrame; // Record the information about which 'chunk' this data belongs to: ChunkDescriptor* newTailChunk; if (fTailChunk == NULL) { newTailChunk = fHeadChunk = new ChunkDescriptor(destFileOffset, sourceDataSize, frameSize, frameDuration, presentationTime); } else { newTailChunk = fTailChunk->extendChunk(destFileOffset, sourceDataSize, frameSize, frameDuration, presentationTime); } if (newTailChunk != fTailChunk) { // This data created a new chunk, rather than extending the old one ++fNumChunks; fTailChunk = newTailChunk; } return numSamples;} void SubsessionIOState::onSourceClosure() { fOurSourceIsActive = False; fOurSink.onSourceClosure1();}Boolean SubsessionIOState::syncOK(struct timeval presentationTime) { QuickTimeFileSink& s = fOurSink; // abbreviation if (!s.fSyncStreams) return True; // we don't care if (s.fNumSyncedSubsessions < s.fNumSubsessions) { // Not all subsessions have yet been synced. Check whether ours was // one of the unsynced ones, and, if so, whether it is now synced: if (!fHaveBeenSynced) { // We weren't synchronized before if (fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) { // But now we are fHaveBeenSynced = True; fSyncTime = presentationTime; ++s.fNumSyncedSubsessions; if (timevalGE(fSyncTime, s.fNewestSyncTime)) { s.fNewestSyncTime = fSyncTime; } } } } // Check again whether all subsessions have been synced: if (s.fNumSyncedSubsessions < s.fNumSubsessions) return False; // 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;}void Count64::operator+=(unsigned arg) { unsigned newLo = lo + arg; if (newLo < lo) { // lo has overflowed ++hi; } lo = newLo;}ChunkDescriptor::ChunkDescriptor(unsigned 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(unsigned 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -