一、知识点
常见的直播协议有以下几种
-
RTMP(Real-Time Messaging Protocol)实时消息传输协议:是一种用于互联网应用中的协议,最初由 Adobe 公司设计,用于 Flash Player 向 Flash Media Server 或其他支持 RTMP 的服务器之间传输音频、视频和数据。
-
HLS(HTTP Live Streaming):是苹果公司为移动设备(如 iPhone 和 iPad)和 Mac OS X
平台开发的 HTTP 数据流协议。HLS 使用标准的 HTTP 协议作为传输协议,可以有效地传输多种类型的媒体流,如音频、视频和文本。 -
FLV(Flash Video):是一种用于互联网电视(IPTV)和在线多媒体应用程序的 Adobe Flash 技术。FLV文件可以通过 Adobe Flash Player 在 Web 浏览器中播放。
-
HDS(HTTP Dynamic Streaming):是 Adobe 公司为 Flash Player
播放器设计的一种流式播放协议,它使用 HTTP 作为传输协议,可以有效地传输动态的音频和视频。 -
DASH(Dynamic Adaptive Streaming over HTTP):是一种使用 HTTP
协议传输音频和视频的流式传输协议,支持多种编码格式和分辨率。DASH支持自适应比特率流(ABR),可以根据用户的网络带宽和设备性能调整播放质量。 -
HTTP-FLV 是通过 HTTP 协议传输 FLV 视频流的一种技术,是直播领域的一种新型协议。在传统的 RTMP 协议中,需要使用专门的服务端软件实现直播服务,而 HTTP-FLV 则可以使用普通的 Web 服务器即可实现视频直播服务,因此可以降低服务器成本、提高可扩展性。
HTTP-FLV 技术基于 HTTP 协议和 FLV 格式,将视频数据以小块分割并存储在 Web 服务器上,用户使用 HTTP 请求获取这些分块数据并将其合并成完整的视频流。HTTP-FLV 技术支持多种编码格式和分辨率的视频数据,并且具有自适应比特率调整功能,可以根据用户的网络带宽和设备性能调整视频质量。HTTP-FLV 技术的出现,拓宽了视频直播技术的应用范围,使得更多的视频网站和应用程序能够通过 HTTP-FLV 实现低延迟、高性能、高质量的视频直播服务。目前,HTTP-FLV 已被广泛应用于各种视频直播场景中。 -
WebSocket-FLV: 基于WebSocket传输FLV,依赖浏览器支持播放FLV。WebSocket建立在HTTP之上,建立WebSocket连接前还要先建立HTTP连接。
以上是常见的几种直播协议,每种协议都有其特点和应用场景,您可以根据实际需求选择合适的协议。
ps:WebSocket-FLV 和 HTTP-FLV 都它们之间的主要区别包括:
传输协议不同:HTTP-FLV 是基于 HTTP 协议,而 WebSocket-FLV 是基于 WebSocket 协议。相较于 HTTP 协议,WebSocket 具有双向通信、低延迟等优势,因此可以实现更加稳定和高效的视频直播服务。
连接方式不同:HTTP-FLV 采用短连接方式,即客户端和服务器建立一次连接后就断开;WebSocket-FLV 则采用长连接方式,即客户端和服务器建立一个持久性的连接,并且可以进行双向通信。
实时性不同:HTTP-FLV 的实时性较弱,需要不断地向服务器发送请求获取数据,因此延迟较高;WebSocket-FLV 则可以实现实时、低延迟的视频直播服务。
实现难度不同:HTTP-FLV 的实现相对比较简单,可以使用普通的 Web 服务器即可;WebSocket-FLV 的实现则相对复杂,需要使用支持 WebSocket 的服务器,并且需要开发 WebSocket 客户端代码。
二、正文
目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是 HTML 并不标准支持 RTSP 流。除了 Firefox 浏览器可以直接播放 RTSP 流之外,几乎没有其他浏览器可以直接播放 RTSP 流。Electron 应用是其于 Chromium 内核的,因此也不能直接播放 RTSP流
在借助一定工具的情况下,可以实现在 Web 页面上放 RTSP流。本文介绍的方法可以应用于传统 Web 应用和 Electron 应用中,唯一的区别是将Electron 应用的主进程当作传统 Web 应用的服务器
1.基于 flv.js 的 RTSP 播放无插件方案
flv.js 是 Bilibili 开源的一款 HTML5 浏览器。依赖于 Media Source Extension 进行视频播放,视频通过 HTTP-FLV 或 WebSocket-FLV 协议传输,视频格式需要为 FLV 格式。
flv.js在获取到FLV格式的音视频数据后将 FLV 文件流转码复用成 ISO BMFF(MP4 碎片)片段,再通过Media Source Extensions API 传递给原生HTML5 Video标签进行播放;
Flv.js 是 HTML5 Flash 视频(FLV)播放器,纯原生 JavaScript 开发,没有用到 Flash。由 bilibili 网站开源。
该项目依托于 Media Source Extensions,受到 hls.js 的启发。
概览:
一个实现了在 HTML5 视频中播放 FLV 格式视频的 JavaScript 库。它的工作原理是将 FLV 文件流转码复用成 ISO BMFF(MP4 碎片)片段,然后通过 Media Source Extensions 将 MP4 片段喂进浏览器。
flv.js 是使用 ECMAScript 6 编写的,然后通过 Babel Compiler 编译成 ECMAScript 5,使用 Browserify 打包。
功能:
FLV 容器,具有 H.264 + AAC 编解码器播放功能
多部分分段视频播放
HTTP FLV 低延迟实时流播放
FLV 通过 WebSocket 实时流播放
兼容 Chrome, FireFox, Safari 10, IE11 和 Edge
十分低开销,并且通过你的浏览器进行硬件加速
- 服务器端(主进程)
服务器端采用 express + express-ws 框架进行编写,当有 HTTP 请求发送到指定的地址时,启动 ffmpeg 串流程序,直接将 RTSP 流封装成 FLV 格式的视频流,推送到指定的 WebSocket 响应流中。
import * as express from "express";
import * as expressWebSocket from "express-ws";
import ffmpeg from "fluent-ffmpeg";
import webSocketStream from "websocket-stream/stream";
import WebSocket from "websocket-stream";
import * as http from "http";
function localServer() {
let app = express();
app.use(express.static(__dirname));
expressWebSocket(app, null, {
perMessageDeflate: true
});
app.ws("/rtsp/:id/", rtspRequestHandle)
app.listen(8888);
console.log("express listened")
}
function rtspRequestHandle(ws, req) {
console.log("rtsp request handle");
const stream = webSocketStream(ws, {
binary: true,
browserBufferTimeout: 1000000
}, {
browserBufferTimeout: 1000000
});
let url = req.query.url;
console.log("rtsp url:", url);
console.log("rtsp params:", req.params);
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);
}
}
当然这个实现还比较粗糙。当有多个相同地址的请求时,应当增加 ffmpeg 的输出,而不是启动一个新的 ffmpeg 进程串流
浏览器端(渲染进程)
示例使用 Vue 框架进行页面设计。
npm install --save flv.js
// "flv.js": "^1.6.2",
<template>
<div>
<video class="demo-video" ref="player" muted autoplay></video>
</div>
</template>
<script>
import flvjs from "flv.js";
export default {
data () {
return {
player: ''
}
},
mounted () {
if (flvjs.isSupported()) {
let video = this.$refs.player;
if (video) {
this.player = flvjs.createPlayer({
type: "flv",
isLive: true,
url: `https://dxsnvr.com:2443/live/ch1443_0_1.flv`
});
this.player.attachMediaElement(video);
try {
this.player.load();
this.player.play();
} catch (error) {
console.log(error);
};
}
}
},
beforeDestroy () {
this.player.destory();
}
}
</script>
<style>
.demo-video {
max-width: 880px;
max-height: 660px;
}
</style>
2.基于有插件方案
近期在做摄像头监控视频在网页中播放的工作,现在大部分摄像头厂商如海康威视、大华、华为等都支持标准的RTSP协议,RTSP协议的优势是实时性高、流畅度度高,同时支持H.265和H.264,清晰度也更高,对于要求比较高的安防、交通等领域很适合,交通行业特殊需要延迟低于300毫秒。
比如海康威视提供的开发包,优势就是延迟比较低,但要求浏览器需要支持NPAPI插件,而Chrome等高版本浏览器是不支持的,如果使用老版本浏览器也可以暂时解决问题,但是漏洞比较多,商用还是有很大风险,并且不同的厂商的方案也只针对本厂商产品,不能兼容其他厂商的摄像头。
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
海康开放平台
下载h5集成工具
将开发包bin目录下的文件放入public目录下(我这里是放入了public/static目录下)
3.在public/index.html文件下引入
<script src="static/h5player.min.js"></script>
5.在src/utils下也放入一个h5player.min.js,方便后面引用。在使用页面引入js
import '@/utils/h5player.min.js'
<div id="player"></div>
<script>
import '@/utils/h5player.min.js'
export default {
data() {
return {
// 播放器对象
player: null
}
}
mounted() {
this.$nextTick(() => {
this.initPlayer()
})
}
methods: {
/**
* 初始化播放器
*/
initPlayer() {
this.player = new window.JSPlugin({
// 需要英文字母开头 必填
szId: 'player',
// 必填,引用H5player.min.js的js相对路径
szBasePath: '../../../utils'
// // 当容器div#play_window有固定宽高时,可不传iWidth和iHeight,窗口大小将自适应容器宽高
// iWidth: 600,
// iHeight: 400,
// // 分屏播放,默认最大分屏4*4
// iMaxSplit: 16,
// iCurrentSplit: 1,
// // 样式
// oStyle: {
// border: '#343434',
// borderSelect: '#FFCC00',
// background: '#000'
// }
})
this.initPlugin()
},
/**
* 事件初始化
*/
initPlugin() {
this.player.JS_SetWindowControlCallback({
windowEventSelect(iWindIndex) {
// 插件选中窗口回调
console.log('windowSelect callback: ', iWindIndex)
},
pluginErrorHandler(iWindIndex, iErrorCode, oError) {
// 插件错误回调
console.error(`window-${iWindIndex}, errorCode: ${iErrorCode}`, oError)
},
windowEventOver(iWindIndex) {
// 鼠标移过回调
console.log('鼠标移过回调', iWindIndex)
},
windowEventOut(iWindIndex) {
// 鼠标移出回调
console.log('鼠标移出回调', iWindIndex)
},
windowFullCcreenChange(bFull) {
// 全屏切换回调
console.log('全屏切换回调', bFull)
},
firstFrameDisplay(iWndIndex, iWidth, iHeight) {
// 首帧显示回调
console.log('首帧显示回调', iWndIndex, iWidth, iHeight)
},
performanceLack(iWndIndex) {
// 性能不足回调
console.log('性能不足回调', iWndIndex)
}
})
this.play()
},
/**
* 播放
*/
play() {
let preUrl = '' // 播放地址
const param = {
playURL: preUrl,
// 1:高级模式 0:普通模式,高级模式支持所有
mode: 0
}
// 当前播放窗口下标
let index = 0
console.log(this.playerArr)
this.player.JS_Play(preUrl, param, index).then(
() => {
// 播放成功回调
console.log('播放成功')
},
err => {
console.log('播放失败')
console.info('JS_Play failed:', err)
}
)
},
}
}
</script>