- A+
所属分类:.NET技术
WEB端播放华为海康大华视频方案
类似标题:谷歌浏览器播放华为海康大华视频方案
方案
以下方案相当于给需要播放视频的WEB系统做了一个专用的浏览器,通过专用浏览器的CS客户端组件播放视频,当然,这个专用浏览器是需要安装的
- 使用WPF编写一个客户端程序,嵌入CefSharp浏览器控件,除了浏览器之外无其它界面元素,客户端窗体也没有边框和标题栏,如效果图所示
- 浏览器加载WEB系统,WEB系统登录后把Token传给WPF客户端,客户端使用Token可以请求WEB系统的接口,获取数据。
- WEB系统需要播放实时视频或录像回放时,使用nanoid.js为播放器生成一个唯一标识,计算播放器在浏览器中的位置、长宽,然后把播放器唯一标识、位置、长宽、摄像机设备ID集合通过JS调用C#方法传给客户端,客户端根据位置、长宽显示播放器组件。
说明
- WEB端为Vue开发的系统,WEB系统内有Tab页,如果有子系统跳转,或window.open,客户端会打开新窗体加载跳转页
效果图
效果图说明
- 页面为BS页面,视频弹窗是用layui做的,也是BS的,视频播放是CS控件,WPF通过WindowsFormsHost加载的Winform控件播放视频
- 当BS弹窗最大化、还原、移动时,通过JS调用C#方法更新视频播放CS控件的位置、长宽
- BS弹窗最小化时隐藏视频播放CS控件,还原时显示
- BS弹窗关闭时,释放视频播放CS控件占用的资源
- 视频播放CS控件,全屏功能正常,单视频全屏功能正常,单视频仅在视频播放CS控件中最大化显示功能正常
WEB端测试页面代码
<!DOCTYPE html> <html> <head> <title>CefSharpDemo</title> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script type="text/javascript" src="jquery-1.9.1.js"></script> <script type="text/javascript" src="nanoid.js"></script> <script src="layui/layui.js"></script> <style type="text/css"> input { height: 21px; line-height: 21px; border: solid 1px #666; outline: none; } </style> <script type="text/javascript"> //测试JS调用C#方法 function testCallCSharp() { if (jsObjExists()) jsObj.TestCallCSharp("测试参数123"); } //测试C#调用JS方法 function testCallJs(data) { alert("测试C#调用JS方法:" + data); } //判断jsObj是否存在 function jsObjExists() { return typeof (jsObj) != typeof (undefined); } //获取url参数 getQueryString = function (name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); var r = window.location.search.substring(1).match(reg); if (r != null) return decodeURIComponent(r[2]); return null; } </script> </head> <body> <div style="margin-bottom: 10px; font-weight: bold;"> <span id="title"></span> </div> <div style="height: 30px;"> <span>测试输入框:</span><input type="text" /> </div> <div style="height: 30px; user-select: none;"> <span id="msg" style="line-height: 30px;"></span> </div> <div style="height: 30px; user-select: none;"> <span id="msg2" style="line-height: 30px;"></span> </div> <div style="user-select: none;"> <input type="button" value="测试调用C#方法" title="测试调用C#方法" onclick="testCallCSharp()" /> <input type="button" value="跳转" title="跳转" onclick="openNew()" /> <input type="button" value="设置Token或登录信息" title="设置Token或登录信息" onclick="setToken()" /> </div> <div style="user-select: none; margin-top: 20px;"> <input type="button" value="播放单个视频" title="播放单个视频" onclick="playSingle()" /> <input type="button" value="播放多路视频" title="播放多路视频" onclick="playMulti()" /> <input type="button" value="追加播放" title="追加播放" onclick="appendToPlayMulti()" /> <input type="button" value="录像回放" title="录像回放" onclick="playRecord()" /> <input type="button" value="追加录像回放" title="追加录像回放" onclick="appendToPlayRecord()" /> </div> <div style="position: absolute; float: right; top:5px;right:5px;"> <input type="button" value="关闭" title="关闭" onclick="closeApp()" /> </div> <!-- 一个BS组件 --> <div style="position: absolute; float: right; top: 200px; right: 100px; width: 300px; height: 300px; background-color:#FFEE33; padding: 10px; color: #333333;"> <span> 这是一个BS组件,视频能把我挡住 </span> <br /> <span style="margin-top: 100px; display: block;"> 这是一个BS组件,视频能把我挡住 </span> <br /> <span style="margin-top: 100px; display: block;"> 这是一个BS组件,视频能把我挡住 </span> </div> <script type="text/javascript"> //JS调用C#方法的接口,jsObj.XXX(...) //下面所有方法,调用的C#方法是首字母大写 let title = getQueryString("title"); $("#title").text(title); //页面加载完成时调用C#方法 if (jsObjExists()) jsObj.WebPageLoaded(title); //跳转 function openNew() { window.open("http://localhost:8066/?title=跳转页"); } //设置Token或登录信息 function setToken() { if (jsObjExists()) jsObj.SetToken("这是登录信息或Token"); } //窗口ID let windowIdForSingle; let windowIdForMulti; let windowIdForPlayRecord; //播放单个视频 function playSingle() { if (windowIdForSingle) { if (jsObjExists()) jsObj.ShowWindow(windowIdForSingle); alert("单个视频播放窗口已存在"); return; } let moveWindow = function () { if (!windowIdForSingle) return; let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = $("#layui-layer" + handle).width(); let height = $("#layui-layer" + handle).height() - 51; jsObj.MoveWindow(windowIdForSingle, left, top, width, height); } let interval = setInterval(() => { moveWindow(); }, 100); windowIdForSingle = nanoid(); //窗口ID let handle = layer.open({ type: 1, title: '视频', shadeClose: false, shade: false, maxmin: true, //开启最大化最小化按钮 area: ['500px', '351px'], content: '<div>空白页面</div>', cancel: function () { if (jsObjExists()) { clearInterval(interval); jsObj.CloseWindow(windowIdForSingle); windowIdForSingle = undefined; } }, min: function () { if (jsObjExists()) { jsObj.HideWindow(windowIdForSingle); } }, restore: function () { if (jsObjExists()) { jsObj.ShowWindow(windowIdForSingle); moveWindow(); } }, full: function () { moveWindow(); } }); let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = 500; let height = 300; let cameraId = "BC77ACF9F04D1A48E053070C0822157A"; //摄像机设备ID if (jsObjExists()) jsObj.PlaySingle(left, top, width, height, windowIdForSingle, cameraId); } //播放多个视频 function playMulti() { if (windowIdForMulti) { if (jsObjExists()) jsObj.ShowWindow(windowIdForMulti); alert("多路视频播放窗口已存在,您可以追加播放"); return; } let moveWindow = function () { if (!windowIdForMulti) return; let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = $("#layui-layer" + handle).width(); let height = $("#layui-layer" + handle).height() - 51; jsObj.MoveWindow(windowIdForMulti, left, top, width, height); } let interval = setInterval(() => { moveWindow(); }, 100); windowIdForMulti = nanoid(); //窗口ID let handle = layer.open({ type: 1, title: '视频', shadeClose: false, shade: false, maxmin: true, //开启最大化最小化按钮 area: ['1100px', '651px'], content: '<div>空白页面</div>', cancel: function () { if (jsObjExists()) { clearInterval(interval); jsObj.CloseWindow(windowIdForMulti); windowIdForMulti = undefined; } }, min: function () { if (jsObjExists()) { jsObj.HideWindow(windowIdForMulti); } }, restore: function () { if (jsObjExists()) { jsObj.ShowWindow(windowIdForMulti); moveWindow(); } }, full: function () { moveWindow(); } }); let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = 1000; let height = 600; let cameraIds = "BC77ACF9F04D1A48E053070C0822157A,BC77ACF9EFE01A48E053070C0822157A"; //摄像机设备ID if (jsObjExists()) jsObj.PlayMulti(left, top, width, height, windowIdForMulti, cameraIds); } //追加播放 function appendToPlayMulti() { let cameraIds = "BC77ACF9EFCE1A48E053070C0822157A,BC77ACF9F0801A48E053070C0822157A"; //摄像机设备ID if (jsObjExists()) jsObj.AppendToPlayMulti(windowIdForMulti, cameraIds); } //录像回放 function playRecord() { layer.msg("请稍等......", { icon: 1, offset: "t" }); if (windowIdForPlayRecord) { if (jsObjExists()) jsObj.ShowWindow(windowIdForPlayRecord); alert("视频回放弹框已存在,您可以追加回放"); return; } let moveWindow = function () { if (!windowIdForPlayRecord) return; let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = $("#layui-layer" + handle).width(); let height = $("#layui-layer" + handle).height() - 51; jsObj.MoveWindow(windowIdForPlayRecord, left, top, width, height); } let interval = setInterval(() => { moveWindow(); }, 100); windowIdForPlayRecord = nanoid(); //窗口ID let handle = layer.open({ type: 1, title: '视频', shadeClose: false, shade: false, maxmin: true, //开启最大化最小化按钮 area: ['1100px', '651px'], content: '<div style="padding: 20px;">请稍等......</div>', cancel: function () { if (jsObjExists()) { clearInterval(interval); jsObj.CloseWindow(windowIdForPlayRecord); windowIdForPlayRecord = undefined; } }, min: function () { if (jsObjExists()) { jsObj.HideWindow(windowIdForPlayRecord); } }, restore: function () { if (jsObjExists()) { jsObj.ShowWindow(windowIdForPlayRecord); moveWindow(); } }, full: function () { moveWindow(); } }); let left = parseFloat($("#layui-layer" + handle).css('left').replace('px', '')); let top = parseFloat($("#layui-layer" + handle).css('top').replace('px', '')) + 51; let width = 1100; let height = 600; let cameraId = "BC77ACF9F04D1A48E053070C0822157A"; //摄像机设备ID let startTime = "2022-03-11 15:00:00"; let endTime = "2022-03-11 16:00:00"; if (jsObjExists()) jsObj.PlayRecord(left, top, width, height, windowIdForPlayRecord, cameraId, startTime, endTime); } //追加录像回放 function appendToPlayRecord() { layer.msg("请稍等......", { icon: 1, offset: "t" }); let cameraIds = "BC77ACF9EFCE1A48E053070C0822157A"; //摄像机设备ID let startTime = "2022-03-11 16:00:00"; let endTime = "2022-03-11 17:00:00"; if (jsObjExists()) jsObj.AppendToPlayRecord(windowIdForPlayRecord, cameraIds, startTime, endTime); } //关闭App function closeApp() { if (jsObjExists()) jsObj.CloseApp(); } </script> </body> </html>