f null the last used device will be used. * @param {string} [options.mediaSource = null] - The media source to use. If * null the last used media source will be used. * @param {boolean} [options.showPreviewControlButtons = null] - Whether to * show the preview control buttons. If null the last used value will be used. */ async startPreview({ deviceId = null, mediaSource = null, showPreviewControlButtons = null, } = {}) { // We can only start preview once the element is connected to the DOM and // the video element is available. // If you run into this error you're calling the preview method too early, // or you forgot to add it to the DOM. if (!this.isConnected || !this.videoEl) { throw new Error("Can not start preview: Not connected yet."); } if (deviceId != null) { this.deviceId = deviceId; } if (mediaSource != null) { this.mediaSource = mediaSource; } if (showPreviewControlButtons != null) { this.showPreviewControlButtons = showPreviewControlButtons; } if (this.deviceId == null) { throw new Error("Missing deviceId"); } // Stop any existing preview. this.stopPreview(); this.#abortController = new AbortController(); let { signal } = this.#abortController; this._loading = true; this._previewActive = true; // Use the same constraints for both camera and screen share preview. let constraints = { video: { mediaSource: this.mediaSource, deviceId: { exact: this.deviceId }, frameRate: 30, width: 854, height: 480, }, }; let stream; try { stream = await navigator.mediaDevices.getUserMedia(constraints); if (lazy.testGumDelayMs > 0) { await new Promise(resolve => setTimeout(resolve, lazy.testGumDelayMs)); } } catch (error) { if (signal.aborted) { this.#dispatchTestEvent("aborted"); return; } this._loading = false; if ( error.name == "OverconstrainedError" && error.constraint == "deviceId" ) { // Source has disappeared since enumeration, which can happen. // No preview. this.stopPreview(); this.#dispatchTestEvent("error"); return; } console.error(`error in preview: ${error.message} ${error.constraint}`); this.#dispatchTestEvent("error"); return; } if (signal.aborted) { stream.getTracks().forEach(t => t.stop()); this.#dispatchTestEvent("aborted"); return; } this.videoEl.srcObject = stream; this.#stream = stream; this.#dispatchTestEvent("success"); } #dispatchTestEvent(result) { if (lazy.testGumDelayMs > 0) { this.dispatchEvent( new CustomEvent("test-preview-complete", { detail: { result } }) ); } } /** * Stop the preview. */ stopPreview() { // Abort any pending gUM request. this.#abortController?.abort(); this.#abortController = null; this._loading = false; // Stop any existing playback. this.#stream?.getTracks().forEach(t => t.stop()); this.#stream = null; if (this.videoEl) { this.videoEl.srcObject = null; } this._previewActive = false; } render() { return html`
this.startPreview()} ?hidden=${this.deviceId == null || !this.showPreviewControlButtons || this._previewActive} > Loading
this.stopPreview()} ?hidden=${!this.showPreviewControlButtons || !this._previewActive} > `; } } customElements.define("webrtc-preview", WebRTCPreview); PK