mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +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:
parent
3b0ca4527a
commit
1d5d4b29e7
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
121
plugins/win-wasapi/wasapi-notify.cpp
Normal file
121
plugins/win-wasapi/wasapi-notify.cpp
Normal 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);
|
||||
}
|
34
plugins/win-wasapi/wasapi-notify.hpp
Normal file
34
plugins/win-wasapi/wasapi-notify.hpp
Normal 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;
|
||||
};
|
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user