Bug 1762041 - Update libcubeb to d97fea4 r=cubeb-reviewers,kinetik

Pick commits:
d97fea4 - Switch device only when the users don't specifiy a particular device (#697)
bdf2837 - Don't reset device if DISABLE_DEVICE_SWITCHING is set
2f50db3 - Fire error callback when reinit fails
4bca265 - Make sure input latency is larger than zero
2d64fff - Return matched device from wasapi_find_bt_handsfree_output_device
f9927c4 - Rename function
8a3d20b - highlight type cast
342ff3c - Avoid duplicate GetDevicePeriod call
d292915 - Call wasapi_create_device only when necessary
016e72e - Don't reset input_bluetooth_handsfree when setting output
86210a1 - Group related lines
1e13faa - Get default_period only when we need it

Differential Revision: https://phabricator.services.mozilla.com/D142365
This commit is contained in:
Chun-Min Chang 2022-03-30 01:17:49 +00:00
parent b3c8440b42
commit d75068e0ab
2 changed files with 128 additions and 71 deletions

View File

@ -19,5 +19,5 @@ origin:
license: "ISC"
# update.sh will update this value
release: "5a2a20c6055d26e0a30fae8f1cf6f6cb975c5c97 (2022-03-22 14:15:59 +1300)"
release: "d97fea4c9015b5d79e11ad68e9cdac610c27ca07 (2022-03-24 17:53:27 -0700)"

View File

@ -946,16 +946,16 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
return out_frames;
}
int
bool
trigger_async_reconfigure(cubeb_stream * stm)
{
XASSERT(stm && stm->reconfigure_event);
LOG("Try reconfiguring the stream");
BOOL ok = SetEvent(stm->reconfigure_event);
if (!ok) {
LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
return CUBEB_ERROR;
}
return CUBEB_OK;
return static_cast<bool>(ok);
}
/* This helper grabs all the frames available from a capture client, put them in
@ -984,8 +984,16 @@ get_input_buffer(cubeb_stream * stm)
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
// Application can recover from this error. More info
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
LOG("Device invalidated error, reset default device");
trigger_async_reconfigure(stm);
LOG("Input device invalidated error");
// No need to reset device if user asks to use particular device, or
// switching is disabled.
if (stm->input_device_id ||
(stm->input_stream_params.prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) ||
!trigger_async_reconfigure(stm)) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return false;
}
return true;
}
@ -1090,8 +1098,16 @@ get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count)
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
// Application can recover from this error. More info
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
LOG("Device invalidated error, reset default device");
trigger_async_reconfigure(stm);
LOG("Output device invalidated error");
// No need to reset device if user asks to use particular device, or
// switching is disabled.
if (stm->output_device_id ||
(stm->output_stream_params.prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) ||
!trigger_async_reconfigure(stm)) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return false;
}
return true;
}
@ -2091,6 +2107,8 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
cubeb_stream_params * mix_params,
com_ptr<IMMDevice> & device)
{
XASSERT(direction == eCapture || direction == eRender);
HRESULT hr;
bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK;
if (is_loopback && direction != eCapture) {
@ -2099,6 +2117,10 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
}
stm->stream_reset_lock.assert_current_thread_owns();
// If user doesn't specify a particular device, we can choose another one when
// the given devid is unavailable.
bool allow_fallback =
direction == eCapture ? !stm->input_device_id : !stm->output_device_id;
bool try_again = false;
// This loops until we find a device that works, or we've exhausted all
// possibilities.
@ -2148,7 +2170,7 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
DIRECTION_NAME, hr);
// A particular device can't be activated because it has been
// unplugged, try fall back to the default audio device.
if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED && allow_fallback) {
LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
devid = nullptr;
device = nullptr;
@ -2225,42 +2247,37 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
}
// Sanity check the latency, it may be that the device doesn't support it.
REFERENCE_TIME minimum_period;
REFERENCE_TIME default_period;
hr = audio_client->GetDevicePeriod(&default_period, &minimum_period);
if (FAILED(hr)) {
LOG("Could not get device period: %lx", hr);
return CUBEB_ERROR;
}
REFERENCE_TIME latency_hns = frames_to_hns(stream_params->rate, stm->latency);
stm->input_bluetooth_handsfree = false;
wasapi_default_devices default_devices(stm->device_enumerator.get());
// Adjust input latency and check if input is using bluetooth handsfree
// protocol.
if (direction == eCapture) {
stm->input_bluetooth_handsfree = false;
cubeb_device_info device_info;
if (wasapi_create_device(stm->context, device_info,
stm->device_enumerator.get(), device.get(),
&default_devices) == CUBEB_OK) {
const char * HANDSFREE_TAG = "BTHHFENUM";
size_t len = sizeof(HANDSFREE_TAG);
if (direction == eCapture) {
uint32_t default_period_frames =
hns_to_frames(device_info.default_rate, default_period);
wasapi_default_devices default_devices(stm->device_enumerator.get());
cubeb_device_info device_info;
if (wasapi_create_device(stm->context, device_info,
stm->device_enumerator.get(), device.get(),
&default_devices) == CUBEB_OK) {
// This multiplicator has been found empirically.
XASSERT(device_info.latency_hi > 0);
uint32_t latency_frames = device_info.latency_hi * 8;
LOG("Input: latency increased to %u frames from a default of %u",
latency_frames, device_info.latency_hi);
latency_hns = frames_to_hns(device_info.default_rate, latency_frames);
const char * HANDSFREE_TAG = "BTHHFENUM";
size_t len = sizeof(HANDSFREE_TAG);
if (strlen(device_info.group_id) >= len &&
strncmp(device_info.group_id, HANDSFREE_TAG, len) == 0) {
LOG("Input device is using bluetooth handsfree protocol");
stm->input_bluetooth_handsfree = true;
}
// This multiplicator has been found empirically.
uint32_t latency_frames = default_period_frames * 8;
LOG("Input: latency increased to %u frames from a default of %u",
latency_frames, default_period_frames);
latency_hns = frames_to_hns(device_info.default_rate, latency_frames);
wasapi_destroy_device(&device_info);
} else {
LOG("Could not get cubeb_device_info. Skip customizing input settings");
}
wasapi_destroy_device(&device_info);
} else {
LOG("Could not get cubeb_device_info.");
}
if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) {
@ -2316,8 +2333,10 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
#undef DIRECTION_NAME
void
wasapi_find_matching_output_device(cubeb_stream * stm)
// Returns a non-null cubeb_devid if we find a matched device, or nullptr
// otherwise.
cubeb_devid
wasapi_find_bt_handsfree_output_device(cubeb_stream * stm)
{
HRESULT hr;
cubeb_device_info * input_device = nullptr;
@ -2326,19 +2345,21 @@ wasapi_find_matching_output_device(cubeb_stream * stm)
// Only try to match to an output device if the input device is a bluetooth
// device that is using the handsfree protocol
if (!stm->input_bluetooth_handsfree) {
return;
return nullptr;
}
wchar_t * tmp = nullptr;
hr = stm->input_device->GetId(&tmp);
if (FAILED(hr)) {
LOG("Couldn't get input device id in wasapi_find_matching_output_device");
return;
LOG("Couldn't get input device id in "
"wasapi_find_bt_handsfree_output_device");
return nullptr;
}
com_heap_ptr<wchar_t> device_id(tmp);
cubeb_devid input_device_id = intern_device_id(stm->context, device_id.get());
cubeb_devid input_device_id = reinterpret_cast<cubeb_devid>(
intern_device_id(stm->context, device_id.get()));
if (!input_device_id) {
return;
return nullptr;
}
int rv = wasapi_enumerate_devices(
@ -2346,7 +2367,7 @@ wasapi_find_matching_output_device(cubeb_stream * stm)
(cubeb_device_type)(CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT),
&collection);
if (rv != CUBEB_OK) {
return;
return nullptr;
}
// Find the input device, and then find the output device with the same group
@ -2358,19 +2379,36 @@ wasapi_find_matching_output_device(cubeb_stream * stm)
}
}
for (uint32_t i = 0; i < collection.count; i++) {
cubeb_device_info & dev = collection.device[i];
if (dev.type == CUBEB_DEVICE_TYPE_OUTPUT && dev.group_id && input_device &&
!strcmp(dev.group_id, input_device->group_id) &&
dev.default_rate == input_device->default_rate) {
LOG("Found matching device for %s: %s", input_device->friendly_name,
dev.friendly_name);
stm->output_device_id =
utf8_to_wstr(reinterpret_cast<char const *>(dev.devid));
cubeb_devid matched_output = nullptr;
if (input_device) {
for (uint32_t i = 0; i < collection.count; i++) {
cubeb_device_info & dev = collection.device[i];
if (dev.type == CUBEB_DEVICE_TYPE_OUTPUT && dev.group_id &&
!strcmp(dev.group_id, input_device->group_id) &&
dev.default_rate == input_device->default_rate) {
LOG("Found matching device for %s: %s", input_device->friendly_name,
dev.friendly_name);
matched_output = dev.devid;
break;
}
}
}
wasapi_device_collection_destroy(stm->context, &collection);
return matched_output;
}
std::unique_ptr<wchar_t[]>
copy_wide_string(const wchar_t * src)
{
XASSERT(src);
size_t len = wcslen(src);
std::unique_ptr<wchar_t[]> copy(new wchar_t[len + 1]);
if (wcsncpy_s(copy.get(), len + 1, src, len) != 0) {
return nullptr;
}
return copy;
}
int
@ -2383,6 +2421,17 @@ setup_wasapi_stream(cubeb_stream * stm)
XASSERT((!stm->output_client || !stm->input_client) &&
"WASAPI stream already setup, close it first.");
std::unique_ptr<const wchar_t[]> selected_output_device_id;
if (stm->output_device_id) {
if (std::unique_ptr<wchar_t[]> tmp =
move(copy_wide_string(stm->output_device_id.get()))) {
selected_output_device_id = move(tmp);
} else {
LOG("Failed to copy output device identifier.");
return CUBEB_ERROR;
}
}
if (has_input(stm)) {
LOG("(%p) Setup capture: device=%p", stm, stm->input_device_id.get());
rv = setup_wasapi_stream_one_side(
@ -2414,8 +2463,12 @@ setup_wasapi_stream(cubeb_stream * stm)
// device, and the default device is the same bluetooth device, pick the
// right output device, running at the same rate and with the same protocol
// as the input.
if (!stm->output_device_id) {
wasapi_find_matching_output_device(stm);
if (!selected_output_device_id) {
cubeb_devid matched = wasapi_find_bt_handsfree_output_device(stm);
if (matched) {
selected_output_device_id =
move(utf8_to_wstr(reinterpret_cast<char const *>(matched)));
}
}
}
@ -2429,23 +2482,24 @@ setup_wasapi_stream(cubeb_stream * stm)
stm->output_stream_params.channels = stm->input_stream_params.channels;
stm->output_stream_params.layout = stm->input_stream_params.layout;
if (stm->input_device_id) {
size_t len = wcslen(stm->input_device_id.get());
std::unique_ptr<wchar_t[]> tmp(new wchar_t[len + 1]);
if (wcsncpy_s(tmp.get(), len + 1, stm->input_device_id.get(), len) != 0) {
LOG("Failed to copy device identifier while copying input stream"
" configuration to output stream configuration to drive loopback.");
if (std::unique_ptr<wchar_t[]> tmp =
move(copy_wide_string(stm->input_device_id.get()))) {
XASSERT(!selected_output_device_id);
selected_output_device_id = move(tmp);
} else {
LOG("Failed to copy device identifier while copying input stream "
"configuration to output stream configuration to drive loopback.");
return CUBEB_ERROR;
}
stm->output_device_id = move(tmp);
}
stm->has_dummy_output = true;
}
if (has_output(stm)) {
LOG("(%p) Setup render: device=%p", stm, stm->output_device_id.get());
LOG("(%p) Setup render: device=%p", stm, selected_output_device_id.get());
rv = setup_wasapi_stream_one_side(
stm, &stm->output_stream_params, stm->output_device_id.get(), eRender,
__uuidof(IAudioRenderClient), stm->output_client,
stm, &stm->output_stream_params, selected_output_device_id.get(),
eRender, __uuidof(IAudioRenderClient), stm->output_client,
&stm->output_buffer_frame_count, stm->refill_event, stm->render_client,
&stm->output_mix_params, stm->output_device);
if (rv != CUBEB_OK) {
@ -2675,12 +2729,15 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
return rv;
}
if (!((input_stream_params ? (input_stream_params->prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING)
: 0) ||
(output_stream_params ? (output_stream_params->prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING)
: 0))) {
// Follow the system default devices when not specifying devices explicitly
// and CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING is not set.
if ((!input_device && input_stream_params &&
!(input_stream_params->prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING)) ||
(!output_device && output_stream_params &&
!(output_stream_params->prefs &
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING))) {
LOG("Follow the system default input or/and output devices");
HRESULT hr = register_notification_client(stm.get());
if (FAILED(hr)) {
/* this is not fatal, we can still play audio, but we won't be able