庖丁解牛-----Live555源码彻底解密(RTP打包)

简介: 本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据MediaServer讲解Rtsp的建立过程) http://blog.

本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容;

庖丁解牛-----Live555源码彻底解密(根据MediaServer讲解Rtsp的建立过程)

http://blog.csdn.net/smilestone_322/article/details/18923139

在收到客户端的Play命令后,调用StartStream函数启动流

void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,

                            void* streamToken,

                            TaskFunc* rtcpRRHandler,

                            void* rtcpRRHandlerClientData,

                            unsignedshort& rtpSeqNum,

                            unsigned& rtpTimestamp,

                            ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,

                            void* serverRequestAlternativeByteHandlerClientData) {

  StreamState* streamState = (StreamState*)streamToken;

  Destinations* destinations

    = (Destinations*)(fDestinationsHashTable->Lookup((charconst*)clientSessionId));

  if (streamState != NULL) {

    //启动流

    streamState->startPlaying(destinations,

                    rtcpRRHandler, rtcpRRHandlerClientData,

                    serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);

    RTPSink* rtpSink = streamState->rtpSink(); // alias

if (rtpSink != NULL) {

 //获取序列号与时间戳

      rtpSeqNum = rtpSink->currentSeqNo();

      rtpTimestamp = rtpSink->presetNextTimestamp();

    }

  }

}

 

接着跟踪streamState类中的startPlaying函数;源码如下:

void StreamState

::startPlaying(Destinations* dests,

            TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,

            ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,

            void* serverRequestAlternativeByteHandlerClientData) {

  if (dests == NULL) return;

 

  if (fRTCPInstance == NULL && fRTPSink != NULL) {

// Create (and start) a 'RTCP instance' for this RTP sink:

//用来发送RTCP数据包

    fRTCPInstance

      = RTCPInstance::createNew(fRTPSink->envir(), fRTCPgs,

                   fTotalBW, (unsignedchar*)fMaster.fCNAME,

                   fRTPSink, NULL /* we're a server */);

        // Note: This starts RTCP running automatically

  }

 

  if (dests->isTCP) {

// Change RTP and RTCP to use the TCP socket instead of UDP:

//使用TCP Socket代替UDP socket,使用什么socket由客户端确定,客户端在Setup时,将socket的连接方式告诉服务端;

 

    if (fRTPSink != NULL) {

      fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);

      RTPInterface

     ::setServerRequestAlternativeByteHandler(fRTPSink->envir(), dests->tcpSocketNum,

                             serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);

        // So that we continue to handle RTSP commands from the client

    }

    if (fRTCPInstance != NULL) {

      fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);

      fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,

                         rtcpRRHandler, rtcpRRHandlerClientData);

    }

  } else {

    // Tell the RTP and RTCP 'groupsocks' about this destination

    // (in case they don't already have it):

    if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort);

    if (fRTCPgs != NULL) fRTCPgs->addDestination(dests->addr, dests->rtcpPort);

    if (fRTCPInstance != NULL) {

      fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,

                         rtcpRRHandler, rtcpRRHandlerClientData);

    }

  }

 

  if (fRTCPInstance != NULL) {

    // Hack: Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will (likely) be able to

    // get RTCP-synchronized presentation times immediately:

    fRTCPInstance->sendReport();

  }

 

  if (!fAreCurrentlyPlaying && fMediaSource != NULL) {

if (fRTPSink != NULL) {

 //启动流

      fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState,this);

      fAreCurrentlyPlaying = True;

    } else if (fUDPSink != NULL) {

      fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState,this);

      fAreCurrentlyPlaying = True;

    }

  }

}

 

下面主要分析:

fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);

代码;RTPSink* fRTPSink;RTPSink继承自MediaSink,所以fRTPSink调用的是MediaSink中的startPlaying函数;跟踪进入到startPlaying函数;

    Boolean MediaSink::startPlaying(MediaSource& source,

                   afterPlayingFunc* afterFunc,

                   void* afterClientData) {

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

  if (fSource != NULL) {

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

    return False;

  }

 

  // Make sure our source is compatible:

  if (!sourceIsCompatibleWithUs(source)) {

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

    return False;

  }

 

  //保存下一些变量

  fSource = (FramedSource*)&source;

  fAfterFunc = afterFunc;

  fAfterClientData = afterClientData;

  return continuePlaying();

}

 

这个函数的内容对于客户端和服务端来说,都差不多,就是Sink跟source要数据,对服务器来说,source就是读文件或读实时流,将数据数据传递到sink,sink负责打包发送,对于客户端来说,source就是从网络上接收数据包,组成帧,而sink就是数据的解码等内容;下面接着跟进到continuePlaying();

  virtual Boolean continuePlaying() = 0;函数在MediaSink类中定义的是一个纯虚函数,实现就是在它的子类里面实现了。跟进代码,看在哪个子类中实现该函数;

 

Boolean MultiFramedRTPSink::continuePlaying() {

  // Send the first packet.

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

  buildAndSendPacket(True);

  return True;

}

 

在MultiFrameRTPSink中找到continuePlaying()函数,该函数很简单,就是调用buildAndSendPacket(True);函数;MultiFrameRTPSink是一个与帧有关的类,它每次从source中获得一帧数据,buildAndSendPacket函数,顾名思义就是打包和发送的函数了。

 

void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {

  fIsFirstPacket = isFirstPacket;

 

  // Set up the RTP header:

  //填充RTP包头

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

  rtpHdr |= (fRTPPayloadType<<16); //负载类型

  rtpHdr |= fSeqNo; // sequence number //序列号

  //往包buff中加入rtpHdr

  fOutBuf->enqueueWord(rtpHdr);

 

  // Note where the RTP timestamp will go.

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

  fTimestampPosition = fOutBuf->curPacketSize();

 

  //缓冲区中空出一个时间戳的位置,时间戳在以后在填充

  fOutBuf->skipBytes(4); // leave a hole for the timestamp

 

  //缓冲区中填入SSRC内容;

  fOutBuf->enqueueWord(SSRC());

 

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

  // RTP header:

  fSpecialHeaderPosition = fOutBuf->curPacketSize();

  fSpecialHeaderSize = specialHeaderSize();

  fOutBuf->skipBytes(fSpecialHeaderSize);

 

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

  fTotalFrameSpecificHeaderSizes = 0;

  fNoFramesLeft = False;

  fNumFramesUsedSoFar = 0;

  //前面的内容都是填充RTP包头,packFrame就是打包数据了

  packFrame();

}

 

PackFrame函数源码如下:

void MultiFramedRTPSink::packFrame() {

  // Get the next frame.

 

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

  if (fOutBuf->haveOverflowData()) {

     //上一帧的数据太大,溢出了

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

    unsigned frameSize = fOutBuf->overflowDataSize();

    struct timeval presentationTime = fOutBuf->overflowPresentationTime();

    unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();

    fOutBuf->useOverflowData();

 

    afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);

  } else {

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

    if (fSource == NULL) return;

    //更新缓冲区的位置信息

    fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();

    fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();

    fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);

    fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;

   

//再次从source要数据, fOutBuf->curPtr()表示数据存放的起始Buff地址;第2个参数表示Buff可用缓冲区的size,afterGettingFrame为收到一帧数据的回调函数,对该帧数据进行处理;ourHandleClosure在关闭文件时调用该函数;

    fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),

                afterGettingFrame, this, ourHandleClosure,this);

  }

}

GetNextFrame函数就是Source读文件或某个设备(比如IP Camera)中读取一帧数据,读完后返回给Sink,然后调用afterGettingFrame函数;

 

下面接着讲解getNextFrame函数;

void FramedSource::getNextFrame(unsignedchar* to,unsigned maxSize,

                   afterGettingFunc* afterGettingFunc,

                   void* afterGettingClientData,

                   onCloseFunc* onCloseFunc,

                   void* onCloseClientData) {

  // Make sure we're not already being read:

  if (fIsCurrentlyAwaitingData) {

    envir() << "FramedSource[" <<this <<"]::getNextFrame(): attempting to read more than once at the same time!\n";

    envir().internalError();

  }

 

//保存一些变量

  fTo = to;

  fMaxSize = maxSize;

  fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()

  fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()

  fAfterGettingFunc = afterGettingFunc;

  fAfterGettingClientData = afterGettingClientData;

  fOnCloseFunc = onCloseFunc;

  fOnCloseClientData = onCloseClientData;

  fIsCurrentlyAwaitingData = True;

 

  doGetNextFrame();

}

 

调用doGetNextFrame()函数取下一帧数据;

H264FUAFragmenter类是H264VideoRTPSink的中调用,为H264VideoRTPSink的一个成员变量,H264VideoRTPSink继承自VideoRTPSink,而VideoRTPSink又继承自MultiFramedRTPSink;MultiFramedRTPSink继承自MediaSink;H264FUAFragmenter类取代了H264VideoStreamFramer成为和RTPSink的source,RTPSink要获取数据帧时,从H264FUAFragmenter获取。

 

void H264FUAFragmenter::doGetNextFrame() {

  if (fNumValidDataBytes == 1) {

// We have no NAL unit data currently in the buffer. Read a new one:

//buff中没有数据,则调用fInputSource->getNextFrame函数从source中获取数据;

//fInputSource为H264VideoStreamFramer,H264VideoStreamFramer的getNextFrame()会调用H264VideoStreamParser的parser(),parser()又调用ByteStreamFileSource获取数据;

    fInputSource->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,

                     afterGettingFrame, this,                       

                     FramedSource::handleClosure, this);

  } else {

    // We have NAL unit data in the buffer. There are three cases to consider:

    // 1. There is a new NAL unit in the buffer, and it's small enough to deliver

    //    to the RTP sink (as is).

    // 2. There is a new NAL unit in the buffer, but it's too large to deliver to

    //    the RTP sink in its entirety.  Deliver the first fragment of this data,

    //    as a FU-A packet, with one extra preceding header byte.

    // 3. There is a NAL unit in the buffer, and we've already delivered some

    //    fragment(s) of this.  Deliver the next fragment of this data,

    //    as a FU-A packet, with two extra preceding header bytes.

 

    if (fMaxSize < fMaxOutputPacketSize) {// shouldn't happen

      envir() << "H264FUAFragmenter::doGetNextFrame(): fMaxSize ("

           << fMaxSize << ") is smaller than expected\n";

    } else {

      fMaxSize = fMaxOutputPacketSize;

    }

 

fLastFragmentCompletedNALUnit = True; // by default

//1)非分片包

    if (fCurDataOffset == 1) { // case 1 or 2

      if (fNumValidDataBytes - 1 <= fMaxSize) {// case 1

     memmove(fTo, &fInputBuffer[1], fNumValidDataBytes - 1);

     fFrameSize = fNumValidDataBytes - 1;

     fCurDataOffset = fNumValidDataBytes;

      } else { // case 2

     // We need to send the NAL unit data as FU-A packets. Deliver the first

     // packet now.  Note that we add FU indicator and FU header bytes to the front

     // of the packet (reusing the existing NAL header byte for the FU header).

    //2)为FU-A的第一个包

     fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator

     fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // FU header (with S bit)

     memmove(fTo, fInputBuffer, fMaxSize);

     fFrameSize = fMaxSize;

     fCurDataOffset += fMaxSize - 1;

     fLastFragmentCompletedNALUnit = False;

      }

    } else { // case 3

      // We are sending this NAL unit data as FU-A packets. We've already sent the

      // first packet (fragment).  Now, send the next fragment.  Note that we add

      // FU indicator and FU header bytes to the front. (We reuse these bytes that

      // we already sent for the first fragment, but clear the S bit, and add the E

      //3) bit if this is the last fragment.)

      //为FU-A的中间的包,复用FU indicator and FU header,清除掉FU header (no S bit开始标记)

      fInputBuffer[fCurDataOffset-2] = fInputBuffer[0]; // FU indicator

      fInputBuffer[fCurDataOffset-1] = fInputBuffer[1]&~0x80; // FU header (no S bit)

      unsigned numBytesToSend = 2 + fNumValidDataBytes - fCurDataOffset;

      if (numBytesToSend > fMaxSize) {

     // We can't send all of the remaining data this time:

     numBytesToSend = fMaxSize;

     fLastFragmentCompletedNALUnit = False;

      } else {

     // This is the last fragment:

     //4)这是FU(分片包28)的最后一个包了,将FU头部的设置成E表示End,方便客户端组帧

     fInputBuffer[fCurDataOffset-1] |= 0x40; // set the E bit in the FU header

     fNumTruncatedBytes = fSaveNumTruncatedBytes;

      }

      memmove(fTo, &fInputBuffer[fCurDataOffset-2], numBytesToSend);

      fFrameSize = numBytesToSend;

      fCurDataOffset += numBytesToSend - 2;

    }

 

    if (fCurDataOffset >= fNumValidDataBytes) {

      // We're done with this data.  Reset the pointers for receiving new data:

      fNumValidDataBytes = fCurDataOffset = 1;

    }

 

    // Complete delivery to the client:

    FramedSource::afterGetting(this);

  }

}

 

该函数的else部分实现RTP数据打包工作;live555只处理2种包;单独的包,比如sps,pps信息,一个包就是一个数据帧,2)包很大,拆包,采用FU-A的方法拆包,参考RTP打包协议!

 http://blog.csdn.net/smilestone_322/article/details/7574253

//fInputSource->getNextFrame()后调用回调函数:

void H264FUAFragmenter::afterGettingFrame(void* clientData,unsigned frameSize,

                         unsigned numTruncatedBytes,

                         struct timeval presentationTime,

                         unsigned durationInMicroseconds) {

  H264FUAFragmenter* fragmenter = (H264FUAFragmenter*)clientData;

  fragmenter->afterGettingFrame1(frameSize, numTruncatedBytes, presentationTime,

                    durationInMicroseconds);

}

 

void H264FUAFragmenter::afterGettingFrame1(unsigned frameSize,

                          unsigned numTruncatedBytes,

                          struct timeval presentationTime,

                          unsigned durationInMicroseconds) {

  fNumValidDataBytes += frameSize;

  fSaveNumTruncatedBytes = numTruncatedBytes;

  fPresentationTime = presentationTime;

  fDurationInMicroseconds = durationInMicroseconds;

 

  // Deliver data to the client:

  doGetNextFrame();

}

 

doGetNextFrame();获取到一帧数据后,就打包将数据发送给客户端;调用H264FUAFragmenter的doGetNextFrame()函数,对数据进行分析处理;这时走的doGetNextFrame()的else部分;

 

afterGettingFrame函数的源码如下:

void MultiFramedRTPSink

::afterGettingFrame(void* clientData,unsigned numBytesRead,

             unsigned numTruncatedBytes,

             struct timeval presentationTime,

             unsigned durationInMicroseconds) {

  MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData;

  sink->afterGettingFrame1(numBytesRead, numTruncatedBytes,

                 presentationTime, durationInMicroseconds);

}

 

 

afterGettingFrame又调用afterGettingFrame1来消费数据,afterGettingFrame1我猜是发送数据;

看源码;

void MultiFramedRTPSink

::afterGettingFrame1(unsigned frameSize,unsigned numTruncatedBytes,

              struct timeval presentationTime,

              unsigned durationInMicroseconds) {

  if (fIsFirstPacket) {

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

    gettimeofday(&fNextSendTime, NULL);

  }

 

  fMostRecentPresentationTime = presentationTime;

  if (fInitialPresentationTime.tv_sec == 0 && fInitialPresentationTime.tv_usec == 0) {

    fInitialPresentationTime = presentationTime;

  }   

 

  if (numTruncatedBytes > 0) {

    unsigned const bufferSize = fOutBuf->totalBytesAvailable();

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

         << bufferSize << ").  "

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

         << OutPacketBuffer::maxSize + numTruncatedBytes << ", *before* creating this 'RTPSink'.  (Current value is "

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

  }

  unsigned curFragmentationOffset = fCurFragmentationOffset;

  unsigned numFrameBytesToUse = frameSize;

  unsigned overflowBytes = 0;

 

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

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

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

  // new frame; that check comes later.)

  if (fNumFramesUsedSoFar > 0) {

    if ((fPreviousFrameEndedFragmentation

      && !allowOtherFramesAfterLastFragment())

     || !frameCanAppearAfterPacketStart(fOutBuf->curPtr(), frameSize)) {

      // Save away this frame for next time:

      numFrameBytesToUse = 0;

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

                     presentationTime, durationInMicroseconds);

    }

  }

  fPreviousFrameEndedFragmentation = False;

 

//缓冲区太小了,数据帧被截断了,提示用户增加缓冲区大小

  if (numFrameBytesToUse > 0) {

    // Check whether this frame overflows the packet

    if (fOutBuf->wouldOverflow(frameSize)) {

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

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

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

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

      if (isTooBigForAPacket(frameSize)

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

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

        overflowBytes = computeOverflowForNewFrame(frameSize);

        numFrameBytesToUse -= overflowBytes;

        fCurFragmentationOffset += numFrameBytesToUse;

      } else {

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

        overflowBytes = frameSize;

        numFrameBytesToUse = 0;

      }

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

                     overflowBytes, presentationTime, durationInMicroseconds);

    } else if (fCurFragmentationOffset > 0) {

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

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

      fCurFragmentationOffset = 0;

      fPreviousFrameEndedFragmentation = True;

    }

  }

 

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

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

//发送数据包

    sendPacketIfNecessary();

  } else {

    // Use this frame in our outgoing packet:

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

    fOutBuf->increment(numFrameBytesToUse);

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

 

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

    doSpecialFrameHandling(curFragmentationOffset, frameStart,

                 numFrameBytesToUse, presentationTime,

                 overflowBytes);

 

    ++fNumFramesUsedSoFar;

 

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

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

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

// count its duration yet.

//更新时间戳

    if (overflowBytes == 0) {

      fNextSendTime.tv_usec += durationInMicroseconds;

      fNextSendTime.tv_sec += fNextSendTime.tv_usec/1000000;

      fNextSendTime.tv_usec %= 1000000;

    }

 

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

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

    //      read would overflow the packet, or

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

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

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

//1)数据包的size已经是一个恰当的大小了,在往里面打包数据可能造成缓冲区溢出了;

//2)已经包括了分片包的最后一个包了;

//3)容许一帧一个数据包

    if (fOutBuf->isPreferredSize()

        || fOutBuf->wouldOverflow(numFrameBytesToUse)

        || (fPreviousFrameEndedFragmentation &&

            !allowOtherFramesAfterLastFragment())

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

                          frameSize) ) {

      // The packet is ready to be sent now

      //发送数据包

      sendPacketIfNecessary();

    } else {

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

     //继承打包

      packFrame();

    }

  }

}

 

下面继续看发送数据的函数:

void MultiFramedRTPSink::sendPacketIfNecessary() {

  if (fNumFramesUsedSoFar > 0) {

    // Send the packet:

#ifdef TEST_LOSS

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

#endif

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

     // if failure handler has been specified, call it

     if (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);

      }

    ++fPacketCount;

    fTotalOctetCount += fOutBuf->curPacketSize();

    fOctetCount += fOutBuf->curPacketSize()

      - rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;

 

    ++fSeqNo; // for next time

  }

 

  if (fOutBuf->haveOverflowData()

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

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

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

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

    // into place when building the next packet:

    unsigned newPacketStart = fOutBuf->curPacketSize()

      - (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());

    fOutBuf->adjustPacketStart(newPacketStart);

  } else {

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

    fOutBuf->resetPacketStart();

  }

  fOutBuf->resetOffset();

  fNumFramesUsedSoFar = 0;

 

  if (fNoFramesLeft) {

    // We're done:

    onSourceClosure(this);

  } else {

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

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

    // sending the next packet.

    struct timeval timeNow;

    gettimeofday(&timeNow, NULL);

    int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;

    int64_t uSecondsToGo = secsDiff*1000000 + (fNextSendTime.tv_usec - timeNow.tv_usec);

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

      uSecondsToGo = 0;

    }

 

    // Delay this amount of time:

    nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext,this);

  }

}

 

在发送数据的函数中使用延迟任务,为了延迟包的发送,使用delay task来执行下次打包发送任务,看sendNext的代码;

void MultiFramedRTPSink::sendNext(void* firstArg) {

  MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;

  sink->buildAndSendPacket(False);

}

它又调用了buildAndSendPacket函数,看下该函数参数的作用,True和False的区别;True表示该帧是第一帧,记下实际Play的时间;在afterGettingFrame1中有如下代码:

  if (fIsFirstPacket) {

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

    gettimeofday(&fNextSendTime, NULL);

  }

 

在MultiFramedRTPSink中数据包和帧的缓冲区队列是同一个,使用了一些标记和对指针的移动来操作数据打包和发送数据帧;注意:如果数据帧溢出,时间戳会计算不准确;

 

from:http://blog.csdn.net/smilestone_322/article/details/18923711

目录
相关文章
|
视频直播
视频直播源码技术知识分享:连麦功能(一)
我们开发视频直播平台就要去了解视频直播开发相关功能知识,这对我们开发平台有着重要的作用,连麦技术就是视频直播源码重要的技术功能之一,每一个功能技术都有自己的用武之地
视频直播源码技术知识分享:连麦功能(一)
|
视频直播
详解!视频直播源码平台搭建开发:录制功能
视频直播源码平台的录制功能能够为用户提供更多方便、灵活性,同时也增加了直播内容的传播和价值,这也使录制功能成为布谷科技视频直播源码平台的必备功能之一。
详解!视频直播源码平台搭建开发:录制功能
|
视频直播 定位技术 UED
海外视频直播源码技术视频直播间的搭建
大家听到这里是不是感觉这个源码平台非常的熟悉,没错,这个源码平台就海外视频直播源码平台,而我说的这个房间就是视频直播间,主持人就是视频直播间内的主播,今天我就为大家分享海外视频直播源码技术视频直播间的搭建。
海外视频直播源码技术视频直播间的搭建
|
域名解析 网络协议 安全
Live555源码阅读笔记(四):groupsock 目录详解
Live555源码阅读笔记(四):groupsock 目录详解
349 0
Live555源码阅读笔记(四):groupsock 目录详解
|
存储 调度
Live555源码阅读笔记(三):BasicUsageEnvironment 目录详解-BasicTaskScheduler、DelayQueue、HandlerSet
Live555源码阅读笔记(三):BasicUsageEnvironment 目录详解-BasicTaskScheduler、DelayQueue、HandlerSet
174 0
Live555源码阅读笔记(三):BasicUsageEnvironment 目录详解-BasicTaskScheduler、DelayQueue、HandlerSet
|
调度 C++
Live555源码阅读笔记(二):UsageEnvironment 目录详解
Live555源码阅读笔记(二):UsageEnvironment 目录详解
157 0
Live555源码阅读笔记(二):UsageEnvironment 目录详解
|
视频直播
新人主播如何赚钱,3点告诉你视频直播源码的变现方式
全民直播的时代已经到来抓住,绝大多数人是发现了直播市场的红利期,想要分一杯羹,作为新人主播如何通过视频直播源码的功能来赚钱,首先要了解市场需求,明确自己的优势,凸显自己的竞争力。
新人主播如何赚钱,3点告诉你视频直播源码的变现方式
|
存储 缓存 NoSQL
开发视频直播系统,视频直播系统源码是核心技术
了解视频直播行业的朋友应该清楚,对于开发视频直播系统而言,视频直播系统源码是真正的核心技术
开发视频直播系统,视频直播系统源码是核心技术
|
搜索推荐 视频直播
视频直播源码提高用户满意度必须要具有的特性
目前直播app应用开发市场已进入快速发展时期,不仅越来越多的直播app应用陆续投放到应用市场中,同时用户对于手机直播app应用软件的要求也越来越高。随着年轻用户群体的个性化需求提升,如何提高用户满意度,便成为了视频直播源码的开发最为关键的问题之一。  
视频直播源码提高用户满意度必须要具有的特性
|
视频直播
直播系统的顶梁柱:视频直播源码中的礼物功能
我们了解的直播系统功能是十分的实用且丰富的,一般来说,视频直播源码自带的功能有:直播功能、私信功能、邀请奖励、财务管理功能、短视频功能、直播PK功能、多种礼物功能、分享功能、拉黑功能、带货商城功能、家族功能、标签功能、评论功能等等实质性的功能,今天就针对礼物功能来做一下详细的介绍。
直播系统的顶梁柱:视频直播源码中的礼物功能