最近遇到一个这样的需求,希望通过对海康摄像头的画面信息编辑后,视频画面在前端显示,要求视频信息播放不依赖任何第三方插件。
试了很多种办法,其实都没有实现最终的效果。这篇文章是通过webrtc进行的尝试,最后延迟大,不稳定。对于上面的需求不是一种成功的解决方案。姑且认为在通过webrtc实现这种方案的时候,遇到了一些问题,汇总记录一下,供其他场景参考。
对于这个问题,尝试过的集中办法对比如下
1. 依赖flash插件的nginx+rtmp方案:依赖第三方插件,前端延迟大,服务端解码推流不稳定。
2. 利用streamedian这个库进行播放:收费,而且智能直接拉取rtsp视频流,并且运行的时候发现1路摄像头也会有不稳定情况,产生延迟(从摄像头时间可以看出来),而且实际测试播放一段时间视频流需要重新加载,不知道的hi免费版的原因还是哪里配置的问题。这些情况对于无人值守的情况还是有问题。
3. 通过websocket 前端与后台建立连接,后台视频帧推送,会产生解码错误(opencv 编解码有关,原因未知),而且由于视频画面是高清画质,单次推送信息大小至少都是70万字节起,推送很慢(感觉和网络带宽有关);
尝试的三种方法对比如上。接下来着重记录一下通过websocket尝试实现这种方法的过程。
分别在前端后台实现websockett。后台通过一个C++开源项目https://www.codeproject.com/Articles/371188/A-Cplusplus-Websocket-Server-For-realtime-interact。
这个项目是个win32的项目,可以直接编译运行,前后端也成功连接。
由于项目的其他的运行环境都是x64所以需要编译x64的版本。对于win32 与x64的差别,对于本项目主要是由于数据长度导致的类型检查错误,以及由于数据截断导致的运行时错误。系统了解win32 与 x64的差别戳这里https://social.msdn.microsoft.com/Forums/vstudio/en-US/15f7d186-26ee-4f4a-8231-98d67359a90e/having-trouble-porting-directory-watch-program-to-64-bit?forum=vcgeneral。
我们这里把注意力放在如何快速编译并运行项目。具体发生类型转换和数据截断错误的在一下几处调用API的地方,分别是
CreateIoCompletionPort();
GetQueuedCompletionStatus();
GetQueuedCompletionStatus();
主要差别在LPDWORD / DWORD 和 PULONG_PTR / ULONG_PTR这几个参数的转换上。不管win32还是x64 尽量统一使用PULONG_PTR / ULONG_PTR就不会有问题了。
以上是引入这个开源实现遇到的问题。其实在这之前尝试过其他方式实现websocket尝试过通过boost实现的websocket.Windows平台websocket 都是通过完成端口实现的,效率最优。
前端调用就很简单了。H5原生支持websocket.
function connect() {
var host = "ws://localhost:81/test";
try {
socket = new WebSocket(host);
// OutputLog('Socket Status: ' + socket.readyState);
socket.onopen = function () {
// OutputLog('Socket Status: ' + socket.readyState + ' (open)');
// var pseudoName = $('#pseudo').val();
socket.send('0zy');
}
socket.onmessage = function (msg) {
var str = "";
str = msg.data;
var id = str.substr(0, 1);
var separator = str.indexOf("|");
var arg1 = "";
var arg2 = "";
if (separator != -1) {
arg1 = str.substr(1, separator - 1);
arg2 = str.substr(separator + 1);
}
else
arg1 = str.substr(1);
if (id == "0") {
OutputLog('Server reply : ' + arg1);
}
if (id == "1") {
OutputLog('Server echo msg : ' + arg1);
}
if (id == "2") {
OutputLog(arg1 + ' said : ' + arg2);
}
if (id == "3") {
OutputLog(arg1 + ' broadcasted : ' + arg2);
}
if (id == "4") {
// OutputLog('Server streamed : ' + arg1);
$('#target').src = "data:image/png;base64," + arg1;
}
}
socket.onclose = function () {
OutputLog('Socket Status: ' + socket.readyState + ' (Closed)');
}
} catch (exception) {
// OutputLog('Error' + exception);
}
}