*************************************************************************************************************************************************
注意:強烈建議使用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后正常。
