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