summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorKevin Mitchell <kevmitch@gmail.com>2015-12-21 09:45:32 -0800
committerKevin Mitchell <kevmitch@gmail.com>2015-12-21 16:58:51 -0800
commit5afa68835ade9f21f9c709f791319bf9d2e35265 (patch)
treec9a427353ca228c4c4bd415d93e631e8ee0c27d5 /audio
parent5360baa49e630048b2454504c9a5d2ffe4e3651f (diff)
downloadmpv-5afa68835ade9f21f9c709f791319bf9d2e35265.tar.bz2
mpv-5afa68835ade9f21f9c709f791319bf9d2e35265.tar.xz
ao_wasapi: fix delay calculation
Make sure that subtraction of performance counters is done correctly. Follow the *exact* instructions for converting performance counter to something comparable to the QPCposition returned by IAudioClient::GetPosition https://msdn.microsoft.com/en-us/library/windows/desktop/dd370889%28v=vs.85%29.aspx Also make sure that subtraction of unsigned integers is stored into a signed integer to avoid nastiness. Also be more careful about overflow in the conversion of the device position into number of samples. Avoid casting mp_time_us() to a double, and use llrint to convert the double precision delay_us back to integer for ao_read_data. Finally, actually check the return value of ao_read_data and add a verbose message if it is not the expected value. Unfortunately, there is no way to tell WASAPI when this happens since the frame_count in ReleaseBuffer must match GetBuffer.
Diffstat (limited to 'audio')
-rw-r--r--audio/out/ao_wasapi.c57
1 files changed, 36 insertions, 21 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index bafeae1d63..8e21bb3260 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -18,6 +18,7 @@
*/
#include <stdlib.h>
+#include <math.h>
#include <inttypes.h>
#include <process.h>
#include <initguid.h>
@@ -33,7 +34,15 @@
#include "osdep/timer.h"
#include "osdep/io.h"
-static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
+
+static UINT64 uint64_scale(UINT64 x, UINT64 num, UINT64 den)
+{
+ return (x / den) * num
+ + ((x % den) * (num / den))
+ + ((x % den) * (num % den)) / den;
+}
+
+static HRESULT get_device_delay(struct wasapi_state *state, double *delay_us) {
UINT64 sample_count = atomic_load(&state->sample_count);
UINT64 position, qpc_position;
HRESULT hr;
@@ -48,21 +57,24 @@ static HRESULT get_device_delay(struct wasapi_state *state, double *delay) {
}
EXIT_ON_ERROR(hr);
- LARGE_INTEGER qpc_count;
- QueryPerformanceCounter(&qpc_count);
- double qpc_diff = (qpc_count.QuadPart * 1e7 / state->qpc_frequency.QuadPart)
- - qpc_position;
-
- position += state->clock_frequency * (uint64_t) (qpc_diff / 1e7);
-
- // convert position to the same base as sample_count
- position = position * state->format.Format.nSamplesPerSec
- / state->clock_frequency;
-
- double diff = sample_count - position;
- *delay = diff / state->format.Format.nSamplesPerSec;
-
- MP_TRACE(state, "Device delay: %g samples (%g ms)\n", diff, *delay * 1000);
+ // convert position to number of samples careful to avoid overflow
+ UINT64 sample_position = uint64_scale(position,
+ state->format.Format.nSamplesPerSec,
+ state->clock_frequency);
+ INT64 diff = sample_count - sample_position;
+ *delay_us = diff * 1e6 / state->format.Format.nSamplesPerSec;
+
+ // Correct for any delay in IAudioClock_GetPosition above.
+ // This should normally be very small (<1 us), but just in case. . .
+ LARGE_INTEGER qpc;
+ QueryPerformanceCounter(&qpc);
+ // apparently, we're supposed to allow the qpc scale to overflow to be
+ // comparable to qpc_position (100ns units), so don't do anything fancy
+ INT64 qpc_diff = qpc.QuadPart * 10000000 / state->qpc_frequency.QuadPart
+ - qpc_position;
+ *delay_us -= qpc_diff / 10.0; // convert to us
+
+ MP_TRACE(state, "Device delay: %g us\n", *delay_us);
return S_OK;
exit_label:
@@ -86,9 +98,11 @@ static void thread_feed(struct ao *ao)
MP_TRACE(ao, "Frame to fill: %"PRIu32". Padding: %"PRIu32"\n",
frame_count, padding);
}
- double delay;
- hr = get_device_delay(state, &delay);
+ double delay_us;
+ hr = get_device_delay(state, &delay_us);
EXIT_ON_ERROR(hr);
+ // add the buffer delay
+ delay_us += frame_count * 1e6 / state->format.Format.nSamplesPerSec;
BYTE *pData;
hr = IAudioRenderClient_GetBuffer(state->pRenderClient,
@@ -97,10 +111,11 @@ static void thread_feed(struct ao *ao)
BYTE *data[1] = {pData};
- ao_read_data(ao, (void**)data, frame_count, (int64_t) (
- mp_time_us() + delay * 1e6 +
- frame_count * 1e6 / state->format.Format.nSamplesPerSec));
+ ao_read_data(ao, (void **)data, frame_count,
+ mp_time_us() + (int64_t)llrint(delay_us));
+ // note, we can't use ao_read_data return value here since we already
+ // commited to frame_count above in the GetBuffer call
hr = IAudioRenderClient_ReleaseBuffer(state->pRenderClient,
frame_count, 0);
EXIT_ON_ERROR(hr);