Re: ff_audio_multichannel_support

Torsten Jager <[email protected]> Tue, 21 May 2013 20:02:37 +0200
Newsgroups gmane.comp.video.xine.devel
Message-ID <[email protected]>
Hello,

> i test your new patch and i found a bug:
>
> when movie is eac3 5.1 and switching to 2.0 for advertising ,xine crash.
>
> i upload a new sample for this bug.
>
>
> http://dl.free.fr/rHbXvFhHa
>
> or here
>
> http://speedy.sh/5uWY8/expert.zip

The second site worked fine with those terminals, thanks.

Oh dear.

I thought I could do this step by step but it seems not to work that
way. So here is a shitload of changes:

* Observe channel configuration immediately after av_decode_audio* ().
    Do not try to access nonexistant channels after a 5.1 -> 2.0 switch
    for example.

* Add NULL plane pointer paranoia.

* Assume generic channel layout when no detailled one provided.
    Needed for wma2.

* Follow user speaker arrangement changes on the fly.

* Defer opening audio out until we have something to play.

* Do not reopen audio out with identical settings. This and the
    previous item should help avoiding waiting on some drivers.

* Hard wire output to int16_t. Some of the code did assume that,
    and we are converting to that anyway.

* Do not read sample format from bits_per_coded_sample. Decoders
    neither alter that field, nor do they force its value to their
    output.

* Rename some vars for better readability.

[ff_audio_channel_mixer_5.diff]
ff_audio_dec: fix multichannel playback


Torsten

------------------------------------------------------------------------------
Try New Relic Now & We'll Send You this Cool Shirt
New Relic is the only SaaS-based application performance monitoring service 
that delivers powerful full stack analytics. Optimize and monitor your
browser, app, & servers with just a few lines of code. Try New Relic
and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may

_______________________________________________
xine-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xine-devel
ff_audio_channel_mixer_5.diff (text/x-patch, 30.2 KB)
--- xine-lib-1.2-20130415/src/combined/ffmpeg/ff_audio_decoder.c	2013-04-15 22:10:34.000000000 +0200
+++ xine-lib-1.2-20130415/src/combined/ffmpeg/ff_audio_decoder.c	2013-05-20 15:48:45.000000000 +0200
@@ -46,6 +46,8 @@
 
 #define AUDIOBUFSIZE (64 * 1024)
 
+#define MAX_CHANNELS 6
+
 typedef struct {
   audio_decoder_class_t   decoder_class;
 
@@ -59,11 +61,6 @@ typedef struct ff_audio_decoder_s {
 
   xine_stream_t    *stream;
 
-  int               output_open;
-  int               audio_channels;
-  int               audio_bits;
-  int               audio_sample_rate;
-
   unsigned char    *buf;
   int               bufsize;
   int               size;
@@ -78,6 +75,29 @@ typedef struct ff_audio_decoder_s {
 #if AVAUDIO > 3
   AVFrame          *av_frame;
 #endif
+
+  /* decoder settings */
+  int               ff_channels;
+  int               ff_bits;
+  int               ff_sample_rate;
+  int64_t           ff_map;
+
+  /* channel mixer settings */
+  /* map[ao_channel] = ff_channel */
+  int8_t            map[MAX_CHANNELS];
+  int8_t            left[4], right[4];
+  /* how many left[] / right[] entries are in use */
+  int               front_mixes;
+  /* volume adjustment */
+  int               downmix_shift;
+
+  /* audio out settings */
+  int               output_open;
+  int               ao_channels;
+  int               new_mode;
+  int               ao_mode;
+  int               ao_caps;
+
 } ff_audio_decoder_t;
 
 
@@ -163,11 +183,11 @@ static void ff_audio_init_codec(ff_audio
   /* Current ffmpeg audio decoders usually use 16 bits/sample
    * buf->decoder_info[2] can't be used as it doesn't refer to the output
    * bits/sample for some codecs (e.g. MS ADPCM) */
-  this->audio_bits = 16;
+  this->ff_bits = 16;
 
-  this->context->bits_per_sample = this->audio_bits;
-  this->context->sample_rate = this->audio_sample_rate;
-  this->context->channels    = this->audio_channels;
+  this->context->bits_per_sample = this->ff_bits;
+  this->context->sample_rate = this->ff_sample_rate;
+  this->context->channels    = this->ff_channels;
   this->context->codec_id    = this->codec->id;
   this->context->codec_type  = this->codec->type;
   this->context->codec_tag   = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_FOURCC);
@@ -237,8 +257,8 @@ static void ff_handle_header_buffer(ff_a
   }
 
   if(buf->decoder_flags & BUF_FLAG_STDHEADER) {
-    this->audio_sample_rate = buf->decoder_info[1];
-    this->audio_channels    = buf->decoder_info[3];
+    this->ff_sample_rate = buf->decoder_info[1];
+    this->ff_channels    = buf->decoder_info[3];
 
     if(this->size) {
       audio_header = (xine_waveformatex *)this->buf;
@@ -259,15 +279,15 @@ static void ff_handle_header_buffer(ff_a
 
     switch(codec_type) {
     case BUF_AUDIO_14_4:
-      this->audio_sample_rate = 8000;
-      this->audio_channels    = 1;
+      this->ff_sample_rate = 8000;
+      this->ff_channels    = 1;
 
       this->context->block_align = 240;
       break;
     case BUF_AUDIO_28_8:
-      this->audio_sample_rate = _X_BE_16(&this->buf[0x30]);
-      this->audio_channels    = this->buf[0x37];
-      /* this->audio_bits = buf->content[0x35] */
+      this->ff_sample_rate = _X_BE_16(&this->buf[0x30]);
+      this->ff_channels    = this->buf[0x37];
+      /* this->ff_bits = buf->content[0x35] */
 
       this->context->block_align = _X_BE_32(&this->buf[0x18]);
 
@@ -284,34 +304,34 @@ static void ff_handle_header_buffer(ff_a
 
       xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
               "ffmpeg_audio_dec: 28_8 audio channels %d bits %d sample rate %d block align %d\n",
-              this->audio_channels, this->audio_bits, this->audio_sample_rate,
+              this->ff_channels, this->ff_bits, this->ff_sample_rate,
               this->context->block_align);
       break;
     case BUF_AUDIO_COOK:
       {
         int version;
         int data_len;
         int extradata;
 
         version = _X_BE_16 (this->buf+4);
         if (version == 4) {
-          this->audio_sample_rate = _X_BE_16 (this->buf+48);
-          this->audio_bits = _X_BE_16 (this->buf+52);
-          this->audio_channels = _X_BE_16 (this->buf+54);
+          this->ff_sample_rate = _X_BE_16 (this->buf+48);
+          this->ff_bits = _X_BE_16 (this->buf+52);
+          this->ff_channels = _X_BE_16 (this->buf+54);
           data_len = _X_BE_32 (this->buf+67);
           extradata = 71;
         } else {
-          this->audio_sample_rate = _X_BE_16 (this->buf+54);
-          this->audio_bits = _X_BE_16 (this->buf+58);
-          this->audio_channels = _X_BE_16 (this->buf+60);
+          this->ff_sample_rate = _X_BE_16 (this->buf+54);
+          this->ff_bits = _X_BE_16 (this->buf+58);
+          this->ff_channels = _X_BE_16 (this->buf+60);
           data_len = _X_BE_32 (this->buf+74);
           extradata = 78;
         }
         this->context->block_align = _X_BE_16 (this->buf+44);
 
         xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
                 "ffmpeg_audio_dec: cook audio channels %d bits %d sample rate %d block align %d\n",
-                this->audio_channels, this->audio_bits, this->audio_sample_rate,
+                this->ff_channels, this->ff_bits, this->ff_sample_rate,
                 this->context->block_align);
 
         if (extradata + data_len > this->size)
@@ -363,11 +395,146 @@ static void ff_audio_output_close(ff_aud
     this->output_open = 0;
   }
 
-  this->audio_bits = 0;
-  this->audio_sample_rate = 0;
-  this->audio_channels = 0;
+  this->ff_sample_rate = 0;
+  this->ao_mode = 0;
+}
+
+static void ff_map_channels (ff_audio_decoder_t *this) {
+  uint64_t ff_map;
+  int caps = this->stream->audio_out->get_capabilities (this->stream->audio_out);
+
+  /* safety kludge for very old libavcodec */
+#ifdef AV_CH_FRONT_LEFT
+  ff_map = this->context->channel_layout;
+  if (!ff_map) /* wma2 bug */
+#endif
+    ff_map = (1 << this->context->channels) - 1;
+
+  if ((caps != this->ao_caps) || (ff_map != this->ff_map)) {
+    int i, j;
+    /* ff: see names[] below; xine: L R RL RR C LFE */
+    const int8_t base_map[] = {0, 1, 4, 5, 2, 3, -1, -1, -1, 2, 3};
+    int8_t name_map[MAX_CHANNELS];
+    const int modes[] = {
+      AO_CAP_MODE_MONO, AO_CAP_MODE_STEREO,
+      AO_CAP_MODE_4CHANNEL, AO_CAP_MODE_4_1CHANNEL,
+      AO_CAP_MODE_5CHANNEL, AO_CAP_MODE_5_1CHANNEL
+    };
+    const int num_modes = sizeof (modes) / sizeof (modes[0]);
+    const int8_t mode_channels[]   = {1, 2, 4, 6, 6, 6};
+    const int8_t wishlist[] = {
+      0, 1, 2, 3, 4, 5, /* mono */
+      1, 2, 3, 4, 5, 0, /* stereo */
+      5, 4, 3, 2, 1, 0, /* center + lfe */
+      4, 5, 2, 3, 1, 0, /* center */
+      3, 5, 2, 4, 1, 0, /* lfe */
+      2, 3, 4, 5, 1, 0  /* 4.0 */
+    };
+    const int8_t *tries;
+
+    this->ao_caps     = caps;
+    this->ff_map      = ff_map;
+    this->ff_channels = this->context->channels;
+
+    /* silence out */
+    for (i = 0; i < MAX_CHANNELS; i++)
+      this->map[i] = -1;
+    for (i = 0; i < 4; i++)
+      this->left[i] = this->right[i] = -1;
+
+    /* set up raw map and ao mode wishlist */
+    if (this->ff_channels == 1) { /* mono */
+      name_map[0] = 2;
+      this->left[0] = this->right[0] = 0;
+      tries = wishlist + 0 * num_modes;
+    } else if (this->ff_channels == 2) { /* stereo */
+      name_map[0] = 0;
+      name_map[1] = 1;
+      this->left[0] = 0;
+      this->right[0] = 1;
+      tries = wishlist + 1 * num_modes;
+    } else {
+      for (i = j = 0; i < sizeof (base_map) / sizeof (base_map[0]); i++) {
+        if ((ff_map >> i) & 1) {
+          int8_t target = base_map[i];
+          if ((target >= 0) && (this->map[target] < 0))
+            this->map[target] = j;
+          name_map[j] = i; /* for debug output below */
+          j++;
+        }
+      }
+      this->left[0]  = this->map[0] < 0 ? 0 : this->map[0];
+      this->map[0]   = -1;
+      this->right[0] = this->map[1] < 0 ? 1 : this->map[1];
+      this->map[1]   = -1;
+      tries = wishlist
+        + (2 + (this->map[4] < 0 ? 2 : 0) + (this->map[5] < 0 ? 1 : 0)) * num_modes;
+    }
+    this->front_mixes = 1;
+
+    /* find ao mode */
+    for (i = 0; i < num_modes; i++) if (caps & modes[tries[i]]) break;
+    i = i == num_modes ? 1 : tries[i];
+    this->new_mode = modes[i];
+    this->ao_channels = mode_channels[i];
+
+    /* mix center to front */
+    if ((this->map[4] >= 0) && !((0x30 >> i) & 1)) {
+      this->left[this->front_mixes]    = this->map[4];
+      this->right[this->front_mixes++] = this->map[4];
+      this->map[4] = -1;
+    }
+    /* mix lfe to front */
+    if ((this->map[5] >= 0) && !((0x28 >> i) & 1)) {
+      this->left[this->front_mixes]    = this->map[5];
+      this->right[this->front_mixes++] = this->map[5];
+      this->map[5] = -1;
+    }
+    /* mix surround to front */
+    if ((this->map[2] >= 0) && (this->map[3] >= 0) && !((0x3c >> i) & 1)) {
+      this->left[this->front_mixes]    = this->map[2];
+      this->right[this->front_mixes++] = this->map[3];
+      this->map[2] = -1;
+      this->map[3] = -1;
+    }
+
+    this->downmix_shift = this->front_mixes > 1 ? 1 : 0;
+    /* this will be on the safe side but usually too soft?? */
+#if 0
+    if (this->front_mixes > 2)
+      this->downmix_shift = 2;
+#endif
+
+    if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG) {
+      const int8_t *names[] = {
+        "left", "right", "center", "bass",
+        "rear left", "rear right",
+        "half left", "half right",
+        "rear center",
+        "side left", "side right"
+      };
+      int8_t buf[200];
+      int p = sprintf (buf, "ff_audio_dec: channel layout: ");
+      int8_t *indx = this->left;
+      for (i = 0; i < 2; i++) {
+        buf[p++] = '[';
+        for (j = 0; j < this->front_mixes; j++)
+          p += sprintf (buf + p, "%s%s", names[name_map[indx[j]]], (j < this->front_mixes - 1) ? " + " : "");
+        buf[p++] = ']';
+        buf[p++] = ' ';
+        indx = this->right;
+      }
+      for (i = 2; i < this->ao_channels; i++)
+        p += sprintf (buf + p, "[%s] ",
+          ((this->map[i] < 0) || (this->map[i] > 5)) ? (const int8_t *)"-" : names[name_map[this->map[i]]]);
+      buf[p++] = '\n';
+      fwrite (buf, 1, p, stdout);
+    }
+  }
 }
 
+#define CLIP_16(v) ((v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v)
+
 static int ff_audio_decode (ff_audio_decoder_t *this,
   int16_t *decode_buffer, int *decode_buffer_size, uint8_t *buf, int size) {
   int consumed;
@@ -408,106 +575,250 @@ static int ff_audio_decode (ff_audio_dec
   avpkt.flags = AV_PKT_FLAG_KEY;
 #  if AVAUDIO > 3
   int got_frame;
-  const float gain = this->class->gain;
+  float gain = this->class->gain;
   if (!this->av_frame)
     this->av_frame = avcodec_alloc_frame ();
 
   consumed = avcodec_decode_audio4 (this->context, this->av_frame, &got_frame, &avpkt);
   if ((consumed >= 0) && got_frame) {
+    /* setup may have altered while decoding */
+    ff_map_channels (this);
+
     int16_t *q = decode_buffer;
     int samples = this->av_frame->nb_samples;
-    int channels = this->context->channels;
-    int bytes, i, j;
+    int channels = this->ao_channels;
+    int bytes, i, j, shift = this->downmix_shift;
     /* limit buffer */
-    if (channels > 12)
-      channels = 12;
     if (*decode_buffer_size < samples * channels * 2)
       samples = *decode_buffer_size / (channels * 2);
     bytes = samples * channels * 2;
     *decode_buffer_size = bytes;
-    /* convert to packed int16_t. I guess there is something
-       in libavfilter but also another dependency... */
+    /* TJ. convert to packed int16_t while respecting the user's speaker arrangement.
+       I tried to speed up and not to pull in libswresample. */
+    for (i = 2; i < channels; i++) if (this->map[i] < 0) {
+      /* clear if there is an upmix mute channel */
+      memset (q, 0, bytes);
+      break;
+    }
+    /* For mono output, downmix to stereo first */
+    if ((channels == 1) && (this->ff_channels > 1))
+      channels = 2;
+    gain /= (float)(1 << shift);
     switch (this->context->sample_fmt) {
+#define MIX_AUDIO(stype,planar,idx,num,dindx) {\
+    stype *p1, *p2, *p3, *p4;\
+    int i, sstep;\
+    int8_t *x = idx;\
+    int16_t *dptr = (int16_t *)decode_buffer + dindx;\
+    if (planar) {\
+      p1 = (stype *)this->av_frame->extended_data[x[0]];\
+      sstep = 1;\
+    } else {\
+      p1 = (stype *)this->av_frame->extended_data[0] + x[0];\
+      sstep = this->ff_channels;\
+    }\
+    if (num == 1) {\
+      if (p1) for (i = 0; i < samples; i++) {\
+        int32_t v = MIX_FIX(*p1);\
+        p1       += sstep;\
+        v       >>= shift;\
+        *dptr     = (v);\
+        dptr     += channels;\
+      }\
+    } else {\
+      if (planar)\
+        p2 = (stype *)this->av_frame->extended_data[x[1]];\
+      else\
+        p2 = (stype *)this->av_frame->extended_data[0] + x[1];\
+      if (num == 2) {\
+        if (p1 && p2) for (i = 0; i < samples; i++) {\
+          int32_t v = MIX_FIX(*p1);\
+          p1       += sstep;\
+          v        += MIX_FIX(*p2);\
+          p2       += sstep;\
+          v       >>= shift;\
+          *dptr     = CLIP_16(v);\
+          dptr     += channels;\
+        }\
+      } else {\
+        if (planar)\
+          p3 = (stype *)this->av_frame->extended_data[x[2]];\
+        else\
+          p3 = (stype *)this->av_frame->extended_data[0] + x[2];\
+        if (num == 3) {\
+          if (p1 && p2 && p3) for (i = 0; i < samples; i++) {\
+            int32_t v = MIX_FIX(*p1);\
+            p1       += sstep;\
+            v        += MIX_FIX(*p2);\
+            p2       += sstep;\
+            v        += MIX_FIX(*p3);\
+            p3       += sstep;\
+            v       >>= shift;\
+            *dptr     = CLIP_16(v);\
+            dptr     += channels;\
+          }\
+        } else {\
+          if (planar)\
+            p4 = (stype *)this->av_frame->extended_data[x[3]];\
+          else\
+            p4 = (stype *)this->av_frame->extended_data[0] + x[3];\
+          if (p1 && p2 && p3 && p4) for (i = 0; i < samples; i++) {\
+            int32_t v = MIX_FIX(*p1);\
+            p1       += sstep;\
+            v        += MIX_FIX(*p2);\
+            p2       += sstep;\
+            v        += MIX_FIX(*p3);\
+            p3       += sstep;\
+            v        += MIX_FIX(*p4);\
+            p4       += sstep;\
+            v       >>= shift;\
+            *dptr     = CLIP_16(v);\
+            dptr     += channels;\
+          }\
+        }\
+      }\
+    }\
+  }
+#define MIX_FIX(v) (((int16_t)(v)<<8)^0x8000)
       case AV_SAMPLE_FMT_U8P:
-        if (channels > 1) {
-          uint8_t *p[12];
-          for (i = 0; i < channels; i++)
-            p[i] = (uint8_t *)this->av_frame->extended_data[i];
-          for (i = samples; i; i--) {
-            for (j = 0; j < channels; j++)
-              *q++ = ((uint16_t)(*p[j]++) << 8) ^ 0x8000;
-          }
-          break;
-        }
+        MIX_AUDIO (uint8_t, 1, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (uint8_t, 1, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (uint8_t, 1, this->map + j, 1, j);
+      break;
       case AV_SAMPLE_FMT_U8:
-        {
-          uint8_t *p = (uint8_t *)this->av_frame->extended_data[0];
-          for (i = samples * channels; i; i--)
-            *q++ = ((uint16_t)(*p++) << 8) ^ 0x8000;
-        }
+        MIX_AUDIO (uint8_t, 0, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (uint8_t, 0, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (uint8_t, 0, this->map + j, 1, j);
       break;
+#undef MIX_FIX
+#define MIX_FIX(v) (v)
       case AV_SAMPLE_FMT_S16P:
-        if (channels > 1) {
-          int16_t *p[12];
-          for (i = 0; i < channels; i++)
-            p[i] = (int16_t *)this->av_frame->extended_data[i];
-          for (i = samples; i; i--) {
-            for (j = 0; j < channels; j++)
-              *q++ = *p[j]++;
-          }
-          break;
-        }
+        MIX_AUDIO (int16_t, 1, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (int16_t, 1, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (int16_t, 1, this->map + j, 1, j);
+      break;
       case AV_SAMPLE_FMT_S16:
-        xine_fast_memcpy (q, this->av_frame->extended_data[0], bytes);
+        MIX_AUDIO (int16_t, 0, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (int16_t, 0, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (int16_t, 0, this->map + j, 1, j);
       break;
+#undef MIX_FIX
+#define MIX_FIX(v) ((v)>>16)
       case AV_SAMPLE_FMT_S32P:
-        if (channels > 1) {
-          int32_t *p[12];
-          for (i = 0; i < channels; i++)
-            p[i] = (int32_t *)this->av_frame->extended_data[i];
-          for (i = samples; i; i--) {
-            for (j = 0; j < channels; j++)
-              *q++ = *p[j]++ >> 16;
-          }
-          break;
-        }
+        MIX_AUDIO (int32_t, 1, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (int32_t, 1, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (int32_t, 1, this->map + j, 1, j);
+      break;
       case AV_SAMPLE_FMT_S32:
-        {
-          int32_t *p = (int32_t *)this->av_frame->extended_data[0];
-          for (i = samples * channels; i; i--)
-            *q++ = *p++ >> 16;
-        }
+        MIX_AUDIO (int32_t, 0, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (int32_t, 0, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (int32_t, 0, this->map + j, 1, j);
       break;
+#undef MIX_FIX
+#undef MIX_AUDIO
+#define MIX_AUDIO(stype,planar,idx,num,dindx) {\
+    stype *p1, *p2, *p3, *p4;\
+    int i, sstep;\
+    int8_t *x = idx;\
+    int16_t *dptr = (int16_t *)decode_buffer + dindx;\
+    if (planar) {\
+      p1 = (stype *)this->av_frame->extended_data[x[0]];\
+      sstep = 1;\
+    } else {\
+      p1 = (stype *)this->av_frame->extended_data[0] + x[0];\
+      sstep = this->ff_channels;\
+    }\
+    if (num == 1) {\
+      if (p1) for (i = 0; i < samples; i++) {\
+        int32_t v = (*p1) * gain;\
+        p1       += sstep;\
+        *dptr     = CLIP_16(v);\
+        dptr     += channels;\
+      }\
+    } else {\
+      if (planar)\
+        p2 = (stype *)this->av_frame->extended_data[x[1]];\
+      else\
+        p2 = (stype *)this->av_frame->extended_data[0] + x[1];\
+      if (num == 2) {\
+        if (p1 && p2) for (i = 0; i < samples; i++) {\
+          int32_t v = (*p1 + *p2) * gain;\
+          p1       += sstep;\
+          p2       += sstep;\
+          *dptr     = CLIP_16(v);\
+          dptr     += channels;\
+        }\
+      } else {\
+        if (planar)\
+          p3 = (stype *)this->av_frame->extended_data[x[2]];\
+        else\
+          p3 = (stype *)this->av_frame->extended_data[0] + x[2];\
+        if (num == 3) {\
+          if (p1 && p2 && p3) for (i = 0; i < samples; i++) {\
+            int32_t v = (*p1 + *p2 + *p3) * gain;\
+            p1       += sstep;\
+            p2       += sstep;\
+            p3       += sstep;\
+            *dptr     = CLIP_16(v);\
+            dptr     += channels;\
+          }\
+        } else {\
+          if (planar)\
+            p4 = (stype *)this->av_frame->extended_data[x[3]];\
+          else\
+            p4 = (stype *)this->av_frame->extended_data[0] + x[3];\
+          if (p1 && p2 && p3 && p4) for (i = 0; i < samples; i++) {\
+            int32_t v = (*p1 + *p2 + *p3 + *p4) * gain;\
+            p1       += sstep;\
+            p2       += sstep;\
+            p3       += sstep;\
+            p4       += sstep;\
+            *dptr     = CLIP_16(v);\
+            dptr     += channels;\
+          }\
+        }\
+      }\
+    }\
+  }
       case AV_SAMPLE_FMT_FLTP: /* the most popular one */
-        if (channels > 1) {
-          float *p[12];
-          for (i = 0; i < channels; i++)
-            p[i] = (float *)this->av_frame->extended_data[i];
-          for (i = samples; i; i--) {
-            for (j = 0; j < channels; j++) {
-              int v = *p[j]++ * gain;
-              *q++ = (v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v;
-            }
-          }
-          break;
-        }
+        MIX_AUDIO (float, 1, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (float, 1, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (float, 1, this->map + j, 1, j);
+      break;
       case AV_SAMPLE_FMT_FLT:
-        {
-          float *p = (float *)this->av_frame->extended_data[0];
-          for (i = samples * channels; i; i--) {
-            int v = *p++ * gain;
-            *q++ = (v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v;
-          }
-        }
+        MIX_AUDIO (float, 0, this->left,  this->front_mixes, 0);
+        MIX_AUDIO (float, 0, this->right, this->front_mixes, 1);
+        for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+          MIX_AUDIO (float, 0, this->map + j, 1, j);
       break;
       default: ;
     }
+    if (channels > this->ao_channels) {
+      /* final mono downmix */
+      int16_t *p = decode_buffer;
+      q = p;
+      for (i = samples; i; i--) {
+        int v = *p++;
+        v += *p++;
+        *q++ = v >> 1;
+      }
+      *decode_buffer_size = samples * 2;
+    }
   } else *decode_buffer_size = 0;
 #  else
   consumed = avcodec_decode_audio3 (this->context, decode_buffer, decode_buffer_size, &avpkt);
+  ff_map_channels (this);
 #  endif
 #else
   consumed = avcodec_decode_audio2 (this->context, decode_buffer, decode_buffer_size, buf, size);
+  ff_map_channels (this);
 #endif
 
   if (consumed < 0) {
@@ -567,23 +878,25 @@ static void ff_audio_decode_data (audio_
       while (this->size>=0) {
         decode_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
 
-	bytes_consumed =
-          ff_audio_decode(this,
-                          (int16_t *)this->decode_buffer, &decode_buffer_size,
-                          &this->buf[offset], this->size);
+        bytes_consumed = ff_audio_decode (this, (int16_t *)this->decode_buffer, &decode_buffer_size,
+          &this->buf[offset], this->size);
 
-        if (bytes_consumed<0) {
-          this->size=0;
-          return;
-        } else if (bytes_consumed == 0 && decode_buffer_size == 0) {
+        if (bytes_consumed < 0)
+          break;
+
+        offset     += bytes_consumed;
+        this->size -= bytes_consumed;
+
+        if (decode_buffer_size == 0) {
+          if (bytes_consumed)
+            continue;
           if (offset)
             memmove(this->buf, &this->buf[offset], this->size);
           return;
         }
 
-        if (this->audio_bits        != this->context->bits_per_sample ||
-            this->audio_sample_rate != this->context->sample_rate ||
-            this->audio_channels    != this->context->channels) {
+        if (this->ff_sample_rate != this->context->sample_rate ||
+            this->ao_mode        != this->new_mode) {
           xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
                   _("ffmpeg_audio_dec: codec parameters changed\n"));
           /* close if it was open, and always trigger 1 new open attempt below */
@@ -591,12 +904,11 @@ static void ff_audio_decode_data (audio_
         }
 
 	if (!this->output_open) {
-	  if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) {
-	    this->audio_bits = this->context->bits_per_sample;
-	    this->audio_sample_rate = this->context->sample_rate;
-	    this->audio_channels = this->context->channels;
+	  if (!this->ff_sample_rate || !this->ao_mode) {
+	    this->ff_sample_rate = this->context->sample_rate;
+	    this->ao_mode        = this->new_mode;
 	  }
-	  if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) {
+	  if (!this->ff_sample_rate || !this->new_mode) {
 	    xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
 		    _("ffmpeg_audio_dec: cannot read codec parameters from packet\n"));
 	    /* try to decode next packet. */
@@ -606,8 +918,8 @@ static void ff_audio_decode_data (audio_
 	    buf->pts = 0;
 	  } else {
 	    this->output_open = (this->stream->audio_out->open) (this->stream->audio_out,
-								 this->stream, this->audio_bits, this->audio_sample_rate,
-								 _x_ao_channels2mode(this->audio_channels));
+								 this->stream, 16, this->ff_sample_rate,
+								 this->ao_mode);
 	    if (!this->output_open) {
 	      xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
 		      "ffmpeg_audio_dec: error opening audio output\n");
@@ -617,6 +929,84 @@ static void ff_audio_decode_data (audio_
 	  }
 	}
 
+#if AVAUDIO < 4
+        /* Old style postprocessing */
+        if (codec_type == BUF_AUDIO_WMAPRO) {
+          /* the above codecs output float samples, not 16-bit integers */
+          int samples = decode_buffer_size / sizeof(float);
+          float gain  = this->class->gain;
+          float *p    = (float *)this->decode_buffer;
+          int16_t *q  = (int16_t *)this->decode_buffer;
+          int i;
+          for (i = samples; i; i--) {
+            int v = *p++ * gain;
+            *q++ = CLIP_16 (v);
+          }
+          decode_buffer_size = samples * 2;
+        }
+
+        if ((this->ao_channels != this->ff_channels) || (this->ao_channels > 2)) {
+          /* Channel reordering and/or mixing */
+          int samples     = decode_buffer_size / (this->ff_channels * 2);
+          int channels    = this->ao_channels;
+          int ff_channels = this->ff_channels;
+          int16_t *p      = (int16_t *)this->decode_buffer;
+          int16_t *q      = p;
+          int shift       = this->downmix_shift, i, j;
+          /* downmix mono output to stereo first */
+          if ((channels == 1) && (ff_channels > 1))
+            channels = 2;
+          /* move to end of buf for in-place editing */
+          p += AVCODEC_MAX_AUDIO_FRAME_SIZE - decode_buffer_size;
+          if (p >= q + decode_buffer_size)
+            xine_fast_memcpy (p, q, decode_buffer_size);
+          else
+            memmove (p, q, decode_buffer_size);
+          /* not very optimized but it only hits when playing multichannel audio through
+             old ffmpeg - and its still better than previous code there */
+          if (this->front_mixes < 2) {
+            /* just reorder and maybe upmix */
+            for (i = samples; i; i--) {
+              q[0] = p[0];
+              q[1] = p[this->right[0]];
+              for (j = 2; j < channels; j++)
+                q[j] = this->map[j] < 0 ? 0 : p[this->map[j]];
+              p += ff_channels;
+              q += channels;
+            }
+          } else {
+            /* downmix */
+            for (i = samples; i; i--) {
+              int left  = p[0];
+              int right = p[this->right[0]];
+              for (j = 1; j < this->front_mixes; j++) {
+                left  += p[this->left[j]];
+                right += p[this->right[j]];
+              }
+              left  >>= shift;
+              q[0]    = CLIP_16 (left);
+              right >>= shift;
+              q[1]    = CLIP_16 (right);
+              for (j = 2; j < channels; j++)
+                q[j] = this->map[j] < 0 ? 0 : p[this->map[j]] >> shift;
+              p += ff_channels;
+              q += channels;
+            }
+          }
+          /* final mono downmix */
+          if (channels > this->ao_channels) {
+            p = (int16_t *)this->decode_buffer;
+            q = p;
+            for (i = samples; i; i--) {
+              int v = *p++;
+              v += *p++;
+              *q++ = v >> 1;
+            }
+          }
+          decode_buffer_size = samples * this->ao_channels * 2;
+        }
+#endif
+
         /* dispatch the decoded audio */
         out = 0;
         while (out < decode_buffer_size) {
@@ -636,40 +1026,16 @@ static void ff_audio_decode_data (audio_
           }
 
           /* fill up this buffer */
-#if AVAUDIO < 4
-          if (codec_type == BUF_AUDIO_WMAPRO) {
-            /* the above codecs output float samples, not 16-bit integers */
-            int bytes_per_sample = sizeof(float);
-            if (((decode_buffer_size - out) * 2 / bytes_per_sample) > audio_buffer->mem_size)
-              bytes_to_send = audio_buffer->mem_size * bytes_per_sample / 2;
-            else
-              bytes_to_send = decode_buffer_size - out;
-
-            int16_t *int_buffer = calloc(1, bytes_to_send * 2 / bytes_per_sample);
-            int i;
-            for (i = 0; i < (bytes_to_send / bytes_per_sample); i++) {
-              float *float_sample = (float *)&this->decode_buffer[i * bytes_per_sample + out];
-              int_buffer[i] = (int16_t)lrintf(*float_sample * 32768.);
-            }
+          if ((decode_buffer_size - out) > audio_buffer->mem_size)
+            bytes_to_send = audio_buffer->mem_size;
+          else
+            bytes_to_send = decode_buffer_size - out;
 
-            out += bytes_to_send;
-            bytes_to_send = bytes_to_send * 2 / bytes_per_sample;
-            xine_fast_memcpy(audio_buffer->mem, int_buffer, bytes_to_send);
-            free(int_buffer);
-          } else
-#endif
-          {
-            if ((decode_buffer_size - out) > audio_buffer->mem_size)
-              bytes_to_send = audio_buffer->mem_size;
-            else
-              bytes_to_send = decode_buffer_size - out;
-
-            xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], bytes_to_send);
-            out += bytes_to_send;
-          }
+          xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], bytes_to_send);
+          out += bytes_to_send;
 
           /* byte count / 2 (bytes / sample) / channels */
-          audio_buffer->num_frames = bytes_to_send / 2 / this->audio_channels;
+          audio_buffer->num_frames = bytes_to_send / 2 / this->ao_channels;
 
           audio_buffer->vpts = buf->pts;
 
@@ -677,11 +1043,7 @@ static void ff_audio_decode_data (audio_
           this->stream->audio_out->put_buffer (this->stream->audio_out,
             audio_buffer, this->stream);
         }
-
-        this->size -= bytes_consumed;
-        offset += bytes_consumed;
       }
-
       /* reset internal accumulation buffer */
       this->size = 0;
     }
@@ -765,7 +1127,7 @@ static audio_decoder_t *ff_audio_open_pl
   this->audio_decoder.dispose             = ff_audio_dispose;
 
   this->output_open = 0;
-  this->audio_channels = 0;
+  this->ff_channels = 0;
   this->stream = stream;
   this->buf = NULL;
   this->size = 0;