live555源码分析----RTP的打包与发送

转载地址:http://blog.csdn.net/gavinr/article/details/7035799

 这里主要分析一下,live555中关于RTP打包发送的部分。在处理完PLAY命令之后,就开始发送RTP数据包了(其实在发送PLAY命令的response包之前,就会发送一个RTP包,这里传输就已经开始了)
    RTP包的发送是从MediaSink::startPlaying函数调用开始的

  1. Boolean MediaSink::startPlaying(MediaSource& source,  
  2.                 afterPlayingFunc* afterFunc,  
  3.                 void* afterClientData) {  
  4.   // Make sure we're not already being played:  
  5.   if (fSource != NULL) {  
  6.     envir().setResultMsg("This sink is already being played");  
  7.     return False;  
  8.   }  
  9.   
  10.   
  11.   // Make sure our source is compatible:  
  12.   if (!sourceIsCompatibleWithUs(source)) {  
  13.     envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!");  
  14.     return False;  
  15.   }  
  16.   fSource = (FramedSource*)&source;  
  17.   
  18.   
  19.   fAfterFunc = afterFunc;  
  20.   fAfterClientData = afterClientData;  
  21.   return continuePlaying();     //重要的函数在这里  
  22. }  


    这个函数只有最后一句最重要,即continuePlaying函数的调用。continuePlaying函数是定义在MediaSink类中的纯虚函数,需要到特定媒体的sink子类中实现,对于H264来讲是在H264VideoRTPSink中实现的。
    H264VideoRTPSink继承关系:H264VideoRTPSink->VideoRTPSink->MultiFramedRTPSink->RTPSink->MediaSink。
  1. Boolean H264VideoRTPSink::continuePlaying() {  
  2.   // First, check whether we have a 'fragmenter' class set up yet.  
  3.   // If not, create it now:  
  4.   if (fOurFragmenter == NULL) {  
  5.     //创建一个辅助类H264FUAFragmenter,用于H264的RTP打包  
  6.   
  7.   
  8.     fOurFragmenter = new H264FUAFragmenter(envir(), fSource, OutPacketBuffer::maxSize,  
  9.                        ourMaxPacketSize() - 12/*RTP hdr size*/);  
  10.     fSource = fOurFragmenter;  
  11.   }  
  12.   
  13.   
  14.   // Then call the parent class's implementation:  
  15.   return MultiFramedRTPSink::continuePlaying();  
  16. }  


    上面的代码中创建了一个辅助类H264FUAFragmenter,因为H264的RTP包,有些特殊需要进一步处理,可以参考RFC3986。接着调用MultiFramedRTPSink类的continuePlaying实现

  1. Boolean MultiFramedRTPSink::continuePlaying() {  
  2.   // Send the first packet.  
  3.   // (This will also schedule any future sends.)  
  4.   buildAndSendPacket(True);  
  5.   return True;  
  6. }  
  7.     这时调用buildAndSendPacket函数时,看名字就知道其中将完成打包并发送工作。传递了一个True参数,表示这是第一个packet。继续看buildAndSendPacket函数定义  
  8. void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {  
  9.   fIsFirstPacket = isFirstPacket;  
  10.     //  
  11.     //设置RTP头,注意,接收端需要根据RTP包的序号fSeqNo来重新排序  
  12.     //  
  13.   // Set up the RTP header:  
  14.   unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)  
  15.   rtpHdr |= (fRTPPayloadType<<16);  
  16.   rtpHdr |= fSeqNo; // sequence number  
  17.   fOutBuf->enqueueWord(rtpHdr);  
  18.   
  19.   
  20.   //保留一个4 bytes空间,用于设置time stamp  
  21.   // Note where the RTP timestamp will go.  
  22.   // (We can't fill this in until we start packing payload frames.)  
  23.   fTimestampPosition = fOutBuf->curPacketSize();  
  24.   fOutBuf->skipBytes(4); // leave a hole for the timestamp  
  25.   
  26.   
  27.   fOutBuf->enqueueWord(SSRC());     //跟RTCP相关,作用暂不清楚  
  28.       
  29.     //在RTP头后面,添加一个payload-format-specific头,  
  30. // Allow for a special, payload-format-specific header following the  
  31.   // RTP header:  
  32.   fSpecialHeaderPosition = fOutBuf->curPacketSize();  
  33.     //  
  34.     //specialHeaderSize在MultiFramedRTPSink中的默认实现返回0,对于H264的实现不需要处理这个字段  
  35.     //  
  36.   fSpecialHeaderSize = specialHeaderSize();  
  37.   fOutBuf->skipBytes(fSpecialHeaderSize);   //预留空间  
  38.   
  39.   
  40.    //填充尽可能多的frames到packet中  
  41.   // Begin packing as many (complete) frames into the packet as we can:  
  42.   fTotalFrameSpecificHeaderSizes = 0;  
  43.   fNoFramesLeft = False;  
  44.   fNumFramesUsedSoFar = 0;  
  45.   packFrame();  
  46. }  


    buildAndSendPacket函数中,完成RTP头的准备工作。可以看到RTP头是非常简单的,RTP头中的序号非常重要,客户端需要据此进行RTP包的重排序操作。RTP包内容存放在一个OutPacketBuffer类型的fOutBuf成员变量中,OutPacketBuffer类的细节在文章的最后还会讨论。在RTP头中预留了一些空间没有进行实际的填充,这个工作将在doSpecialFrameHandling中进行,后面会有讨论。进一步的工作,在packFrame函数中进行,它将为RTP包填充数据。
  1. void MultiFramedRTPSink::packFrame() {  
  2.   // Get the next frame.  
  3.     //  
  4.     //首先需要检查buffer中是否还存在溢出的数据(frame)  
  5.     //      
  6. // First, see if we have an overflow frame that was too big for the last pkt  
  7.   if (fOutBuf->haveOverflowData()) {  
  8.     // Use this frame before reading a new one from the source  
  9.     unsigned frameSize = fOutBuf->overflowDataSize();  
  10.     struct timeval presentationTime = fOutBuf->overflowPresentationTime();  
  11.     unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();  
  12.     //  
  13.     //使用溢出的数据作为packet的内容,注意,这里并不一定进行memcopy操作,  
  14.     //因为可能已经把packet 的开始位置重置到overflow data的位置   
  15.     //  
  16.     fOutBuf->useOverflowData();   
  17.   
  18.   
  19.     //  
  20.     //获取了数据,就可以准备发送了,当然若是数据量太小,将需要获取更多的数据  
  21.     //  
  22.     afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);  
  23.   } else {  
  24.     // Normal case: we need to read a new frame from the source  
  25.     if (fSource == NULL) return;  
  26.   
  27.   
  28.     //这里,给予当前帧预留空间的机会,保存一些特殊信息,当然frameSpecificHeaderSize函数默认返回0  
  29.     fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();  
  30.     fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();          
  31.     fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);  
  32.     fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;  
  33.   
  34.   
  35.     //  
  36.     //从source中获取数据,然后调用回调函数afterGettingFrame。注意,在C++中类成员函数是不能作为回调用函数的。  
  37.     //我们可以看到afterGettingFrame中直接调用了afterGettingFrame1函数,与上面的第一次情况处理类似了。不过这里为什么要用回调函数回?  
  38.     //  
  39.     fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),  
  40.               afterGettingFrame, this, ourHandleClosure, this);  
  41.   }  
  42. }  


    packFrame函数需要处理两种情况:
1).buffer中存在未发送的数据(overflow data),这时可以将调用afterGettingFrame1函数进行后续处理工作。
2).buffer不存在数据,这时需要调用source上的getNextFrame函数获取数据。getNextFrame调用时,参数中有两个回调用函数:afterGettingFrame函数将在获取到数据后调用,其中只是简单的调用了afterGettingFrame1函数而已,这是因为C++中是不充许类成员函数作为回调用函数的;ourHandleClosure函数将在数据已经处理完毕时调用,如文件结束。
    getNextFrame函数的实现,这里暂不讨论。来看afterGettingFrame1函数的实现。

  1. void MultiFramedRTPSink  
  2. ::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,  
  3.              struct timeval presentationTime,  
  4.              unsigned durationInMicroseconds) {  
  5.   if (fIsFirstPacket) {  
  6.   
  7.   
  8.     //第一个packet,则记录下当前时间  
  9.     // Record the fact that we're starting to play now:  
  10.     gettimeofday(&fNextSendTime, NULL);  
  11.   }  
  12.   
  13.   
  14.   //  
  15.   //这里的处理要注意了,当一个Frame大于OutPacketBuffer::maxSize(默认值为60000)时,则会丢弃剩下的部分,numTruncatedBytes即为超出部分的大小。  
  16.   //  
  17.   if (numTruncatedBytes > 0) {  
  18.     unsigned const bufferSize = fOutBuf->totalBytesAvailable();  
  19.     envir() << "MultiFramedRTPSink::afterGettingFrame1(): The input frame data was too large for our buffer size ("  
  20.         << bufferSize << ").  "  
  21.         << numTruncatedBytes << " bytes of trailing data was dropped!  Correct this by increasing \"OutPacketBuffer::maxSize\" to at least "  
  22.         << OutPacketBuffer::maxSize + numTruncatedBytes << ", *before* creating this 'RTPSink'.  (Current value is "  
  23.         << OutPacketBuffer::maxSize << ".)\n";  
  24.   }  
  25.   unsigned curFragmentationOffset = fCurFragmentationOffset;  
  26.   unsigned numFrameBytesToUse = frameSize;  
  27.   unsigned overflowBytes = 0;  
  28.   
  29.   
  30.   // If we have already packed one or more frames into this packet,  
  31.   // check whether this new frame is eligible to be packed after them.  
  32.   // (This is independent of whether the packet has enough room for this  
  33.   // new frame; that check comes later.)  
  34.     //  
  35.     //fNumFramesUsedSoFar>0 表示packet已经存在frame,需要检查是否充许在packet中加入新的frame  
  36.     //  
  37.   if (fNumFramesUsedSoFar > 0) {          
  38.     if ((fPreviousFrameEndedFragmentation  
  39.      && !allowOtherFramesAfterLastFragment())   //不充许在前一个分片之后,跟随一个frame  
  40.     || !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize)) { //frame不能出现在非packet的开始位置  
  41.       // Save away this frame for next time:  
  42.       numFrameBytesToUse = 0;  
  43.     //  
  44.     //不充许添加新的frame,则保存为溢出数据,下次处理  
  45.     //  
  46.       fOutBuf->setOverflowData(fOutBuf->curPacketSize(), frameSize,  
  47.                    presentationTime, durationInMicroseconds);  
  48.     }  
  49.   }  
  50.   fPreviousFrameEndedFragmentation = False;  
  51.   
  52.   
  53.   if (numFrameBytesToUse > 0) {  
  54.       
  55.     // Check whether this frame overflows the packet  
  56.     if (fOutBuf->wouldOverflow(frameSize)) {  
  57.     //  
  58.     //若frame将导致packet溢出,应该将其保存到packet的溢出数据中,在下一个packet中发送。  
  59.     //如果frame本身大于pakcet 的max size, 就需要对frame进行分片操作。不过需要调用allowFragmentationAfterStart  
  60.     //函数以确定是否充许分片,例如对于H264而言,  
  61.     //  
  62.       // Don't use this frame now; instead, save it as overflow data, and  
  63.       // send it in the next packet instead.  However, if the frame is too  
  64.       // big to fit in a packet by itself, then we need to fragment it (and  
  65.       // use some of it in this packet, if the payload format permits this.)  
  66.       if (isTooBigForAPacket(frameSize)  
  67.           && (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart())) {  
  68.         // We need to fragment this frame, and use some of it now:  
  69.         overflowBytes = computeOverflowForNewFrame(frameSize);  
  70.         numFrameBytesToUse -= overflowBytes;  
  71.         fCurFragmentationOffset += numFrameBytesToUse;  
  72.       } else {  
  73.         // We don't use any of this frame now:  
  74.         overflowBytes = frameSize;  
  75.         numFrameBytesToUse = 0;  
  76.       }  
  77.       fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,  
  78.                    overflowBytes, presentationTime, durationInMicroseconds);  
  79.     } else if (fCurFragmentationOffset > 0) {  
  80.       // This is the last fragment of a frame that was fragmented over  
  81.       // more than one packet.  Do any special handling for this case:  
  82.       fCurFragmentationOffset = 0;  
  83.       fPreviousFrameEndedFragmentation = True;  
  84.     }  
  85.   }  
  86.   
  87.   
  88.   if (numFrameBytesToUse == 0 && frameSize > 0) {  
  89.     // Send our packet now, because we have filled it up:  
  90.     sendPacketIfNecessary();    //发送RTP包  
  91.   } else {  
  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.     //还记得RTP头中序留的空间吗,将在这个函数中进行填充  
  98.     //  
  99.     // Here's where any payload format specific processing gets done:  
  100.     doSpecialFrameHandling(curFragmentationOffset, frameStart,  
  101.                numFrameBytesToUse, presentationTime,  
  102.                overflowBytes);  
  103.   
  104.   
  105.     ++fNumFramesUsedSoFar;  
  106.       
  107.     //  
  108.     //设置下一个packet的时间信息,这里若存在overflow数据,就不需要更新时间,因为这是同一个frame的不同分片,需要保证时间一致  
  109.     //  
  110.     // Update the time at which the next packet should be sent, based  
  111.     // on the duration of the frame that we just packed into it.  
  112.     // However, if this frame has overflow data remaining, then don't  
  113.     // count its duration yet.  
  114.     if (overflowBytes == 0) {  
  115.       fNextSendTime.tv_usec += durationInMicroseconds;  
  116.       fNextSendTime.tv_sec += fNextSendTime.tv_usec/1000000;  
  117.       fNextSendTime.tv_usec %= 1000000;  
  118.     }  
  119.   
  120.   
  121.     // Send our packet now if (i) it's already at our preferred size, or  
  122.     // (ii) (heuristic) another frame of the same size as the one we just  
  123.     //      read would overflow the packet, or  
  124.     // (iii) it contains the last fragment of a fragmented frame, and we  
  125.     //      don't allow anything else to follow this or  
  126.     // (iv) one frame per packet is allowed:  
  127.     if (fOutBuf->isPreferredSize()  
  128.         || fOutBuf->wouldOverflow(numFrameBytesToUse)  
  129.         || (fPreviousFrameEndedFragmentation &&  
  130.             !allowOtherFramesAfterLastFragment())  
  131.         || !frameCanAppearAfterPacketStart(fOutBuf->curPtr() - frameSize,  
  132.                        frameSize) ) {  
  133.       // The packet is ready to be sent now  
  134.       sendPacketIfNecessary();      //发送RTP包  
  135.     } else {  
  136.       // There's room for more frames; try getting another:  
  137.       packFrame();      //packet中还可以容纳frame,这里将形成递归调用   
  138.     }  
  139.   }  
  140. }  


    afterGettingFrame1的复杂之处在于处理frame的分片,若一个frame大于TCP/UDP有效载荷(程序中定义为1448个字节),就必需分片了。最简单的情况就是一个packet(RTP包)中最多只充许一个frame,即一个RTP包中存在一个frame或者frame的一个分片,H264就是这样处理的。,方法是将剩余的数据记录为buffer的溢出部分。下次调用packFrame函数时,直接从溢出部分复制到packet中。不过应该注意,一个frame的大小不能超过buffer的大小(默认为60000),否则会真的溢出, 那就应该考虑增加buffer大小了。
    上面的代码中还调用了doSpecialFrameHandling,子类需要重新实现进行一些特殊处理,文章最后还会讨论这个问题。
    在packet中充许出现多个frame的情况下(大多数情况下应该没必要用到),采用了递归来实现,可以看到afterGettingFrame1函数的最后有调用packFrame的代码。
    再来看RTP的发送函数sendPacketIfNecessary

  1. void MultiFramedRTPSink::sendPacketIfNecessary() {  
  2.     //  
  3.     //packet中存在frame,则发送出去  
  4.     //  
  5.   if (fNumFramesUsedSoFar > 0) {  
  6.     // Send the packet:  
  7.     //  
  8.     //可以通过TEST_LOSS宏,模拟10%丢包  
  9.     //  
  10. #ifdef TEST_LOSS  
  11.     if ((our_random()%10) != 0) // simulate 10% packet loss #####  
  12. #endif  
  13.     //  
  14.     //现在通过调用RTPInterface::sendPacket函数发送packet  
  15.     //       
  16.  if (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())) {  
  17.     // if failure handler has been specified, call it  
  18.     if (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);    //错误处理  
  19.       }  
  20.     ++fPacketCount;  
  21.     fTotalOctetCount += fOutBuf->curPacketSize();  
  22.     fOctetCount += fOutBuf->curPacketSize()  
  23.       - rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;  
  24.   
  25.   
  26.     ++fSeqNo; // for next time  
  27.   }  
  28.   
  29.   
  30.   if (fOutBuf->haveOverflowData()  
  31.       && fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize()/2) {  
  32.     //  
  33.     //为了提高效率,可以直接重置buffer中的packet开始位置,这样就不需要拷贝一遍overflow数据了。  
  34.     //在一个packet只能包含一个frame的情况下,是不是可以考虑修改这里的判断条件呢?  
  35.     //      
  36.     // Efficiency hack: Reset the packet start pointer to just in front of  
  37.     // the overflow data (allowing for the RTP header and special headers),  
  38.     // so that we probably don't have to "memmove()" the overflow data  
  39.     // into place when building the next packet:  
  40.     unsigned newPacketStart = fOutBuf->curPacketSize()  
  41.       - (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());  
  42.     fOutBuf->adjustPacketStart(newPacketStart); //调整buffer中的packet 开始位置  
  43.   } else {  
  44.     // Normal case: Reset the packet start pointer back to the start:  
  45.     fOutBuf->resetPacketStart();    //这种情况,若存在overflow data,就需要进行copy操作了  
  46.   }  
  47.   fOutBuf->resetOffset();   //packet已经发送了,可以重置buffer中的数据offset了  
  48.   fNumFramesUsedSoFar = 0;  //清零packet中的frame数  
  49.     //  
  50.     //数据已经发送完毕(例如文件传输完毕),可以关闭了  
  51.     //    
  52.   if (fNoFramesLeft) {  
  53.     // We're done:  
  54.     onSourceClosure(this);  
  55.   } else {  
  56.     //  
  57.     //准备下一次发送任务  
  58.     //  
  59.     // We have more frames left to send.  Figure out when the next frame  
  60.     // is due to start playing, then make sure that we wait this long before  
  61.     // sending the next packet.  
  62.     struct timeval timeNow;  
  63.     gettimeofday(&timeNow, NULL);  
  64.     int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;   //若是同一个frame的不同分片,这个值将为0  
  65.     int64_t uSecondsToGo = secsDiff*1000000 + (fNextSendTime.tv_usec - timeNow.tv_usec);  
  66.     if (uSecondsToGo < 0 || secsDiff < 0) { // sanity check: Make sure that the time-to-delay is non-negative:  
  67.       uSecondsToGo = 0;       
  68.     }  
  69.     //  
  70.     //作延时时间,处理函数,将入到任务调试器中,以便进行下一次发送操作  
  71.     //      
  72. // Delay this amount of time:  
  73.     nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);  
  74.   }  
  75. }  

    sendPacketIfNecessary函数处理一些发送的细节,我们来看最重要的两点。
    1)RTP包还是转交给了RTPInterface::sendPacket函数,等下再看其具体实现。
    2)将下一次RTP的发送操作加入到任务调度器中,参数中传递了sendNext函数指针,其实现比较简单,如下

  1. void MultiFramedRTPSink::sendNext(void* firstArg) {  
  2.   MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;  
  3.   sink->buildAndSendPacket(False);  //现在已经不是第一次调用了  
  4. }  

    sendNext函数中又调用了buildAndSendPacket函数,轮回了。。。现在来看RTPInterface::sendPacket函数

  1. Boolean RTPInterface::sendPacket(unsigned char* packet, unsigned packetSize) {  
  2.   Boolean success = True; // we'll return False instead if any of the sends fail  
  3.   
  4.    //一般情况下,使用UDP发送  
  5.   // Normal case: Send as a UDP packet:  
  6.   if (!fGS->output(envir(), fGS->ttl(), packet, packetSize)) success = False;  
  7.   
  8.   //使用TCP发送  
  9.   // Also, send over each of our TCP sockets:  
  10.   for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;  
  11.        streams = streams->fNext) {  
  12.     if (!sendRTPOverTCP(packet, packetSize,  
  13.             streams->fStreamSocketNum, streams->fStreamChannelId)) {  
  14.       success = False;  
  15.     }  
  16.   }  
  17.   
  18.   
  19.   return success;  
  20. }  


    若是使用UDP方式发送,将调用Groupsock::output函数,可以实现组播功能。groupsock只实现了UDP发送功能,当用TCP方式传送时调用sendRTPOverTcP函数,这个函数中直接调用socket的send函数。
    现在RTP的发送终于结束了,groupsock的实现留待下次分析。现在再来看一个遗留的问题,MultiFramedRTPSink::doSpecialFrameHandling的实现。它是定义在MultiFramedRTPSink中的虚函数,先来看其默认的实现


  1. void MultiFramedRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/,  
  2.              unsigned char/*frameStart*/,  
  3.              unsigned /*numBytesInFrame*/,  
  4.              struct timeval framePresentationTime,  
  5.              unsigned /*numRemainingBytes*/) {  
  6.   // default implementation: If this is the first frame in the packet,  
  7.   // use its presentationTime for the RTP timestamp:  
  8.   if (isFirstFrameInPacket()) {  
  9.     setTimestamp(framePresentationTime);  
  10.   }  
  11. }  


可以看到默认实现中只是在第一次调用时,设置RTP包中的的时间信息,下面来看H264VideoRTPSink上的实现

  1. void H264VideoRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/,  
  2.                           unsigned char/*frameStart*/,  
  3.                           unsigned /*numBytesInFrame*/,  
  4.                           struct timeval framePresentationTime,  
  5.                           unsigned /*numRemainingBytes*/) {  
  6.     //  
  7.     //设置RTP头中的M位  
  8.     //  
  9.   // Set the RTP 'M' (marker) bit iff  
  10.   // 1/ The most recently delivered fragment was the end of (or the only fragment of) an NAL unit, and  
  11.   // 2/ This NAL unit was the last NAL unit of an 'access unit' (i.e. video frame).  
  12.   if (fOurFragmenter != NULL) {  
  13.     H264VideoStreamFramer* framerSource  
  14.       = (H264VideoStreamFramer*)(fOurFragmenter->inputSource());  
  15.     // This relies on our fragmenter's source being a "H264VideoStreamFramer".  
  16.     if (fOurFragmenter->lastFragmentCompletedNALUnit()  
  17.     && framerSource != NULL && framerSource->pictureEndMarker()) {  
  18.       setMarkerBit();  
  19.       framerSource->pictureEndMarker() = False;  
  20.     }  
  21.   }  
  22.     //  
  23.     //设置时间戳  
  24.     //  
  25.   setTimestamp(framePresentationTime);  
  26. }  

    解释一下RTP头中的M位,H264图像可能被封装成多个NALU,这些NALU就拥有相同的同时间。根据RFC3984的定义,当RTP中封装的是同一图像的最后一个NALU时,就需要设置M位


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值