Skip to content

设备与视频

设备与视频流程都通过 Command WS 发起控制指令。Video WS 只用于输出视频帧。

推荐流程:

text
device.list -> device.get -> device.open -> video.start -> connect Video WS -> video.stop 或 device.close

1. 获取设备列表

json
{
  "request_id": "req-device-list-001",
  "method": "device.list",
  "params": {}
}

响应包含 devicescount。每个设备包含 device_idmodeldisplay_namevidpidstatusauthorizedsupports_video 等字段。

2. 获取设备详情

json
{
  "request_id": "req-device-get-001",
  "method": "device.get",
  "params": {
    "device_id": "mock-device-01"
  }
}

设备详情会返回可用于预览的 resolutions

json
{
  "width": 1280,
  "height": 720,
  "real_width": 1280,
  "real_height": 720,
  "fps": 15,
  "pixel_format": "mjpeg",
  "is_default": true
}

3. 打开设备

json
{
  "request_id": "req-device-open-001",
  "method": "device.open",
  "params": {
    "device_id": "mock-device-01",
    "width": 1280,
    "height": 720,
    "fps": 15,
    "pixel_format": "mjpeg"
  }
}

成功响应会返回 opened: truevideo.start 依赖设备已打开状态。

4. 启动视频流

json
{
  "request_id": "req-video-start-001",
  "method": "video.start",
  "params": {
    "device_id": "mock-device-01",
    "width": 1280,
    "height": 720,
    "fps": 15,
    "pixel_format": "mjpeg"
  }
}

成功响应:

json
{
  "request_id": "req-video-start-001",
  "code": 0,
  "message": "ok",
  "data": {
    "device_id": "mock-device-01",
    "stream_id": "stream-1",
    "session_token": "mock-session-token",
    "pixel_format": "mjpeg",
    "width": 1280,
    "height": 720,
    "fps": 15
  },
  "ts": 1710000000
}

用响应中的 session_tokenstream_id 连接默认 Video WSS:

text
wss://sdk-runtime.localhost:18091?session_token=mock-session-token&stream_id=stream-1

5. 渲染视频帧

Video WS 不是把“元数据 + 图片”塞在同一个 WebSocket message 里,而是每一帧按顺序发送两条 message:

  1. 文本 message:JSON 事件 stream.frame_meta,描述下一条图片帧。
  2. 二进制 message:同一帧的图片字节。当前推荐的 pixel_formatmjpeg,因此二进制内容就是一张 JPEG 图片。

运行时按这个顺序先发送 stream.frame_meta 文本事件,再发送 binary frame。二进制 message 本身不带 JSON,也不带 stream_id;客户端应把它和刚收到的 stream.frame_meta 配对使用。

Video WS 帧传输:先发元数据,再发图片字节

stream.frame_meta 示例:

json
{
  "event": "stream.frame_meta",
  "code": 0,
  "message": "ok",
  "payload": {
    "device_id": "mock-device-01",
    "stream_id": "stream-1",
    "frame_seq": 1,
    "timestamp_ms": 1710000000000,
    "width": 1280,
    "height": 720,
    "pixel_format": "mjpeg"
  },
  "ts": 1710000000
}

客户端处理逻辑可以按下面理解:

ts
socket.binaryType = 'arraybuffer'

let latestMeta: FrameMeta | null = null

socket.onmessage = async (event) => {
  if (typeof event.data === 'string') {
    const message = JSON.parse(event.data)
    if (message.event === 'stream.frame_meta') {
      latestMeta = message.payload
    }
    return
  }

  // 这里收到的是上一条 stream.frame_meta 描述的图片字节
  const bytes = event.data as ArrayBuffer
  const blob = new Blob([bytes], { type: 'image/jpeg' })
  const bitmap = await createImageBitmap(blob)
  canvasContext.drawImage(bitmap, 0, 0, latestMeta?.width ?? bitmap.width, latestMeta?.height ?? bitmap.height)
  bitmap.close()
}

stream.frame_meta 主要用于校验和渲染辅助:用 stream_id 确认属于当前预览流,用 frame_seq 丢弃旧帧,用 width / height 设置 Canvas 尺寸,用 pixel_format 选择解码方式;如果开启实时检测,payload 还可能包含 detected_rectsdetected_rects_source,用于在 Canvas 上叠加检测框。

建议客户端将 MJPEG 二进制帧包装为 image/jpeg Blob,优先使用 createImageBitmap 解码后通过 drawImage 绘制到 Canvas;不支持 createImageBitmap 时,可退回到 Image + URL.createObjectURL。渲染时仍应采用最新帧优先策略,避免解码或绘制慢于输入帧率时造成延迟堆积。

6. 停止视频或关闭设备

只停止视频流:

json
{
  "request_id": "req-video-stop-001",
  "method": "video.stop",
  "params": {
    "device_id": "mock-device-01"
  }
}

释放设备:

json
{
  "request_id": "req-device-close-001",
  "method": "device.close",
  "params": {
    "device_id": "mock-device-01"
  }
}

device.close 会自动停止该连接下的活跃视频流并释放设备句柄。页面切换设备、切换分辨率或退出预览时,推荐优先调用 device.close

7. 处理设备移除事件

如果已打开的设备被拔出或不可用,Command WS 会推送 device.removed

json
{
  "event": "device.removed",
  "code": 0,
  "message": "ok",
  "payload": {
    "device_id": "mock-device-01",
    "reason": "hotplug_removed",
    "was_opened": true,
    "was_streaming": true,
    "ts_ms": 1710000000000
  },
  "ts": 1710000000
}

收到后应立即停止预览渲染、清理当前设备和视频流状态,并重新调用 device.list 刷新设备列表。

CZUR Open Platform Documentation