videocalling

Camera Control

Feature

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

  1. Show Friendly Names: Display device.label instead of deviceId
  2. Default Selection: Select system default or previously used camera
  3. Camera Preview: Show preview when hovering over camera options
  4. 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

  1. Match Network Conditions: Reduce resolution/framerate on poor connections
  2. Adaptive Quality: Use getStats() to monitor bandwidth and adjust constraints
  3. Stop Unused Tracks: Always stop tracks when switching cameras
  4. 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

References

Related Terms