对于并发量并不大而且对性能要求不是很高的流媒体传输模块,live555还是很好的选择,下面说一下我所实现的流媒体代理服务器(目前只能实现对H264单视频的转发)
代理转发主要分为对RTSP的转发与对RTP的转发(没有实现对rtcp的转发),尽量做到不破坏原有程序框架,所以还是要将整个代理过程融合于ServerMediaSubsession、Source、Sink的循环中,按照流程,RTSP OPTIONS不是必需进行转发,直接转到DESCRIBE的转发过程。
DECRIBE转发:在看过很多大型的流媒体服务器协议以后,会发现有一个相同点:前端设备在注册到流媒体服务器以后会将其自身的无论是实时资源还是录像资源都上报到流媒体服务器中集中管理,那么此处同理,在流媒体服务器上线(主动注册与保活)后改为主动向前端请求资源,到此处也就是发送DESCRIBE请求,我实现的代理服务器中,专门定义了一个类,继承自RTSPClient,向前端设备发送DESCRIBE请求,成功接收到携带SDP信息的反馈以后,根据SDP信息生成MediaSession,再执行其中每一个MediaSubsession的initiate(),这样获取由前端发送来数据的Source就已经准备就绪,同时,在每一个MediaSubsession建立以后,在对应流的ServerMediaSession中加入ServerMediaSubsession(此处ServerMediaSubsession继承自OnDemandServerMediaSubsession),这样就做到了一个ServerMediaSubsession对应一个MediaSubsession(这里一定要区分ServerMediaSubsession与MediaSubsession),两路会话的连接,下面就是客户端发起了DESCRIBE请求,转到RTSPServer::RTSPClientSession::handleCmd_DESCRIBE(),再转到ServerMediaSession::generateSDPDescription(),因为是转发,而且不能让客户端知道是转发,所以sdp信息在m字段前面的参数都是基于代理服务器本身,从m开始便是真正的媒体信息,原有的live555结构中,获取每一个track的sdpLine是由virtual char const* OnDemandServerMediaSubsession::sdpLines();得到,于是,我们可以重写此virtual函数,返回什么呢,返回的就是我们刚才传入的MediaSubsession的savedSDPLines()返回值。这样,SDP信息就可以在不破坏原有live555循环的基础上构造出来,并实现转发了。
SETUP转发:同DESCRIBE一样,原有框架已经形成,在收到SETUP请求以后,找到RTSPServer::RTSPClientSession::handleCmd_SETUP(),再找到virtual void OnDemandServerMediaSubsession::getStreamParameters(),在原有live555循环中,此函数作用为建立本地接收端口,建立createNewSource与createNewSink,将数据传输循环中的各个部件都准备妥当,一旦收到Play命令就开始循环工作,那么同样重写此virtual函数,在重写中实现向前端发送SETUP请求,等待响应成功,再执行本地的SETUP,向前端发送SETUP实现可以同样由刚才传入的"RTSPClient继承"与MediaSubsession来实现发送命令,等待返回,再继续执行OnDemandServerMediaSubsession::getStreamParameters()实现本地的SETUP,如此一来,本地与远程的SETUP都已完成。
PLAY转发:同样,按照流程,重写OnDemandServerMediaSubsession::startStream()函数,发送Play请求到远程,等待返回,再执行本地的startStream(),本地与远程的Play都已完成。
上面已经将RTSP的转发实现,而且基本是在live555原有基础上进行,没有破坏原有框架结构,下面进行rtp的转发。
rtp的转发实现的重点是在建立source到sink的循环,重写OnDemandServerMediaSubsession::createNewStreamSource,与OnDemandServerMediaSubsession::createNewRTPSink,理想中的转发是能够实现收到rtp包不进行任何重组,直接发送即可,但我所实现的只能是由MediaSubsession中的readSource获取完整的一帧后,再交给rtpSink进行重新切片组装,具体到实现上,我实现了一个集成自H264VideoStreamFramer的类,并将MediaSubsession->readSource作为inputSource传入,重写doGetNextFrame(),从inputSource中获取完整一帧,copy到fTo,再交由createNewRTPSink()返回的H264VideoRTPSink进行重新组装并发送,后来考虑到此种做法,有利有弊,与不重新组装,直接发送,各有考虑。
如此,便实现了RTSP与RTP的转发功能,多有不妥,后续会持续改进,还望能够在留言中多多指正!
2012.3.23
有朋友问到rtcp的转发问题,这里阐述下我的理解:
转发RTCP实际的意义并不是很大,一个方面是rtcp控制的可能是两条不通的数据链路,对拥堵的控制总是在较小的链路基础上,牺牲太大;第二是作为服务器考虑,服务器通过双重身份将数据取来再发出去,控制协调应当完全自主掌控,如果后期在服务器中加入缓存机制实现抖动控制和拥堵控制,可以通过对进线与出线的rtcp控制达到控制作用,而且效果较好!
------------------------------------------------------------
本文转自www.easydarwin.org,更多开源流媒体解决方案,请关注我们的微信:EasyDarwin