0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-19 20:32:15 +02:00

win-wasapi: Handle changes to the default monitoring device

Splits the WASAPINotify class out of win-wasapi.cpp and makes it a
shared object in the plugin, then also creates a callback to reset
audio monitoring if the default output device changes.
This commit is contained in:
tt2468 2022-06-26 01:44:54 -07:00 committed by Lain
parent 3b0ca4527a
commit 1d5d4b29e7
5 changed files with 214 additions and 64 deletions

View File

@ -5,7 +5,8 @@ legacy_check()
add_library(win-wasapi MODULE)
add_library(OBS::wasapi ALIAS win-wasapi)
target_sources(win-wasapi PRIVATE enum-wasapi.cpp enum-wasapi.hpp plugin-main.cpp win-wasapi.cpp)
target_sources(win-wasapi PRIVATE win-wasapi.cpp wasapi-notify.cpp wasapi-notify.hpp enum-wasapi.cpp enum-wasapi.hpp
plugin-main.cpp)
configure_file(cmake/windows/obs-module.rc.in win-wasapi.rc)
target_sources(win-wasapi PRIVATE win-wasapi.rc)

View File

@ -1,9 +1,12 @@
#include "wasapi-notify.hpp"
#include <obs-module.h>
#include <util/windows/win-version.h>
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("win-wasapi", "en-US")
MODULE_EXPORT const char *obs_module_description(void)
{
return "Windows WASAPI audio input/output sources";
@ -13,6 +16,26 @@ void RegisterWASAPIInput();
void RegisterWASAPIDeviceOutput();
void RegisterWASAPIProcessOutput();
WASAPINotify *notify = nullptr;
static void default_device_changed_callback(EDataFlow flow, ERole, LPCWSTR)
{
const char *id;
obs_get_audio_monitoring_device(nullptr, &id);
if (!id || strcmp(id, "default") != 0)
return;
if (flow != eRender)
return;
auto task = [](void *) {
obs_reset_audio_monitoring();
};
obs_queue_task(OBS_TASK_UI, task, nullptr, false);
}
bool obs_module_load(void)
{
/* MS says 20348, but process filtering seems to work earlier */
@ -30,5 +53,23 @@ bool obs_module_load(void)
RegisterWASAPIDeviceOutput();
if (process_filter_supported)
RegisterWASAPIProcessOutput();
notify = new WASAPINotify();
notify->AddDefaultDeviceChangedCallback(
obs_current_module(), default_device_changed_callback);
return true;
}
void obs_module_unload(void)
{
if (notify) {
delete notify;
notify = nullptr;
}
}
WASAPINotify *GetNotify()
{
return notify;
}

View File

@ -0,0 +1,121 @@
#include "wasapi-notify.hpp"
#include <windows.h>
#include <assert.h>
#include <util/threading.h>
class NotificationClient : public IMMNotificationClient {
volatile long refs = 1;
WASAPINotifyDefaultDeviceChangedCallback cb;
public:
NotificationClient(WASAPINotifyDefaultDeviceChangedCallback cb) : cb(cb)
{
assert(cb);
}
STDMETHODIMP_(ULONG) AddRef()
{
return (ULONG)os_atomic_inc_long(&refs);
}
STDMETHODIMP_(ULONG) STDMETHODCALLTYPE Release()
{
long val = os_atomic_dec_long(&refs);
if (val == 0)
delete this;
return (ULONG)val;
}
STDMETHODIMP QueryInterface(REFIID riid, void **ptr)
{
if (riid == IID_IUnknown) {
*ptr = (IUnknown *)this;
} else if (riid == __uuidof(IMMNotificationClient)) {
*ptr = (IMMNotificationClient *)this;
} else {
*ptr = nullptr;
return E_NOINTERFACE;
}
InterlockedIncrement(&refs);
return S_OK;
}
STDMETHODIMP OnDeviceAdded(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceRemoved(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
STDMETHODIMP OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY)
{
return S_OK;
}
STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role,
LPCWSTR id)
{
if (cb && id)
cb(flow, role, id);
return S_OK;
}
};
WASAPINotify::WASAPINotify()
{
HRESULT res = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(LPVOID *)enumerator.Assign());
if (SUCCEEDED(res)) {
notificationClient = new NotificationClient(
std::bind(&WASAPINotify::OnDefaultDeviceChanged, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
enumerator->RegisterEndpointNotificationCallback(
notificationClient);
} else {
enumerator.Clear();
}
}
WASAPINotify::~WASAPINotify()
{
if (enumerator) {
enumerator->UnregisterEndpointNotificationCallback(
notificationClient);
enumerator.Clear();
}
notificationClient.Clear();
}
void WASAPINotify::AddDefaultDeviceChangedCallback(
void *handle, WASAPINotifyDefaultDeviceChangedCallback cb)
{
if (!handle)
return;
std::lock_guard<std::mutex> l(mutex);
defaultDeviceChangedCallbacks[handle] = cb;
}
void WASAPINotify::RemoveDefaultDeviceChangedCallback(void *handle)
{
if (!handle)
return;
std::lock_guard<std::mutex> l(mutex);
defaultDeviceChangedCallbacks.erase(handle);
}
void WASAPINotify::OnDefaultDeviceChanged(EDataFlow flow, ERole role,
LPCWSTR id)
{
std::lock_guard<std::mutex> l(mutex);
for (const auto &cb : defaultDeviceChangedCallbacks)
cb.second(flow, role, id);
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <util/windows/ComPtr.hpp>
#include <Mmdeviceapi.h>
#include <unordered_map>
#include <functional>
#include <mutex>
typedef std::function<void(EDataFlow, ERole, LPCWSTR)>
WASAPINotifyDefaultDeviceChangedCallback;
class NotificationClient;
class WASAPINotify {
public:
WASAPINotify();
~WASAPINotify();
void AddDefaultDeviceChangedCallback(
void *handle, WASAPINotifyDefaultDeviceChangedCallback cb);
void RemoveDefaultDeviceChangedCallback(void *handle);
private:
void OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR id);
std::mutex mutex;
ComPtr<IMMDeviceEnumerator> enumerator;
ComPtr<NotificationClient> notificationClient;
std::unordered_map<void *, WASAPINotifyDefaultDeviceChangedCallback>
defaultDeviceChangedCallbacks;
};

View File

@ -1,3 +1,4 @@
#include "wasapi-notify.hpp"
#include "enum-wasapi.hpp"
#include <obs-module.h>
@ -28,6 +29,7 @@ using namespace std;
#define OPT_WINDOW "window"
#define OPT_PRIORITY "priority"
WASAPINotify *GetNotify();
static void GetWASAPIDefaults(obs_data_t *settings);
#define OBS_KSAUDIO_SPEAKER_4POINT1 \
@ -154,7 +156,6 @@ protected:
};
class WASAPISource {
ComPtr<IMMNotificationClient> notify;
ComPtr<IMMDeviceEnumerator> enumerator;
ComPtr<IAudioClient> client;
ComPtr<IAudioCaptureClient> capture;
@ -313,57 +314,6 @@ public:
HWND GetHwnd();
};
class WASAPINotify : public IMMNotificationClient {
long refs = 0; /* auto-incremented to 1 by ComPtr */
WASAPISource *source;
public:
WASAPINotify(WASAPISource *source_) : source(source_) {}
STDMETHODIMP_(ULONG) AddRef()
{
return (ULONG)os_atomic_inc_long(&refs);
}
STDMETHODIMP_(ULONG) STDMETHODCALLTYPE Release()
{
long val = os_atomic_dec_long(&refs);
if (val == 0)
delete this;
return (ULONG)val;
}
STDMETHODIMP QueryInterface(REFIID riid, void **ptr)
{
if (riid == IID_IUnknown) {
*ptr = (IUnknown *)this;
} else if (riid == __uuidof(IMMNotificationClient)) {
*ptr = (IMMNotificationClient *)this;
} else {
*ptr = nullptr;
return E_NOINTERFACE;
}
os_atomic_inc_long(&refs);
return S_OK;
}
STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role,
LPCWSTR id)
{
source->SetDefaultDevice(flow, role, id);
return S_OK;
}
STDMETHODIMP OnDeviceAdded(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceRemoved(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
STDMETHODIMP OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY)
{
return S_OK;
}
};
WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
SourceType type)
: source(source_),
@ -414,20 +364,12 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
if (!reconnectSignal.Valid())
throw "Could not create reconnect signal";
notify = new WASAPINotify(this);
if (!notify)
throw "Could not create WASAPINotify";
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(enumerator.Assign()));
if (FAILED(hr))
throw HRError("Failed to create enumerator", hr);
hr = enumerator->RegisterEndpointNotificationCallback(notify);
if (FAILED(hr))
throw HRError("Failed to register endpoint callback", hr);
/* OBS will already load DLL on startup if it exists */
const HMODULE rtwq_module = GetModuleHandle(L"RTWorkQ.dll");
@ -511,12 +453,19 @@ WASAPISource::WASAPISource(obs_data_t *settings, obs_source_t *source_,
WASAPISource::CaptureThread, this,
0, nullptr);
if (!captureThread.Valid()) {
enumerator->UnregisterEndpointNotificationCallback(
notify);
throw "Failed to create capture thread";
}
}
auto notify = GetNotify();
if (notify) {
notify->AddDefaultDeviceChangedCallback(
this,
std::bind(&WASAPISource::SetDefaultDevice, this,
std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3));
}
Start();
}
@ -561,7 +510,11 @@ void WASAPISource::Stop()
WASAPISource::~WASAPISource()
{
enumerator->UnregisterEndpointNotificationCallback(notify);
auto notify = GetNotify();
if (notify) {
notify->RemoveDefaultDeviceChangedCallback(this);
}
Stop();
}