海康 Web 无插件版接入指南:用法与注意事项

本文讲海康 无插件版bNoPlugin: true + jsPlugin / WASM)在网页中的接入方式与注意事项。不绑定具体业务,只聚焦播放器技术链路。适合和《插件版》对照阅读。

无插件版的核心变化:用户不用装 OCX (Windows 上可嵌入网页的 ActiveX 本地控件,插件版依赖它),解码走浏览器侧的 WASM / Worker;设备侧通常需要能走 WebSocket 取流


一、无插件版是什么

同一套前端门面仍是 WebVideoCtrl,但运行时开关不同:

arduino 复制代码
I_InitPlugin('100%', '100%', {
  bNoPlugin: true, // 关键:走无插件
  // ...
})

SDK 会再去加载 jsPlugin、PlayCtrlWasm 等资源,用 Canvas/WebGL + Worker 播 H.264 / H.265。

对比项 插件版 无插件版
安装 必需本地插件 不需要
能力检测 I_CheckPluginInstall I_SupportNoPlugin
销毁 I_DestroyPlugin I_DestroyWorker
取流 本地插件通道 常见为 WebSocket(可经 Nginx 代理)
浏览器 历史方案偏 IE/定制壳 建议 Chrome 91+ / Chromium

二、静态资源与加载

典型目录(示意):

arduino 复制代码
public/static/hcWebSdk/
  webVideoCtrl.js
  jsPlugin/
    jsPlugin-3.0.0.min.js
    playctrl/PlayCtrlWasm/...
    transform/...
  encryption/

index.html 提前注入 jQuery、加密脚本、webVideoCtrl.js。业务启动时:

javascript 复制代码
async function ensureNoPluginReady() {
  const ok = await waitForWebVideoCtrl()
  if (!ok) return false

  // SDK 提供的能力探测
  if (!WebVideoCtrl.I_SupportNoPlugin()) {
    console.warn('请使用 Chrome 91+(Chromium),并确认设备支持 WebSocket 取流')
    return false
  }
  return true
}

三、标准流程:Init → Insert → Login → 预览 / 回放

scss 复制代码
I_SupportNoPlugin() === true
    ↓
I_InitPlugin(..., { bNoPlugin: true, cbInitPluginComplete })
    ↓
I_InsertOBJECTPlugin(containerId)   // 返回 -1 失败
    ↓
延时 I_Resize(w, h)                 // 避免 0 高度黑屏
    ↓
I_Login(ip, protocol, port, user, pass)
    ↓
I_GetDevicePort → 检查 WebSocket 端口 !== -1
    ↓
I_StartRealPlay / I_RecordSearch + I_StartPlayback
    ↓
离开:I_StopAll → I_DestroyWorker → I_Logout

1. 初始化(务必 bNoPlugin: true

typescript 复制代码
function initInContainer(containerId: string) {
  return new Promise<void>((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error('无插件初始化超时')), 20000)

    WebVideoCtrl.I_InitPlugin('100%', '100%', {
      bWndFull: true,
      iPackageType: 2,
      bNoPlugin: true,
      iWndowType: 1, // 1×1;2→2×2;3→3×3
      cbInitPluginComplete() {
        clearTimeout(timer)
        const ret = WebVideoCtrl.I_InsertOBJECTPlugin(containerId)
        if (ret === -1) {
          reject(new Error('I_InsertOBJECTPlugin 失败'))
          return
        }
        resolve()
      },
      cbPluginErrorHandler(wndIndex: number, errorCode: number) {
        console.warn('播放错误', wndIndex, errorCode)
        // 出错后通常 Stop 当前窗口,避免残窗
      },
      cbPerformanceLack() {
        console.warn('性能不足')
      },
      cbSecretKeyError(wndIndex: number) {
        console.warn('码流加密密钥错误', wndIndex)
      },
      // proxyAddress: { ip, port }  // 慎用,见下文「代理」一节
    })
  })
}

布局就绪后再:

scss 复制代码
// 侧栏动画、flex 定高之后再调,可用 200--300ms 或 ResizeObserver
WebVideoCtrl.I_Resize(container.clientWidth, container.clientHeight)
WebVideoCtrl.I_ChangeWndNum(1) // 1/2/3 → 1/4/9 分屏

2. 登录

typescript 复制代码
const iProtocol = location.protocol === 'https:' ? 2 : 1
const deviceIdentify = `${ip}_${port}`

const ret = WebVideoCtrl.I_Login(ip, iProtocol, port, username, password, {
  success() {},
  error(err: { errorCode?: number }) {
    if (err?.errorCode === 2001) {
      // 已登录:切页未 Logout 时很常见,可按成功处理
      return
    }
    console.error('登录失败', err)
  },
})

if (ret === -1) {
  // 同步已登录
}

3. 确认设备支持无插件取流

javascript 复制代码
const ports = WebVideoCtrl.I_GetDevicePort(deviceIdentify)
// 关注 iWebSocketPort / iWebSocketsPort
// 约定:为 -1 时设备未开启或对不齐无插件 WebSocket 取流
if (ports.iWebSocketPort === -1) {
  throw new Error('设备不支持 WebSocket 取流,无插件无法出画')
}

可选:I_GetAnalogChannelInfo / I_GetDigitalChannelInfo 拉通道列表(返回多为 XML,需用 jQuery 等解析)。

4. 实时预览

javascript 复制代码
const iWndIndex = 0
const status = WebVideoCtrl.I_GetWindowStatus(iWndIndex)
if (status) {
  WebVideoCtrl.I_Stop({ iWndIndex })
}

WebVideoCtrl.I_StartRealPlay(deviceIdentify, {
  iWndIndex,
  iStreamType: 1,
  iChannelID: channelId,
  bZeroChannel: false,
  bProxy: true,          // 是否经网关/WebSocket 代理取流
  iRtspPort: rtspPort,   // I_GetDevicePort 拿到且 > 0 时可传
  success() {},
  error(status) {
    // 403:常与鉴权 / 未开通 WS / 代理策略有关
  },
})

5. 回放

javascript 复制代码
WebVideoCtrl.I_RecordSearch(deviceIdentify, channelId, start, end, {
  iStreamType: 1,
  iSearchPos: 0,
  success(xmlDoc) {
    // OK | MORE | NO MATCHES
    // MORE → 增大 iSearchPos 继续搜
  },
})

WebVideoCtrl.I_StartPlayback(deviceIdentify, {
  iStreamType: 1,
  iChannelID: channelId,
  szStartTime: '2026-07-15 10:00:00',
  szEndTime: '2026-07-15 11:00:00',
  bProxy: true,
  success() {},
  error() {},
})

// 控制:I_Pause / I_Resume / I_Stop
// 倍速:I_PlaySlow / I_PlayFast
// 进度:I_GetOSDTime
// 拖动进度常用「Stop + 新 startTime 再 StartPlayback」

6. 云台

与插件版同一套 I_PTZControl:先 bStop=false,成功后短延时再 bStop=true;速度通常 1--7。

7. 销毁(和无插件生命周期强相关)

csharp 复制代码
async function destroyNoPlugin() {
  if (typeof WebVideoCtrl.I_StopAll === 'function') {
    await WebVideoCtrl.I_StopAll() // 无插件里常返回 Promise
  }
  if (typeof WebVideoCtrl.I_DestroyWorker === 'function') {
    WebVideoCtrl.I_DestroyWorker() // 释放 WASM Worker,否则下次 Init 易失败
  }
  await WebVideoCtrl.I_Logout(deviceIdentify)
}

仅切换通道、子组件卸载、又要保留设备登录时:可以 只 StopAll + DestroyWorker,不 Logout;整页离开则建议连 Logout 一起做。


四、代理怎么配:最容易踩的坑

实战里常有两层能力混在一起:

  1. 取流代理 (预览/回放 WebSocket)→ I_StartRealPlay / I_StartPlaybackbProxy: true
  2. ISAPI 代理 (登录、查通道等 HTTP API)→ I_InitPluginproxyAddress

常见错误组合:

Nginx 反代了 WebSocket 取流,却把 proxyAddress 指到同一个网关。

结果:I_Login 的 ISAPI 打到网关 → 404,登录直接挂。

更稳妥的两种方案:

方案 做法
A. 同源改写 ISAPI 页面跑在 localhost:3000,把 /ISAPI 代理到设备或业务后端;不要 轻易给 Init 传 proxyAddress。Cookie / 同源策略更顺。
B. 网关同时代理 ISAPI + WS 只有在网关完整具备 ISAPI 转发时,才给 I_InitPluginproxyAddress: { ip, port }

bProxy 负责「码流走不走代理」,proxyAddress 负责「ISAPI 打哪」------两者不是一回事,不要绑死成同一个开关。


五、注意事项清单

1. 浏览器基线

不支持时直接提示用户换 Chrome 91+ / Chromium 内核。Edge(Chromium)、国内双核浏览器的「极速模式」一般可用;IE、极旧内核基本可以直接放弃。

2. 设备必须开 WebSocket 取流

I_GetDevicePort 里 WebSocket 端口为 -1 → 无插件出不了画。这是 设备能力/配置问题,不是前端少调一个 API。

3. 编码只认 H.264 / H.265

错误码 1006:编码不支持。前端改不了设备编码时,只能换通道/改设备编码配置。

4. 容器必须是空 DOM + 有尺寸

  • I_InsertOBJECTPlugin 会改写节点,Vue/React 不要往里面塞子树
  • 父级 display:none / 高度为 0 时 Init,极易黑屏
  • 布局变化后补 I_Resize
  • 无插件一般 没有 旧的 I_ShowPlugin / I_HidPlugin 可依赖,显隐靠停播或重建

5. Worker 一定要释放

切路由、切容器时若只停播不 I_DestroyWorker

  • 下次 I_InitPlugin 异常
  • 内存和 Worker 堆积
  • 「本地点了就绪,其实内核已乱」

换容器前:先 StopAll + DestroyWorker(以及按需 Logout),再对新容器 Init。

6. 登录 2001 / 同步 -1

含义仍是「已登录」。页面卸载时若没 Logout,返回视频页会高频撞上。策略:

  • 常态:离页 Logout + DestroyWorker
  • 容错:2001 / -1 当成功,且本地标识同步为已登录

7. 先停窗口再开流

I_GetWindowStatus 有占用 → I_Stop → 再 I_StartRealPlay / I_StartPlayback。多路分屏切换布局时,先停各窗,再 I_ChangeWndNum

8. HTTPS / HTTP 一致性

登录 iProtocol 跟页面协议走。站点 HTTPS、设备仅 HTTP、中间又无可靠网关时,会卡在混合内容或证书问题------这通常要运维侧反向代理解决,而不是前端硬切协议。

9. 错误回调要落表

初始化时挂上 cbPluginErrorHandler,把海康 demo 里常见错误码映射成可读文案,客服和排障会轻松很多:

错误码 含义
1001 码流传输过程异常
1002 回放结束
1003 取流失败,连接被动断开
1006 编码不支持(仅 H264/H265)
1007 网络异常导致 WebSocket 断开
1008 首帧回调超时
1012 播放资源不足
1015 获取播放 URL 失败
1017 密码错误
1019 未找到录像片段

另外:

  • cbSecretKeyError:码流加密密钥不对 → 停窗
  • cbPerformanceLack:机器解码吃力 → 降分屏、降码流、少开窗口

预览/回放 HTTP 403 :优先查 WS 是否通、鉴权、代理白名单,而不是反复 Init

10. 性能与分屏

无插件是浏览器硬解码/软解码在扛。9 宫格 + 主码流很容易触发性能回调。产品上可:

  • 默认主码流,弱网改子码流(iStreamType
  • 限制同时窗口数
  • 弱机提示降分屏

六、一张生命周期图

scss 复制代码
┌──────────── 页面进入 ────────────┐
│ wait script → I_SupportNoPlugin │
│ I_InitPlugin(bNoPlugin)         │
│ I_InsertOBJECTPlugin + I_Resize │
│ I_Login → 检查 WS 端口           │
│ I_StartRealPlay / Playback      │
└─────────────────────────────────┘
                 │
                 ▼
┌──────────── 页面离开 ────────────┐
│ I_StopAll                       │
│ I_DestroyWorker                 │
│ I_Logout(建议)                 │
└─────────────────────────────────┘

七、和插件版怎么选

场景 更合适
客户机不可控、不能装软件 无插件
只要 Chrome 内网工作站 无插件(运维成本低)
需要覆盖多操作系统 无插件(见下)
设备完全不提供 WS 取流 插件版或其它取流方案
专机房可统一装包、旧固件(且多为 Windows) 插件版仍可能是权宜之计

多操作系统:无插件的明显优势

插件版强依赖本地安装包,实践中多半绑死 Windows ;Mac 包经常缺位,国产系统(如 麒麟)也难有对应 OCX 可装。

无插件版跑在浏览器里(WASM / Worker),只要客户端是支持 I_SupportNoPluginChromium 内核浏览器,同一套前端即可覆盖:

  • Windows
  • macOS
  • 麒麟 等国产操作系统(使用自带或预装的 Chromium 系浏览器)

落地时仍建议在目标 OS + 浏览器组合上做一次冒烟:确认 I_SupportNoPlugin() === true、WebSocket 取流畅通、音视频可出画。系统再多,瓶颈通常不在「能不能装插件」,而在浏览器能力与设备取流是否开通。

长期看:无插件 + 网关正确拆分「ISAPI / WebSocket」是 Web 监控页的主流方向,也更适合多 OS 终端并存的现场。


小结

无插件版记住四句话就够落地:

  1. bNoPlugin: true + I_SupportNoPlugin 是准入条件
  2. 设备 WebSocket 端口不能是 -1
  3. bProxy 管取流,proxyAddress 管 ISAPI,别混
  4. 离页必须 StopAll + DestroyWorker(外加建议 Logout

把容器 DOM、尺寸、错误码表和代理拓扑一次设计清楚,比纠结某个按钮回调细节更重要。


相关推荐
aaron2 小时前
前端演进史:当你没留意时,前端发生了什么
前端
不好听6132 小时前
前端路由完全指南(上篇):从原理到 6 种路由模式
前端·react.js
像我这样帅的人丶你还2 小时前
🚀大文件上传的那些事
前端·javascript·架构
不好听6132 小时前
前端路由完全指南(中篇):React Router Hooks 完全指南
前端·react.js
hunterandroid2 小时前
[鸿蒙从零到一] ArkUI 声明式 UI 基础实战
前端
七牛开发者2 小时前
RAG 工程里的上下文剪枝:如何减少 68% 的上下文
前端·css·github
橘子星3 小时前
从零理解流式输出 —— 一个 Vue + DeepSeek 的前端实战
前端·javascript·人工智能
智驭未来掌门人3 小时前
如何让你的前端服务在服务器上nohub运行
前端
颜进强3 小时前
Claude Code - 18 从 Figma 到 React:一套让 AI 生成代码"不瞎编"的实战工作流
前端·后端·ai编程