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可以直接编译运行,调试学习非常方便。

时间: 2024-10-25 09:22:13

vlc源码分析之调用live555接收RTSP数据的相关文章

vlc源码分析(七) 调试学习HLS协议

HTTP Live Streaming(HLS)是苹果公司提出来的流媒体传输协议.与RTP协议不同的是,HLS可以穿透某些允许HTTP协议通过的防火墙. 一.HLS播放模式 (1) 点播模式(Video on demand, VOD) 点播模式是指当前时间点可以获取到所有index文件和ts文件,二级index文件中记录了所有ts文件的地址.这种模式允许客户端访问全部内容.上面的例子中就是一个点播模式下的m3u8的结构. (2) 直播模式(Live) 直播模式是指实时生成M3u8和ts文件.它的

VLC源码分析知识总结

1.  关于#和## 1.1).在C语言的宏中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号. 比如在早期的VLC版本中,有如下宏定义: [cpp] view plaincopy #define STRINGIFY(z)   UGLY_KLUDGE(z) #define UGLY_KLUDGE(z)  #z #define MODULE_STRING  STRINGIFY(MODULE_NAME) 而

VLC源码分析总结 ——入门纲领

http://blog.chinaunix.net/uid-24951403-id-3022939.html VLC源码分析总结 1. 概述 VLC属于Video LAN开源项目组织中的一款全开源的流媒体服务器和多媒体播放器.作为流媒体服务器,VLC跨平台,支持多操作系统和计算机体系结构:作为多媒体播放器,VLC可以播放多种格式的媒体文件.主要包括有:WMV.ASF.MPG.MP.AVI.H.264等多种常见媒体格式. VLC采用全模块化结构,在系统内部,通过动态的载入所需的模块,放入一个mod

调用Live555接收RTSP直播流,转换为Http Live Streaming(iOS直播)协议

Live555接收RTSP直播流,转换Http Live Streaming(iOS直播)协议 RTSP协议也是广泛使用的直播/点播流媒体协议,之前实现过一个通过live555接收RTSP协议,然后转换为HLS(Http Live Streaming)直播协议文件的程序,为的是可以接收远端设备或服务器的多路RTSP直播数据,实时转换为HLS协议文件,以实现iPhone或iPad等设备观看RTSP直播源的需求.现在把实现的思路分享如下. 要点分析 首先,程序的主要目的,是从多路RTSP输入源中提取

vlc源码分析(六) 调用OpenMAX硬解码H.265

http://www.cnblogs.com/jiayayao/p/6964506.html H.265(HEVC)编码格式能够在得到相同编码质量视频的前提下,使用相当于H.264(AVC)一半的存储容量,虽然H.265的算法复杂度比H.264高一个数量级,但是硬件水平在不断提高,因此H.265使用场合逐渐多了起来.好多硬件厂商芯片内部实现了H.265的硬解码.最近调试了vlc-android调用OpenMAX硬解码H.265的部分,使用的硬件平台是ZX-2000,系统是Android5.1.

vlc源码分析(一) vlc-android native调试配置

http://www.cnblogs.com/jiayayao/p/6770479.html 1, 按照官网给出的链接,git clone代码,配置好android sdk,ndk...后运行compile.sh; 2, 出现一个protobuf相关的问题(貌似要求protobuf3),自己去网站下载后安装,即可编译通过; 3, 还是找个能上外网的,或者能FQ的帐号吧,编译过程中会下载好多与google相关的软件,为了避免出现意想不到的错误,你懂的; 4, 配置vlc-android:点击Edi

Spring IOC 容器源码分析 - getBean调用方法解析(三) -- 实例化 Bean 对象

1. createBeanInstance protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // 解析 bean ,将 bean 类名解析为 class 引用 Class<?> beanClass = resolveBeanClass(mbd, beanName); /* * 检测类的访问权限.默认情况下,对于非 public

vlc源码分析(五) 流媒体的音视频同步

http://www.cnblogs.com/jiayayao/p/6890882.html vlc播放流媒体时实现音视频同步,简单来说就是发送方发送的RTP包带有时间戳,接收方根据此时间戳不断校正本地时钟,播放音视频时根据本地时钟进行同步播放.首先了解两个概念:stream clock和system clock.stream clock是流时钟,可以理解为RTP包中的时间戳:system clock是本地时钟,可以理解为当前系统的Tick数.第一个RTP包到来时: fSyncTimestamp

vlc源码分析(二) 播放流程

http://www.cnblogs.com/jiayayao/p/6752388.html 当点击播放文件或者输入要播放的文件后,vlc会执行一系列的流程. 首先需要了解视频以及流媒体处理及播放的流程,由链接中的描述,视频以及流媒体处理时,首先要解协议(http,rtmp,rtsp等),然后是解封装(mp4,mkv,avi等),获得音频码流和视频码流,分别解码后,再根据时间戳进行同步.使用vlc点击播放后,就是这一系列的流程. 点击播放支持RTSP协议的摄像头后,vlc会开启线程对输入的文件或