mirror of
https://github.com/HumanAIGC-Engineering/gradio-webrtc.git
synced 2026-02-05 18:09:23 +08:00
支持设备选择
This commit is contained in:
@@ -22,7 +22,9 @@
|
|||||||
|
|
||||||
let video_source: HTMLVideoElement;
|
let video_source: HTMLVideoElement;
|
||||||
let available_video_devices: MediaDeviceInfo[] = [];
|
let available_video_devices: MediaDeviceInfo[] = [];
|
||||||
|
let available_audio_devices: MediaDeviceInfo[] = [];
|
||||||
let selected_device: MediaDeviceInfo | null = null;
|
let selected_device: MediaDeviceInfo | null = null;
|
||||||
|
let selected_audio_device: MediaDeviceInfo | null = null;
|
||||||
let _time_limit: number | null = null;
|
let _time_limit: number | null = null;
|
||||||
export let time_limit: number | null = null;
|
export let time_limit: number | null = null;
|
||||||
let stream_state: "open" | "waiting" | "closed" = "closed";
|
let stream_state: "open" | "waiting" | "closed" = "closed";
|
||||||
@@ -72,14 +74,28 @@
|
|||||||
const handle_device_change = async (event: InputEvent): Promise<void> => {
|
const handle_device_change = async (event: InputEvent): Promise<void> => {
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
const device_id = target.value;
|
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) => {
|
async (local_stream) => {
|
||||||
stream = local_stream;
|
stream = local_stream;
|
||||||
selected_device =
|
selected_device =
|
||||||
available_video_devices.find(
|
available_video_devices.find(
|
||||||
(device) => device.deviceId === device_id
|
(device) => device.deviceId === videoDeviceId
|
||||||
) || null;
|
) || null;
|
||||||
|
selected_audio_device = include_audio ?
|
||||||
|
available_audio_devices.find(
|
||||||
|
(device) => device.deviceId === audioDeviceId
|
||||||
|
) || null
|
||||||
|
: null;
|
||||||
options_open = false;
|
options_open = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -90,21 +106,29 @@
|
|||||||
get_video_stream(include_audio, video_source, null, track_constraints)
|
get_video_stream(include_audio, video_source, null, track_constraints)
|
||||||
.then(async (local_stream) => {
|
.then(async (local_stream) => {
|
||||||
webcam_accessed = true;
|
webcam_accessed = true;
|
||||||
available_video_devices = await get_devices();
|
let available_devices = await get_devices();
|
||||||
stream = local_stream;
|
stream = local_stream;
|
||||||
|
return available_devices
|
||||||
})
|
})
|
||||||
.then(() => set_available_devices(available_video_devices))
|
// .then(() => set_available_devices(available_video_devices))
|
||||||
.then((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
|
const used_devices = stream
|
||||||
.getTracks()
|
.getTracks()
|
||||||
.map((track) => track.getSettings()?.deviceId)[0];
|
.map((track) => track.getSettings()?.deviceId);
|
||||||
|
used_devices.forEach((device_id) => {
|
||||||
selected_device = used_devices
|
const used_device = devices.find(
|
||||||
? devices.find((device) => device.deviceId === used_devices) ||
|
(device) => device.deviceId === device_id
|
||||||
available_video_devices[0]
|
);
|
||||||
: available_video_devices[0];
|
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) {
|
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||||
@@ -266,31 +290,62 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if options_open && selected_device}
|
{#if options_open && selected_device}
|
||||||
<select
|
<div
|
||||||
class="select-wrap"
|
class="select-container"
|
||||||
aria-label="select source"
|
|
||||||
use:click_outside={handle_click_outside}
|
use:click_outside={handle_click_outside}
|
||||||
on:change={handle_device_change}
|
|
||||||
>
|
>
|
||||||
<button
|
<select
|
||||||
class="inset-icon"
|
class="select-wrap"
|
||||||
on:click|stopPropagation={() => (options_open = false)}
|
aria-label="select source"
|
||||||
|
on:change={handle_device_change}
|
||||||
>
|
>
|
||||||
<DropdownArrow />
|
<button
|
||||||
</button>
|
class="inset-icon"
|
||||||
{#if available_video_devices.length === 0}
|
on:click|stopPropagation={() => (options_open = false)}
|
||||||
<option value="">{i18n("common.no_devices")}</option>
|
>
|
||||||
{:else}
|
<DropdownArrow />
|
||||||
{#each available_video_devices as device}
|
</button>
|
||||||
<option
|
{#if available_video_devices.length === 0}
|
||||||
value={device.deviceId}
|
<option value="">{i18n("common.no_devices")}</option>
|
||||||
selected={selected_device.deviceId === device.deviceId}
|
{:else}
|
||||||
>
|
{#each available_video_devices as device}
|
||||||
{device.label}
|
<option
|
||||||
</option>
|
value={device.deviceId}
|
||||||
{/each}
|
selected={selected_device.deviceId === device.deviceId}
|
||||||
|
>
|
||||||
|
{device.label}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</select>
|
||||||
|
{#if include_audio=== true}
|
||||||
|
<select
|
||||||
|
class="select-wrap"
|
||||||
|
aria-label="select source"
|
||||||
|
on:change={handle_device_change}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="inset-icon"
|
||||||
|
on:click|stopPropagation={() => (options_open = false)}
|
||||||
|
>
|
||||||
|
<DropdownArrow />
|
||||||
|
</button>
|
||||||
|
{#if available_audio_devices.length === 0}
|
||||||
|
<option value="">{i18n("common.no_devices")}</option>
|
||||||
|
{:else}
|
||||||
|
{#each available_audio_devices as device}
|
||||||
|
<option
|
||||||
|
value={device.deviceId}
|
||||||
|
selected={selected_audio_device.deviceId === device.deviceId}
|
||||||
|
>
|
||||||
|
{device.label}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</select>
|
||||||
{/if}
|
{/if}
|
||||||
</select>
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@@ -376,17 +431,23 @@
|
|||||||
.flip {
|
.flip {
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
|
.select-container {
|
||||||
|
width: 95%;
|
||||||
|
left: 50%;
|
||||||
|
text-align: center;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
position: absolute;
|
||||||
|
bottom: var(--size-2);
|
||||||
|
}
|
||||||
.select-wrap {
|
.select-wrap {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
color: var(--button-secondary-text-color);
|
color: var(--button-secondary-text-color);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
width: 95%;
|
|
||||||
font-size: var(--text-md);
|
font-size: var(--text-md);
|
||||||
position: absolute;
|
|
||||||
bottom: var(--size-2);
|
|
||||||
background-color: var(--block-background-fill);
|
background-color: var(--block-background-fill);
|
||||||
box-shadow: var(--shadow-drop-lg);
|
box-shadow: var(--shadow-drop-lg);
|
||||||
border-radius: var(--radius-xl);
|
border-radius: var(--radius-xl);
|
||||||
@@ -396,8 +457,7 @@
|
|||||||
line-height: var(--size-4);
|
line-height: var(--size-4);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
max-width: var(--size-52);
|
max-width: var(--size-52);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export function handle_error(error: string): void {
|
|||||||
|
|
||||||
export function set_local_stream(
|
export function set_local_stream(
|
||||||
local_stream: MediaStream | null,
|
local_stream: MediaStream | null,
|
||||||
video_source: HTMLVideoElement,
|
video_source: HTMLVideoElement
|
||||||
): void {
|
): void {
|
||||||
video_source.srcObject = local_stream;
|
video_source.srcObject = local_stream;
|
||||||
video_source.muted = true;
|
video_source.muted = true;
|
||||||
@@ -16,10 +16,10 @@ export function set_local_stream(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function get_video_stream(
|
export async function get_video_stream(
|
||||||
include_audio: boolean,
|
include_audio: boolean | { deviceId: { exact: string } },
|
||||||
video_source: HTMLVideoElement,
|
video_source: HTMLVideoElement,
|
||||||
device_id?: string,
|
device_id?: string,
|
||||||
track_constraints?: MediaTrackConstraints,
|
track_constraints?: MediaTrackConstraints
|
||||||
): Promise<MediaStream> {
|
): Promise<MediaStream> {
|
||||||
const fallback_constraints = track_constraints || {
|
const fallback_constraints = track_constraints || {
|
||||||
width: { ideal: 500 },
|
width: { ideal: 500 },
|
||||||
@@ -43,10 +43,10 @@ export async function get_video_stream(
|
|||||||
|
|
||||||
export function set_available_devices(
|
export function set_available_devices(
|
||||||
devices: MediaDeviceInfo[],
|
devices: MediaDeviceInfo[],
|
||||||
kind: "videoinput" | "audioinput" = "videoinput",
|
kind: "videoinput" | "audioinput" = "videoinput"
|
||||||
): MediaDeviceInfo[] {
|
): MediaDeviceInfo[] {
|
||||||
const cameras = devices.filter(
|
const cameras = devices.filter(
|
||||||
(device: MediaDeviceInfo) => device.kind === kind,
|
(device: MediaDeviceInfo) => device.kind === kind
|
||||||
);
|
);
|
||||||
|
|
||||||
return cameras;
|
return cameras;
|
||||||
|
|||||||
Reference in New Issue
Block a user