如何快速的搭建简易的流媒体服务器

343 阅读4分钟

ps:后端用node koa2 框架 前端h5

1.node-media-server介绍

node-media-server 是一个基于 Node.js 的流媒体服务器,它提供了构建和管理实时音视频流媒体应用程序所需的功能。它是一个开源项目,具有灵活性和可扩展性,适用于各种流媒体应用场景。 以下是一些 node-media-server 的特点和功能:

  1. RTMP支持:node-media-server 支持 RTMP(Real-Time Messaging Protocol)协议,用于接收和传输实时的音视频流。RTMP 适用于实时直播和互动应用等场景。
  2. 多路并发流支持:node-media-server 具有多路并发流处理能力,可以同时处理多个流媒体的接收、转码、推流和录制等操作。
  3. 高性能和低延迟:node-media-server 的设计注重高性能和低延迟,使其适用于实时应用场景,如实时直播、互动直播和视频聊天等。
  4. 支持多种编码格式:node-media-server 支持多种常用的音视频编码格式,如 H.264、AAC、VP8 等,使其能够处理不同类型的流媒体数据。
  5. 功能丰富的 API:node-media-server 提供了丰富的 API,方便开发人员进行配置和管理。你可以通过编写代码来定制和扩展服务器的功能。
  6. 高度可配置:node-media-server 具有灵活的配置选项,允许你根据特定需求进行定制。你可以配置服务器的端口、流媒体路径、认证方式等。

2.安装使用

npm install node-media-server --save
npm install koa --save

新建app.js

const Koa = require('koa')
const app = new Koa()
const  NodeMediaServer  = require('node-media-server');
const numCPUs = require('os').cpus().length;
//配置 node-media-server
const config = {
  rtmp: {
    port: 1935, // RTMP服务器监听的端口,默认为1935
    chunk_size: 60000, // 数据块大小,默认64KB
    gop_cache: true, // 是否启用GOP缓存, 默认true
    ping: 30, //服务器发送ping消息的间隔,默认30s
    ping_timeout: 60 //客户端响应ping消息的超市时间,默认60s
  },
  http: {
    port: 8000, // http服务端监听的端口,默认8000
    allow_origin: '*' // 设置跨域允许的域 默认为‘*’(允许所有的域)
  },
  //cluster:{ // 多核模式
  // num:numCPUS
  //},
  //auth:{ //鉴权验证
  // play: true,
  // publish: true,
  // secret:'nodemedia2017privatekey'
  //}
}
// 例子
//1.请求过期时间为: 2017/8/23 11:25:21 ,则请求过期时间戳为:

//> 1503458721

//2.md5计算结合“完整流地址-失效时间-密钥”的字符串:

//> HashValue = md5("/live/stream-1503458721-nodemedia2017privatekey”)  
//> HashValue = 80c1d1ad2e0c2ab63eebb50eed64201a

//3.最终请求地址为

//> rtmp://192.168.0.10/live/stream?sign=1503458721-80c1d1ad2e0c2ab63eebb50eed64201a  
//> 注意:'sign' 关键字不能修改为其他的
// 创建NodeMediaServer实例
const nms = new NodeMediaServer(config)
nms.on('preConnect', (id, args) => {
  console.log('preConnect', id, args);
})
nms.on('postConnect', (id, args) => {
  console.log('postConnect', id, args);
})
nms.on('doneConnect', (id, args) => {
  console.log('doneConnect', id, args);
})
nms.on('prePublish', (id, StreamPath, args) => {
  console.log('prePublish', id, StreamPath, args);
})
nms.on('postPublish', (id, StreamPath, args) => {
  console.log('postPublish', id, StreamPath, args);
})
nms.on('donePublish', (id, StreamPath, args) => {
  console.log('donePublish', id, StreamPath, args);
})
nms.on('prePlay', (id, StreamPath, args) => {
  console.log('prePlay', id, StreamPath, args);
})
nms.on('postPlay', (id, StreamPath, args) => {
  console.log('postPlay', id, StreamPath, args);
})
nms.on('donePlay', (id, StreamPath, args) => {
  console.log('donePlay', id, StreamPath, args);
});
nms.run();
app.listen(3000);

运行指令 node app.js

3.推流工具

3.1 uniapp live-pusher组件

<template>
    <view>
        <live-pusher id='livePusher' ref="livePusher" class="livePusher" url="rtmp://127.0.0.1:1935/live/1234" //替换成自己的地址
        mode="SD" :muted="true" :enable-camera="true" :auto-focus="true" :beauty="1" whiteness="2"
        aspect="9:16" @statechange="statechange" @netstatus="netstatus" @error = "error"
        ></live-pusher>
        <button class="btn" @click="start">开始推流</button>
        <button class="btn" @click="pause">暂停推流</button>
        <button class="btn" @click="resume">resume</button>
        <button class="btn" @click="stop">停止推流</button>
        <button class="btn" @click="snapshot">快照</button>
        <button class="btn" @click="startPreview">开启摄像头预览</button>
        <button class="btn" @click="stopPreview">关闭摄像头预览</button>
        <button class="btn" @click="switchCamera">切换摄像头</button>
    </view>
</template>

<script>
    export default {
        data() {
			return {}
        },
        onReady() {
            // 注意:需要在onReady中 或 onLoad 延时
            this.context = uni.createLivePusherContext("livePusher", this);
        },
        methods: {
            statechange(e) {
                console.log("statechange:" + JSON.stringify(e));
            },
            netstatus(e) {
                console.log("netstatus:" + JSON.stringify(e));
            },
            error(e) {
                console.log("error:" + JSON.stringify(e));
            },
            start: function() {
                this.context.start({
                    success: (a) => {
                        console.log("livePusher.start:" + JSON.stringify(a));
                    }
                });
            },
            close: function() {
                this.context.close({
                    success: (a) => {
                        console.log("livePusher.close:" + JSON.stringify(a));
                    }
                });
            },
            snapshot: function() {
                this.context.snapshot({
                    success: (e) => {
                        console.log(JSON.stringify(e));
                    }
                });
            },
            resume: function() {
                this.context.resume({
                    success: (a) => {
                        console.log("livePusher.resume:" + JSON.stringify(a));
                    }
                });
            },
            pause: function() {
                this.context.pause({
                    success: (a) => {
                        console.log("livePusher.pause:" + JSON.stringify(a));
                    }
                });
            },
            stop: function() {
                this.context.stop({
                    success: (a) => {
                        console.log(JSON.stringify(a));
                    }
                });
            },
            switchCamera: function() {
                this.context.switchCamera({
                    success: (a) => {
                        console.log("livePusher.switchCamera:" + JSON.stringify(a));
                    }
                });
            },
            startPreview: function() {
                this.context.startPreview({
                    success: (a) => {
                        console.log("livePusher.startPreview:" + JSON.stringify(a));
                    }
                });
            },
            stopPreview: function() {
                this.context.stopPreview({
                    success: (a) => {
                        console.log("livePusher.stopPreview:" + JSON.stringify(a));
                    }
                });
            }
        }
    }
</script>

3.2 OBS

image.png

4.h5端使用

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>直播</title>
</head>
<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/flv.js/1.5.0/flv.min.js"></script>
    <video id="videoElement" width="100%" controls></video>
    <script>
        if (flvjs.isSupported()) {
            var videoElement = document.getElementById('videoElement');
            var flvPlayer = flvjs.createPlayer({
                type: 'flv',
                url: '' //替换成自己的地址
            });
            flvPlayer.attachMediaElement(videoElement);
            flvPlayer.load();
            flvPlayer.play();
        }
    </script>
</body>
</html>

5. 服务器nginx配置 rtmp

5.1 安装nginx-rtmp-module 模块

下载模块

cd /usr/local/nginx/modile //更改成自己安装的路径 
git clone https://github.com/arut/nginx-rtmp-module.git //下载模块

进入源编译文件夹,加上模块参数再编译


// 获取原来nginx参数
$ nginx -V //一定是大写
nginx version: nginx/1.20.2
built by gcc 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC) 
built with OpenSSL 1.1.1k  FIPS 25 Mar 2021
TLS SNI support enabled
configure arguments: --prefix=/usr/soft/nginx --with-http_stub_status_module --with-http_ssl_module //复制该段内容
$ cd nginx-1.20.2/  
$ ./configure --prefix=/usr/soft/nginx --with-http_stub_status_module --with-http_ssl_module --add-module=/usr/soft/nginx/nginx-rtmp-module
$ make //千万不能用 make install; 否则会把之前的已经安装的替换掉!!!

备份原有的已安装好的nginx

mv /usr/soft/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak

把新的nginx程序拷贝进来

cp objs/nginx /usr/soft/nginx/sbin/

验证并重启新程序

$ nginx -V
--add-module=/usr/soft/nginx/nginx-rtmp-module //后面出现这个则代表成功!
$ nginx -s reload // 重启服务

nginx.conf 配置

rtmp {
	server {
		listen 1935;
		chunk_size 4096;
		application live {
			live on;
			record off;
		}
	}
}
http {
................
}

6.感谢大家的阅读!!!