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用到的流媒体协 ...
随机推荐
- C#使用SqlSugar操作MySQL数据库实现简单的增删改查
公众号「DotNet学习交流」,分享学习DotNet的点滴. SqlSugar简介 SqlSugar 是一款 老牌 .NET 开源多库架构ORM框架(EF Core单库架构),由果糖大数据科技团队 维 ...
- css零散笔记——修改input样式input:-internal-autofill-selected背景色
闲聊: 小颖项目中的登录页需将 input 背景色设为透明,小颖将 input 的背景色设置后,发现表单自动填充后还是会有背景色,然后发现 浏览器 自带了背景色: 效果图: ...
- c语言实现this指针效果
概要 由于目前在做一个比较复杂的嵌入式项目,想要借此提升一下代码的结构设计能力,所以想要以面向对象的思想来完成这个项目,即把每个板载外设资源视为一个对象,采用msp+bsp的模式,对每个bsp外设实现 ...
- 文心一言 VS 讯飞星火 VS chatgpt (149)-- 算法导论12.2 4题
五.用go语言,证明:如果一棵二叉搜索树中的一个结点有两个孩子,那么它的后继没有左孩子,它的前驱没有右孩子. 灵捷3.5 . 文心一言: 为了证明这个性质,我们首先需要明确二叉搜索树(BST)的定义和 ...
- GOF23--23种设计模式(一)
一.什么是设计模式 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决一系列特定问题的套路. 它不是语法规定,而是一套用来提高代码复用性,可读性,可维护性,稳健性,安全性的 ...
- STM32外设:定时器TIM
主要外设: TIM:Timer 定时器 TIM中的基本电路 定时器 计数器的基本功能 复位:计数器值=初值.产生一个输出脉冲.产生更新事件(UEV)脉冲.更新中断标志UIF=1 计数:计数器值递增或递 ...
- NodeJS连接mysql,报错ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
我是mysql8.0以上的版本,在用NodeJS连接服务器中mysql数据库时开始报错 这表示服务器启动起来,但是数据库中密码协议出错,我从网上查到的结果告诉我,是mysql8.0支持了一个新的密码协 ...
- [CF1844G] Tree Weights
题目描述 You are given a tree with $ n $ nodes labelled $ 1,2,\dots,n $ . The $ i $ -th edge connects no ...
- 终结篇:==和equals有什么区别?
== 和 equals 有什么区别?这个问题本身不难,但是被问到的频率很高,且大部分人的回答都不够全面,让人听了有种"恨铁不成钢"的感觉,所以今天咱们就来好好聊聊这个问题. 1.典 ...
- Scrapy爬虫文件代码基本认识和细节解释
import scrapy from scrapy.http.request import Request from scrapy.http.response.html import HtmlResp ...