[vlc-devel] [PATCH 3/3] wasapi: start deferred
Thomas Guillem
thomas at gllm.fr
Mon Sep 30 16:31:06 CEST 2019
An other goal of this patch set is to remove the following code in src/audio_output/dec.c in aout_DecSynchronize():
if (owner->sync.discontinuity)
{
/* Chicken-egg situation for most aout modules that can't be started
* deferred (all except PulseAudio). These modules will start to play
* data immediately and ignore the given play_date (that take the clock
* jitter into account). We don't want to let aout_RequestRetiming()
* handle the first silence (from the "Early audio output" case) since
* this function will first update the clock without taking the jitter
* into account. Therefore, we manually insert silence that correspond
* to the clock jitter value before updating the clock.
*/
vlc_tick_t play_date =
vlc_clock_ConvertToSystem(owner->sync.clock, system_now + delay,
dec_pts, owner->sync.rate);
vlc_tick_t jitter = play_date - system_now;
if (jitter > 0)
{
aout_DecSilence (aout, jitter, dec_pts - delay);
if (aout->time_get(aout, &delay) != 0)
return;
}
}
If removed, I can move this hack into the secondary aout modules (alsa, directsound, oss...).
On Mon, Sep 30, 2019, at 16:26, Thomas Guillem wrote:
> The idea is to act like PulseAudio and coreaudio recently.
>
> Instead of returning a valid date from the first TimeGet() to let the core
> handle the initial delay, the initial delay is handled inside this module by
> deferring the Start.
>
> The big advantage of this solution is that this module can now handle a change
> of play date, like when the clock jitter is changed by the video decoder
> latency for example (cf. the "Trying to fix the big one: Frame threading
> regressions" patch set).
>
> A Windows Timer is used to defer this start. Audio samples are written while
> the AudioClient is not started (behavior handled according to the
> documentation). If the audio buffer is full before the AudioClient is started,
> the Play function will wait for it, cf. last commit about event-driven
> buffering.
>
> RFC: Am I OK regarding threading COM/MTA ? I confess that I don't fully
> comprehend this part. Also there is no documentation about threading in the
> AudioClient API.
> ---
> modules/audio_output/wasapi.c | 96 +++++++++++++++++++++++++++++++++--
> 1 file changed, 91 insertions(+), 5 deletions(-)
>
> diff --git a/modules/audio_output/wasapi.c b/modules/audio_output/wasapi.c
> index 62827caa2e..f09ab29843 100644
> --- a/modules/audio_output/wasapi.c
> +++ b/modules/audio_output/wasapi.c
> @@ -33,6 +33,7 @@
> #include <vlc_common.h>
> #include <vlc_codecs.h>
> #include <vlc_aout.h>
> +#include <vlc_atomic.h>
> #include <vlc_plugin.h>
>
> #include <audioclient.h>
> @@ -91,6 +92,12 @@ typedef struct aout_stream_sys
> {
> IAudioClient *client;
> HANDLE hEvent;
> + HANDLE hTimer;
> +
> +#define STARTED_STATE_INIT 0
> +#define STARTED_STATE_OK 1
> +#define STARTED_STATE_ERROR 2
> + atomic_char started_state;
>
> uint8_t chans_table[AOUT_CHAN_MAX];
> uint8_t chans_to_reorder;
> @@ -102,7 +109,6 @@ typedef struct aout_stream_sys
> UINT32 frames; /**< Total buffer size (frames) */
> } aout_stream_sys_t;
>
> -
> /*** VLC audio output callbacks ***/
> static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t *restrict delay)
> {
> @@ -111,6 +117,9 @@ static HRESULT TimeGet(aout_stream_t *s, vlc_tick_t
> *restrict delay)
> UINT64 pos, qpcpos, freq;
> HRESULT hr;
>
> + if (atomic_load(&sys->started_state) != STARTED_STATE_OK)
> + return E_FAIL;
> +
> hr = IAudioClient_GetService(sys->client, &IID_IAudioClock, &pv);
> if (FAILED(hr))
> {
> @@ -141,6 +150,50 @@ static HRESULT TimeGet(aout_stream_t *s,
> vlc_tick_t *restrict delay)
> return hr;
> }
>
> +static void CALLBACK StartDeferredCallback(void *val, BOOLEAN timeout)
> +{
> + aout_stream_t *s = val;
> + aout_stream_sys_t *sys = s->sys;
> +
> + HRESULT hr = IAudioClient_Start(sys->client);
> + atomic_store(&sys->started_state,
> + SUCCEEDED(hr) ? STARTED_STATE_OK :
> STARTED_STATE_ERROR);
> + (void) timeout;
> +}
> +
> +static HRESULT StartDeferred(aout_stream_t *s, vlc_tick_t date)
> +{
> + aout_stream_sys_t *sys = s->sys;
> + vlc_tick_t written = vlc_tick_from_frac(sys->written, sys->rate);
> + vlc_tick_t start_delay = date - vlc_tick_now() - written;
> + DWORD start_delay_ms = MS_FROM_VLC_TICK(start_delay);
> + BOOL res;
> +
> + /* Create or update the current timer */
> + if (sys->hTimer == NULL)
> + res = CreateTimerQueueTimer(&sys->hTimer, NULL,
> StartDeferredCallback,
> + s, start_delay_ms, 0,
> + WT_EXECUTEDEFAULT |
> WT_EXECUTEONLYONCE);
> + else
> + res = ChangeTimerQueueTimer(NULL, sys->hTimer, start_delay_ms,
> 0);
> +
> + if (!res)
> + {
> + msg_Warn(s, "timer update failed, starting now");
> + HRESULT hr = IAudioClient_Start(sys->client);
> + if (FAILED(hr))
> + {
> + atomic_store(&sys->started_state, STARTED_STATE_ERROR);
> + return hr;
> + }
> + atomic_store(&sys->started_state, STARTED_STATE_OK);
> + }
> + else
> + msg_Dbg(s, "deferring start (%"PRId64" us)", start_delay);
> +
> + return S_OK;
> +}
> +
> static HRESULT Play(aout_stream_t *s, block_t *block, vlc_tick_t date)
> {
> (void) date;
> @@ -148,6 +201,19 @@ static HRESULT Play(aout_stream_t *s, block_t
> *block, vlc_tick_t date)
> void *pv;
> HRESULT hr;
>
> + char started_state = atomic_load(&sys->started_state);
> + if (unlikely(started_state == STARTED_STATE_ERROR))
> + {
> + hr = E_FAIL;
> + goto out;
> + }
> + else if (started_state == STARTED_STATE_INIT)
> + {
> + hr = StartDeferred(s, date);
> + if (FAILED(hr))
> + goto out;
> + }
> +
> if (sys->chans_to_reorder)
> aout_ChannelReorder(block->p_buffer, block->i_buffer,
> sys->chans_to_reorder, sys->chans_table,
> sys->format);
> @@ -192,7 +258,6 @@ static HRESULT Play(aout_stream_t *s, block_t
> *block, vlc_tick_t date)
> msg_Err(s, "cannot release buffer (error 0x%lX)", hr);
> break;
> }
> - IAudioClient_Start(sys->client);
>
> block->p_buffer += copy;
> block->i_buffer -= copy;
> @@ -216,13 +281,29 @@ out:
> return hr;
> }
>
> +static HRESULT ResetTimerAndStop(aout_stream_t *s)
> +{
> + aout_stream_sys_t *sys = s->sys;
> + if (sys->hTimer != NULL)
> + {
> + DeleteTimerQueueTimer(NULL, sys->hTimer, INVALID_HANDLE_VALUE);
> + sys->hTimer = NULL;
> + }
> + if (atomic_load(&sys->started_state) == STARTED_STATE_OK)
> + return IAudioClient_Stop(sys->client);
> + return S_OK;
> +}
> +
> static HRESULT Pause(aout_stream_t *s, bool paused)
> {
> aout_stream_sys_t *sys = s->sys;
> HRESULT hr;
>
> if (paused)
> - hr = IAudioClient_Stop(sys->client);
> + {
> + hr = ResetTimerAndStop(s);
> + /* Don't reset the timer state, we won't have to start
> deferred again. */
> + }
> else
> hr = IAudioClient_Start(sys->client);
> if (FAILED(hr))
> @@ -236,7 +317,9 @@ static HRESULT Flush(aout_stream_t *s)
> aout_stream_sys_t *sys = s->sys;
> HRESULT hr;
>
> - IAudioClient_Stop(sys->client);
> + ResetTimerAndStop(s);
> + /* Reset the timer state, the next start need to be deferred. */
> + atomic_store(&sys->started_state, STARTED_STATE_INIT);
>
> hr = IAudioClient_Reset(sys->client);
> if (SUCCEEDED(hr))
> @@ -466,7 +549,8 @@ static void Stop(aout_stream_t *s)
> {
> aout_stream_sys_t *sys = s->sys;
>
> - IAudioClient_Stop(sys->client); /* should not be needed */
> + ResetTimerAndStop(s);
> +
> CloseHandle(sys->hEvent);
> IAudioClient_Release(sys->client);
>
> @@ -486,6 +570,8 @@ static HRESULT Start(aout_stream_t *s,
> audio_sample_format_t *restrict pfmt,
> return E_OUTOFMEMORY;
> sys->client = NULL;
> sys->hEvent = NULL;
> + sys->hTimer = NULL;
> + atomic_init(&sys->started_state, STARTED_STATE_INIT);
>
> /* Configure audio stream */
> WAVEFORMATEXTENSIBLE_IEC61937 wf_iec61937;
> --
> 2.20.1
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
More information about the vlc-devel
mailing list