videocalling
摄像头控制(Camera Control)

摄像头控制(Camera Control)

功能

允许用户在视频通话期间选择、配置和调整摄像头设置的功能

什么是摄像头控制?

WebRTC 中的摄像头控制是指一套允许用户和应用程序选择摄像头设备、配置摄像头设置以及在视频通话期间动态调整视频参数的 API 和功能。这包括设备选择、分辨率设置、帧率调整,以及在支持的硬件上,诸如云台变焦(PTZ)、对焦和曝光等高级控制。

现代摄像头控制功能使得专业级的视频体验能够直接在浏览器中实现,无需插件,支持从简单的设备切换到针对高端会议设备的复杂摄像头操作。

摄像头设备选择

枚举可用摄像头

摄像头控制的第一步是使用 enumerateDevices() 发现可用设备:

// 获取所有媒体设备列表
const devices = await navigator.mediaDevices.enumerateDevices();

// 过滤视频输入设备(摄像头)
const cameras = devices.filter(device => device.kind === 'videoinput');

// 显示摄像头列表
cameras.forEach(camera => {
  console.log(`${camera.label} (${camera.deviceId})`);
  // 示例:"FaceTime HD Camera (default)" 或 "Logitech Webcam C920"
});

每个 MediaDeviceInfo 对象包含:

  • deviceId:设备的唯一标识符
  • groupId:属于同一物理设备的设备共享此 ID
  • kind:设备类型("videoinput"、"audioinput"、"audiooutput")
  • label:人类可读的名称(如果未授予权限则为空字符串)

选择特定摄像头

使用 deviceId 约束来请求特定的摄像头:

// 通过 deviceId 选择特定摄像头
const stream = await navigator.mediaDevices.getUserMedia({
  video: {
    deviceId: { exact: selectedDeviceId }
  },
  audio: true
});

// 或使用 'ideal' 以实现优雅降级
const stream = await navigator.mediaDevices.getUserMedia({
  video: {
    deviceId: { ideal: selectedDeviceId },
    width: { ideal: 1280 },
    height: { ideal: 720 }
  }
});

通话期间切换摄像头

要在通话进行时切换摄像头,需停止当前轨道并请求新轨道:

async function switchCamera(newDeviceId) {
  // 停止当前视频轨道
  const currentVideoTrack = localStream.getVideoTracks()[0];
  currentVideoTrack.stop();
  
  // 获取新摄像头流
  const newStream = await navigator.mediaDevices.getUserMedia({
    video: { deviceId: { exact: newDeviceId } }
  });
  
  const newVideoTrack = newStream.getVideoTracks()[0];
  
  // 在对等连接中替换轨道
  const sender = peerConnection.getSenders()
    .find(s => s.track?.kind === 'video');
  
  await sender.replaceTrack(newVideoTrack);
  
  // 更新本地流引用
  localStream.removeTrack(currentVideoTrack);
  localStream.addTrack(newVideoTrack);
}

媒体约束

分辨率控制

使用宽度和高度约束指定视频分辨率:

const constraints = {
  video: {
    width: { min: 640, ideal: 1280, max: 1920 },
    height: { min: 480, ideal: 720, max: 1080 }
  }
};

const stream = await navigator.mediaDevices.getUserMedia(constraints);

常见分辨率:

  • VGA:640x480(0.3 MP)- 低带宽,基本质量
  • HD(720p):1280x720(0.9 MP)- 视频通话标准
  • Full HD(1080p):1920x1080(2.1 MP)- 高质量,更高带宽
  • 4K:3840x2160(8.3 MP)- 专业用途,非常高的带宽

帧率控制

使用帧率约束控制视频流畅度:

const constraints = {
  video: {
    frameRate: { ideal: 30, max: 60 }
  }
};

// 对于较低带宽,降低帧率
const lowBandwidthConstraints = {
  video: {
    width: { ideal: 640 },
    height: { ideal: 480 },
    frameRate: { ideal: 15, max: 24 }
  }
};

宽高比

保持特定的宽高比:

const constraints = {
  video: {
    aspectRatio: { ideal: 16/9 } // 宽屏
  }
};

// 用于竖屏视频(移动设备)
const portraitConstraints = {
  video: {
    aspectRatio: { ideal: 9/16 }
  }
};

朝向模式(移动设备)

在移动设备上,选择前置或后置摄像头:

// 前置摄像头(自拍)
const frontCamera = {
  video: {
    facingMode: 'user'
  }
};

// 后置摄像头
const rearCamera = {
  video: {
    facingMode: { exact: 'environment' }
  }
};

动态约束更新

使用 applyConstraints()

在不停止和重启流的情况下更新摄像头设置:

const videoTrack = localStream.getVideoTracks()[0];

// 检查当前能力
const capabilities = videoTrack.getCapabilities();
console.log('支持的分辨率:', capabilities.width, capabilities.height);
console.log('支持的帧率:', capabilities.frameRate);

// 应用新约束
await videoTrack.applyConstraints({
  width: { ideal: 1920 },
  height: { ideal: 1080 },
  frameRate: { ideal: 30 }
});

// 获取当前设置
const settings = videoTrack.getSettings();
console.log(`当前:${settings.width}x${settings.height} @ ${settings.frameRate}fps`);

这种方法比停止/重启更平滑,因为它在不中断的情况下维护流。

高级摄像头控制(PTZ)

云台和变焦

从 Chrome 87 开始,浏览器支持兼容摄像头的 PTZ 控制:

// 检查是否支持 PTZ
const supports = navigator.mediaDevices.getSupportedConstraints();
if (supports.pan && supports.tilt && supports.zoom) {
  console.log('PTZ 控制可用');
}

// 获取 PTZ 能力
const videoTrack = stream.getVideoTracks()[0];
const capabilities = videoTrack.getCapabilities();

if (capabilities.pan) {
  console.log(`平移范围:${capabilities.pan.min} 到 ${capabilities.pan.max}`);
}
if (capabilities.zoom) {
  console.log(`缩放范围:${capabilities.zoom.min} 到 ${capabilities.zoom.max}`);
}

// 应用 PTZ 控制
await videoTrack.applyConstraints({
  advanced: [{
    pan: 100,    // 向右平移
    tilt: -50,   // 向下倾斜  
    zoom: 2.0    // 2 倍变焦
  }]
});

PTZ 支持:

  • 桌面:在兼容摄像头上完全支持云台、倾斜和变焦(Chrome 87+)
  • Android:仅支持变焦,不支持云台/倾斜
  • iOS Safari:支持有限,较新设备上可用变焦

对焦控制

控制摄像头对焦模式和距离:

const capabilities = videoTrack.getCapabilities();

if (capabilities.focusMode) {
  // 设置对焦模式
  await videoTrack.applyConstraints({
    advanced: [{
      focusMode: 'continuous' // 或 'manual'、'single-shot'
    }]
  });
}

if (capabilities.focusDistance) {
  // 手动对焦距离(如果支持)
  await videoTrack.applyConstraints({
    advanced: [{
      focusMode: 'manual',
      focusDistance: 0.5 // 0-1 范围,0=近,1=远
    }]
  });
}

曝光控制

针对不同光照条件调整曝光设置:

// 自动曝光
await videoTrack.applyConstraints({
  advanced: [{
    exposureMode: 'continuous'
  }]
});

// 手动曝光
await videoTrack.applyConstraints({
  advanced: [{
    exposureMode: 'manual',
    exposureCompensation: 1.0, // 亮度调整
    exposureTime: 100 // 快门速度,单位为 100μs
  }]
});

白平衡

控制色温:

// 自动白平衡
await videoTrack.applyConstraints({
  advanced: [{
    whiteBalanceMode: 'continuous'
  }]
});

// 手动白平衡
await videoTrack.applyConstraints({
  advanced: [{
    whiteBalanceMode: 'manual',
    colorTemperature: 5500 // 开尔文(日光)
  }]
});

最佳实践

设备选择界面

  1. 显示友好名称:显示 device.label 而不是 deviceId
  2. 默认选择:选择系统默认或之前使用过的摄像头
  3. 摄像头预览:在鼠标悬停在摄像头选项上时显示预览
  4. 持久化选择:将用户的摄像头选择保存到 localStorage

约束处理

// 使用 ideal 约束以实现优雅降级
const constraints = {
  video: {
    deviceId: selectedDeviceId ? { ideal: selectedDeviceId } : undefined,
    width: { ideal: 1280 },
    height: { ideal: 720 },
    frameRate: { ideal: 30 }
  }
};

try {
  const stream = await navigator.mediaDevices.getUserMedia(constraints);
  // 检查实际获得的设置
  const settings = stream.getVideoTracks()[0].getSettings();
  console.log(`获得:${settings.width}x${settings.height} @ ${settings.frameRate}fps`);
} catch (error) {
  console.error('摄像头访问失败:', error);
  // 回退到基本约束
}

权限

  • 提前请求:在用户流程早期请求摄像头权限
  • 解释原因:显示界面说明为何需要摄像头访问
  • 处理拒绝:如果权限被拒绝,提供清晰的说明
  • PTZ 权限:PTZ 控制需要超出基本摄像头访问的明确用户权限

性能优化

  1. 匹配网络条件:在连接不良时降低分辨率/帧率
  2. 自适应质量:使用 getStats() 监控带宽并调整约束
  3. 停止未使用的轨道:在切换摄像头时始终停止轨道
  4. 联播(Simulcast):对于 SFU 架构,发送多个质量层

常见用例

  • 设备选择:允许用户从多个网络摄像头、内置摄像头或外接摄像头中选择
  • 质量预设:提供低(480p)、中(720p)、高(1080p)质量选项
  • 移动摄像头切换:在移动设备上在前置和后置摄像头之间切换
  • 会议 PTZ:控制会议室中的高端 PTZ 摄像头
  • 演示对焦:在共享实物对象时手动对焦控制
  • 曝光调整:补偿背光或光线不足的条件
  • 细节变焦:数字或光学变焦以显示精细细节

平台示例

  • Zoom:设备选择、虚拟背景优化的摄像头设置、高清/原始质量切换
  • Google Meet:智能摄像头取景(自动 PTZ)、分辨率自适应、设备切换
  • Microsoft Teams:多摄像头支持、摄像头预览、具有最佳设置的自定义背景
  • Discord:FPS 和分辨率预设、摄像头切换、质量优化

浏览器兼容性(2025)

  • Chrome/Edge:完全支持所有功能,包括 PTZ(桌面),出色的约束处理
  • Firefox:基本约束支持良好,PTZ 支持有限
  • Safari:支持正在改善,移动 Safari 支持 facingMode,PTZ 有限
  • 移动浏览器:Android Chrome 支持变焦,iOS Safari 支持 facingMode 和有限变焦

参考资料