author | Matthew Gregan <kinetik@flim.org> |
Tue, 12 Oct 2021 19:25:57 +0000 (2021-10-12) | |
changeset 595574 | c7768090c505715c543b2a71e42b07fcb905d7a9 |
parent 595573 | acc9a2f97b7e93cfcdfc36306d22f68d3f0f4964 |
child 595575 | 1f7cf38e9094ad0ba8f0a3b6506f765400fdc8c7 |
push id | 38872 |
push user | malexandru@mozilla.com |
push date | Wed, 13 Oct 2021 03:44:20 +0000 (2021-10-13) |
treeherder | mozilla-central@9b9f8bfe2625 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | cubeb-reviewers, padenot |
bugs | 1732479 |
milestone | 95.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/media/libcubeb/moz.yaml +++ b/media/libcubeb/moz.yaml @@ -14,10 +14,10 @@ bugzilla: origin: name: "cubeb" description: "Cross platform audio library" url: "https://github.com/kinetiknz/cubeb" license: "ISC" # update.sh will update this value - release: "f495dc982572a70701c5df930ef454b68bb6fe20 (2021-07-28 11:43:17 +1200)" + release: "ef5a1ffde99fe98ba0c28dab5128476e8aa384b8 (2021-10-12 08:09:34 +1300)"
--- a/media/libcubeb/src/cubeb_wasapi.cpp +++ b/media/libcubeb/src/cubeb_wasapi.cpp @@ -2173,45 +2173,40 @@ setup_wasapi_stream_one_side(cubeb_strea 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; - - uint32_t latency_frames = stm->latency; + REFERENCE_TIME latency_hns = frames_to_hns(stream_params->rate, stm->latency); + stm->input_bluetooth_handsfree = false; + cubeb_device_info device_info; - int rv = wasapi_create_device(stm->context, device_info, - stm->device_enumerator.get(), device.get()); - if (rv == CUBEB_OK) { + if (wasapi_create_device(stm->context, device_info, + stm->device_enumerator.get(), + device.get()) == 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); if (strlen(device_info.group_id) >= len && strncmp(device_info.group_id, HANDSFREE_TAG, len) == 0) { stm->input_bluetooth_handsfree = true; - } else { - stm->input_bluetooth_handsfree = false; } // This multiplicator has been found empirically. - latency_frames = default_period_frames * 8; + 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); } - latency_hns = frames_to_hns(device_info.default_rate, latency_frames); - wasapi_destroy_device(&device_info); } else { - stm->input_bluetooth_handsfree = false; - latency_hns = frames_to_hns(mix_params->rate, latency_frames); LOG("Could not get cubeb_device_info."); } if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) { if (initialize_iaudioclient2(audio_client) != CUBEB_OK) { LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError()); // This is not fatal. }
--- a/media/libcubeb/src/cubeb_winmm.c +++ b/media/libcubeb/src/cubeb_winmm.c @@ -116,16 +116,20 @@ struct cubeb_stream { int free_buffers; int shutdown; int draining; HANDLE event; HWAVEOUT waveout; CRITICAL_SECTION lock; uint64_t written; float soft_volume; + /* For position wrap-around handling: */ + size_t frame_size; + DWORD prev_pos_lo_dword; + DWORD pos_hi_dword; }; static size_t bytes_per_frame(cubeb_stream_params params) { size_t bytes; switch (params.format) { @@ -552,16 +556,20 @@ winmm_stream_init(cubeb * context, cubeb if (r != MMSYSERR_NOERROR) { winmm_stream_destroy(stm); return CUBEB_ERROR; } winmm_refill_stream(stm); } + stm->frame_size = bytes_per_frame(stm->params); + stm->prev_pos_lo_dword = 0; + stm->pos_hi_dword = 0; + *stream = stm; return CUBEB_OK; } static void winmm_stream_destroy(cubeb_stream * stm) { @@ -704,55 +712,113 @@ winmm_stream_stop(cubeb_stream * stm) return CUBEB_ERROR; } stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); return CUBEB_OK; } +/* +Microsoft wave audio docs say "samples are the preferred time format in which +to represent the current position", but relying on this causes problems on +Windows XP, the only OS cubeb_winmm is used on. + +While the wdmaud.sys driver internally tracks a 64-bit position and ensures no +backward movement, the WinMM API limits the position returned from +waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The +higher 32 bits are chopped off, and to an API consumer the position can appear +to move backward. + +In theory, even a 32-bit TIME_SAMPLES position should provide plenty of +playback time for typical use cases before this pseudo wrap-around, e.g: + (2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo; + (2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo. +In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a +32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES: + SamplePos = (BytePos * 8) / BitsPerFrame, + where BitsPerFrame = Channels * BitsPerSample, +Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32 +samples, so the maximum for TIME_SAMPLES should be: + (2^29 - 1)/48000 = ~03:06:25; + (2^29 - 1)/44100 = ~03:22:54. +This might still be OK for typical browser usage, but there's also a bug in the +formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without +first casting it to 64 bits, so the highest 3 bits, if set, would get shifted +out, and the maximum possible TIME_SAMPLES drops unacceptably low: + (2^26 - 1)/48000 = ~00:23:18; + (2^26 - 1)/44100 = ~00:25:22. + +To work around these limitations, we just get the position in TIME_BYTES, +recover the 64-bit value, and do our own conversion to samples. +*/ + +/* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */ +static uint64_t +update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword) +{ + /* Caller should be holding stm->lock. */ + if (pos_lo_dword < stm->prev_pos_lo_dword) { + stm->pos_hi_dword++; + LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx", + stm->prev_pos_lo_dword, pos_lo_dword); + LOG("Wrap-around count = %#lx", stm->pos_hi_dword); + LOG("Current 64-bit position = %#llx", + (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword)); + } + stm->prev_pos_lo_dword = pos_lo_dword; + + return (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword); +} + static int winmm_stream_get_position(cubeb_stream * stm, uint64_t * position) { MMRESULT r; MMTIME time; EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; + /* See the long comment above for why not just use TIME_SAMPLES here. */ + time.wType = TIME_BYTES; r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - LeaveCriticalSection(&stm->lock); - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { + if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) { + LeaveCriticalSection(&stm->lock); return CUBEB_ERROR; } - *position = time.u.sample; + *position = update_64bit_position(stm, time.u.cb) / stm->frame_size; + LeaveCriticalSection(&stm->lock); return CUBEB_OK; } static int winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { MMRESULT r; MMTIME time; - uint64_t written; + uint64_t written, position; EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; + /* See the long comment above for why not just use TIME_SAMPLES here. */ + time.wType = TIME_BYTES; r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); + + if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) { + LeaveCriticalSection(&stm->lock); + return CUBEB_ERROR; + } + + position = update_64bit_position(stm, time.u.cb); written = stm->written; LeaveCriticalSection(&stm->lock); - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { - return CUBEB_ERROR; - } - - XASSERT(written - time.u.sample <= UINT32_MAX); - *latency = (uint32_t)(written - time.u.sample); + XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX); + *latency = (uint32_t)(written - (position / stm->frame_size)); return CUBEB_OK; } static int winmm_stream_set_volume(cubeb_stream * stm, float volume) { EnterCriticalSection(&stm->lock);
--- a/media/libcubeb/update.sh +++ b/media/libcubeb/update.sh @@ -76,13 +76,13 @@ if [ -n "$rev" ]; then version=$version-dirty echo "WARNING: updating from a dirty git repository." fi sed -i.bak -e "s/^ *release:.*/ release: \"$version ($date)\"/" moz.yaml if [[ ! "$( grep "$version" moz.yaml )" ]]; then echo "Updating moz.yaml failed." exit 1 fi - rm moz.yaml.bak + rm -f moz.yaml.bak [[ -n "$commits" ]] && echo -e "Pick commits:\n$commits" else echo "Remember to update moz.yaml with the version details." fi