使用nginx的rtmp模块搭建一个流式媒体(直播)服务器

作者:iVampireSP.com 发布日期:2020年4月5日
分类:野生技术协会

近期在家里闲着没事干,然后和群员聊着聊着,突然想起了之前想要搭建的流式媒体服务器,但是没有搭建成功。然而在这个超长的寒假中,我成功实现了(后来发现很简单)

结构

在搭建后不久,我做了很多修改,大概是这个样子

前端:完全静态,因为是自己的流式媒体服务器,自己播着玩玩也用不到动态。

流式媒体服务器:Nginx 的rtmp模块。

后端:Nodejs,做实时评论用,关于为啥不用php等。。我觉得nodejs来的更快。当然代码都是Ctrl+C,Ctrl+V来的

前端

这里我推荐使用hls.js,因为hls流的话很方便,就是有点延时。。。代码可以自由发挥了。

搭建

我这里建议使用Debian系的Linux系统,当然Windows也不是不可以,我都会在后面放出。

为什么建议使用Debian系的Linux系统呢?因为apt一下很方便。其他可以可以自行搜索了,编译安装。

请额外放行端口:1935,3000(可能),80,443

PS:树莓派也可以

安装必要的软件包

首先准备一个纯净的系统(不是纯净的自己看着改代码)

# 更新系统
sudu apt update
# 安装nginx和rtmp模块及wget(万一没有)
sudo apt install nginx-full libnginx-mod-rtmp wget -y

设置nginx.conf

sudo vim /etc/nginx/nginx.conf

你需要在第一个server 前加一段

rtmp {
    server {
        listen 1935;
        application live {
            live on;
        }
        application hls {
            live on;
            hls on;
            hls_path /var/www/html/hls;
            hls_fragment 2s;
        }
    }
}

他看上去应该像这样:

hls_path你可以修改,但是建议放在和前端文件同级的一个目录下。

然后重启nginx

sudo systemctl restart nginx

此时rtmp服务器你就搭建完成了

就这么简单

现在你可以推个流试试了,串流密钥随便写。

使用OBS推流,服务器地址为:rtmp://IP:1935/live/,密钥随便

拉流:rtmp://IP:1935/live/你的密钥

HLS流:使用OBS推流,服务器地址为:rtmp://IP:1935/hls/,密钥随便

拉流:rtmp://IP:1935/hls/你的密钥

或者用浏览器播放hls的m3u8

http(s)://IP/hls/密钥.m3u8

实时评论

这里就直接用nodejs实现了

参考:https://www.jianshu.com/p/5539ccd8d9c4

首先安装nodejs和npm以及创建目录,安装依赖

sudo apt install nodejs npm -y
cd ~
mkdir livechat
cd livechat
npm install express socket.io

apt里的nodejs的版本是足够了的

创建一个目录,然后开始安装软件,代码在上面的URL里。

全部弄完后启动就行,然后试试能不能访问。能访问的话就可以配置nginx的反向代理了。

配置反向代理

为何要反向代理?因为支持https并且利用nginx的rtmp模块。

那为什么不直接用nodejs搭建rtmp服务器呢?因为我没尝试过,既然nginx都跑起来了,那顺便用nginx搭建其他网站也不是问题。

接下来你需要在现有的http{}中添加一个sever,你可以新建配置文件,或者直接在nginx.conf中写,如果你还要架设多个网站的话,我还是推荐新建配置文件,这里我就直接在nginx.conf中写了。

vim /etc/nginx/nginx.conf
server {
        listen   8081;
    server_name live.nwl.im;
        location / {
            proxy_pass http://127.0.0.1:3000/;
        add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

            if ($request_method = 'OPTIONS') {
                return 204;
            }
        }       
        # 为什么需要下面这段呢?因为我的教程里是设置的反代,如果不加这段是直接从nodejs那里读取,但是nodejs那里是没有ts和m3u8文件的。
        location /hls {  
            #server hls fragments  
            types{  
                application/vnd.apple.mpegurl m3u8;  
                video/mp2t ts;  
            }  
            # 把下面的alias后面的目录改成和hls_path相同的目录。
            alias temp/hls;  
            expires -1;  
            # 设置CORS,如果不需要将下面那一段删除
            add_header 'Access-Control-Allow-Origin' '*'; 
        }
    }

他看起来应该像这样(可能会和我的不一样,我的配置文件也许和你的环境不符合,请按照你的实际环境修改),当然我这里是加了ssl的。如果你更改了nodejs的端口,你也需要在上方配置文件中修改一下proxy_pass部分。

然后检查nginx的配置文件是否有误

sudo nginx -t

如果返回类似如下消息,你的流式媒体服务器搭建完成了,接下来就是前端。

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

重启nginx

sudo systemctl nginx restart

前端

这里我推荐前端使用nodejs,因为。。需要依靠socket.io

打开你按照上方URL中创建的index.html,你可以在里面加入如下视频标签和hls.js

<video muted="muted" preload="auto" id="video" controls></video>
        <script src="https://cdn.bootcss.com/hls.js/0.13.2/hls.min.js"></script>
        <script>
            var video = document.getElementById('video');
            if (Hls.isSupported()) {
                var hls = new Hls();
                hls.loadSource('http(s)://你的IP或者域名/密钥.m3u8');
                hls.attachMedia(video);
                hls.on(Hls.Events.MANIFEST_PARSED, function() {
                    video.play();
                });
            } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
                video.src = 'http(s)://你的IP或者域名/hls/密钥.m3u8';
                video.addEventListener('loadedmetadata', function() {
                    video.play();
                });
            }
        </script>

然后做些适当的修改,就完成了,接下来你可以把评论区整合进去。

我这放一下我的index.html,如果你喜欢的话,可以拿去用。

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, user-scalable=no" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta name="renderer" content="webkit">
    <link rel="stylesheet" href="//cdnjs.loli.net/ajax/libs/mdui/0.4.3/css/mdui.min.css" />
    <link rel="icon" href="https://ivampiresp.com/wp-content/uploads/2020/02/cropped-illust_78879291_20200207_181713-32x32.jpg" sizes="32x32" />
    <link rel="icon" href="https://ivampiresp.com/wp-content/uploads/2020/02/cropped-illust_78879291_20200207_181713-192x192.jpg" sizes="192x192" />
    <link rel="apple-touch-icon" href="https://ivampiresp.com/wp-content/uploads/2020/02/cropped-illust_78879291_20200207_181713-180x180.jpg" />
    <script src="//cdnjs.loli.net/ajax/libs/mdui/0.4.3/js/mdui.min.js"></script>
    <title>iVampireSPのLive</title>
</head>

<body class="mdui-drawer-body-left mdui-appbar-with-toolbar  mdui-theme-primary-indigo mdui-theme-accent-pink">
    <header class="mdui-appbar mdui-appbar-fixed">
        <div class="mdui-toolbar mdui-color-theme">
            <span class="mdui-btn mdui-btn-icon mdui-ripple mdui-ripple-white" mdui-drawer="{target: '#main-drawer', swipe: true}"><i class="mdui-icon material-icons">menu</i></span>
            <a href="#" class="mdui-typo-headline mdui-typo-title">iVampireSPのLive</a>

            <div class="mdui-toolbar-spacer"></div>
        </div>
    </header>
    <div class="mdui-drawer" id="main-drawer">
        <div class="mdui-list" mdui-collapse="{accordion: true}" style="margin-bottom: 76px;">
            <div class="mdui-collapse-item ">
                <div class="mdui-collapse-item-header mdui-list-item mdui-ripple">
                    <i class="mdui-list-item-icon mdui-icon material-icons mdui-text-color-blue">near_me</i>
                    <div class="mdui-list-item-content">菜单</div>
                    <i class="mdui-collapse-item-arrow mdui-icon material-icons">keyboard_arrow_down</i>
                </div>
                <div class="mdui-collapse-item-body mdui-list">
                    <a href="./" class="mdui-list-item mdui-ripple ">刷新</a>
                    <a href="https://ivampiresp.com" class="mdui-list-item mdui-ripple ">博客</a>
                </div>
            </div>
        </div>
    </div>
    <div class="mdui-container" style="float: left;">
        <br />
        <video muted="muted" preload="auto" class="mdui-video-fluid" poster="https://i.loli.net/2020/04/01/ylL1nsNXDdBxwMT.jpg" id="video" controls></video>
        <script src="https://cdn.bootcss.com/hls.js/0.13.2/hls.min.js"></script>
        <script>
            var video = document.getElementById('video');
            if (Hls.isSupported()) {
                var hls = new Hls();
                hls.loadSource('http(s)://你的IP或者域名/hls/iVampireSP.com.m3u8');
                hls.attachMedia(video);
                hls.on(Hls.Events.MANIFEST_PARSED, function() {
                    video.play();
                });
            } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
                video.src = 'http(s)://你的IP或者域名/hls/iVampireSP.com.m3u8';
                video.addEventListener('loadedmetadata', function() {
                    video.play();
                });
            }
        </script>
        <h1 class="mdui-text-color-theme">实时评论区</h1>
        <ul id="messages"></ul>

        <form name="chat" action="">
            <div class="mdui-textfield">
                <label class="mdui-textfield-label">昵称</label>
                <input class="mdui-textfield-input" id="username" type="text" placeholder="昵称" oninput="getUser()" autocomplete="on" required />

            </div>
            <div class="mdui-textfield">
                <label class="mdui-textfield-label">实时评论</label>
                <input class="mdui-textfield-input" id="comment" type="text" onclick="echoUser()" placeholder="请遵守格式,谢谢! 格式为:“昵称:内容”(不包括引号)" autocomplete="off" maxlength="35" required />
                <br />
                <button class="mdui-btn mdui-btn-raised mdui-ripple mdui-color-theme-accent">发送</button>
            </div>
        </form>

        <script src="/socket.io/socket.io.js"></script>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script type="text/javascript">
            function getUser() {
                userName = ($("#username").val());
            }

            function echoUser() {
                $("#comment").attr("value", userName + ':');
            }
            var socket = io();
            $('form').submit(function() {
                //点击发送按钮,提交输入的信息
                socket.emit('chat message', $('#comment').val());
                $('#comment').val(userName + ':');
                return false;
            });
            //接收到chat message时
            socket.on('chat message', function(msg) {
                //将chat message显示在页面
                $('#messages').append($('<li>').text(msg));
            });
        </script>

        <p>本直播页面是使用Nginx RTMP模块实现的,并且使用了hls.js和socket.io做到的接收流和实时评论,缺点是不能查看以前的评论。后端:nodejs。搭建教程:<a href="https://ivampiresp.com/2020/04/05/%e4%bd%bf%e7%94%a8nginx%e7%9a%84rtmp%e6%a8%a1%e5%9d%97%e6%90%ad%e5%bb%ba%e4%b8%80%e4%b8%aa%e6%b5%81%e5%bc%8f%e5%aa%92%e4%bd%93%ef%bc%88%e7%9b%b4%e6%92%ad%ef%bc%89%e6%9c%8d%e5%8a%a1%e5%99%a8%e5%b9%b6.html">点我查看</a></p>
    </div>
</body>

</html>

接下来就是Windows端,Windows端nodejs部分和上面是一样的,nginx.conf少做修改也可以。

带rtmp的nginx Windows端:https://github.com/illuspas/nginx-rtmp-win32

接下来你可以随心所欲的修改了,比如添加一些功能。

希望你使用愉快~

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=u06zxiwvgd12

标签: , , , , , ,

下一篇

Linux/macOS下玩Minecraft Bedrock(基岩版)

"使用nginx的rtmp模块搭建一个流式媒体(直播)服务器"
中有4条评论

  1. k_ying说道:

    tql,马克一下,之后想折腾

    1. iVampireSP.com说道:

      加油!(ง •_•)ง

  2. 一眉说道:

    路过吃瓜

留下足迹

Crafted with love by iVampireSP.com开往Travellings