[vlc-devel] [PATCH] Erase regularily the already played directsound buffer outside of the play callback

Rémi Denis-Courmont remi at remlab.net
Thu Apr 24 09:26:48 CEST 2014


Le 2014-04-23 00:49, Denis Charmet a écrit :
> Since Directsound is unable to detect an underrun while looping, this
> might be the simplest solution to avoid audio glitches when the audio
> track finishes before the other es tracks.
>
> It may look overkill but this properly fix #11145 once and for all.
> ---
>  modules/audio_output/directsound.c | 163
> +++++++++++++++++++++++++++++++------
>  1 file changed, 136 insertions(+), 27 deletions(-)
>
> diff --git a/modules/audio_output/directsound.c
> b/modules/audio_output/directsound.c
> index 3cca59c..7b83521 100644
> --- a/modules/audio_output/directsound.c
> +++ b/modules/audio_output/directsound.c
> @@ -49,7 +49,7 @@ static HRESULT StreamStart( aout_stream_t *,
> audio_sample_format_t *,
>  static HRESULT StreamStop( aout_stream_t * );
>  static int ReloadDirectXDevices( vlc_object_t *, const char *,
>                                   char ***, char *** );
> -
> +static void * PlayedDataEraser( void * );
>  /* Speaker setup override options list */
>  static const char *const speaker_list[] = { "Windows default",
> "Mono", "Stereo",
>                                              "Quad", "5.1", "7.1" };
> @@ -112,6 +112,11 @@ typedef struct aout_stream_sys
>      vlc_fourcc_t format;
>
>      size_t  i_write;
> +
> +    bool b_playing;
> +    vlc_mutex_t lock;
> +    vlc_cond_t cond;
> +    vlc_thread_t eraser_thread;
>  } aout_stream_sys_t;
>
>  /**
> @@ -173,27 +178,15 @@ static HRESULT FillBuffer( vlc_object_t *obj,
> aout_stream_sys_t *p_sys,
>      size_t towrite = (p_buffer != NULL) ? p_buffer->i_buffer : 
> DS_BUF_SIZE;
>      void *p_write_position, *p_wrap_around;
>      unsigned long l_bytes1, l_bytes2;
> -    DWORD i_read;
> -    size_t i_size;
> -    mtime_t i_buf = towrite;
>      HRESULT dsresult;
>
> -    /* Erase all the data already played */
> -    dsresult = IDirectSoundBuffer_GetCurrentPosition( 
> p_sys->p_dsbuffer,
> -                                                      &i_read, NULL 
> );
> -    if( dsresult == DS_OK )
> -    {
> -        int64_t max = (int64_t)i_read - (int64_t)p_sys->i_write;
> -        if( max <= 0 )
> -            max += DS_BUF_SIZE;
> -        i_buf = max;
> -    }
> +    vlc_mutex_lock( &p_sys->lock );
>
>      /* Before copying anything, we have to lock the buffer */
>      dsresult = IDirectSoundBuffer_Lock(
>             p_sys->p_dsbuffer,    /* DS buffer */
>             p_sys->i_write,       /* Start offset */
> -           i_buf,                /* Number of bytes */
> +           towrite,                /* Number of bytes */
>             &p_write_position,    /* Address of lock start */
>             &l_bytes1,            /* Count of bytes locked before
> wrap around */
>             &p_wrap_around,       /* Buffer address (if wrap around) 
> */
> @@ -205,7 +198,7 @@ static HRESULT FillBuffer( vlc_object_t *obj,
> aout_stream_sys_t *p_sys,
>          dsresult = IDirectSoundBuffer_Lock(
>                                 p_sys->p_dsbuffer,
>                                 p_sys->i_write,
> -                               i_buf,
> +                               towrite,
>                                 &p_write_position,
>                                 &l_bytes1,
>                                 &p_wrap_around,
> @@ -217,6 +210,7 @@ static HRESULT FillBuffer( vlc_object_t *obj,
> aout_stream_sys_t *p_sys,
>          msg_Warn( obj, "cannot lock buffer" );
>          if( p_buffer != NULL )
>              block_Release( p_buffer );
> +        vlc_mutex_unlock( &p_sys->lock );
>          return dsresult;
>      }
>
> @@ -232,18 +226,13 @@ static HRESULT FillBuffer( vlc_object_t *obj,
> aout_stream_sys_t *p_sys,
>                                   p_sys->chans_to_reorder, 
> p_sys->chan_table,
>                                   p_sys->format );
>
> +        memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 );
> +        if( p_wrap_around && l_bytes2 )
> +            memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1,
> l_bytes2 );
>
> -        i_size = ( p_buffer->i_buffer < l_bytes1 ) ?
> p_buffer->i_buffer : l_bytes1;
> -        memcpy( p_write_position, p_buffer->p_buffer, i_size );
> -        memset( (uint8_t*) p_write_position + i_size, 0, l_bytes1 -
> i_size );
> +        if( unlikely( ( l_bytes1 + l_bytes2 ) < p_buffer->i_buffer ) 
> )
> +            msg_Err( obj, "Buffer overrun");
>
> -        if( l_bytes1 < p_buffer->i_buffer)
> -        {   /* Compute the remaining buffer space to be written */
> -            i_buf = i_buf - i_size - (l_bytes1 - i_size);
> -            i_size = ( p_buffer->i_buffer - l_bytes1 < l_bytes2 ) ?
> p_buffer->i_buffer - l_bytes1 : l_bytes2;
> -            memcpy( p_wrap_around, p_buffer->p_buffer + l_bytes1, 
> i_size );
> -            memset( (uint8_t*) p_wrap_around + i_size, 0, i_buf - 
> i_size );
> -        }
>          block_Release( p_buffer );
>      }
>
> @@ -253,6 +242,7 @@ static HRESULT FillBuffer( vlc_object_t *obj,
> aout_stream_sys_t *p_sys,
>
>      p_sys->i_write += towrite;
>      p_sys->i_write %= DS_BUF_SIZE;
> +    vlc_mutex_unlock( &p_sys->lock );
>
>      return DS_OK;
>  }
> @@ -261,7 +251,6 @@ static HRESULT Play( vlc_object_t *obj,
> aout_stream_sys_t *sys,
>                       block_t *p_buffer )
>  {
>      HRESULT dsresult;
> -
>      dsresult = FillBuffer( obj, sys, p_buffer );
>      if( dsresult != DS_OK )
>          return dsresult;
> @@ -277,6 +266,14 @@ static HRESULT Play( vlc_object_t *obj,
> aout_stream_sys_t *sys,
>      }
>      if( dsresult != DS_OK )
>          msg_Err( obj, "cannot start playing buffer" );
> +    else
> +    {
> +        vlc_mutex_lock( &sys->lock );
> +        sys->b_playing = true;
> +        vlc_cond_broadcast(&sys->cond);

NIT: By convention, signal is used when only one thread waits on the 
variable.

> +        vlc_mutex_unlock( &sys->lock );
> +
> +    }
>      return dsresult;
>  }
>
> @@ -298,6 +295,14 @@ static HRESULT Pause( aout_stream_sys_t *sys,
> bool pause )
>          hr = IDirectSoundBuffer_Stop( sys->p_dsbuffer );
>      else
>          hr = IDirectSoundBuffer_Play( sys->p_dsbuffer, 0, 0,
> DSBPLAY_LOOPING );
> +    if( hr == DS_OK )
> +    {
> +        vlc_mutex_lock( &sys->lock );
> +        sys->b_playing = !pause;
> +        if( sys->b_playing )
> +            vlc_cond_broadcast( &sys->cond );
> +        vlc_mutex_unlock( &sys->lock );
> +    }
>      return hr;
>  }
>
> @@ -513,6 +518,12 @@ static HRESULT CreateDSBufferPCM( vlc_object_t
> *obj, aout_stream_sys_t *sys,
>   */
>  static HRESULT Stop( aout_stream_sys_t *p_sys )
>  {
> +    vlc_mutex_lock( &p_sys->lock );
> +    p_sys->b_playing =  true;
> +    vlc_cond_broadcast( &p_sys->cond );

Leaving cancellation enabled in vlc_cond_wait() would be simpler IMHO.

> +    vlc_cancel( p_sys->eraser_thread );
> +    vlc_mutex_unlock( &p_sys->lock );
> +    vlc_join( p_sys->eraser_thread, NULL );
>      if( p_sys->p_notify != NULL )
>      {
>          IDirectSoundNotify_Release(p_sys->p_notify );
> @@ -729,7 +740,27 @@ static HRESULT Start( vlc_object_t *obj,
> aout_stream_sys_t *sys,
>          }
>      }
>
> +    int ret = vlc_clone(&sys->eraser_thread, PlayedDataEraser, 
> (void*) obj,
> +                        VLC_THREAD_PRIORITY_LOW);
> +    if( unlikely( ret ) )
> +    {
> +        if( ret != ENOMEM )
> +            msg_Err( obj, "Couldn't start eraser thread" );
> +        if( sys->p_notify != NULL )
> +        {
> +            IDirectSoundNotify_Release( sys->p_notify );
> +            sys->p_notify = NULL;
> +        }
> +        IDirectSoundBuffer_Release( sys->p_dsbuffer );
> +        sys->p_dsbuffer = NULL;
> +        IDirectSound_Release( sys->p_dsobject );
> +        sys->p_dsobject = NULL;
> +        return ret;
> +    }
> +    sys->b_playing = false;
>      sys->i_write = 0;
> +    vlc_mutex_unlock( &sys->lock );
> +
>      return DS_OK;
>
>  error:
> @@ -1028,6 +1059,9 @@ static int Open(vlc_object_t *obj)
>      aout_DeviceReport(aout, dev);
>      free(dev);
>
> +    vlc_mutex_init(&sys->s.lock);
> +    vlc_cond_init(&sys->s.cond);
> +
>      return VLC_SUCCESS;
>  }
>
> @@ -1035,8 +1069,83 @@ static void Close(vlc_object_t *obj)
>  {
>      audio_output_t *aout = (audio_output_t *)obj;
>      aout_sys_t *sys = aout->sys;
> +    vlc_cond_destroy( &sys->s.cond );
> +    vlc_mutex_destroy( &sys->s.lock );
>
>      var_Destroy(aout, "directx-audio-device");
>      FreeLibrary(sys->hdsound_dll); /* free DSOUND.DLL */
>      free(sys);
>  }
> +
> +static void * PlayedDataEraser( void * data )
> +{
> +    const audio_output_t *aout = (audio_output_t *) data;
> +    aout_stream_sys_t *p_sys = &aout->sys->s;
> +    void *p_write_position, *p_wrap_around;
> +    unsigned long l_bytes1, l_bytes2;
> +    DWORD i_read;
> +    int64_t toerase, tosleep;
> +    HRESULT dsresult;
> +
> +    for(;;)
> +    {
> +        int canc = vlc_savecancel();
> +        vlc_mutex_lock( &p_sys->lock );
> +
> +        while( !p_sys->b_playing )
> +           vlc_cond_wait( &p_sys->cond, &p_sys->lock );
> +
> +        toerase = 0;
> +        tosleep = 0;
> +
> +        dsresult = IDirectSoundBuffer_GetCurrentPosition( 
> p_sys->p_dsbuffer,
> +                                                          &i_read, 
> NULL );
> +        if( dsresult == DS_OK )
> +        {
> +            int64_t max = (int64_t) i_read - (int64_t) 
> p_sys->i_write;
> +            tosleep = -max;
> +            if( max <= 0 )
> +                max += DS_BUF_SIZE;
> +            else
> +                tosleep += DS_BUF_SIZE;
> +            toerase = max;
> +            tosleep = ( tosleep / p_sys->i_bytes_per_sample ) *
> CLOCK_FREQ / p_sys->i_rate;
> +        }
> +
> +        tosleep = __MAX( tosleep, 20000 );
> +        dsresult = IDirectSoundBuffer_Lock( p_sys->p_dsbuffer,
> +                                            p_sys->i_write,
> +                                            toerase,
> +                                            &p_write_position,
> +                                            &l_bytes1,
> +                                            &p_wrap_around,
> +                                            &l_bytes2,
> +                                            0 );
> +        if( dsresult == DSERR_BUFFERLOST )
> +        {
> +            IDirectSoundBuffer_Restore( p_sys->p_dsbuffer );
> +            dsresult = IDirectSoundBuffer_Lock( p_sys->p_dsbuffer,
> +                                                p_sys->i_write,
> +                                                toerase,
> +                                                &p_write_position,
> +                                                &l_bytes1,
> +                                                &p_wrap_around,
> +                                                &l_bytes2,
> +                                                0 );
> +        }
> +        if( dsresult != DS_OK )
> +            goto wait;
> +
> +        memset( p_write_position, 0, l_bytes1 );
> +        memset( p_wrap_around, 0, l_bytes2 );
> +
> +        IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer,
> p_write_position, l_bytes1,
> +                                   p_wrap_around, l_bytes2 );
> +wait:
> +        vlc_mutex_unlock(&p_sys->lock);
> +        vlc_restorecancel(canc);
> +        msleep(tosleep);
> +    }
> +    vlc_mutex_unlock(&p_sys->lock);

Dead code?

> +    return NULL;
> +}

-- 
Rémi Denis-Courmont



More information about the vlc-devel mailing list