多媒体框架
本章主要介绍爱智多媒体框架。
概述
爱智提供多种多媒体处理模块,用于处理多媒体应用的解码、视频和流媒体服务,具体请参考:
- MediaDecoder:多媒体解码模块,请参考 Multi-Media/MediaDecoder。
- VideoOverlay:视频预览模块,请参考 Multi-Media/VideoOverlay。
- WebMedia:流媒体服务器框架,请参考 Multi-Media/WebMedia。
应用示例
爱智提供以下多媒体应用示例,供开发者参考:
功能介绍
定制 MediaSource
以流媒体服务为例,需要自定义 MediaSource 接口,具体请参考 Multi-Media/WebMedia,新实现的 CameraSource 位于 camera_src.js 文件中。
参考以下示例,CameraSource 直接继承至 JSRE 内置的 FlvSrc 类(注册名称: flv),在 FlvSrc 基础上已经具备基础的流解析和分发功能。
var FlvSrc = require("webmedia/source/flv"); class CameraSource extends FlvSrc { constructor(ser, mode, inOpts, outOpts) { super(ser, mode, inOpts, outOpts); // ... } // ... }
参考以下示例,重写
start()
接口,将 MediaDecoder 对象封装到 CameraSource 中,使 CameraSource 类能够完整地接收和处理 rtsp 流。var MediaDecoder = require('mediadecoder'); // ... start() { var netcam = new MediaDecoder(); this._netcam = netcam; var self = this; var input = this.inOpts; var url = `rtsp://${input.user}:${input.pass}@${input.host}:${input.port}${input.path}`; var name = `${input.host}:${input.port}${input.path}`; new Promise((resolve, reject) => { netcam.open(url, { proto: 'tcp', name: name }, 10000, (err) => { // ... netcam.on('remux', self.onStream.bind(self)); netcam.on('header', self.onStream.bind(self)); netcam.on('eof', self.onEnd.bind(self)); } } }
参考以下示例,netcam 对象将转换后的流交由
onStream()
方法处理, 由于 FlvSrc 实现了 pushStream() 接口,只需直接将流推送给 FlvSrc 处理即可,CameraSource 实现了完整的获取、转换、分发流功能。onStream(frame) { if (!this._netcam) { return; } var buf = Buffer.from(frame.arrayBuffer); try { this.pushStream(buf); } catch (e) { console.error(e); this.stop(); } }
创建流媒体服务器
参考以下流媒体服务示例,先在 WebMedia 框架中注册 CameraSource(main.js),注册完成后,就可以使用名称为 camera-flv 的 MediaSource。
var WebMedia = require("webmedia"); var CameraSource = require("./camera_src"); const sourceName = "camera-flv"; WebMedia.registerSource(sourceName, CameraSource);
参考以下示例,创建流媒体服务器。
方式一
引用 @edgeros/jsre-medias 模块创建流媒体服务器。
const { Manager } = require("@edgeros/jsre-medias");
@edgeros/jsre-medias 主要由 Media 和 Manager 构成:
- Manager 集成了 onvif 模块,可以自动发现、记录设备,创建和管理摄像头对象,创建和管理 Media 对象。
- Media 是对 WebMedia 服务器的封装,Manager 服务可以同时接入多路流媒体。
方式二
使用 Manager 创建流媒体服务器。
var server = undefined; // ... function createMediaSer() { console.log("Create media server."); if (server) { return server; } var opts = { mediaTimeout: 1800000, searchCycle: 20000, autoGetCamera: false, }; server = new Manager(app, null, opts, (opts) => { return { source: sourceName, inOpts: opts, outOpts: null, }; }); // ... }
Manager 创建的流媒体服务器是双通道模式,流媒体通道与数据通道都是基于 WebSocket 协议,Manager 内部自动创建 WebSocket 服务器,并动态生成流媒体通道访问路径。
获取设备列表
参考以下流媒体示例,前端通过 REST 接口请求设备列表,交由后端处理。
app.get("/api/list", (req, res) => {
// ...
var devs = [];
server.iterDev((key, dev) => {
var info = dev.dev;
var stream = dev.mainStream;
var media = stream ? stream.media : null;
devs.push({
devId: key,
alias: `${info.hostname}:${info.port}${info.path}`,
report: info.urn,
path: media ? "/" + media.sid : "",
status: media ? true : false,
});
});
res.send(JSON.stringify(devs));
});
连接设备
参考以下流媒体示例,前端获取到设备列表后,对于未连接的设备(media 为空)需提供账号密码尝试连接设备,用于创建流媒体服务。
app.post("/api/login", bodyParser.json(), (req, res) => { // ... var ret = { result: false, msg: "error" }; var info = req.body; try { connectMedia(info, (media) => { if (!media || media instanceof Error) { ret.msg = `Device ${info.devId} login fail.`; console.warn(media ? media.message : ret.msg); } else { ret.result = true; ret.msg = "ok"; ret.path = "/" + media.sid; } res.send(JSON.stringify(ret)); }); } // ... });
参考以下示例,connectMedia() 是创建流媒体服务过程,回调返回 media 对象,这个过程可能会失败,失败原因可能是账号密码不正确,也可能是该设备不是 rtsp 设备。
- 识别摄像头设备:
function connectMedia(info, cb) { var devId = info.devId; var dev = server.findDev(devId); // ... var stream = dev.mainStream; if (!stream) { server.createStream(devId, info, (err, streams) => { if (err) { cb(err); } else { getMedia(cb); } }); } // ... }
- 创建流媒体服务:
function connectMedia(info, cb) { // ... function getMedia(cb) { var stream = dev.mainStream; if (!stream) { return cb(); } else if (stream.media) { return cb(stream.media); } server.createMedia(devId, stream.token, info, (err, media) => { if (err) { cb(err); } else { cb(media); } }); } }
连接流媒体
本示例前端采用 Vue 实现,位于前端项目的 web 目录下,以下代码在 Player.vue 组件中。
前端连接流媒体服务时,双通道有握手过程,使用 @edgeros/web-mediaclient 模块处理连接过程,参考以下示例在页面中引用模块。
<script type="text/javascript" src="./mediaclient.min.js"></script>
参考以下示例,页面初始化时创建 MediaClient 对象。
this.np = new NodePlayer(); // ... var proto = location.protocol === "http:" ? "ws:" : "wss:"; var host = `${proto}//${window.location.host}`; var mediaClient = new MediaClient( host, (client, path) => { this.np.start(host + path); }, { path: this.dev.path } );
参考以下示例,可使用 mediaClient 打开连接。
startPlay: function () { console.log('Start play.'); if (!this.isStarting) { console.log('Start.'); this.isStarting = true; this.mediaClient.open(getAuth()); } }
参考以下示例,可使用 mediaClient 关闭连接。
stopPlay: function() { if (this.isStarting) { console.log('Stop.'); this.mediaClient.close(); } } mediaClient.on('close', () => { console.log('MediaClient on close'); this.np.stop(); this.isStarting = false; });