live555学习笔记12-h264 rtp包的时间戳

十二 h264 rtp包的时间戳

这次我们一起来分析一下live555中是怎样为rtp包打时间戳的.就以h264为例吧.

[cpp]  view plain  copy
  1. void H264VideoRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/,  
  2.         unsigned char/*frameStart*/,  
  3.         unsigned /*numBytesInFrame*/,  
  4.         struct timeval framePresentationTime,  
  5.         unsigned /*numRemainingBytes*/)  
  6. {  
  7.     // Set the RTP 'M' (marker) bit iff  
  8.     // 1/ The most recently delivered fragment was the end of (or the only fragment of) an NAL unit, and  
  9.     // 2/ This NAL unit was the last NAL unit of an 'access unit' (i.e. video frame).  
  10.     if (fOurFragmenter != NULL) {  
  11.         H264VideoStreamFramer* framerSource = (H264VideoStreamFramer*) (fOurFragmenter->inputSource());  
  12.         // This relies on our fragmenter's source being a "H264VideoStreamFramer".  
  13.         if (fOurFragmenter->lastFragmentCompletedNALUnit()  
  14.                 && framerSource != NULL && framerSource->pictureEndMarker()) {  
  15.             setMarkerBit();  
  16.             framerSource->pictureEndMarker() = False;  
  17.         }  
  18.     }  
  19.   
  20.     setTimestamp(framePresentationTime);  
  21. }  
函数中先检测是否是一个帧的最后一个包,如果是,打上'M'标记.然后就设置时间戳.这个间戳是哪来的呢?需看函数doSpecialFrameHandling()是被谁调用的,经查找,是被MultiFramedRTPSink::afterGettingFrame1()调用的.MultiFramedRTPSink::afterGettingFrame1()的参数presentationTime传给了doSpecialFrameHandling().MultiFramedRTPSink::afterGettingFrame1()是在调用source的getNextFrame()时传给了source.传给哪个source呢?传给了H264FUAFragmenter,还记得暗渡陈仓那件事吗?所以H264FUAFragmenter在获取一个nal unit后调用了MultiFramedRTPSink::afterGettingFrame1().也就是H264FUAFragmenter::afterGettingFrame1()调用了MultiFramedRTPSink::afterGettingFrame1().
H264FUAFragmenter::afterGettingFrame1()是被它自己的source的afterGettingFrame1()调用的.H264FUAFragmenter的source是谁呢?是H264VideoStreamFramer,是在暗渡陈仓时传给H264FUAFragmenter的构造函数的.
H264VideoStreamFramer的afterGettingFrame1()是没有的,代替之的是MPEGVideoStreamFramer::continueReadProcessin().它被MPEGVideoStreamParser暗中传给了StreamParser的构造函数.所以StreamParser在分析完一帧(或nal unit)之后,调用的就是MPEGVideoStreamFramer::continueReadProcessin().以下即是证明: (补充:以下函数并不是在parser分析完一帧(或nal unit)之后调用,而是parser利用ByteStreamFileSuorce获取到原始数据后调用,然后MPEGVideoStreamFramer再调用Parser的parser()函数分析原始数据)

[cpp]  view plain  copy
  1. void StreamParser::afterGettingBytes(void* clientData,  
  2.         unsigned numBytesRead,  
  3.         unsigned /*numTruncatedBytes*/,  
  4.         struct timeval presentationTime,  
  5.         unsigned /*durationInMicroseconds*/)  
  6. {  
  7.     StreamParser* parser = (StreamParser*) clientData;  
  8.     if (parser != NULL)  
  9.         parser->afterGettingBytes1(numBytesRead, presentationTime);  
  10. }  
  11.   
  12. void StreamParser::afterGettingBytes1(unsigned numBytesRead,  
  13.         struct timeval presentationTime)  
  14. {  
  15.     // Sanity check: Make sure we didn't get too many bytes for our bank:  
  16.     if (fTotNumValidBytes + numBytesRead > BANK_SIZE) {  
  17.         fInputSource->envir()  
  18.                 << "StreamParser::afterGettingBytes() warning: read "  
  19.                 << numBytesRead << " bytes; expected no more than "  
  20.                 << BANK_SIZE - fTotNumValidBytes << "\n";  
  21.     }  
  22.   
  23.     fLastSeenPresentationTime = presentationTime;  
  24.   
  25.     unsigned char* ptr = &curBank()[fTotNumValidBytes];  
  26.     fTotNumValidBytes += numBytesRead;  
  27.   
  28.     // Continue our original calling source where it left off:  
  29.     restoreSavedParserState();  
  30.     // Sigh... this is a crock; things would have been a lot simpler  
  31.     // here if we were using threads, with synchronous I/O...  
  32.     fClientContinueFunc(fClientContinueClientData, ptr, numBytesRead,  
  33.             presentationTime);  
  34. }  
fClientContinueFunc就是MPEGVideoStreamFramer::continueReadProcessin(),而且我们看到时间戳被传入fClientContinueFunc.
然而,MPEGVideoStreamFramer::continueReadProcessin()中跟本就不理这个时间戳,因为这个时间戳是ByteStreamFileSource计算出来的,它跟本就不可能正确.

[cpp]  view plain  copy
  1. void MPEGVideoStreamFramer::continueReadProcessing(void* clientData,  
  2.         unsigned char/*ptr*/,  
  3.         unsigned /*size*/,  
  4.         struct timeval /*presentationTime*/)  
  5. {  
  6.     MPEGVideoStreamFramer* framer = (MPEGVideoStreamFramer*) clientData;  
  7.     framer->continueReadProcessing();  
  8. }  
看来真正的时间戳是在MPEGVideoStreamFramer中计算的,但是H264VideoStreamFramer并没有用到MPEGVideoStreamFramer中那些计算时间戳的函数,而是另外计算,其实H264VideoStreamFramer也没有自己去计算,而是利用H264VideoStreamParser计算的.是在哪个函数中呢?在parser()中!

[cpp]  view plain  copy
  1. unsigned H264VideoStreamParser::parse()  
  2. {  
  3.     try {  
  4.         // The stream must start with a 0x00000001:  
  5.         if (!fHaveSeenFirstStartCode) {  
  6.             // Skip over any input bytes that precede the first 0x00000001:  
  7.             u_int32_t first4Bytes;  
  8.             while ((first4Bytes = test4Bytes()) != 0x00000001) {  
  9.                 get1Byte();  
  10.                 setParseState(); // ensures that we progress over bad data  
  11.             }  
  12.             skipBytes(4); // skip this initial code  
  13.   
  14.             setParseState();  
  15.             fHaveSeenFirstStartCode = True; // from now on  
  16.         }  
  17.   
  18.         if (fOutputStartCodeSize > 0) {  
  19.             // Include a start code in the output:  
  20.             save4Bytes(0x00000001);  
  21.         }  
  22.   
  23.         // Then save everything up until the next 0x00000001 (4 bytes) or 0x000001 (3 bytes), or we hit EOF.  
  24.         // Also make note of the first byte, because it contains the "nal_unit_type":  
  25.         u_int8_t firstByte;  
  26.         if (haveSeenEOF()) {  
  27.             // We hit EOF the last time that we tried to parse this data,  
  28.             // so we know that the remaining unparsed data forms a complete NAL unit:  
  29.             unsigned remainingDataSize = totNumValidBytes() - curOffset();  
  30.             if (remainingDataSize == 0)  
  31.                 (void) get1Byte(); // forces another read, which will cause EOF to get handled for real this time  
  32.             if (remainingDataSize == 0)  
  33.                 return 0;  
  34.             firstByte = get1Byte();  
  35.             saveByte(firstByte);  
  36.   
  37.             while (--remainingDataSize > 0) {  
  38.                 saveByte(get1Byte());  
  39.             }  
  40.         } else {  
  41.             u_int32_t next4Bytes = test4Bytes();  
  42.             firstByte = next4Bytes >> 24;  
  43.             while (next4Bytes != 0x00000001  
  44.                     && (next4Bytes & 0xFFFFFF00) != 0x00000100) {  
  45.                 // We save at least some of "next4Bytes".  
  46.                 if ((unsigned) (next4Bytes & 0xFF) > 1) {  
  47.                     // Common case: 0x00000001 or 0x000001 definitely doesn't begin anywhere in "next4Bytes", so we save all of it:  
  48.                     save4Bytes(next4Bytes);  
  49.                     skipBytes(4);  
  50.                 } else {  
  51.                     // Save the first byte, and continue testing the rest:  
  52.                     saveByte(next4Bytes >> 24);  
  53.                     skipBytes(1);  
  54.                 }  
  55.                 next4Bytes = test4Bytes();  
  56.             }  
  57.             // Assert: next4Bytes starts with 0x00000001 or 0x000001, and we've saved all previous bytes (forming a complete NAL unit).  
  58.             // Skip over these remaining bytes, up until the start of the next NAL unit:  
  59.             if (next4Bytes == 0x00000001) {  
  60.                 skipBytes(4);  
  61.             } else {  
  62.                 skipBytes(3);  
  63.             }  
  64.         }  
  65.   
  66.         u_int8_t nal_ref_idc = (firstByte & 0x60) >> 5;  
  67.         u_int8_t nal_unit_type = firstByte & 0x1F;  
  68.   
  69.         switch (nal_unit_type) {  
  70.         case 6: { // Supplemental enhancement information (SEI)  
  71.             analyze_sei_data();  
  72.             // Later, perhaps adjust "fPresentationTime" if we saw a "pic_timing" SEI payload??? #####  
  73.             break;  
  74.         }  
  75.         case 7: { // Sequence parameter set  
  76.             // First, save a copy of this NAL unit, in case the downstream object wants to see it:  
  77.             usingSource()->saveCopyOfSPS(fStartOfFrame + fOutputStartCodeSize,  
  78.                     fTo - fStartOfFrame - fOutputStartCodeSize);  
  79.   
  80.             // Parse this NAL unit to check whether frame rate information is present:  
  81.             unsigned num_units_in_tick, time_scale, fixed_frame_rate_flag;  
  82.             analyze_seq_parameter_set_data(num_units_in_tick, time_scale,  
  83.                     fixed_frame_rate_flag);  
  84.             if (time_scale > 0 && num_units_in_tick > 0) {  
  85.                 usingSource()->fFrameRate = time_scale  
  86.                         / (2.0 * num_units_in_tick);  
  87.             } else {  
  88.             }  
  89.             break;  
  90.         }  
  91.         case 8: { // Picture parameter set  
  92.             // Save a copy of this NAL unit, in case the downstream object wants to see it:  
  93.             usingSource()->saveCopyOfPPS(fStartOfFrame + fOutputStartCodeSize,  
  94.                     fTo - fStartOfFrame - fOutputStartCodeSize);  
  95.         }  
  96.         }  
  97.   
  98.         //更新时间戳变量  
  99.         usingSource()->setPresentationTime();  
  100.   
  101.         // If this NAL unit is a VCL NAL unit, we also scan the start of the next NAL unit, to determine whether this NAL unit  
  102.         // ends the current 'access unit'.  We need this information to figure out when to increment "fPresentationTime".  
  103.         // (RTP streamers also need to know this in order to figure out whether or not to set the "M" bit.)  
  104.         Boolean thisNALUnitEndsAccessUnit = False; // until we learn otherwise  
  105.         if (haveSeenEOF()) {  
  106.             // There is no next NAL unit, so we assume that this one ends the current 'access unit':  
  107.             thisNALUnitEndsAccessUnit = True;  
  108.         } else {  
  109.             Boolean const isVCL = nal_unit_type <= 5 && nal_unit_type > 0; // Would need to include type 20 for SVC and MVC #####  
  110.             if (isVCL) {  
  111.                 u_int32_t first4BytesOfNextNALUnit = test4Bytes();  
  112.                 u_int8_t firstByteOfNextNALUnit = first4BytesOfNextNALUnit  
  113.                         >> 24;  
  114.                 u_int8_t next_nal_ref_idc = (firstByteOfNextNALUnit & 0x60)  
  115.                         >> 5;  
  116.                 u_int8_t next_nal_unit_type = firstByteOfNextNALUnit & 0x1F;  
  117.                 if (next_nal_unit_type >= 6) {  
  118.                     // The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':  
  119.                     thisNALUnitEndsAccessUnit = True;  
  120.                 } else {  
  121.                     // The next NAL unit is also a VLC.  We need to examine it a little to figure out if it's a different 'access unit'.  
  122.                     // (We use many of the criteria described in section 7.4.1.2.4 of the H.264 specification.)  
  123.                     Boolean IdrPicFlag = nal_unit_type == 5;  
  124.                     Boolean next_IdrPicFlag = next_nal_unit_type == 5;  
  125.                     if (next_IdrPicFlag != IdrPicFlag) {  
  126.                         // IdrPicFlag differs in value  
  127.                         thisNALUnitEndsAccessUnit = True;  
  128.                     } else if (next_nal_ref_idc != nal_ref_idc  
  129.                             && next_nal_ref_idc * nal_ref_idc == 0) {  
  130.                         // nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0  
  131.                         thisNALUnitEndsAccessUnit = True;  
  132.                     } else if ((nal_unit_type == 1 || nal_unit_type == 2  
  133.                             || nal_unit_type == 5)  
  134.                             && (next_nal_unit_type == 1  
  135.                                     || next_nal_unit_type == 2  
  136.                                     || next_nal_unit_type == 5)) {  
  137.                         // Both this and the next NAL units begin with a "slice_header".  
  138.                         // Parse this (for each), to get parameters that we can compare:  
  139.   
  140.                         // Current NAL unit's "slice_header":  
  141.                         unsigned frame_num, pic_parameter_set_id, idr_pic_id;  
  142.                         Boolean field_pic_flag, bottom_field_flag;  
  143.                         analyze_slice_header(  
  144.                                 fStartOfFrame + fOutputStartCodeSize, fTo,  
  145.                                 nal_unit_type, frame_num, pic_parameter_set_id,  
  146.                                 idr_pic_id, field_pic_flag, bottom_field_flag);  
  147.   
  148.                         // Next NAL unit's "slice_header":  
  149.                         u_int8_t next_slice_header[NUM_NEXT_SLICE_HEADER_BYTES_TO_ANALYZE];  
  150.                         testBytes(next_slice_header, sizeof next_slice_header);  
  151.                         unsigned next_frame_num, next_pic_parameter_set_id,  
  152.                                 next_idr_pic_id;  
  153.                         Boolean next_field_pic_flag, next_bottom_field_flag;  
  154.                         analyze_slice_header(next_slice_header,  
  155.                                 &next_slice_header[sizeof next_slice_header],  
  156.                                 next_nal_unit_type, next_frame_num,  
  157.                                 next_pic_parameter_set_id, next_idr_pic_id,  
  158.                                 next_field_pic_flag, next_bottom_field_flag);  
  159.   
  160.                         if (next_frame_num != frame_num) {  
  161.                             // frame_num differs in value  
  162.                             thisNALUnitEndsAccessUnit = True;  
  163.                         } else if (next_pic_parameter_set_id  
  164.                                 != pic_parameter_set_id) {  
  165.                             // pic_parameter_set_id differs in value  
  166.                             thisNALUnitEndsAccessUnit = True;  
  167.                         } else if (next_field_pic_flag != field_pic_flag) {  
  168.                             // field_pic_flag differs in value  
  169.                             thisNALUnitEndsAccessUnit = True;  
  170.                         } else if (next_bottom_field_flag  
  171.                                 != bottom_field_flag) {  
  172.                             // bottom_field_flag differs in value  
  173.                             thisNALUnitEndsAccessUnit = True;  
  174.                         } else if (next_IdrPicFlag == 1  
  175.                                 && next_idr_pic_id != idr_pic_id) {  
  176.                             // IdrPicFlag is equal to 1 for both and idr_pic_id differs in value  
  177.                             // Note: We already know that IdrPicFlag is the same for both.  
  178.                             thisNALUnitEndsAccessUnit = True;  
  179.                         }  
  180.                     }  
  181.                 }  
  182.             }  
  183.         }  
  184.   
  185.         //注意!注意!注意!此处计算时间戳!!  
  186.         if (thisNALUnitEndsAccessUnit) {  
  187.             usingSource()->fPictureEndMarker = True;  
  188.             ++usingSource()->fPictureCount;  
  189.   
  190.             // Note that the presentation time for the next NAL unit will be different:  
  191.             struct timeval& nextPT = usingSource()->fNextPresentationTime; // alias  
  192.             nextPT = usingSource()->fPresentationTime;  
  193.             double nextFraction = nextPT.tv_usec / 1000000.0  
  194.                     + 1 / usingSource()->fFrameRate;  
  195.             unsigned nextSecsIncrement = (long) nextFraction;  
  196.             nextPT.tv_sec += (long) nextSecsIncrement;  
  197.             nextPT.tv_usec = (long) ((nextFraction - nextSecsIncrement)  
  198.                     * 1000000);  
  199.         }  
  200.         setParseState();  
  201.   
  202.         return curFrameSize();  
  203.     } catch (int /*e*/) {  
  204.         return 0; // the parsing got interrupted  
  205.     }  
  206. }  

每当开始一个新帧时,计算新的时间戳.时间戳保存在fNextPresentationTime中,在usingSource()->setPresentationTime()中传给fPresentationTime.
哇,我们看到live555的类之间调用关系曲折复杂,的确有点不易维护啊!同时我写的也不够清析,自己看着都晕,如果把你搞晕了,这很正常哦!

fPresentationTime是64位的时间,经convertToRTPTimestamp转换为32的rtp时间戳,见函数:

[cpp]  view plain  copy
  1. u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv)  
  2. {  
  3.     // Begin by converting from "struct timeval" units to RTP timestamp units:  
  4.     u_int32_t timestampIncrement = (fTimestampFrequency * tv.tv_sec);  
  5.     timestampIncrement += (u_int32_t)(  
  6.             (2.0 * fTimestampFrequency * tv.tv_usec + 1000000.0) / 2000000);  
  7.     // note: rounding  
  8.   
  9.     // Then add this to our 'timestamp base':  
  10.     if (fNextTimestampHasBeenPreset) {  
  11.         // Make the returned timestamp the same as the current "fTimestampBase",  
  12.         // so that timestamps begin with the value that was previously preset:  
  13.         fTimestampBase -= timestampIncrement;  
  14.         fNextTimestampHasBeenPreset = False;  
  15.     }  
  16.   
  17.     u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement;  
  18.       
  19.     return rtpTimestamp;  
  20. }  
其实时间戳的转换主要就是把以秒为单位的时间,提升成按频率为单位的时间.也就是提升后,时间间隔不是以秒为单位,而是以1/fTimestampFrequency为单位,也就是1/9000秒。然后再强转为32。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaopengsun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值