
Definition at line 109 of file QuickTimeFileSink.cpp.
| typedef unsigned(QuickTimeFileSink::*) SubsessionIOState::atomCreationFunc() |
Definition at line 149 of file QuickTimeFileSink.cpp.
| SubsessionIOState::SubsessionIOState | ( | QuickTimeFileSink & | sink, | |
| MediaSubsession & | subsession | |||
| ) |
Definition at line 527 of file QuickTimeFileSink.cpp.
References fBuffer, QuickTimeFileSink::fBufferSize, fCurrentTrackNumber, fOurSink, fOurSourceIsActive, QuickTimeFileSink::fPacketLossCompensate, fPrevBuffer, fPrevFrameState, fTrackID, NULL, MediaSubsession::readSource(), and subsession.
00529 : fHintTrackForUs(NULL), fTrackHintedByUs(NULL), 00530 fOurSink(sink), fOurSubsession(subsession), 00531 fLastPacketRTPSeqNum(0), fHaveBeenSynced(False), fQTTotNumSamples(0), 00532 fHeadChunk(NULL), fTailChunk(NULL), fNumChunks(0), 00533 fHeadSyncFrame(NULL), fTailSyncFrame(NULL) { 00534 fTrackID = ++fCurrentTrackNumber; 00535 00536 fBuffer = new SubsessionBuffer(fOurSink.fBufferSize); 00537 fPrevBuffer = sink.fPacketLossCompensate 00538 ? new SubsessionBuffer(fOurSink.fBufferSize) : NULL; 00539 00540 FramedSource* subsessionSource = subsession.readSource(); 00541 fOurSourceIsActive = subsessionSource != NULL; 00542 00543 fPrevFrameState.presentationTime.tv_sec = 0; 00544 fPrevFrameState.presentationTime.tv_usec = 0; 00545 fPrevFrameState.seqNum = 0; 00546 }
| SubsessionIOState::~SubsessionIOState | ( | ) | [virtual] |
Definition at line 548 of file QuickTimeFileSink.cpp.
References chunk, fBuffer, fHeadChunk, fHeadSyncFrame, ChunkDescriptor::fNextChunk, fPrevBuffer, SyncFrame::nextSyncFrame, and NULL.
00548 { 00549 delete fBuffer; delete fPrevBuffer; 00550 00551 // Delete the list of chunk descriptors: 00552 ChunkDescriptor* chunk = fHeadChunk; 00553 while (chunk != NULL) { 00554 ChunkDescriptor* next = chunk->fNextChunk; 00555 delete chunk; 00556 chunk = next; 00557 } 00558 00559 // Delete the list of sync frames: 00560 SyncFrame* syncFrame = fHeadSyncFrame; 00561 while (syncFrame != NULL) { 00562 SyncFrame* next = syncFrame->nextSyncFrame; 00563 delete syncFrame; 00564 syncFrame = next; 00565 } 00566 }
| Boolean SubsessionIOState::setQTstate | ( | ) |
Definition at line 568 of file QuickTimeFileSink.cpp.
References QuickTimeFileSink::addAtom_dummy(), QuickTimeFileSink::addAtom_genericMedia(), QuickTimeFileSink::addAtom_soundMediaGeneral(), MediaSubsession::codecName(), envir(), False, QuickTimeFileSink::fMovieFPS, MediaSubsession::fmtp_config(), fourChar, fOurSink, fOurSubsession, fQTAudioDataType, fQTBytesPerFrame, fQTcomponentName, fQTcomponentSubtype, fQTEnableTrack, fQTMediaDataAtomCreator, fQTMediaInformationAtomCreator, fQTSamplesPerFrame, fQTSoundSampleVersion, fQTTimeScale, fQTTimeUnitsPerSample, isHintTrack(), MediaSubsession::mediumName(), MediaSubsession::rtpTimestampFrequency(), samplingFrequencyFromAudioSpecificConfig(), and True.
Referenced by QuickTimeFileSink::QuickTimeFileSink().
00568 { 00569 char const* noCodecWarning1 = "Warning: We don't implement a QuickTime "; 00570 char const* noCodecWarning2 = " Media Data Type for the \""; 00571 char const* noCodecWarning3 = "\" track, so we'll insert a dummy \"????\" Media Data Atom instead. A separate, codec-specific editing pass will be needed before this track can be played.\n"; 00572 00573 do { 00574 fQTEnableTrack = True; // enable this track in the movie by default 00575 fQTTimeScale = fOurSubsession.rtpTimestampFrequency(); // by default 00576 fQTTimeUnitsPerSample = 1; // by default 00577 fQTBytesPerFrame = 0; 00578 // by default - indicates that the whole packet data is a frame 00579 fQTSamplesPerFrame = 1; // by default 00580 00581 // Make sure our subsession's medium is one that we know how to 00582 // represent in a QuickTime file: 00583 if (isHintTrack()) { 00584 // Hint tracks are treated specially 00585 fQTEnableTrack = False; // hint tracks are marked as inactive 00586 fQTcomponentSubtype = fourChar('h','i','n','t'); 00587 fQTcomponentName = "hint media handler"; 00588 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_gmhd; 00589 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_rtp; 00590 } else if (strcmp(fOurSubsession.mediumName(), "audio") == 0) { 00591 fQTcomponentSubtype = fourChar('s','o','u','n'); 00592 fQTcomponentName = "Apple Sound Media Handler"; 00593 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_smhd; 00594 fQTMediaDataAtomCreator 00595 = &QuickTimeFileSink::addAtom_soundMediaGeneral; // by default 00596 fQTSoundSampleVersion = 0; // by default 00597 00598 // Make sure that our subsession's codec is one that we can handle: 00599 if (strcmp(fOurSubsession.codecName(), "X-QT") == 0 || 00600 strcmp(fOurSubsession.codecName(), "X-QUICKTIME") == 0) { 00601 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_genericMedia; 00602 } else if (strcmp(fOurSubsession.codecName(), "PCMU") == 0) { 00603 fQTAudioDataType = "ulaw"; 00604 fQTBytesPerFrame = 1; 00605 } else if (strcmp(fOurSubsession.codecName(), "GSM") == 0) { 00606 fQTAudioDataType = "agsm"; 00607 fQTBytesPerFrame = 33; 00608 fQTSamplesPerFrame = 160; 00609 } else if (strcmp(fOurSubsession.codecName(), "PCMA") == 0) { 00610 fQTAudioDataType = "alaw"; 00611 fQTBytesPerFrame = 1; 00612 } else if (strcmp(fOurSubsession.codecName(), "QCELP") == 0) { 00613 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_Qclp; 00614 fQTSamplesPerFrame = 160; 00615 } else if (strcmp(fOurSubsession.codecName(), "MPEG4-GENERIC") == 0 || 00616 strcmp(fOurSubsession.codecName(), "MP4A-LATM") == 0) { 00617 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_mp4a; 00618 fQTTimeUnitsPerSample = 1024; // QT considers each frame to be a 'sample' 00619 // The time scale (frequency) comes from the 'config' information. 00620 // It might be different from the RTP timestamp frequency (e.g., aacPlus). 00621 unsigned frequencyFromConfig 00622 = samplingFrequencyFromAudioSpecificConfig(fOurSubsession.fmtp_config()); 00623 if (frequencyFromConfig != 0) fQTTimeScale = frequencyFromConfig; 00624 } else { 00625 envir() << noCodecWarning1 << "Audio" << noCodecWarning2 00626 << fOurSubsession.codecName() << noCodecWarning3; 00627 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_dummy; 00628 fQTEnableTrack = False; // disable this track in the movie 00629 } 00630 } else if (strcmp(fOurSubsession.mediumName(), "video") == 0) { 00631 fQTcomponentSubtype = fourChar('v','i','d','e'); 00632 fQTcomponentName = "Apple Video Media Handler"; 00633 fQTMediaInformationAtomCreator = &QuickTimeFileSink::addAtom_vmhd; 00634 00635 // Make sure that our subsession's codec is one that we can handle: 00636 if (strcmp(fOurSubsession.codecName(), "X-QT") == 0 || 00637 strcmp(fOurSubsession.codecName(), "X-QUICKTIME") == 0) { 00638 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_genericMedia; 00639 } else if (strcmp(fOurSubsession.codecName(), "H263-1998") == 0 || 00640 strcmp(fOurSubsession.codecName(), "H263-2000") == 0) { 00641 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_h263; 00642 fQTTimeScale = 600; 00643 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS; 00644 } else if (strcmp(fOurSubsession.codecName(), "H264") == 0) { 00645 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_avc1; 00646 fQTTimeScale = 600; 00647 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS; 00648 } else if (strcmp(fOurSubsession.codecName(), "MP4V-ES") == 0) { 00649 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_mp4v; 00650 fQTTimeScale = 600; 00651 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS; 00652 } else { 00653 envir() << noCodecWarning1 << "Video" << noCodecWarning2 00654 << fOurSubsession.codecName() << noCodecWarning3; 00655 fQTMediaDataAtomCreator = &QuickTimeFileSink::addAtom_dummy; 00656 fQTEnableTrack = False; // disable this track in the movie 00657 } 00658 } else { 00659 envir() << "Warning: We don't implement a QuickTime Media Handler for media type \"" 00660 << fOurSubsession.mediumName() << "\""; 00661 break; 00662 } 00663 00664 #ifdef QT_SUPPORT_PARTIALLY_ONLY 00665 envir() << "Warning: We don't have sufficient codec-specific information (e.g., sample sizes) to fully generate the \"" 00666 << fOurSubsession.mediumName() << "/" << fOurSubsession.codecName() 00667 << "\" track, so we'll disable this track in the movie. A separate, codec-specific editing pass will be needed before this track can be played\n"; 00668 fQTEnableTrack = False; // disable this track in the movie 00669 #endif 00670 00671 return True; 00672 } while (0); 00673 00674 envir() << ", so a track for the \"" << fOurSubsession.mediumName() 00675 << "/" << fOurSubsession.codecName() 00676 << "\" subsession will not be included in the output QuickTime file\n"; 00677 return False; 00678 }
| void SubsessionIOState::setFinalQTstate | ( | ) |
Definition at line 680 of file QuickTimeFileSink.cpp.
References chunk, ChunkDescriptor::fFrameDuration, fHeadChunk, QuickTimeFileSink::fMaxTrackDurationM, ChunkDescriptor::fNextChunk, ChunkDescriptor::fNumFrames, fOurSink, fQTDurationM, fQTDurationT, fQTTimeScale, QuickTimeFileSink::movieTimeScale(), and NULL.
Referenced by QuickTimeFileSink::completeOutputFile().
00680 { 00681 // Compute derived parameters, by running through the list of chunks: 00682 fQTDurationT = 0; 00683 00684 ChunkDescriptor* chunk = fHeadChunk; 00685 while (chunk != NULL) { 00686 unsigned const numFrames = chunk->fNumFrames; 00687 unsigned const dur = numFrames*chunk->fFrameDuration; 00688 fQTDurationT += dur; 00689 00690 chunk = chunk->fNextChunk; 00691 } 00692 00693 // Convert this duration from track to movie time scale: 00694 double scaleFactor = fOurSink.movieTimeScale()/(double)fQTTimeScale; 00695 fQTDurationM = (unsigned)(fQTDurationT*scaleFactor); 00696 00697 if (fQTDurationM > fOurSink.fMaxTrackDurationM) { 00698 fOurSink.fMaxTrackDurationM = fQTDurationM; 00699 } 00700 }
| void SubsessionIOState::afterGettingFrame | ( | unsigned | packetDataSize, | |
| struct timeval | presentationTime | |||
| ) |
Definition at line 702 of file QuickTimeFileSink.cpp.
References QuickTimeFileSink::addAtom_genericMedia(), SubsessionBuffer::addBytes(), SubsessionBuffer::bytesInUse(), QuickTimeFileSink::continuePlaying(), RTPSource::curPacketRTPSeqNum(), fBuffer, fLastPacketRTPSeqNum, QuickTimeFileSink::fMovieFPS, QuickTimeFileSink::fMovieHeight, QuickTimeFileSink::fMovieWidth, fourChar, fOurSink, fOurSubsession, QuickTimeFileSink::fPacketLossCompensate, fPrevBuffer, fQTBytesPerFrame, fQTMediaDataAtomCreator, fQTSamplesPerFrame, fQTTimeScale, fQTTimeUnitsPerSample, QuickTimeGenericRTPSource::QTState::height, QuickTimeGenericRTPSource::qtState, SubsessionBuffer::reset(), MediaSubsession::rtpSource(), QuickTimeGenericRTPSource::QTState::sdAtom, QuickTimeGenericRTPSource::QTState::sdAtomSize, SubsessionBuffer::setPresentationTime(), QuickTimeGenericRTPSource::QTState::timescale, useFrame(), and QuickTimeGenericRTPSource::QTState::width.
Referenced by QuickTimeFileSink::afterGettingFrame().
00703 { 00704 // Begin by checking whether there was a gap in the RTP stream. 00705 // If so, try to compensate for this (if desired): 00706 unsigned short rtpSeqNum 00707 = fOurSubsession.rtpSource()->curPacketRTPSeqNum(); 00708 if (fOurSink.fPacketLossCompensate && fPrevBuffer->bytesInUse() > 0) { 00709 short seqNumGap = rtpSeqNum - fLastPacketRTPSeqNum; 00710 for (short i = 1; i < seqNumGap; ++i) { 00711 // Insert a copy of the previous frame, to compensate for the loss: 00712 useFrame(*fPrevBuffer); 00713 } 00714 } 00715 fLastPacketRTPSeqNum = rtpSeqNum; 00716 00717 // Now, continue working with the frame that we just got 00718 if (fBuffer->bytesInUse() == 0) { 00719 fBuffer->setPresentationTime(presentationTime); 00720 } 00721 fBuffer->addBytes(packetDataSize); 00722 00723 // If our RTP source is a "QuickTimeGenericRTPSource", then 00724 // use its 'qtState' to set some parameters that we need: 00725 if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_genericMedia){ 00726 QuickTimeGenericRTPSource* rtpSource 00727 = (QuickTimeGenericRTPSource*)fOurSubsession.rtpSource(); 00728 QuickTimeGenericRTPSource::QTState& qtState = rtpSource->qtState; 00729 fQTTimeScale = qtState.timescale; 00730 if (qtState.width != 0) { 00731 fOurSink.fMovieWidth = qtState.width; 00732 } 00733 if (qtState.height != 0) { 00734 fOurSink.fMovieHeight = qtState.height; 00735 } 00736 00737 // Also, if the media type in the "sdAtom" is one that we recognize 00738 // to have a special parameters, then fix this here: 00739 if (qtState.sdAtomSize >= 8) { 00740 char const* atom = qtState.sdAtom; 00741 unsigned mediaType = fourChar(atom[4],atom[5],atom[6],atom[7]); 00742 switch (mediaType) { 00743 case fourChar('a','g','s','m'): { 00744 fQTBytesPerFrame = 33; 00745 fQTSamplesPerFrame = 160; 00746 break; 00747 } 00748 case fourChar('Q','c','l','p'): { 00749 fQTBytesPerFrame = 35; 00750 fQTSamplesPerFrame = 160; 00751 break; 00752 } 00753 case fourChar('H','c','l','p'): { 00754 fQTBytesPerFrame = 17; 00755 fQTSamplesPerFrame = 160; 00756 break; 00757 } 00758 case fourChar('h','2','6','3'): { 00759 fQTTimeUnitsPerSample = fQTTimeScale/fOurSink.fMovieFPS; 00760 break; 00761 } 00762 } 00763 } 00764 } else if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_Qclp) { 00765 // For QCELP data, make a note of the frame size (even though it's the 00766 // same as the packet data size), because it varies depending on the 00767 // 'rate' of the stream, and this size gets used later when setting up 00768 // the 'Qclp' QuickTime atom: 00769 fQTBytesPerFrame = packetDataSize; 00770 } 00771 00772 useFrame(*fBuffer); 00773 if (fOurSink.fPacketLossCompensate) { 00774 // Save this frame, in case we need it for recovery: 00775 SubsessionBuffer* tmp = fPrevBuffer; // assert: != NULL 00776 fPrevBuffer = fBuffer; 00777 fBuffer = tmp; 00778 } 00779 fBuffer->reset(); // for the next input 00780 00781 // Now, try getting more frames: 00782 fOurSink.continuePlaying(); 00783 }
| void SubsessionIOState::onSourceClosure | ( | ) |
Definition at line 1088 of file QuickTimeFileSink.cpp.
References False, fOurSink, fOurSourceIsActive, and QuickTimeFileSink::onSourceClosure1().
Referenced by QuickTimeFileSink::onRTCPBye(), and QuickTimeFileSink::onSourceClosure().
01088 { 01089 fOurSourceIsActive = False; 01090 fOurSink.onSourceClosure1(); 01091 }
| Boolean SubsessionIOState::syncOK | ( | struct timeval | presentationTime | ) |
Definition at line 1093 of file QuickTimeFileSink.cpp.
References SubsessionBuffer::dataStart(), False, fBuffer, fHaveBeenSynced, QuickTimeFileSink::fNumSubsessions, QuickTimeFileSink::fNumSyncedSubsessions, fOurSink, fOurSubsession, fQTMediaDataAtomCreator, QuickTimeFileSink::fSyncStreams, fSyncTime, H264_IDR_FRAME, RTPSource::hasBeenSynchronizedUsingRTCP(), MediaSubsession::rtpSource(), timevalGE(), and True.
Referenced by QuickTimeFileSink::afterGettingFrame().
01093 { 01094 QuickTimeFileSink& s = fOurSink; // abbreviation 01095 if (!s.fSyncStreams) return True; // we don't care 01096 01097 if (s.fNumSyncedSubsessions < s.fNumSubsessions) { 01098 // Not all subsessions have yet been synced. Check whether ours was 01099 // one of the unsynced ones, and, if so, whether it is now synced: 01100 if (!fHaveBeenSynced) { 01101 // We weren't synchronized before 01102 if (fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) { 01103 // H264 ? 01104 if (fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1) { 01105 // special case: audio + H264 video: wait until audio is in sync 01106 if ((s.fNumSubsessions == 2) && (s.fNumSyncedSubsessions < (s.fNumSubsessions - 1))) return False; 01107 01108 // if audio is in sync, wait for the next IDR frame to start 01109 unsigned char* const frameSource = fBuffer->dataStart(); 01110 if (*frameSource != H264_IDR_FRAME) return False; 01111 } 01112 // But now we are 01113 fHaveBeenSynced = True; 01114 fSyncTime = presentationTime; 01115 ++s.fNumSyncedSubsessions; 01116 01117 if (timevalGE(fSyncTime, s.fNewestSyncTime)) { 01118 s.fNewestSyncTime = fSyncTime; 01119 } 01120 } 01121 } 01122 } 01123 01124 // Check again whether all subsessions have been synced: 01125 if (s.fNumSyncedSubsessions < s.fNumSubsessions) return False; 01126 01127 // Allow this data if it is more recent than the newest sync time: 01128 return timevalGE(presentationTime, s.fNewestSyncTime); 01129 }
| void SubsessionIOState::setHintTrack | ( | SubsessionIOState * | hintedTrack, | |
| SubsessionIOState * | hintTrack | |||
| ) | [static] |
Definition at line 1131 of file QuickTimeFileSink.cpp.
References fHintTrackForUs, fTrackHintedByUs, hintedTrack, and NULL.
Referenced by QuickTimeFileSink::QuickTimeFileSink().
01132 { 01133 if (hintedTrack != NULL) hintedTrack->fHintTrackForUs = hintTrack; 01134 if (hintTrack != NULL) hintTrack->fTrackHintedByUs = hintedTrack; 01135 }
| Boolean SubsessionIOState::isHintTrack | ( | ) | const [inline] |
Definition at line 126 of file QuickTimeFileSink.cpp.
References fTrackHintedByUs, and NULL.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and setQTstate().
00126 { return fTrackHintedByUs != NULL; }
| Boolean SubsessionIOState::hasHintTrack | ( | ) | const [inline] |
Definition at line 127 of file QuickTimeFileSink.cpp.
References fHintTrackForUs, and NULL.
Referenced by QuickTimeFileSink::completeOutputFile(), useFrame(), and while().
00127 { return fHintTrackForUs != NULL; }
| UsageEnvironment& SubsessionIOState::envir | ( | ) | const [inline] |
Definition at line 129 of file QuickTimeFileSink.cpp.
References Medium::envir(), and fOurSink.
Referenced by QuickTimeFileSink::afterGettingFrame(), QuickTimeFileSink::onRTCPBye(), setQTstate(), and useFrameForHinting().
| void SubsessionIOState::useFrame | ( | SubsessionBuffer & | buffer | ) | [private] |
Definition at line 785 of file QuickTimeFileSink.cpp.
References QuickTimeFileSink::addWord(), SubsessionBuffer::bytesInUse(), SubsessionBuffer::dataStart(), destFileOffset, duration, fHaveBeenSynced, fHeadSyncFrame, fHintTrackForUs, fourChar, fOurSink, fOurSubsession, QuickTimeFileSink::fOutFid, fPrevFrameState, fQTcomponentSubtype, fQTMediaDataAtomCreator, fQTSamplesPerFrame, fQTTimeScale, fQTTimeUnitsPerSample, fQTTotNumSamples, frameSize, QuickTimeFileSink::fSyncStreams, fTailSyncFrame, H264_IDR_FRAME, RTPSource::hasBeenSynchronizedUsingRTCP(), hasHintTrack(), SyncFrame::nextSyncFrame, NULL, SubsessionBuffer::presentationTime(), presentationTime, MediaSubsession::rtpSource(), TellFile64(), useFrame1(), and useFrameForHinting().
Referenced by afterGettingFrame().
00785 { 00786 unsigned char* const frameSource = buffer.dataStart(); 00787 unsigned const frameSize = buffer.bytesInUse(); 00788 struct timeval const& presentationTime = buffer.presentationTime(); 00789 int64_t const destFileOffset = TellFile64(fOurSink.fOutFid); 00790 unsigned sampleNumberOfFrameStart = fQTTotNumSamples + 1; 00791 Boolean avcHack = fQTMediaDataAtomCreator == &QuickTimeFileSink::addAtom_avc1; 00792 00793 // If we're not syncing streams, or this subsession is not video, then 00794 // just give this frame a fixed duration: 00795 if (!fOurSink.fSyncStreams 00796 || fQTcomponentSubtype != fourChar('v','i','d','e')) { 00797 unsigned const frameDuration = fQTTimeUnitsPerSample*fQTSamplesPerFrame; 00798 unsigned frameSizeToUse = frameSize; 00799 if (avcHack) frameSizeToUse += 4; // H.264/AVC gets the frame size prefix 00800 00801 fQTTotNumSamples += useFrame1(frameSizeToUse, presentationTime, frameDuration, destFileOffset); 00802 } else { 00803 // For synced video streams, we use the difference between successive 00804 // frames' presentation times as the 'frame duration'. So, record 00805 // information about the *previous* frame: 00806 struct timeval const& ppt = fPrevFrameState.presentationTime; //abbrev 00807 if (ppt.tv_sec != 0 || ppt.tv_usec != 0) { 00808 // There has been a previous frame. 00809 double duration = (presentationTime.tv_sec - ppt.tv_sec) 00810 + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0; 00811 if (duration < 0.0) duration = 0.0; 00812 unsigned frameDuration 00813 = (unsigned)((2*duration*fQTTimeScale+1)/2); // round 00814 unsigned frameSizeToUse = fPrevFrameState.frameSize; 00815 if (avcHack) frameSizeToUse += 4; // H.264/AVC gets the frame size prefix 00816 00817 unsigned numSamples 00818 = useFrame1(frameSizeToUse, ppt, frameDuration, fPrevFrameState.destFileOffset); 00819 fQTTotNumSamples += numSamples; 00820 sampleNumberOfFrameStart = fQTTotNumSamples + 1; 00821 } 00822 00823 if (avcHack && (*frameSource == H264_IDR_FRAME)) { 00824 SyncFrame* newSyncFrame = new SyncFrame(fQTTotNumSamples + 1); 00825 if (fTailSyncFrame == NULL) { 00826 fHeadSyncFrame = newSyncFrame; 00827 } else { 00828 fTailSyncFrame->nextSyncFrame = newSyncFrame; 00829 } 00830 fTailSyncFrame = newSyncFrame; 00831 } 00832 00833 // Remember the current frame for next time: 00834 fPrevFrameState.frameSize = frameSize; 00835 fPrevFrameState.presentationTime = presentationTime; 00836 fPrevFrameState.destFileOffset = destFileOffset; 00837 } 00838 00839 if (avcHack) fOurSink.addWord(frameSize); 00840 00841 // Write the data into the file: 00842 fwrite(frameSource, 1, frameSize, fOurSink.fOutFid); 00843 00844 // If we have a hint track, then write to it also: 00845 if (hasHintTrack()) { 00846 // Because presentation times are used for RTP packet timestamps, 00847 // we don't starting writing to the hint track until we've been synced: 00848 if (!fHaveBeenSynced) { 00849 fHaveBeenSynced 00850 = fOurSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP(); 00851 } 00852 if (fHaveBeenSynced) { 00853 fHintTrackForUs->useFrameForHinting(frameSize, presentationTime, 00854 sampleNumberOfFrameStart); 00855 } 00856 } 00857 }
| void SubsessionIOState::useFrameForHinting | ( | unsigned | frameSize, | |
| struct timeval | presentationTime, | |||
| unsigned | startSampleNumber | |||
| ) | [private] |
Definition at line 859 of file QuickTimeFileSink.cpp.
References QuickTimeFileSink::addByte(), QuickTimeFileSink::addHalfWord(), QuickTimeFileSink::addWord(), MediaSubsession::codecName(), RTPSource::curPacketMarkerBit(), SubsessionIOState::hinf::dimm, SubsessionIOState::hinf::dmax, SubsessionIOState::hinf::dmed, duration, envir(), fHINF, MediaSubsession::fmtp_indexlength(), MediaSubsession::fmtp_sizelength(), H263plusVideoRTPSource::fNumSpecialHeaders, fOurSink, fOurSubsession, QuickTimeFileSink::fOutFid, H263plusVideoRTPSource::fPacketSizes, fPrevFrameState, fQTBytesPerFrame, fQTSamplesPerFrame, fQTTimeScale, fQTTimeUnitsPerSample, fQTTotNumSamples, H263plusVideoRTPSource::fSpecialHeaderBytes, H263plusVideoRTPSource::fSpecialHeaderBytesLength, fTrackHintedByUs, if(), maxPacketSize, MediaSubsession::mediumName(), NULL, SubsessionIOState::hinf::nump, SubsessionIOState::hinf::pmax, rtpHeader, RTPSource::rtpPayloadFormat(), MediaSubsession::rtpSource(), MediaSubsession::rtpTimestampFrequency(), seqNum, TellFile64(), SubsessionIOState::hinf::tpyl, SubsessionIOState::hinf::trpy, and useFrame1().
Referenced by useFrame().
00861 { 00862 // At this point, we have a single, combined frame - not individual packets. 00863 // For the hint track, we need to split the frame back up into separate packets. 00864 // However, for some RTP sources, then we also need to reuse the special 00865 // header bytes that were at the start of each of the RTP packets. 00866 Boolean hack263 = strcmp(fOurSubsession.codecName(), "H263-1998") == 0; 00867 Boolean hackm4a_generic = strcmp(fOurSubsession.mediumName(), "audio") == 0 00868 && strcmp(fOurSubsession.codecName(), "MPEG4-GENERIC") == 0; 00869 Boolean hackm4a_latm = strcmp(fOurSubsession.mediumName(), "audio") == 0 00870 && strcmp(fOurSubsession.codecName(), "MP4A-LATM") == 0; 00871 Boolean hackm4a = hackm4a_generic || hackm4a_latm; 00872 Boolean haveSpecialHeaders = (hack263 || hackm4a_generic); 00873 00874 // If there has been a previous frame, then output a 'hint sample' for it. 00875 // (We use the current frame's presentation time to compute the previous 00876 // hint sample's duration.) 00877 RTPSource* const rs = fOurSubsession.rtpSource(); // abbrev 00878 struct timeval const& ppt = fPrevFrameState.presentationTime; //abbrev 00879 if (ppt.tv_sec != 0 || ppt.tv_usec != 0) { 00880 double duration = (presentationTime.tv_sec - ppt.tv_sec) 00881 + (presentationTime.tv_usec - ppt.tv_usec)/1000000.0; 00882 if (duration < 0.0) duration = 0.0; 00883 unsigned msDuration = (unsigned)(duration*1000); // milliseconds 00884 if (msDuration > fHINF.dmax) fHINF.dmax = msDuration; 00885 unsigned hintSampleDuration 00886 = (unsigned)((2*duration*fQTTimeScale+1)/2); // round 00887 if (hackm4a) { 00888 // Because multiple AAC frames can appear in a RTP packet, the presentation 00889 // times of the second and subsequent frames will not be accurate. 00890 // So, use the known "hintSampleDuration" instead: 00891 hintSampleDuration = fTrackHintedByUs->fQTTimeUnitsPerSample; 00892 00893 // Also, if the 'time scale' was different from the RTP timestamp frequency, 00894 // (as can happen with aacPlus), then we need to scale "hintSampleDuration" 00895 // accordingly: 00896 if (fTrackHintedByUs->fQTTimeScale != fOurSubsession.rtpTimestampFrequency()) { 00897 unsigned const scalingFactor 00898 = fOurSubsession.rtpTimestampFrequency()/fTrackHintedByUs->fQTTimeScale ; 00899 hintSampleDuration *= scalingFactor; 00900 } 00901 } 00902 00903 int64_t const hintSampleDestFileOffset = TellFile64(fOurSink.fOutFid); 00904 00905 unsigned const maxPacketSize = 1450; 00906 unsigned short numPTEntries 00907 = (fPrevFrameState.frameSize + (maxPacketSize-1))/maxPacketSize; // normal case 00908 unsigned char* immediateDataPtr = NULL; 00909 unsigned immediateDataBytesRemaining = 0; 00910 if (haveSpecialHeaders) { // special case 00911 numPTEntries = fPrevFrameState.numSpecialHeaders; 00912 immediateDataPtr = fPrevFrameState.specialHeaderBytes; 00913 immediateDataBytesRemaining 00914 = fPrevFrameState.specialHeaderBytesLength; 00915 } 00916 unsigned hintSampleSize 00917 = fOurSink.addHalfWord(numPTEntries);// Entry count 00918 hintSampleSize += fOurSink.addHalfWord(0x0000); // Reserved 00919 00920 unsigned offsetWithinSample = 0; 00921 for (unsigned i = 0; i < numPTEntries; ++i) { 00922 // Output a Packet Table entry (representing a single RTP packet): 00923 unsigned short numDTEntries = 1; 00924 unsigned short seqNum = fPrevFrameState.seqNum++; 00925 // Note: This assumes that the input stream had no packets lost ##### 00926 unsigned rtpHeader = fPrevFrameState.rtpHeader; 00927 if (i+1 < numPTEntries) { 00928 // This is not the last RTP packet, so clear the marker bit: 00929 rtpHeader &=~ (1<<23); 00930 } 00931 unsigned dataFrameSize = (i+1 < numPTEntries) 00932 ? maxPacketSize : fPrevFrameState.frameSize - i*maxPacketSize; // normal case 00933 unsigned sampleNumber = fPrevFrameState.startSampleNumber; 00934 00935 unsigned char immediateDataLen = 0; 00936 if (haveSpecialHeaders) { // special case 00937 ++numDTEntries; // to include a Data Table entry for the special hdr 00938 if (immediateDataBytesRemaining > 0) { 00939 if (hack263) { 00940 immediateDataLen = *immediateDataPtr++; 00941 --immediateDataBytesRemaining; 00942 if (immediateDataLen > immediateDataBytesRemaining) { 00943 // shouldn't happen (length byte was bad) 00944 immediateDataLen = immediateDataBytesRemaining; 00945 } 00946 } else { 00947 immediateDataLen = fPrevFrameState.specialHeaderBytesLength; 00948 } 00949 } 00950 dataFrameSize = fPrevFrameState.packetSizes[i] - immediateDataLen; 00951 00952 if (hack263) { 00953 Boolean PbitSet 00954 = immediateDataLen >= 1 && (immediateDataPtr[0]&0x4) != 0; 00955 if (PbitSet) { 00956 offsetWithinSample += 2; // to omit the two leading 0 bytes 00957 } 00958 } 00959 } 00960 00961 // Output the Packet Table: 00962 hintSampleSize += fOurSink.addWord(0); // Relative transmission time 00963 hintSampleSize += fOurSink.addWord(rtpHeader|seqNum); 00964 // RTP header info + RTP sequence number 00965 hintSampleSize += fOurSink.addHalfWord(0x0000); // Flags 00966 hintSampleSize += fOurSink.addHalfWord(numDTEntries); // Entry count 00967 unsigned totalPacketSize = 0; 00968 00969 // Output the Data Table: 00970 if (haveSpecialHeaders) { 00971 // use the "Immediate Data" format (1): 00972 hintSampleSize += fOurSink.addByte(1); // Source 00973 unsigned char len = immediateDataLen > 14 ? 14 : immediateDataLen; 00974 hintSampleSize += fOurSink.addByte(len); // Length 00975 totalPacketSize += len; fHINF.dimm += len; 00976 unsigned char j; 00977 for (j = 0; j < len; ++j) { 00978 hintSampleSize += fOurSink.addByte(immediateDataPtr[j]); // Data 00979 } 00980 for (j = len; j < 14; ++j) { 00981 hintSampleSize += fOurSink.addByte(0); // Data (padding) 00982 } 00983 00984 immediateDataPtr += immediateDataLen; 00985 immediateDataBytesRemaining -= immediateDataLen; 00986 } 00987 // use the "Sample Data" format (2): 00988 hintSampleSize += fOurSink.addByte(2); // Source 00989 hintSampleSize += fOurSink.addByte(0); // Track ref index 00990 hintSampleSize += fOurSink.addHalfWord(dataFrameSize); // Length 00991 totalPacketSize += dataFrameSize; fHINF.dmed += dataFrameSize; 00992 hintSampleSize += fOurSink.addWord(sampleNumber); // Sample number 00993 hintSampleSize += fOurSink.addWord(offsetWithinSample); // Offset 00994 // Get "bytes|samples per compression block" from the hinted track: 00995 unsigned short const bytesPerCompressionBlock 00996 = fTrackHintedByUs->fQTBytesPerFrame; 00997 unsigned short const samplesPerCompressionBlock 00998 = fTrackHintedByUs->fQTSamplesPerFrame; 00999 hintSampleSize += fOurSink.addHalfWord(bytesPerCompressionBlock); 01000 hintSampleSize += fOurSink.addHalfWord(samplesPerCompressionBlock); 01001 01002 offsetWithinSample += dataFrameSize;// for the next iteration (if any) 01003 01004 // Tally statistics for this packet: 01005 fHINF.nump += 1; 01006 fHINF.tpyl += totalPacketSize; 01007 totalPacketSize += 12; // add in the size of the RTP header 01008 fHINF.trpy += totalPacketSize; 01009 if (totalPacketSize > fHINF.pmax) fHINF.pmax = totalPacketSize; 01010 } 01011 01012 // Make note of this completed hint sample frame: 01013 fQTTotNumSamples += useFrame1(hintSampleSize, ppt, hintSampleDuration, 01014 hintSampleDestFileOffset); 01015 } 01016 01017 // Remember this frame for next time: 01018 fPrevFrameState.frameSize = frameSize; 01019 fPrevFrameState.presentationTime = presentationTime; 01020 fPrevFrameState.startSampleNumber = startSampleNumber; 01021 fPrevFrameState.rtpHeader 01022 = rs->curPacketMarkerBit()<<23 01023 | (rs->rtpPayloadFormat()&0x7F)<<16; 01024 if (hack263) { 01025 H263plusVideoRTPSource* rs_263 = (H263plusVideoRTPSource*)rs; 01026 fPrevFrameState.numSpecialHeaders = rs_263->fNumSpecialHeaders; 01027 fPrevFrameState.specialHeaderBytesLength = rs_263->fSpecialHeaderBytesLength; 01028 unsigned i; 01029 for (i = 0; i < rs_263->fSpecialHeaderBytesLength; ++i) { 01030 fPrevFrameState.specialHeaderBytes[i] = rs_263->fSpecialHeaderBytes[i]; 01031 } 01032 for (i = 0; i < rs_263->fNumSpecialHeaders; ++i) { 01033 fPrevFrameState.packetSizes[i] = rs_263->fPacketSizes[i]; 01034 } 01035 } else if (hackm4a_generic) { 01036 // Synthesize a special header, so that this frame can be in its own RTP packet. 01037 unsigned const sizeLength = fOurSubsession.fmtp_sizelength(); 01038 unsigned const indexLength = fOurSubsession.fmtp_indexlength(); 01039 if (sizeLength + indexLength != 16) { 01040 envir() << "Warning: unexpected 'sizeLength' " << sizeLength 01041 << " and 'indexLength' " << indexLength 01042 << "seen when creating hint track\n"; 01043 } 01044 fPrevFrameState.numSpecialHeaders = 1; 01045 fPrevFrameState.specialHeaderBytesLength = 4; 01046 fPrevFrameState.specialHeaderBytes[0] = 0; // AU_headers_length (high byte) 01047 fPrevFrameState.specialHeaderBytes[1] = 16; // AU_headers_length (low byte) 01048 fPrevFrameState.specialHeaderBytes[2] = ((frameSize<<indexLength)&0xFF00)>>8; 01049 fPrevFrameState.specialHeaderBytes[3] = (frameSize<<indexLength); 01050 fPrevFrameState.packetSizes[0] 01051 = fPrevFrameState.specialHeaderBytesLength + frameSize; 01052 } 01053 }
| unsigned SubsessionIOState::useFrame1 | ( | unsigned | sourceDataSize, | |
| struct timeval | presentationTime, | |||
| unsigned | frameDuration, | |||
| int64_t | destFileOffset | |||
| ) | [private] |
Definition at line 1055 of file QuickTimeFileSink.cpp.
References ChunkDescriptor::extendChunk(), fHeadChunk, fNumChunks, fQTBytesPerFrame, fQTSamplesPerFrame, frameSize, fTailChunk, and NULL.
Referenced by useFrame(), and useFrameForHinting().
01058 { 01059 // Figure out the actual frame size for this data: 01060 unsigned frameSize = fQTBytesPerFrame; 01061 if (frameSize == 0) { 01062 // The entire packet data is assumed to be a frame: 01063 frameSize = sourceDataSize; 01064 } 01065 unsigned const numFrames = sourceDataSize/frameSize; 01066 unsigned const numSamples = numFrames*fQTSamplesPerFrame; 01067 01068 // Record the information about which 'chunk' this data belongs to: 01069 ChunkDescriptor* newTailChunk; 01070 if (fTailChunk == NULL) { 01071 newTailChunk = fHeadChunk 01072 = new ChunkDescriptor(destFileOffset, sourceDataSize, 01073 frameSize, frameDuration, presentationTime); 01074 } else { 01075 newTailChunk = fTailChunk->extendChunk(destFileOffset, sourceDataSize, 01076 frameSize, frameDuration, 01077 presentationTime); 01078 } 01079 if (newTailChunk != fTailChunk) { 01080 // This data created a new chunk, rather than extending the old one 01081 ++fNumChunks; 01082 fTailChunk = newTailChunk; 01083 } 01084 01085 return numSamples; 01086 }
unsigned SubsessionIOState::fCurrentTrackNumber = 0 [static] |
| unsigned SubsessionIOState::fTrackID |
Definition at line 133 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and SubsessionIOState().
Definition at line 134 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::completeOutputFile(), hasHintTrack(), setHintTrack(), useFrame(), while(), and QuickTimeFileSink::~QuickTimeFileSink().
Definition at line 134 of file QuickTimeFileSink.cpp.
Referenced by isHintTrack(), setHintTrack(), and useFrameForHinting().
Definition at line 136 of file QuickTimeFileSink.cpp.
Referenced by afterGettingFrame(), QuickTimeFileSink::continuePlaying(), SubsessionIOState(), syncOK(), and ~SubsessionIOState().
Definition at line 136 of file QuickTimeFileSink.cpp.
Referenced by afterGettingFrame(), SubsessionIOState(), and ~SubsessionIOState().
Definition at line 137 of file QuickTimeFileSink.cpp.
Referenced by afterGettingFrame(), QuickTimeFileSink::afterGettingFrame(), envir(), QuickTimeFileSink::onRTCPBye(), onSourceClosure(), setFinalQTstate(), setQTstate(), SubsessionIOState(), syncOK(), useFrame(), and useFrameForHinting().
Definition at line 138 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), QuickTimeFileSink::onRTCPBye(), setQTstate(), syncOK(), useFrame(), useFrameForHinting(), and while().
| unsigned short SubsessionIOState::fLastPacketRTPSeqNum |
Definition at line 141 of file QuickTimeFileSink.cpp.
Referenced by onSourceClosure(), QuickTimeFileSink::onSourceClosure1(), and SubsessionIOState().
struct timeval SubsessionIOState::fSyncTime [read] |
Definition at line 147 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), setQTstate(), and useFrame().
| char const* SubsessionIOState::fQTcomponentName |
Definition at line 151 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), setQTstate(), syncOK(), and useFrame().
| char const* SubsessionIOState::fQTAudioDataType |
Definition at line 152 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and setQTstate().
| unsigned short SubsessionIOState::fQTSoundSampleVersion |
Definition at line 153 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and setQTstate().
| unsigned SubsessionIOState::fQTTimeScale |
Definition at line 154 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), if(), setFinalQTstate(), setQTstate(), useFrame(), useFrameForHinting(), and while().
Definition at line 155 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), setQTstate(), useFrame(), and useFrameForHinting().
| unsigned SubsessionIOState::fQTBytesPerFrame |
Definition at line 156 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), setQTstate(), useFrame1(), and useFrameForHinting().
Definition at line 157 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), afterGettingFrame(), setQTstate(), useFrame(), useFrame1(), and useFrameForHinting().
| unsigned SubsessionIOState::fQTTotNumSamples |
Definition at line 160 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), useFrame(), and useFrameForHinting().
| unsigned SubsessionIOState::fQTDurationM |
| unsigned SubsessionIOState::fQTDurationT |
Definition at line 165 of file QuickTimeFileSink.cpp.
Definition at line 168 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), QuickTimeFileSink::completeOutputFile(), setFinalQTstate(), useFrame1(), and ~SubsessionIOState().
| unsigned SubsessionIOState::fNumChunks |
Definition at line 169 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and useFrame1().
Definition at line 170 of file QuickTimeFileSink.cpp.
Referenced by QuickTimeFileSink::addAtom_hdlr2(), useFrame(), and ~SubsessionIOState().
Referenced by QuickTimeFileSink::addAtom_hdlr2(), and useFrameForHinting().
| unsigned SubsessionIOState::frameSize |
struct timeval SubsessionIOState::presentationTime [read] |
| unsigned SubsessionIOState::startSampleNumber |
Definition at line 206 of file QuickTimeFileSink.cpp.
| unsigned short SubsessionIOState::seqNum |
| unsigned SubsessionIOState::rtpHeader |
| unsigned char SubsessionIOState::numSpecialHeaders |
Definition at line 209 of file QuickTimeFileSink.cpp.
Definition at line 210 of file QuickTimeFileSink.cpp.
| unsigned char SubsessionIOState::specialHeaderBytes[SPECIAL_HEADER_BUFFER_SIZE] |
Definition at line 211 of file QuickTimeFileSink.cpp.
| unsigned SubsessionIOState::packetSizes[256] |
Definition at line 212 of file QuickTimeFileSink.cpp.
struct { ... } SubsessionIOState::fPrevFrameState [private] |
Referenced by SubsessionIOState(), useFrame(), and useFrameForHinting().
1.5.2