[vlc-commits] aout: rewrite and robustify conversion pipelines

Rémi Denis-Courmont git at videolan.org
Thu Nov 15 21:42:22 CET 2012


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Thu Nov 15 22:37:36 2012 +0200| [3a2ba96b99e5ea08212759e5c5845f6054584f03] | committer: Rémi Denis-Courmont

aout: rewrite and robustify conversion pipelines

This should fix problems when remixing is required but the FL32
format is not involved, as well as decoding on non-FPU platforms.

This also disables (or rather avoids) remixing in A52 and DTS decoders.
I do not really see the point in using the A52 downmixer anyway.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=3a2ba96b99e5ea08212759e5c5845f6054584f03
---

 src/audio_output/filters.c |  220 +++++++++++++++++++++++++++-----------------
 1 file changed, 137 insertions(+), 83 deletions(-)

diff --git a/src/audio_output/filters.c b/src/audio_output/filters.c
index 14f9e66..bd4dbbb 100644
--- a/src/audio_output/filters.c
+++ b/src/audio_output/filters.c
@@ -83,50 +83,6 @@ static filter_t * FindFilter( vlc_object_t *obj,
 }
 
 /**
- * Splits audio format conversion in two simpler conversions
- * @return 0 on successful split, -1 if the input and output formats are too
- * similar to split the conversion.
- */
-static int SplitConversion( const audio_sample_format_t *restrict infmt,
-                            const audio_sample_format_t *restrict outfmt,
-                            audio_sample_format_t *midfmt )
-{
-    *midfmt = *outfmt;
-
-    /* Lastly: resample (after format conversion and remixing) */
-    if( infmt->i_rate != outfmt->i_rate )
-        midfmt->i_rate = infmt->i_rate;
-    else
-    /* Penultimately: remix channels (after format conversion) */
-    if( infmt->i_physical_channels != outfmt->i_physical_channels
-     || infmt->i_original_channels != outfmt->i_original_channels )
-    {
-        midfmt->i_physical_channels = infmt->i_physical_channels;
-        midfmt->i_original_channels = infmt->i_original_channels;
-    }
-    else
-    /* Second: convert linear to S16N as intermediate format */
-    if( AOUT_FMT_LINEAR( infmt ) )
-    {
-        /* All conversion from linear to S16N must be supported directly. */
-        if( outfmt->i_format == VLC_CODEC_S16N )
-            return -1;
-        midfmt->i_format = VLC_CODEC_S16N;
-    }
-    else
-    /* First: convert non-linear to FI32 as intermediate format */
-    {
-        if( outfmt->i_format == VLC_CODEC_FI32 )
-            return -1;
-        midfmt->i_format = VLC_CODEC_FI32;
-    }
-
-    assert( !AOUT_FMTS_IDENTICAL( infmt, midfmt ) );
-    aout_FormatPrepare( midfmt );
-    return 0;
-}
-
-/**
  * Destroys a chain of audio filters.
  */
 static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
@@ -140,77 +96,175 @@ static void aout_FiltersPipelineDestroy(filter_t *const *filters, unsigned n)
     }
 }
 
+static filter_t *TryFormat (vlc_object_t *obj, vlc_fourcc_t codec,
+                            audio_sample_format_t *restrict fmt)
+{
+    audio_sample_format_t output = *fmt;
+
+    assert (codec != fmt->i_format);
+    output.i_format = codec;
+    aout_FormatPrepare (&output);
+
+    filter_t *filter = FindFilter (obj, fmt, &output);
+    if (filter != NULL)
+        *fmt = output;
+    return filter;
+}
+
 /**
  * Allocates audio format conversion filters
  * @param obj parent VLC object for new filters
  * @param filters table of filters [IN/OUT]
- * @param nb_filters pointer to the number of filters in the table [IN/OUT]
- * @param max_filters size of filters table [IN]
+ * @param count pointer to the number of filters in the table [IN/OUT]
+ * @param max size of filters table [IN]
  * @param infmt input audio format
  * @param outfmt output audio format
  * @return 0 on success, -1 on failure
  */
 static int aout_FiltersPipelineCreate(vlc_object_t *obj, filter_t **filters,
-                                 unsigned *nb_filters, unsigned max_filters,
+                                      unsigned *count, unsigned max,
                                  const audio_sample_format_t *restrict infmt,
                                  const audio_sample_format_t *restrict outfmt)
 {
-    audio_sample_format_t curfmt = *outfmt;
-    unsigned i = 0;
-
-    max_filters -= *nb_filters;
-    filters += *nb_filters;
-    aout_FormatsPrint( obj, "filter(s)", infmt, outfmt );
+    aout_FormatsPrint (obj, "conversion:", infmt, outfmt);
+    max -= *count;
+    filters += *count;
+
+    /* There is a lot of second guessing on what the conversion plugins can
+     * and cannot do. This seems hardly avoidable, the conversion problem need
+     * to be reduced somehow. */
+    audio_sample_format_t input = *infmt;
+    bool same_codec = infmt->i_format == outfmt->i_format;
+    bool same_rate = infmt->i_rate == outfmt->i_rate;
+    bool same_mix = infmt->i_physical_channels == outfmt->i_physical_channels
+                 && infmt->i_original_channels == outfmt->i_original_channels;
+    unsigned n = 0;
 
-    while( !AOUT_FMTS_IDENTICAL( infmt, &curfmt ) )
+    /* Encapsulate or decode non-linear formats */
+    if (!AOUT_FMT_LINEAR(infmt) && !same_codec)
     {
-        if( i >= max_filters )
+        if (n == max)
+            goto overflow;
+
+        filter_t *f = NULL;
+        if (!AOUT_FMT_LINEAR(outfmt))
+           f = TryFormat (obj, outfmt->i_format, &input);
+        if (f == NULL)
+            f = TryFormat (obj, VLC_CODEC_FI32, &input);
+        if (f == NULL)
+            f = TryFormat (obj, VLC_CODEC_FL32, &input);
+        if (f == NULL)
         {
-            msg_Err( obj, "maximum of %u filters reached", max_filters );
-            dialog_Fatal( obj, _("Audio filtering failed"),
-                          _("The maximum number of filters (%u) was reached."),
-                          max_filters );
-            goto rollback;
+            msg_Err (obj, "cannot find %s for conversion pipeline",
+                     "decoder");
+            goto error;
         }
 
-        /* Make room and prepend a filter */
-        memmove( filters + 1, filters, i * sizeof( *filters ) );
+        filters[n++] = f;
+        same_codec = input.i_format == outfmt->i_format;
+    }
+
+    assert (AOUT_FMT_LINEAR(&input));
+
+    /* Conversion cannot be done in foreign endianess. */
+    /* TODO: convert to native endian if needed */
 
-        *filters = FindFilter( obj, infmt, &curfmt );
-        if( *filters != NULL )
+    /* Remix channels */
+    if (!same_mix)
+    {   /* Remixing currently requires FL32... TODO: S16N */
+        if (input.i_format != VLC_CODEC_FL32)
         {
-            i++;
-            break; /* done! */
+            if (n == max)
+                goto overflow;
+
+            filter_t *f = TryFormat (obj, VLC_CODEC_FL32, &input);
+            if (f == NULL)
+            {
+                msg_Err (obj, "cannot find %s for conversion pipeline",
+                         "pre-mix converter");
+                goto error;
+            }
+
+            filters[n++] = f;
+            same_codec = input.i_format == outfmt->i_format;
         }
 
-        audio_sample_format_t midfmt;
-        /* Split the conversion */
-        if( SplitConversion( infmt, &curfmt, &midfmt ) )
+        if (n == max)
+            goto overflow;
+
+        audio_sample_format_t output;
+        output.i_format = input.i_format;
+        output.i_rate = input.i_rate;
+        output.i_physical_channels = outfmt->i_physical_channels;
+        output.i_original_channels = outfmt->i_original_channels;
+        aout_FormatPrepare (&output);
+
+        filter_t *f = FindFilter (obj, &input, &output);
+        if (f == NULL)
         {
-            msg_Err( obj, "conversion pipeline failed: %4.4s -> %4.4s",
-                     (const char *)&infmt->i_format,
-                     (const char *)&outfmt->i_format );
-            goto rollback;
+            msg_Err (obj, "cannot find %s for conversion pipeline",
+                     "remixer");
+            goto error;
         }
 
-        *filters = FindFilter( obj, &midfmt, &curfmt );
-        if( *filters == NULL )
+        input = output;
+        filters[n++] = f;
+        //same_mix = true;
+    }
+
+    /* Resample */
+    if (!same_rate)
+    {   /* Resampling works with any linear format, but may be ugly. */
+        if (n == max)
+            goto overflow;
+
+        audio_sample_format_t output = input;
+        output.i_rate = outfmt->i_rate;
+
+        filter_t *f = FindFilter (obj, &input, &output);
+        if (f == NULL)
         {
-            msg_Err( obj, "cannot find filter for simple conversion" );
-            goto rollback;
+            msg_Err (obj, "cannot find %s for conversion pipeline",
+                     "resampler");
+            goto error;
         }
-        curfmt = midfmt;
-        i++;
+
+        input = output;
+        filters[n++] = f;
+        //same_rate = true;
+    }
+
+    if (!same_codec)
+    {
+        if (max == 0)
+            goto overflow;
+
+        filter_t *f = TryFormat (obj, outfmt->i_format, &input);
+        if (f == NULL)
+        {
+            msg_Err (obj, "cannot find %s for conversion pipeline",
+                     "post-mix converter");
+            goto error;
+        }
+        filters[n++] = f;
+        //same_codec = true;
     }
 
-    msg_Dbg( obj, "conversion pipeline completed" );
-    *nb_filters += i;
+    /* TODO: convert to foreign endian if needed */
+
+    msg_Dbg (obj, "conversion pipeline complete");
+    *count += n;
     return 0;
 
-rollback:
-    aout_FiltersPipelineDestroy (filters, i);
+overflow:
+    msg_Err (obj, "maximum of %u conversion filters reached", max);
+    dialog_Fatal (obj, _("Audio filtering failed"),
+                  _("The maximum number of filters (%u) was reached."), max);
+error:
+    aout_FiltersPipelineDestroy (filters, n);
     return -1;
 }
+
 #define aout_FiltersPipelineCreate(obj,f,n,m,i,o) \
         aout_FiltersPipelineCreate(VLC_OBJECT(obj),f,n,m,i,o)
 



More information about the vlc-commits mailing list