vlc源碼分析之調用live555接收RTSP數據
首先了解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數據