SRS+flv.js打造兼容性較高的直播、點播平臺

*************************************************************************************************************************************************html

注意:強烈建議使用srs3.0,srs2.0存在的問題(回調,跨域)等問題不須要修改源碼,並且能夠修改生成mp4視頻。java

回調:node

# 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)

 跨域:python

  沒測,一直用nginx代理linux

MP4:nginx

  後期打算直接生成mp4,替換以前的flvgit

*************************************************************************************************************************************************github

1、公司以前用的是:

直播:rtmp+jwplayer數據庫

點播:h5(mp4文件)npm

弊端:兼容性差,貌似跟系統版本,瀏覽器,瀏覽器版本都有關。還有就是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一致,不然會報跨域的錯。

至此整個直播點播服務的雛形就搭建成功!

 

六、添加回調

但願在開始推流的時候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返回值,但願往後搞明白說明緣由致使。

若是有讀者知道緣由,還請告知,謝謝。

七、視頻編輯

剪切:

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

 

八、壓力測試

~/Downloads/flazr-0.7-RC2# ./client.sh rtmp://192.168.2.134:1935/live/a54b2dceda5911e7a5b1365426bed70e -load 200

查看srs服務器的網卡信息:

ethtool eth0

查看 srs服務器的流量:

iftop

九、轉了一圈回到原點

前段時間用以上方案搭建的直播點播系統測試結果仍是比較滿意的

筆記本(百兆網卡)網線直連開發板(千兆網卡):

子碼流(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的後續版本能夠解決這樣的問題。

 

十、起色

測試幾天發現只有錄播設備播放文件通道的時候聲音會卡頓,直接將其餘電腦的音視頻接入沒有卡頓,可是隨着播放時間加長,會出現聲音延遲的現象。

 

 

 

 

十一、環境和部署腳本:

├── 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腳步路徑

十二、文件瘦身

strip   objs/srs

(arm版本:arm-hisiv300-linux-strip)

能夠從7-8兆減到2-3兆

 

1三、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", "*");

從新編譯安裝便可

 

1四、集羣和負載均衡

集羣很簡單參考: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策略好一點。

 

1五、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要好)

延時和流暢不可兼得,需求不一樣要設置不一樣參數。

 

1六、音頻不一樣步和卡頓的解決

參考:https://github.com/Bilibili/flv.js/issues/136

解決方法是 fixAudioTimestampGap: false,注意這個配置要在config的位置

 

1七、添加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後正常。

相關文章
相關標籤/搜索