Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion app/components-react/windows/SourceProperties.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import React, { useMemo, useState } from 'react';
import { shell } from '@electron/remote';
import { Services } from '../service-provider';
import { IObsFormProps, ObsForm } from '../obs/ObsForm';
import { TObsFormData } from '../../components/obs/inputs/ObsInput';
import { ModalLayout } from '../shared/ModalLayout';
import Display from '../shared/Display';
import { assertIsDefined } from '../../util/properties-type-guards';
import { useSubscription } from '../hooks/useSubscription';
import { useVuex } from '../hooks';
import { $t } from 'services/i18n';

const SUPPORTED_WEBCAMS: Set<string> = new Set([
'0x046d-0x0943',
'0x046d-0x0946',
'0x046d-0x0919',
'0x046d-0x0944',
'0x046d-0x091d',
'0x046d-0x085e',
'0x046d-0x086b',
'0x046d-0x082d',
'0x046d-0x0892',
'0x046d-0x08e5',
'0x046d-0x085c',
'0x046d-0x0883',
'0x046d-0x0894',
'0x046d-0x091b',
'0x046d-0x091c',
]);

// Returns the vid and pid from a logitech device id
export function parseId(id?: string) {
if (!id) return '';
//Id strings have a lot of elements but we want to pull the vid and pid
const match = id.match(/vid_([\w\d]+)&pid_([\w\d]+)/);
if (!match) return '';
const [_, vid, pid] = [...match];
return `0x${vid}-0x${pid}`;
}

export default function SourceProperties() {
const {
Expand Down Expand Up @@ -52,6 +81,16 @@ export default function SourceProperties() {
]);
}

const videoDevice =
source &&
['dshow_input', 'macos_avcapture'].includes(source.type) &&
source.getSettings().video_device_id;
const isSupportedWebcam = SUPPORTED_WEBCAMS.has(parseId(videoDevice));

function configureInGHub() {
shell.openExternal(`lghubapp://devices/${parseId(videoDevice)}/default`);
}

// make the URL field debounced for the browser_source
const extraProps: IObsFormProps['extraProps'] = {};
if (source && source.type === 'browser_source') {
Expand All @@ -69,6 +108,11 @@ export default function SourceProperties() {
extraProps={extraProps}
layout="horizontal"
/>
{isSupportedWebcam && (
<a onClick={configureInGHub} style={{ marginLeft: 184 }}>
{$t('Configure on G HUB')}
</a>
)}
</ModalLayout>
);
}
71 changes: 46 additions & 25 deletions app/components-react/windows/advanced-audio/SourceSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useRef, useMemo, useEffect } from 'react';
import { shell } from '@electron/remote';
import { Button, Collapse, Tooltip } from 'antd';
import {
SliderInput,
Expand All @@ -17,6 +18,9 @@ import { Source } from 'services/sources';
import { $t } from 'services/i18n';
import Utils from 'services/utils';
import styles from './AdvancedAudio.m.less';
import { parseId } from 'components-react/windows/SourceProperties';

const SUPPORTED_MICS: Set<string> = new Set(['0x046d-0x0afc']);

const { Panel } = Collapse;

Expand Down Expand Up @@ -247,13 +251,17 @@ function PanelForm(p: { source: AudioSource }) {
max={5000}
uncontrolled={false}
/>
{!isProcessCapture && <SwitchInput
label={$t('Downmix to Mono')}
value={forceMono}
name="forceMono"
onChange={value => handleSettingsChange('forceMono', value)}
tooltip={$t('Route audio to the central channel instead of left or right stereo channels')}
/>}
{!isProcessCapture && (
<SwitchInput
label={$t('Downmix to Mono')}
value={forceMono}
name="forceMono"
onChange={value => handleSettingsChange('forceMono', value)}
tooltip={$t(
'Route audio to the central channel instead of left or right stereo channels',
)}
/>
)}
<ListInput
label={$t('Audio Monitoring')}
options={p.source.monitoringOptions}
Expand Down Expand Up @@ -298,11 +306,14 @@ function DeviceInputs(p: { source: Source }) {
});
setStatefulSettings({ ...statefulSettings, [name]: value });
}

const isOutputCapture: boolean = p.source.type === 'wasapi_process_output_capture';
let windowMatchPriorityOptions = null;
if (isOutputCapture) {
const priorityProperty = sourceProperties.find(prop => prop.name === 'priority');
windowMatchPriorityOptions = (priorityProperty as IObsListInput<TObsValue> | undefined)?.options.map(option => ({
windowMatchPriorityOptions = (priorityProperty as
| IObsListInput<TObsValue>
| undefined)?.options.map(option => ({
label: option.description,
value: option.value,
}));
Expand All @@ -312,9 +323,14 @@ function DeviceInputs(p: { source: Source }) {
const inputLabel = isOutputCapture ? 'Window' : 'Device';
const foundDevice: boolean = deviceOptions.some(option => option.value === settingId);

const isSupportedMic = SUPPORTED_MICS.has(parseId(settingId));

function configureInGHub() {
shell.openExternal(`lghubapp://devices/${parseId(settingId)}/default`);
}

// Ensure the input is still valid. If not, reset to Default device which should be at index 0.
const inputId =
foundDevice || deviceOptions.length === 0 ? settingId : deviceOptions[0].value;
const inputId = foundDevice || deviceOptions.length === 0 ? settingId : deviceOptions[0].value;
if (!foundDevice && deviceOptions.length > 0) {
handleInput(inputField, deviceOptions[0].value);
}
Expand All @@ -329,21 +345,26 @@ function DeviceInputs(p: { source: Source }) {
onChange={value => handleInput(inputField, value)}
/>
}
{
windowMatchPriorityOptions && (
<ListInput
label={$t('Window Match Priority')}
options={windowMatchPriorityOptions}
value={statefulSettings.priority}
onChange={value => handleInput('priority', value)}
/>
)
}
{!isOutputCapture && <SwitchInput
label={$t('Use Device Timestamps')}
value={statefulSettings.use_device_timing}
onChange={value => handleInput('use_device_timing', value)}
/>}
{windowMatchPriorityOptions && (
<ListInput
label={$t('Window Match Priority')}
options={windowMatchPriorityOptions}
value={statefulSettings.priority}
onChange={value => handleInput('priority', value)}
/>
)}
{!isOutputCapture && (
<SwitchInput
label={$t('Use Device Timestamps')}
value={statefulSettings.use_device_timing}
onChange={value => handleInput('use_device_timing', value)}
/>
)}
{isSupportedMic && (
<a onClick={configureInGHub} style={{ marginLeft: 184 }}>
{$t('Configure on G HUB')}
</a>
)}
</>
);
}
3 changes: 2 additions & 1 deletion app/i18n/en-US/sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -441,5 +441,6 @@
"Health changes": "Health changes",
"Search...": "Search...",
"Virtual Cam": "Virtual Cam",
"Virtual Cam Error": "Virtual Cam Error"
"Virtual Cam Error": "Virtual Cam Error",
"Configure on G HUB": "Configure on G HUB"
}
7 changes: 3 additions & 4 deletions app/services/sources/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -759,9 +759,8 @@ export class SourcesService extends StatefulService<ISourcesState> {
// 'monitor_capture',
// 'window_capture',
'game_capture',
// 'dshow_input',
'dshow_input',
// 'wasapi_input_capture',
'wasapi_input_capture',
// 'wasapi_output_capture',
// 'decklink-input',
// 'scene',
Expand All @@ -771,9 +770,9 @@ export class SourcesService extends StatefulService<ISourcesState> {
// 'liv_capture',
// 'ovrstream_dc_source',
// 'vlc_source',
// 'coreaudio_input_capture',
'coreaudio_input_capture',
// 'coreaudio_output_capture',
// 'macos_avcapture',
'macos_avcapture',
// 'display_capture',
// 'audio_line',
// 'syphon-input',
Expand Down
Loading