[vlc-devel] [RFC PATCH 5/6] decoder: add audio decoder fallback

Thomas Guillem thomas at gllm.fr
Fri Jul 1 18:14:54 CEST 2016


An audio decoder module will be able to request that an audio passthrough
feature.  If such module fails in aout_update_format() (the audio_output can't
do SPDIF), the DecoderThread will try a new decoder module and will refuse any
module that need audio passthrough.

The audio device change callback is also listened in order to restart decoders
when an audio device change.
---
 include/vlc_codec.h |  9 ++++++
 src/input/decoder.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/libvlccore.sym  |  1 +
 3 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/include/vlc_codec.h b/include/vlc_codec.h
index 132d935..d250281 100644
--- a/include/vlc_codec.h
+++ b/include/vlc_codec.h
@@ -366,6 +366,15 @@ static inline int decoder_UpdateAudioFormat( decoder_t *dec )
         return -1;
 }
 
+/**
+ * This function asks to the decoder if it's possible for the module to do
+ * audio passthrough
+ * @param b_request true to request, false to cancel a previous successfull
+ * request
+ * @return true if the module can do audio passthrough
+ */
+VLC_API bool decoder_RequestAudioPassthrough( decoder_t *dec, bool b_request );
+
 static inline void decoder_AddState( decoder_t *dec, int state )
 {
     atomic_fetch_or( &dec->state, state );
diff --git a/src/input/decoder.c b/src/input/decoder.c
index 5cf2b63..20de6e5 100644
--- a/src/input/decoder.c
+++ b/src/input/decoder.c
@@ -54,6 +54,14 @@
 
 #include "../video_output/vout_control.h"
 
+enum audio_passthrough
+{
+    AUDIO_PASSTHROUGH_DISABLED,
+    AUDIO_PASSTHROUGH_AVAILABLE,
+    AUDIO_PASSTHROUGH_ENABLED,
+    AUDIO_PASSTHROUGH_FAILED,
+};
+
 struct decoder_owner_sys_t
 {
     input_thread_t  *p_input;
@@ -80,6 +88,8 @@ struct decoder_owner_sys_t
     /* */
     bool           b_fmt_description;
     vlc_meta_t     *p_description;
+    atomic_bool    aout_device_changed;
+    enum audio_passthrough audio_passthrough;
 
     /* fifo */
     block_fifo_t *p_fifo;
@@ -230,6 +240,18 @@ static vout_thread_t *aout_request_vout( void *p_private,
     return p_vout;
 }
 
+
+static int aout_device_changed( vlc_object_t *p_obj, const char *p_var,
+                                vlc_value_t prev, vlc_value_t cur, void *p_data)
+{
+    (void) p_obj; (void) p_var; (void) prev; (void) cur;
+    decoder_t *p_dec = p_data;
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+    atomic_store( &p_owner->aout_device_changed, true );
+
+    return VLC_SUCCESS;
+}
+
 static int aout_update_format( decoder_t *p_dec )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
@@ -243,6 +265,7 @@ static int aout_update_format( decoder_t *p_dec )
         vlc_mutex_lock( &p_owner->lock );
         p_owner->p_aout = NULL;
         vlc_mutex_unlock( &p_owner->lock );
+        var_DelCallback( p_aout, "device", aout_device_changed, p_dec );
         aout_DecDelete( p_aout );
 
         input_resource_PutAout( p_owner->p_resource, p_aout );
@@ -288,6 +311,8 @@ static int aout_update_format( decoder_t *p_dec )
                 input_resource_PutAout( p_owner->p_resource, p_aout );
                 p_aout = NULL;
             }
+            else
+                var_AddCallback( p_aout, "device", aout_device_changed, p_dec );
         }
 
         vlc_mutex_lock( &p_owner->lock );
@@ -560,6 +585,26 @@ block_t *decoder_NewAudioBuffer( decoder_t *dec, int samples )
     return block;
 }
 
+bool decoder_RequestAudioPassthrough( decoder_t *p_dec, bool b_request )
+{
+    decoder_owner_sys_t *p_owner = p_dec->p_owner;
+
+    if( b_request )
+    {
+        if( p_owner->audio_passthrough == AUDIO_PASSTHROUGH_AVAILABLE )
+        {
+            p_owner->audio_passthrough = AUDIO_PASSTHROUGH_ENABLED;
+            return true;
+        }
+    }
+    else
+    {
+        assert( p_owner->audio_passthrough == AUDIO_PASSTHROUGH_ENABLED );
+        p_owner->audio_passthrough = AUDIO_PASSTHROUGH_AVAILABLE;
+    }
+    return false;
+}
+
 subpicture_t *decoder_NewSubpicture( decoder_t *p_decoder,
                                      const subpicture_updater_t *p_dyn )
 {
@@ -1328,8 +1373,38 @@ static void DecoderProcess( decoder_t *p_dec, block_t *p_block )
 {
     decoder_owner_sys_t *p_owner = p_dec->p_owner;
 
-    if( atomic_load( &p_dec->state ) & DECODER_STATE_ERROR )
-        goto error;
+    bool b_audio_passthrough_failed = false;
+    int i_error = atomic_load( &p_dec->state ) & DECODER_STATE_ERROR;
+    if( i_error != 0 )
+    {
+        if( p_owner->audio_passthrough == AUDIO_PASSTHROUGH_ENABLED
+         && i_error == DECODER_STATE_ERROR_OUTPUT )
+            b_audio_passthrough_failed = true;
+        else
+            goto error;
+    }
+
+    if( p_owner->audio_passthrough != AUDIO_PASSTHROUGH_DISABLED
+     && ( atomic_exchange( &p_owner->aout_device_changed, false )
+       || b_audio_passthrough_failed ) )
+    {
+        msg_Warn( p_dec, "Restarting decoder due since %s",
+                  b_audio_passthrough_failed ?
+                  "there is no available SPDIF output" :
+                  "the audio device changed" );
+
+        UnloadDecoder( p_dec, false );
+
+        /* If passthrough previously failed, we don't want to retry, except if
+         * the device changed */
+        p_owner->audio_passthrough = b_audio_passthrough_failed ?
+                                     AUDIO_PASSTHROUGH_FAILED :
+                                     AUDIO_PASSTHROUGH_AVAILABLE;
+
+        if( LoadDecoder( p_dec, false, NULL ) )
+            goto error;
+        atomic_fetch_and( &p_dec->state, ~DECODER_STATE_ERROR_OUTPUT );
+    }
 
     if( p_block )
     {
@@ -1580,6 +1655,10 @@ static decoder_t * CreateDecoder( vlc_object_t *p_parent,
     p_owner->b_draining = false;
     atomic_init( &p_owner->drained, false );
     p_owner->b_idle = false;
+    p_owner->audio_passthrough =
+        fmt->i_cat == AUDIO_ES && var_InheritBool( p_dec, "spdif" ) ?
+        AUDIO_PASSTHROUGH_AVAILABLE : AUDIO_PASSTHROUGH_DISABLED;
+    atomic_init( &p_owner->aout_device_changed, false );
 
     es_format_Init( &p_owner->fmt, UNKNOWN_ES, 0 );
 
@@ -1698,6 +1777,7 @@ static void DeleteDecoder( decoder_t * p_dec )
     if( p_owner->p_aout )
     {
         /* TODO: REVISIT gap-less audio */
+        var_DelCallback( p_owner->p_aout, "device", aout_device_changed, p_dec );
         aout_DecFlush( p_owner->p_aout, false );
         aout_DecDelete( p_owner->p_aout );
         input_resource_PutAout( p_owner->p_resource, p_owner->p_aout );
diff --git a/src/libvlccore.sym b/src/libvlccore.sym
index 89e74f8..2a1f286 100644
--- a/src/libvlccore.sym
+++ b/src/libvlccore.sym
@@ -82,6 +82,7 @@ decoder_GetDisplayRate
 decoder_GetInputAttachments
 decoder_NewAudioBuffer
 decoder_NewSubpicture
+decoder_RequestAudioPassthrough
 decoder_SynchroChoose
 decoder_SynchroDate
 decoder_SynchroDecode
-- 
2.8.1



More information about the vlc-devel mailing list