文档章节

live555学习笔记-RTP打包与发送

雷霄骅
 雷霄骅
发布于 2014/08/16 13:41
字数 2211
阅读 230
收藏 1

RTP打包与发送

rtp传送开始于函数:MediaSink::startPlaying()。想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当于directshow的拉模式)。

看一下这个函数:

[cpp]    view plain   copy  

  1. Boolean MediaSink::startPlaying(MediaSource& source,  

  2.         afterPlayingFunc* afterFunc, void* afterClientData)  

  3. {  

  4.     //参数afterFunc是在播放结束时才被调用。  

  5.     // Make sure we're not already being played:  

  6.     if (fSource != NULL) {  

  7.         envir().setResultMsg("This sink is already being played");  

  8.         return False;  

  9.     }  

  10.   

  11.   

  12.     // Make sure our source is compatible:  

  13.     if (!sourceIsCompatibleWithUs(source)) {  

  14.         envir().setResultMsg(  

  15.                 "MediaSink::startPlaying(): source is not compatible!");  

  16.         return False;  

  17.     }  

  18.     //记下一些要使用的对象  

  19.     fSource = (FramedSource*) &source;  

  20.   

  21.   

  22.     fAfterFunc = afterFunc;  

  23.     fAfterClientData = afterClientData;  

  24.     return continuePlaying();  

  25. }  

为了进一步封装(让继承类少写一些代码),搞出了一个虚函数continuePlaying()。让我们来看一下:

[cpp]    view plain   copy  

  1. Boolean MultiFramedRTPSink::continuePlaying() {  

  2.     // Send the first packet.  

  3.     // (This will also schedule any future sends.)  

  4.     buildAndSendPacket(True);  

  5.     return True;  

  6. }  

MultiFramedRTPSink是与帧有关的类,其实它要求每次必须从source获得一个帧的数据,所以才叫这个name。可以看到continuePlaying()完全被buildAndSendPacket()代替。看一下buildAndSendPacket():

[cpp]    view plain   copy  

  1. void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket)   

  2. {  

  3.     //此函数中主要是准备rtp包的头,为一些需要跟据实际数据改变的字段留出位置。  

  4.     fIsFirstPacket = isFirstPacket;  

  5.   

  6.   

  7.     // Set up the RTP header:  

  8.     unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)  

  9.     rtpHdr |= (fRTPPayloadType << 16);  

  10.     rtpHdr |= fSeqNo; // sequence number  

  11.     fOutBuf->enqueueWord(rtpHdr);//向包中加入一个字  

  12.   

  13.   

  14.     // Note where the RTP timestamp will go.  

  15.     // (We can't fill this in until we start packing payload frames.)  

  16.     fTimestampPosition = fOutBuf->curPacketSize();  

  17.     fOutBuf->skipBytes(4); // leave a hole for the timestamp 在缓冲中空出时间戳的位置  

  18.   

  19.   

  20.     fOutBuf->enqueueWord(SSRC());   

  21.   

  22.   

  23.     // Allow for a special, payload-format-specific header following the  

  24.     // RTP header:  

  25.     fSpecialHeaderPosition = fOutBuf->curPacketSize();  

  26.     fSpecialHeaderSize = specialHeaderSize();  

  27.     fOutBuf->skipBytes(fSpecialHeaderSize);  

  28.   

  29.   

  30.     // Begin packing as many (complete) frames into the packet as we can:  

  31.     fTotalFrameSpecificHeaderSizes = 0;  

  32.     fNoFramesLeft = False;  

  33.     fNumFramesUsedSoFar = 0; // 一个包中已打入的帧数。  

  34.     //头准备好了,再打包帧数据  

  35.     packFrame();  

  36. }  

继续看packFrame():

[cpp]    view plain   copy  

  1. void MultiFramedRTPSink::packFrame()  

  2. {  

  3.     // First, see if we have an overflow frame that was too big for the last pkt  

  4.     if (fOutBuf->haveOverflowData()) {  

  5.         //如果有帧数据,则使用之。OverflowData是指上次打包时剩下的帧数据,因为一个包可能容纳不了一个帧。  

  6.         // Use this frame before reading a new one from the source  

  7.         unsigned frameSize = fOutBuf->overflowDataSize();  

  8.         struct timeval presentationTime = fOutBuf->overflowPresentationTime();  

  9.         unsigned durationInMicroseconds =fOutBuf->overflowDurationInMicroseconds();  

  10.         fOutBuf->useOverflowData();  

  11.   

  12.   

  13.         afterGettingFrame1(frameSize, 0, presentationTime,durationInMicroseconds);  

  14.     } else {  

  15.         //一点帧数据都没有,跟source要吧。  

  16.         // Normal case: we need to read a new frame from the source  

  17.         if (fSource == NULL)  

  18.             return;  

  19.   

  20.   

  21.         //更新缓冲中的一些位置  

  22.         fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();  

  23.         fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();  

  24.         fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);  

  25.         fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;  

  26.   

  27.   

  28.         //从source获取下一帧  

  29.         fSource->getNextFrame(fOutBuf->curPtr(),//新数据存放开始的位置  

  30.                 fOutBuf->totalBytesAvailable(),//缓冲中空余的空间大小  

  31.                 afterGettingFrame,  //因为可能source中的读数据函数会被放在任务调度中,所以把获取帧后应调用的函数传给source  

  32.                 this,  

  33.                 ourHandleClosure, //这个是source结束时(比如文件读完了)要调用的函数。  

  34.                 this);  

  35.     }  

  36. }  

可以想像下面就是source从文件(或某个设备)中读取一帧数据,读完后返回给sink,当然不是从函数返回了,而是以调用afterGettingFrame这个回调函数的方式。所以下面看一下afterGettingFrame():

[cpp]    view plain   copy  

  1. void MultiFramedRTPSink::afterGettingFrame(void* clientData,  

  2.         unsigned numBytesRead, unsigned numTruncatedBytes,  

  3.         struct timeval presentationTime, unsigned durationInMicroseconds)  

  4. {  

  5.     MultiFramedRTPSink* sink = (MultiFramedRTPSink*) clientData;  

  6.     sink->afterGettingFrame1(numBytesRead, numTruncatedBytes, presentationTime,  

  7.             durationInMicroseconds);  

  8. }  

没什么可看的,只是过度为调用成员函数,所以afterGettingFrame1()才是重点:

[cpp]    view plain   copy  

  1. void MultiFramedRTPSink::afterGettingFrame1(  

  2.         unsigned frameSize,  

  3.         unsigned numTruncatedBytes,  

  4.         struct timeval presentationTime,  

  5.         unsigned durationInMicroseconds)  

  6. {  

  7.     if (fIsFirstPacket) {  

  8.         // Record the fact that we're starting to play now:  

  9.         gettimeofday(&fNextSendTime, NULL);  

  10.     }  

  11.   

  12.   

  13.     //如果给予一帧的缓冲不够大,就会发生截断一帧数据的现象。但也只能提示一下用户  

  14.     if (numTruncatedBytes > 0) {  

  15.   

  16.   

  17.         unsigned const bufferSize = fOutBuf->totalBytesAvailable();  

  18.         envir()  

  19.                 << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("  

  20.                 << bufferSize  

  21.                 << ").  "  

  22.                 << numTruncatedBytes  

  23.                 << " bytes of trailing data was dropped!  Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "  

  24.                 << OutPacketBuffer::maxSize + numTruncatedBytes  

  25.                 << ", *before* creating this 'RTPSink'.  (Current value is "  

  26.                 << OutPacketBuffer::maxSize << ".)\n";  

  27.     }  

  28.     unsigned curFragmentationOffset = fCurFragmentationOffset;  

  29.     unsigned numFrameBytesToUse = frameSize;  

  30.     unsigned overflowBytes = 0;  

  31.   

  32.   

  33.     //如果包只已经打入帧数据了,并且不能再向这个包中加数据了,则把新获得的帧数据保存下来。  

  34.     // If we have already packed one or more frames into this packet,  

  35.     // check whether this new frame is eligible to be packed after them.  

  36.     // (This is independent of whether the packet has enough room for this  

  37.     // new frame; that check comes later.)  

  38.     if (fNumFramesUsedSoFar > 0) {  

  39.         //如果包中已有了一个帧,并且不允许再打入新的帧了,则只记录下新的帧。  

  40.         if ((fPreviousFrameEndedFragmentation && !allowOtherFramesAfterLastFragment())  

  41.                 || !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize))  

  42.         {  

  43.             // Save away this frame for next time:  

  44.             numFrameBytesToUse = 0;  

  45.             fOutBuf->setOverflowData(fOutBuf->curPacketSize(), frameSize,  

  46.                     presentationTime, durationInMicroseconds);  

  47.         }  

  48.     }  

  49.       

  50.     //表示当前打入的是否是上一个帧的最后一块数据。  

  51.     fPreviousFrameEndedFragmentation = False;  

  52.   

  53.   

  54.     //下面是计算获取的帧中有多少数据可以打到当前包中,剩下的数据就作为overflow数据保存下来。  

  55.     if (numFrameBytesToUse > 0) {  

  56.         // Check whether this frame overflows the packet  

  57.         if (fOutBuf->wouldOverflow(frameSize)) {  

  58.             // Don't use this frame now; instead, save it as overflow data, and  

  59.             // send it in the next packet instead.  However, if the frame is too  

  60.             // big to fit in a packet by itself, then we need to fragment it (and  

  61.             // use some of it in this packet, if the payload format permits this.)  

  62.             if (isTooBigForAPacket(frameSize)  

  63.                     && (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart())) {  

  64.                 // We need to fragment this frame, and use some of it now:  

  65.                 overflowBytes = computeOverflowForNewFrame(frameSize);  

  66.                 numFrameBytesToUse -= overflowBytes;  

  67.                 fCurFragmentationOffset += numFrameBytesToUse;  

  68.             } else {  

  69.                 // We don't use any of this frame now:  

  70.                 overflowBytes = frameSize;  

  71.                 numFrameBytesToUse = 0;  

  72.             }  

  73.             fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,  

  74.                     overflowBytes, presentationTime, durationInMicroseconds);  

  75.         } else if (fCurFragmentationOffset > 0) {  

  76.             // This is the last fragment of a frame that was fragmented over  

  77.             // more than one packet.  Do any special handling for this case:  

  78.             fCurFragmentationOffset = 0;  

  79.             fPreviousFrameEndedFragmentation = True;  

  80.         }  

  81.     }  

  82.   

  83.   

  84.       

  85.     if (numFrameBytesToUse == 0 && frameSize > 0) {  

  86.         //如果包中有数据并且没有新数据了,则发送之。(这种情况好像很难发生啊!)  

  87.         // Send our packet now, because we have filled it up:  

  88.         sendPacketIfNecessary();  

  89.     } else {  

  90.         //需要向包中打入数据。  

  91.           

  92.         // Use this frame in our outgoing packet:  

  93.         unsigned char* frameStart = fOutBuf->curPtr();  

  94.         fOutBuf->increment(numFrameBytesToUse);  

  95.         // do this now, in case "doSpecialFrameHandling()" calls "setFramePadding()" to append padding bytes  

  96.   

  97.   

  98.         // Here's where any payload format specific processing gets done:  

  99.         doSpecialFrameHandling(curFragmentationOffset, frameStart,  

  100.                 numFrameBytesToUse, presentationTime, overflowBytes);  

  101.   

  102.   

  103.         ++fNumFramesUsedSoFar;  

  104.   

  105.   

  106.         // Update the time at which the next packet should be sent, based  

  107.         // on the duration of the frame that we just packed into it.  

  108.         // However, if this frame has overflow data remaining, then don't  

  109.         // count its duration yet.  

  110.         if (overflowBytes == 0) {  

  111.             fNextSendTime.tv_usec += durationInMicroseconds;  

  112.             fNextSendTime.tv_sec += fNextSendTime.tv_usec / 1000000;  

  113.             fNextSendTime.tv_usec %= 1000000;  

  114.         }  

  115.   

  116.   

  117.         //如果需要,就发出包,否则继续打入数据。  

  118.         // Send our packet now if (i) it's already at our preferred size, or  

  119.         // (ii) (heuristic) another frame of the same size as the one we just  

  120.         //      read would overflow the packet, or  

  121.         // (iii) it contains the last fragment of a fragmented frame, and we  

  122.         //      don't allow anything else to follow this or  

  123.         // (iv) one frame per packet is allowed:  

  124.         if (fOutBuf->isPreferredSize()  

  125.                 || fOutBuf->wouldOverflow(numFrameBytesToUse)  

  126.                 || (fPreviousFrameEndedFragmentation  

  127.                         && !allowOtherFramesAfterLastFragment())  

  128.                 || !frameCanAppearAfterPacketStart(  

  129.                         fOutBuf->curPtr() - frameSize, frameSize)) {  

  130.             // The packet is ready to be sent now  

  131.             sendPacketIfNecessary();  

  132.         } else {  

  133.             // There's room for more frames; try getting another:  

  134.             packFrame();  

  135.         }  

  136.     }  

  137. }  


看一下发送数据的函数:

[cpp]    view plain   copy  

  1. void MultiFramedRTPSink::sendPacketIfNecessary()  

  2. {  

  3.     //发送包  

  4.     if (fNumFramesUsedSoFar > 0) {  

  5.         // Send the packet:  

  6. #ifdef TEST_LOSS  

  7.         if ((our_random()%10) != 0) // simulate 10% packet loss #####  

  8. #endif  

  9.         if (!fRTPInterface.sendPacket(fOutBuf->packet(),fOutBuf->curPacketSize())) {  

  10.             // if failure handler has been specified, call it  

  11.             if (fOnSendErrorFunc != NULL)  

  12.                 (*fOnSendErrorFunc)(fOnSendErrorData);  

  13.         }  

  14.         ++fPacketCount;  

  15.         fTotalOctetCount += fOutBuf->curPacketSize();  

  16.         fOctetCount += fOutBuf->curPacketSize() - rtpHeaderSize  

  17.                 - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;  

  18.   

  19.   

  20.         ++fSeqNo; // for next time  

  21.     }  

  22.   

  23.   

  24.     //如果还有剩余数据,则调整缓冲区  

  25.     if (fOutBuf->haveOverflowData()  

  26.             && fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize() / 2) {  

  27.         // Efficiency hack: Reset the packet start pointer to just in front of  

  28.         // the overflow data (allowing for the RTP header and special headers),  

  29.         // so that we probably don't have to "memmove()" the overflow data  

  30.         // into place when building the next packet:  

  31.         unsigned newPacketStart = fOutBuf->curPacketSize()-   

  32.                 (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());  

  33.         fOutBuf->adjustPacketStart(newPacketStart);  

  34.     } else {  

  35.         // Normal case: Reset the packet start pointer back to the start:  

  36.         fOutBuf->resetPacketStart();  

  37.     }  

  38.     fOutBuf->resetOffset();  

  39.     fNumFramesUsedSoFar = 0;  

  40.   

  41.   

  42.     if (fNoFramesLeft) {  

  43.         //如果再没有数据了,则结束之  

  44.         // We're done:  

  45.         onSourceClosure(this);  

  46.     } else {  

  47.         //如果还有数据,则在下一次需要发送的时间再次打包发送。  

  48.         // We have more frames left to send.  Figure out when the next frame  

  49.         // is due to start playing, then make sure that we wait this long before  

  50.         // sending the next packet.  

  51.         struct timeval timeNow;  

  52.         gettimeofday(&timeNow, NULL);  

  53.         int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;  

  54.         int64_t uSecondsToGo = secsDiff * 1000000  

  55.                 + (fNextSendTime.tv_usec - timeNow.tv_usec);  

  56.         if (uSecondsToGo < 0 || secsDiff < 0) { // sanity check: Make sure that the time-to-delay is non-negative:  

  57.             uSecondsToGo = 0;  

  58.         }  

  59.   

  60.   

  61.         // Delay this amount of time:  

  62.         nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo,  

  63.                 (TaskFunc*) sendNext, this);  

  64.     }  

  65. }  


可以看到为了延迟包的发送,使用了delay task来执行下次打包发送任务。

sendNext()中又调用了buildAndSendPacket()函数,呵呵,又是一个圈圈。

总结一下调用过程:


最后,再说明一下包缓冲区的使用:

MultiFramedRTPSink中的帧数据和包缓冲区共用一个,只是用一些额外的变量指明缓冲区中属于包的部分以及属于帧数据的部分(包以外的数据叫做overflow data)。它有时会把overflow data以mem move的方式移到包开始的位置,有时把包的开始位置直接设置到overflow data开始的地方。那么这个缓冲的大小是怎样确定的呢?是跟据调用者指定的的一个最大的包的大小+60000算出的。这个地方把我搞胡涂了:如果一次从source获取一个帧的话,那这个缓冲应设为不小于最大的一个帧的大小才是,为何是按包的大小设置呢?可以看到,当缓冲不够时只是提示一下:

[cpp]    view plain   copy  

  1. if (numTruncatedBytes > 0) {  

  2.   

  3.   

  4.     unsigned const bufferSize = fOutBuf->totalBytesAvailable();  

  5.     envir()  

  6.             << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("  

  7.             << bufferSize  

  8.             << ").  "  

  9.             << numTruncatedBytes  

  10.             << " bytes of trailing data was dropped!  Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "  

  11.             << OutPacketBuffer::maxSize + numTruncatedBytes  

  12.             << ", *before* creating this 'RTPSink'.  (Current value is "  

  13.             << OutPacketBuffer::maxSize << ".)\n";  

  14. }  

当然此时不会出错,但有可能导致时间戳计算不准,或增加时间戳计算与source端处理的复杂性(因为一次取一帧时间戳是很好计算的)。


原文地址:http://blog.csdn.net/niu_gao/article/details/6921145

live555源代码(VC6):http://download.csdn.net/detail/leixiaohua1020/6374387

本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/11850969

雷霄骅
粉丝 211
博文 419
码字总数 2129
作品 4
朝阳
程序员
私信 提问
RTSP学习笔记(4)LiveMediaServer流程分析

Live555MediaServer 1、初始化 BasicTaskschedular BasicUsageEnvironment RTSPServer |--------new RTSPServer |------setupOurSocket创建监听客户端连接用的socket |------turnOnBackgroun......

Sean-x
2016/02/23
190
0
Live555源代码解读(6)

七、RTP打包与发送 rtp传送开始于函数:MediaSink::startPlaying()。想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当于directshow的拉模式)。 看一下这个...

Sean-x
2016/02/25
85
0
rtsp server(live555)详细设计

/* *本文基于LIVE555的嵌入式的RTSP流媒体服务器一个设计文档,个中细节现剖于此,有需者可参考指正,同时也方便后期自己查阅。(本版本是基于2011年的live555) 作者:llf_17@qq.com */ RTSP...

llf_17
2016/09/01
0
0
ffmpeg的使用

ffmpeg的使用,及发送媒体流的一些简单介绍 在以前的文章谈过对ffmpeg的编译。这里谈谈ffmpeg的最简单的使用。 ffmpeg使用语法: ffmpeg [[options][`-i' input_file]]... {[options] output...

sunrc
2014/10/13
0
0
Live555源代码解读(10)

十一 、h264 RTP传输详解(3) 书接上回:H264FUAFragmenter又对数据做了什么呢? [cpp] view plaincopy void H264FUAFragmenter::doGetNextFrame() { if (fNumValidDataBytes == 1) { // We h......

Sean-x
2016/02/25
27
0

没有更多内容

加载失败,请刷新页面

加载更多

PCB设计-Allegro软件入门系列-铺铜操作(下)

铺铜是PCB很常见的操作,PCB的敷铜一般都是覆地铜,增大地线面积,有利于地线阻抗降低,使电源和信号传输稳定,在高频的信号线附近敷铜,可大大减少电磁辐射干扰,起屏蔽作用。 本讲讲解啊一...

demyar
7分钟前
1
0
如何通过WASI SDK 在Linux上编译ZXing C++

Mozilla在今年三月份的时候公布了WASI。WASI的目标就是让WebAssembly在任何地方都可以运行,而不仅仅像现在这样只能运行在Node.js和Web浏览器中。WASI目前依然处于初级阶段,这篇文章分享下如...

yushulx
9分钟前
1
0
.Net界面开发神器—DevExpress官方汉化包免费下载!还在等什么?

点击获取DevExpress v19.1.7新版试用下载 DevExpress Localization Service允许您创建一组自定义的附属程序集,要将语言包添加到程序集中,请查看本文中为大家列出的对应版本的汉化包,下载并...

FILA6666
9分钟前
2
0
php生成二维码

        header('Content-Type: image/png');        //清除缓冲区,防止之前面不知道的情况下被加头部信息导致不显示图片内容        ob_clean();        $...

横着走的螃蟹
14分钟前
2
0
伪类和伪元素

伪类和伪元素 伪类和伪元素,对于绝大多数同学来说,都是耳熟能详的名字,但确实又有很多人搞不清楚它们之间的区别,以致于混淆概念。而当概念都混淆的时候,也往往意味着你不会经常使用它,...

不负好时光
17分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部