目标
调用海康综合安防平台api,通过摄像头的cameraIndexCode调用【获取监控点预览取流URLv2】api,得到websocket 的url,然后在blazor server中使用htplayer.js播放摄像头实时画面。
步骤
- 根据摄像头名字,调用【查询监控点列表v2】获取摄像头的cameraIndexCode;
- 调用【获取监控点预览取流URLv2】api,获得websocket 的url;
- 在blazor中封装组件,通过JSRuntime与htplayer.js交互;
- 将2步url传给htplayer.js播放,播放。
环境
- .net8 blazor server
- 海康运行管理中心v1.5.118
- 海康综合管理平台
- 海康web播放库 H5player_2.1.3
- 海康OpenAPI安全认证库(C#) V1.0.1
其中 海康运行管理中心和综合管理平台已经存在,H5Player和C# OpenAPI库可以从海康开放平台 (hikvision.com) 下载(手机+短信登录)。
前提一:获得appKey, appSecret
可以通过海康运行管理中心的合作方功能中获得。
前提二: 海康综合管理平台网页能够播放视像头
遇到海康桌面客户端可以,网页不能播放的情况,海康平台需要打补丁。
第一步 调用【查询监控点列表v2】获取摄像头的cameraIndexCode
可以在http://ip:9017/artemis-web/api/index 【API网关】中,搜查询监控点列表v2,然后点【在线调试】,其中body 里面的name 为在海康综合管理平台中的一个能够在线播放的摄像头名称(如截图中的 一号门入口3)。调用成功会得到cameraIndexCode(27eccee91fc34d38a7d9deec11c947c9)
第二步 调用【获取监控点预览取流URLv2】api,获得websocket 的url
【API网关】中,搜查询 获取监控点预览取流URLv2,用上一步得到的cameraIndexCode(27eccee91fc34d38a7d9deec11c947c9),获取ws或wss流地址(ws://172.17.18.246:559/openUrl/9m6RJNS),注意该地址需要在5分钟内使用,否则失效。
第三步 在blazor server中封装组件,通过JSRuntime与htplayer.js交互
封装组件需要一个<div>元素,并且给定id(如hkplayerdiv)。注意,要在组件AfterRender后再调用htplayer.js,否则这个div还不存在。代码中假设cameraIndexCode已知,不再调用【查询监控点列表v2】。
<div id="hkplayerdiv" style="height:800px; width:100%;"> </div>
组件需要通过https调用【获取监控点预览取流URLv2】api,用cameraIndexCode获得预览ws或wss的url。这里需要用到 海康OpenAPI安全认证库(C#) V1.0.1 中的HttpUtillib.cs,但需要注释掉272 if(_isHttps) 括起来的代码,否则会报错。
/*if (_isHttps)
{
// set remote certificate Validation auto pass
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(remoteCertificateValidate);
// FIX:修复不同.Net版对一些SecurityProtocolType枚举支持情况不一致导致编译失败等问题,这里统一使用数值
ServicePointManager.SecurityProtocol = (SecurityProtocolType)48 | (SecurityProtocolType)3072 | (SecurityProtocolType)768 | (SecurityProtocolType)192;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
}*/
组件的代码部分。
public partial class HKPlayer
{
bool visible = false;
[Parameter]
public string Title { get; set; } = "实时监控画面";
[Inject]
public IJSRuntime JSRuntime { get; set; } = null!;
[Inject]
public IMessageService messageService { get; set; }
[Inject]
public HttpClient HttpClient { get; set; }
HKCameraTemplate template = new();
protected override async Task OnInitializedAsync()
{
HttpUtillib.SetPlatformInfo(template.AppKey, template.AppSecret, template.Ip, template.Port, template.IsHttps);
}
bool firstVisibleRender = true;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (visible && firstVisibleRender)
{
firstVisibleRender = false;
}
if (visible && !string.IsNullOrEmpty(_cameraIndexCode))
{
await playAsync(_cameraIndexCode);
}
}
string _cameraIndexCode = string.Empty;
public void Play(string cameraIndexCode)
{
_cameraIndexCode = cameraIndexCode;
visible = true;
StateHasChanged();
}
private async Task playAsync(string cameraIndexCode)
{
HttpUtillib.SetPlatformInfo(template.AppKey, template.AppSecret, template.Ip, template.Port, template.IsHttps);
var previewRequest = new PreviewRequest() { cameraIndexCode = cameraIndexCode };
string body = JsonConvert.SerializeObject(previewRequest);
byte[] result = null;
try
{
result = HttpUtillib.HttpPost(template.PreviewUrl, body, 15);
}
catch (Exception ex)
{
await messageService.Error($"获取流地址失败:{ex.Message}");
}
if (null == result)
{
// 请求失败
await messageService.Error("/artemis/api/video/v2/cameras/previewURLs: POST fail");
}
else
{
var json = Encoding.UTF8.GetString(result);
var obj = JsonConvert.DeserializeObject<PreviewApiResponse>(json);
if (obj != null && obj.Msg == "success")
{
await JSRuntime.InvokeVoidAsync("HKPlayer.play", obj.Data.Url);
}
}
}
private void onVisibleChange(bool b)
{
visible = b;
if (!b)
{
_ = JSRuntime.InvokeVoidAsync("HKPlayer.stop");
}
}
public void Dispose()
{
_ = JSRuntime.InvokeVoidAsync("HKPlayer.stop");
}
}
//后台存储的信息,用于向海康服务端请求流地址
class HKCameraTemplate
{
public string PreviewUrlFull { get; set; } = "https://172.17.18.250:443/artemis/api/video/v2/cameras/previewURLs";
public string PreviewUrl { get; set; } = "/artemis/api/video/v2/cameras/previewURLs";
public string AppKey { get; set; } = "23079615";
public string AppSecret { get; set; } = "eKF7H7c9EC6GcRSKo20D";
public string Ip { get; set; } = "172.17.18.250";
public int Port { get; set; } = 443;
public bool IsHttps { get; set; } = true;
}
//流地址请求的body
class PreviewRequest
{
public string cameraIndexCode { get;set; }
public string protocol { get; set; } = "ws";
public int streamType { get; set; } = 0;
public int transmode { get; set; } = 1;
}
class PreviewApiResponse
{
public string Code { get; set; }
public string Msg { get; set; }
public PreviewApiResponseData Data { get; set; }
}
class PreviewApiResponseData
{
public string Url { get; set; }
}
第四步 调用htplayer播放
这部分逻辑在js中实现,其中hkpalyerdiv就是前面<div>的id,szBasePath就是官网下载的海康web播放库 H5player_2.1.3中bin里面的全部内容,我放在了wwwroot/hkplayer 文件夹中了。
var h = {
cameras: {},
play: function (wsurl) {
if (!h.plugin) {
h.plugin = new JSPlugin({
szId: 'hkplayerdiv', //需要英文字母开头 必填
szBasePath: './hkplayer'
});
}
h.plugin.JS_Resize();
let playURL = wsurl;
let mode = 0;
h.plugin.JS_Play(
playURL,
{
playURL, // 流媒体播放时必传
mode, // 解码类型:0=普通模式; 1=高级模式 默认为0
// 设置直连时的认证参数等
// ...
},
0//curIndex
).then(
() => {
},
(err) => {
console.info('JS_Play failed:', err);
}
);
},
stop: function () {
h.plugin.JS_Stop(0).then(
() => {
},
(err) => {
console.info('JS_Stop failed:', err);
}
);
}
};
window.HKPlayer = h;
如果一切顺利,就可以播放摄像头啦~~~
总结
网页播放摄像头需要通过web socket,因为网页不支持rtsp协议,因此需要服务端进行协议转换(我的第一篇博客就讲了如何将rtsp转web socket以及如何在网页中播放)。海康服务器已经做了协议转换,并提供了ws的url和h5player.js库,该库中可以对h265,h264解码播放,因此本文才能够实现网页摄像头的在线浏览。