From 793b3779c2835e3cab99682460b27027e559d679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8D=E8=B6=85?= Date: Tue, 21 Jan 2025 16:16:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=AE=BE=E5=A4=87=E9=80=89?= =?UTF-8?q?=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/shared/Webcam.svelte | 136 +++++++++++++++++++++++--------- frontend/shared/stream_utils.ts | 10 +-- 2 files changed, 103 insertions(+), 43 deletions(-) diff --git a/frontend/shared/Webcam.svelte b/frontend/shared/Webcam.svelte index 5809826..404e185 100644 --- a/frontend/shared/Webcam.svelte +++ b/frontend/shared/Webcam.svelte @@ -22,7 +22,9 @@ let video_source: HTMLVideoElement; let available_video_devices: MediaDeviceInfo[] = []; + let available_audio_devices: MediaDeviceInfo[] = []; let selected_device: MediaDeviceInfo | null = null; + let selected_audio_device: MediaDeviceInfo | null = null; let _time_limit: number | null = null; export let time_limit: number | null = null; let stream_state: "open" | "waiting" | "closed" = "closed"; @@ -72,14 +74,28 @@ const handle_device_change = async (event: InputEvent): Promise => { const target = event.target as HTMLInputElement; const device_id = target.value; + let videoDeviceId + let audioDeviceId + if (include_audio && available_audio_devices.find(audio_device => audio_device.deviceId === device_id)) { + audioDeviceId = device_id + } else { + videoDeviceId = device_id + } - await get_video_stream(include_audio, video_source, device_id, track_constraints).then( + await get_video_stream(audioDeviceId ? { + deviceId: { exact: audioDeviceId }, + }: include_audio, video_source, videoDeviceId, track_constraints).then( async (local_stream) => { stream = local_stream; selected_device = available_video_devices.find( - (device) => device.deviceId === device_id + (device) => device.deviceId === videoDeviceId ) || null; + selected_audio_device = include_audio ? + available_audio_devices.find( + (device) => device.deviceId === audioDeviceId + ) || null + : null; options_open = false; } ); @@ -90,21 +106,29 @@ get_video_stream(include_audio, video_source, null, track_constraints) .then(async (local_stream) => { webcam_accessed = true; - available_video_devices = await get_devices(); + let available_devices = await get_devices(); stream = local_stream; + return available_devices }) - .then(() => set_available_devices(available_video_devices)) + // .then(() => set_available_devices(available_video_devices)) .then((devices) => { - available_video_devices = devices; + available_video_devices = set_available_devices(devices, "videoinput"); + available_audio_devices = set_available_devices(devices, "audioinput"); const used_devices = stream .getTracks() - .map((track) => track.getSettings()?.deviceId)[0]; - - selected_device = used_devices - ? devices.find((device) => device.deviceId === used_devices) || - available_video_devices[0] - : available_video_devices[0]; + .map((track) => track.getSettings()?.deviceId); + used_devices.forEach((device_id) => { + const used_device = devices.find( + (device) => device.deviceId === device_id + ); + if (used_device && used_device?.kind.includes('video')) { + selected_device = used_device; + } else if (used_device && used_device?.kind.includes('audio')) { + selected_audio_device = used_device; + } + }); + !selected_device && (selected_device = available_video_devices[0]) }); if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { @@ -266,31 +290,62 @@ {/if} {#if options_open && selected_device} - - - - {#if available_video_devices.length === 0} - - {:else} - {#each available_video_devices as device} - - {/each} + + {#if available_video_devices.length === 0} + + {:else} + {#each available_video_devices as device} + + {/each} + {/if} + + {#if include_audio=== true} + {/if} - + + {/if} {/if} @@ -376,17 +431,23 @@ .flip { transform: scaleX(-1); } - + .select-container { + width: 95%; + left: 50%; + text-align: center; + transform: translate(-50%, 0); + position: absolute; + bottom: var(--size-2); + } .select-wrap { -webkit-appearance: none; -moz-appearance: none; appearance: none; color: var(--button-secondary-text-color); background-color: transparent; - width: 95%; + font-size: var(--text-md); - position: absolute; - bottom: var(--size-2); + background-color: var(--block-background-fill); box-shadow: var(--shadow-drop-lg); border-radius: var(--radius-xl); @@ -396,8 +457,7 @@ line-height: var(--size-4); white-space: nowrap; text-overflow: ellipsis; - left: 50%; - transform: translate(-50%, 0); + max-width: var(--size-52); } diff --git a/frontend/shared/stream_utils.ts b/frontend/shared/stream_utils.ts index b603fa2..4ad6329 100644 --- a/frontend/shared/stream_utils.ts +++ b/frontend/shared/stream_utils.ts @@ -8,7 +8,7 @@ export function handle_error(error: string): void { export function set_local_stream( local_stream: MediaStream | null, - video_source: HTMLVideoElement, + video_source: HTMLVideoElement ): void { video_source.srcObject = local_stream; video_source.muted = true; @@ -16,10 +16,10 @@ export function set_local_stream( } export async function get_video_stream( - include_audio: boolean, + include_audio: boolean | { deviceId: { exact: string } }, video_source: HTMLVideoElement, device_id?: string, - track_constraints?: MediaTrackConstraints, + track_constraints?: MediaTrackConstraints ): Promise { const fallback_constraints = track_constraints || { width: { ideal: 500 }, @@ -43,10 +43,10 @@ export async function get_video_stream( export function set_available_devices( devices: MediaDeviceInfo[], - kind: "videoinput" | "audioinput" = "videoinput", + kind: "videoinput" | "audioinput" = "videoinput" ): MediaDeviceInfo[] { const cameras = devices.filter( - (device: MediaDeviceInfo) => device.kind === kind, + (device: MediaDeviceInfo) => device.kind === kind ); return cameras;