📄 quicktimefilesink.cpp
字号:
} case fourChar('h','2','6','3'): { fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS; break; } } } } else if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_Qclp) { // For QCELP data, make a note of the frame size (even though it's the // same as the packet data size), because it varies depending on the // 'rate' of the stream, and this size gets used later when setting up // the 'Qclp' QuickTime atom: fQTBytesPerFrame = packetDataSize; } useFrame(*fBuffer); if (fOurSink.fPacketLossCompensate) { // Save this frame, in case we need it for recovery: 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(); int64_t const destFileOffset = TellFile64(fOurSink.fOutFid); unsigned sampleNumberOfFrameStart = fQTTotNumSamples + 1; Boolean avcHack = fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1; // 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; unsigned frameSizeToUse = frameSize; if (avcHack) frameSizeToUse += 4; // H.264/AVC gets the frame size prefix fQTTotNumSamples += useFrame1(frameSizeToUse, 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 frameSizeToUse = fPrevFrameState.frameSize; if (avcHack) frameSizeToUse += 4; // H.264/AVC gets the frame size prefix unsigned numSamples = useFrame1(frameSizeToUse, ppt, frameDuration, fPrevFrameState.destFileOffset); fQTTotNumSamples += numSamples; sampleNumberOfFrameStart = fQTTotNumSamples + 1; } if (avcHack && (*frameSource == H264_IDR_FRAME)) { SyncFrame* newSyncFrame = new SyncFrame(fQTTotNumSamples + 1); if (fTailSyncFrame == NULL) { fHeadSyncFrame = newSyncFrame; } else { fTailSyncFrame->nextSyncFrame = newSyncFrame; } fTailSyncFrame = newSyncFrame; } // Remember the current frame for next time: fPrevFrameState.frameSize = frameSize; fPrevFrameState.presentationTime = presentationTime; fPrevFrameState.destFileOffset = destFileOffset; } if (avcHack) fOurSink.addWord(frameSize); // 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; } } int64_t const hintSampleDestFileOffset = TellFile64(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, int64_t 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()) { // H264 ? if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1) { // special case: audio + H264 video: wait until audio is in sync if ((s.fNumSubsessions == 2) && (s.fNumSyncedSubsessions < (s.fNumSubsessions - 1))) return False; // if audio is in sync, wait for the next IDR frame to start unsigned char* const frameSource = fBuffer->dataStart(); if (*frameSource != H264_IDR_FRAME) return False; } // 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -