zoukankan      html  css  js  c++  java
  • 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后正常。

  • 相关阅读:
    Docker 文档编译
    Docker CentOS 安装方法
    Docker CentOS 安装要求
    Docsify 的文档页面标题在那里设置
    Docsify 的 GitHub 链接在那里设置的
    GitHub 中如何启用 GitHub Pages 中的子域名
    Spring API 的 CORS 测试 提示错误 Reason: header ‘authorization’ is not allowed
    Spring API 的 CORS 测试
    CentOS 7 安装 JDK 11
    Postman API 获得文件如何保存
  • 原文地址:https://www.cnblogs.com/lanqie/p/7804466.html
Copyright © 2011-2022 走看看