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