@@ -26,6 +26,12 @@ using namespace std;
2626
2727#define OPT_DEVICE_ID " device_id"
2828#define OPT_USE_DEVICE_TIMING " use_device_timing"
29+
30+ #define OPT_STREAM_ORIGIN " stream_origin"
31+ #define OPT_STREAM_CATEGORY " stream_category"
32+
33+ #define OPT_HARDWARE_OFFLOADED " hardware_offloaded"
34+
2935#define OPT_WINDOW " window"
3036#define OPT_PRIORITY " priority"
3137
@@ -162,6 +168,9 @@ class WASAPISource {
162168 string window_class;
163169 string title;
164170 string executable;
171+ string streamCategory;
172+ bool isOriginStream;
173+ bool isHardwareOffStream;
165174 HWND hwnd = NULL ;
166175 DWORD process_id = 0 ;
167176 const SourceType sourceType;
@@ -241,6 +250,8 @@ class WASAPISource {
241250 static ComPtr<IMMDevice> InitDevice (IMMDeviceEnumerator *enumerator, bool isDefaultDevice, SourceType type,
242251 const string device_id);
243252 static ComPtr<IAudioClient> InitClient (IMMDevice *device, SourceType type, DWORD process_id,
253+ const string &streamCategory, bool isOriginStream,
254+ bool isHardwareOffStream,
244255 PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
245256 speaker_layout &speakers, audio_format &format, uint32_t &sampleRate);
246257 static void InitFormat (const WAVEFORMATEX *wfex, enum speaker_layout &speakers, enum audio_format &format,
@@ -259,6 +270,10 @@ class WASAPISource {
259270 string window_class;
260271 string title;
261272 string executable;
273+
274+ string streamCategory;
275+ bool isOriginStream;
276+ bool isHardwareOffStream;
262277 };
263278
264279 UpdateParams BuildUpdateParams (obs_data_t *settings);
@@ -496,6 +511,10 @@ WASAPISource::UpdateParams WASAPISource::BuildUpdateParams(obs_data_t *settings)
496511 params.executable = executable;
497512 bfree (executable);
498513 }
514+ } else if (sourceType == SourceType::Input) {
515+ params.streamCategory = obs_data_get_string (settings, OPT_STREAM_CATEGORY);
516+ params.isOriginStream = obs_data_get_bool (settings, OPT_STREAM_ORIGIN);
517+ params.isHardwareOffStream = obs_data_get_bool (settings, OPT_HARDWARE_OFFLOADED);
499518 }
500519
501520 return params;
@@ -510,6 +529,9 @@ void WASAPISource::UpdateSettings(UpdateParams &¶ms)
510529 window_class = std::move (params.window_class );
511530 title = std::move (params.title );
512531 executable = std::move (params.executable );
532+ streamCategory = std::move (params.streamCategory );
533+ isOriginStream = params.isOriginStream ;
534+ isHardwareOffStream = params.isHardwareOffStream ;
513535}
514536
515537void WASAPISource::LogSettings ()
@@ -536,11 +558,16 @@ void WASAPISource::Update(obs_data_t *settings)
536558{
537559 UpdateParams params = BuildUpdateParams (settings);
538560
539- const bool restart = (sourceType == SourceType::ProcessOutput)
540- ? ((priority != params.priority ) || (window_class != params.window_class ) ||
541- (title != params.title ) || (executable != params.executable ))
542- : (device_id.compare (params.device_id ) != 0 );
543-
561+ bool restart = false ;
562+ if (sourceType == SourceType::ProcessOutput) {
563+ restart = ((priority != params.priority ) || (window_class != params.window_class ) ||
564+ (title != params.title ) || (executable != params.executable ));
565+ } else if (sourceType == SourceType::DeviceOutput) {
566+ restart = (device_id.compare (params.device_id ) != 0 );
567+ } else if (sourceType == SourceType::Input) {
568+ restart = (device_id.compare (params.device_id ) != 0 ) || isOriginStream != params.isOriginStream ||
569+ streamCategory != params.streamCategory || isHardwareOffStream != params.isHardwareOffStream ;
570+ }
544571 UpdateSettings (std::move (params));
545572 LogSettings ();
546573
@@ -630,7 +657,33 @@ static DWORD GetSpeakerChannelMask(speaker_layout layout)
630657 return (DWORD)layout;
631658}
632659
660+ static inline AUDIO_STREAM_CATEGORY GetAudioStreamCategory (const string &category)
661+ {
662+ if (category == " Communications" )
663+ return AudioCategory_Communications;
664+ else if (category == " Alerts" )
665+ return AudioCategory_Alerts;
666+ else if (category == " SoundEffects" )
667+ return AudioCategory_SoundEffects;
668+ else if (category == " GameEffects" )
669+ return AudioCategory_GameEffects;
670+ else if (category == " GameMedia" )
671+ return AudioCategory_GameMedia;
672+ else if (category == " GameChat" )
673+ return AudioCategory_GameChat;
674+ else if (category == " Speech" )
675+ return AudioCategory_Speech;
676+ else if (category == " Movie" )
677+ return AudioCategory_Movie;
678+ else if (category == " Media" )
679+ return AudioCategory_Media;
680+ else
681+ return AudioCategory_Other;
682+ }
683+
633684ComPtr<IAudioClient> WASAPISource::InitClient (IMMDevice *device, SourceType type, DWORD process_id,
685+ const string &streamCategory, bool isOriginStream,
686+ bool isHardwareOffStream,
634687 PFN_ActivateAudioInterfaceAsync activate_audio_interface_async,
635688 speaker_layout &speakers, audio_format &format, uint32_t &samples_per_sec)
636689{
@@ -707,10 +760,52 @@ ComPtr<IAudioClient> WASAPISource::InitClient(IMMDevice *device, SourceType type
707760 DWORD flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
708761 if (type != SourceType::Input)
709762 flags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
763+
764+ if (type == SourceType::Input && streamCategory != " default" ) {
765+ ComPtr<IAudioClient2> audioClient2 = nullptr ;
766+ client->QueryInterface (__uuidof (IAudioClient2), (void **)(&audioClient2));
767+ if (audioClient2) {
768+ AudioClientProperties properties = {};
769+ properties.cbSize = sizeof (AudioClientProperties);
770+ BOOL isSupportOffloadStream = FALSE ;
771+ properties.Options = isOriginStream ? AUDCLNT_STREAMOPTIONS_RAW : AUDCLNT_STREAMOPTIONS_NONE;
772+ audioClient2->IsOffloadCapable (properties.eCategory , &isSupportOffloadStream);
773+ flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
774+ properties.eCategory = GetAudioStreamCategory (streamCategory);
775+
776+ if (isSupportOffloadStream) {
777+ properties.bIsOffload = isHardwareOffStream ? TRUE : FALSE ;
778+ }
779+ audioClient2->SetClientProperties (&properties);
780+ }
781+ }
782+
710783 res = client->Initialize (AUDCLNT_SHAREMODE_SHARED, flags, BUFFER_TIME_100NS, 0 , pFormat, nullptr );
711784 if (FAILED (res))
712785 throw HRError (" Failed to initialize audio client" , res);
713786
787+ if (type == SourceType::Input) {
788+ ComPtr<IAudioEffectsManager> audioEffectsManager;
789+ HRESULT hr = client->GetService (IID_PPV_ARGS (audioEffectsManager.Assign ()));
790+ if (audioEffectsManager) {
791+ CoTaskMemPtr<AUDIO_EFFECT> audioEffects = nullptr ;
792+ UINT32 numEffects = 0 ;
793+ audioEffectsManager->GetAudioEffects (&audioEffects, &numEffects);
794+
795+ for (UINT32 i = 0 ; i < numEffects; ++i) {
796+ if (audioEffects[i].id == AUDIO_EFFECT_TYPE_DEEP_NOISE_SUPPRESSION) {
797+ blog (LOG_INFO, " DEEP_NOISE_SUPPRESSION STATE:%d, Can Set:%d" ,
798+ audioEffects[i].state , audioEffects[i].canSetState );
799+ } else if (audioEffects[i].id == AUDIO_EFFECT_TYPE_AUTOMATIC_GAIN_CONTROL) {
800+ blog (LOG_INFO, " AUTOMATIC_GAIN_CONTROL STATE:%d, Can Set:%d" ,
801+ audioEffects[i].state , audioEffects[i].canSetState );
802+ } else if (audioEffects[i].id == AUDIO_EFFECT_TYPE_ACOUSTIC_ECHO_CANCELLATION) {
803+ blog (LOG_INFO, " ACOUSTIC_ECHO_CANCELLATION STATE:%d, Can Set:%d" ,
804+ audioEffects[i].state , audioEffects[i].canSetState );
805+ }
806+ }
807+ }
808+ }
714809 return client;
715810}
716811
@@ -834,8 +929,9 @@ void WASAPISource::Initialize()
834929
835930 ResetEvent (receiveSignal);
836931
837- ComPtr<IAudioClient> temp_client = InitClient (device, sourceType, process_id, activate_audio_interface_async,
838- speakers, format, sampleRate);
932+ ComPtr<IAudioClient> temp_client = InitClient (device, sourceType, process_id, streamCategory, isOriginStream,
933+ isHardwareOffStream, activate_audio_interface_async, speakers,
934+ format, sampleRate);
839935 if (sourceType == SourceType::DeviceOutput)
840936 ClearBuffer (device);
841937 ComPtr<IAudioCaptureClient> temp_capture = InitCapture (temp_client, receiveSignal);
@@ -1324,6 +1420,9 @@ static void GetWASAPIDefaultsInput(obs_data_t *settings)
13241420{
13251421 obs_data_set_default_string (settings, OPT_DEVICE_ID, " default" );
13261422 obs_data_set_default_bool (settings, OPT_USE_DEVICE_TIMING, false );
1423+ obs_data_set_default_bool (settings, OPT_STREAM_ORIGIN, false );
1424+ obs_data_set_default_string (settings, OPT_STREAM_CATEGORY, " default" );
1425+ obs_data_set_default_bool (settings, OPT_HARDWARE_OFFLOADED, false );
13271426}
13281427
13291428static void GetWASAPIDefaultsDeviceOutput (obs_data_t *settings)
@@ -1454,6 +1553,24 @@ static bool UpdateWASAPIMethod(obs_properties_t *props, obs_property_t *, obs_da
14541553 return true ;
14551554}
14561555
1556+ static bool StreamCategoryChanged (obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
1557+ {
1558+ std::string category = obs_data_get_string (settings, OPT_STREAM_CATEGORY);
1559+
1560+ obs_property_t *streamOrigin = obs_properties_get (props, OPT_STREAM_ORIGIN);
1561+ obs_property_t *streamHardwareOffload = obs_properties_get (props, OPT_HARDWARE_OFFLOADED);
1562+
1563+ if (category != " default" ) {
1564+ obs_property_set_enabled (streamOrigin, true );
1565+ obs_property_set_enabled (streamHardwareOffload, true );
1566+ } else {
1567+ obs_property_set_enabled (streamOrigin, false );
1568+ obs_property_set_enabled (streamHardwareOffload, false );
1569+ }
1570+
1571+ return true ;
1572+ }
1573+
14571574static obs_properties_t *GetWASAPIPropertiesInput (void *)
14581575{
14591576 obs_properties_t *props = obs_properties_create ();
@@ -1474,6 +1591,23 @@ static obs_properties_t *GetWASAPIPropertiesInput(void *)
14741591
14751592 obs_properties_add_bool (props, OPT_USE_DEVICE_TIMING, obs_module_text (" UseDeviceTiming" ));
14761593
1594+ obs_property_t *stream_category = obs_properties_add_list (props, OPT_STREAM_CATEGORY,
1595+ obs_module_text (" StreamCategory" ),
1596+ OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
1597+ obs_property_set_modified_callback (stream_category, StreamCategoryChanged);
1598+
1599+ obs_property_list_add_string (stream_category, obs_module_text (" Default" ), " default" );
1600+ obs_property_list_add_string (stream_category, obs_module_text (" Communications" ), " Communications" );
1601+ obs_property_list_add_string (stream_category, obs_module_text (" Speech" ), " Speech" );
1602+ obs_property_list_add_string (stream_category, obs_module_text (" VoiceTyping" ), " VoiceTyping" );
1603+ obs_property_list_add_string (stream_category, obs_module_text (" GameChat" ), " GameChat" );
1604+
1605+ obs_property_t *streamOrigin =
1606+ obs_properties_add_bool (props, OPT_STREAM_ORIGIN, obs_module_text (" StreamOrigin" ));
1607+ obs_property_set_enabled (streamOrigin, false );
1608+ obs_property_t *streamHardwareOffload =
1609+ obs_properties_add_bool (props, OPT_HARDWARE_OFFLOADED, obs_module_text (" HardwareOffloaded" ));
1610+ obs_property_set_enabled (streamHardwareOffload, false );
14771611 return props;
14781612}
14791613
0 commit comments