WEB端播放华为海康大华视频方案
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 style="background-color: #ffffff; border: solid 2px #ff0000; padding: 0; margin: 0;">
    <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="playSingleReplace()" />
        <input type="button" value="播放多路视频" title="播放多路视频" onclick="playMulti()" />
        <input type="button" value="追加播放" title="追加播放" onclick="appendToPlayMulti()" />
        <input type="button" value="播放单个录像" title="播放单个录像" onclick="playOneRecord()" />
        <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="minimizeApp()" style="margin-right: 10px;" />
        <input type="button" value="最大化" title="最大化" onclick="maximizeApp()" style="margin-right: 10px;" />
        <input type="button" value="还原" title="还原" onclick="normalizeApp()" style="margin-right: 10px;" />
        <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 windowIdForOneRecord;
        let windowIdForPlayRecord;
        let windowIdForPtz;
        //播放单个视频
        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);
                        //jsObj.StopWindowPlay(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 = "BD4F35031E61B4BCE053070C0822BF04"; //摄像机设备ID
            if (jsObjExists()) jsObj.PlaySingle(left, top, width, height, windowIdForSingle, cameraId);
        }
        //替换播放单个视频
        function playSingleReplace() {
            let cameraId = "BC77ACF9EFCE1A48E053070C0822157A"; //摄像机设备ID
            if (jsObjExists()) jsObj.PlaySingleReplace(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;
                        hidePTZ();
                    }
                },
                min: function () {
                    if (jsObjExists()) {
                        jsObj.HideWindow(windowIdForMulti);
                        //jsObj.StopWindowPlay(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 = "BD4F31B4DBFEB3A1E053070C0822FD40,BC77ACF9F04D1A48E053070C0822157A"; //摄像机设备ID
            if (jsObjExists()) jsObj.PlayMulti(left, top, width, height, windowIdForMulti, 4, true, cameraIds);
        }
        //追加播放
        function appendToPlayMulti() {
            let cameraIds = "BC77ACF9EFCE1A48E053070C0822157A,BC77ACF9F0801A48E053070C0822157A,BC77ACF9F04D1A48E053070C0822157A,BD4F31B4DBFEB3A1E053070C0822FD40,BD4F31B4DC10B3A1E053070C0822FD40,BD4F35031E50B4BCE053070C0822BF04,BD4F35031E61B4BCE053070C0822BF04"; //摄像机设备ID
            if (jsObjExists()) jsObj.AppendToPlayMulti(windowIdForMulti, cameraIds);
        }
        //播放单个录像
        function playOneRecord() {
            layer.msg("请稍等......", { icon: 1, offset: "t" });
            if (windowIdForOneRecord) {
                if (jsObjExists()) jsObj.ShowWindow(windowIdForOneRecord);
                alert("视频回放弹框已存在,您可以追加回放");
                return;
            }
            let moveWindow = function () {
                if (!windowIdForOneRecord) 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(windowIdForOneRecord, left, top, width, height);
            }
            let interval = setInterval(() => {
                moveWindow();
            }, 100);
            windowIdForOneRecord = nanoid(); //窗口ID
            let handle = layer.open({
                type: 1,
                title: '视频',
                shadeClose: false,
                shade: false,
                maxmin: true, //开启最大化最小化按钮
                area: ['500px', '351px'],
                content: '<div style="padding: 20px;">请稍等......</div>',
                cancel: function () {
                    if (jsObjExists()) {
                        clearInterval(interval);
                        jsObj.CloseWindow(windowIdForOneRecord);
                        windowIdForOneRecord = undefined;
                    }
                },
                min: function () {
                    if (jsObjExists()) {
                        jsObj.HideWindow(windowIdForOneRecord);
                        //jsObj.StopWindowPlay(windowIdForOneRecord); //可选操作,停止窗体所有正在播放的视频
                    }
                },
                restore: function () {
                    if (jsObjExists()) {
                        jsObj.ShowWindow(windowIdForOneRecord);
                        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 = "BD4F31B4DBFEB3A1E053070C0822FD40"; //摄像机设备ID
            let startTime = "2022-04-20 01:00:00";
            let endTime = "2022-04-20 02:00:00";
            if (jsObjExists()) jsObj.PlayOneRecord(left, top, width, height, windowIdForOneRecord, cameraId, startTime, endTime);
        }
        //录像回放
        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);
                        //jsObj.StopWindowPlay(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 = "BD4F31B4DBFEB3A1E053070C0822FD40"; //摄像机设备ID
            let startTime = "2022-04-20 01:00:00";
            let endTime = "2022-04-20 02:00:00";
            if (jsObjExists()) jsObj.PlayRecord(left, top, width, height, windowIdForPlayRecord, 4, cameraId, startTime, endTime);
        }
        //追加录像回放
        function appendToPlayRecord() {
            layer.msg("请稍等......", { icon: 1, offset: "t" });
            let cameraIds = "BD4F31B4DBFEB3A1E053070C0822FD40"; //摄像机设备ID
            let startTime = "2022-04-20 01:00:00";
            let endTime = "2022-04-20 02:00:00";
            if (jsObjExists()) jsObj.AppendToPlayRecord(windowIdForPlayRecord, cameraIds, startTime, endTime);
        }
        //关闭App
        function closeApp() {
            if (jsObjExists()) jsObj.CloseApp();
        }
        //显示摄像机的云台控制
        function showPTZ(cameraId) {
            layer.msg("显示摄像机的云台控制,cameraId=" + cameraId, { icon: 1, offset: "t" });
            if (!windowIdForPtz) {
                windowIdForPtz = nanoid(); //窗口ID
                jsObj.ShowPTZ(1603, 650, windowIdForPtz, cameraId);
            }
        }
        //隐藏摄像机的云台控制
        function hidePTZ(cameraId) {
            layer.msg("隐藏摄像机的云台控制,cameraId=" + cameraId, { icon: 1, offset: "t" });
            if (windowIdForPtz) {
                jsObj.ClosePTZ(windowIdForPtz);
                windowIdForPtz = undefined;
            }
        }
        //最大化
        function maximizeApp() {
            if (jsObjExists()) jsObj.Maximize();
        }
        //最小化
        function minimizeApp() {
            if (jsObjExists()) jsObj.Minimize();
        }
        //还原
        function normalizeApp() {
            if (jsObjExists()) jsObj.Normalize();
        }
        //窗体最大化事件
        function onMaximize() {
            layer.msg("窗体最大化", { icon: 1, offset: "t" });
        }
        //窗体还原事件
        function onNormalize() {
            layer.msg("窗体还原", { icon: 1, offset: "t" });
        }
        //设置body高度
        function setBodyHeight() {
            let windowHeight = $(window).height();
            $("body").height(windowHeight - 4);
        }
        setBodyHeight();
        window.onresize = function () {
            setBodyHeight();
        }
    </script>
</body>
</html>
												
											WEB端播放华为海康大华视频方案的更多相关文章
- 海康&大华&DSS视频拉流-RTSP转RTMP多媒体播放技术
		
海康&大华&DSS获取RTSP 实时流 海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/ ...
 - EasyPlayer播放海康大华RTSP流时RTSPClient客户端连接兼容问题的解决
		
在之前的博客<EasyPlayer RTSP播放器对RTSP播放地址url的通用兼容修改意见>中,我描述了遇到的一个客户在播放大华某款摄像机时地址不兼容的问题,这不,团队刚刚参考我的这个意 ...
 - 浏览器低延时播放监控摄像头RTSP海康大华硬盘录像机NVR视频(EasyNVR播放FLV视频流)
		
背景描述 EasyNVR的使用者应该都是清楚的知道,EasyNVR一个强大的功能就是可以进行全平台的无插件直播.主要原因在于rtsp协议的视频流(默认是需要插件才可以播放的)经由EasyNVR处理后可 ...
 - RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播
		
方案介绍 目前互联网直播的CDN和标准RTMP流媒体服务器通常只能接收RTMP格式的音视频推流.目前市场上有一些自带RTMP推流的摄像机和编码器,可以直接在其rtmp推流配置里面配置推送到RTMP流媒 ...
 - 海康/大华 IpCamera RTSP地址和格式
		
海康:rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[subtype]/av_stream说明:username: 用户名.例如 ...
 - 海康大华RTSP格式
		
海康实时流:rtsp://admin:12345@192.2.82.50:554/h264/ch4/main/av_stream海康回放流(模拟通道):rtsp://admin:12345@192.2 ...
 - 将海康大华等网络摄像机RTSP流进行网页Flash rtmp和H5 hls直播的技术方案
		
前言 再小的技术点也会有他的市场! 一直以来,都有一些不被看好,认为是成本太高,无法大规模展开的软件和产品形态,就好比每一座城市都会有他的著名小吃一样,即使是慕名而来的人源源不断,受众群体也总是有限, ...
 - 来自iSpy整理的最全海康大华IPC的RTSP连接地址
		
来自iSpy整理的最全海康大华IPC的RTSP连接地址 先贴出处: 海康:http://www.ispyconnect.com/man.aspx?n=Hikvision 大华:http://www.i ...
 - php在web端播放amr语音(如微信语音)
		
在使用微信JSSDK的上传下载语音接口时,发现一个问题: 下载的语音在iPhone上不能播放,测试了之后原因竟然是: 微信接口返回的音频内容是amr格式的,但iPhone不支持播放此类型格式. 那么转 ...
 - 在做RTSP摄像机H5无插件直播中遇到的对接海康摄像机发送OPTIONS心跳时遇到的坑
		
我们在实现一套EasyNVR无插件直播方案时,选择了采用厂家无关化的通用协议RTSP/Onvif接入摄像机IPC/NVR设备,总所周知,Onvif是摄像机的发现与控制管理协议,Onvif用到的流媒体协 ...
 
随机推荐
- Ubuntu 20.04 查看显示器信息
			
安装 ddcutil apt install ddcutil 输入命令 ddcutil detect --verbose 输出类似如下: Output level: Verbose Reporting ...
 - APISIX proxy-cache 插件用法
			
APISIX 的 proxy-cache 插件可以对上游的查询进行缓存,这样就不需要上游的应用服务自己实现缓存了,或者也能少实现一部分缓存,通用的交给插件来做. 下面的操作都是基于 APISIX 3. ...
 - AntDesignBlazor示例——新建项目
			
本示例是AntDesign Blazor的入门示例,在学习的同时分享出来,以供新手参考. 1. 开发环境 VS2022 17.8.2 .NET8 AntDesign 0.16.2 2. 学习目标 创建 ...
 - Gson和fastJson应用场景
			
如果有性能上面的要求可以使用Gson将bean转换json确保数据的正确,使用FastJson将Json转换Bean 二.Google的Gson包的使用简介. Gson类:解析json的最基础的工 ...
 - [ABC268G] Random Student ID
			
Problem Statement Takahashi Elementary School has $N$ new students. For $i = 1, 2, \ldots, N$, the n ...
 - scroll-view和swiper的使用
			
源码: <template> <viex class="out"> <view class="b ...
 - nginx 反向代理teleport
			
nginx 反向代理teleport 普通配置(以Nginx服务与TP服务在同一台主机上为例) # ...其他内容... server { listen 80; server_name www.you ...
 - ubuntu安装cudnn
			
有些忙,这一段时间,博客就随便写写了--- 默认cuda安装好了,这里就不多说了,我们从cuda的环境变量开始说起: 配置cuda环境变量: 打开终端,输入"gedit ~/.bashrc& ...
 - SpringBoot整合Liquibase
			
1.是什么? Liquibase官网 Liquibase是一个开源的数据库管理工具,可以帮助开发人员管理和跟踪数据库变更.它可以与各种关系型数据库和NoSQL数据库一起使用,并提供多种数据库任务自动化 ...
 - LeetCode:不用加号的加法(位运算)
			
解题思路:位运算,只能用位运算符.a.b同号比较好处理.主要是异号的情况,考虑 a>0,b<0,因为 a,b的绝对值都不会超过2^32,因此取模数为2^32.根据同余方程可知 (a+b)% ...