引言
現在很多項目都有視頻實時播放的功能需求,例如監(jiān)控,直播等,原始的攝像頭采集的視頻流協(xié)議一般都是rtsp協(xié)議,在舊版的瀏覽器中使用FLASH可以支撐其進行播放,但是現在各大主流瀏覽器都關閉了對FLASH的支持,這就需要我們把rtsp轉為瀏覽器支持的http,業(yè)務體量很大的公司一般會把這種事情委托給專業(yè)的第三方公司去做,但很多公司在這方面沒有那么大的業(yè)務量,往往只是播放一下監(jiān)控錄像之類的需求,則是搭建了自己的流媒體服務器來應對,現在比較主流的方式是使用FFmpeg進行轉流,再使用Nginx進行轉發(fā),下面我們一起來看一下吧?。ㄋ璋惭b包請查看文末獲取)
安裝yasm和FFmpeg
安裝FFmpeg還是比較簡單的,但在安裝之前,需要先安裝一下yasm,否則執(zhí)行./configure時,報yasm/nasm not found or too old. Use --disable-yasm for a crippledbuild錯誤。
yasm是匯編編譯器,ffmpeg為了提高效率使用了匯編指令,如MMX和SSE等。所以系統(tǒng)中未安裝yasm時,就會報上面錯誤。
安裝yasm:
解壓安裝包:
tar zxvf yasm-1.3.0.tar.gz
切換路徑:
cd yasm-1.3.0
執(zhí)行配置:
./configure
編譯:
make
安裝:
make install
安裝FFmpeg:
解壓安裝包:
tar xvf ffmpeg-4.1.tar.xz
切換路徑:
cd ffmpeg-4.1
執(zhí)行配置:
./configure
編譯:
make
安裝:
make install
測試FFmpeg:
輸入ffmpeg -version命令,如下,安裝成功!
root@mach9:~# ffmpeg -version
ffmpeg version 3.1 Copyright (c) 2000-2016 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
configuration: --prefix=/usr/local/ffmpeg --enable-decoder=h264
libavutil 55. 27.100 / 55. 27.100
libavcodec 57. 48.101 / 57. 48.101
libavformat 57. 40.101 / 57. 40.101
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 46.102 / 6. 46.102
libswscale 4. 1.100 / 4. 1.100
libswresample 2. 1.100 / 2. 1.100
nginx安裝nginx-rtmp-module模塊
nginx的安裝方式大同小異,相信大家已經非常熟悉了,不多贅述,這里主要介紹一下如何在已安裝的nginx上添加nginx-rtmp-module模塊,因為想要通過nginx轉發(fā)視頻流需要這一個組件,相關依賴包請看文末。
查看原有nginx的配置參數并拷貝出來 (V大寫),如下,configure arguments:后面就是我們所需要的。
nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3)
built with OpenSSL 1.0.1f 6 Jan 2014
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx/
下載跟原有版本一樣的nginx安裝包,這里以nginx-1.18.0為例(如果之前的安裝包沒刪,可以直接用之前的)
解壓nginx-rtmp-module-master.zip(文末獲取安裝包)
unzip nginx-rtmp-module-master.zip
解壓nginx安裝包,cd到解壓目錄下,然后執(zhí)行配置:
./configure --prefix=/usr/local/nginx/(之前拷貝的參數) --add-module=/nginx-rtmp-module-master(填寫實際的解壓位置)
編譯:
make
編譯完成后,我們需要在我們 /nginx-1.18.0/objs/ 目錄下。找到剛剛編譯好的 nginx 文件( 沒有擴展名),然后將nginx文件復制到我們之前安裝的 /usr/local/nginx/sbin/ 目錄(以實際目錄為準),替換舊的 nginx 文件,替換之前記得備份。
接下來我們執(zhí)行nginx -V,可以發(fā)現已經有了nginx-rtmp-module模塊,至此,nginx安裝nginx-rtmp-module模塊成功!
修改nginx配置
nginx的rtmp-module模塊可以幫助我們接收ffmpeg推送的流媒體文件,使用http進行訪問。
在文件里加入下面內容(加在最外層,屬于獨立模塊):
rtmp{
server{
listen 1935;
application live{
live on;
record off;
}
application hls{
live on;
hls on;
hls_path /server/hls;
#hls_cleanup off;
hls_fragment 8s;
}
}
}
重載nginx
nginx -s reload
FFmpeg轉流推流
nginx配置完畢,接下來我們測試ffmpeg的轉流和向nginx推流,執(zhí)行以下命令:
ffmpeg -rtsp_transport tcp -i "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov" -vcodec copy -acodec copy -f flv -an -b 1024k -y "rtmp://127.0.0.1:1935/hls/mystream"
rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov是我找的公網rtsp測試地址,執(zhí)行完以上命令之后如下圖,則表示轉流成功:
轉流成功后在我們之前配置的nginx rtmp模塊的接收路徑下(/server/hls)會生成m3u8索引文件,m3u8其實就是ts文件的索引,ffmpeg會把一個直播源的數據分割成很多個ts文件,訪問m3u8可以獲取ts文件的播放順序,逐個播放,ts文件達到一定數量會自動刪除前面無用的ts,并且如果ffmpeg停止轉流,文件夾底下的文件也會自動清除,nginx的rtmp模塊幫我們做了這一點來防止內存溢出的問題,生成的文件如下:
為了可以直接用http訪問m3u8文件,我們在nginx的http模塊下加入以下配置:
server {
listen 8080;
location /hls {
#server hls fragments
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias /server/hls;
expires -1;
#跨域一定要放開
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
}
使用VLC軟件測試(下載地址 VLC下載):
打開網絡串流(填寫自己服務器的地址):
打開成功:
代碼實現自動轉流
在前面我們利用ffmpeg的轉流命令成功把rtsp視頻流轉化為了http流地址,但在實際的程序應用中不可能手動去做這些事情,所以我們利用java實現一個自動轉流方法,調用該方法返回轉流后的m3u8地址供前臺訪問,核心代碼如下:
public static List<Process> processes = new CopyOnWriteArrayList<>();
private final String hlsPath = "/server/hls/";
/**
* 避免process過多導致服務器卡死
* (正常操作應該是返回前臺一個唯一標識,當前臺關閉直播流的時候關閉對應的進程,這里我們簡單處理)
*/
@Scheduled(cron = "10 * * * * ?")
public void removeProcess() {
try {
//如果進程大于50個,去除第一個進程
if (processes.size() > 50) {
processes.get(0).destroy();
processes.remove(0);
log.warn("more than 50,remove the first success!!");
removeProcess();
}
} catch (Exception e) {
log.error("destroy process error??!");
}
}
@SneakyThrows
@Override
public static Map<String, Object> getM3u8(String rtspUrl) {
String uuid = UUID.randomUUID().toString().replaceAll("-","");
log.warn("===" + deviceNo + ":start change to m3u8...====");
//轉rtmp的shell 在hls目錄下會生成m3u8文件
String shell = "ffmpeg -rtsp_transport tcp -i \"" + rtspUrl + "\" -vcodec copy -acodec copy -f flv -an -b 1024k -y \"rtmp://127.0.0.1:1935/hls/mystream_" + uuid + "\"";
log.warn("======the shell is " + shell);
String[] cmd = new String[]{"sh", "-c", shell};
Process process = Runtime.getRuntime().exec(cmd);
//放入map中
processes.add(process);
log.warn("====change to m3u8 has run...");
File file = new File(hlsPath + "mystream_" + uuid + ".m3u8");
//循環(huán)查找m3u8文件
for (int i = 0; i < 600; i++) {
Thread.sleep(100);
if (file.exists()) {
log.warn("the m3u8 file has been found");
break;
}
}
//前臺拼接前面的ip:port或域名即可
map.put("m3u8Url", "/hls/mystream_" + uuid + ".m3u8");
return map;
}
利用上面的代碼我們可以封裝一個http服務來實現訪問接口自動轉流,這樣才算一個完成的流媒體服務!
前臺利用video.js播放視頻流
在前臺我們可以利用video.js來對m3u8索引文件進行播放,使用方式也十分簡單,代碼如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>前端播放m3u8格式視頻</title>
<link rel="stylesheet">
<script src='https://vjs.zencdn.net/7.4.1/video.js'></script>
<!-- videojs-contrib-hls 用于在電腦端播放 如果只需手機播放可以不引入 -->
<script src="https://cdn.bootcdn.net/ajax/libs/videojs-contrib-hls/5.15.0/videojs-contrib-hls.min.js"></script>
</head>
<body>
<style>
.video-js .vjs-tech {position: relative !important;}
</style>
<div>
<video id="myVideo" class="video-js vjs-default-skin vjs-big-play-centered" controls preload="auto" data-setup='{}' style='width: 60%;height: auto'>
<source id="source" src="你的m3u8地址" type="application/x-mpegURL"></source>
</video>
</div>
<div class="qiehuan" style="width:100px;height: 100px;background: red;margin:0 auto;line-height: 100px;color:#fff;text-align: center">切換視頻</div>
</body>
<script>
// videojs 簡單使用
var myVideo = videojs('myVideo', {
bigPlayButton: true,
textTrackDisplay: false,
posterImage: false,
errorDisplay: false,
})
myVideo.play()// 視頻播放
myVideo.pause() // 視頻暫停
var changeVideo = function (vdoSrc) {
if (/\.m3u8$/.test(vdoSrc)) { //判斷視頻源是否是m3u8的格式
myVideo.src({
src: vdoSrc,
type: 'application/x-mpegURL' //在重新添加視頻源的時候需要給新的type的值
})
} else {
myVideo.src(vdoSrc)
}
myVideo.load();
myVideo.play();
}
//測試地址
var src = 'http://1252093142.vod2.myqcloud.com/4704461fvodcq1252093142/f865d8a05285890787810776469/playlist.f3.m3u8';
document.querySelector('.qiehuan').addEventListener('click', function () {
changeVideo(src);
})
</script>
</html>
效果:
至此,實現完整的視頻直播服務成功!
結語
現在視頻直播功能出現在越來越多的項目當中,掌握直播服務器的搭建流程以及開發(fā)思路對我們來說還是比較重要的,本文只是簡單的做了一個雛形,還有很多地方可以優(yōu)化完善,希望可以對你有所幫助,下次更新再見啦!
附:關注公眾號 螺旋編程極客 發(fā)送 視頻流 獲取相關資源和安裝包