首页 > 代码库 > vlc源码分析之调用live555接收RTSP数据
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 = http://www.mamicode.com/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 =http://www.mamicode.com/ 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数据