读本文前,务必先阅读前面这篇文章,手把手搭建流媒体服务器详细步骤。因为本篇文章是在这篇文章的基础上搭建。
1.HLS简述
HLS是Apple 提出的⼀种基于 HTTP 的协议,HLS(HTTP Live Streaming)⽤于解决实时⾳视频流的传输。尤其是在ios移动端,由于 iOS /H5 不⽀持 flash,使得 HLS 成了ios移动端实时视频流传输的⾸选。HLS经常⽤在直播领域,⼀些国内的直播云通常⽤ HLS 拉流(将视频流从服务器拉到客户端)。HLS最大的缺点就是延迟严重,延迟通常在10-30s 之间。
英文本协议地址如下,可以详细阅读。
HLS英文版协议:https://tools.ietf.org/html/draft-pantos-http-live-streaming-06
协议有如下部分:
苹果开发者官网:https://developer.apple.com/streaming/
2.HLS数据流整体框架
HLS数据流向的整体框架如下图所示。其主要分为以下几步:
(1)推流端把采集,编码,封装的数据数据发送到服务端。
(2)Stream segmenter是指把码流分片。对直播或点播流进行分片,分片的数量是固定,每个分片的时长也是固定,如5个分片,每个分片为5s,分片时间没有办法精确到毫秒,因为分片一般默认都是从I帧开始,保证一个完整的GOP。如果分配数量为5,那么就是最多缓存5个分片,只会保存最新分片,过时的分片就会删除。如有当前有1个分片。当第6个分片加进来,则第一个分片就会被删除。
(3)index file就是存储目前的分片信息,如把分片1,分片2写入index file。如下图:
(4)推送给web server,然后存储ts文件。
(5)通过HTTP协议读取服务器文件,先读取index file,然后解析index file,最后读取相应的ts文件。服务器就把ts文件送出去,然后 播放。
(6)因为index file是保存分片信息,分片是实时更新,所以index file也是持续更新。只要有新的ts文件生成那就会更新。每次index file的ts文件读取完毕,就会再次读取index file文件,获取新的index file,继续读取最新的ts文件,如此往复。
3.搭建HLS流媒体服务器
先参考前面这篇文章:手把手搭建流媒体服务器详细步骤
下面前面2步,在这篇文章手把手搭建流媒体服务器详细步骤有详细说明。
(1)srs官⽹:https://github.com/ossrs/srs
码云的源速度快:https://gitee.com/winlinvip/srs.oschina.git
github的源速度慢:https://github.com/ossrs/srs.git
选择当前最新的release版本3.0
第⼀步,获取SRS。详细参考GIT获取代码:https://github.com/ossrs/srs/wiki/v1_CN_Git
git clone https://gitee.com/winlinvip/srs.oschina.git
cd srs.oschina
cd trunk
第⼆步,编译SRS。详细参考Build:https://github.com/ossrs/srs/wiki/v3_CN_Build
./configure && make
(3)编写SRS配置⽂件。详细参考RTMP分发:https://github.com/ossrs/srs/wiki/v1_CN_DeliveryRTMP,Delivery HLS:https://github.com/ossrs/srs/wiki/v3_CN_SampleHLS,Delivery HTTP FLV:https://github.com/ossrs/srs/wiki/v3_CN_SampleHttpFlv,编辑 conf/srs.conf (尤其是hls和http_remux部分),服务器启动时指定该配置⽂件(srs的conf⽂件夹有该⽂件)。
listen 1935;
max_connections 1000;
srs_log_tank file;
srs_log_file ./objs/srs.log;
http_api {
enabled on;
listen 1985;
}
http_server {
enabled on;
listen 8080;#改为8081应该也可以
dir ./objs/nginx/html;
}
stats {
network 0;
disk sda sdb xvda xvdb;
}
vhost __defaultVhost__ {
#hls antonio
hls{
enabled on;
hls_path ./objs/nginx/html;#生成ts文件路经
hls_fragment 5; # 分⽚时⻓ 秒
hls_window 25; # 最⼤缓存的时⻓秒,也决定了最大延迟时间
}
#http-flv for antonio
http_remux
{
enabled on;
mount [vhost]/[app]/[stream].flv;
hstrs on;
}
}
(4)启动SRS。
-c表示是读取配置文件。
在这个路经执行:
./objs/srs -c conf/srs.conf
出现如下,就代表通过后台跑起来了。
也可以再输入如下命令,让其在前台显示,并查看log信息:
在如下目录:
前台查看log命令如下:
tail -f objs/srs.log
出现如下,代表可以成功查看信息:
使用ffmpeg推流一定要搭建好ffmpeg环境以及到带有xxx.flv文件的指定目录去执行命令。
ffmpeg推流:ffmpeg -re -i source.200kbps.768x320.flv -vcodec copy -acodec copy -f flv -y
拉流:ffplay rtmp://172.16.204.132/live/livestream
注意:上述命令中的flv完整路径,以及srs server ip,⼤家根据情况⾃⾏替换为实际值。 另外:默认情况下srs的rtmp采⽤1935端⼝,如果该端⼝被占⽤或修改了srs.conf中的端⼝,注意根据情况调整;防⽕墙如果开了,也要检测下1935是否允许访问。
出现如下界面,就代表搭建成功。
十分注意:很多朋友都没注意一个概念,就是以为hls有推流端,实时是hls只有拉流的说法。
(5)拉流RTMP/HLS/HTTP-FLV流
RTMP拉流地址:ffplay rtmp://172.16.204.132/live/livestream
HTTP FLV拉流地址:ffplay http://172.16.204.132:8080/live/livestream.flv
HLS拉流地址:ffplay http://172.16.204.132:8080/live/livestream.m3u8
同时拉流端也可以使用可以使⽤ffplay或者vlc以及 http://ossrs.net/srs.release/trunk/research/players/srs_player.html(经过测试这个播放器也是能拉取到各类流)或其它拉流工具进⾏测试。
对比延时时间:
对比拉取RTMP流和http的livestream.m3u8流。其中左图是RTMP流,右图是livestream.m3u8流,可以看出HLS相较RTMP,延时多了接近16s左右,hls这个延时就是最大确点。
另外,经过测试,拉流FLV这个也是ffplay http://172.16.204.132:8080/live/livestream.flv,也是能够拉取到。
本篇文章就分析到这里,欢迎大家关注欢迎关注,点赞,转发,收藏,分享,评论区讨论。
后面关于项目知识,后期会更新。欢迎关注微信公众号"记录世界 from antonio"。
srs官网:SRS官网
码云的源速度快:码云的源速度快
github的源速度慢:github的源速度慢
选择当前最新的release版本3.0
第一步,获取SRS。详细参考GIT获取代码
git clone https://gitee.com/winlinvip/srs.oschina.git srs.3.0-20200720
cd srs.3.0-20200720
#使⽤当前最新的3.0版本
git checkout 3.0release
cd trunk
第二步,编译SRS。详细参考Build
./configure && make
第三步,编写SRS配置文件。详细参考RTMP分发,Delivery HLS,Delivery HTTP FLV
编辑 conf/srs.conf ,服务器启动时指定该配置文件(srs的conf文件夹有该文件)。
1 listen 1935;
2 max_connections 1000;
3 srs_log_tank file;
4 srs_log_file ./objs/srs.log;
5 http_api {
6 enabled on;
7 listen 1985;
8 }
9 http_server {
10 enabled on;
11 listen 8081; # http监听端⼝,注意⾃⼰配置的端⼝
12 dir ./objs/nginx/html;
13 }
14 stats {
15 network 0;
16 disk sda sdb xvda xvdb;
17 }
18 vhost __defaultVhost__ {
19 # hls darren
20 hls {
21 enabled on;
22 hls_path ./objs/nginx/html;
23 hls_fragment 10;
24 hls_window 60;
25 }
26 # http-flv darren
27 http_remux {
28 enabled on;
29 mount [vhost]/[app]/[stream].flv;
30 hstrs on;
31 }
32 }
SRS全网独一份的视频文档资料私信1,进群免费领取,更有【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂
第四步,启动SRS。
./objs/srs -c conf/srs.conf
1 ubuntu@VM-0-13-ubuntu:~/0voice/media/srs.3.0-20200720/trunk$ ./objs/s
2rs -c conf/srs.conf
2 后台运⾏结果
3 [2020-07-20 17:34:48.061][Trace][30433][0] XCORE-SRS/3.0.141(OuXuli)
4 [2020-07-20 17:34:48.061][Trace][30433][0] config parse complete
5 [2020-07-20 17:34:48.061][Trace][30433][0] write log to file ./objs/s
rs.log
6 [2020-07-20 17:34:48.061][Trace][30433][0] you can: tailf ./objs/srs.
log
7 [2020-07-20 17:34:48.061][Trace][30433][0] @see: https://github.com/o
ssrs/srs/wiki/v1_CN_SrsLog
确认是否已经正常启动
1 ubuntu@VM-0-13-ubuntu:~/0voice/media/srs.3.0-20200720/trunk$ sudo ps
-ef | grep srs
2 ubuntu 30435 1 0 17:34 pts/0 00:00:00 ./objs/srs -c conf/sr
s.conf
显示到ubuntu 30435 1 0 17:34 pts/0 00:00:00 ./objs/srs -c conf/srs.conf
安全退出正在运行的srs
sudo kill -SIGQUIT srs_pid
默认是后台启动的方式,如果是要方便GDB调试则需要修改配置文件为前台启动。
1 listen 1935;
2 max_connections 1000;
3 #srs_log_tank file;
4 #srs_log_file ./objs/srs.log;
5 # 前台运⾏
6 daemon off;
7 # 打印到终端控制台
8 srs_log_tank console;
9 http_api {
10 enabled on;
11 listen 1985;
12 }
313 http_server {
14 enabled on;
15 listen 8081; # http监听端⼝
16 dir ./objs/nginx/html;
17 }
18 stats {
19 network 0;
20 disk sda sdb xvda xvdb;
21 }
22 vhost __defaultVhost__ {
23 # hls darren
24 hls {
25 enabled on;
26 hls_path ./objs/nginx/html;
27 hls_fragment 10;
28 hls_window 60;
29 }
30 # http-flv darren
31 http_remux {
32 enabled on;
33 mount [vhost]/[app]/[stream].flv;
34 hstrs on;
35 }
36 }
执行方法:./objs/srs -c conf/srs.conf
在终端运行,log也在终端显示
1 [2020-07-20 17:46:33.586][Trace][1533][0] system default latency(ms)
: mw(0-350) + mr(0-350) + play-queue(0-30000)
2 [2020-07-20 17:46:33.586][Warn][1533][0][0] SRS/3.0.141 is beta
3 [2020-07-20 17:46:33.586][Trace][1533][0] http flv live stream, vhos
t=__defaultVhost__, mount=[vhost]/[app]/[stream].flv
4 [2020-07-20 17:46:33.586][Trace][1533][0] http: root mount to ./objs
/nginx/html
5 [2020-07-20 17:46:33.586][Trace][1533][0] st_init success, use epoll
6 [2020-07-20 17:46:33.586][Trace][1533][380] server main cid=380, pid
45
=1533, ppid=2337, asprocess=0
7 [2020-07-20 17:46:33.586][Trace][1533][380] write pid=1533 to ./objs
/srs.pid success!
8 [2020-07-20 17:46:33.586][Trace][1533][380] RTMP listen at tcp://0.0
.0.0:1935, fd=7
9 [2020-07-20 17:46:33.586][Trace][1533][380] HTTP-API listen at tcp:/
/0.0.0.0:1985, fd=8
10 [2020-07-20 17:46:33.586][Trace][1533][380] HTTP-Server listen at tc
p://0.0.0.0:8081, fd=9
11 [2020-07-20 17:46:33.586][Trace][1533][380] signal installed, reload
=1, reopen=10, fast_quit=15, grace_quit=3
12 [2020-07-20 17:46:33.586][Trace][1533][380] http: api mount /console
to ./objs/nginx/html/console
trunk目录
3rdparty auto conf configure doc etc ide modules research scripts src usr
src下的源码
app core kernel libs main protocol service utest
app应用
├── app
│ ├── srs_app_async_call.cpp
│ ├── srs_app_async_call.hpp 可以用来执行异步任务,通过execute()函数 push任务,然后
在cycle()执行
│ ├── srs_app_bandwidth.cpp
│ ├── srs_app_bandwidth.hpp 提供带宽测试接口
│ ├── srs_app_caster_flv.cpp
│ ├── srs_app_caster_flv.hpp 支持POST一个flv流到服务器,类似相当于RTMP的publish
│ ├── srs_app_config.cpp
│ ├── srs_app_config.hpp 读取配置文件
│ ├── srs_app_conn.cpp
│ ├── srs_app_conn.hpp srs的基本连接,每个连接对应一个协程,所有的连接都被管理
│ ├── srs_app_coworkers.cpp
│ ├── srs_app_coworkers.hpp SrsCoWorkers For origin cluster
│ ├── srs_app_dash.cpp
│ ├── srs_app_dash.hpp SrsDash 流媒体DASH业务 The MPEG-DASH encoder,
transmux RTMP to DASH.
│ ├── srs_app_dvr.cpp
│ ├── srs_app_dvr.hpp SrsDvr 录制RTMP流程flv或者mp4文件
│ ├── srs_app_edge.cpp
│ ├── srs_app_edge.hpp SrsEdgeRtmpUpstream 边缘节点业务,比如从源站拉流到边缘,边
缘回溯到源站
│ ├── srs_app_empty.cpp
│ ├── srs_app_empty.hpp 没有内容
│ ├── srs_app_encoder.cpp
│ ├── srs_app_encoder.hpp SrsEncoder 可以使用多个ffmpeg来转换指定的流,最终调用
SrsFFMPEG来转流
│ ├── srs_app_ffmpeg.cpp
│ ├── srs_app_ffmpeg.hpp SrsFFMPEG 使用ffmpeg来转换流
│ ├── srs_app_forward.cpp
│ ├── srs_app_forward.hpp SrsForwarder 将流转发到其他服务器
│ ├── srs_app_fragment.cpp
│ ├── srs_app_fragment.hpp SrsFragment 表示一个分片,如HLS分片、DVR分片或DASH分
片。它是一个媒体文件,例如FLV或MP4,有持续时间。
│ ├── srs_app_hds.cpp
│ ├── srs_app_hds.hpp SrsHds 将RTMP转成Adobe HDS流
│ ├── srs_app_heartbeat.cpp
│ ├── srs_app_heartbeat.hpp SrsHttpHeartbeat HHTP心跳
│ ├── srs_app_hls.cpp
│ ├── srs_app_hls.hpp SrsHls HLS业务,Transmux RTMP stream to HLS(m3u8 and
ts).
│├── srs_app_hourglass.cpp
│ ├── srs_app_hourglass.hpp SrsHourGlass 滴答tick的处理程序
│ ├── srs_app_http_api.cpp
│ ├── srs_app_http_api.hpp SrsHttpApi HTTP业务API7
│ ├── srs_app_http_client.cpp
│ ├── srs_app_http_client.hpp 没有内容
│ ├── srs_app_http_conn.cpp
│ ├── srs_app_http_conn.hpp SrsHttpConn,HTTP连接,继承于SrsConnection
│ ├── srs_app_http_hooks.cpp
│ ├── srs_app_http_hooks.hpp SrsHttpHooks HTTP勾子,HTTP回调API
│ ├── srs_app_http_static.cpp
│ ├── srs_app_http_static.hpp SrsHttpStaticServer HTTP静态服务器实例,为HTTP静态文
件和FLV/MP4视频点播服务
│ ├── srs_app_http_stream.cpp
│ ├── srs_app_http_stream.hpp SrsHttpStreamServer HTTP直播流服务,支持
FLV/TS/MP3/AAC流
│ ├── srs_app_ingest.cpp
│ ├── srs_app_ingest.hpp SrsIngester摄取文件/流/设备,用FFMPEG编码(可选), 通过
RTMP推送到SRS(或其他RTMP服务器)
│ ├── srs_app_listener.cpp
│ ├── srs_app_listener.hpp SrsTcpListener SrsUdpListener TCP/UDP监听器
│ ├── srs_app_log.cpp
│ ├── srs_app_log.hpp SrsFastLog 日志
│ ├── srs_app_mpegts_udp.cpp
│ ├── srs_app_mpegts_udp.hpp SrsMpegtsOverUdpThe mpegts over udp stream caster
│ ├── srs_app_ng_exec.cpp
│ ├── srs_app_ng_exec.hpp SrsNgExec
│ ├── srs_app_pithy_print.cpp
│ ├── srs_app_pithy_print.hpp SrsPithyPrint 收集信息,然后打印
│ ├── srs_app_process.cpp
│ ├── srs_app_process.hpp SrsProcess启动和停止进程,当被终止时调用cycle重新启动进
程
│ ├── srs_app_recv_thread.cpp
│ ├── srs_app_recv_thread.hpp SrsHttpRecvThread HTTP数据读取,
SrsPublishRecvThread推流数据读取,SrsQueueRecvThread从队列读取;SrsRecvThread封装的协程
│ ├── srs_app_refer.cpp
│ ├── srs_app_refer.hpp SrsRefer
│ ├── srs_app_reload.cpp
│ ├── srs_app_reload.hpp ISrsReloadHandler 重新读取配置文件的处理
│ ├── srs_app_rtmp_conn.cpp
│ ├── srs_app_rtmp_conn.hpp SrsRtmpConn RTMP连接
│ ├── srs_app_rtsp.cpp
│ ├── srs_app_rtsp.hpp SrsRtpConn RTSP连接,SrsRtspCaster RTSP业务
│ ├── srs_app_security.cpp8
│ ├── srs_app_security.hpp SrsSecurity 安全限制,主要是限制url
│ ├── srs_app_server.cpp
│ ├── srs_app_server.hpp SrsServer SRS服务,对应的rtmp、rtsp、http-flv等等业务在这里启动
│ ├── srs_app_source.cpp
│ ├── srs_app_source.hpp SrsSource 对应一个源,支持多个SrsConsumer来拉流,
SrsSourceManager管理源, SrsMetaCache⽤于源缓存Meta数据,SrsConsumer源的消费者,
SrsGopCache GOP缓存
│ ├── srs_app_statistic.cpp
│ ├── srs_app_statistic.hpp SrsStatistic流统计
│ ├── srs_app_st.cpp
│ ├── srs_app_st.hpp SrsSTCoroutine协程相关
│ ├── srs_app_thread.cpp
│ ├── srs_app_thread.hpp SrsCoroutineManager协程管理
│ ├── srs_app_utility.cpp
│ └── srs_app_utility.hpp 工具类:SrsPlatformInfo、SrsNetworkDevices、SrsMemInfo、
SrsDiskStat等等
├── core
│ ├── srs_core_autofree.cpp
│ ├── srs_core_autofree.hpp 通过栈上的方式构建自动释放堆申请的对象,这个设计还是非常值
得我们学习
│ ├── srs_core.cpp
│ ├── srs_core.hpp 版本相关的⼀些信息
│ ├── srs_core_mem_watch.cpp
│ ├── srs_core_mem_watch.hpp 内存监测接口
│ ├── srs_core_performance.cpp
│ ├── srs_core_performance.hpp 性能测试相关
│ ├── srs_core_time.cpp
│ ├── srs_core_time.hpp 时间单位相关
│ ├── srs_core_version3.cpp
│ └── srs_core_version3.hpp 版本信息
├── kernel // 音视频格式相关的
│ ├── srs_kernel_aac.cpp
│ ├── srs_kernel_aac.hpp SrsAacTransmuxer 合成AAC音频流,带ADTS header
│ ├── srs_kernel_balance.cpp
│ ├── srs_kernel_balance.hpp SrsLbRoundRobin负载均衡,用于边缘节点拉流和其他多个服
务器的功能
│ ├── srs_kernel_buffer.cpp
│ ├── srs_kernel_buffer.hpp SrsBuffer读取字节的实用类
│ ├── srs_kernel_codec.cpp9
│ ├── srs_kernel_codec.hpp 编码器相关,包括视频和音频,非常核心的文件;SrsFlvVideo用来检测FLV的video tag对应内容;SrsFlvAudio用来检测FLV的audio tag对应内容;SrsMaxNbSamples
256表示video最大的NALUS个数,audio最大的packet数量;SrsFrame存储帧,SrsAudioFrame 存储
AAC帧,SrsVideoFrame存储视频帧;SrsFormat编码器格式,包含了一个或者多个流,比如为RTMP
format时,包含一个视频和一个音频帧。先猜测推流时的数据实例是保存在SrsFormat?
│ ├── srs_kernel_consts.cpp
│ ├── srs_kernel_consts.hpp SRS的常量定义,比如播放的标记#define
SRS_CONSTS_LOG_PLAY "PLA";发布的标记#define SRS_CONSTS_LOG_CLIENT_PUBLISH
"CPB";SRS_CONSTS_HTTP_XXX等HTTP响应码;SRS_CONSTS_RTSP_XXX响应码等等。
│ ├── srs_kernel_error.cpp
│ ├── srs_kernel_error.hpp 返回值常量定义,ERROR_XXX;SrsCplxError 异常类
│ ├── srs_kernel_file.cpp
│ ├── srs_kernel_file.hpp 文件的读写,SrsFileWriter文件写入器,SrsFileReader文件读取器
│ ├── srs_kernel_flv.cpp
│ ├── srs_kernel_flv.hpp FLV SrsFlvDecoder解析,SrsFlvTransmuxer将RTMP转成FLV流;
SrsSharedPtrMessage对应RTMP的消息
│ ├── srs_kernel_io.cpp
│ ├── srs_kernel_io.hpp IO读写接口类
│ ├── srs_kernel_log.cpp
│ ├── srs_kernel_log.hpp 日志相关
│ ├── srs_kernel_mp3.cpp
│ ├── srs_kernel_mp3.hpp SrsMp3Transmuxer将RTMP转成MP3流
│ ├── srs_kernel_mp4.cpp
│ ├── srs_kernel_mp4.hpp SrsMp4Encoder MP4复用器;
│ ├── srs_kernel_stream.cpp
│ ├── srs_kernel_stream.hpp SrsSimpleStream用vector实现的简单的字节append类,主要在
hls和http中使用,将来需要进行改进。
│ ├── srs_kernel_ts.cpp
│ ├── srs_kernel_ts.hpp SrsTsTransmuxer将RTMP流转成http-ts流,该文件实现了ts格式相
关的接口
│ ├── srs_kernel_utility.cpp
│ └── srs_kernel_utility.hpp 工具函数,比如bool srs_string_ends_with(std::string str,
std::string flag)
├── libs
│ ├── srs_lib_bandwidth.cpp
│ ├── srs_lib_bandwidth.hpp SrsBandwidthClient srs-librtmp 客户端带宽统计
│ ├── srs_librtmp.cpp
│ ├── srs_librtmp.hpp srs提供的客户端rtmp库
│ ├── srs_lib_simple_socket.cpp
│ └── srs_lib_simple_socket.hpp SimpleSocketStream rtmp客户端的socket封装10
├── main
│ ├── srs_main_ingest_hls.cpp 拉取hls发布到rtmp流媒体服务器
│ ├── srs_main_mp4_parser.cpp MP4 box解析
│ └── srs_main_server.cpp srs流媒体服务器主入口
├── protocol 流媒体协议相关的协议都在这里
│ ├── srs_http_stack.cpp
│ ├── srs_http_stack.hpp HTTP协议
│ ├── srs_protocol_amf0.cpp
│ ├── srs_protocol_amf0.hpp Amf0解析
│ ├── srs_protocol_format.cpp
│ ├── srs_protocol_format.hpp SrsRtmpFormat继承了SrsFormat, 代表RTMP格式
│ ├── srs_protocol_io.cpp
│ ├── srs_protocol_io.hpp 协议数据读取的IO封装接口,比如ISrsProtocolReadWriter
│ ├── srs_protocol_json.cpp
│ ├── srs_protocol_json.hpp json类
│ ├── srs_protocol_kbps.cpp
│ ├── srs_protocol_kbps.hpp 比特率统计相关
│ ├── srs_protocol_stream.cpp
│ ├── srs_protocol_stream.hpp 流读取,从ISrsReader读取数据到buffer里面
│ ├── srs_protocol_utility.cpp
│ ├── srs_protocol_utility.hpp 协议工具函数
│ ├── srs_raw_avc.cpp
│ ├── srs_raw_avc.hpp SrsRawH264Stream H264裸流解析,SrsRawAacStream AAC
裸流解析
│ ├── srs_rtmp_handshake.cpp
│ ├── srs_rtmp_handshake.hpp RTMP握手,包括SrsSimpleHandshake和
SrsComplexHandshake
│ ├── srs_rtmp_msg_array.cpp
│ ├── srs_rtmp_msg_array.hpp SrsMessageArray消息数组
│ ├── srs_rtmp_stack.cpp
│ ├── srs_rtmp_stack.hpp RTMP协议栈
│ ├── srs_rtsp_stack.cpp
│ └── srs_rtsp_stack.hpp RTSP协议栈
├── service
│ ├── srs_service_conn.cpp
│ ├── srs_service_conn.hpp ISrsConnection HTTP/RTMP/RTSP等对象的连接接口;
IConnectionManager管理连接接口
│ ├── srs_service_http_client.cpp
│ ├── srs_service_http_client.hpp SrsHttpClient HTTP客户端
│ ├── srs_service_http_conn.cpp11
│ ├── srs_service_http_conn.hpp HTTP连接 SrsHttpParser,SrsHttpMessage,
SrsHttpResponseWriter,SrsHttpResponseReader
│ ├── srs_service_log.cpp
│ ├── srs_service_log.hpp SrsConsoleLog日志相关
│ ├── srs_service_rtmp_conn.cpp
│ ├── srs_service_rtmp_conn.hpp SrsBasicRtmpClient RTMP客户端类
│ ├── srs_service_st.cpp
│ ├── srs_service_st.hpp 对st-thread协程的封装
│ ├── srs_service_utility.cpp
│ └── srs_service_utility.hpp service组件的工具类
└── utest
├── srs_utest_amf0.cpp
├── srs_utest_amf0.hpp
├── srs_utest_app.cpp
├── srs_utest_app.hpp
├── srs_utest_avc.cpp
├── srs_utest_avc.hpp
├── srs_utest_config.cpp
├── srs_utest_config.hpp
........... 还有其他utest文件,这里忽略
8 directories, 203 files
ava 监控直播流rtsp协议转rtmp、hls、httpflv协议返回浏览器
目录
在做之前的项目的时候有一个对接摄像头实时播放的需求,由于我们摄像头的购买量不是很多,海康威视不给我们提供流媒体云服务器,所以需要我们自己去 一个去满足我们能在浏览器看到监控画面。项目源代码在以前公司没有拷贝就不能复习,最近又在准备面试,所以写了这个博客来复盘和扩展一下,由于我现在没有Liunx,我就用Windows来演示,生产环境还是要使用Liunx,下面这些操作在Liunx也是一样的流程,大家自行百度。
媒体流协议对比 | ||||
协议 | HttpFlv | RTMP | HLS | Dash |
全称 | FLASH VIDEO over HTTP | Real Time Message Protocol | HTTP Living Streaming | |
传输方式 | HTTP长连接 | TCP长连接 | HTTP短连接 | HTTP短连接 |
视频封装格式 | FLV | FLV TAG | TS文件 | Mp4 3gp webm |
原理 | 同 RTMP ,使用HTTP协议(80端口) | 每个时刻的数据收到后立刻转发 | 集合一段时间的数据,生成TS切片文件(三片),并更新m3u8索引 | |
延时 | 低 1~3秒 | 低 1~3秒 | 高 5~20秒(依切片情况) | 高 |
数据分段 | 连续流 | 连续流 | 切片文件 | 切片文件 |
Html5播放 | 可通过HTML5解封包播放 (flv.js) | 不支持 | 可通过HTML5解封包播放 (hls.js) | 如果dash文件列表是MP4, webm文件,可直接播放 |
其它 | 需要Flash技术支持,不支持多音频流、多视频流,不便于seek(即拖进度条) | 跨平台支持较差,需要Flash技术支持 | 播放时需要多次请求,对于网络质量要求高 |
nginx:下载地址: http://nginx-win.ecsds.eu/download/
nginx-rtmp-module:nginx 的扩展,安装后支持rtmp协议,下载地址: https://github.com/arut/nginx-rtmp-module
解压nginx-rtmp-module到nginx根目录下,并修改其文件夹名为nginx-rtmp-module(原名为nginx-rtmp-module-master)
到nginx根目录下的conf目录下复制一份nginx-win.conf 重命名 nginx-win-rtmp.conf
nginx-win-rtmp.conf:
#user nobody;
# multiple workers works !
worker_processes 2;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 8192;
# max value 32768, nginx recycling connections+registry optimization =
# this.value * 20 = max concurrent connections currently tested with one worker
# C1000K should be possible depending there is enough ram/cpu power
# multi_accept on;
}
rtmp {
server {
listen 1935;
chunk_size 4000;
application live {
live on;
# 播放时进行回调,如果HttpRespone statusCode不等于200会断开
# on_play http://localhost:8081/auth;
}
application hls {
live on;
# 开启hls切片
hls on;
# m3u8地址
hls_path html/hls;
# 一个切片多少秒
hls_fragment 8s;
# on_play http://localhost:8081/auth;
# on_publish http://localhost:8081/auth;
# on_done http://localhost:8081/auth;
}
}
}
http {
#include /nginx/conf/naxsi_core.rules;
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr:$remote_port - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# # loadbalancing PHP
# upstream myLoadBalancer {
# server 127.0.0.1:9001 weight=1 fail_timeout=5;
# server 127.0.0.1:9002 weight=1 fail_timeout=5;
# server 127.0.0.1:9003 weight=1 fail_timeout=5;
# server 127.0.0.1:9004 weight=1 fail_timeout=5;
# server 127.0.0.1:9005 weight=1 fail_timeout=5;
# server 127.0.0.1:9006 weight=1 fail_timeout=5;
# server 127.0.0.1:9007 weight=1 fail_timeout=5;
# server 127.0.0.1:9008 weight=1 fail_timeout=5;
# server 127.0.0.1:9009 weight=1 fail_timeout=5;
# server 127.0.0.1:9010 weight=1 fail_timeout=5;
# least_conn;
# }
sendfile off;
#tcp_nopush on;
server_names_hash_bucket_size 128;
## Start: Timeouts ##
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 30;
send_timeout 10;
keepalive_requests 10;
## End: Timeouts ##
#gzip on;
server {
listen 5080;
server_name localhost;
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root nginx-rtmp-module/;
}
location /control {
rtmp_control all;
}
location /hls {
# Serve HLS fragments
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
expires -1;
add_header Access-Control-Allow-Origin *;
}
#charset koi8-r;
#access_log logs/host.access.log main;
## Caching Static Files, put before first location
#location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
# expires 14d;
# add_header Vary Accept-Encoding;
#}
# For Naxsi remove the single # line for learn mode, or the ## lines for full WAF mode
location / {
#include /nginx/conf/mysite.rules; # see also http block naxsi include line
##SecRulesEnabled;
##DeniedUrl "/RequestDenied";
##CheckRule "$SQL >= 8" BLOCK;
##CheckRule "$RFI >= 8" BLOCK;
##CheckRule "$TRAVERSAL >= 4" BLOCK;
##CheckRule "$XSS >= 8" BLOCK;
root html;
index index.html index.htm;
}
# For Naxsi remove the ## lines for full WAF mode, redirect location block used by naxsi
##location /RequestDenied {
## return 412;
##}
## Lua examples !
# location /robots.txt {
# rewrite_by_lua '
# if ngx.var.http_host ~= "localhost" then
# return ngx.exec("/robots_disallow.txt");
# end
# ';
# }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000; # single backend process
# fastcgi_pass myLoadBalancer; # or multiple, see example above
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl spdy;
# server_name localhost;
# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_timeout 5m;
# ssl_prefer_server_ciphers On;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:ECDH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!eNULL:!MD5:!DSS:!EXP:!ADH:!LOW:!MEDIUM;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
nginx.exe -c conf\nginx-win-rtmp.conf
测试:浏览器输入 http://localhost:5080/stat,看到
代表安装成功
ffmpeg:一个处理音视频强大的库,我们需要用它来转协议,下载地址: https://www.gyan.dev/ffmpeg/builds/ ,这里可以下载essential和full版本,essential就是简版,只包含ffmpeg.exe、ffplay.exe、
ffprobe.exe, 而full版本就包含了动态库和相关头文件,方便我们在开发中调用。
将ffmpeg解压后里面的bin路径复制到Path里面去
cmd ffmpeg -version 命令看到代表成功
下载地址: https://www.videolan.org/vlc/
我这里截图是海康威视的
现在没有测试的流,我找了个点播的rtsp
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,用这个代替是一样的
ffmpeg强大,命令也是复杂,我们cmd 执行
ffmpeg -re -rtsp_transport tcp -stimeout 20000000 -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -buffer_size 1024000 -max_delay 500000 -codec:v libx264 -r 25 -rtbufsize 10M -s 1280x720 -map:v 0 -an -f flv rtmp://127.0.0.1:1935/live/test
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,是输入源头
rtmp://127.0.0.1:1935/live/test 是输出地址
如果没有报错的话,到现在rtsp就已经转换好了
ffmpeg命令学习: https://www.jianshu.com/p/df3216a52e59 、 https://blog.csdn.net/fuhanghang/article/details/123565920
我们打开VLC,媒体->打开网络串流->输入 rtmp://127.0.0.1:1935/live/test -> 播放
等待几秒钟看到有视频播放就是成功了
rtmp的优点是延迟低,效率高,但是在浏览器需要安装flash才能放,也就老版本的浏览器在用,rtmp可能会在别的地方支持,所以还是把他方式方法贴出来了。
在前面已经贴出来了,其中这几个是针对hls的
ffmpeg -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -vcodec libx264 -acodec aac -f flv rtmp://127.0.0.1:1935/hls/test
生成了代表一切正常
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>前端播放m3u8格式视频</title>
<!--https://www.bootcdn.cn/video.js/-->
<link href="https://cdn.bootcss.com/video.js/7.6.5/alt/video-js-cdn.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/video.js/6.6.2/video.js"></script>
<!--https://www.bootcdn.cn/videojs-contrib-hls/-->
<script src="https://cdn.bootcss.com/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>
</head>
<body>
<video id="myVideo" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" width="1080" height="708" data-setup='{}'>
<source id="source" src="http://127.0.0.1:5080/hls/test.m3u8" type="application/x-mpegURL">
</video>
</body>
<script>
// videojs 简单使用
var myVideo = videojs('myVideo',{
bigPlayButton : true,
textTrackDisplay : false,
posterImage: false,
errorDisplay : false,
})
myVideo.play() // 视频播放
myVideo.pause() // 视频暂停
</script>
</html>
source标签的src属性: http://你的nginx ip:nginx http端口/hls/test.m3u8
rtsp转HLS成功!
m3u8文件里面存储了一个索引,以文本格式打开是这样的
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:56
#EXT-X-TARGETDURATION:13
#EXTINF:10.381,
test-56.ts
#EXTINF:10.422,
test-57.ts
#EXTINF:13.453,
test-58.ts
m3u8文件它不是视频源,源头是ts后缀文件
转HLS协议及网页加载过程:
ffmepg收到rtsp的流时候,会等一个切片的时间,一个切片时间到了,切片ts会放到服务器中,同时m3u8文件中加一个索引,对应着新进入的切片。网页在加载m3u8的时候,就是读取m3u8中的的索引去加载ts文件,所以在不断的请求ts,对ts进行解析,不断的和TCP握手,这就是为什么HLS延迟高和对网速的要求高的原因,我们监控肯定是要延迟低的,HLS兼容性好,适合点播。
这个插件需要编译,教程: https://blog.csdn.net/KayChanGEEK/article/details/105095844
我这里已经编译好了,直接下载启动:
https://gitee.com/isyuesen/nginx-flv-file
看我git里面的https://gitee.com/isyuesen/nginx-flv-file/blob/master/conf/nginx.conf,和默认的config差别主要是添加了这几个
rtmp {
server {
listen 1935;
# 流复用的最大块大小
chunk_size 4000;
application liveapp {
live on;
# 推流开始
on_publish http://localhost:8081/auth;
# 推流关闭
on_publish_done http://localhost:8081/auth;
# 客户端开始播放
on_play http://localhost:8081/auth;
# 客户端结束播放
on_play_done http://localhost:8081/auth;
}
}
}
location /live {
flv_live on;
chunked_transfer_encoding on;
add_header 'Access-Control-Allow-Credentials' 'true'; #add additional HTTP header
add_header 'Access-Control-Allow-Origin' '*'; #add additional HTTP header
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header 'Cache-Control' 'no-cache';
}
nginx rtmp配置中有配置on_publish钩子接口 http://localhost:8081/auth,这个回调HttpResponse stausCode如果不等于200会拒绝I/O,更多回调钩子看: https://github.com/arut/nginx-rtmp-module/wiki/Directives#on_connect
@PostMapping("/auth")
public void getVideo(String token, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
if (token.equals("tokenValue")) {
httpServletResponse.setStatus(200);
} else {
// 拒绝服务
httpServletResponse.setStatus(500);
}
}
ffmpeg -re -rtsp_transport tcp -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4" -f flv -vcodec h264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -s 640*360 -q 10 "rtmp://127.0.0.1:1935/liveapp/test"
依赖 javaCV
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.2</version>
</dependency>
public class App {
public static void main( String[] args ) throws IOException, InterruptedException {
String name = "test";
// rtsp地址
String rtspDir = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";
// rtmp地址
String rtmpDir = "rtmp://192.168.0.140:1935/liveapp/" + name + "?token=tokenValue";
String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
ProcessBuilder pb = new ProcessBuilder(ffmpeg,
"-re",
"-rtsp_transport",
"tcp",
"-i",
rtspDir,
"-f",
"flv",
"-vcodec",
"h264",
"-vprofile",
"baseline",
"-acodec",
"aac",
"-ar",
"44100",
"-strict",
"-2",
"-ac",
"1",
"-f",
"flv",
"-s",
"640*360",
"-q",
"10",
rtmpDir
);
pb.inheritIO().start().waitFor();
}
}
如果你跟着我做的,那链接就是 http://127.0.0.1:18080/live?port=1935&app=liveapp&stream=test&token=tokenValue,在VLC播放器中点击媒体 -> 打开网络串流 -> 输入http://127.0.0.1:18080/live?port=1935&app=liveapp&stream=test&token=tokenValue -> 播放
有视频证明你离成功就差最后一步
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>播放http-flv</title>
</head>
<body>
<video id="videoElement"></video>
<script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.6.2/flv.min.js"></script>
<script>
if (flvjs.isSupported()) {
const videoElement = document.getElementById('videoElement');
const flvPlayer = flvjs.createPlayer({
type: 'flv',
url: 'http://127.0.0.1:18080/live?port=1935&app=liveapp&stream=test&token=tokenValue'
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load();
flvPlayer.play();
}
</script>
</body>
</html>
*请认真填写需求信息,我们会在24小时内与您取得联系。