[MPlayer-dev-eng] [PATCH] Automatic downmix
Clément Bœsch
ubitux at gmail.com
Thu Sep 23 22:07:52 CEST 2010
On Thu, Sep 23, 2010 at 07:34:57PM +0200, Nicolas George wrote:
> Le duodi 2 vendémiaire, an CCXIX, Clément Bœsch a écrit :
> > Normal downmix case (Flac 5.1, no options):
> >
> > Before downmix, filter list is:
> > [dummy]
> > After downmix + reinit, filter list is:
> > [format] → [pan] → [dummy]
> >
> > And with -channels 2 -af pan=6:...:
> >
> > Before downmix, filter list is:
> > [format] → [pan (upmix 6 chan)]
> > After downmix + reinit, filter list is:
> > [format] → [pan (downmix 2 chan)] → [pan (upmix 6 chan)]
> >
> > Is this what you would like? Or as I said, forced before the format filter?
In fact, the prepend version issues a problem: since the pan2 downmix is
placed before the pan6 filter, in the second run, it still observes a 6
channels output at the end of the chain, and then adds again the downmix
filter. So we get something like that:
First af_init: [format] → [pan2] → [pan6]
Second af_init: [format] → [pan2] → [pan6] → [pan2]
So I introduce a first_run variable in order to avoid that. Then, here are
the new results I observed with the attached patch.
> That looks right. Unless I am mistaken, the format filter is automatically
> added and necessary because the codec outputs shorts and pan only accepts
> floats¹.
>
> Could you just do two more checks please:
>
> - The above cases with a codec that handles downmixing natively, AC3 for
> example. I expect to see something like that:
>
> Normal case: [dummy]
>
Input #0, ac3, from '6_Channel_ID.ac3':
Duration: 00:00:05.85, bitrate: 191 kb/s
Stream #0.0: Audio: ac3, 44100 Hz, 5.1, s16, 192 kb/s
* Test case 6_Channel_ID.ac3
[AF_INIT first_run=1]
Before downmix check: [dummy (2ch)] → End
After downmix check: [dummy (2ch)] → End
AO: [alsa] 48000Hz 2ch s16le (2 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [dummy (2ch)] → End
After downmix check: [dummy (2ch)] → End
> -channels 2 -af pan=6: [format] -> [pan 6] -> [dummy]
>
* Test case 6_Channel_ID.ac3 -channels 2 -af pan=6:
[AF_INIT first_run=1]
Before downmix check: [format (2ch)] → [pan (6ch)] → End
After downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
AO: [alsa] 44100Hz 6ch floatle (4 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
After downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
> - The above cases with a AO driver that does not handle float, oss for
> example. I expect something like that:
>
> [format] -> [pan] -> [format]
> [format] -> [pan 2] -> [pan 6] -> [format]
>
* Test case 6_Channel_ID.ac3 -channels 2 -af pan=6: -ao oss
[AF_INIT first_run=1]
Before downmix check: [format (2ch)] → [pan (6ch)] → End
After downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
AO: [oss] 44100Hz 6ch s16le (2 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
After downmix check: [format (2ch)] → [pan (2ch)] → [pan (6ch)] → End
> (not sure where the dummy would get exactly)
>
> If this works satisfactorily, I think I can take on me to apply it at last.
> Is this the version in your mail dated "Thu, 16 Sep 2010 21:31:39 +0200"?
>
I attached the prepend version of the patch in this email.
As the patch has changed since last time, here again the same tests with
Flac sample:
* Test case 6_Channel_ID.flac
[AF_INIT first_run=1]
Before downmix check: [dummy (6ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [dummy (2ch)] → End
AO: [alsa] 48000Hz 2ch floatle (4 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [format (6ch)] → [pan (2ch)] → [dummy (2ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [dummy (2ch)] → End
* Test case 6_Channel_ID.flac -channels 2 -af pan=6:
[AF_INIT first_run=1]
Before downmix check: [format (6ch)] → [pan (6ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
AO: [alsa] 44100Hz 6ch floatle (4 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
* Test case 6_Channel_ID.flac -channels 2 -af pan=6: -ao oss
[AF_INIT first_run=1]
Before downmix check: [format (6ch)] → [pan (6ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
AO: [oss] 44100Hz 6ch s16le (2 bytes per sample)
[AF_INIT first_run=0]
Before downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
After downmix check: [format (6ch)] → [pan (2ch)] → [pan (6ch)] → End
I hope it's fine like this!
Regards,
--
Clément B.
-------------- next part --------------
Index: libaf/af.c
===================================================================
--- libaf/af.c (revision 32345)
+++ libaf/af.c (working copy)
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include "osdep/strsep.h"
+#include "libmpcodecs/dec_audio.h"
#include "af.h"
@@ -412,6 +413,24 @@
return AF_OK;
}
+/**
+ * Automatic downmix to stereo in case the codec does not implement it.
+ */
+static af_instance_t *af_downmix(af_stream_t* s)
+{
+ static char * const downmix_strs[AF_NCH + 1] = {
+ /* FL FR RL RR FC LF AL AR */
+ [3] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0.4",
+ [4] = "pan=2:" "0.6:0:" "0:0.6:" "0.4:0:" "0:0.4",
+ [5] = "pan=2:" "0.5:0:" "0:0.5:" "0.2:0:" "0:0.2:" "0.3:0.3",
+ [6] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0.1",
+ [7] = "pan=2:" "0.4:0:" "0:0.4:" "0.2:0:" "0:0.2:" "0.3:0.3:" "0.1:0:" "0:0.1",
+ [8] = "pan=2:" "0.4:0:" "0:0.4:" "0.15:0:" "0:0.15:" "0.25:0.25:" "0.1:0.1:" "0.1:0:" "0:0.1",
+ };
+ char *af_pan_str = downmix_strs[s->last->data->nch];
+ return af_pan_str ? af_prepend(s, s->first, af_pan_str) : NULL;
+}
+
/* Initialize the stream "s". This function creates a new filter list
if necessary according to the values set in input and output. Input
and output should contain the format of the current movie and the
@@ -423,7 +442,7 @@
The return value is 0 if success and -1 if failure */
int af_init(af_stream_t* s)
{
- int i=0;
+ int i=0, first_run;
// Sanity check
if(!s) return -1;
@@ -437,7 +456,8 @@
s->cfg.force = (s->cfg.force & ~AF_INIT_TYPE_MASK) | AF_INIT_TYPE;
// Check if this is the first call
- if(!s->first){
+ first_run = !s->first;
+ if(first_run){
// Add all filters in the list (if there are any)
if(!s->cfg.list){ // To make automatic format conversion work
if(!af_append(s,s->first,"dummy"))
@@ -460,6 +480,13 @@
if (!af_append(s,s->first,"dummy") || AF_OK != af_reinit(s,s->first))
return -1;
+ // Append a downmix pan filter to the end of the chain if needed
+ if (first_run && audio_output_channels == 2) {
+ af_instance_t *filter = af_downmix(s);
+ if (filter && AF_OK != af_reinit(s, filter))
+ return -1;
+ }
+
// Check output format
if((AF_INIT_TYPE_MASK & s->cfg.force) != AF_INIT_FORCE){
af_instance_t* af = NULL; // New filter
More information about the MPlayer-dev-eng
mailing list