概述

众所周知,rtsp的流是无法在浏览器中播放的,这就导致海康摄像头、海康ISC等平台的视频流无法直接在浏览器中播放。

当下是web最盛行的时代,恨不得所有功能、系统、平台。。。都装在浏览器中。

本文简单介绍如何间接实现在浏览器中播放rtsp的流,涉及技术点和工具较多,本文仅做功能实现思路的梳理和简单的代码实践,后续整理更深入的实现原理。

大致流程为:

  • 准备一个rtsp流,可以通过直连摄像头、vlc自己推流等
  • 使用node技术栈搭建一个基于express的应用
  • 在express应用中通过 fluent-ffmpeg(可以简单认为是ffmpeg的包装器) 和ffmpeg进行交互
  • 使用ffmpeg解码rtsp为flv视频格式,并返回数据流
  • 在express应用中通过 express-ws 创建一个WebsocketServer服务,此服务接收ffmpeg返回的数据流,并发送到连接此服务的WebsocketClient上
  • 使用flvjs(b站开源)播放flv格式的视频,即充当WebsocketClient

    ,实际是通过接收WebsocketServer发送的数据并渲染到video标签上面

环境

本文示例代码是在如下环境下调试的

  • IDE:VsCode
  • node:16.15.0
  • npm:8.5.5
  • 脚手架:未使用express脚手架工具
  • ffmpeg:6.1.0

项目目录清单


¦ package-lock.json
¦ package.json
¦
+---client
¦ client.html
¦ flv-1.6.2.js
+---public
¦ index.html
¦ yikepingguo.png
¦
+---server
server.js
websocket.js
  • package.json :npm包管理器配置文件
  • client:WebsocketClient
    • client.html:一个单独的html页面,用来播放视频。独立于express,需要从windows资源管理器中访问,并在浏览器中打开
    • flv.js:当前最新版本1.6.2
  • public:express应用静态资源存放目录,当前有一个helloWorld的html页面和一张示例图片。仅作用验证express应用是否启动。
  • server:WebsocketServer
    • server.js:实例化express应用
    • websocker.js:实例化一个WebsockerServer;借助ffmpeg将rtsp转换为flv

项目搭建步骤

新建一个文件夹,假设取名 RtspToFlv,并在VsCode中打开此文件夹,后续所有操作均在此文件夹操作。

在RtspToFlv中创建server文件夹。

在RtspToFlv中创建client文件夹。

引入相关npm依赖

新建一个文件 package.json ,并填入如下内容:



{
    "name": "RtspToWebsocket",
    "version": "1.0.0",
    "main": "index.js",
    "scripts": {
        "start": "nodemon ./server/server.js"
    },
    "keywords": [],
    "author": {
        "email": "liangchen_beijing@163.com",
        "name": "一颗苹果",
        "url": ""
    },
    "license": "Apache-2.0",
    "dependencies": {
        "express": "4.18.3",
        "express-ws": "5.0.2",
        "nodemon": "3.1.0",
        "ws": "8.16.0",
        "websocket-stream": "5.5.2",       
        "fluent-ffmpeg": "2.1.2"
    }
}
  • script.start:配置启动脚本,执行server/server.js
  • nodemon:nodemon 是一种工具,可在检测到目录中的文件更改时通过自动重新启动 node 应用程序来帮助开发基于 node.js 的应用程序
  • dependencies:依赖包,均使用当前最新版本
  • fluent-ffmpeg:该库提供了一组函数和实用程序来抽象 ffmpeg 的命令行用法。简单来说就是包装了一层使用ffmpeg的胶水代码。要使用此库,需要已经安装了 ffmpeg。也就是说本机必须安装了ffmpeg并已经添加到了系统环境变量。

使用 npm install 安装所有的依赖。

实例化一个express应用

在server文件夹中新建一个server.js 文件,并填入如下内容:



const express = require('express')

const app = express()

const expressWs = require('express-ws')
const websocket = require('./websocket') expressWs(app);
app.use(express.static('public'))
app.use('/websocket', websocket)
app.get('*', (req, res) => {})
app.listen(8009, () => {
  console.log('server is listening on port 8009')
})
  • app.use('/websocket', websocket):将后面创建的websocker.js模块加载进来

创建WebsocketServer并解析rtsp

在server文件夹中新建一个 websocket.js 文件,并填入如下内容:



const express = require('express');
const expressWs = require('express-ws')
var webSocketStream = require("websocket-stream/stream");
var ffmpeg = require("fluent-ffmpeg"); const router = express.Router()
expressWs(router);
router.ws('/test', (ws, req) => {
  ws.send('连接成功')
  //推视频流
  const stream = webSocketStream(ws, {
    binary: true,
    browserBufferTimeout: 1000000
  }, {
    browserBufferTimeout: 1000000
  });
   var url = "rtsp://xxx:xxx@192.168.1.xxx:xxx/ch1/main/av_stream";
  try {
    ffmpeg(url)
      .addInputOption("-rtsp_transport", "tcp", "-buffer_size", "102400") // 这里可以添加一些 RTSP 优化的参数
      .on("start", function () {
        console.log(url, "Stream started.");
      })
      .on("codecData", function () {
        console.log(url, "Stream codecData.")// 摄像机在线处理
      })
      .on("error", function (err) {
        console.log(url, "An error occured: ", err.message);
      })
      .on("end", function () {
        console.log(url, "Stream end!");// 摄像机断线的处理
      })
      .outputFormat("flv").videoCodec("copy").noAudio().pipe(stream);
  } catch (error) {
    console.log(error);
  }
  //接收到消息
  ws.on('message', msg => {
    console.log("收到消息");
    ws.send(msg)
  })
})
module.exports = router
  • 创建了一个Websocket端点(/websocket/test),提供给客户端连接
  • webSocketStream:创建了一个stream对象,将ffmpeg解析出来的数据流存入stream中,并实时发送到了WebsocketClient
  • ffmpeg有丰富的配置参数,可以通过参照官方文档进行调优

至此,WebsocketServer已经搭建好了,使用 npm start 命令尝试启动express应用。

使用flv播放

从flvjs官网下载flv.js文件,并放到client文件夹下面。

在client文件夹中新建一个文件 client.html ,并填入如下内容:



<html>
<head>
    <meta charset="UTF-8">
    <title>websocket客户端,使用flvjs播放视频</title>
    <style>
        .websocket {
            width: 500px;
            height: 500px;
            border: 1px solid #ccc;
            margin: 10px auto 0 auto;
            text-align: center;
        }
        #receive-box {
            width: 300px;
            height: 200px;
            overflow: auto;
            margin: 0 auto 10px auto;
            border: 1px solid #25d1ff;
        }
        #msg-need-send {
            width: 300px;
            height: 100px;
            resize: none;
            border-radius: 5px;
        }
        #send-btn {
            border: none;
            border-radius: 5px;
            background: #25d1ff;
            padding: 5px 10px;
            color: #fff;
            cursor: pointer;
        }
        #exit {
            border: 1px solid #ccc;
            border-radius: 5px;
            background: none;
            padding: 5px 10px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h5>socket-client</h5>
    <div class="websocket">
        <div class="receive">
            <p>服务端返回的消息:</p>
            <div id="receive-box"></div>
        </div>
        <div class="send">
            <p>给服务端发送消息:</p>
            <textarea type="text" id="msg-need-send"></textarea>
            <p>
                <button id="send-btn">点击发消息给服务端</button>
            </p>
        </div>
        <button id="exit">关闭连接</button>
    </div>
    <h5>播放器</h5>
    <div>
        <video id="my-player" preload="auto" muted autoplay type="rtmp/flv">
            <source src="">
        </video>
    </div>
   
    <script src="./flv-1.6.2.js"></script>
    <script>
        window.onload = function () {
            // test();
            play();
           
        };
        //测试websocket收发消息
        function test() {
            const ws = new WebSocket('ws://127.0.0.1:8009/websocket/test')
            ws.onopen = e => {
                console.log(`WebSocket 连接状态: ${ws.readyState}`)
            }
            ws.onmessage = data => {
                console.log("接收到消息");
                const receiveBox = document.getElementById('receive-box')
                receiveBox.innerHTML += `<p>${data.data}</p>`
                receiveBox.scrollTo({
                    top: receiveBox.scrollHeight,
                    behavior: "smooth"
                })
            }
            ws.onclose = data => {
                console.log('WebSocket连接已关闭')
                console.log(data);
            }
            //发送msg
            var sendBtn = document.getElementById("send-btn");
            sendBtn.onclick = () => {
                ws.send(document.getElementById('msg-need-send').value)
            }
            //关闭websocket连接
            var exitBtn = document.getElementById("exit");
            exitBtn.onclick = () => {
                ws.close()
            }
        }
        //播放视频
        function play() {
            videoElement = document.getElementById('my-player');
            if (flvjs.isSupported()) {
                flvPlayer = flvjs.createPlayer({
                    type: 'flv',                    //媒体类型
                    //flv格式媒体URL,即ws-server地址
                    url: 'ws://127.0.0.1:8009/websocket/test',
                    isLive: true,                   //数据源是否为直播流
                    hasAudio: false,                //数据源是否包含有音频
                    hasVideo: true,                 //数据源是否包含有视频
                    enableStashBuffer: false        //是否启用缓存区
                }, {
                    enableWorker: false,            //不启用分离线程
                    enableStashBuffer: false,       //关闭IO隐藏缓冲区
                    autoCleanupSourceBuffer: true   //自动清除缓存
                });
                flvPlayer.attachMediaElement(videoElement); //将播放实例注册到节点
                flvPlayer.load();                   //加载数据流
                flvPlayer.play();                   //播放数据流
            } else {
                alert("not support flvjs");
            }
        }
       
    </script>
</body>
  • html文件中分两部分,第一部分是一个简单的Websocket功能测试,第二部分放了一个video的标签,flvjs将把视频流渲染到video中
  • js代码中有两个方法,test用于测试Websocket连接和通信,play方法用于播放flv视频流
  • url:注意url中path为/websocket/test,在服务的使用了

    express.Router,实际WebsocketServer对外提供服务的端点就是/websocket/test
  • flvjs参数:flvjs有丰富的各种参数和回调,可以参照官方文档做调优

浏览器中测试

进入windows的文件资源管理器并使用edge打开client.html,将可以看到rtsp视频流的画面已经渲染到了浏览器中,并且网络连接的地方不停的接收这WebSocketServer发送的flv视频流数据。

ws请求地址为:ws://127.0.0.1:8009/websocket/test

代码

引用

Rtsp转Flv在浏览器中播放的更多相关文章

  1. 使用 JS 嵌入的方式来加载 Flash 插件,在各浏览器中播放视频

    嵌入插件 使用 object 和 embed 标签 这种方法用到的是 Object 和 Embed 标签,可以看到 object 的很多参数和 embed 里面的很多属性是重复的.浏览器兼容性,有的浏 ...

  2. 浏览器中页面的visibility状态及变化监听

    需求 在浏览器中播放视频,当用户进行页面切换操作时.需要根据视频播放页是否处于可见状态,来控制视频的暂停及重新播放. 相关文档 参考MDN中,关于页面的可见性相关的API说明.https://deve ...

  3. 在Chrome、Firefox等高版本浏览器中实现低延迟播放海康、大华RTSP

    一.背景 现在到处是摄像头的时代,随着带宽的不断提速和智能手机的普及催生出火热的网络直播行业,新冠病毒的大流行又使网络视频会议系统成为商务会议的必然选择,因此RTSP实时视频流播放及处理不再局限于安防 ...

  4. 【开源技术分享】无需流媒体服务,让浏览器直接播放rtsp/rtmp的神器:EasyMedia

    不同于市面上其他需要各种转发到流媒体服务的中间件来说,EasyMedia不需要依赖任何nginx-rtmp,srs,zlmediakit等等第三方流媒体服务,只需要你有rtsp或者rtmp等等协议的视 ...

  5. asp.net 网页中播放 flash 和flv

    需求:在网页中播放powerpoint保存的pps文件和mp4文件 经过查阅:发现网页上直接播放pps文件比较麻烦(office web apps server),所以通过工具,将pps文件转换为sw ...

  6. 如果摄像头不支持Web Socket,猿大师播放器还能在网页中播放RTSP流吗?

    问: 我们的情况比较复杂,摄像头设备品牌和数量都比较多,分布在全国各地都有,地点分布比较广泛,有的甚至是比较老的型号,如果摄像头设备不支持Web Socket,猿大师播放器还可以在网页中播放RTSP流 ...

  7. 在iOS微信浏览器中自动播放HTML5 audio(音乐)的2种正确方式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. [JavaScript] audio在浏览器中自动播放

    audio 在浏览器中自动播放 autoplay 属性 autoplay 属性规定一旦音频就绪马上开始播放. 如果设置了该属性,音频将自动播放. 使用 autoplay 属性进行播放 //使用auto ...

  9. 解决H5微信浏览器中audio兼容-- 背景音乐无法自动播放

    我们知道,ios 在safari浏览器中,audio标签不能在没有用户交互的情况下自动播放或有js直接控制播放,这是系统限制的一些原因. 但是背景音乐在微信浏览器可以设置自动播放,config配置一下 ...

  10. 【bug解决】ios微信浏览器中背景音乐无法播放

    我记得之前在一次项目中,出现过浏览报错: 当时的文档链接如右:[解决]HTML5新标签audio的autoplay自动播放属性失效的解决方案 所以在这次H5的制作中,我使用了iframe来加载音频文件 ...

随机推荐

  1. 【6】VScode 无法在终端输入问题,提示:无法在只读编辑器中编辑

    相关文章: [1]VScode中文界面方法-------超简单教程 [2]VScode搭建python和tensorflow环境 [3]VSCode 主题设置推荐,自定义配色方案,修改注释高亮颜色 [ ...

  2. 6.1 C++ STL 序列映射容器

    Map/Multimap 映射容器属于关联容器,它的每个键对应着每个值,容器的数据结构同样采用红黑树进行管理,插入的键不允许重复,但值是可以重复的,如果使用Multimap声明映射容器,则同样可以插入 ...

  3. C# 笔记之基本语法

    C#是一种现代的.通用的编程语言,由微软公司开发和推广.它于2000年发布,是一种结构化.面向对象和组件化的语言,旨在为Windows操作系统和Microsoft .NET框架提供强大的支持.可用于开 ...

  4. P2216 [HAOI2007] 理想的正方形 题解

    题目链接:理想的正方形 比较明显的,我们可以用二维 ST 表解决,具体的二维 ST 表的实现,只需要知道一点: 对于 \(st[i][j][t]=max(i \sim i+2^t,j \sim j+2 ...

  5. 制作包含最新更新的Windows 10 LTSC 2021 ISO

    介绍 在制作桌面云windows 模板的时候,一般需要安装最新的更新.更新安装过程非常耗时,并且安装更新会导致桌面模板的磁盘空间膨胀.制作出的模板会占用很大的磁盘空间.如果不安装更新,模板大小约5G. ...

  6. 洛谷P2415 集合求和(数学问题,使用集合子集求和公式)

    可以知道对于一个有n个数据的集合,其子集个数有2^n个 至于证明可以这样理解,对于n个数据,其子集就是对数据进行组和,而对于每个位置上的数据,组合时仅有两种状态即有此数据或无此数据,也就是有两种可能, ...

  7. 【Flink入门修炼】1-3 Flink WordCount 入门实现

    本篇文章将带大家运行 Flink 最简单的程序 WordCount.先实践后理论,对其基本输入输出.编程代码有初步了解,后续篇章再对 Flink 的各种概念和架构进行介绍. 下面将从创建项目开始,介绍 ...

  8. 开源.NetCore通用工具库Xmtool使用连载 - OSS文件上传篇

    [Github源码] <上一篇> 介绍了Xmtool工具库中的图像处理类库,今天我们继续为大家介绍其中的OSS文件上传类库. 将本地文件上传到服务器是软件系统经常会遇到的需求,例如:设置用 ...

  9. UVA12467 Secret Word 题解

    题目传送门 前置知识 前缀函数与 KMP 算法 解法 考虑将 \(S\) 翻转后得到 \(S'\),然后就转化为求 \(S'\) 的一个最长子串使得其是 \(S\) 的前缀.使用 KMP 求解即可. ...

  10. VSCode 编写vue项目之一键生成.vue模版

    1.安装插件Vetur 2.新建用户片段(.vue代码模板) 在弹出的输入框输入:vue.json (如果没有反应,那就尝试只输入"vue") ,接着enter 3.将.vue模板 ...