视频流传输协议(RTSP、RTMP、HLS)
视频相关的协议有很多,不同的公司,甚至有自己的协议标准。本文介绍的协议是本人开发中接触较多的几个,主要从前端对接的角度去了解和使用以及比对不同协议的优缺点。
1、RTSP
RTSP(Real Time Streaming Protocol)实时流传输协议,是TCP/IP协议体系中的一个应用层协议,由哥伦比亚大学、网景和RealNetworks公司提交的IETF RFC标准。该协议定义了一对多应用程序如何有效地通过IP网络传送多媒体数据。RTSP在体系结构上位于RTP(Realtime Transport Potocol 实时传输协议:提供时间标志,序列号以及其他能够保证在实时数据传输时处理时间的方法)和RTCP(Realtime Transport Control Potocol 实时传输控制协议:是RTP的控制部分,用来保证服务质量和成员管理。RTP和RTCP是一起使用的)之上,它使用TCP或UDP完成数据传输,rtsp负责建立和控制会话,rtp负责多媒体的传输,rtcp配合rtp做控制和流量统计,他们是合作的关系
RTSP具体数据传输交给RTP,提供对流的远程控制,RTP是基于 UDP协议的, UDP不用建立连接,效率更高;但允许丢包, 这就要求在重新组装媒体的时候多做些工作,RTP只是包裹内容信息,而RTCP是交换控制信息的,Qos是通过RTCP实现的
应用程序对应的是play, seek, pause, stop等命令,RTSP则是处理这些命令,在UDP传输时并使用RTP(RTCP)来完成。如果是TCP连接则不会使用RTP(RTCP)。
RTSP传输协议的视频流地址主要由rtsp://
开头,对于网络摄像头提供的RTSP视频流一般使用的地址如rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov
。
开发过程中他的缺点是H5原生不支持RTSP协议,所以在H5开发中无法使用RTSP协议进行视频流的播放,因此面对需要在H5中对接RTSP协议的视频流的情况下只能通过服务端去转换视频流协议实现。
2、RTMP
RTMP(Real Time Message Protocol)实时信息传输协议,是Adobe公司为Flash播放器和服务器之间提供音视频数据传输服务而设计的应用层私有协议,RTMP协议主要的特点有:多路复用,分包和应用层协议。
2.1、多路复用
多路复用(multiplex)指的是信号发送端通过一个信道同时传输多路信号,然后信号接收端将一个信道中传递过来的多个信号分别组合起来,分别形成独立完整的信号信息,以此来更加有效地使用通信线路。
简而言之,就是在一个 TCP 连接上,将需要传递的Message分成一个或者多个 Chunk,同一个Message 的多个Chunk 组成 ChunkStream,在接收端,再把 ChunkStream 中一个个 Chunk 组合起来就可以还原成一个完整的 Message,这就是多路复用的基本理念
2.2 、分包
RTMP协议的第二个大的特性就是分包,与RTSP协议相比,分包是RTMP的一个特点。与普通的业务应用层协议不一样的是,在多媒体网络传输案例中,绝大多数的多媒体传输的音频和视频的数据包都相对比较偏大,在TCP这种可靠的传输协议之上进行大的数据包传递,很有可能阻塞连接,导致优先级更高的信息无法传递,分包传输就是为了解决这个问题而出现的。RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发
RTMP相较于RTSP来说以前浏览器对它的支持比较好,能应用于浏览器,加载flash插件后就能直接播放,但是随着adobe公司宣布不再维护更新flash后,chrome等浏览器也逐渐放弃对flash的依赖,最终在2020年的12月彻底移除flash,因此RTMP协议视频流也无法在不支持flash的浏览器上播放
播放RTMP需要使用第三方库进行播放,当前使用的是videojs。
<link href="./video-js.css" rel="stylesheet" /> <script src="./video.js"></script> <video id="my-video" class="video-js" controls autoplay preload="auto" style="width: 800px; height: 400px"> <!-- rtmp://rtmp01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0 --> <source src="rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp" type="rtmp/flv" /> <p class="vjs-no-js"> To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="https://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a> </p> </video>
3、HLS
HLS(HTTP Live Streaming)是苹果公司提出的基于HTTP的流媒体网络传输协议。类似于MPEG-DASH,但是HLS更加简洁,它的基本原理也是服务端把文件或媒体流按照不同的码率切分成一个个小片段进行传输,客户端在播放码流时,可以根据自身的带宽及性能限制,在同一视频内容的不同码率的备用源中,选择合适码率的码流进行下载播放。在传输会话开始时,客户端首先需要下载描述不同码流元数据的M3U8索引文件(类似于DASH中的MPD文件)。
HLS的出现主要是为了解决RTMP协议存在的一些问题。比如RTMP协议不使用标准的HTTP接口传输数据,所以在一些特殊的网络环境下可能被防火墙屏蔽掉。但是HLS由于使用的HTTP协议传输数据,不会遇到被防火墙屏蔽的情况
另外对于负载,RTMP是一种有状态协议,很难对视频服务器进行平滑扩展,因为需要为每一个播放视频流的客户端维护状态。而HLS基于无状态协议(HTTP),客户端只是按照顺序使用下载存储在服务器的普通TS文件,做负责均衡如同普通的HTTP文件服务器的负载均衡一样简单。
虽然HLS有上述优势,但也同时存在延迟过大的劣势。采用HLS直播的视频流延时一般在10秒以上,而RTMP直播的延迟最低可达到3、4秒,因此,在对实时性要求较高的场合,如互动直播,就要慎用HLS了。
3.1、HLS流程
根据媒体流的生成及流向,HLS的结构可划分为如下几个部分:
从左到右讲,左下方的inputs的视频源是什么格式都无所谓,他与server之间的通信协议也可以任意(比如RTMP),总之只要把视频数据传输到服务器上即可。这个视频在server服务器上被转换成HLS格式的视频(既TS和m3u8文件)文件。细拆分来看server里面的Media encoder的是一个转码模块负责将视频源中的视频数据转码到目标编码格式(H264)的视频数据,视频源的编码格式可以是任何的视频编码格式。转码成H264视频数据之后,在stream segmenter模块将视频切片,切片的结果就是index file(m3u8)和ts文件了。图中的Distribution其实只是一个普通的HTTP文件服务器,然后客户端只需要访问一级index文件的路径就会自动播放HLS视频流了。
3.2、M3U8文件描述
#EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:1 #EXT-TS-OFFSET-BEGIN:1 #EXT-X-DISCONTINUITY #EXTINF:1.997, http://whhls4.ys7.com:7888/openlivedata/E68018732_1_2/a4e0dbf80fed42068987b1f140b19bf6-1.ts?Usr=8dfa3df53cef47c1b712aa70475d1870 #EXT-TS-OFFSET-BEGIN:3 #EXTINF:1.997, http://whhls4.ys7.com:7888/openlivedata/E68018732_1_2/a4e0dbf80fed42068987b1f140b19bf6-2.ts?Usr=8dfa3df53cef47c1b712aa70475d1870 #EXT-TS-OFFSET-BEGIN:5 #EXTINF:1.996, http://whhls4.ys7.com:7888/openlivedata/E68018732_1_2/a4e0dbf80fed42068987b1f140b19bf6-3.ts?Usr=8dfa3df53cef47c1b712aa70475d1870
#EXTM3U:每一个M3U文件第一行必须是这个tag,请标示做用
#EXT-X-VERSION:用以标示协议版本
#EXT-X-ALLOW-CACHE:是否允许cache
#EXT-X-TARGETDURATION:指定最大的媒体段时间长(秒)。所以#EXTINF中指定的时间长度必须小于或是等于这个最大值
#EXT-X-MEDIA-SEQUENCE:每一个media URI 在 PlayList中只有唯一的序号,相邻之间序号+1,默认为0
#EXT-TS-OFFSET-BEGIN:当前视频的时间戳是(相对首次播放时间)
#EXT-X-DISCONTINUITY:表明其前一个切片与下一个切片之间存在中断,当文件格式、数字、类型、媒体标识符等变化时需要使用该标识符
#EXTINF:指定每个媒体段(ts)的持续时间,这个仅对其后面的URI有效,每两个媒体段URI间被这个tag分隔开
更多M3U8文件属性不在这里详细说明,需要了解的可以自行了解。
3.3、HLS的使用
HLS是提供一个m3u8地址,Apple的Safari浏览器直接就能打开m3u8地址,譬如:
http://hls01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0.m3u8
Android不能直接打开,需要使用html5的video标签,然后在浏览器中打开这个页面即可,譬如:
<video style="width: 100%;" autoplay controls autobuffer muted playsInline webkit-playsinline src="http://hls01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0.m3u8" type="application/vnd.apple.mpegurl"> </video>
PC端播放HLS需要通过转换后才能播放,当前所选择的是hls.js,它通过将MPEG-2传输流和AAC / MP3流转换为ISO BMFF(MP4)片段,实现如下:
<body> <video id="video" controls playsInline webkit-playsinline muted></video> </body> <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> <script> (function(){ var Hls = window.Hls; var url = 'http://hls01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0.m3u8'; if (Hls.isSupported()) { var hls = new Hls(); hls.loadSource(url); hls.attachMedia(video); // 将hls stream attach到video // 监听MANIFEST_PARSED事件,通知video开始播放 hls.on(Hls.Events.MANIFEST_PARSED, function () { video.play(); }) } else if (video.canPlayType('application/vnd.apple.mpegurl')) { video.src = url; video.addEventListener('canplay', function () { video.play(); }) } })() </script>
4、协议转换
一般来说网络摄像头输出的视频流rtsp会占较多数,例如海康、大华等摄像头厂商。那面对输出的rtsp流但是又需要再浏览器端播放实时监控我们又该如何呢,在这种情况下我们需要对视频流进行转换,比如将rtsp转换为rtmp或hls、rtmp转换为hls等。而协议之间的恶转换需要用到工具,本文使用到的是FFMPEG,它是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序,提供了录制、转换以及流化音视频的完整解决方案。FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、[Mac OS X](https://baike.baidu.com/item/Mac OS X/470629)等。
4.1、环境安装
需要转换视频流并能使用我们需要搭建一个简易的流媒体服务,这里我们使用nginx搭建流媒体服务并提供视频流的转发。首先我们需要安装nginx并添加nginx 的rtmp module,通过rtmp module可以将nginx和ffmpeg组合成一个功能相对比较完善的流媒体服务器。
nginx安装命令:brew install nginx-full --with-rtmp-module
;
ffmpeg安装命令:brew install ffmpeg
nginx配合ffmpeg做流媒体服务器的原理是: nginx通过rtmp模块提供rtmp服务, ffmpeg推送一个rtmp流到nginx,然后客户端通过访问nginx来收看实时视频流. HLS也是差不多的原理,只是最终客户端是通过HTTP协议来访问的,但是ffmpeg推送流仍然是rtmp的。
4.2、nginx配置
在nginx.conf的http后面添加如下配置:
rtmp { server { listen 7001; # RTMP 直播流配置 application rtmplive { live on; } # HLS 直播流配置 application hls { live on; # 开启实时 hls on; #开启hls hls_path /Users/edy/Desktop/self/nginx/live; hls_fragment 2s; #一个ts文件的时长2s hls_playlist_length 6s; # HLS播放列表长度 } } }
配置HLS拉流,需要在http中添加一个location
location /live { types { # 指定格式 application/vnd.apple.mpegurl m3u8; video/mp2t ts; } root /Users/edy/Desktop/self/nginx; add_header Cache-Control no-cache; }
配置完成后重启nginx。
4.3、FFmpeg推流测试
RTMP流,推流至rtmplive:
ffmpeg -re -i rtmp://rtmp01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0 -vcodec libx264 -acodec aac -g 48 -ar 44100 -ac 1 -f flv -s 1920x1080 rtmp://127.0.0.1:7001/rtmplive/test1
HLS流,推流至hls:
ffmpeg -re -i rtmp://rtmp01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0 -vcodec libx264 -acodec aac -g 48 -ar 44100 -ac 1 -f flv -s 1920x1080 rtmp://127.0.0.1:7001/hls/test2
rtsp和rtmp作为来源流他们的推流方式一致,rtsp推流如下:
ffmpeg -re -i rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov -vcodec libx264 -acodec aac -g 48 -ar 44100 -ac 1 -f flv -s 1920x1080 rtmp://127.0.0.1:7001/hls/test2
现在我们的流媒体服务器就有两个实时流了,一个是rtmp的,一个是hls的;拉流地址如下
RTMP流:rtmp://127.0.0.1:7001/rtmplive/test1
HLS流:http://127.0.0.1:7071/live/test2.m3u8
在测试推流测试阶段,HLS流表现较明显,在nginx的临时目录下,直观的可看到m3u8索引文件和N多个.ts文件。m3u8列表会实时更新,且会动态更改当前播放索引切片(.ts)。这种实时更新的机制,不会使得.ts文件长时间存在于Nginx服务器上,且当推流结束之后,该目录下的内容会被全部清除,这样无形中减缓了nginx服务器的压力。
当然FFmpeg也可以将rtmp或其他的视频转为本地的hls保存在本地而不被清除:
ffmpeg -re -i rtmp://rtmp01open.ys7.com/openlive/7ebec024f34540f4be99d4f20c72f8b0 -vcodec libx264 -acodec aac -g 48 -ar 44100 -ac 1 -f segment -segment_list /Users/edy/Desktop/self/nginx/mu/playlist.m3u8 -segment_list_flags +live -segment_time 10 /Users/edy/Desktop/self/nginx/mu/out%03d.ts
HLS流:http://127.0.0.1:7071/mu/playlist.m3u8
FFmpeg配置项描述
FFmpeg 的命令行参数非常多,可以分成五个部分
ffmpeg {1} {2} -i {3} {4} {5}
全局参数
输入文件参数
输入文件
输出文件参数
输出文件
-re
: 将输入的读取速度降低到输入的本地帧速率-i
:输入文件,后面接我们的输入源地址-vcodec libx264
:强制使用libx264编解码方式-acodec aac
:音频使用codec编解码 如:-acodec AAC 使用AAC音频编码-g 48
设置图像组大小 这里设置GOP大小,也表示两个I帧之间的间隔(若不设置或设置为0可能会导致VLC播放输出的rtmp没有图像只有声音)-ar 44100
:设置音频采样率-ac 1
:设置音频通道 缺省为1,即单通道-f flv
:强制输出视频流采用格式flv-s 1920x1080
:设置帧大小 格式为WXH 缺省160X128