SRS总结 - 4
6.2.拉流客户端发送getStreamLength、play和set Buffer Length消息
拉流客户端接收到SRS服务器的_result消息后,会连续发三个消息:getStreamLength、play和set Buffer Length消息给SRS服务器。
6.2.1. getStreamLength消息
1.wiresharek截图:
2.拉流客户端生成'getStreamLength'调用并将其发送到服务器。如果服务器知道所选流的持续时间,它将以秒为单位进行应答。
3. FFmpeg对应代码:
static int gen_get_stream_length(URLContext *s, RTMPContext *rt)
6.2.2. play消息
1.wiresharek截图
2. 由客户端向服务器发起请求从服务器端接受数据(如果传输的信息是视频的话就是请求开始播流),可以多次调用,这样本地就会形成一组数据流的接收者。注意其中有一个reset字段,表示是覆盖之前的播流(设为true)还是重新开始一路播放(设为false)。
3.play命令的结构如下:
4.FFmpeg对应代码:
static int gen_play(URLContext *s, RTMPContext *rt)
6.2.3. set Buffer Length消息
1.wiresharek截图:
2.生成客户端缓冲区时间并将其发送到服务器。
static int gen_buffer_time(URLContext *s, RTMPContext *rt)
6.3. SRS服务器发送Stream Begin、onStatus(NetStream.Play.Reset)和onStatus(NetStream.Play.Start)等消息给拉流客户端
对应函数为srs_error_t SrsRtmpServer::start_play(int stream_id)
7.从SRS服务器拉RTMP流
7.1. SrsRtmpConn::stream_service_cycle
1.客户端从SRS服务器拉流主要逻辑入口在SrsRtmpConn::stream_service_cycle:
2.如果客户端类型为SrsRtmpConnPlay表示拉流。其中:
- http_hooks_on_play() 方法中回调 on_play() 方法通知 vhost,用户已经开始 play。
- http_hooks_on_stop() 方法中回调 on_stop() 方法通知 vhost,用户已经停止 play。
- 最重要的函数是 playing(source),进入该函数。
7.2. SrsRtmpConn::playing
7.3. SrsRtmpConn::do_playing
SrsRtmpConn::do_playing主要有三个功能。
- 播放控制信息,比如结束,暂停等。
- 从消费者列表中取出Rtmp message(SrsMessageQueue)
- 发送message给拉流客户端。
7.3.1.播放控制信息
SrsRtmpConn::do_playing-- process_play_control_msg(consumer, msg) SrsRtmpConn::process_play_control_msg
7.3.2. 从消费者列表中取出Rtmp message
1.从SrsMessageQueue获取messages,对应函数为:
SrsLiveConsumer::dump_packets
数据实际存储在:SrsMessageQueue的SrsFastVector中,注:
RTMP推流时,通过SrsMessageQueue::enqueue函数将onMetadata、audio和video数据加入到SrsFastVector数组中。
7.3.3. 发送message给拉流客户端
1.SrsProtocol::send_and_free_messages函数负责将message发送给拉流客户端。
2.调用do_send_messages(msgs, nb_msgs)进行发送到拉流客户端。
3.发送消息。
SrsProtocol::do_iovs_send-- srs_write_large_iovs
8.从SRS服务器拉HTTP-FLV流
8.1.协议分析
8.1.1. http-flv技术的实现
1.HTTP协议中有个约定:content-length字段,http的body部分的长度,服务器回复http请求的时候如果有这个字段,客户端就接收这个长的数据然后就认为数据传输完成了。
2.如果服务器回复http请求中没有这个字段,客户端就⼀直接收数据,直到服务器跟客户端的socket连接断开。
3.http-flv直播就是利⽤了这个原理,服务器回复客户端请求的时候不加content-length字段,在回复了http内容之后,紧接着发送flv数据,客户端就⼀直接收数据了。
4.请求SRS返回的是:
5.注:wiresharek过滤条件为:http or tcp.port==8080
8.1.2. srs配置http和http-flv服务
1.主要分为两部分:
- 配置http 服务
- 配置http-flv服务
2.配置⽂件如下所示:
3.播放
可用VLC、ffplay、SRS播放器播放。
http://10.10.14.103:8080/live/Camera_00001.flv
4.http_remux配置说明
- 会根据mount的值,如[vhost]/[app]/[stream].flv,判断什么样的播放类型。
- http不仅支持FLV的拉流,还支持TS,AAC,MP3类型的拉流,以下为http_remux的配置。
5. vhost配置说明
(1)vhost作为应用配置的单元,能隔离客户,应用不同的配置。
(2)Vhost的主要应用场景包括:
- 一个分发网络支持多个客户:譬如CDN,一个分发网络中,有N个客户公用一套流媒体系统,如何区分用户、计费、监控等等?通过app么?大家可能都叫做live之类。最好是通过各自的域名。
- 不同的应用配置:譬如FMLE推上来的流是h264+mp3,可以将音频转码后放到其他的vhost分发hls,这样接入h264+mp3的vhost就不用切hls。
8.1.3. 基本信息
- SrsLiveStream::do_serve_http 处理客户端的数据发送。
- 每个http client连接对应⼀个SrsHttpConn,和SrsRtmpConn连接类似。
- 每个SrsHttpConn也会对应⼀个消费者SrsConsumer,即是SrsConsumer对应rtmp、http-flv都是通⽤的,作为中间数据的缓存。
1. 相关类说明
- SrsBufferCache:HTTP直播流编码器的缓存
- SrsFlvStreamEncoder:将RTMP转成HTTP FLV流
- SrsTsStreamEncoder:将RTMP转成HTTP TS流
- SrsAacStreamEncoder:将RTMP含有的AAC成分转成HTTP AAC流
- SrsMp3StreamEncoder:将RTMP含有的MP3成分转成HTTP MP3流
- SrsBufferWriter:将流直接写⼊到HTTP响应
- SrsLiveStream:HTTP直播流,将RTMP转成HTTP-FLV或者其他格式,其实际是handler
- SrsLiveEntry:直播⼊⼝,⽤来处理HTTP 直播流
- SrsHttpStreamServer:HTTP直播流服务,服务FLV/TS/MP3/AAC流
- SrsHttpResponseWriter:负责将数据发送给客户端,本质是调⽤SrsStSocket进⾏发送
- SrsHttpServeMux HTTP:请求多路复⽤器,⾥⾯记录了path以及对应的handler
8.2. RTMP推流
1.推流的时候根据url创建对应的handler,拉流的时候根据url找到对应处理的handler。
2.rtmp推流调用stack:
3.对应代码在SrsHttpStreamServer::http_mount:
srs_error_t SrsHttpStreamServer::http_mount(SrsSource* s, SrsRequest* r)
4.主要过程有:
- 创建SrsLiveEntry并标明类型,比如flv还是ts。
- 创建SrsLiveStream,HTTP直播流,将RTMP转成HTTP-FLV或者其他格式。
- 根据给的mount挂载handler (SrsLiveStream)。
8.2.1. 创建SrsLiveEntry并标明类型
比如flv还是ts, 见SrsLiveEntry::SrsLiveEntry函数。
8.2.2. 创建SrsLiveStreamHTTP直播流
将RTMP转成HTTP-FLV或者其他格式,见SrsLiveStream::SrsLiveStream函数。
SrsLiveStream::SrsLiveStream(SrsSource* s, SrsRequest* r, SrsBufferCache* c)
8.2.3. 根据给的mount挂载handler (SrsLiveStream)
1.见函数SrsHttpServeMux::handle:
srs_error_t SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) //handle是SrsLiveStream对象(每个source都会绑定一个)
2. 接收客户端推流message是在 SrsRtmpConn::process_publish_message 里负责接收音视频数据并存入copy to all consumer
8.3. Http-Flv拉流
1. wiresharek截图
当拉流客户端请求HTTP-FLV流时,会带URI。如:
2.主要入口代码
srs_error_t SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
3.调用栈
4.主要过程有:
- SRS会根据URl去匹配,找到对应的handle(SrsLiveStream)
- 通过handle发送HTTP-FLV流到客户端。
8.3.1.找对应的handle(SrsLiveStream)
SRS会根据URI去匹配,找到对应的handle(SrsLiveStream)
1. srs_error_t SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph)
2. 通过uri匹配对应的handle(SrsLiveStream),见函数SrsHttpServeMux::match:
srs_error_t SrsHttpServeMux::match(ISrsHttpMessage* r, ISrsHttpHandler** ph)
3.调用栈如下:
8.3.2. 客户端拉取HTTP-FLV
1.函数见:SrsLiveStream::do_serve_http
2.调用栈:
3.对应函数如下,主要功能有:
- 根据pattern后缀设置不同的ISrsBufferEncoder,如flv时是SrsFlvStreamEncoder
如flv时是SrsFlvStreamEncoder
- 创建消费者并从存储音视频数据的队列queue取出messages。
见SrsSource::create_consumer和SrsConsumer::dump_packets,与从SRS服务器拉RTMP流分析过对应函数。
- 将messages封装成tag发送到拉流客户端。
见函数SrsFlvStreamEncoder::write_tags,功能分为两部分:
- 发送flv header到拉流客户端
1.先发送flv header到拉流客户端。
2. 实际执行函数为SrsFlvTransmuxer::write_header:
3. 发送时是写flv header和第一个previousTagSize:
4.wireshark抓包
2)发送flv body到拉流客户端
1.flv tag data由4字节的previousTagSize和flv tag组成。
2.其中flv tag又是由11字节的flv tag header和flv tag data组成。
3.所以可以分为flv body可以看成由三部分组成:previousTagSize、tag header和tag data。
4.代码SrsFlvTransmuxer::write_tags如下:
srs_error_t SrsFlvTransmuxer::write_tags(SrsSharedPtrMessage** msgs, int count)
5.其中不同的tag header结构不一样,需要独立构造。
6.构造audio tag header:
void SrsFlvTransmuxer::cache_audio(int64_t timestamp, char* data, int size, char* cache)
7.构造video tag header:
void SrsFlvTransmuxer::cache_video(int64_t timestamp, char* data, int size, char* cache)
8.构造metadata tag header
void SrsFlvTransmuxer::cache_metadata(char type, char* data, int size, char* cache)
- 至此,完成拉流客户端从SRS服务器拉流过程。
8.4.http协议
8.4.1.协议的主要方法
8.4.2.srs运行中的一张截图
8.4.3 Cookie
8.4.4 状态码
8.4.5 http1.1通用首部字段
8.4.6 https协议
9. SRS的vhost
9.1. vhost概述
1.Vhost(Virtual Host)就是虚拟域,用来隔离客户或业务。
2.Vhost的主要应用场景包括:
- 一个分发网络支持多个客户:譬如CDN,一个分发网络中,有N个客户公用一套流媒体系统,通过各自的域名区分用户,计费,监控等等。
- 不同的应用配置:譬如FMLE推上来的流是h264+mp3,可以将音频转码后放到其他的vhost分发hls,这样接入h264+mp3的vhost就不用切hls。
3.总结:vhost作为应用配置的单元,能隔离客户,应用不同的配置。
9.2. 标准RTMP URL
1.标准RTMP URL指的是最大兼容的RTMP URL,基本上所有的服务器和播放器都能识别的URL,和HTTP URL很相似,例如:
2.其中:
- Schema:协议头,HTTP为HTTP或HTTPS,RTMP为RTMP/RTMPS/RTMPE/RTMPT等众多协议,还有新出的RTMFP。
- Host:主机,表示要连接的主机,可以为主机DNS名称或者IP地址。商用时,一般不会用IP地址,而是DNS名称,这样可以用CDN分发内容(CDN一般使用DNS调度,即不同网络和地理位置的用户,通过DNS解析到的IP不一样,实现用户的就近访问)。
- Port:端口,HTTP默认为80,RTMP默认为1935。当端口没有指定时,使用默认端口。
- Path:路径,HTTP访问的文件路径。
- App:RTMP的Application(应用)名称,可以类比为文件夹。以文件夹来分类不同的流,没有特殊约定,可以任意划分。
- Stream:RTMP的Stream(流)名称,可以类比为文件。
9.3. NO Vhost
1.vhost大多数用户都用不到,而且不推荐用,有点复杂,一般的用户用app就可以了。因为vhost/app/stream,只是一个分类方法而已。vhost需要在配置文件中说明,app/stream都不需要配置。
2.如果你是提供服务,譬如你有100个客户,都要用一套平台,走同样的流媒体服务器分发。那可以每个客户一个vhost,这样他们的app和stream可以相同都可以。
3.一般的用法,举个例子,有个视频网站,自己搭建服务器,所以只有他自己一个客户,就不要用vhost了,直接用app就足够了。假设视频网站提供聊天服务,聊天有不同的话题类型,每个话题就是一个app,譬如:军事栏目,读书栏目,历史栏目三个分类,每个分类下面有很多聊天室。只要这么配置就好:
4.生成网页时,比如军事栏目的网页,都用app名称为military,某个聊天室叫做火箭,这个页面的流可以用:rtmp://yourdomain.com/military/rock,编码器也推这个流,所有观看这个军事栏目/火箭聊天室的页面的人,都播放这个流。
5.军事栏目另外的网页,都用同样的app名称military,但是流不一样,譬如某个聊天室叫做雷达,这个页面的流可以用:rtmp://yourdomain.com/military/radar,推流和观看一样。
6.如此类推,军事栏目页面生成时,不用更改srs的任何配置。也就是说,新增聊天室,不用改服务器配置;新增分类,譬如加个公开课的聊天室,也不用改服务器配置。
7.另外,读书栏目可以用app名称为reader,栏目下的某个聊天室叫红楼梦,这个页面的流可以用:rtmp://yourdomain.com/reader/red_mansion,所有在这个聊天室的人都是播放这个流。
9.4. Vhost使用介绍
1.RTMP的Vhost和HTTP的Vhost概念是一样的:虚拟主机。详见下表(假设域名demo.srs.com被解析到IP为192.168.1.10的服务器):
2.Vhost主要的作用是:
- 支持多用户:当一台服务器需要服务多个客户,譬如CDN有cctv(央视)和wasu(华数传媒)两个客户时,如何隔离他们两个的资源?相当于不同的用户共用一台计算机,他们可以在自己的文件系统建立同样的文件目录结构,但是彼此不会冲突。
- 域名调度:CDN分发内容时,需要让用户访问离自己最近的边缘节点,边缘节点再从源站或上层节点获取数据,达到加速访问的效果。一般的做法就是Host是DNS域名,这样可以根据用户的信息解析到不同的节点。
- 支持多配置:有时候需要使用不同的配置,考虑一个支持多终端(PC/Apple/Android)的应用,PC上RTMP分发,Apple和Android是HLS分发,如何让PC延迟最低,同时HLS也能支持,而且终端播放时尽量地址一致(降低终端开发难度)?
- 可以使用两个Vhost,PC和HLS;PC配置为最低延迟的RTMP,并且将流转发给HLS的Vhost,可以对音频转码(可能不是H264/AAC)后切片为HLS。
- PC和HLS这两个Vhost的配置肯定是不一样的,播放时,流名称是一样,只需要使用不同的Host就可以。
9.5. Multiple Customers
1.假设cctv和wasu都运行在一台边缘节点(192.168.1.10)上,用户访问这两个媒体的流时,Vhost的作用见下表:
2.在边缘节点(192.168.1.10)上的SRS,需要配置Vhost,例如:
9.6. 配置单元
1.以上面举的例子,若cctv需要延迟最低(意味着启动时只有声音,画面是黑屏),而wasu需要快速启动(打开就能看到视频,服务器cache了最后一个gop,延迟会较大)。
2.只需要对这两个Vhost进行不同的配置,例如:
3.总之,这两个Vhost的配置完全没有关系,不会相互影响。
9.7. 默认vhost
1.FMS的__defaultVhost__是默认的vhost,当用户请求的vhost没有匹配成功时,若配置了defaultVhost,则使用它来提供服务。若匹配失败,也没有defaultVhost,则返回错误。
2.譬如,服务器192.168.1.10上的SRS配置如下:
listen 1935;
vhost demo.srs.com {
enabled on;
}
3.那么,当用户访问以下vhost时:
- rtmp://demo.srs.com/live/livestream:成功,匹配vhost为demo.srs.com
- rtmp://192.168.1.10/live/livestream:失败,没有找到vhost,也没有defaultVhost。
4.defaultVhost和其他vhost的规则一样,只是用来匹配那些没有匹配成功的vhost的请求的。
9.8. Locate Vhost
1.访问某台服务器上的Vhost有两个方法:
- 配置hosts:因为Vhost实际上就是DNS解析,所以可以配置客户端的hosts,将域名(Vhost)解析到指定的服务器,就可以访问这台服务器上的指定的vhost。
- 使用app的参数:需要服务器支持。在app后面带参数指定要访问的Vhost。SRS支持?vhost=VHOST和...vhost...VHOST这两种方式,后面的方式是避免一些播放器不识别?和=等特殊字符。
2.普通用户不用这么麻烦,直接访问RTMP地址就好了,有时候运维需要看某台机器上的Vhost的流是否有问题,就需要这种特殊的访问方式。考虑下面的例子:
3.各种访问方式见下表:
9.9. URL of FMLE
1.FMLE推流时,URL那个地方,有三个可以输入的框,参考Adobe FMLE:
- FMS URL: 需要输入rtmp://host:port/app,例如:rtmp://demo.srs.com/live
- Backup URL: 备份的服务器,格式同FMS URL。若指定了备份服务器,FMLE会同时推送给这两个服务器。
- Stream: 流名称,例如:livestream
2.实际上是将RTMP URL分成了两部分,stream前面那部分和stream。原因:
- 支持多级app和Stream:我们目前举的例子都是一级app和一级stream,实际上RTMP支持多级app和stream,就像子文件夹,实际上很少用得到。所以SRS的URL都是一个地址,默认最后一个/后面就是stream,前面是app。
- 支持流名称带参数:Adobe的HLS/HDS非常之麻烦,那个地址是个完全不一致。参考FMS livepkgr,例如发布一个rtmp,并切片成HLS:
- SRS的简洁方案:
9.10. URL参数
1.RTMP URL一般是不带参数,类似于http的query,有时候为了特殊的要求,会在RTMP URL中带参数,譬如:
- Vhost:在app后面加参数,可以访问指定服务器的指定Vhost。这个SRS的特殊约定,方便排错。
- FMLE的Stream后面的参数,指定event之类的。SRS不需要这么麻烦,HLS是内置支持,无需这种复杂的配置。Callback也是http的,FMS为了支持服务器端脚本,需要很复杂的配置和复杂的参数,实在是很麻烦的设计。
- token认证:SRS还未实现。在连接服务器时,在app后面指定token(方式和vhost一样),例如rtmp://server/live?vhost=xxx&token=xxx/livestream,服务器可以取出token,进行验证,若验证失败则断开连接,这种是比Refer更高级的防盗链。
2.app和stream后面带参数,这两者有何区别,为何SRS把参数放在app后面?客户端播放流的as3代码大约
3.从RTMP协议的角度来看:
- NetConnection.connect(vhost+app):这一步会完成握手,connect到vhost,切换到app。类似于登录到vhost后,cd到app这个目录。也就是vhost的验证,都可以在这一步做,也就是指定vhost也是在一步了,所以app后面跟的参数都是和vhost/app相关的。
- NetStream.play(stream):这一步是播放指定的直播流。所以和stream相关的事件,都可以传递参数,譬如Adobe的event。SRS是没有这些事件的,流启动时,若配置了HLS会自动开始切片。
9.11. SRS的URL
1.SRS的RTMP URL使用标准的RTMP URL,一般不需要对app和stream加参数,或者更改他们的意义。除了两个地方:
- vhost支持参数访问:为了方便运维访问某台服务器的vhost,不需要设置hosts。不影响普通用户。
- 支持token验证:为了支持token验证,在app后面带参数,这个是token验证必须的方式。
2.另外,SRS建议用户使用一级app和一级stream,不使用多级app和多级stream。譬如:
3.srs播放器(srs_player)和srs编码器(srs_publisher)不支持多级app和stream,他们认为最后一个斜杠(/)后面的就是stream,前面的是app。即:
4.做此简化的好处是,srs播放器和编码器,只需要指定一个url,而且两者的url是一样的。
5.SRS常见的三种RTMP URL,详细见下表:
9.12. SRS的vhost例子
1.SRS的full.conf配置文件中,有很多Vhost,主要是为了说明各个功能,每个功能都单独列出一个vhost。所有功能都放在demo.srs.com这个vhost中。
2.SRS的demo.conf配置文件中,包含了demo用到的一些vhost,参考Usage: Demo。