SRS总结 - 4

6.2.拉流客户端发送getStreamLength、play和set Buffer Length消息

拉流客户端接收到SRS服务器的_result消息后,会连续发三个消息:getStreamLength、play和set Buffer Length消息给SRS服务器。

6.2.1. getStreamLength消息

1.wiresharek截图:

https://cdn.nlark.com/yuque/0/2022/png/2544508/1645673514252-187e315f-6c3a-43ac-9c36-f93a06a25148.png

2.拉流客户端生成'getStreamLength'调用并将其发送到服务器。如果服务器知道所选流的持续时间,它将以秒为单位进行应答。

3. FFmpeg对应代码:

static int gen_get_stream_length(URLContext *s, RTMPContext *rt)

6.2.2. play消息

1.wiresharek截图

image.png

2. 由客户端向服务器发起请求从服务器端接受数据(如果传输的信息是视频的话就是请求开始播流),可以多次调用,这样本地就会形成一组数据流的接收者。注意其中有一个reset字段,表示是覆盖之前的播流(设为true)还是重新开始一路播放(设为false)。

3.play命令的结构如下:

4.FFmpeg对应代码:

static int gen_play(URLContext *s, RTMPContext *rt)

6.2.3. set Buffer Length消息

1.wiresharek截图:

image.png

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

https://cdn.nlark.com/yuque/0/2022/png/2544508/1643031036336-43a1e0c5-bd51-4c2e-8985-41597f5803a9.png

8.3. Http-Flv拉流

1. wiresharek截图

当拉流客户端请求HTTP-FLV流时,会带URI。如:

https://cdn.nlark.com/yuque/0/2022/png/2544508/1645696101970-088590a9-9cb2-4df5-b5ef-c90d7504f29b.png

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,功能分为两部分:

  1. 发送flv header到拉流客户端

1.先发送flv header到拉流客户端。

2. 实际执行函数为SrsFlvTransmuxer::write_header:

3. 发送时是写flv header和第一个previousTagSize:

4.wireshark抓包

https://cdn.nlark.com/yuque/0/2022/png/2544508/1645776134588-b9b65db1-f917-459c-8a7c-0b3722213f8d.png

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)

  1. 至此,完成拉流客户端从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)就是虚拟域,用来隔离客户或业务。

https://cdn.nlark.com/yuque/0/2022/png/2544508/1645153372865-55756286-b42f-4738-aab4-0c1393af3f2d.png

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很相似,例如:

image.png

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的服务器):

image.png

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的作用见下表:

image.png

https://cdn.nlark.com/yuque/0/2022/png/2544508/1645156375308-45144044-222e-498c-8ed3-5836f5548e2e.png

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:

  1. 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

posted @ 2023-10-08 11:34  泽良_小涛  阅读(119)  评论(0编辑  收藏  举报