v0.1首个正式支持的版本化规范查看版本范围

capture

capture 在用户明确授权后创建摄像头或麦克风流。宿主代持真实设备、系统权限和采集缓冲,guest 只得到类型化句柄及有界媒体数据。

基本示例

// 必须直接发生在可信的用户操作处理器中。
async function startCamera() {
  const stream = await host.capture.requestCamera({
    facing: 'user',
    maxWidth: 1280,
    maxHeight: 720,
  });
  if (!stream) return; // 用户取消

  const target = new Uint8Array(stream.frameBytes);
  const frame = await host.capture.frame(stream.camera, target);
  consumeFrame(target.subarray(0, frame.written), frame);
}

适用场景

  • 扫码、头像拍摄、视频特效的单帧或连续摄像头输入。
  • 录音、音量分析、语音处理的麦克风 PCM 输入。
  • 需要随权限撤销立即终止的数据流。

屏幕捕获、系统音频、后台监听、设备枚举和长期设备标识不在 v0.1。

L1 边界

宿主独占的是摄像头/麦克风设备、系统权限、可信采集指示与撤销状态。guest 无法靠计算取得这些数据;媒体分析、效果、编码和录制文件结构在拿到受限帧后可由 guest 实现,不进入 L1。

能力声明

{
  "capabilities": [
    "host:capture.requestCamera",
    "host:capture.requestMic"
  ],
  "events": ["capture.ended"]
}

摄像头和麦克风分别声明;申请一种设备不会获得另一种。framereadAudiostop 由返回句柄中的 read/close 权限授权,不是独立 capability。

Reference

方法

事件

  • capture.ended:权限撤销、设备断开、应用停止或采集错误。

约束对象

v0.1 只接受宿主白名单字段:摄像头朝向、最大宽高、最大帧率;麦克风最大声道数和偏好采样率。它们是偏好或上限,不是浏览器原始 constraints 透传。

v0.1 视频固定为紧密或带 padding 的 rgba8;音频固定为交错、little-endian f32 PCM。申请成功时宿主返回会话内不变的实际格式:

CameraConstraints {
  facing?: "user" | "environment"
  maxWidth?: u32
  maxHeight?: u32
  maxFrameRate?: u32
}

MicrophoneConstraints {
  preferredSampleRate?: u32
  maxChannels?: 1 | 2
}

CameraStream {
  camera: Handle<camera-stream, read+close, external-resource, session>
  facing: "user" | "environment"
  width: u32, height: u32, stride: u32, format: "rgba8", frameBytes: u32
  nominalFrameRate: u32
}

MicrophoneStream {
  microphone: Handle<microphone-stream, read+close, external-resource, session>
  sampleRate: u32, channels: 1 | 2, format: "f32le", maxFramesPerRead: u32
}

VideoFrameResult { written: u32, width: u32, height: u32, stride: u32, format: "rgba8", sequence: u64 }
AudioReadResult  { written: u32, sampleRate: u32, channels: 1 | 2, format: "f32le",
                   frames: u32, sequence: u64, droppedFrames: u32 }

capture.frame(camera, sink, afterSequence?, options?) 返回 sequence 严格大于 afterSequence 的最新帧;省略时返回调用开始时已缓冲的最新帧,没有则异步等待。afterSequence 若提供,必须是此前由同一句柄成功返回过的序号,否则以 invalid-sequence 失败。它允许跳过中间视频帧,不返回相同序号。

capture.frame 的 sink 容量必须至少为 frameBytes;成功时恰好写入 frameByteswritten === frameBytes === stride × heightcapture.readAudio(microphone, sink, options?) 的 sink 容量必须是 channels × 4 的正整数倍;一次成功读取 1..min(sinkCapacity / (channels × 4), maxFramesPerRead) 个完整帧并令 written === frames × channels × 4。它异步等待至少一个完整采样帧并按时间顺序消费。sequence 是返回块首个音频帧在本会话中的零基绝对序号;若有界宿主缓冲因 guest 读取过慢而丢帧,droppedFrames 精确报告本块之前新丢弃的帧数。两者都可取消,取消不关闭句柄;同一句柄同时存在第二个读取时以 busy 失败。

摄像头源的第一个捕获帧 sequence 为 0,之后每个实际捕获帧递增 1;首次读取最新帧可以观察到大于 0 的序号,间隙就是未返回的视频帧数。nominalFrameRate 是宿主选择的名义采集上限,必须为正;请求提供 maxFrameRate 时不得超过它。实际到达频率可以更低,不形成计时承诺。音频 sequence 以单个多声道采样帧为单位,从 0 开始;相邻返回块满足 next.sequence = previous.sequence + previous.frames + next.droppedFrames。任一序列即将耗尽 u64 时,宿主必须先以 error 终止会话,不得回绕。所有返回的格式、朝向、宽高、stride、采样率和声道必须与申请结果一致。

麦克风工作流示例

const stream = await host.capture.requestMic({
  preferredSampleRate: 48_000,
  maxChannels: 1,
});
if (!stream) return;

const bytes = new Uint8Array(stream.maxFramesPerRead * stream.channels * 4);
let sequence;
for (;;) {
  const result = await host.capture.readAudio(stream.microphone, bytes);
  sequence = result.sequence + BigInt(result.frames);
  if (result.droppedFrames) reportGap(result.droppedFrames);
  if (result.frames > 0) analyzePcm(bytes.subarray(0, result.written), result);
}

maxChannels 是硬上限;提供时返回 channels <= maxChannels,省略时宿主选择 1 或 2。preferredSampleRate 是偏好,返回值可以不同,但必须是 [8000, 192000] 内整数。maxFramesPerRead 恰好等于宿主公布的 capture.maxAudioFramesPerRead

约束字段都可省略,但对象本身必填;空对象表示接受宿主默认。所有数值必须为正并受公布预算限制。成功的摄像头尺寸必须为正,满足 stride >= width × 4frameBytes === stride × height <= capture.maxFrameBytes。宿主选择不超过各 max* 的实际值;指定 facing 时只能返回同一朝向,未指定时可选择任一朝向并在 CameraStream.facing 中如实返回。无法满足时以 device-unavailable 失败,不得悄悄超过上限或切换指定朝向。

每次 readAudio 都会等待数据或终止,不需要另一个“可读”事件;完成后继续调用不会形成忙轮询。

原生 WASM 示例

CameraStream stream = host_capture_request_camera(camera_constraints());
if (!stream.camera.valid) return; // 用户取消

uint8_t *buffer = frame_pool_acquire(stream.frame_bytes);
VideoFrameResult r = host_capture_frame(
  stream.camera,
  byte_sink(buffer, stream.frame_bytes),
  NO_PREVIOUS_SEQUENCE,
  NO_CANCEL
);
process_frame(buffer, r.written, r.width, r.height, r.stride);

生命周期与授权

  • 句柄为 Handle<camera-stream, read+close, external-resource, session>Handle<microphone-stream, read+close, external-resource, session>
  • request* 必须继承可信用户操作,并由宿主显示设备类型和应用身份。
  • 平台只有在可信选择器能可靠区分“关闭”和“拒绝”时才以 null 表示关闭;浏览器无法区分时使用 denied,调用方取消使用 cancelled,系统错误返回对应 HostError
  • 权限撤销、设备断开、应用隐藏策略或 stop 使句柄立即失效。
  • stop 幂等,不能在关闭后继续交付缓冲数据。

安全与预算

  • 信任档位:yellow;数据来自独立治理的外部资源。
  • 宿主不返回设备名、序列号或稳定 deviceId。
  • v0.1 预算键为 capture.maxStreams(摄像头与麦克风合计的每应用活动流数)、capture.maxFrameBytes(每个视频帧)、capture.maxAudioFramesPerRead(每次音频读取)和 capture.readsPerSecond(两种读取合计的每应用调用数)。
  • 视频仅 rgba8,音频仅 f32le;元数据不能夹带浏览器设备对象。
  • 采集状态必须在可信壳层持续可见,并提供停止入口。

错误与测试

特有错误包括 activation-requireddenieddevice-unavailableinvalid-sequencesink-too-smallstale-handlebusylimit-exceeded。一致性测试至少覆盖平台可区分时的选择器关闭、权限拒绝与撤销、设备热拔插、视频跳帧、音频缓冲溢出与精确 droppedFrames、朝向/格式协商、后台策略、读取取消、并发读取、重复停止和跨类型句柄误用。

Host API 版本与分层概览Host API v0.1版本非正式提议提议canvas API呈现canvas.getInfo()方法canvas.draw()方法canvas.present()方法canvas.measureText()方法canvas.frame event事件canvas.resize event事件asset API呈现asset.read()方法asset.loadFont()方法playback API呈现playback.open()方法playback.write()方法playback.stop()方法playback.ready event事件playback.ended event事件input API输入input.focus()方法input.pointer event事件input.key event事件input.wheel event事件input.focusChange event事件ime API输入ime.openSession()方法ime.updateState()方法ime.updateGeometry()方法ime.closeSession()方法ime.textUpdate event事件ime.composition event事件ime.formatUpdate event事件ime.characterBoundsRequest event事件ime.ended event事件capture API输入capture.requestCamera()方法capture.requestMic()方法capture.frame()方法capture.readAudio()方法capture.stop()方法capture.ended event事件store API数据store.get()方法store.put()方法store.delete()方法store.list()方法store.deletePrefix()方法clipboard API数据clipboard.readText()方法clipboard.writeText()方法env API平台集成env.snapshot()方法env.change event事件开始调用 Host API指南构建交互式绘图应用指南构建自绘文本编辑器指南管理媒体采集与播放指南组织本地数据与同步指南