抖音网友 發表於 2020-9-14 13:50:00

html5 播放 rtsp

<p>目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是&nbsp;html&nbsp;并不标准支持 RTSP 流。除了 Firefox&nbsp;浏览器可以直接播放 RTSP 流之外,几乎没有其他浏览器可以直接播放 RTSP 流。Electron 应用是基于 Chromium 内核的,因此也不能直接播放 RTSP 流。</p>
<p>在借助一定工具的情况下,可以实现在 Web 页面上播放 RTSP 流。本文介绍的方法可以应用于传统 Web 应用和 Electron 应用中,唯一的区别是将 Electron 应用的主进程当作传统 Web 应用的服务器。</p>
<p>&nbsp;</p>
<h2 id="h2_1">目前已有 RTSP 播放方案的对比</h2>
<p>既然是做直播,就需要延迟较低。当摄像头掉线时,也应当有一定的事件提示。处于这两点,对目前已有的已经实现、无需购买许可证的 RTSP 播放方案进行对比(处于原理阶段的暂时不分析)。</p>
<p><img src="http://api.fly63.com/vue_blog/public/Uploads/20200528/5ecfc6950ed8c.jpg"></p>
<p>&nbsp;</p>
<h2 id="h2_2">基于 flv.js&nbsp;的 RTSP 播放方案</h2>
<p>flv.js是 Bilibili 开源的一款&nbsp;html5 浏览器。依赖于 Media Source Extension 进行视频播放,视频通过 HTTP-FLV 或 WebSocket-FLV 协议传输,视频格式需要为 FLV 格式。</p>
<h3 id="h3_3">服务器端(主进程)</h3>
<p>服务器端采用 express + express-ws&nbsp;框架进行编写,当有 HTTP 请求发送到指定的地址时,启动 ffmpeg 串流程序,直接将 RTSP 流封装成 FLV 格式的视频流,推送到指定的 WebSocket 响应流中。</p>
<div>
<pre><code class="hljs javascript"><span class="hljs-keyword">import * <span class="hljs-keyword">as express <span class="hljs-keyword">from <span class="hljs-string">"express";
<span class="hljs-keyword">import * <span class="hljs-keyword">as expressWebSocket <span class="hljs-keyword">from <span class="hljs-string">"express-ws";
<span class="hljs-keyword">import ffmpeg <span class="hljs-keyword">from <span class="hljs-string">"fluent-ffmpeg";
<span class="hljs-keyword">import webSocketStream <span class="hljs-keyword">from <span class="hljs-string">"websocket-stream/stream";
<span class="hljs-keyword">import WebSocket <span class="hljs-keyword">from <span class="hljs-string">"websocket-stream";
<span class="hljs-keyword">import * <span class="hljs-keyword">as http <span class="hljs-keyword">from <span class="hljs-string">"http";

<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">localServer(<span class="hljs-params">) {
    <span class="hljs-keyword">let app = express();
    app.use(express.static(__dirname));
    expressWebSocket(app, <span class="hljs-literal">null, {
      <span class="hljs-attr">perMessageDeflate: <span class="hljs-literal">true
    });
    app.ws(<span class="hljs-string">"/rtsp/:id/", rtspRequestHandle)
    app.listen(<span class="hljs-number">8888);
    <span class="hljs-built_in">console.log(<span class="hljs-string">"express listened")
}

<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">rtspRequestHandle(<span class="hljs-params">ws, req) {
    <span class="hljs-built_in">console.log(<span class="hljs-string">"rtsp request handle");
    <span class="hljs-keyword">const</span> stream = webSocketStream(ws, {
      <span class="hljs-attr">binary: <span class="hljs-literal">true,
      <span class="hljs-attr">browserBufferTimeout: <span class="hljs-number">1000000
    }, {
      <span class="hljs-attr">browserBufferTimeout: <span class="hljs-number">1000000
    });
    <span class="hljs-keyword">let url = req.query.url;
    <span class="hljs-built_in">console.log(<span class="hljs-string">"rtsp url:", url);
    <span class="hljs-built_in">console.log(<span class="hljs-string">"rtsp params:", req.params);
    <span class="hljs-keyword">try {
      ffmpeg(url)
            .addInputOption(<span class="hljs-string">"-rtsp_transport", <span class="hljs-string">"tcp", <span class="hljs-string">"-buffer_size", <span class="hljs-string">"102400")<em><span class="hljs-comment">// 这里可以添加一些 RTSP 优化的参数
</span></em>            .on(<span class="hljs-string">"start", <span class="hljs-function"><span class="hljs-keyword">function (<span class="hljs-params">) {
                <span class="hljs-built_in">console.log(url, <span class="hljs-string">"Stream started.");
            })
            .on(<span class="hljs-string">"codecData", <span class="hljs-function"><span class="hljs-keyword">function (<span class="hljs-params">) {
                <span class="hljs-built_in">console.log(url, <span class="hljs-string">"Stream codecData.")
                                  <em><span class="hljs-comment">// 摄像机在线处理
</span></em>            })
            .on(<span class="hljs-string">"error", <span class="hljs-function"><span class="hljs-keyword">function (<span class="hljs-params">err) {
                <span class="hljs-built_in">console.log(url, <span class="hljs-string">"An error occured: ", err.message);
            })
            .on(<span class="hljs-string">"end", <span class="hljs-function"><span class="hljs-keyword">function (<span class="hljs-params">) {
                <span class="hljs-built_in">console.log(url, <span class="hljs-string">"Stream end!");
                                  <em><span class="hljs-comment">// 摄像机断线的处理
</span></em>            })
            .outputFormat(<span class="hljs-string">"flv").videoCodec(<span class="hljs-string">"copy").noAudio().pipe(stream);
    } <span class="hljs-keyword">catch (error) {
      <span class="hljs-built_in">console.log(error);
    }
}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div>
<p>为了实现较低的加载时间,可以为 ffmpeg 添加如下参数:</p>
<ul>
<li>analyzeduration 可以降低解析码流所需要的时间</li>
<li>max_delay 资料上写的具体作用不太记得了,效果没有 analyzeduration 明显</li>
</ul>
<p>当然这个实现还比较粗糙。当有多个相同地址的请求时,应当增加 ffmpeg 的输出,而不是启动一个新的 ffmpeg 进程串流。</p>
<p>&nbsp;</p>
<h3 id="h3_4">浏览器端(渲染进程)</h3>
<p>示例使用&nbsp;vue&nbsp;框架进行页面设计。</p>
<p><span style="position: relative; left: -100000px">广州vi设计公司 http://www.maiqicn.com</span> <span style="position: relative; left: -100000px">我的007办公资源网 https://www.wode007.com</span></p>
<div>
<pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">template&gt;
    <span class="hljs-tag">&lt;<span class="hljs-tag"><span class="hljs-name">div</span></span><span class="hljs-tag">&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">video <span class="hljs-attr">ref=<span class="hljs-string">"player"&gt;<span class="hljs-tag">&lt;/<span class="hljs-name">video&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-tag"><span class="hljs-name">div</span></span><span class="hljs-tag">&gt;
<span class="hljs-tag">&lt;/<span class="hljs-name">template&gt;

<span class="hljs-tag">&lt;<span class="hljs-name">script&gt;<span class="javascript">
<span class="hljs-keyword">import flvjs <span class="hljs-keyword">from <span class="hljs-string">"flv.js";
<span class="hljs-keyword">export <span class="hljs-keyword">default {
    <span class="hljs-attr">props: {
      <span class="hljs-attr">rtsp: <span class="hljs-built_in">String,
      <span class="hljs-attr">id: <span class="hljs-built_in">String
    },
    <span class="hljs-comment">/**
   * @returns {{player: flvjs.Player}}
   */
    data () {
      <span class="hljs-keyword">return {
            <span class="hljs-attr">player: <span class="hljs-literal">null
      }
    },
    mounted () {
      <span class="hljs-keyword">if (flvjs.isSupported()) {
            <span class="hljs-keyword">let video = <span class="hljs-keyword">this.$refs.player;
            <span class="hljs-keyword">if (video) {
                <span class="hljs-keyword">this.player = flvjs.createPlayer({
                  <span class="hljs-attr">type: <span class="hljs-string">"flv",
                  <span class="hljs-attr">isLive: <span class="hljs-literal">true,
                  <span class="hljs-attr">url: <span class="hljs-string">`ws://localhost:8888/rtsp/<span class="hljs-subst">${<span class="hljs-keyword">this.id}/?url=<span class="hljs-subst">${<span class="hljs-keyword">this.rtsp}`
                });
                <span class="hljs-keyword">this.player.attachMediaElement(video);
                <span class="hljs-keyword">try {
                  <span class="hljs-keyword">this.player.load();
                  <span class="hljs-keyword">this.player.play();
                } <span class="hljs-keyword">catch (error) {
                  <span class="hljs-built_in">console.log(error);
                };
            }
      }
    },
    beforeDestroy () {
      <span class="hljs-keyword">this.player.destory();
    }
}
<span class="hljs-tag">&lt;/<span class="hljs-name">script&gt;

<span class="hljs-tag">&lt;<span class="hljs-name">style&gt;<span class="css">
    <span class="hljs-selector-class">.demo-video {
      <span class="hljs-attribute">max-width: <span class="hljs-number">480px;
      <span class="hljs-attribute">max-height: <span class="hljs-number">360px;
    }
<span class="hljs-tag">&lt;/<span class="hljs-name">style&gt;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
</div><br><br>
来源:https://www.cnblogs.com/qianxiaox/p/13666264.html
頁: [1]
查看完整版本: html5 播放 rtsp