[vlc-devel] [PATCH] avcodec: Re-prepare on audio sample format change (DTS-HD)

Jack Andersen jackoalan at gmail.com
Fri Oct 18 09:35:51 CEST 2019


I have encountered an edge case in the avcodec module while playing DTS-HD
tracks on a BDMV title (specifically Star Trek: TNG during the DTS trailer).

Decoded DTS blocks may alternate between HD and non-HD formats and VLC
currently does not fully handle this transition. DTS-HD blocks are decoded to
a 32-bit-per-sample format by libavcodec, and non-HD blocks are decoded to a
16-bit format. When a 32->16 transition occurs, the interleaving buffers are
correctly shrunk, but the i_bitspersample field remains at 32-bits. This
ultimately causes a segfault when half of the interleaving buffer has been
processed.

VLC already responds to changes in the channel layout. This patch
additionally detects for changes in the sample format.
---
 modules/codec/avcodec/audio.c | 110 ++++++++++++++++++----------------
 1 file changed, 60 insertions(+), 50 deletions(-)

diff --git a/modules/codec/avcodec/audio.c b/modules/codec/avcodec/audio.c
index 27d4e511f6..32ef88e754 100644
--- a/modules/codec/avcodec/audio.c
+++ b/modules/codec/avcodec/audio.c
@@ -65,6 +65,7 @@ typedef struct
     int     pi_extraction[AOUT_CHAN_MAX];
     int     i_previous_channels;
     uint64_t i_previous_layout;
+    vlc_fourcc_t i_previous_codec;
 } decoder_sys_t;
 
 #define BLOCK_FLAG_PRIVATE_REALLOCATED (1 << BLOCK_FLAG_PRIVATE_SHIFT)
@@ -250,6 +251,7 @@ int InitAudioDec( vlc_object_t *obj )
     p_sys->b_extract = false;
     p_sys->i_previous_channels = 0;
     p_sys->i_previous_layout = 0;
+    p_sys->i_previous_codec = 0;
 
     /* */
     /* Try to set as much information as possible but do not trust it */
@@ -587,67 +589,75 @@ static void SetupOutputFormat( decoder_t *p_dec, bool b_trust )
     p_dec->fmt_out.audio.i_rate = p_sys->p_context->sample_rate;
 
     /* */
-    if( p_sys->i_previous_channels == p_sys->p_context->channels &&
-        p_sys->i_previous_layout == p_sys->p_context->channel_layout )
-        return;
-    if( b_trust )
+    bool b_channel_layout_changed =
+        p_sys->i_previous_channels != p_sys->p_context->channels ||
+        p_sys->i_previous_layout != p_sys->p_context->channel_layout;
+    if( b_channel_layout_changed )
     {
-        p_sys->i_previous_channels = p_sys->p_context->channels;
-        p_sys->i_previous_layout = p_sys->p_context->channel_layout;
-    }
+        if( b_trust )
+        {
+            p_sys->i_previous_channels = p_sys->p_context->channels;
+            p_sys->i_previous_layout = p_sys->p_context->channel_layout;
+        }
 
-    const unsigned i_order_max = sizeof(pi_channels_map)/sizeof(*pi_channels_map);
-    uint32_t pi_order_src[i_order_max];
+        const unsigned i_order_max = sizeof(pi_channels_map)/sizeof(*pi_channels_map);
+        uint32_t pi_order_src[i_order_max];
 
-    int i_channels_src = 0;
-    uint64_t channel_layout =
-        p_sys->p_context->channel_layout ? p_sys->p_context->channel_layout :
-        (uint64_t)av_get_default_channel_layout( p_sys->p_context->channels );
+        int i_channels_src = 0;
+        uint64_t channel_layout =
+            p_sys->p_context->channel_layout ? p_sys->p_context->channel_layout :
+            (uint64_t)av_get_default_channel_layout( p_sys->p_context->channels );
 
-    if( channel_layout )
-    {
-        for( unsigned i = 0; i < i_order_max
-         && i_channels_src < p_sys->p_context->channels; i++ )
+        if( channel_layout )
         {
-            if( channel_layout & pi_channels_map[i][0] )
-                pi_order_src[i_channels_src++] = pi_channels_map[i][1];
-        }
+            for( unsigned i = 0; i < i_order_max
+             && i_channels_src < p_sys->p_context->channels; i++ )
+            {
+                if( channel_layout & pi_channels_map[i][0] )
+                    pi_order_src[i_channels_src++] = pi_channels_map[i][1];
+            }
+
+            if( i_channels_src != p_sys->p_context->channels && b_trust )
+                msg_Err( p_dec, "Channel layout not understood" );
 
-        if( i_channels_src != p_sys->p_context->channels && b_trust )
-            msg_Err( p_dec, "Channel layout not understood" );
+            /* Detect special dual mono case */
+            if( i_channels_src == 2 && pi_order_src[0] == AOUT_CHAN_CENTER
+             && pi_order_src[1] == AOUT_CHAN_CENTER )
+            {
+                p_dec->fmt_out.audio.i_chan_mode |= AOUT_CHANMODE_DUALMONO;
+                pi_order_src[0] = AOUT_CHAN_LEFT;
+                pi_order_src[1] = AOUT_CHAN_RIGHT;
+            }
 
-        /* Detect special dual mono case */
-        if( i_channels_src == 2 && pi_order_src[0] == AOUT_CHAN_CENTER
-         && pi_order_src[1] == AOUT_CHAN_CENTER )
+            uint32_t i_layout_dst;
+            int      i_channels_dst;
+            p_sys->b_extract = aout_CheckChannelExtraction( p_sys->pi_extraction,
+                                                            &i_layout_dst, &i_channels_dst,
+                                                            NULL, pi_order_src, i_channels_src );
+            if( i_channels_dst != i_channels_src && b_trust )
+                msg_Warn( p_dec, "%d channels are dropped", i_channels_src - i_channels_dst );
+
+            /* No reordering for Ambisonic order 1 channels encoded in AAC... */
+            if (p_dec->fmt_out.audio.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS
+                && p_dec->fmt_in.i_codec == VLC_CODEC_MP4A
+                && i_channels_src == 4)
+                p_sys->b_extract = false;
+
+            p_dec->fmt_out.audio.i_physical_channels = i_layout_dst;
+        }
+        else
         {
-            p_dec->fmt_out.audio.i_chan_mode |= AOUT_CHANMODE_DUALMONO;
-            pi_order_src[0] = AOUT_CHAN_LEFT;
-            pi_order_src[1] = AOUT_CHAN_RIGHT;
+            msg_Warn( p_dec, "no channel layout found");
+            p_dec->fmt_out.audio.i_physical_channels = 0;
+            p_dec->fmt_out.audio.i_channels = p_sys->p_context->channels;
         }
-
-        uint32_t i_layout_dst;
-        int      i_channels_dst;
-        p_sys->b_extract = aout_CheckChannelExtraction( p_sys->pi_extraction,
-                                                        &i_layout_dst, &i_channels_dst,
-                                                        NULL, pi_order_src, i_channels_src );
-        if( i_channels_dst != i_channels_src && b_trust )
-            msg_Warn( p_dec, "%d channels are dropped", i_channels_src - i_channels_dst );
-
-        /* No reordering for Ambisonic order 1 channels encoded in AAC... */
-        if (p_dec->fmt_out.audio.channel_type == AUDIO_CHANNEL_TYPE_AMBISONICS
-            && p_dec->fmt_in.i_codec == VLC_CODEC_MP4A
-            && i_channels_src == 4)
-            p_sys->b_extract = false;
-
-        p_dec->fmt_out.audio.i_physical_channels = i_layout_dst;
     }
-    else
+
+    if( b_channel_layout_changed || p_sys->i_previous_codec != p_dec->fmt_out.i_codec )
     {
-        msg_Warn( p_dec, "no channel layout found");
-        p_dec->fmt_out.audio.i_physical_channels = 0;
-        p_dec->fmt_out.audio.i_channels = p_sys->p_context->channels;
+        if( b_trust )
+            p_sys->i_previous_codec = p_dec->fmt_out.i_codec;
+        aout_FormatPrepare( &p_dec->fmt_out.audio );
     }
-
-    aout_FormatPrepare( &p_dec->fmt_out.audio );
 }
 
-- 
2.23.0



More information about the vlc-devel mailing list