зеркало из https://github.com/mozilla/gecko-dev.git
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_devicef9927c4
- Rename function 8a3d20b - highlight type cast342ff3c
- Avoid duplicate GetDevicePeriod calld292915
- 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:
Родитель
b3c8440b42
Коммит
d75068e0ab
|
@ -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)"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче