整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

手把手配置HLS流媒体服务器

读本文前,务必先阅读前面这篇文章,手把手搭建流媒体服务器详细步骤。因为本篇文章是在这篇文章的基础上搭建。

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"。

RS环境搭建

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

SRS源码目录

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协议返回浏览器

目录

  • 一:了解音视频流协议:
  • 二:方案一 rtsp 转rtmp1、下载nginx + nginx-rtmp-module3、cmd 到nginx根目录启动nginx8、查摄像头的rtsp协议格式10、测试rtmp是否转换成功12、为什么放弃了用rtmp
  • 四:方案三rtsp 转httpflv(采用)1、安装nginx-flv-module4.1 采用java代码去执行ffmepg命令6、前端使用flv.js播放:

需求背景:

在做之前的项目的时候有一个对接摄像头实时播放的需求,由于我们摄像头的购买量不是很多,海康威视不给我们提供流媒体云服务器,所以需要我们自己去 一个去满足我们能在浏览器看到监控画面。项目源代码在以前公司没有拷贝就不能复习,最近又在准备面试,所以写了这个博客来复盘和扩展一下,由于我现在没有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技术支持

播放时需要多次请求,对于网络质量要求高


二:方案一 rtsp 转rtmp

1、下载nginx + nginx-rtmp-module

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)

2、nginx配置文件

到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;
    #    }
    #}

}

3、cmd 到nginx根目录启动nginx

nginx.exe -c conf\nginx-win-rtmp.conf

测试:浏览器输入 http://localhost:5080/stat,看到

代表安装成功

4、下载ffmpeg安装

ffmpeg:一个处理音视频强大的库,我们需要用它来转协议,下载地址: https://www.gyan.dev/ffmpeg/builds/ ,这里可以下载essential和full版本,essential就是简版,只包含ffmpeg.exe、ffplay.exe、

ffprobe.exe, 而full版本就包含了动态库和相关头文件,方便我们在开发中调用。

5、配置ffmpeg环境变量

将ffmpeg解压后里面的bin路径复制到Path里面去

6、测试ffmpeg

cmd ffmpeg -version 命令看到代表成功

7、下载VLC播放器

下载地址: https://www.videolan.org/vlc/

8、查摄像头的rtsp协议格式

我这里截图是海康威视的

现在没有测试的流,我找了个点播的rtsp

rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4,用这个代替是一样的

9、执行ffmpeg命令

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

10、测试rtmp是否转换成功

我们打开VLC,媒体->打开网络串流->输入 rtmp://127.0.0.1:1935/live/test -> 播放

11、测试是否成功

等待几秒钟看到有视频播放就是成功了

12、为什么放弃了用rtmp

rtmp的优点是延迟低,效率高,但是在浏览器需要安装flash才能放,也就老版本的浏览器在用,rtmp可能会在别的地方支持,所以还是把他方式方法贴出来了。

三:方案二 rtsp转hls

1、nginx配置:

在前面已经贴出来了,其中这几个是针对hls的

2、执行ffmepg命令

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

3、查看nginx根目录 -> hls -> test.m3u8 是否生成

生成了代表一切正常

4、m3u8在网页上播放

<!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成功!

5、认识一下m3u8格式

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后缀文件

6、为什么放弃了用HLS

转HLS协议及网页加载过程:

ffmepg收到rtsp的流时候,会等一个切片的时间,一个切片时间到了,切片ts会放到服务器中,同时m3u8文件中加一个索引,对应着新进入的切片。网页在加载m3u8的时候,就是读取m3u8中的的索引去加载ts文件,所以在不断的请求ts,对ts进行解析,不断的和TCP握手,这就是为什么HLS延迟高和对网速的要求高的原因,我们监控肯定是要延迟低的,HLS兼容性好,适合点播。

四:方案三rtsp 转httpflv(采用)

1、安装nginx-flv-module

这个插件需要编译,教程: https://blog.csdn.net/KayChanGEEK/article/details/105095844

我这里已经编译好了,直接下载启动:

https://gitee.com/isyuesen/nginx-flv-file

2、nginx配置

看我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';
}

3、做java权限认证

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);
        }
    }

4、执行ffmepg命令:

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"

4.1 采用java代码去执行ffmepg命令

依赖 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();
    }
}

5、测试http-flv链接

如果你跟着我做的,那链接就是 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 -> 播放

有视频证明你离成功就差最后一步

6、前端使用flv.js播放:

<!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>

7、大功告成