html5 播放 rtsp
<p>目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是 html 并不标准支持 RTSP 流。除了 Firefox 浏览器可以直接播放 RTSP 流之外,几乎没有其他浏览器可以直接播放 RTSP 流。Electron 应用是基于 Chromium 内核的,因此也不能直接播放 RTSP 流。</p><p>在借助一定工具的情况下,可以实现在 Web 页面上播放 RTSP 流。本文介绍的方法可以应用于传统 Web 应用和 Electron 应用中,唯一的区别是将 Electron 应用的主进程当作传统 Web 应用的服务器。</p>
<p> </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> </p>
<h2 id="h2_2">基于 flv.js 的 RTSP 播放方案</h2>
<p>flv.js是 Bilibili 开源的一款 html5 浏览器。依赖于 Media Source Extension 进行视频播放,视频通过 HTTP-FLV 或 WebSocket-FLV 协议传输,视频格式需要为 FLV 格式。</p>
<h3 id="h3_3">服务器端(主进程)</h3>
<p>服务器端采用 express + express-ws 框架进行编写,当有 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><span style="position: relative; left: -100000px">https://www.51220.cn</span> <span style="position: relative; left: -100000px">51220网站目录</span></p>
<h3 id="h3_4">浏览器端(渲染进程)</h3>
<p>示例使用 vue 框架进行页面设计。</p>
<p> </p>
<div>
<pre><code class="hljs handlebars"><span class="xml"><span class="hljs-tag"><<span class="hljs-name">template>
<span class="hljs-tag"><<span class="xml"><span class="hljs-tag"><span class="hljs-name">div</span></span></span><span class="xml"><span class="hljs-tag">>
<span class="hljs-tag"><<span class="hljs-name">video <span class="hljs-attr">ref=<span class="hljs-string">"player"><span class="hljs-tag"></<span class="hljs-name">video>
<span class="hljs-tag"></<span class="xml"><span class="hljs-tag"><span class="hljs-name">div</span></span></span><span class="xml"><span class="hljs-tag">>
<span class="hljs-tag"></<span class="hljs-name">template>
<span class="hljs-tag"><<span class="hljs-name">script><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 <span class="hljs-template-variable">{{<span class="hljs-name">player: flvjs.Player}}<span class="xml"><span class="javascript">
*/
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-built_in">this.$refs.player;
<span class="hljs-keyword">if (video) {
<span class="hljs-built_in">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-built_in">this.id}/?url=<span class="hljs-subst">${<span class="hljs-built_in">this.rtsp}`
});
<span class="hljs-built_in">this.player.attachMediaElement(video);
<span class="hljs-keyword">try {
<span class="hljs-built_in">this.player.load();
<span class="hljs-built_in">this.player.play();
} <span class="hljs-keyword">catch (error) {
<span class="hljs-built_in">console.log(error);
};
}
}
},
beforeDestroy () {
<span class="hljs-built_in">this.player.destory();
}
}
<span class="hljs-tag"></<span class="hljs-name">script>
<span class="hljs-tag"><<span class="hljs-name">style><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"></<span class="hljs-name">style></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><br><br>
来源:https://www.cnblogs.com/Qooo/p/15066325.html
頁:
[1]