[vlc-devel] [PATCH 3/3] wasapi: start deferred

Thomas Guillem thomas at gllm.fr
Mon Sep 30 17:23:37 CEST 2019



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);

I should use the global delay here, not only the internal one. 

> +    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