1. 程式人生 > >vlc源碼分析之調用live555接收RTSP數據

vlc源碼分析之調用live555接收RTSP數據

sca loss 問題 賦值 mage 轉載 linda 結構 mex

  首先了解RTSP/RTP/RTCP相關概念,尤其是了解RTP協議:RTP與RTCP協議介紹(轉載)。

  vlc使用模塊加載機制調用live555,調用live555的文件是live555.cpp。

一、幾個重要的類  

  以下向左箭頭(“<-”)為繼承關系。

1. RTPInterface

  RTPInterface是RTPSource的成員變量,其成員函數handleRead會讀取網絡數據存入BufferedPacket內,該類最終會調到UDP的發送接收函數。

Boolean RTPInterface::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
                 unsigned
& bytesRead, struct sockaddr_in& fromAddress, Boolean& packetReadWasIncomplete)

2. BufferedPacket

  BufferedPacket:用於存儲媒體的RTP數據包

  BufferedPacket<-H264BufferedPacket:用於存儲H264媒體RTP數據包

  該類有一個重要函數fillInData,是由RTPInterface讀取數據存入包中。

Boolean BufferedPacket::fillInData(RTPInterface& rtpInterface, Boolean& packetReadWasIncomplete);

  相對於BufferedPacket,有對應的工廠類:

  BufferedPacketFactory:工廠模式生成BufferedPacket包
  BufferedPacketFactory<-H264BufferedPacketFactory:專門生產H264BufferedPacket的工廠

  在SessionsSetup的時候(也是模塊加載的時候),會根據Source類型,選定生產BufferedPacket的工廠類型,即如果Source是H264格式的話,就會new H264BufferedPacketFactory,之後在接收數據的時候就會生產H264BufferedPacket用於存儲H264媒體數據。

  ReorderingPacketBuffer:MultiFramedRTPSource的成員變量,用於管理多個BufferedPacket。

3. Source相關類  

  Source相關類的繼承關系:Medium<-MediaSource<-FramedSource<-RTPSource<-MultiFramedRTPSource<-H264VideoRTPSource。
  在SessionsSetup的時候,會根據數據源的類型,選定Source的類型,即如果數據源是H264格式的話,就會調用

static H264VideoRTPSource* createNew(UsageEnvironment& env, Groupsock* RTPgs,
  unsigned char rtpPayloadFormat,
  unsigned rtpTimestampFrequency = 90000);

二、播放流程的建立

  播放流程的建立可以參考vlc源碼分析之播放流程。

三、接收RTSP數據

  vlc在播放IPC時,會開啟一個線程調用Demux()。Demux()首先將必要的接口,如StreamRead、StreamClose註冊下去,然後就進入事件循環:

p_sys->scheduler->doEventLoop( &p_sys->event_data );

  如果有網絡數據到來了,Demux()會做兩件事,第一件事是分析RTP包,放入ReorderingPacketBuffer管理的BufferedPacket中,堆棧如下圖所示:

技術分享  第二件事是讀取的BufferedPacket,進行一系列拆包操作後,將數據放入數據fifo中,堆棧如下圖所示:

技術分享

  doEventLoop會進入死循環,直到p_sys->event_data的值被中斷或者超時改變,從而退出循環。當有網絡數據到來的時候,doEventLoop會執行SingleStep->...->doGetNextFrame1(),在doGetNextFrame1()函數中讀取RTP數據。這個過程的代碼及註釋如下:

// 做了兩件事,一件是分析RTP包,放入ReorderingPacketBuffer管理的BufferedPacket中;
// 另一件是讀取的BufferedPacket,進行一系列拆包操作後,將數據放入數據fifo中
void MultiFramedRTPSource::networkReadHandler1() {
  BufferedPacket* bPacket = fPacketReadInProgress;
  if (bPacket == NULL) {
    // Normal case: Get a free BufferedPacket descriptor to hold the new network packet:
    bPacket = fReorderingBuffer->getFreePacket(this);
  }

  // Read the network packet, and perform sanity checks on the RTP header:
  Boolean readSuccess = False;
  // do-while(0)結構,出現錯誤直接break
  do {
    Boolean packetReadWasIncomplete = fPacketReadInProgress != NULL;
    if (!bPacket->fillInData(fRTPInterface, packetReadWasIncomplete)) break;
    if (packetReadWasIncomplete) {
      // We need additional read(s) before we can process the incoming packet:
      fPacketReadInProgress = bPacket;
      return;
    } else {
      fPacketReadInProgress = NULL;
    }
#ifdef TEST_LOSS
    setPacketReorderingThresholdTime(0);
       // don‘t wait for ‘lost‘ packets to arrive out-of-order later
    if ((our_random()%10) == 0) break; // simulate 10% packet loss
#endif

    // Check for the 12-byte RTP header:
    if (bPacket->dataSize() < 12) break;
    // 讀取RTP頭,向前移4個字節
    unsigned rtpHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
    // 讀取RTP頭中的標記位
    Boolean rtpMarkerBit = (rtpHdr&0x00800000) != 0;
    // 讀取時間戳,向前移4個字節
    unsigned rtpTimestamp = ntohl(*(u_int32_t*)(bPacket->data()));ADVANCE(4);
    // 讀取SSRC,向前移4個字節
    unsigned rtpSSRC = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);

    // Check the RTP version number (it should be 2):
    // 檢查RTP頭版本,不是2的話,break
    if ((rtpHdr&0xC0000000) != 0x80000000) break;

    // Skip over any CSRC identifiers in the header:
    // 跳過CSRC計數字節
    unsigned cc = (rtpHdr>>24)&0xF;
    if (bPacket->dataSize() < cc) break;
    ADVANCE(cc*4);

    // Check for (& ignore) any RTP header extension
    // 如果擴展頭標誌被置位
    if (rtpHdr&0x10000000) {
      if (bPacket->dataSize() < 4) break;
      // 獲取擴展頭
      unsigned extHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
      // 獲取擴展字節數
      unsigned remExtSize = 4*(extHdr&0xFFFF);
      if (bPacket->dataSize() < remExtSize) break;
      // 直接跳過擴展字節???
      ADVANCE(remExtSize);
    }

    // Discard any padding bytes:
    // 如果填充標誌被置位,直接丟棄不處理
    if (rtpHdr&0x20000000) {
      if (bPacket->dataSize() == 0) break;
      unsigned numPaddingBytes
    = (unsigned)(bPacket->data())[bPacket->dataSize()-1];
      if (bPacket->dataSize() < numPaddingBytes) break;
      bPacket->removePadding(numPaddingBytes);
    }
    // Check the Payload Type.
    // 檢查載荷類型,如果源數據H264類型,則其值為96
    // 如果與我們生成的source類型不同,則break
    if ((unsigned char)((rtpHdr&0x007F0000)>>16)
    != rtpPayloadFormat()) {
      break;
    }

    // The rest of the packet is the usable data.  Record and save it:
    if (rtpSSRC != fLastReceivedSSRC) {
      // The SSRC of incoming packets has changed.  Unfortunately we don‘t yet handle streams that contain multiple SSRCs,
      // but we can handle a single-SSRC stream where the SSRC changes occasionally:
      fLastReceivedSSRC = rtpSSRC;
      fReorderingBuffer->resetHaveSeenFirstPacket();
    }
    // RTP包序號,隨RTP數據包而自增,由接收者用來探測包損失
    unsigned short rtpSeqNo = (unsigned short)(rtpHdr&0xFFFF);
    Boolean usableInJitterCalculation
      = packetIsUsableInJitterCalculation((bPacket->data()),
                          bPacket->dataSize());
    struct timeval presentationTime; // computed by:
    Boolean hasBeenSyncedUsingRTCP; // computed by:
    // 根據數據包的一些信息,進行一些計算和記錄
    receptionStatsDB()
      .noteIncomingPacket(rtpSSRC, rtpSeqNo, rtpTimestamp,
              timestampFrequency(),
              usableInJitterCalculation, presentationTime,
              hasBeenSyncedUsingRTCP, bPacket->dataSize());

    // Fill in the rest of the packet descriptor, and store it:
    struct timeval timeNow;
    gettimeofday(&timeNow, NULL);
    // 將計算所得的一些參數再賦值到包中
    bPacket->assignMiscParams(rtpSeqNo, rtpTimestamp, presentationTime,
                  hasBeenSyncedUsingRTCP, rtpMarkerBit,
                  timeNow);
    // 經過以上判斷和檢查,沒有發現問題,則由管理類fReorderingBuffer存儲包
    if (!fReorderingBuffer->storePacket(bPacket)) break;

    readSuccess = True;// 讀取成功
  } while (0);
  if (!readSuccess) fReorderingBuffer->freePacket(bPacket);// 如果讀取不成功,則釋放內存

  // 將讀取到的數據包送至數據fifo中,等待解碼線程解碼
  doGetNextFrame1();
  // If we didn‘t get proper data this time, we‘ll get another chance
}

  將讀取到的數據包送至數據fifo中,之後就是等待解碼線程從數據fifo中拿數據,解碼和渲染了,具體可參考vlc源碼分析之播放流程。

  附:

  配置好的Windows版vlc工程下載:https://github.com/jiayayao/vlc_2.1.0-vs_2010,下載後使用vs2010可以直接編譯運行,調試學習非常方便。

vlc源碼分析之調用live555接收RTSP數據