The problem: 6-channel AAC has a different channel order than MPlayer outputs. MPlayer wants: 0 - front left 1 - front right 2 - rear left 3 - rear right 4 - front center 5 - lfe MPlayer gets: 0 - front center 1 - front left 2 - front right 3 - rear left 4 - rear right 5 - lfe -------------------------------------------------------------------------- For a test source you can probably use any recent hd trailer from Apple. Or: http://movies.apple.com/movies/universal/king_kong/king_kong-tlr_h480p.mov This works fine because AAC does the downmixing internally: $ mplayer king_kong-tlr_h480p.mov -channels 2 This sounds odd -- the channels are definitely mixed up: $ mplayer king_kong-tlr_h480p.mov -channels 6 If you don't have a 5.1 speaker setup (I don't either), then you can do a quick-n-dirty downmix: $ mplayer king_kong-tlr_h480p.mov -channels 6 \ -af pan=2:0.4:0:0:0.4:0.2:0:0:0.2:0.3:0.3:0.5:0.5 ...or an hrtf one: $ mplayer king_kong-tlr_h480p.mov -channels 6 -af hrtf Either way, you'll hear that the channel position isn't right. -------------------------------------------------------------------------- The attached patch uses af_channels to handle the reordering. ad_faad.c:init() sets up a routing map and af.c:af_init() uses that map to put a channels filter at the beginning of the audio chain. Try the above examples with a patched mplayer -- they should work correctly. As far as I know the patch doesn't break anything. Probably it does, but not that I know of. :) Anyway, I don't consider it finished. Some notes: * It would probably be ideal to have faad handle the reordering, but I don't see a way to do that. * If this kind of approach seems acceptable, I should switch to af_pan to handle upmixing and downmixing where channels have to be split or combined. * I don't have any code to deal with 3- or 4- channel AAC. With af_pan I could probably upmix or downmix these appropriately to match the output channel order. Does anyone have a sample? * This could be applied to audio formats other than AAC, of course. Does anyone have an example of another format that could benefit? -------------------------------------------------------------------------- So, what do you think? Am I barking up the wrong tree or should I keep working in this direction? -Corey Index: libaf/af.c =================================================================== RCS file: /cvsroot/mplayer/main/libaf/af.c,v retrieving revision 1.51 diff -u -r1.51 af.c --- libaf/af.c 20 Oct 2005 09:12:28 -0000 1.51 +++ libaf/af.c 22 Feb 2006 09:04:06 -0000 @@ -355,17 +355,40 @@ // Check if this is the first call if(!s->first){ - // 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")) - return -1; + /* remap channels if the codec/demuxer provides a channel map */ + if (s->chan_map) { + af_control_ext_t arg; + arg.ch = s->input.nch; + af_instance_t *af = NULL; + af=af_append(s, s->first, "channels"); + /* set up af_channels to route */ + if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_ROUTER, &s->input.nch))) + return -1; + if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_NR, &s->input.nch))) + return -1; + /* set up each route */ + for (arg.ch = 0; arg.ch < s->input.nch; ++arg.ch) { + arg.arg = s->chan_map + 2*arg.ch; /* two by two... */ + if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_ROUTING, &arg))) + return -1; + } + if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS, &s->input.nch))) + return -1; + if (AF_OK != af_reinit(s,af)) + return -1; } - else{ + // Add all filters in the list (if there are any) + if (s->cfg.list) { while(s->cfg.list[i]){ if(!af_append(s,s->last,s->cfg.list[i++])) return -1; } } + // To make automatic format conversion work + if (!s->first){ + if(!af_append(s,s->first,"dummy")) + return -1; + } } // Init filters @@ -711,3 +734,23 @@ { data->bps = af_fmt2bits(data->format)/8; } + +int *af_set_channel_map(int channels, char *routes){ + int *chan_map, *ptr; + ptr=chan_map=malloc(2 * channels * sizeof(int)); + if (!chan_map) { + mp_msg(MSGT_DEMUX, MSGL_ERR, "set_channel_map: cannot malloc for %d channels\n", channels); + return 0; + } + while (channels-- > 0) { + if (*routes == '\0' || *(routes+1) == '\0') { + mp_msg(MSGT_DEMUX, MSGL_ERR, "set_channel_map: not enough routes\n"); + free(chan_map); + return 0; + } + /* two by two... */ + *ptr++ = *routes++ - '0'; + *ptr++ = *routes++ - '0'; + } + return chan_map; +} Index: libaf/af.h =================================================================== RCS file: /cvsroot/mplayer/main/libaf/af.h,v retrieving revision 1.28 diff -u -r1.28 af.h --- libaf/af.h 1 Oct 2005 12:55:34 -0000 1.28 +++ libaf/af.h 22 Feb 2006 09:04:06 -0000 @@ -104,6 +104,7 @@ af_data_t output; // Configuration for this stream af_cfg_t cfg; + int *chan_map; }af_stream_t; /********************************************* @@ -329,6 +330,16 @@ */ void af_fix_parameters(af_data_t *data); +/** + * \brief set up channel remapping + * \param number of output channels + * \param string of from-to routes + * \return int array of routes + * + * Makes an int array from a string of provided routes. + */ +int *af_set_channel_map(int channels, char *routes); + /** Memory reallocation macro: if a local buffer is used (i.e. if the filter doesn't operate on the incoming buffer this macro must be called to ensure the buffer is big enough. Index: libmpcodecs/ad_faad.c =================================================================== RCS file: /cvsroot/mplayer/main/libmpcodecs/ad_faad.c,v retrieving revision 1.24 diff -u -r1.24 ad_faad.c --- libmpcodecs/ad_faad.c 12 Jan 2006 20:04:34 -0000 1.24 +++ libmpcodecs/ad_faad.c 22 Feb 2006 09:04:06 -0000 @@ -154,6 +154,23 @@ mp_msg(MSGT_DECAUDIO,MSGL_V,"FAAD: Negotiated samplerate: %ldHz channels: %d\n", faac_samplerate, faac_channels); sh->channels = faac_channels; if (audio_output_channels <= 2) sh->channels = faac_channels > 1 ? 2 : 1; + + /* re-map channels */ + switch (sh->channels) { + default: + case 1: /* no action needed */ + case 2: /* no action needed */ + case 3: /* no suitable default behavior? */ + case 4: /* no suitable default behavior? */ + break; + case 5: /* mplayer treats this like 6-channel */ + case 6: + sh->chan_map = af_set_channel_map(6, "04" "10" "21" "32" "43" "55"); + break; + case 7: /* not supported by mplayer? */ + break; + } + sh->samplerate = faac_samplerate; sh->samplesize=2; //sh->o_bps = sh->samplesize*faac_channels*faac_samplerate; Index: libmpcodecs/dec_audio.c =================================================================== RCS file: /cvsroot/mplayer/main/libmpcodecs/dec_audio.c,v retrieving revision 1.50 diff -u -r1.50 dec_audio.c --- libmpcodecs/dec_audio.c 16 Feb 2006 22:43:42 -0000 1.50 +++ libmpcodecs/dec_audio.c 22 Feb 2006 09:04:06 -0000 @@ -309,6 +309,7 @@ // filter config: memcpy(&afs->cfg,&af_cfg,sizeof(af_cfg_t)); + afs->chan_map = sh_audio->chan_map; mp_msg(MSGT_DECAUDIO, MSGL_V, MSGTR_BuildingAudioFilterChain, afs->input.rate,afs->input.nch,af_fmt2str_short(afs->input.format), Index: libmpdemux/stheader.h =================================================================== RCS file: /cvsroot/mplayer/main/libmpdemux/stheader.h,v retrieving revision 1.42 diff -u -r1.42 stheader.h --- libmpdemux/stheader.h 18 Feb 2006 09:26:39 -0000 1.42 +++ libmpdemux/stheader.h 22 Feb 2006 09:04:07 -0000 @@ -18,6 +18,7 @@ int samplerate; int samplesize; int channels; + int *chan_map; int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec) int i_bps; // == bitrate (compressed bytes/sec) // in buffers: