node通过ffmpeg将多路rtsp、rtmp流媒体转换为多端口websocket流供前端播放

node通过ffmpeg将多路rtsp、rtmp流媒体转换为多端口websocket流供前端播放

在此补充另一套方案

不使用(node-rtsp-stream)插件实现只占用一个端口的方案
链接: node实现简单的websocket服务器,基于ffmpeg将rtmp、rtsp流转码为ws流

1 安装node

node官网下载安装:https://nodejs.org/zh-cn/download/prebuilt-installer

2 安装ffmpeg

ffmpeg官网下载:https://ffmpeg.org/
github下载:https://github.com/FFmpeg/FFmpeg

3 【重要】使用node搭建rtsp、rtmp转码服务器(必须要提前安装ffmpeg)

// 初始化项目并安装插件node-rtsp-stream
npm init -y
npm install express
npm install node-rtsp-stream
 //注意!get-port可能存在版本问题导致冲突异常,建议不要安装太高版本,我这里采用的是npm install get-port@5.0.0
npm install get-port 

新建index.js作为主文件,代码如下

const Stream = require("node-rtsp-stream"); // 引入node-rtsp-stream模块,rtsp/rtmp转码工具
const express = require("express"); // 引入express模块,http模块封装了http协议
const getPort = require("get-port");
const app = express(); // 创建一个express实例,提供http服务
/* **************************************************http-config************************************************ */

// 设置跨域资源共享
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*"); // 允许任何源
  res.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
});
app.use((req, res, next) => {
  res.setHeader(
    "Content-Security-Policy",
    "default-src https: 'self'; script-src https: 'self' 'unsafe-inline' 'unsafe-eval' http://trusted-domain.com;"
  );
  next();
});
/* **************************************************var************************************************ */
// ws流使用端口列表
let PostList = [];
/* **************************************************api************************************************ */
/* 关闭组件进程未关闭 */
// Name of the stream, used to identify it in the API
// 定义接口


/* 清除某一端口任务 */
app.get("/cleraWsPort", (req, res) => {
  let query = req.query;
  if (PostList.length == 0) {
    res.send({ status: "success", message: "未找到该端口" });
    return;
  }
  PostList.forEach((element, index) => {
    if (element.port == query.port) {
      element.stream.stop();
      PostList.splice(index, 1);
      res.send({ status: "success", message: "关闭成功:" + port });
    } else {
      res.send({ status: "success", message: "未找到该端口" });
    }
  });
});

/* 清空所有端口任务 */
app.get("/cleraWsAll", async (req, res) => {
  for (let index = 0; index < PostList.length; index++) {
    await PostList[index].stream.stop();
  }
  PostList = [];
  console.log("PostList->>>", PostList);
  res.send({ status: "success", message: "已清空所有端口" });
});

/* rtmp、rtsp转weboscket流 */
app.get("/rtmpToWebsocket", (req, res) => {
  let query = req.query; // 获取请求参数
  // 获取该端口是否已经存在,存在则返回该端口,不存在则创建可用端口
  for (let index = 0; index < PostList.length; index++) {
    const element = PostList[index];
     /* 判断已记录的starem是否中断(stream.exitCode)?如果中断则执行stop命令结束端口占用 */
    if (element.stream.stream.exitCode !== null) {
      element.stream.stop()
    }
    /* 判断记录的视频中是否存在请求视频,如果存在则已存在推流,将记录中的websocket端口返回,否则新增websocket服务 */
    if (element.url === query.rtmpUrl && element.stream.stream.exitCode === null) {
      /* 存在则返回该端口 */
      res.send({ port: element.port, wsPath: element.name, message: '已存在该端口', data: element.stream.stream.exitCode })
      return
    }
  }
  // 该视频流为发现,获取可用端口,并进行发布websocket流
  getPort({ port: getPort.makeRange(20000, 21000) }) // 获取可用端口
    .then((port) => {
      try {
        // log
        console.log("rtmpUrl->>>", query.rtmpUrl);
        console.log("port->>>", port);
        // 创建一个Stream实例,将rtmpUrl转换为websocket流并发布到(getport)指定的端口
        let stream = new Stream({
          name: "name",
          streamUrl: query.rtmpUrl, //query.rtmpUrl, 
          wsPort: port, //18965,随机端口号
          wsPath: "/" + query.name, //暂时无效,如果需要使用效果需要修改videoStram.js中new WebSocket函数
          /* ffmpegOptions,其他配置 */
          ffmpegOptions: {
            // options ffmpeg flags
            "-stats": "", // 
            "-r": "60", //key,帧率
            "-s": "1920x1080", //帧像素
          },
        });

        PostList.push({ name: query.name, port: port, url: query.rtmpUrl, stream: stream });
      } catch (error) {
        console.log("error->>>", error);
      }

      res.send({ port, wsPath: query.name, message: '创建成功' });
    })
    .catch((err) => {
      console.error(`Error: ${err}`);
    });
});

/* ************************************************http_listen************************************************ */
// 监听3000端口
app.listen(9001, () => {
  console.log("Server running on http://localhost:9001");
});
// 执行命令为
node ./index.js
// 这里如果安装了nodemon可以使用以下命令
nodemon ./index.js
// 这里如果安装了pm2,可以使用以下命令(推荐,防止node因为异常导致的崩溃)
pm2 start ./index.js

传参:
rtmpUrl:‘rtmp或rtsp流媒体路径’
name:‘自己随机定义,唯一标识,name重复的rtmp或rtsp不会进行转码’
请求样例:
http://127.0.0.1:9001/rtmpToWebsocket?rtmpUrl=‘rtmp://xxx’&name=‘name1’
回参样例:
{
port:‘20000’,
message: “创建成功”
}

可以通过前端播放ws://127.0.0.1:20000了

4 前端(vue3)播放websocket流

// 安装jsmpeg-player
npm i jsmpeg-player
npm i axios
<script setup>
// jsmpeg播放器
import JSMpeg from 'jsmpeg-player'
import axios from 'axios'
import { onMounted, onBeforeUnmount, ref } from 'vue'
...
let videoDom = null
let wsport = 0
onMounted(() => {
  axios.get(`http://127.0.0.1:9001/rtmpToWebsocket?rtmpUrl='rtmp://xxx'&name='name1'`).then((res) => {
        wsport = res.data.port 
		videoDom = new JSMpeg.Player(`ws://127.0.0.1:` + wsport + `/`, {
        bufferSize: 8,
        videoBufferSize: 20484096,
        canvas: document.getElementById('video')
   		})
	})
})
...
onBeforeUnmount(() => {
  if (videoDom) {
    videoDom.stop()
    videoDom.destroy();
    console.log('视频连接已关闭');
  }
})
...
</script>
<template>
<canvas :id="video" style="width: 100%; height: 100%"></canvas>
</template>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlanAPlanB

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值