一个RtspServer的设计与实现和RTSP2.0简介
前段时间着手实现了一个RTSP Server,能够正常实现多路RTSP流的直播播放,因项目需要,只做了对H.264和AAC编码的支持,但是相信其他编码的实现基本逻辑也是想通的。这里我把主要设计和思考过程,以及实现框架分享一下。因为关注的是直播,这里只讨论RTSP直播协议。
RTSP协议概述与RTSP2.0
众所周知,RTSP协议是一个流媒体协议,可以实现直播和点播形式的音频与视频流的播放。RTSP协议定义了多种服务器-客户端之间交互的接口,主要有OPTIONS,DESCRIBE,SETUP,PLAY,TEARDOWN,RECORD,ANNOUNCE。网络上已经有很多针对RTSP协议的文章,我这里不准备进行过多介绍,详细的协议定义,可以参见RFC2326。RTSP并不包括具体数据的传输,该功能一般由RTP与RTCP协议来实现,并可以通过TCP或UDP两种底层传输方式进行。
下图是典型的RTSP直播过程中服务端-客户端主要交互过程:
当然,直播过程中也可能在服务器与客户端之间调用GET_PARAMETER等其他接口,上图偷懒省略了。上图绿色部分表示的是数据传输。之前说过,流媒体数据传输不是RTSP协议的内容,由RTP包来做。但是具体在实现上,RTP包可以通过UDP或TCP的方式来进行,而且这两种传输方式,区别其实还不小,下面具体说下。
RTSP 数据传输流程
1. RTSP over UDP
对于udp模式,客户端在发送PLAY以后,就开始建立udp端口,以接收服务器发来的RTP包,同样,服务器也会建立udp端口,并向客户端发送RTP包。这是网上大部分程序所采用的方式,优点是逻辑清晰,实现方便,不过缺点也很明显,就是udp所固有的,容易丢包,尤其是在高分辨率高码率下。
2. RTSP over TCP
对tcp模式,通过SETUP接口来指定传输方式,服务器返回同样数据以确定双方通过tcp方式来传输数据。不过跟udp最大的不同是,rtsp over tcp的形式,不再创建单独的tcp通道,而是直接使用之前rtsp通信所使用的tcp通道,流程如下所示:
由于跟rtsp消息使用同一个tcp端口,为了区分,rtp以及rtcp包,增加了4个字节额外的字段,并通过特殊的标识'$',与正常的rtsp消息进行了区分。
RTSP Live Server 设计与实现
1. 程序框架
我这次所实现的RTSP Server,主要功能是采集摄像头和麦克风数据,进行h.264编码以及aac编码,并对外提供RTSP直播流服务。我在实际写代码中,也是首先实现了rtsp over udp的模式,然而,通过实际测试,我发现在高分辨率高码率情况下,由于h.264 NAL单元过大,会拆分成很多的rtp包,而udp不可靠的传输方式,总是难免丢包,在低码率的时候还不明显,高码率情况下,丢包导致的花屏会频繁出现,这样体验特别差。于是我重新实现了一份rtsp over tcp模式的代码,顺利解决了这个问题。
2. 关于h264在sdp中的描述
h264在sdp中的媒体信息,大多都是可以直接填写的,但是有两项数据需要根据编码后的数据来提取,就是profile-level-id和sprop-parameter-sets。这两项字符串数据的计算公式
profile-level-id = "Base16(sps[1])" "Base16(sps[2])" "Base16(sps[3])"
sprop-parameter-sets = "Base64(sps)" "," "Base64(pps)"
3. 主要代码
3.1 Rtsp服务接口
3.2 RtspSession在TCP通道里处理RTSP消息与RTP报文
4. 运行效果
同时用vlc和ffplay进行多路播放,以tcp请求的方式,效果如下,延迟极低。
关于RTSP 2.0
2016年IETF发布了新的RTSP标准,这就是就是RTSP2.0协议(RFC7826),新标准还是有不少修改的,除了完善一些原协议的中的定义,还有一些我觉得比较重要的是,对接口method进行了修改,比如删除了RECORD和ANNOUNCE方法,新增了PLAY_NOTIFY方法。
删除了RECORD,这表示你不能再通过这个接口来控制服务器进行数据的录制了,可以选择在PLAY方法中,添加一些参数,来实现服务器对直播数据进行录制,还可以分隔录制。
删除了ANNOUNCE,这意味着,不能像RTMP一样,客户端通过向服务器推送数据,来实现本机数据对外直播了,这可能需要其他的推送途径来进行替代了。
至于PLAY_NOTIFY,它替代来原来Server向Client端发送ANNOUNCE方法,所实现的功能,也就是告诉客户端,需要根据新参数来调整直播播放状态。
删除通过UDP传输RTSP消息的形式
删除通过发PLAY消息来keep alive的方式(用SET_PARAMETER来做)
RTSP Server也可向Client发TEARDOWN消息
支持IPV6
RTSP请求,支持pipelining的形式,也就是聚合Request。比如可以不等服务器返回,把SETUP和PLAY一起发送,这样可以提高至少一个RTT的启动时间。当然需要在消息里加上相关字段。
重写了状态机,完善了服务器对客户端来说在各个状态之间的转换和行为
RTSP消息内支持URI了
扩展了REDIRECT方法,等,等等。
haibindev.cnblogs.com,合作请联系QQ。(转载请注明作者和出处~)
基于Live555实现RtspServer及高清高码率视频传输优化
最近做了一些pc和嵌入式平台的RTSP服务器项目,大多数的要求是简单但是功能全面,并且性能还要强劲。综合考虑后,基本都是在基于live555的基础上进行开发,在进行Live555本身的优化以及程序内部视频数据传输的优化后,不仅实现了需求而且性能还超出预期,实现了10Mbps高码率的1080p以上高分辨率高清视频的流畅直播。这里将一些优化点分享一下:
为什么基于Live555开发
其实之前我就已经开发过一个RTSP Server程序,并且写了一篇文章进行了介绍“一个RtspServer的设计与实现和RTSP2.0简介”,不过当时开发的目的除了实现RTSP直播以外,主要目的还是简化代码以方便定制,因此并没有完全实现RTSP协议里的所有交互细节,要在它的基础上扩展全面,可能会拖延项目进展。基于项目考虑,选择了自己比较了解也认为比较优秀的RTSP开源项目Live555作为基础,开发RTSP Server程序。
Live555是一个跨平台的流媒体解决方案,以C 为开发语言,实现了RTSP包括服务器-客户端的整套结构,并且支持H.264, H.265, MPEG, AAC等多种视频和音频编码,是很知名的一个开源项目。作为RTSP Server,源码里只有对于本地文件的视频源,不过它的扩展性强,可以在Live555提供的一些基类基础上开发出适合自己项目需求的服务程序。
Live555架构和RTSP数据流程
Live555的核心模块
RTSP服务器和客户端的交互流程
Live555流媒体模块及服务端的处理流程
Live555的流媒体模块基本分为Source和Sink两大部分,当然他们也有一个共同的基类Medium。对服务器来说,Source为数据来源,Sink为数据输出,视频数据就通过MediaSource传递给MediaSink,最终通过RTPInterface网络传输给客户端。一下为服务端所用到的模块以及继承关系:
如同上图所示意的,通过完成自己的ServerMediaSubsession和MediaSource来实现将需要直播的H.264编码数据传递给live555,以实现RTSP直播。
高码率视频数据传输的优化点
对高清高码率的视频画面,每一帧的视频数据就会比较大,这个数值往往会超出live555内部默认的内存处理大小,因为对于live555的优化,主要就是集中在内存缓冲大小的扩大,以及避免内存数据拷贝。以下为根据实际开发和测试所总结出来的有效的优化点:
扩展帧解析buffer大小,即BANK_SIZE,默认值为150k,根据传输的H264数据帧大小,至少设置为300k。否则超出大小,可能会被Live555抛弃。
增加OutPacketBuffer::maxSize大小,同样为了容纳超大帧数据,否则可能会导致数据丢失。
在RTPInterface中,增加socket发送缓冲区大小,即increaseSendBufferTo函数的参数值
对MultiFramedRTPSink::sendPacketIfNecessary中,可以直接调用sendNext尝试组建RTP报文发送数据包,这样修改的优点是已读取的数据会被尽快发送出去,不过也多占用一些线程时间。
对于应用程序将数据从自己的线程传递给Live555的时候,应该尽量减少内存拷贝,最好是通过内存池的形式,以避免拷贝内存阻塞Live555事件循环
经过以上修改,以及应用程序内部代码的优化,在实际应用中,已经实现了10Mbps高码率的1080p以上高分辨率高清视频的流畅直播。