设备调试台WEB SDK DEMO
正在初始化

设备

操作后在右侧查看调用与结果

有线 USB
01设备
02图传设置
停止图传后可修改
03图传控制
首帧就绪后进入图传中
04灯光
当前:未知
正在检测浏览器与解码能力…

实时预览

解码方式未运行
解码器
图传状态已停止
编码
分辨率
等待连接设备

连接设备 → 选择配置 → 开启图传

正在初始化
const state = sdk.get_wasm_state();
点击左侧操作,查看对应调用与结果。
公开接口返回结果

      

返回结果

以下为公开 SDK 接口的调用与返回;无返回值时显示操作完成后的状态快照。

公开 SDK 接入示例

工作台只调用公开 API,点击设备操作可在右侧查看对应示例与真实结果。

01 · 初始化与连接

示例软件解码器为可选依赖。SDK 优先使用 WebCodecs;适配器文件不存在或加载失败时不提供软件解码,继续检查 WebCodecs 能力。

import {create_mooeli_sdk} from './mooeli.mjs';
const {hevc_fallback} = await import('./example-hevc-fallback.mjs')
  .catch(() => ({})); // 可选:加载失败时仅使用 WebCodecs

const sdk = await create_mooeli_sdk({hevc_fallback});
connectButton.onclick = () => sdk.connect_device_wired().catch(showError);
// 区域由 SDK 根据浏览器首选语言推断。

连接后读取 sdk.get_device_info()。仅当 capabilities.supports_secure_check 为 1 时可使用设备功能;否则只可读取设备信息、断开或释放 SDK。区域和品牌校验仍须通过。

02 · 配置与显式开流

连接成功不会自动开流。只从可用列表选择配置;修改编码或分辨率前,先等待停流完成。

const {available, default: preferred} = sdk.get_wasm_capabilities();
if (preferred) {
  // 与 FFI 相同的 DataStreamMode / StreamResolution 枚举值。
  const mode = {jpeg: 1, hevc: 2}[preferred.encoding];
  const resolution = {'2k': 0, '1080p': 1, '4k': 2, '720p': 3}[preferred.resolution];
  await sdk.set_transit_stream_mode(mode, resolution); // 成功返回 1,失败 reject
  const lights = sdk.get_wasm_capabilities().lights;
  await sdk.enable_image_transit(lights.includes('white') ? 1 : -1);
  // 本次有效首帧解码后才成功;无白灯能力时保留灯光状态。
}
await sdk.disable_image_transit(sdk.get_wasm_capabilities().lights.includes('off') ? 0 : -1);
// 停流并按能力关灯;也可取消等待首帧。
// 灯光参数:-1 保持不变,0 关灯,1 白灯,2 UV。

03 · 显示与释放 VideoFrame

注册一个帧消费方。下面直接绘制;工作台按浏览器绘制节奏只保留最新画面,并释放被替换的帧。

sdk.subscribe({
  on_frame(frame) {
    try {
      canvas.width = frame.displayWidth;
      canvas.height = frame.displayHeight;
      canvas.getContext('2d').drawImage(frame, 0, 0);
    } finally { frame.close(); }
  },
  on_state: renderState,
  on_error: showError
});

04 · 截图、断开与资源回收

截图返回 JPEG Blob,页面负责下载及回收对象 URL。完成使用后等待 SDK 释放。

const image = await sdk.capture_jpeg();
const url = URL.createObjectURL(image);
downloadLink.href = url;
downloadLink.download = 'device-capture.jpg';
downloadLink.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);

await sdk.disconnect_device(); // 保留实例,之后可重新连接
await sdk.dispose();    // 释放实例,之后需重新初始化

常见问题

USB 权限与 HEVC 解码是两条独立链路。授权成功不会启用解码器,解码失败也不需要重新设置 USB 权限。

为什么我的 Chrome 没有使用 WebCodecs 解码 HEVC?

WebCodecs 是调用浏览器解码器的接口,不保证支持所有编码、profile 或分辨率。存在 VideoDecoder 不等于当前 HEVC 配置可解码;SDK 会先实际解码测试帧,失败时尝试已注册的软件解码器。example-hevc-fallback.mjs 为可选文件,不存在或加载失败时仅使用 WebCodecs;不会因该文件缺失而直接中断初始化。若 WebCodecs 也不能解码 HEVC,则仍无法初始化,请更新浏览器或部署可用的软件解码器。

实时预览标题右侧显示当前流的解码方式。软件后备表示该配置未能使用 WebCodecs 出帧,具体原因需要结合浏览器能力和系统驱动排查;WebCodecs 本身也不等于硬件加速。

如何排查与处理?

  1. 更新 Chrome,确认使用 HTTPS/localhost;在 Chrome「设置 → 系统」检查图形加速设置,修改后重启浏览器。
  2. 在地址栏手动打开 chrome://gpu,检查 Video Acceleration Information 是否列出 HEVC,以及 Problems Detected。只有“Video Decode: Hardware accelerated”不足以证明当前 HEVC 可用。
  3. Linux 管理员可用已安装的 vainfo 检查 VA-API 是否初始化成功并列出 HEVC 解码能力;nvidia-smi 正常、GPU 渲染正常或 FFmpeg 能解码,均不能单独证明 Chrome 能使用该路径。驱动适配需按实际系统单独验证,不建议依靠强开实验参数作为交付要求。
  4. 已部署软件解码器时,可继续使用软件后备。性能不足时先停流,再选择设备真实支持的 1080p HEVC;能力表没有该配置时无法切换。修改 VideoDecoder 的宽高不会把 2K 码流变成 1080p。

可在当前页面开发者工具控制台查询一个 2K HEVC 配置(仅报告配置支持,最终仍需实际解码;实际码流的 codec/profile 也需匹配):

const config = {
  codec: 'hev1.1.6.L150.90', codedWidth: 2560, codedHeight: 1440,
  optimizeForLatency: true
};
console.log(typeof VideoDecoder === 'function'
  ? await VideoDecoder.isConfigSupported(config)
  : {supported: false, reason: '当前上下文没有 VideoDecoder'});

参考:Chrome WebCodecs 使用说明 · Chromium VA-API 文档

Chrome 连接步骤与 USB 权限配置(含示例)

连接需要两层权限:Chrome 对当前网站的设备授权,以及操作系统允许当前用户访问设备。HTML 可以通过 SDK 发起前者;不能执行 sudo、修改系统权限或跳过浏览器确认。看到设备名称不代表系统已允许打开设备。

  1. 使用新版 Chrome,通过 HTTPS 或 localhost 打开页面;确认网站的 USB 设备权限未被禁止。可在 Chrome「设置 → 隐私和安全 → 网站设置 → 更多权限 → USB 设备」检查;受管理的浏览器还需管理员允许访问。
  2. 插好有线设备,点击「连接设备」,在 Chrome 选择框中选中目标设备,再点弹窗内的「连接」。选择框由浏览器管理。SDK 必须由点击直接调用,不要先等待网络请求或定时器。
  3. 页面显示「已连接」后,手动开启图传。若系统拒绝访问,按下述系统配置完成授权,再回到页面重试。

页面调用示例(USB 选择与连接细节由 SDK 内部处理):

connectButton.onclick = () => {
  sdk.connect_device_wired().catch(error => {
    status.textContent = error.message;
    // USB_ACCESS_DENIED:先处理系统访问权限,再手动重试。
  });
};

Linux:临时授权

在本工程根目录运行下列命令,按终端提示输入本机密码。脚本只为当前设备节点增加当前用户的读写权限,拔插后需要重做:

bash scripts/grant_wasm_usb.sh

仅拿到静态发布文件、没有工程脚本时,可在普通用户终端手动操作。先用 lsusb 查找设备;以下 BBB/DDD 必须替换为输出中的三位 Bus 和 Device 编号(每次拔插可能变化)。setfacl/getfacl 由系统的 acl 工具提供。

lsusb -d fc69:f19a
# 将输出中的 Bus 和 Device 编号分别填入 BBB 和 DDD
sudo setfacl -m "u:$(id -un):rw" /dev/bus/usb/BBB/DDD
getfacl /dev/bus/usb/BBB/DDD
test -r /dev/bus/usb/BBB/DDD && test -w /dev/bus/usb/BBB/DDD && echo "当前用户可读写"

Linux:持久配置 udev(由管理员执行)

下面为当前 SDK 匹配的设备 fc69:f19a 配置专用用户组。规则只允许 root 和组成员读写;部署时使用实际 USB 标识,VID/PID 使用小写十六进制且不带 0x。不要把设备设置成所有用户可写。

先在运行 Chrome 的普通用户终端执行,确保 id -un 是实际桌面用户:

sudo groupadd -f device-usb
sudo usermod -aG device-usb "$(id -un)"
sudoedit /etc/udev/rules.d/70-device-usb.rules

在上述规则文件中加入这一行并保存(已有文件请保留其他规则):

SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="fc69", ATTR{idProduct}=="f19a", MODE="0660", GROUP="device-usb"

重载规则后,注销桌面并重新登录以更新组身份,重新打开 Chrome,然后拔插设备:

sudo udevadm control --reload-rules
# 重新登录、拔插后验证;BBB/DDD 改为最新节点编号
id -nG
ls -l /dev/bus/usb/BBB/DDD
test -r /dev/bus/usb/BBB/DDD && test -w /dev/bus/usb/BBB/DDD && echo "当前用户可读写"

id -nG 应包含 device-usb,设备组应为 device-usb,且当前用户可读写。此后拔插会重新应用规则,无需反复设置临时 ACL;Chrome 的网站授权仍独立存在。

其他系统与排查

  • Windows:目标 USB 接口需要 WinUSB 驱动绑定。由设备供应方提供驱动或固件自动绑定支持;在设备管理器检查目标接口,Linux 的 ACL/udev 命令不适用。
  • macOS:通常无需额外权限规则;目标接口不能被其他程序或内核驱动占用。
  • Android Chrome:选择设备后,还需在系统提示中允许 Chrome 访问 USB;不使用上述 Linux 桌面规则。
  • 仍然失败:关闭占用设备的其他程序/标签页,确认 USB 线支持数据;Linux 再核对最新节点权限。被取消的选择返回 CANCELLED,可再次点击连接。

参考:Chrome WebUSB 授权说明 · 各系统 USB 配置要求。本页示例用于手动配置,不会自动修改系统。

解码提醒能否关闭?

点击预览提醒右侧的 × 即可关闭。该条提醒在本次连接中不再遮挡画面,完整内容保留在「日志」;不同的新提醒仍会显示,重新连接后重新提示。关闭提醒不会改变解码方式或设备图传配置。

部署与使用帮助

即使 SDK 加载失败,仍可阅读这些说明。

环境检查与常见问题
等待环境检测结果…

使用新版 Chrome,通过 HTTPS 或 localhost 访问,直接打开本地 HTML 文件不可用。支持桌面 Chrome 及具备相应能力的 Android Chrome;嵌入 iframe 不在首版范围。

HEVC 是整体解码准入条件;WebCodecs 不可用时示例会尝试已加载的第三方软件解码器。软件解码模块可不部署,缺失时仅使用 WebCodecs,并在日志中提示。若没有可用的 HEVC 解码路径,请更新浏览器或部署可用的软件解码器。WebCodecs 标签不保证实际使用硬件加速。

若提示当前区域不可使用该设备,请确认浏览器语言/地区设置,并联系 SDK 提供方核查设备适用范围;浏览器地区偏好不代表实际所在地。

静态目录与服务器配置

完整部署发布目录,并保证模块资源从同一来源加载。设置以下响应头:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-origin

Nginx 示例(根目录填写实际静态目录):

server {
  listen 8080;
  root /path/to/sdk-site;
  index index.html;
  types {
    text/html html;
    text/javascript mjs js;
    application/wasm wasm;
  }
  location / {
    add_header Cross-Origin-Opener-Policy same-origin always;
    add_header Cross-Origin-Embedder-Policy require-corp always;
    add_header Cross-Origin-Resource-Policy same-origin always;
    try_files $uri $uri/ =404;
  }
}

正式站点使用 HTTPS。本机开发可使用 localhost。若配置 CSP,需允许本页内联脚本与样式、WASM 编译及同源/blob Worker;发布版示例解码器通过内嵌 data URL 加载 WASM,connect-src 还需允许 data:。第三方解码资源也须满足跨源隔离要求。发布文件及许可证应一起保留。

图传、性能提醒与超时规则
  • 首帧等待最多 5 秒,可点击停止取消;失败后查看提示,再手动重试。
  • 前台运行连续 5 秒没有新画面会停流;后台保留 60 秒宽限,超过期限停流,返回后需手动开启。
  • 性能不足时会提醒先停止图传,再选择设备实际支持的 1080p HEVC;未提供该组合时无法切换,不自动更改设备输出。
  • 屏幕常亮申请失败时继续运行并提醒。拔出后不自动重连,重新插入后请手动连接。
  • 单击、双击、三击仅展示事件,不触发截图或开关图传。视频录制与固件升级不在本示例范围。
公开状态快照

可用于集成调试;仅展示 SDK 公开的业务状态。

SDK 尚未就绪。