Rtsp轉Flv在瀏覽器中播放

Naylor發表於2024-03-12

目錄
  • 概述
  • 環境
  • 專案目錄清單
  • 專案搭建步驟
    • 引入相關npm依賴
    • 例項化一個express應用
    • 建立WebsocketServer並解析rtsp
    • 使用flv播放
  • 瀏覽器中測試
  • 程式碼
  • 引用

概述

眾所周知,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

程式碼

https://github.com/Naylor55/RtspToWebsocket

引用

  • ffmpeg:https://github.com/FFmpeg/FFmpeg
  • flvjs:https://github.com/bilibili/flv.js
  • express:https://github.com/expressjs/express

相關文章