Camera Control
功能Features that allow users to select, configure, and adjust camera settings during video calls
What is Camera Control?
Camera control in WebRTC refers to the set of APIs and features that allow users and applications to select camera devices, configure camera settings, and dynamically adjust video parameters during video calls. This includes device selection, resolution settings, frame rate adjustments, and on supported hardware, advanced controls like pan, tilt, zoom (PTZ), focus, and exposure.
Modern camera control capabilities enable professional-grade video experiences directly in the browser without plugins, supporting everything from simple device switching to sophisticated camera manipulation for high-end conferencing equipment.
Camera Device Selection
Enumerating Available Cameras
The first step in camera control is discovering available devices using enumerateDevices():
// Get list of all media devices
const devices = await navigator.mediaDevices.enumerateDevices();
// Filter for video input devices (cameras)
const cameras = devices.filter(device => device.kind === 'videoinput');
// Display camera list
cameras.forEach(camera => {
console.log(`${camera.label} (${camera.deviceId})`);
// Example: "FaceTime HD Camera (default)" or "Logitech Webcam C920"
});Each MediaDeviceInfo object contains:
- deviceId: Unique identifier for the device
- groupId: Devices that belong to the same physical device share this
- kind: Type of device ("videoinput", "audioinput", "audiooutput")
- label: Human-readable name (empty string if permissions not granted)
Selecting a Specific Camera
Use the deviceId constraint to request a specific camera:
// Select specific camera by deviceId
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: { exact: selectedDeviceId }
},
audio: true
});
// Or use 'ideal' for graceful fallback
const stream = await navigator.mediaDevices.getUserMedia({
video: {
deviceId: { ideal: selectedDeviceId },
width: { ideal: 1280 },
height: { ideal: 720 }
}
});Switching Cameras During a Call
To switch cameras while a call is active, stop the current track and request a new one:
async function switchCamera(newDeviceId) {
// Stop current video track
const currentVideoTrack = localStream.getVideoTracks()[0];
currentVideoTrack.stop();
// Get new camera stream
const newStream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: { exact: newDeviceId } }
});
const newVideoTrack = newStream.getVideoTracks()[0];
// Replace track in peer connection
const sender = peerConnection.getSenders()
.find(s => s.track?.kind === 'video');
await sender.replaceTrack(newVideoTrack);
// Update local stream reference
localStream.removeTrack(currentVideoTrack);
localStream.addTrack(newVideoTrack);
}Media Constraints
Resolution Control
Specify video resolution using width and height constraints:
const constraints = {
video: {
width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 480, ideal: 720, max: 1080 }
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);Common resolutions:
- VGA: 640x480 (0.3 MP) - Low bandwidth, basic quality
- HD (720p): 1280x720 (0.9 MP) - Standard for video calls
- Full HD (1080p): 1920x1080 (2.1 MP) - High quality, higher bandwidth
- 4K: 3840x2160 (8.3 MP) - Professional use, very high bandwidth
Frame Rate Control
Control video smoothness with frame rate constraints:
const constraints = {
video: {
frameRate: { ideal: 30, max: 60 }
}
};
// For lower bandwidth, reduce frame rate
const lowBandwidthConstraints = {
video: {
width: { ideal: 640 },
height: { ideal: 480 },
frameRate: { ideal: 15, max: 24 }
}
};Aspect Ratio
Maintain specific aspect ratios:
const constraints = {
video: {
aspectRatio: { ideal: 16/9 } // Widescreen
}
};
// For portrait video (mobile)
const portraitConstraints = {
video: {
aspectRatio: { ideal: 9/16 }
}
};Facing Mode (Mobile)
On mobile devices, select front or rear camera:
// Front camera (selfie)
const frontCamera = {
video: {
facingMode: 'user'
}
};
// Rear camera
const rearCamera = {
video: {
facingMode: { exact: 'environment' }
}
};Dynamic Constraint Updates
Using applyConstraints()
Update camera settings without stopping and restarting the stream:
const videoTrack = localStream.getVideoTracks()[0];
// Check current capabilities
const capabilities = videoTrack.getCapabilities();
console.log('Supported resolutions:', capabilities.width, capabilities.height);
console.log('Supported frame rates:', capabilities.frameRate);
// Apply new constraints
await videoTrack.applyConstraints({
width: { ideal: 1920 },
height: { ideal: 1080 },
frameRate: { ideal: 30 }
});
// Get current settings
const settings = videoTrack.getSettings();
console.log(`Current: ${settings.width}x${settings.height} @ ${settings.frameRate}fps`);This approach is smoother than stopping/restarting as it maintains the stream without disruption.
Advanced Camera Controls (PTZ)
Pan, Tilt, and Zoom
Starting in Chrome 87, browsers support PTZ controls for compatible cameras:
// Check if PTZ is supported
const supports = navigator.mediaDevices.getSupportedConstraints();
if (supports.pan && supports.tilt && supports.zoom) {
console.log('PTZ controls available');
}
// Get PTZ capabilities
const videoTrack = stream.getVideoTracks()[0];
const capabilities = videoTrack.getCapabilities();
if (capabilities.pan) {
console.log(`Pan range: ${capabilities.pan.min} to ${capabilities.pan.max}`);
}
if (capabilities.zoom) {
console.log(`Zoom range: ${capabilities.zoom.min} to ${capabilities.zoom.max}`);
}
// Apply PTZ controls
await videoTrack.applyConstraints({
advanced: [{
pan: 100, // Pan right
tilt: -50, // Tilt down
zoom: 2.0 // 2x zoom
}]
});PTZ Support:
- Desktop: Full pan, tilt, and zoom support on compatible cameras (Chrome 87+)
- Android: Zoom only, no pan/tilt support
- iOS Safari: Limited support, zoom available on newer devices
Focus Control
Control camera focus mode and distance:
const capabilities = videoTrack.getCapabilities();
if (capabilities.focusMode) {
// Set focus mode
await videoTrack.applyConstraints({
advanced: [{
focusMode: 'continuous' // or 'manual', 'single-shot'
}]
});
}
if (capabilities.focusDistance) {
// Manual focus distance (if supported)
await videoTrack.applyConstraints({
advanced: [{
focusMode: 'manual',
focusDistance: 0.5 // 0-1 range, 0=near, 1=far
}]
});
}Exposure Control
Adjust exposure settings for different lighting conditions:
// Auto exposure
await videoTrack.applyConstraints({
advanced: [{
exposureMode: 'continuous'
}]
});
// Manual exposure
await videoTrack.applyConstraints({
advanced: [{
exposureMode: 'manual',
exposureCompensation: 1.0, // Brightness adjustment
exposureTime: 100 // Shutter speed in 100μs units
}]
});White Balance
Control color temperature:
// Auto white balance
await videoTrack.applyConstraints({
advanced: [{
whiteBalanceMode: 'continuous'
}]
});
// Manual white balance
await videoTrack.applyConstraints({
advanced: [{
whiteBalanceMode: 'manual',
colorTemperature: 5500 // Kelvin (daylight)
}]
});Best Practices
Device Selection UI
- Show Friendly Names: Display
device.labelinstead of deviceId - Default Selection: Select system default or previously used camera
- Camera Preview: Show preview when hovering over camera options
- Persist Selection: Save user's camera choice to localStorage
Constraint Handling
// Use ideal constraints for graceful degradation
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);
// Check what you actually got
const settings = stream.getVideoTracks()[0].getSettings();
console.log(`Got: ${settings.width}x${settings.height} @ ${settings.frameRate}fps`);
} catch (error) {
console.error('Camera access failed:', error);
// Fall back to basic constraints
}Permissions
- Request Early: Request camera permission early in the user flow
- Explain Why: Show UI explaining why camera access is needed
- Handle Denials: Provide clear instructions if permission is denied
- PTZ Permissions: PTZ controls require explicit user permission beyond basic camera access
Performance Optimization
- Match Network Conditions: Reduce resolution/framerate on poor connections
- Adaptive Quality: Use getStats() to monitor bandwidth and adjust constraints
- Stop Unused Tracks: Always stop tracks when switching cameras
- Simulcast: For SFU architectures, send multiple quality layers
Common Use Cases
- Device Selection: Allow users to choose from multiple webcams, built-in cameras, or external cameras
- Quality Presets: Offer Low (480p), Medium (720p), High (1080p) quality options
- Mobile Camera Switching: Toggle between front and rear cameras on mobile devices
- PTZ for Conferencing: Control high-end PTZ cameras in conference rooms
- Focus for Presentations: Manual focus control when sharing physical objects
- Exposure Adjustment: Compensate for backlighting or poor lighting conditions
- Zoom for Detail: Digital or optical zoom to show fine details
Platform Examples
- Zoom: Device selection, virtual background optimized camera settings, HD/Original quality toggle
- Google Meet: Smart camera framing (auto PTZ), resolution adaptation, device switching
- Microsoft Teams: Multiple camera support, camera preview, custom backgrounds with optimal settings
- Discord: FPS and resolution presets, camera switching, quality optimization
Browser Compatibility (2025)
- Chrome/Edge: Full support for all features including PTZ (desktop), excellent constraint handling
- Firefox: Good support for basic constraints, limited PTZ support
- Safari: Improving support, mobile Safari has facingMode support, limited PTZ
- Mobile Browsers: Android Chrome has zoom support, iOS Safari has facingMode and limited zoom