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:
Alex Chronopoulos 2019-02-13 21:50:03 +00:00
Родитель f4f8bbfb4a
Коммит f26917afec
2 изменённых файлов: 380 добавлений и 8 удалений

Просмотреть файл

@ -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