最近,有个项目需要使用OpenCV
输出的图片推流到网页。这个东西相当的麻烦,主要是我以前从来没做过相关的东西。
这种需求相关的协议有三种:
协议 |
优点 |
缺点 |
RTSP |
超低时延,可以达到毫秒级。 |
技术实现复杂。 |
RTMP |
低时延,秒级;适应性好。 |
需要Flash 支持,目前不流行。 |
HLS |
动态切换码流;苹果安卓都可以用。 |
高时延,不适合做视频直播,一般用作点播或者音频广播。 |
首先排除RTSP
,这东西几乎没啥可用文档。
RTMP
是应用层
协议,全称Real Time Message Protocol
。Adobe
开发的协议,部署起来很简单,但是已经停止开发了。
协议细节请点击这里查看。
不过除非你要基于TCP/IP
写一个RMTP
应用,一般是不用太深入了解,只需要知道它能分组传输消息就行。
RTMP
是基于FLV
文件格式的协议,这种文件格式专门为流媒体设计,可以用一种持续不断“流”的方式进行传输。播放器拿到这种格式的分包后,会根据其中信息进行重排序,进而得到一个完整的媒体文件。
不同于RTMP
,HLS
同是应用层
协议,但是其是基于HTTP
协议传输的。
这个协议最大的特点就是把一个完整的媒体文件切分成许多短媒体,然后在一个m3u8
文件里控制这些短媒体文件的读取。
本质上是在传输这些切分好的文件。
下面是CCTV1
频道的语音主m3u8
文件,它指出了各种带宽应使用的源。
依据奈氏准则,理想低通信道
的最高码元传输速率
= 2W Baud
1 2 3 4 5 6 7 8 9 10
| #EXTM3U #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1800000,RESOLUTION=1280x720 /cctvwbcd/cdrmcctv1_1/index.m3u8?BR=td #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1350000,RESOLUTION=1024x576 /cctvwbcd/cdrmcctv1_1/index.m3u8?BR=ud #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=900000,RESOLUTION=854x480 /cctvwbcd/cdrmcctv1_1/index.m3u8?BR=hd #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,RESOLUTION=640x360 /cctvwbcd/cdrmcctv1_1/index.m3u8?BR=md
|
下面则是从主m3u8
找到的对应的子m3u8
,它控制着每个短流媒体的读取。
在直播场景下,子m3u8
不能有#EXT-X-ENDLIST
标志,否则浏览器会停止请求子m3u8
,进而导致播放列表无法更新,直播断流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #EXTM3U #EXT-X-VERSION:3 #EXT-X-ALLOW-CACHE:NO #EXT-X-TARGETDURATION:12 #EXT-X-MEDIA-SEQUENCE:1684822835 #EXTINF:12.000000, #EXT-X-PROGRAM-DATE-TIME:2023-05-29T16:33:34+0800 cdrmcctv1_1_1800-1684822835.ts #EXTINF:12.000000, #EXT-X-PROGRAM-DATE-TIME:2023-05-29T16:33:46+0800 cdrmcctv1_1_1800-1684822836.ts #EXTINF:12.000000, #EXT-X-PROGRAM-DATE-TIME:2023-05-29T16:33:58+0800 cdrmcctv1_1_1800-1684822837.ts #EXTINF:12.000000, #EXT-X-PROGRAM-DATE-TIME:2023-05-29T16:34:10+0800 cdrmcctv1_1_1800-1684822838.ts
|
实践证明,HLS
大多数时间都是用作点播,直播支持较差。
上面两个协议使用起来体验都不太好。
前者要求浏览器拥有Flash
支持,几乎只能使用本地的VLC
媒体播放器;后者则是要求对媒体进行切分,时间粒度较大,延时较高,而且直播支持不太完善。
所以,对于当下的直播,大多数使用的是HTTP-FLV
。
这种方式结合了RTMP
和HLS
的优点,使用HTTP
来分发FLV
流。
在浏览器的网络监视器中,可以看到一个持续不断的文件请求,类型是x-flv
。
实际上,我们也可以用HTTPS
来传输。
Nginx
有专门的包对RTMP
和HTTP-FLV
做了支持(HLS
一般访问文件即可),所以使用起来很简单。
首先需要从GitHub
上拉取nginx-http-flv-module。这个模块已经支持了RTMP
,所以不用另外加上别的模块。
解压后执行下面的命令进行编译安装:
1 2 3
| ./configure --add-module=/tmp/nginx-http-flv-module --with-http_ssl_module make make install
|
安完后默认在/usr/local/nginx
下,可执行文件在上述路径的sbin
下。
我的网站有SSL
证书,所以配置了HTTPS
。参考配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| worker_processes 1;
events { worker_connections 1024; }
rtmp { server { listen 1935; application live { live on; } } }
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65;
server { listen 443; server_name blueberrycat.site;
ssl on; ssl_certificate /root/blueberrycat.site_nginx/blueberrycat.site_bundle.pem; ssl_certificate_key /root/blueberrycat.site_nginx/blueberrycat.site.key;
location / { root html; index index.html index.htm; }
error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location /live { flv_live on; chunked_transfer_encoding on; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Headers' 'X-Requested-With'; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS'; add_header 'Cache-Control' 'no-cache'; } }
server { listen 80; server_name blueberrycat.site; rewrite ^(.*)$ https://$host$1 permanent; } }
|
推流地址可以是
1
| https://blueberrycat.site/live?port=1935&app=live&stream={ 自定义的推流名 }
|
也可以是
1
| rtmp://blueberrycat.site/live/{ 自定义的推流名 }
|
取决于你想用哪种协议。
回到项目,Python
与OpenCV
推流代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import cv2 import subprocess as sp
cap = cv2.VideoCapture(0)
fps = cap.get(cv2.CAP_PROP_FPS)
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) sizeStr = str(size[0]) + 'x' + str(size[1])
command = ['ffmpeg', '-y', '-f', 'rawvideo', '-pix_fmt', 'bgr24', '-s', sizeStr, '-r', str(fps), '-i', '-', '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-preset', 'ultrafast', '-f', 'flv', '-b', '1000000', 'rtmp://blueberrycat.site/live/123']
pipe = sp.Popen(command, stdin=sp.PIPE, shell=False, bufsize=10**8)
while cap.isOpened(): ret, frame = cap.read() if ret == True: cv2.imshow('frame', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break else: break
pipe.stdin.write(frame.tobytes())
|
为了测试这些协议,我在旧网站上做了大量的实验,结果直接把主机弄炸了,旧网站直接下线。
默哀。
别在 Ubuntu
上用 yum
装 g++
建议使用hlv.js
,Bilibili开发(难以置信)。
官方说明
An HTML5 Flash Video (FLV) Player written in pure JavaScript without Flash. LONG LIVE FLV!
😂
使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script src="flv.min.js"></script> <video id="videoElement"></video> <script> if (flvjs.isSupported()) { var videoElement = document.getElementById('videoElement'); var flvPlayer = flvjs.createPlayer({ type: 'flv', url: '{ 这里换成自己的推流 }' }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); } </script>
|
最终效果是这样的
