diff --git a/index.html b/index.html index 54b5fdf07..aad4cd92f 100644 --- a/index.html +++ b/index.html @@ -100,6 +100,8 @@

Screensharing with getDisplayMedia
  • Control camera pan, tilt, and zoom
  • + +
  • Control exposure
  • Devices:

    Query media devices

    diff --git a/src/content/getusermedia/exposure/index.html b/src/content/getusermedia/exposure/index.html new file mode 100644 index 000000000..3a7f61327 --- /dev/null +++ b/src/content/getusermedia/exposure/index.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + Control Exposure + + + + + + + + + + +
    +

    WebRTC samples + Control Exposure

    + + + + +
    +
    Exposure Mode:
    + +
    + +
    +
    Exposure Time:
    + +
    + +
    +
    Exposure Compensation:
    + +
    + +
    +
    Brightness:
    + +
    + +
    +
    White Balance Mode:
    + +
    + + + +
    + +

    Display the video stream from getUserMedia() in a video + element and control exposureMode, exposureTime and exposureCompensation if camera supports it.

    + +

    The MediaStreamTrack object track is in + global scope, so you can inspect it from the console.

    + + View source on GitHub +
    + + + + + + + + diff --git a/src/content/getusermedia/exposure/js/main.js b/src/content/getusermedia/exposure/js/main.js new file mode 100644 index 000000000..22a01b1bf --- /dev/null +++ b/src/content/getusermedia/exposure/js/main.js @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. + */ +'use strict'; + +// Put variables in global scope to make them available to the browser console. +const constraints = window.constraints = { + audio: false, + video: true +}; + +function handleSuccess(stream) { + const video = document.querySelector('video'); + const videoTracks = stream.getVideoTracks(); + console.log('Got stream with constraints:', constraints); + console.log(`Using video device: ${videoTracks[0].label}`); + video.srcObject = stream; + + // make track variable available to browser console. + [window.track] = stream.getVideoTracks(); + + loadProperties(); + + document.querySelector(`button[id=refreshControls]`).disabled = false; +} + +function loadProperties(refreshValuesOnly) { + const track = window.track; + const capabilities = track.getCapabilities(); + const settings = track.getSettings(); + console.log('Capabilities: ', capabilities); + console.log('Settings: ', settings); + + for (const property of ['exposureMode', 'exposureTime', 'exposureCompensation', 'brightness', 'whiteBalanceMode']) { + // Check whether camera supports exposure. + if (!(property in settings)) { + errorMsg(`Camera does not support ${property}.`); + continue; + } + + let element; + + if (Array.isArray(capabilities[property])) { + // Map it to a select element. + const select = document.querySelector(`select[name=${property}]`); + element = select; + if (capabilities[property] && !refreshValuesOnly) { + for (const mode of capabilities[property]) { + select.insertAdjacentHTML('afterbegin', ``); + } + } + } else { + // Map it to a slider element. + const input = document.querySelector(`input[name=${property}]`); + element = input; + input.min = capabilities[property].min; + input.max = capabilities[property].max; + input.step = capabilities[property].step; + } + + element.value = settings[property]; + element.disabled = false; + if (!refreshValuesOnly) { + element.oninput = async event => { + try { + const constraints = {advanced: [{[property]: element.value}]}; + await track.applyConstraints(constraints); + console.log('Did successfully apply new constraints: ', constraints); + console.log('New camera settings: ', track.getSettings()); + } catch (err) { + console.error('applyConstraints() failed: ', err); + } + }; + } + } +} + +function handleError(error) { + if (error.name === 'NotAllowedError') { + errorMsg('Permissions have not been granted to use your camera, ' + + 'you need to allow the page access to your devices in ' + + 'order for the demo to work.'); + } + errorMsg(`getUserMedia error: ${error.name}`, error); +} + +function errorMsg(msg, error) { + const errorElement = document.querySelector('#errorMsg'); + errorElement.innerHTML += `

    ${msg}

    `; + if (typeof error !== 'undefined') { + console.error(error); + } +} + +async function init(e) { + try { + const stream = await navigator.mediaDevices.getUserMedia(constraints); + handleSuccess(stream); + e.target.disabled = true; + } catch (e) { + handleError(e); + } +} + +document.querySelector('#showVideo').addEventListener('click', e => init(e)); diff --git a/src/content/peerconnection/trickle-ice/js/main.js b/src/content/peerconnection/trickle-ice/js/main.js index bfbe2c727..4f96b1d0a 100644 --- a/src/content/peerconnection/trickle-ice/js/main.js +++ b/src/content/peerconnection/trickle-ice/js/main.js @@ -83,7 +83,7 @@ function selectServer(event) { function addServer() { const scheme = urlInput.value.split(':')[0]; - if (scheme !== 'stun' && scheme !== 'turn' && scheme !== 'turns') { + if (!['stun', 'stuns', 'turn', 'turns'].includes(scheme)) { alert(`URI scheme ${scheme} is not valid`); return; } @@ -155,32 +155,28 @@ async function start() { // Whether we only gather a single set of candidates for RTP and RTCP. console.log(`Creating new PeerConnection with config=${JSON.stringify(config)}`); - document.getElementById('error').innerText = ''; - pc = new RTCPeerConnection(config); - pc.onicecandidate = iceCallback; - pc.onicegatheringstatechange = gatheringStateChange; - pc.onicecandidateerror = iceCandidateError; - if (stream) { - stream.getTracks().forEach(track => pc.addTrack(track, stream)); + const errDiv = document.getElementById('error'); + errDiv.innerText = ''; + let desc; + try { + pc = new RTCPeerConnection(config); + pc.onicecandidate = iceCallback; + pc.onicegatheringstatechange = gatheringStateChange; + pc.onicecandidateerror = iceCandidateError; + if (stream) { + stream.getTracks().forEach(track => pc.addTrack(track, stream)); + } + desc = await pc.createOffer(offerOptions); + } catch (err) { + errDiv.innerText = `Error creating offer: ${err}`; + gatherButton.disabled = false; + return; } - pc.createOffer( - offerOptions - ).then( - gotDescription, - noDescription - ); -} - -function gotDescription(desc) { begin = window.performance.now(); candidates = []; pc.setLocalDescription(desc); } -function noDescription(error) { - console.log('Error creating offer: ', error); -} - // Parse the uint32 PRIORITY field into its constituent parts from RFC 5245, // type preference, local preference, and (256 - component ID). // ex: 126 | 32252 | 255 (126 is host preference, 255 is component ID 1)