设备与视频
设备与视频流程都通过 Command WS 发起控制指令。Video WS 只用于输出视频帧。
推荐流程:
device.list -> device.get -> device.open -> video.start -> connect Video WS -> video.stop 或 device.close1. 获取设备列表
{
"request_id": "req-device-list-001",
"method": "device.list",
"params": {}
}响应包含 devices 和 count。每个设备包含 device_id、model、display_name、vid、pid、status、authorized、supports_video 等字段。
2. 获取设备详情
{
"request_id": "req-device-get-001",
"method": "device.get",
"params": {
"device_id": "mock-device-01"
}
}设备详情会返回可用于预览的 resolutions:
{
"width": 1280,
"height": 720,
"real_width": 1280,
"real_height": 720,
"fps": 15,
"pixel_format": "mjpeg",
"is_default": true
}3. 打开设备
{
"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: true。video.start 依赖设备已打开状态。
4. 启动视频流
{
"request_id": "req-video-start-001",
"method": "video.start",
"params": {
"device_id": "mock-device-01",
"width": 1280,
"height": 720,
"fps": 15,
"pixel_format": "mjpeg"
}
}成功响应:
{
"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_token 和 stream_id 连接默认 Video WSS:
wss://sdk-runtime.localhost:18091?session_token=mock-session-token&stream_id=stream-15. 渲染视频帧
Video WS 不是把“元数据 + 图片”塞在同一个 WebSocket message 里,而是每一帧按顺序发送两条 message:
- 文本 message:JSON 事件
stream.frame_meta,描述下一条图片帧。 - 二进制 message:同一帧的图片字节。当前推荐的
pixel_format是mjpeg,因此二进制内容就是一张 JPEG 图片。
运行时按这个顺序先发送 stream.frame_meta 文本事件,再发送 binary frame。二进制 message 本身不带 JSON,也不带 stream_id;客户端应把它和刚收到的 stream.frame_meta 配对使用。

stream.frame_meta 示例:
{
"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
}客户端处理逻辑可以按下面理解:
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_rects 和 detected_rects_source,用于在 Canvas 上叠加检测框。
建议客户端将 MJPEG 二进制帧包装为 image/jpeg Blob,优先使用 createImageBitmap 解码后通过 drawImage 绘制到 Canvas;不支持 createImageBitmap 时,可退回到 Image + URL.createObjectURL。渲染时仍应采用最新帧优先策略,避免解码或绘制慢于输入帧率时造成延迟堆积。
6. 停止视频或关闭设备
只停止视频流:
{
"request_id": "req-video-stop-001",
"method": "video.stop",
"params": {
"device_id": "mock-device-01"
}
}释放设备:
{
"request_id": "req-device-close-001",
"method": "device.close",
"params": {
"device_id": "mock-device-01"
}
}device.close 会自动停止该连接下的活跃视频流并释放设备句柄。页面切换设备、切换分辨率或退出预览时,推荐优先调用 device.close。
7. 处理设备移除事件
如果已打开的设备被拔出或不可用,Command WS 会推送 device.removed:
{
"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 刷新设备列表。