SRS+flv.js打造兼容性较高的直播点播平台
Posted 懒企鹅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SRS+flv.js打造兼容性较高的直播点播平台相关的知识,希望对你有一定的参考价值。
*************************************************************************************************************************************************
注意:强烈建议使用srs3.0,srs2.0存在的问题(回调,跨域)等问题不需要修改源码,而且可以修改生成mp4视频。
回调:
# SRS推流开始,结束 def live_publish(request): # 添加磁盘检测 if not mounted(): return HttpResponse(1) params = str(request.body, encoding="utf-8") object = json.loads(params) l_uuid = object.get(\'stream\') live = Live.objects.get(uuid=l_uuid) live.status = \'living\' live.save() return HttpResponse(0)
跨域:
没测,一直用nginx代理
MP4:
后期打算直接生成mp4,替换之前的flv
*************************************************************************************************************************************************
1、公司之前用的是:
直播:rtmp+jwplayer
点播:h5(mp4文件)
弊端:兼容性差,貌似跟系统版本,浏览器,浏览器版本都有关。还有就是rtmp推流生成的文件是flv格式,需要转码成mp4才能点播。
2、SRS+flv.js的优点:
当然是兼容性大大提高了,在pc端谷歌,火狐都可以播放,手机端火狐可以,谷歌不行,其他没测。
3、上图,看看效果:
样式什么的没添加,官方的demon 直接copy过来。
4、flv.js下载,构建:
Github:https://github.com/Bilibili/flv.js
解压后进入mater:
构建:
npm install npm install -g gulp gulp release
在dist下生成了我们需要的js
flv.html:
<!DOCTYPE html> <html> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> </head> <body> <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: \'http://192.168.2.192/live/video.flv\' }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); } </script> </body> </html>
type可以是mp4,flv。url的类型要对应,可以是服务器的文件,也可以是rtmp推流的临时文件。
在这一步可以测试下点播是否正常,文件应该放在http服务器下以http协议访问,不能是文件形式访问。http服务器可以是nginx,python,tomcat等都可以
5、如果上一步成功,接下来就是搭建SRS服务器了
Github : https://github.com/ossrs/srs/wiki/v2_CN_SampleHttpFlv
这篇文章介绍的比较详细,下面是简单记录步骤:
假定你已经下载并编译好了SRS,可以参考:SRS服务器搭建,ffmpeg 本地推流
首先复制conf中的http.flv.live.conf为my.http.flv.live.conf,内容:
# the config for srs to remux rtmp to flv live stream. # @see https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHttpStream # @see full.conf for detail config. listen 1935; max_connections 1000; daemon off; srs_log_tank console; http_server { enabled on; listen 80; dir ./objs/nginx/html; } vhost __defaultVhost__ { http_remux { enabled on; mount [vhost]/[app]/[stream].flv; hstrs on; } dvr { # https://github.com/ossrs/srs/wiki/v2_CN_DVR enabled on; dvr_path ./objs/nginx/html/[app]/[stream].flv; dvr_plan session; dvr_duration 30; dvr_wait_keyframe on; time_jitter full; } }
这里该了http的服务端口为80,添加了保存rtmp流文件的配置,指定存储路径./objs/nginx/html/[app]/[stream].flv。
启动SRS:
./objs/srs -c conf/my.http.flv.live.conf
接下来就是推流了。
假定你安装了ffmpeg。
ffmpeg -re -i /root/Videos/video.flv -c copy -f flv rtmp://192.168.2.192/live/video
如果推流成功那就可以在VLC中播放rtmp://192.168.2.192/live/video了,这样之前的html中的url就是:http://192.168.2.192/live/video.flv,
把之前的html/js copy到SRS的/objs/nginx/html/ 下,访问 http://ip/flv.html(这时的http服务由SRS提供,和之前的不一样) ,注意ip要和html中的ip一致,否则会报跨域的错。
至此整个直播点播服务的雏形就搭建成功!
6、添加回调
希望在开始推流的时候srs请求python服务,修改资源的状态为正在直播,推流结束是再次回调,请求python服务,修改状态为停止直播
srs的配置:
# the config for srs to remux rtmp to flv live stream. # @see https://github.com/ossrs/srs/wiki/v2_CN_DeliveryHttpStream # @see full.conf for detail config. listen 1935; max_connections 1000; daemon off; srs_log_tank console; http_server { enabled on; listen 8080; dir ./objs/nginx/html; } vhost __defaultVhost__ { http_remux { enabled on; mount [vhost]/[app]/[stream].flv; hstrs on; } dvr { # https://github.com/ossrs/srs/wiki/v2_CN_DVR enabled on; dvr_path ./objs/nginx/html/[app]/[stream].flv; dvr_plan session; dvr_duration 30; dvr_wait_keyframe on; time_jitter full; } http_hooks { enabled on; on_publish http://localhost:8000/on_publish/; on_unpublish http://localhost:8000/on_unpublish/; } }
注意: on_publish的ip需要根据netstat -pantu 判断,看看监听在哪个地址,比如127.0.0.1:8000,那么就应该保持一致,
按理说写localhost也应该可以,在终端用crul localhos:8000 也是可以访问,但是回调时报错:
[2017-11-30 03:08:22.478][error][20398][220][11] dns resolve server error, ip empty. ret=1029(Resource temporarily unavailable) [2017-11-30 03:08:22.478][warn][20398][220][11] http client failed, server=localhost, port=8000, timeout=30000000, ret=1029 [2017-11-30 03:08:22.478][warn][20398][220][11] http connect server failed. ret=1029 [2017-11-30 03:08:22.478][error][20398][220][11] http post on_publish uri failed. client_id=220, url=http://localhost:8000/on_publish/, request={"action":"on_publish","client_id":220,"ip":"192.168.2.151","vhost":"__defaultVhost__","app":"live","tcUrl":"rtmp://192.168.2.134:1935/live","stream":"f345f5b0d34a11e78008365426bed70e"}, response=, code=-147690992, ret=1029(Resource temporarily unavailable) [2017-11-30 03:08:22.478][error][20398][220][11] hook client on_publish failed. url=http://localhost:8000/on_publish/, ret=1029(Resource temporarily unavailable) [2017-11-30 03:08:22.478][error][20398][220][11] http hook on_publish failed. ret=1029(Resource temporarily unavailable) [2017-11-30 03:08:22.478][error][20398][220][11] stream service cycle failed. ret=1029(Resource temporarily unavailable)
所以还是保持一致的好。
另外:uwsgi并不对外提供访问服务,只由nginx转发,所以服务不要监听在0.0.0.0:8000,更不要写内网ip如192.168.2.111这样的,应为不确定下次启动ip不变。
所以uwsgi最好还是监听在127.0.0.1:8000。
python:
# SRS推流开始,结束 def live_publish(request): params = str(request.body, encoding="utf-8") object = json.loads(params) l_uuid = object.get(\'stream\') live = Live.objects.get(uuid=l_uuid) live.status = \'living\' live.save() return HttpResponse(0) def live_unpublish(request): params = str(request.body, encoding="utf-8") object = json.loads(params) l_uuid = object.get(\'stream\') live = Live.objects.get(uuid=l_uuid) live.status = \'stop\' live.save() return HttpResponse(0)
官方文档的说明:
https://github.com/ossrs/srs/wiki/v2_CN_HTTPCallback
根据:an int value specifies the error code(0 corresponding to success)
大概是说要返回一个0,不过我尝试各种返回值0,“0“,{”code”:0}...都没用
一返回srs就报错:empty response
不知道为毛srs接收不到。
没办法,改源码:
在srs/trunk/src/app/srs_app_http_hooks.cpp
找到报错的位置:
// should never be empty. res = SRS_HTTP_RESPONSE_OK; if (res.empty()) { ret = ERROR_HTTP_DATA_INVALID; srs_error("invalid empty response. ret=%d", ret); return ret; }
在进入判断前先赋值:res = SRS_HTTP_RESPONSE_OK;
然后重新编译安装。
还可以打包传服务器上用。
./scripts/package.sh --x86-x64
当然这只是权宜之计,因为我不需要判断用户的权限来滤用户,所以不用控制response返回值,希望日后搞明白说明原因导致。
如果有读者知道原因,还请告知,谢谢。
7、视频编辑
剪切:
ffmpeg -ss 0:1:30 -t 0:0:20 -i input.avi -vcodec copy -acodec copy output.avi
-ss 开始时间,-t 持续时间
提取图片:
ffmpeg –i test.avi –r 1 –f image2 image-%3d.jpeg
封装:
ffmpeg –i video_file –i audio_file –vcodec copy –acodec copy output_file
flv快速添加关键帧(为了拖动播放):
yamdi -i tmp.flv -o 51e714ded33a11e7889a365426bed70e.flv
8、压力测试
~/Downloads/flazr-0.7-RC2# ./client.sh rtmp://192.168.2.134:1935/live/a54b2dceda5911e7a5b1365426bed70e -load 200
查看srs服务器的网卡信息:
ethtool eth0
查看 srs服务器的流量:
iftop
9、转了一圈回到原点
前段时间用以上方案搭建的直播点播系统测试结果还是比较满意的
笔记本(百兆网卡)网线直连开发板(千兆网卡):
子码流(100-150并发)主码流(10-20)
笔记本(千兆网卡)网线直连开发板(千兆网卡):
子码流(没测,不过不会超过1000,srs中有最大连接数设置)主码流(100-200)(目标就是支持100人在线观看)
但是前两天用录播主机推流到开发板,出现视频流畅,声音卡顿的现象。
之前怀疑过网络不畅通,视频源有问题,录播主机有问题,flv.js。
后来发现是flv.js在处理某些视频流,或视频文件(直播http-flv,点播xx.flv)会发生以上现象。
但是直接用VLC客户端播放没有声音卡顿的现象。
没办法,只好改回JWPlayer(播放rtmp),Video(播放mp4)
这就需要将推流的临时文件xxx.flv重新封装成xxx.mp4。
希望flv.js的后续版本可以解决这样的问题。
10、转机
测试几天发现只有录播设备播放文件通道的时候声音会卡顿,直接将其他电脑的音视频接入没有卡顿,但是随着播放时间加长,会出现声音延迟的现象。
11、环境和部署脚本:
├── CentOS-Base.repo ├── ffmpeg-3.4 ├── install.sh ├── my.http.flv.live.conf ├── nginx-1.12.2 ├── nohup.out ├── Python-3.5.0 ├── run.sh ├── SRS-Ubuntu12-armv7cpu-2.0.243 ├── stop.sh ├── touch ├── touch.conf ├── touch.ini └── yamdi-1.9
install.sh:
#!/bin/bash install_list=\'system python srs nginx deploy env ffmpeg yamdi\' #install_list=\'ffmpeg yamdi\' if [[ ${install_list} =~ system ]] then #替换yum源,更新系统 yum_path=\'/etc/yum.repos.d/\' for file in ${yum_path}* do end_str=${file:0-3} if [ "${end_str}" != \'bak\' ] then mv $file ${file}.bak fi done cp CentOS-Base.repo ${yum_path} yum update #安装基本工具 yum install net-tools yum install nc else echo \'>>>pass system\' fi if [[ ${install_list} =~ "python" ]] then #Python3.5安装 cd Python-3.5.0/ ./configure make make install pip3 install uwsgi cd .. else echo \'>>>pass python\' fi if [[ ${install_list} =~ "nginx" ]] then #nginx 安装 yum -y install zlib zlib-devel openssl openssl--devel pcre pcre-devel cd nginx-1.12.2/ ./configure make make install cd .. else echo \'>>>pass nginx\' fi if [[ ${install_list} =~ "srs" ]] then #srs 安装 yum install redhat-lsb -y cd SRS-Ubuntu12-armv7cpu-2.0.243/ ./INSTALL cd .. else echo \'>>>pass srs\' fi if [[ ${install_list} =~ "ffmpeg" ]] then #ffmpeg 安装 cd ffmpeg-3.4/ #./configure make make install cd .. else echo \'>>>pass ffmpeg\' fi if [[ ${install_list} =~ "yamdi" ]] then #yamdi 安装 cd yamdi-1.9/ make make install cd .. else echo \'>>>pass yamdi\' fi if [[ ${install_list} =~ "deploy" ]] then #部署项目 mkdir /opt/script/ cp my.http.flv.live.conf /usr/local/srs/conf/ cp touch.conf /usr/local/nginx/conf/ cp touch.ini /opt/script/ cp touch /opt/ mkdir /usr/local/nginx/html/images/ cp touch/tmp/* /usr/local/nginx/html/images/ else echo \'>>>pass deploy\' fi if [[ ${install_list} =~ "env" ]] then #安装项目依赖 pip3 install django==1.9.8 pip3 install xadmin pip3 install future pip3 install django_crispy_forms pip3 install django-formtools pip3 install httplib2 pip3 install six pip3 install django_import_export pip3 install django-cors-headers pip3 install django-pure-pagination yum install python-devel zlib-devel libjpeg-turbo-devel -y pip3 install Pillow else echo \'>>>pass env\' fi
run.sh:
#!/bin/bash #启动项目 #touch pkill -9 uwsgi cd /opt/ uwsgi --ini script/touch.ini & chmod 766 /opt/script/touchrnb.sock #nginx pkill -9 nginx cd /usr/local/nginx/ ./sbin/nginx -c conf/touch.conf & #srs pkill -9 srs cd /usr/local/srs/ ./objs/srs -c conf/my.http.flv.live.conf > /dev/null &
stop.sh:
#!/bin/bash #停止项目 #nginx pkill -9 nginx #srs pkill -9 srs #touch pkill -9 uwsgi
开机启动:
编辑/etc/rc.d/rc.local,添加run.sh脚步路径
12、文件瘦身
strip objs/srs
(arm版本:arm-hisiv300-linux-strip)
可以从7-8兆减到2-3兆
13、srs跨域
之前是nginx代理服务器和srs在同一台机器上,但是公司考虑到嵌入式板的性能问题,需要提供更换直播服务器的功能,为了避免修改nginx配置的问题,所以
直播服务器地址由后台配置,存到数据库,而不使用nginx代理。但是这样就有跨域的问题了,我使用的srs版本为2.0,目前解决跨域的方法是修改源码。
参考:https://github.com/ossrs/srs/issues/1002
修改 src/app/srs_app_http_stream.cpp
在486行添加 w->header()->set("Access-Control-Allow-Origin", "*");
重新编译安装即可
14、集群和负载均衡
集群很简单参考:https://github.com/ossrs/srs/wiki/v3_CN_SampleHttpFlvCluster
负载均衡:
如果集群较大推荐CDN,如果小集群可以用nginx
值得一提的是srs在接收nginx的转发请求时不是用的相对路径
nginx配置文件:
worker_processes 4; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; gzip on; client_max_body_size 4096m; upstream localhost{ ip_hash; server 127.0.0.1:8080; server 192.168.2.127:8080; } server { listen 80; server_name 192.168.2.192 ; charset utf-8; location / { add_header \'Access-Control-Allow-Origin\' \'*\'; proxy_pass http://127.0.0.1:8000/; } # 指定静态文件路径 location /static/ { alias /root/GitClient/touch/static_all/; index index.html index.htm; } location /uwsgi_http/ { proxy_pass http://localhost/; } } }
如果upstream localhost 改为upstream aaa,浏览器访问: http://127.0.0.1/uwsgi_http/live/123.flv
srs接收到的是:http://aaa/live/123.flv
所以srs服务器要配置hosts文件,使aaa指向127.0.0.1
这里我直接命名为localhost,这样就可以偷懒啦。
还有负载均衡策略指定ip_hash,因为源站和边缘之间的视频可能不同步,这样可以提高用户体验。
但是如果srs集群的性能差异较大,还是用weight策略好一点。
15、flv.js优化--低延时(为了导播,直播不需要)
<script> if (flvjs.isSupported()) { var videoElement = document.getElementById("myplayer"); var flvPlayer = flvjs.createPlayer({ type: \'flv\', isLive: true, url: \'{{ LIVE_URL }}{{ current_live.uuid }}.flv\', }, { enableWorker: false, enableStashBuffer: false, stashInitialSize: 1, lazyLoad: false, lazyLoadMaxDuration: 1, lazyLoadRecoverDuration: 1, deferLoadAfterSourceOpen: false, autoCleanupMaxBackwardDuration: 1, autoCleanupMinBackwardDuration: 1, statisticsInfoReportInterval: 1, fixAudioTimestampGap: false, }); flvPlayer.attachMediaElement(videoElement); flvPlayer.load(); flvPlayer.play(); } </script>
srs低延时配置:
vhost __defaultVhost__ { gop_cache off; queue_length 10; min_latency on; mr { enabled off; } mw_latency 100; tcp_nodelay on; }
video低延时:
videoElement.addEventListener(\'progress\', function() { var range = 0; var bf = this.buffered; var time = this.currentTime; while(!(bf.start(range) <= time && time <= bf.end(range))) { range += 1; } this.currentTime = this.buffered.end(range) - 0.01; });
设置video低延时会触发waiting事件,出现一个圆圈和降低屏幕亮度,有待处理...
这样大概可以把延时从2~3降到1秒左右。(环境不同可能有差别,在网线接交换机的情况下会比连WiFi要好)
延时和流畅不可兼得,需求不同要设置不同参数。
16、音频不同步和卡顿的解决
参考:https://github.com/Bilibili/flv.js/issues/136
解决方法是 fixAudioTimestampGap: false,注意这个配置要在config的位置
17、添加HLS流
官方文档:https://github.com/ossrs/srs/wiki/v3_CN_SampleHLS
vhost __defaultVhost__ { ... hls { enabled on; hls_fragment 10; hls_window 60; hls_path ./objs/nginx/html; hls_m3u8_file [app]/[stream].m3u8; hls_ts_file [app]/[stream]-[seq].ts; hls_dispose 10; } }
存在的问题:同一地址第一次正常,后面推的都不能看,第一次生成ts切片正常,后面的ts切片会重复丢弃和生成。
参考: 转hls输出时出现的问题 #894:https://github.com/ossrs/srs/issues/894
在SrsHls::on_unpublish的时候设置SrsHls::aac_samples=0后正常。
以上是关于SRS+flv.js打造兼容性较高的直播点播平台的主要内容,如果未能解决你的问题,请参考以下文章