Nginx+FFmpeg打造自己的视频直播服务(超级全面)

引言

现在很多项目都有视频实时播放的功能需求,例如监控,直播等,原始的摄像头采集的视频流协议一般都是rtsp协议,在旧版的浏览器中使用FLASH可以支撑其进行播放,但是现在各大主流浏览器都关闭了对FLASH的支持,这就需要我们把rtsp转为浏览器支持的http,业务体量很大的公司一般会把这种事情委托给专业的第三方公司去做,但很多公司在这方面没有那么大的业务量,往往只是播放一下监控录像之类的需求,则是搭建了自己的流媒体服务器来应对,现在比较主流的方式是使用FFmpeg进行转流,再使用Nginx进行转发,下面我们一起来看一下吧!(所需安装包请查看文末获取)

安装yasm和FFmpeg

安装FFmpeg还是比较简单的,但在安装之前,需要先安装一下yasm,否则执行./configure时,报yasm/nasm not found or too old. Use --disable-yasm for a crippledbuild错误。

yasm是汇编编译器,ffmpeg为了提高效率使用了汇编指令,如MMX和SSE等。所以系统中未安装yasm时,就会报上面错误。

安装yasm:

解压安装包:

tar zxvf yasm-1.3.0.tar.gz

切换路径:

 cd yasm-1.3.0

执行配置:

 ./configure

编译:

make

安装:

make install

安装FFmpeg:

解压安装包:

tar xvf ffmpeg-4.1.tar.xz

切换路径:

cd ffmpeg-4.1

执行配置:

 ./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转发视频流需要这一个组件,相关依赖包请看文末。

查看原有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到解压目录下,然后执行配置:

./configure --prefix=/usr/local/nginx/(之前拷贝的参数) --add-module=/nginx-rtmp-module-master(填写实际的解压位置)

编译:

make

编译完成后,我们需要在我们 /nginx-1.18.0/objs/ 目录下。找到刚刚编译好的 nginx 文件( 没有扩展名),然后将nginx文件复制到我们之前安装的 /usr/local/nginx/sbin/ 目录(以实际目录为准),替换旧的 nginx 文件,替换之前记得备份。

接下来我们执行nginx -V,可以发现已经有了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推流,执行以下命令:

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测试地址,执行完以上命令之后如下图,则表示转流成功:

转流截图

转流成功后在我们之前配置的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");
        //循环查找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 href="https://vjs.zencdn.net/7.4.1/video-js.css" 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>

效果:


浏览器效果

至此,实现完整的视频直播服务成功!

结语

现在视频直播功能出现在越来越多的项目当中,掌握直播服务器的搭建流程以及开发思路对我们来说还是比较重要的,本文只是简单的做了一个雏形,还有很多地方可以优化完善,希望可以对你有所帮助,下次更新再见啦!

附:关注公众号 螺旋编程极客 发送 视频流 获取相关资源和安装包

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容