From c29e1c38ff5e4e7c4a7d6aa4eb680a353c08572c Mon Sep 17 00:00:00 2001 From: Curve Date: Fri, 24 May 2024 22:57:26 +0200 Subject: [PATCH] feat(screenshare): allow to specify audi share granularity --- src/main/venmic.ts | 13 +-- src/preload/VesktopNative.ts | 7 +- src/renderer/components/ScreenSharePicker.tsx | 91 +++++++++++++++---- 3 files changed, 85 insertions(+), 26 deletions(-) diff --git a/src/main/venmic.ts b/src/main/venmic.ts index 404c0d2..94be05c 100644 --- a/src/main/venmic.ts +++ b/src/main/venmic.ts @@ -11,6 +11,7 @@ import { IpcEvents } from "shared/IpcEvents"; import { STATIC_DIR } from "shared/paths"; type LinkData = Parameters[0]; +type Node = Record; let PatchBay: typeof PatchBayType | undefined; let patchBayInstance: PatchBayType | undefined; @@ -66,17 +67,17 @@ function getRendererAudioServicePid() { ); } -ipcMain.handle(IpcEvents.VIRT_MIC_LIST, () => { +ipcMain.handle(IpcEvents.VIRT_MIC_LIST, (_, props: string[]) => { const audioPid = getRendererAudioServicePid(); const list = obtainVenmic() - ?.list() - .filter(s => s["application.process.id"] !== audioPid) - .map(s => s["application.name"]); + ?.list(props) + .filter(s => s["application.process.id"] !== audioPid); - const uniqueTargets = [...new Set(list)]; + const sameProps = (x: Node, y: Node) => props.every(prop => x[prop] === y[prop]); + const unique = list?.filter((x, i, array) => array.findIndex(y => sameProps(x, y)) === i); - return list ? { ok: true, targets: uniqueTargets, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; + return list ? { ok: true, targets: unique, hasPipewirePulse } : { ok: false, isGlibCxxOutdated }; }); ipcMain.handle(IpcEvents.VIRT_MIC_START, (_, targets: string[], workaround?: boolean) => { diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index bea57d4..643cb36 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -61,10 +61,11 @@ export const VesktopNative = { }, /** only available on Linux. */ virtmic: { - list: () => + list: (props?: string[]) => invoke< - { ok: false; isGlibCxxOutdated: boolean } | { ok: true; targets: string[]; hasPipewirePulse: boolean } - >(IpcEvents.VIRT_MIC_LIST), + | { ok: false; isGlibCxxOutdated: boolean } + | { ok: true; targets: Record[]; hasPipewirePulse: boolean } + >(IpcEvents.VIRT_MIC_LIST, props), start: (targets: string[], workaround?: boolean) => invoke(IpcEvents.VIRT_MIC_START, targets, workaround), startSystem: (workaround?: boolean, onlyDefaultSpeakers?: boolean) => invoke(IpcEvents.VIRT_MIC_START_SYSTEM, workaround, onlyDefaultSpeakers), diff --git a/src/renderer/components/ScreenSharePicker.tsx b/src/renderer/components/ScreenSharePicker.tsx index ac9fc0b..82a532b 100644 --- a/src/renderer/components/ScreenSharePicker.tsx +++ b/src/renderer/components/ScreenSharePicker.tsx @@ -35,10 +35,11 @@ interface StreamSettings { resolution: StreamResolution; fps: StreamFps; audio: boolean; - audioSource?: string; + audioSources?: string[]; contentHint?: string; workaround?: boolean; onlyDefaultSpeakers?: boolean; + selectByProcess?: boolean; } export interface StreamPick extends StreamSettings { @@ -118,11 +119,11 @@ export function openScreenSharePicker(screens: Source[], skipPicker: boolean) { modalProps={props} submit={async v => { didSubmit = true; - if (v.audioSource && v.audioSource !== "None") { - if (v.audioSource === "Entire System") { + if (v.audioSources && v.audioSources?.[0] !== "None") { + if (v.audioSources?.[0] === "Entire System") { await VesktopNative.virtmic.startSystem(v.workaround); } else { - await VesktopNative.virtmic.start([v.audioSource], v.workaround); + await VesktopNative.virtmic.start(v.audioSources, v.workaround); } } resolve(v); @@ -291,12 +292,14 @@ function StreamSettings({
{isLinux && ( setSettings(s => ({ ...s, audioSource: source }))} + selectByProcess={settings.selectByProcess} + setAudioSources={source => setSettings(s => ({ ...s, audioSources: source }))} setWorkaround={value => setSettings(s => ({ ...s, workaround: value }))} setOnlyDefaultSpeakers={value => setSettings(s => ({ ...s, onlyDefaultSpeakers: value }))} + setSelectByProcess={value => setSettings(s => ({ ...s, selectByProcess: value }))} /> )}
@@ -305,29 +308,61 @@ function StreamSettings({ } function AudioSourcePickerLinux({ - audioSource, + audioSources, workaround, onlyDefaultSpeakers, - setAudioSource, + selectByProcess, + setAudioSources, setWorkaround, setOnlyDefaultSpeakers }: { - audioSource?: string; + audioSources?: string[]; workaround?: boolean; onlyDefaultSpeakers?: boolean; - setAudioSource(s: string): void; + selectByProcess?: boolean; + setAudioSources(s: string[]): void; setWorkaround(b: boolean): void; setOnlyDefaultSpeakers(b: boolean): void; + setSelectByProcess(b: boolean): void; }) { - const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(), { + const properties = selectByProcess ? ["application.process.id", "media.name"] : undefined; + + const [sources, _, loading] = useAwaiter(() => VesktopNative.virtmic.list(properties), { fallbackValue: { ok: true, targets: [], hasPipewirePulse: true } }); - const allSources = sources.ok ? ["None", "Entire System", ...sources.targets] : null; - const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true; + const specialSources = ["None", "Entire System"]; + const allSources = sources.ok ? [...specialSources, ...sources.targets] : null; + const hasPipewirePulse = sources.ok ? sources.hasPipewirePulse : true; const [ignorePulseWarning, setIgnorePulseWarning] = useState(false); + const getName = (x: any) => { + if (specialSources.includes(x)) { + return x; + } + + if (!selectByProcess) { + return x["application.name"]; + } + + let name = `${x["application.name"] ?? "Unknown"}`; + + if (x["media.name"]) { + name += ` - ${x["media.name"]} `; + } + + return `${name} - ${x["application.process.id"]}`; + }; + + const getValue = (x: any) => { + if (specialSources.includes(x)) { + return x; + } + + return x["object.id"]; + }; + return ( <> Audio Settings @@ -355,9 +390,22 @@ function AudioSourcePickerLinux({ {hasPipewirePulse || ignorePulseWarning ? ( allSources && (