Nginx+FFmpeg打造自己的視頻直播服務(超級全面)

引言

現在很多項目都有視頻實時播放的功能需求,例如監(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模塊幫我們做了這一點來防止內存溢出的問題,生成的文件如下:
m3u8

為了可以直接用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ā)送 視頻流 獲取相關資源和安裝包

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容