2021-10-11 15:20:01 星期一
demo

重要参考

https://ivampiresp.com/2020/04/05/use-the-rtmp-module-of-nginx-to-build-a-streaming-media
基本对着来就行。由于我是在网站上部署,而2021年新版本Chrome对HTTPS的执念非常之强,文中http localhost的方法在HTTPS的环境下常常就不管用了,在GitHub上翻看了很多配置文档才搞定HTTPS的配置,坑实在是太多了。

一些配置文件及常用命令

推流部分

srs配置

centos编译安装
https://github.com/ossrs/srs/wiki/v4_CN_Home#getting-started

Debian(Ubuntu应该差不多,我是用的Debian,按步骤做一点问题没有)
https://www.cnblogs.com/surplus/p/15319061.html

./srs/trunk/conf/https.hls.conf

# the config for srs to delivery hls
# @see https://github.com/ossrs/srs/issues/1657#issuecomment-722971676
# @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;
    https {
        enabled on;
        listen 8088;
        key /root/.acme.sh/binaryenfold.xyz/binaryenfold.xyz.key;
        cert /root/.acme.sh/binaryenfold.xyz/binaryenfold.xyz.cer;
    }
}
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;
    }
}

常用命令

以特定conf运行srs
/srs/trunk/objs/srs -c conf/https.hls.conf
/srs/trunk/objs/srs -c conf/https.flv.live.conf
执行脚本
bash /srs/trunk/scripts/lofi.sh
bash /srs/trunk/scripts/lofiflv.sh
查看srs状态
/srs/trunk/etc/init.d/srs status
关闭srs服务
/srs/trunk/etc/init.d/srs grace
重启srs
/srs/trunk/etc/init.d/srs restart
重载srs
/srs/trunk/etc/init.d/srs reload
查看脚本进程
ps -ef | grep lofi.sh

#screen-用screen是为了在退出ssh的时候保持服务运行,当然方法有很多了
screen -S name #name用于命名screen窗口用途
screen -ls     #查看进程
screen -r      #进入单一进程
screen -r -d 1805 #进入screen进程
screen -X -S 122128 quit #关闭某一进程
ctrl-a +d #保持进程的同时退出当前窗口

自己编写lofi.sh脚本,无限推流lofigirl(需要提前安装好ffmpeg)
./scripts/lofi.sh

#!/bin/sh
while true
do
    lofigirl="$(youtube-dl -g https://www.youtube.com/watch?v=5qap5aO4i9A)"
    youtube="$(ffmpeg -re -i ${lofigirl} -vcodec copy -acodec copy -bsf:a aac_adtstoasc -f flv -flvflags no_duration_filesize "rtmp://45.77.101.70:1935/live/livestream.m3u8")"
    echo "${youtube}"
    sleep 10
done

证书管理

推荐用acme.sh
注意秘钥格式只能用rsa(默认配置即可)。我原本用的一键脚本,结果设置的ecc秘钥(如下所示),srs死活验证不出来,看了srs的源码才知道只能用rsa,人家wiki里也不讲两句……

[Wed Oct  6 19:23:08 UTC 2021] Your cert is in: /root/.acme.sh/binaryenfold.xyz_ecc/binaryenfold.xyz.cer
[Wed Oct  6 19:23:08 UTC 2021] Your cert key is in: /root/.acme.sh/binaryenfold.xyz_ecc/binaryenfold.xyz.key
[Wed Oct  6 19:23:08 UTC 2021] The intermediate CA cert is in: /root/.acme.sh/binaryenfold.xyz_ecc/ca.cer
[Wed Oct  6 19:23:08 UTC 2021] And the full chain certs is there: /root/.acme.sh/binaryenfold.xyz_ecc/fullchain.cer

配置完以后应该就可以直接在Safari浏览器(因为hls本就是苹果开发的格式……)拉流了:https://binaryenfold.xyz:8088/live/livestream.m3u8.m3u8
当然用播放器rtmp也可以:
rtmp://binaryenfold.xyz:8080/live/livestream.m3u8.m3u8
当然http也可以:
http://binaryenfold.xyz:8080/live/livestream.m3u8.m3u8

前端

解决完推拉流的问题就到聊天室实现了。需要提的一点是我的npm包是宝塔面板帮我装的,需要自己配置一下centos全局环境变量export PATH=$PATH:/www/server/nvm/versions/node/v14.18.0/bin。把express,node.js,socket.io都用npm安装好后,再进行后面的步骤。
indexooo.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://img.moegirl.org.cn/common/7/72/Kanae_midsummer_Icon.png" sizes="32x32" />
    <link rel="icon" href="https://img.moegirl.org.cn/common/1/11/Kanae-0.png" sizes="192x192" />
    <link rel="apple-touch-icon" href="https://assets.leetcode-cn.com/aliyun-lc-upload/users/cutesnake/avatar_1632978144.png" />
    <script src="//cdnjs.loli.net/ajax/libs/mdui/0.4.3/js/mdui.min.js"></script>
    <title>Live</title>
    
    
</head>

<body class="mdui-drawer-body-left mdui-appbar-with-toolbar  mdui-color-yellow-200 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">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://blog.cutesnake.top" class="mdui-list-item mdui-ripple ">博客</a>
                    <a href="https://space.bilibili.com/14173132" class="mdui-list-item mdui-ripple ">bilibili</a>
                    <a href="https://leetcode-cn.com/u/cutesnake/" class="mdui-list-item mdui-ripple ">leetcode</a>
                </div>
            </div>
        </div>
    </div>
    <div class="mdui-container" style="float: left;">
        <br />
        <video 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('https://binaryenfold.xyz:8088/live/livestream.m3u8.m3u8');
                hls.attachMedia(video);
                hls.on(Hls.Events.MANIFEST_PARSED, function() {
                    video.play();
                });
            } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
                video.src = 'https://binaryenfold.xyz:8088/live/livestream.m3u8.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>直播源:<a href='https://www.youtube.com/watch?v=5qap5aO4i9A'>lofigirl,</a>  <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'>搭建教程</a>
        我主要在教程基础上解决了srs和socket.io的跨域问题,但是反代理那块socket.io还是没搞定,会报404,没办法大家还是用3000端口访问吧。<a href='https://blog.cutesnake.top/index.php/archives/69/'>这是我的一些配置文件和说明。</a>
        </p>
        
        
    </div>
</body>

</html>

后端

为了适配https,还是花了很长时间才东拼西凑出来。
index.js

const { readFileSync } = require("fs");
const { createServer } = require("https");
const { Server } = require("socket.io");

//使用express模块快速搭建web服务器
const express = require('express');
const app = express();

const httpsServer = createServer({
  key: readFileSync("/www/server/panel/vhost/cert/live.cutesnake.top/privkey.pem"),
  cert: readFileSync("/www/server/panel/vhost/cert/live.cutesnake.top/fullchain.pem")
}, app);


//使用socket.io监听事件
const io = new Server(httpsServer, {/*  */});

//使用express发送css js等静态资源
app.use(express.static('public'));

//express获得GET请求时将indexooo.html文件返回给浏览器
app.get('/',function(req,res){
    res.sendFile(__dirname + '/indexooo.html');
});

//socket监听连接事件
io.on('connection', function(socket){
  console.log('一个用户上线了');
  //socket监听失去连接的事件
  socket.on('disconnect', function(){
      console.log('一个用户下线了');
  });

//当socket监听到了'chat message'事件
  socket.on('chat message', function(msg){
   //将收到的信息返回给所有客户端
    io.emit('chat message',msg);
    console.log(msg);
  });
  
});

//服务器监听端口3000
httpsServer.listen(3000,function(){
    console.log('https server listening on *:3000');
})


写好以后node index.js启动服务。
到这基本就OK了。

反代理

最后,反代理一块,后面有篇参考写得很好。我这里socket.io会报404,还是https特有的证书问题,不过我代码应该是没问题的,出错的原因在于我服务器上有很多个证书,可能哪里默认读了其他的证书。如果只有单一一个网站的话应该就问题不大了。还是贴一下我的反代理代码。

#反向代理,这段代码在监听80和443端口的server大括号内
        location / {
            proxy_pass https://live.cutesnake.top:3000/;
            proxy_ssl_certificate  /www/server/panel/vhost/cert/live.cutesnake.top/fullchain.pem;
            proxy_ssl_certificate_key /www/server/panel/vhost/cert/live.cutesnake.top/privkey.pem;
            proxy_ssl_protocols TLSv1.1 TLSv1.2;
            proxy_ssl_ciphers   EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;

            proxy_ssl_verify_depth  1;
            proxy_ssl_session_reuse on;
    } 

一堆参考网站

nginx https反向代理设置
https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/
总体思路
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
https://www.jianshu.com/p/5539ccd8d9c4
反向代理
https://www.cnblogs.com/ysocean/p/9392908.html#_label3
http REFERER
http://www.ruanyifeng.com/blog/2019/06/http-referer.html
简单app
https://www.cnblogs.com/handongyu/p/6260209.html
express配置 https://blog.csdn.net/bwf_erg/article/details/70649536?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242
node.js https://nodejs.org/api/https.html#https_https_get_options_callback
socket.io
https://socket.io/docs/v4/server-initialization/#with-an-https-server
成果:
https://live.cutesnake.top:3000

#### 一些无关紧要的话
写博客用到emoji的话emoji后面所有的文字都会被卡掉,白写了好多次才发现,太坑了……