版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
十二 h264 rtp包的時間戳
這次我們一起來分析一下live555中是怎樣為rtp包打時間戳的.就以h264為例吧.
[cpp] view plain copyvoid H264VideoRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/, unsigned char* /*frameStart*/, unsigned /*numBytesInFrame*/, struct timeval frameaccess unit' (i.e. video frame). if (fOurFragmenter != NULL) { H264VideoStreamFramer* framerSource = (H264VideoStreamFramer*) (fOurFragmenter->inputSource()); // This relies on our fragmenter's source being a "H264VideoStreamFramer". if (fOurFragmenter->lastFragmentCompletedNALUnit() && framerSource != NULL && framerSource->pictureEndMarker()) { setMarkerBit(); framerSource->pictureEndMarker() = False; } } setTimestamp(framePresentationTime); } 函數(shù)中先檢測是否是一個幀的最后一個包,如果是,打上'M'標記.然后就設(shè)置時間戳.這個間戳是哪來的呢?需看函數(shù)doSpecialFrameHandling()是被誰調(diào)用的,經(jīng)查找,是被MultiFramedRTPSink::afterGettingFrame1()調(diào)用的.MultiFramedRTPSink::afterGettingFrame1()的參數(shù)presentationTime傳給了doSpecialFrameHandling().MultiFramedRTPSink::afterGettingFrame1()是在調(diào)用source的getNextFrame()時傳給了source.傳給哪個source呢?傳給了H264FUAFragmenter,還記得暗渡陳倉那件事嗎?所以H264FUAFragmenter在獲取一個nal unit后調(diào)用了MultiFramedRTPSink::afterGettingFrame1().也就是H264FUAFragmenter::afterGettingFrame1()調(diào)用了MultiFramedRTPSink::afterGettingFrame1().H264FUAFragmenter::afterGettingFrame1()是被它自己的source的afterGettingFrame1()調(diào)用的.H264FUAFragmenter的source是誰呢?是H264VideoStreamFramer,是在暗渡陳倉時傳給H264FUAFragmenter的構(gòu)造函數(shù)的.H264VideoStreamFramer的afterGettingFrame1()是沒有的,代替之的是MPEGVideoStreamFramer::continueReadProcessin().它被MPEGVideoStreamParser暗中傳給了StreamParser的構(gòu)造函數(shù).所以StreamParser在分析完一幀(或nal unit)之后,調(diào)用的就是MPEGVideoStreamFramer::continueReadProcessin().以下即是證明:(補充:以下函數(shù)并不是在parser分析完一幀(或nal unit)之后調(diào)用,而是parser利用ByteStreamFileSuorce獲取到原始數(shù)據(jù)后調(diào)用,然后MPEGVideoStreamFramer再調(diào)用Parser的parser()函數(shù)分析原始數(shù)據(jù))[cpp] view plain copyvoid StreamParser::afterGettingBytes(void* clientData, unsigned numBytesRead, unsigned /*numTruncatedBytes*/, struct timeval presentationTime, unsigned /*durationInMicroseconds*/) { StreamParser* parser = (StreamParser*) clientData; if (parser != NULL) parser->afterGettingBytes1(numBytesRead, presentationTime); } void StreamParser::afterGettingBytes1(unsigned numBytesRead, struct timeval presentationTime) { // Sanity check: Make sure we didn't get too many bytes for our bank: if (fTotNumValidBytes + numBytesRead > BANK_SIZE) { fInputSource->envir() << "StreamParser::afterGettingBytes() warning: read " << numBytesRead << " bytes; expected no more than " << BANK_SIZE - fTotNumValidBytes << "/n"; } fLastSeenPresentationTime = presentationTime; unsigned char* ptr = &curBank()[fTotNumValidBytes]; fTotNumValidBytes += numBytesRead; // Continue our original calling source where it left off: restoreSavedParserState(); // Sigh... this is a crock; things would have been a lot simpler // here if we were using threads, with synchronous I/O... fClientContinueFunc(fClientContinueClientData, ptr, numBytesRead, presentationTime); } fClientContinueFunc就是MPEGVideoStreamFramer::continueReadProcessin(),而且我們看到時間戳被傳入fClientContinueFunc.然而,MPEGVideoStreamFramer::continueReadProcessin()中跟本就不理這個時間戳,因為這個時間戳是ByteStreamFileSource計算出來的,它跟本就不可能正確.[cpp] view plain copyvoid MPEGVideoStreamFramer::continueReadProcessing(void* clientData, unsigned char* /*ptr*/, unsigned /*size*/, struct timeval /*presentationTime*/) { MPEGVideoStreamFramer* framer = (MPEGVideoStreamFramer*) clientData; framer->continueReadProcessing(); } 看來真正的時間戳是在MPEGVideoStreamFramer中計算的,但是H264VideoStreamFramer并沒有用到MPEGVideoStreamFramer中那些計算時間戳的函數(shù),而是另外計算,其實H264VideoStreamFramer也沒有自己去計算,而是利用H264VideoStreamParser計算的.是在哪個函數(shù)中呢?在parser()中![cpp] view plain copyunsigned H264VideoStreamParser::parse() { try { // The stream must start with a 0x00000001: if (!fHaveSeenFirstStartCode) { // Sk每當開始一個新幀時,計算新的時間戳.時間戳保存在fNextPresentationTime中,在usingSource()->setPresentationTime()中傳給fPresentationTime.哇,我們看到live555的類之間調(diào)用關(guān)系曲折復雜,的確有點不易維護啊!同時我寫的也不夠清析,自己看著都暈,如果把你搞暈了,這很正常哦!
fPresentationTime是64位的時間,經(jīng)convertToRTPTimestamp轉(zhuǎn)換為32的rtp時間戳,見函數(shù):
[cpp] view plain copyu_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) { // Begin by converting from "struct timeval" units to RTP timestamp units: u_int32_t timestampIncrement = (fTimestampFrequency * tv.tv_sec); timestampIncrement += (u_int32_t)( (2.0 * fTimestampFrequency * tv.tv_usec + 1000000.0) / 2000000); // note: rounding // Then add this to our 'timestamp base': if (fNextTimestampHasBeenPreset) { // Make the returned timestamp the same as the current "fTimestampBase", // so that timestamps begin with the value that was previously preset: fTimestampBase -= timestampIncrement; fNextTimestampHasBeenPreset = False; } u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement; return rtpTimestamp; } 其實時間戳的轉(zhuǎn)換主要就是把以秒為單位的時間,提升成按頻率為單位的時間.也就是提升后,時間間隔不是以秒為單位,而是以1/fTimestampFrequency為單位,也就是1/9000秒。然后再強轉(zhuǎn)為32。新聞熱點
疑難解答