存档

文章标签 ‘rtsp’

基于liveMedia的RTSP/RTP编程

2012年4月23日
基于liveMedia的RTSP/RTP编程已关闭评论

 

liveMedia项目的源代码包括四个基本的库,各种测试代码以及IVE555 Media Server。
四个基本的库分别是UsageEnvironment&TaskScheduler,groupsock,liveMedia,BasicUsageEnvironment。
UsageEnvironment 和TaskScheduler类用于事件的调度,实现异步读取事件的句柄的设置以及错误信息的输出。另外,还有一个HashTable类定义了一个通用的 hash表,其它代码要用到这个表。这些都是抽象类,在应用程序中基于这些类实现自己的子类。
groupsock类是对网络接口的封装,用于收发数据包。正如名字本身,Groupsock主要是面向多播数据的收发的,它也同时支持单播数据的收发。Groupsock定义了两个构造函数
    Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
              Port port, u_int8_t ttl);
    Groupsock(UsageEnvironment& env, struct in_addr const& groupAddr,
              struct in_addr const& sourceFilterAddr,
              Port port);
前 者是用于SIM(source-independent multicast)组,后者用于SSM(source-specific multicast)组。groupsock库中的Helper例程提供了读写socket等函数,并且屏蔽了不同的操作系统之间的区别,这是在 GroupsockHelper.cpp文件中实现的。
liveMedia库中有一系列类,基类是Medium,这些类针对不同的流媒体类型和编码。
各种测试代码在testProgram目录下,比如openRTSP等,这些代码有助于理解liveMedia的应用。
LIVE555 Media Server是一个纯粹的RTSP服务器。支持多种格式的媒体文件:
      * TS流文件,扩展名ts。
      * PS流文件,扩展名mpg。
      * MPEG-4视频基本流文件,扩展名m4e。
      * MP3文件,扩展名mp3。
      * WAV文件(PCM),扩展名wav。
      * AMR音频文件,扩展名.amr。
      * AAC文件,ADTS格式,扩展名aac。 
用live555开发应用程序
基 于liveMedia的程序,需要通过继承UsageEnvironment抽象类和TaskScheduler抽象类,定义相应的类来处理事件调度,数 据读写以及错误处理。live项目的源代码里有这些类的一个实现,这就是“BasicUsageEnvironment”库。 BasicUsageEnvironment主要是针对简单的控制台应用程序,利用select实现事件获取和处理。这个库利用Unix或者 Windows的控制台作为输入输出,处于应用程序原形或者调试的目的,可以用这个库用户可以开发传统的运行与控制台的应用。 
通过使 用自定义的“UsageEnvironment”和“TaskScheduler”抽象类的子类,这些应用程序就可以在特定的环境中运行,不需要做过多的 修改。需要指出的是在图形环境(GUI toolkit)下,抽象类 TaskScheduler 的子类在实现 doEventLoop()的时候应该与图形环境自己的事件处理框架集成。
先来熟悉在liveMedia库中Source,Sink以及 Filter等概念。Sink就是消费数据的对象,比如把接收到的数据存储到文件,这个文件就是一个Sink。Source就是生产数据的对象,比如通过 RTP读取数据。数据流经过多个’source’和’sink’s,下面是一个示例:
      ‘source1’ -> ‘source2’ (a filter) -> ‘source3’ (a filter) -> ‘sink’
从其它Source接收数据的source也叫做"filters"。Module是一个sink或者一个filter。
数据接收的终点是Sink类,MediaSink是所有Sink类的基类。MediaSink的定义如下:
class MediaSink: public Medium {
public:
    static Boolean lookupByName(UsageEnvironment& env, char const* sinkName,
                                MediaSink*& resultSink);
    typedef void (afterPlayingFunc)(void* clientData);
    Boolean startPlaying(MediaSource& source,
                         afterPlayingFunc* afterFunc,
                         void* afterClientData);
    virtual void stopPlaying();
    // Test for specific types of sink:
    virtual Boolean isRTPSink() const;
    FramedSource* source() const {return fSource;}
protected:
    MediaSink(UsageEnvironment& env); // abstract base class
    virtual ~MediaSink();
    virtual Boolean sourceIsCompatibleWithUs(MediaSource& source);
        // called by startPlaying()
    virtual Boolean continuePlaying() = 0;
        // called by startPlaying()
    static void onSourceClosure(void* clientData);
        // should be called (on ourselves) by continuePlaying() when it
        // discovers that the source we’re playing from has closed.
    FramedSource* fSource;
private:
    // redefined virtual functions:
    virtual Boolean isSink() const;
private:
    // The following fields are used when we’re being played:
    afterPlayingFunc* fAfterFunc;
    void* fAfterClientData;
};
Sink 类实现对数据的处理是通过实现纯虚函数continuePlaying(),通常情况下continuePlaying调用 fSource->getNextFrame来为Source设置数据缓冲区,处理数据的回调函数等,fSource是MediaSink的类型为 FramedSource*的类成员;
基于liveMedia的应用程序的控制流程如下:
应用程序是事件驱动的,使用如下方式的循环
      while (1) {
          通过查找读网络句柄的列表和延迟队列(delay queue)来发现需要完成的任务
          完成这个任务
      }
对于每个sink,在进入这个循环之前,应用程序通常调用下面的方法来启动需要做的生成任务:
      someSinkObject->startPlaying();
任何时候,一个Module需要获取数据都通过调用刚好在它之前的那个Module的FramedSource::getNextFrame()方法。这是通过纯虚函数FramedSource:doGetNextFrame()实现的,每一个Source module都有相应的实现。
Each ‘source’ module’s implementation of "doGetNextFrame()" works by arranging for an ‘after getting’ function to be called (from an event handler) when new data becomes available for the caller.
注意,任何应用程序都要处理从’sources’到’sinks’的数据流,但是并非每个这样的数据流都与从网络接口收发数据相对应。
比 如,一个服务器应用程序发送RTP数据包的时候用到一个或多个"RTPSink" modules。这些"RTPSink" modules以别的方式接收数据,通常是文件 "*Source" modules (e.g., to read data from a file), and, as a side effect, transmit RTP packets. 
一个简单的RTSP客户端程序
在另一个文章里,给出了这个简单的客户端的程序的代码,可以通过修改Makefile来裁剪liveMedia,使得这个客户端最小化。此客户端已经正常运行。
首先是OPTION
然后是DESCRIBE
      建立Media Session,调用的函数是 MediaSession::createNew,在文件liveMedia/MediaSession.cpp中实现。
      为这个Media Session建立RTPSource,这是通过调用 MediaSubsession::initiate来实现的的,这个方法在liveMedia/MediaSession.cpp中实现。
在然后是SETUP
最后是PLAY
rtp数据的句柄:MultiFramedRTPSource::networkReadHandler 在liveMedia/MultiFramedRTPSource.cpp中
rtcp数据处理的句柄:RTCPInstance::incomingReportHandler 在liveMedia/RTCP.cpp中
rtp 数据处理的句柄的设置:MultiFramedRTPSource:doGetNextFrame 在liveMedia/MultiFramedRTPSource.cpp中, 被FileSink::continuePlaying调用在FileSink.cpp中.
rtcp数据处理的句柄设置fRTCPInstance = RTCPInstance::createNew 在/liveMedia/MediaSession.cpp中调用,
createNew调用了构造函数RTCPInstance::RTCPInstance,这个构造函数有如下调用
TaskScheduler::BackgroundHandlerProc* handler = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; 

*********************************************************************************************************************
通过分析live库提供的例子程序OpenRTSP,可以清晰地了解客户端接收来自网络上媒体数据的过程。注意,RTP协议和RTCP协议接收的数据分别是 视音频数据和发送/接收状况的相关信息,其中,RTP协议只负责接收数据,而RTCP协议除了接收服务器的消息之外,还要向服务器反馈。
A.        main函数流程
main(int argc,char *argv[])
{
1.            创建BasicTaskScheduler对象
2.            创建BisicUsageEnvironment对象
3.            分析argv参数,(最简单的用法是:openRTSP rtsp://172.16.24.240/mpeg4video.mp4)以便在下面设置一些相关参数
4.            创建RTSPClient对象
5.            由RTSPClient对象向服务器发送OPTION消息并接受回应
6.            产生SDPDescription字符串(由RTSPClient对象向服务器发送DESCRIBE消息并接受回应,根据回应的信息产生SDPDescription字符串,其中包括视音频数据的协议和解码器类型)
7.            创建MediaSession对象(根据SDPDescription在MediaSession中创建和初始化MediaSubSession子会话对象)
8.            while循环中配置所有子会话对象(为每个子会话创建RTPSource和RTCPInstance对象,并创建两个GroupSock对象,分别对应 RTPSource和RTCPInstance对象,把在每个GroupSock对象中创建的socket描述符置入 BasicTaskScheduler::fReadSet中,RTPSource对象的创建的依据是SDPDescription,例如对于MPEG4 文件来说,视音频RTPSource分别对应MPEG4ESVideoRTPSource和MPEG4GenericRTPSource对象。 RTCPInstance对象在构造函数中完成将Socket描述符、处理接收RTCP数据的函数 (RTCPInstance::incomingReportHandler)以及RTCPInstance本身三者绑定在一个 HandlerDescriptor对象中,并置入BasicTaskScheduler::fReadHandler中。完成绑定后会向服务器发送一条 消息。)
9.            由RTSPClient对象向服务器发送SETUP消息并接受回应。
10.        while循环中为每个子会话创建接收器(FileSink对象),在FileSink对象中根据子会话的codec等属性缺省产生记录视音频数据的文件 名,视音频文件名分别为:video-MP4V-ES-1和audio-MPEG4-GENERIC-2,无后缀名
11.        while循环中为每个子会话的视音频数据装配相应的接收函数,将每个子会话中的RTPSource中的GroupSock对象中的SOCKET描述符, 置入BasicTaskScheduler::fReadSet中,并将描述符、处理接收RTP数据的函数 (MultiFramedRTPSource::networkReadHandler)以及RTPSource本身三者绑定在一个 HandlerDescriptor对象中,并置入BasicTaskScheduler::fReadHandler中,并将FileSink的缓冲区 和包含写入文件操作的一个函数指针配置给RTPSource对象,这个缓冲区将会在networkReadHandler中接收来自网络的视音频数据(分 析和去掉RTP包头的工作由RTPSource完成),而这个函数指针在networkReadHandler中被调用以完成将缓冲区中的数据写入文件。
12.        由RTSPClient对象向服务器发送PLAY消息并接受回应。
13.        进入while循环,调用BasicTaskScheduler::SingleStep()函数接受数据,直到服务器发送TREADOWN消息给客户端,客户端接收到该消息后释放资源,程序退出。

}

IT技术 , , , ,

IP实时传输协议RTP/RTCP详解

2012年3月20日
IP实时传输协议RTP/RTCP详解已关闭评论

IP实时传输协议RTP/RTCP详解

1、简介

目前,在IP网络中实现实时语音、视频通信和应用已经成为网络应用的一个主流技术和发展方向,本文详细介绍IP协议族中用于实时语音、视频数据传输的标准协议RTP( Real-time Transport Protocol)和RTCP(RTP Control Ptotocol)的主要功能。

2、RTP/RTCP协议简介

      RTP 由 IETF(www.ietf.org)定义在 RFC 3550和3551中。

      RTP被定义为传输音频、视频、模拟数据等实时数据的传输协议,与传统的注重的高可靠的数据传输的运输层协议相比,它更加侧重的数据传输的实时性,此协议提供的服务包括数据顺序号、时间标记、传输控制等。

      RTP通常与辅助控制协议RTCP一起工作,RTP只负责实时数据的传输,RTCP负责对RTP的通信和会话进行带外管理(如流量控制、拥塞控制、会话源管理等)。

3、RTP/RTCP协议层次和封装

      RTP位于传输层(通常是UDP)之上,应用程序之下,实时语音、视频数据经过模数转换和压缩编码处理后,先送给RTP封装成为RTP数据单元,RTP数据单元被封装为UDP数据报,然后再向下递交给IP封装为IP数据包。

      RTP分组只包含RTP数据,而控制是由另一个配套协议RTCP提供。RTP在端口号1025到65535之间选择一个未使用的偶数UDP端口号,而在同一次会话中的RTCP则使用下一个奇数UDP端口号。

      RTP通常和RTCP一起工作,在RTP会话期间,各参与者周期的发送RTCP消息。RTCP消息含有已发送数据的丢包统计和网络拥塞等信息,服务器可以利用这些信息动态的改变传输速率,甚至改变净荷的类型。RTCP消息也被封装为UDP数据报进行传输。

4、RTP/RTCP协议头信息

      version (V): 2 bits    
标明RTP版本号。协议初始版本为0,RFC3550中规定的版本号为2。

       padding (P): 1 bit    
如果该位被设置,则在该packet末尾包含了额外的附加信息,附加信息的最后一个字节表示额外附加信息的长度(包含该字节本身)。该字段之所以存在是因为一些加密机制需要固定长度的数据块,或者为了在一个底层协议数据单元中传输多个RTP packets。

      extension (X): 1 bit
如果该位被设置,则在固定的头部后存在一个扩展头部,格式定义在RFC3550 5.3.1节。

      CSRC count (CC): 4 bits
在固定头部后存在多少个CSRC标记。

      marker (M): 1 bit
该位的功能依赖于profile的定义。profile可以改变该位的长度,但是要保持marker和payload type总长度不变(一共是8 bit)。

      payload type (PT): 7 bits
标记着RTP packet所携带信息的类型,标准类型列出在RFC3551中。如果接收方不能识别该类型,必须忽略该packet。

      sequence number: 16 bits
序列号,每个RTP packet发送后该序列号加1,接收方可以根据该序列号重新排列数据包顺序。

      timestamp: 32 bits
时间戳。反映RTP packet所携带信息包中第一个字节的采样时间。

      SSRC: 32 bits
数据源标识。在一个RTP Session其间每个数据流都应该有一个不同的SSRC。

      CSRC list: 0 to 15 items, 每个源标识32 bits
贡献数据源标识。只有存在Mixer的时候才有效。如一个将多声道的语音流合并成一个单声道的语音流,在这里就列出原来每个声道的SSRC。

5、RTCP协议

      RTCP协议处理机根据定义了五种类型的报文:
      RR: receiver report
      SR: sender report
      SDES: source description items.
      BYE: indicates end of participation.
      APP: application specific functions
它们完成接收、分析、产生和发送控制报文的功能。

      RTCP可以说是控制交通的协议,它提供了:

      1)SR发送者报告分组:用来使发送端周期的向所有接收端用多播方式进行报告。内容包括:

该RTP流的SSRC;该RTP流中最新产生的RTP分组的时间戳和绝对时钟时间(或称墙上时间:wall clock time);该RTP流包含的分组数;该RTP流包含的字节数。

绝对时钟时间是必要的。因为RTP要求每一种媒体使用一个流。有了绝对时钟时间就可以进行图形和声音的同步。

      2)RR接收者报告分组:用来使接收端周期性的向所有的点用多播方式进行报告。内容包括

所接收到的RTP流的SSRC;该RTP流的分组丢失率;在该RTP流中的最后一个RTP分组的序号;分组到达时间间隔的抖动等。

发送RR分组有两个目的。第一,可以使所有的接收端和发送端了解当前网络的状态。

第二,可以使所有发送RTCP分组的站点自适应的调整自己发送RTCP分组的速率,RTCP分组的通信量不超过网络中的数据分组的通信量的5%,而接收端分组报告分组的通信量又应小于所有RTCP分组的通信量的75%。

      3)SDES源描述分组:给出会话中参加者的描述,包括参加者的规范名(CNAME)

      4)BYE分组:关闭一个数据流。

      5)APP分组:应用程序能够定义新的分组类型。

6、实时流协议RTSP协议

      1) RTSP协议

          RTSP(Real Time Streaming Protocol)协议定义了如何有效地通过IP网络传送多媒体数据,是一种客户端到服务器端的多媒体描述协议,详见RFC2326。
          RTSP是一个非常类似于HTTP的应用层协议。每个发布和媒体文件也被定义为RTSP UPL。而媒体文件的发布信息被书写进一个被称为媒体发布文件里,这个文件在后面会说明。在这个文件说明的包括编码器,语言,RTSP ULS,地址,端口号以几其它参数。这个发布文件可以在客户端通过EMAIL形式或者HTTP形式获得。

      2) RTSP协议的特点:

      RTSP是应用层协议,与RTP、RSVP一起设计来完全流式服务。
      RTSP有很大的灵活性,可被用在多种操作系统上,它允许客户端和不同厂商的服务平台交互。
      RTSP在体系结构上位于RTP和RTCP之上,它使用RTP完成数据传输。它将流式媒体数据可控制的通过网络传输到客户端。
      RTSP可以保持用户计算机与传输流业务服务器之间的固定连接,用于观看者与单播(Unicast)服务器通信并且还允许双向通信,观看者可以同流媒体服务器通信.
提供类似“VCR”形式的例如暂停、快进、倒转、跳转等操作。操作的资源对象可以是直播流也可以是存储片段。
      RTSP是设还提供了选择传输通道,如使用UDP还是多点UDP或是TCP。

7、资源预留协议RSVP

      1) RSVP协议:
      RSVP (Resorce reSerVation Protocol) 资源预留协议并不是一个路由协议,而是一种IP网络中的信令协议,它与路由协议相结合来实现对网络传输服务质量(QoS)的控制。RSVP是为支持因特网综合业务而提出的。这是解决IP通信中QoS(服务质量)问题的一种技术,用来保证点端到端的传输带宽。

      2) RSVP协议是如何工作:
      RSVP使用控制数据报,这些数据报在向特定地址传输时包括了需要由路由器检查(有些时候需要更新)的信息,如果路由器需要决定是不是要检查数据报的内容的时候对上层数据内容进行语法分析。这种分析的代价可不小。现在的情况是,网络终端利用它向网络申请资源,在这种表明“申请” 的信号中,包含着如下的信息:业务的种类? 使用者类型? 什么时间?需要多大带宽? 其他参考信息? 网络在接收到上类信息后,会根据实际情况为此次连接分配一个优先代码,用户利用优先代码进行信息传递时,网络不需重新对业务进行分析与判别,从另外一个角度来说,利用RSVP 能从一定程度上减少网络对信息处理的时延,提高网络节点的工作效率,改善信息传输的服务质量(QoS)。实时应用用RSVP是为了在传输路径中保持必要的资源以保证请求能确保到达。
      RSVP是IP路由器为提供更好的服务质量向前迈进的具有深刻意义的一步。传统上IP路由器只负责分组转发,通过路由协议知道邻近路由器的地址。而RSVP则类似于电路交换系统的信令协议一样,为一个数据流通知其所经过的每个节点(IP路由器),与端点协商为此数据流提供质量保证。

8、结束语

在前面我们讨论了一些与实时数据传输相关的四个协议:

      1)RTP是实时数据传输协议。它提供时间标志,序列号以及其它能够保证在实时数据传输时处理时间的方法;它是依靠RVSP保证服务质量标准的。
      2)RTCP是RTP的控制部分,是用来保证服务质量和成员管理的。
      3)RTSP是开始和指引流媒体数据从流媒体服务器。它又可叫做"网上录像机控制协议".它是提供远程的控制,具体的数据传输是交给RTP的。
      4)RSVP是Internet上的资源预订协议,使用RSVP预留一部分网络资源(即带宽),能在一定程度上为流媒体的传输提供QoS。就像TCP的重发和滑动窗口等都是

IT技术 , , , ,