讀本文前,務必先閱讀前面這篇文章,手把手搭建流媒體服務器詳細步驟。因為本篇文章是在這篇文章的基礎上搭建。
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小時內與您取得聯系。