diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index 20550671..12fba525 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -21,6 +21,7 @@ import { } from "@vencord/types/webpack/common"; import { Node } from "@vencord/venmic"; import type { Dispatch, SetStateAction } from "react"; +import { patchOverrideDevices } from "renderer/patches/screenShareFixes"; import { addPatch } from "renderer/patches/shared"; import { useSettings } from "renderer/settings"; import { isLinux, isWindows } from "renderer/utils"; @@ -47,6 +48,8 @@ interface StreamSettings { resolution: StreamResolution; fps: StreamFps; audio: boolean; + overrideAudioDevice?: string; + overrideVideoDevice?: string; contentHint?: string; includeSources?: AudioSources; excludeSources?: AudioSources; @@ -140,6 +143,11 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) { } } + patchOverrideDevices({ + audio: v.overrideAudioDevice, + video: v.overrideVideoDevice + }); + resolve(v); }} close={() => { @@ -325,6 +333,16 @@ function StreamSettings({ } ); + const [audioDevices, , audioDevicesPending] = useAwaiter( + () => navigator.mediaDevices.enumerateDevices().then(g => g.filter(d => d.kind === "audioinput")), + { fallbackValue: [] } + ); + + const [videoDevices, , videoDevicesPending] = useAwaiter( + () => navigator.mediaDevices.enumerateDevices().then(g => g.filter(d => d.kind === "videoinput")), + { fallbackValue: [] } + ); + const openSettings = () => { const key = openModal(props => ( + +
+ + {audioDevicesPending ? "Loading audio devices..." : "Audio devices"} + + ({ + label, + value: deviceId + }))} + isSelected={d => settings.overrideVideoDevice === d} + select={d => { + setSettings(v => ({ ...v, overrideVideoDevice: d })); + }} + serialize={String} + popoutPosition="top" + closeOnSelect={true} + isDisabled={videoDevicesPending} + /> +
+ {isWindows && ( label === "vencord-screen-share"); - return audioDevice?.deviceId; - } catch (error) { - return null; - } +const original = navigator.mediaDevices.getDisplayMedia; + +interface OverrideDevices { + audio: string | undefined; + video: string | undefined; +} + +let overrideDevices: OverrideDevices = { audio: undefined, video: undefined }; + +export const patchOverrideDevices = (newOverrideDevices: OverrideDevices) => { + overrideDevices = newOverrideDevices; +}; + +async function getVirtmic() { + try { + const devices = await navigator.mediaDevices.enumerateDevices(); + const audioDevice = devices.find(({ label }) => label === "vencord-screen-share"); + return audioDevice?.deviceId; + } catch (error) { + return null; + } +} + +navigator.mediaDevices.getDisplayMedia = async function (opts) { + const stream = await original.call(this, opts); + + if (overrideDevices.audio) { + const audio = await navigator.mediaDevices.getUserMedia({ + audio: { + deviceId: { exact: overrideDevices.audio }, + autoGainControl: false, + echoCancellation: false, + noiseSuppression: false + } + }); + + stream.getAudioTracks().forEach(t => { + t.stop(); + stream.removeTrack(t); + }); + + audio.getAudioTracks().forEach(t => { + stream.addTrack(t); + }); } - navigator.mediaDevices.getDisplayMedia = async function (opts) { - const stream = await original.call(this, opts); + if (overrideDevices.video) { + const video = await navigator.mediaDevices.getUserMedia({ + video: { + deviceId: { exact: overrideDevices.video } + } + }); + + stream.getVideoTracks().forEach(t => { + t.stop(); + stream.removeTrack(t); + }); + + video.getVideoTracks().forEach(t => { + stream.addTrack(t); + }); + } + + if (isLinux) { const id = await getVirtmic(); const frameRate = Number(currentSettings?.fps); @@ -63,7 +112,7 @@ if (isLinux) { }); audio.getAudioTracks().forEach(t => stream.addTrack(t)); } + } - return stream; - }; -} + return stream; +};