зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1527659 - Update cubeb from upstream to 3afc335. r=kinetik
Differential Revision: https://phabricator.services.mozilla.com/D19676 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f4f8bbfb4a
Коммит
f26917afec
|
@ -19,5 +19,5 @@ origin:
|
|||
license: "ISC"
|
||||
|
||||
# update.sh will update this value
|
||||
release: "feec7e2e893c6dc4188fcb95e15dcf8c19890f46 (2019-01-23 17:15:35 +0200)"
|
||||
release: "3afc3350063ce1d68bbd0287fde4f70b4803d2eb (2019-02-13 10:28:58 +1300)"
|
||||
|
||||
|
|
|
@ -146,9 +146,23 @@ static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
|
|||
|
||||
}
|
||||
|
||||
class wasapi_collection_notification_client;
|
||||
class monitor_device_notifications;
|
||||
|
||||
struct cubeb {
|
||||
cubeb_ops const * ops = &wasapi_ops;
|
||||
cubeb_strings * device_ids;
|
||||
/* Device enumerator to get notifications when the
|
||||
device collection change. */
|
||||
com_ptr<IMMDeviceEnumerator> device_collection_enumerator;
|
||||
com_ptr<wasapi_collection_notification_client> collection_notification_client;
|
||||
/* Collection changed for input (capture) devices. */
|
||||
cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
|
||||
void * input_collection_changed_user_ptr = nullptr;
|
||||
/* Collection changed for output (render) devices. */
|
||||
cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
|
||||
void * output_collection_changed_user_ptr = nullptr;
|
||||
std::unique_ptr<monitor_device_notifications> monitor_notifications;
|
||||
};
|
||||
|
||||
class wasapi_endpoint_notification_client;
|
||||
|
@ -270,6 +284,251 @@ struct cubeb_stream {
|
|||
std::atomic<std::atomic<bool>*> emergency_bailout;
|
||||
};
|
||||
|
||||
class monitor_device_notifications {
|
||||
public:
|
||||
monitor_device_notifications(cubeb * context)
|
||||
: cubeb_context(context)
|
||||
{
|
||||
create_thread();
|
||||
}
|
||||
|
||||
~monitor_device_notifications()
|
||||
{
|
||||
SetEvent(shutdown);
|
||||
WaitForSingleObject(thread, 5000);
|
||||
CloseHandle(thread);
|
||||
|
||||
CloseHandle(input_changed);
|
||||
CloseHandle(output_changed);
|
||||
CloseHandle(shutdown);
|
||||
}
|
||||
|
||||
void notify(EDataFlow flow)
|
||||
{
|
||||
if (flow == eCapture && cubeb_context->input_collection_changed_callback) {
|
||||
bool res = SetEvent(input_changed);
|
||||
if (!res) {
|
||||
LOG("Failed to set input changed event");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (flow == eRender && cubeb_context->output_collection_changed_callback) {
|
||||
bool res = SetEvent(output_changed);
|
||||
if (!res) {
|
||||
LOG("Failed to set output changed event");
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
static unsigned int __stdcall
|
||||
thread_proc(LPVOID args)
|
||||
{
|
||||
XASSERT(args);
|
||||
static_cast<monitor_device_notifications*>(args)
|
||||
->notification_thread_loop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void notification_thread_loop()
|
||||
{
|
||||
struct auto_com {
|
||||
auto_com() {
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
XASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
~auto_com() {
|
||||
CoUninitialize();
|
||||
}
|
||||
} com;
|
||||
|
||||
HANDLE wait_array[3] = {
|
||||
input_changed,
|
||||
output_changed,
|
||||
shutdown,
|
||||
};
|
||||
|
||||
while (true) {
|
||||
Sleep(200);
|
||||
|
||||
DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
|
||||
wait_array,
|
||||
FALSE,
|
||||
INFINITE);
|
||||
if (wait_result == WAIT_OBJECT_0) { // input changed
|
||||
cubeb_context->input_collection_changed_callback(cubeb_context,
|
||||
cubeb_context->input_collection_changed_user_ptr);
|
||||
} else if (wait_result == WAIT_OBJECT_0 + 1) { // output changed
|
||||
cubeb_context->output_collection_changed_callback(cubeb_context,
|
||||
cubeb_context->output_collection_changed_user_ptr);
|
||||
} else if (wait_result == WAIT_OBJECT_0 + 2) { // shutdown
|
||||
break;
|
||||
} else {
|
||||
LOG("Unexpected result %lu", wait_result);
|
||||
}
|
||||
} // loop
|
||||
}
|
||||
|
||||
void create_thread()
|
||||
{
|
||||
output_changed = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!output_changed) {
|
||||
LOG("Failed to create output changed event.");
|
||||
return;
|
||||
}
|
||||
|
||||
input_changed = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!input_changed) {
|
||||
LOG("Failed to create input changed event.");
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown = CreateEvent(nullptr, 0, 0, nullptr);
|
||||
if (!shutdown) {
|
||||
LOG("Failed to create shutdown event.");
|
||||
return;
|
||||
}
|
||||
|
||||
thread = (HANDLE) _beginthreadex(nullptr,
|
||||
256 * 1024,
|
||||
thread_proc,
|
||||
this,
|
||||
STACK_SIZE_PARAM_IS_A_RESERVATION,
|
||||
nullptr);
|
||||
if (!thread) {
|
||||
LOG("Failed to create thread.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE thread = INVALID_HANDLE_VALUE;
|
||||
HANDLE output_changed = INVALID_HANDLE_VALUE;
|
||||
HANDLE input_changed = INVALID_HANDLE_VALUE;
|
||||
HANDLE shutdown = INVALID_HANDLE_VALUE;
|
||||
|
||||
cubeb * cubeb_context = nullptr;
|
||||
};
|
||||
|
||||
class wasapi_collection_notification_client : public IMMNotificationClient
|
||||
{
|
||||
public:
|
||||
/* The implementation of MSCOM was copied from MSDN. */
|
||||
ULONG STDMETHODCALLTYPE
|
||||
AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&ref_count);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE
|
||||
Release()
|
||||
{
|
||||
ULONG ulRef = InterlockedDecrement(&ref_count);
|
||||
if (0 == ulRef) {
|
||||
delete this;
|
||||
}
|
||||
return ulRef;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
QueryInterface(REFIID riid, VOID **ppvInterface)
|
||||
{
|
||||
if (__uuidof(IUnknown) == riid) {
|
||||
AddRef();
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
} else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
AddRef();
|
||||
*ppvInterface = (IMMNotificationClient*)this;
|
||||
} else {
|
||||
*ppvInterface = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
wasapi_collection_notification_client(cubeb * context)
|
||||
: ref_count(1)
|
||||
, cubeb_context(context)
|
||||
{
|
||||
XASSERT(cubeb_context);
|
||||
}
|
||||
|
||||
virtual ~wasapi_collection_notification_client()
|
||||
{ }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
|
||||
{
|
||||
LOG("collection: Audio device default changed, id = %S.", device_id);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* The remaining methods are not implemented, they simply log when called (if
|
||||
log is enabled), for debugging. */
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
|
||||
{
|
||||
LOG("collection: Audio device added.");
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
|
||||
{
|
||||
LOG("collection: Audio device removed.");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
|
||||
{
|
||||
XASSERT(cubeb_context->output_collection_changed_callback ||
|
||||
cubeb_context->input_collection_changed_callback);
|
||||
LOG("collection: Audio device state changed, id = %S, state = %lu.", device_id, new_state);
|
||||
if (new_state == DEVICE_STATE_ACTIVE ||
|
||||
new_state == DEVICE_STATE_NOTPRESENT ||
|
||||
new_state == DEVICE_STATE_UNPLUGGED) {
|
||||
EDataFlow flow;
|
||||
HRESULT hr = GetDataFlow(device_id, &flow);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
cubeb_context->monitor_notifications->notify(flow);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
|
||||
{
|
||||
//Audio device property value changed.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
HRESULT GetDataFlow(LPCWSTR device_id, EDataFlow * flow)
|
||||
{
|
||||
com_ptr<IMMDevice> device;
|
||||
com_ptr<IMMEndpoint> endpoint;
|
||||
|
||||
HRESULT hr = cubeb_context->device_collection_enumerator
|
||||
->GetDevice(device_id, device.receive());
|
||||
if (FAILED(hr)) {
|
||||
LOG("collection: Could not get device: %lx", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = device->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
|
||||
if (FAILED(hr)) {
|
||||
LOG("collection: Could not get endpoint: %lx", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
return endpoint->GetDataFlow(flow);
|
||||
}
|
||||
|
||||
/* refcount for this instance, necessary to implement MSCOM semantics. */
|
||||
LONG ref_count;
|
||||
|
||||
cubeb * cubeb_context = nullptr;
|
||||
};
|
||||
|
||||
class wasapi_endpoint_notification_client : public IMMNotificationClient
|
||||
{
|
||||
public:
|
||||
|
@ -317,7 +576,7 @@ public:
|
|||
HRESULT STDMETHODCALLTYPE
|
||||
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
|
||||
{
|
||||
LOG("Audio device default changed.");
|
||||
LOG("endpoint: Audio device default changed.");
|
||||
|
||||
/* we only support a single stream type for now. */
|
||||
if (flow != eRender && role != eConsole) {
|
||||
|
@ -326,7 +585,7 @@ public:
|
|||
|
||||
BOOL ok = SetEvent(reconfigure_event);
|
||||
if (!ok) {
|
||||
LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
|
||||
LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
@ -336,27 +595,27 @@ public:
|
|||
log is enabled), for debugging. */
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
|
||||
{
|
||||
LOG("Audio device added.");
|
||||
LOG("endpoint: Audio device added.");
|
||||
return S_OK;
|
||||
};
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
|
||||
{
|
||||
LOG("Audio device removed.");
|
||||
LOG("endpoint: Audio device removed.");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
|
||||
{
|
||||
LOG("Audio device state changed.");
|
||||
LOG("endpoint: Audio device state changed.");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
|
||||
{
|
||||
LOG("Audio device property value changed.");
|
||||
//Audio device property value changed.
|
||||
return S_OK;
|
||||
}
|
||||
private:
|
||||
|
@ -1052,6 +1311,46 @@ HRESULT get_endpoint(com_ptr<IMMDevice> & device, LPCWSTR devid)
|
|||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT register_collection_notification_client(cubeb * context)
|
||||
{
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
|
||||
NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(context->device_collection_enumerator.receive()));
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get device enumerator: %lx", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
context->collection_notification_client.reset(new wasapi_collection_notification_client(context));
|
||||
|
||||
hr = context->device_collection_enumerator->RegisterEndpointNotificationCallback(
|
||||
context->collection_notification_client.get());
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not register endpoint notification callback: %lx", hr);
|
||||
context->collection_notification_client.reset();
|
||||
context->device_collection_enumerator.reset();
|
||||
}
|
||||
|
||||
context->monitor_notifications.reset(new monitor_device_notifications(context));
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT unregister_collection_notification_client(cubeb * context)
|
||||
{
|
||||
HRESULT hr = context->device_collection_enumerator->
|
||||
UnregisterEndpointNotificationCallback(context->collection_notification_client.get());
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
context->collection_notification_client = nullptr;
|
||||
context->device_collection_enumerator = nullptr;
|
||||
|
||||
context->monitor_notifications.reset();
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction)
|
||||
{
|
||||
com_ptr<IMMDeviceEnumerator> enumerator;
|
||||
|
@ -2375,6 +2674,79 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * coll
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
wasapi_register_device_collection_changed(cubeb * context,
|
||||
cubeb_device_type devtype,
|
||||
cubeb_device_collection_changed_callback collection_changed_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (collection_changed_callback) {
|
||||
// Make sure it has been unregistered first.
|
||||
XASSERT(((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
|
||||
!context->input_collection_changed_callback) ||
|
||||
((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
|
||||
!context->output_collection_changed_callback));
|
||||
|
||||
// Stop the notification client. Notifications arrive on
|
||||
// a separate thread. We stop them here to avoid
|
||||
// synchronization issues during the update.
|
||||
if (context->device_collection_enumerator.get()) {
|
||||
HRESULT hr = unregister_collection_notification_client(context);
|
||||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
context->input_collection_changed_callback = collection_changed_callback;
|
||||
context->input_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
context->output_collection_changed_callback = collection_changed_callback;
|
||||
context->output_collection_changed_user_ptr = user_ptr;
|
||||
}
|
||||
|
||||
HRESULT hr = register_collection_notification_client(context);
|
||||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (!context->device_collection_enumerator.get()) {
|
||||
// Already unregistered, ignore it.
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
HRESULT hr = unregister_collection_notification_client(context);
|
||||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
context->input_collection_changed_callback = nullptr;
|
||||
context->input_collection_changed_user_ptr = nullptr;
|
||||
}
|
||||
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
context->output_collection_changed_callback = nullptr;
|
||||
context->output_collection_changed_user_ptr = nullptr;
|
||||
}
|
||||
|
||||
// If after the updates we still have registered
|
||||
// callbacks restart the notification client.
|
||||
if (context->input_collection_changed_callback ||
|
||||
context->output_collection_changed_callback) {
|
||||
hr = register_collection_notification_client(context);
|
||||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
cubeb_ops const wasapi_ops = {
|
||||
/*.init =*/ wasapi_init,
|
||||
/*.get_backend_id =*/ wasapi_get_backend_id,
|
||||
|
@ -2396,6 +2768,6 @@ cubeb_ops const wasapi_ops = {
|
|||
/*.stream_get_current_device =*/ NULL,
|
||||
/*.stream_device_destroy =*/ NULL,
|
||||
/*.stream_register_device_changed_callback =*/ NULL,
|
||||
/*.register_device_collection_changed =*/ NULL
|
||||
/*.register_device_collection_changed =*/ wasapi_register_device_collection_changed,
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
|
Загрузка…
Ссылка в новой задаче