Hello,
This patch makes mplayer and mencoder auto-insert the channels filter
when necessary in order to maintain the proper order. Remapping is
necessary when reading from or writing to formats that have a different
native order from what mplayer and mencoder use internally.
There was some recent discussion on this matter in the thread "[RFC]
6-channel AAC and channel reordering".
I'm going to be gone on a camping trip until late Monday. If nobody has
objected by then, then I'll start applying this patch in independent
pieces. Since this is the first draft I've submitted for inclusion,
though, I wouldn't be surprised if there are any corrections I need to make.
Thanks,
Corey
Index: libmpcodecs/ae_faac.c
===================================================================
--- libmpcodecs/ae_faac.c (revision 20811)
+++ libmpcodecs/ae_faac.c (working copy)
@@ -68,6 +68,23 @@
((MPEGLAYER3WAVEFORMAT *) (mux_a->wf))->nBlockSize = mux_a->wf->nBlockAlign;
((MPEGLAYER3WAVEFORMAT *) (mux_a->wf))->nFramesPerBlock = 1;
((MPEGLAYER3WAVEFORMAT *) (mux_a->wf))->nCodecDelay = 0;
+
+ /* remap channels */
+ switch (mux_a->wf->nChannels) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* no suitable default behavior? */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FC FL FR BL BR */
+ encoder->output_chan_map = "40" "01" "12" "23" "34";
+ break;
+ case 6: /* FC FL FR BL BR LFE */
+ encoder->output_chan_map = "40" "01" "12" "23" "34" "55";
+ break;
+ default:
+ break;
+ }
// Fix allocation
mux_a->wf = realloc(mux_a->wf, sizeof(WAVEFORMATEX)+mux_a->wf->cbSize);
Index: libmpcodecs/ae_pcm.c
===================================================================
--- libmpcodecs/ae_pcm.c (revision 20811)
+++ libmpcodecs/ae_pcm.c (working copy)
@@ -27,6 +27,23 @@
mux_a->wf->wBitsPerSample=16;
mux_a->wf->cbSize=0; // FIXME for l3codeca.acm
+ /* re-map channels */
+ switch (mux_a->wf->nChannels) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* unknown; may not exist */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FL FR C BL BR */
+ encoder->output_chan_map = "00" "11" "42" "23" "34";
+ break;
+ case 6: /* FL FR C LFE BL BR */
+ encoder->output_chan_map = "00" "11" "42" "53" "24" "35";
+ break;
+ default:
+ break;
+ }
+
encoder->input_format = (mux_a->wf->wBitsPerSample==8) ? AF_FORMAT_U8 : AF_FORMAT_S16_LE;
encoder->min_buffer_size = 16384;
encoder->max_buffer_size = mux_a->wf->nAvgBytesPerSec;
Index: libmpcodecs/ad_pcm.c
===================================================================
--- libmpcodecs/ad_pcm.c (revision 20811)
+++ libmpcodecs/ad_pcm.c (working copy)
@@ -22,6 +22,22 @@
WAVEFORMATEX *h=sh_audio->wf;
sh_audio->i_bps=h->nAvgBytesPerSec;
sh_audio->channels=h->nChannels;
+ /* re-map channels */
+ switch (sh_audio->channels) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* unknown; may not exist */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FL FR C BL BR */
+ sh_audio->input_chan_map = "00" "11" "24" "32" "43";
+ break;
+ case 6: /* FL FR C LFE BL BR */
+ sh_audio->input_chan_map = "00" "11" "24" "35" "42" "53";
+ break;
+ default:
+ break;
+ }
sh_audio->samplerate=h->nSamplesPerSec;
sh_audio->samplesize=(h->wBitsPerSample+7)/8;
sh_audio->sample_format=AF_FORMAT_S16_LE; // default
Index: libmpcodecs/ad_faad.c
===================================================================
--- libmpcodecs/ad_faad.c (revision 20811)
+++ libmpcodecs/ad_faad.c (working copy)
@@ -152,6 +152,24 @@
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) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* no suitable default behavior? */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FC FL FR BL BR */
+ sh->input_chan_map = "04" "10" "21" "32" "43";
+ break;
+ case 6: /* FC FL FR BL BR LFE */
+ sh->input_chan_map = "04" "10" "21" "32" "43" "55";
+ break;
+ default:
+ break;
+ }
+
sh->samplerate = faac_samplerate;
sh->samplesize=2;
//sh->o_bps = sh->samplesize*faac_channels*faac_samplerate;
Index: libmpcodecs/ad_dmo.c
===================================================================
--- libmpcodecs/ad_dmo.c (revision 20811)
+++ libmpcodecs/ad_dmo.c (working copy)
@@ -40,6 +40,22 @@
}
sh_audio->i_bps=sh_audio->wf->nAvgBytesPerSec;
sh_audio->channels=chans;
+ /* re-map channels */
+ switch (chans) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* apparently does not exist */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FL FR FC BL BR */
+ sh_audio->input_chan_map = "00" "11" "23" "34" "42";
+ break;
+ case 6: /* FL FR FC LFE BL BR */
+ sh_audio->input_chan_map = "00" "11" "24" "35" "42" "53";
+ break;
+ default:
+ break;
+ }
sh_audio->samplerate=sh_audio->wf->nSamplesPerSec;
sh_audio->samplesize=2;
sh_audio->audio_in_minsize=4*sh_audio->wf->nBlockAlign;
Index: libmpcodecs/dec_audio.c
===================================================================
--- libmpcodecs/dec_audio.c (revision 20811)
+++ libmpcodecs/dec_audio.c (working copy)
@@ -311,6 +311,8 @@
// filter config:
memcpy(&afs->cfg,&af_cfg,sizeof(af_cfg_t));
+ afs->input_chan_map = sh_audio->input_chan_map;
+ afs->output_chan_map = sh_audio->output_chan_map;
mp_msg(MSGT_DECAUDIO, MSGL_V, MSGTR_BuildingAudioFilterChain,
afs->input.rate,afs->input.nch,af_fmt2str_short(afs->input.format),
Index: libmpcodecs/ae.h
===================================================================
--- libmpcodecs/ae.h (revision 20811)
+++ libmpcodecs/ae.h (working copy)
@@ -39,6 +39,7 @@
int (*encode)(struct audio_encoder_s *encoder, uint8_t *dest, void *src, int nsamples, int max_size);
void (*fixup)(struct audio_encoder_s *encoder);
int (*close)(struct audio_encoder_s *encoder);
+ char *output_chan_map;
} audio_encoder_t;
audio_encoder_t *new_audio_encoder(muxer_stream_t *stream, audio_encoding_params_t *params);
Index: libmpcodecs/ae_lavc.c
===================================================================
--- libmpcodecs/ae_lavc.c (revision 20811)
+++ libmpcodecs/ae_lavc.c (working copy)
@@ -40,6 +40,22 @@
mux_a->wf->nAvgBytesPerSec = (lavc_actx->bit_rate / 8);
mux_a->avg_rate= lavc_actx->bit_rate;
mux_a->h.dwRate = mux_a->wf->nAvgBytesPerSec;
+ /* remap channels */
+ switch (lavc_actx->channels) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* unknown */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FL FC FR BL BR */
+ encoder->output_chan_map = "00" "21" "32" "43" "14";
+ break;
+ case 6: /* FL FC FR BL BR LFE */
+ encoder->output_chan_map = "00" "21" "32" "43" "14" "55";
+ break;
+ default:
+ break;
+ }
if(lavc_actx->block_align)
mux_a->h.dwSampleSize = mux_a->h.dwScale = lavc_actx->block_align;
else
Index: libao2/ao_pcm.c
===================================================================
--- libao2/ao_pcm.c (revision 20811)
+++ libao2/ao_pcm.c (working copy)
@@ -120,6 +120,23 @@
ao_data.samplerate=rate;
ao_data.format=format;
ao_data.bps=channels*rate*(bits/8);
+
+ /* re-map channels */
+ switch (ao_data.channels) {
+ case 1: /* FC, no action needed */
+ case 2: /* FL FR, no action needed */
+ case 3: /* unknown; may not exist */
+ case 4: /* FL FR BL BR, no action needed */
+ break;
+ case 5: /* FL FR C BL BR */
+ ao_data.output_chan_map = "00" "11" "42" "23" "34";
+ break;
+ case 6: /* FL FR C LFE BL BR */
+ ao_data.output_chan_map = "00" "11" "42" "53" "24" "35";
+ break;
+ default:
+ break;
+ }
wavhdr.channels = le2me_16(ao_data.channels);
wavhdr.sample_rate = le2me_32(ao_data.samplerate);
Index: libao2/audio_out.h
===================================================================
--- libao2/audio_out.h (revision 20811)
+++ libao2/audio_out.h (working copy)
@@ -39,6 +39,7 @@
int outburst;
int buffersize;
int pts;
+ char *output_chan_map;
} ao_data_t;
extern char *ao_subdevice;
Index: DOCS/man/en/mplayer.1
===================================================================
--- DOCS/man/en/mplayer.1 (revision 20811)
+++ DOCS/man/en/mplayer.1 (working copy)
@@ -1377,6 +1377,14 @@
Always falls back on content-based demuxer selection.
.
.TP
+.B \-noremap-channels
+Some audio formats have a different channel order than MPlayer and
+MEncoder use internally.
+Normally, the channels audio filter is automatically inserted to remap
+the channels as necessary.
+This option disables the remapping.
+.
+.TP
.B \-passwd <password> (also see \-user) (network only)
Specify password for HTTP authentication.
.
Index: mplayer.c
===================================================================
--- mplayer.c (revision 20811)
+++ mplayer.c (working copy)
@@ -311,6 +311,7 @@
float force_fps=0;
static int force_srate=0;
+static int remap_channels=1;
static int audio_output_format=-1; // AF_FORMAT_UNKNOWN
int frame_dropping=0; // option 0=no drop 1= drop vo 2= drop decode
static int play_n_frames=-1;
@@ -1203,6 +1204,8 @@
new_srate = 192000;
playback_speed = (float)new_srate / (float)sh_audio->samplerate;
}
+ if (!remap_channels)
+ sh_audio->input_chan_map = sh_audio->output_chan_map = NULL;
result = init_audio_filters(sh_audio, new_srate,
sh_audio->channels, sh_audio->sample_format,
&ao_data->samplerate, &ao_data->channels, &ao_data->format,
@@ -2645,6 +2648,8 @@
ao_data.format=audio_output_format;
#if 1
// first init to detect best values
+ if (!remap_channels)
+ sh_audio->input_chan_map = sh_audio->output_chan_map = NULL;
if(!preinit_audio_filters(sh_audio,
// input:
(int)(sh_audio->samplerate*playback_speed),
@@ -2682,6 +2687,7 @@
// init audio filters:
#if 1
current_module="af_init";
+ sh_audio->output_chan_map = ao_data.output_chan_map;
if(!build_afilter_chain(sh_audio, &ao_data)) {
mp_msg(MSGT_CPLAYER,MSGL_ERR,MSGTR_NoMatchingFilter);
// mp_msg(MSGT_CPLAYER,MSGL_ERR,"Couldn't find matching filter / ao format! -> NOSOUND\n");
Index: cfg-common.h
===================================================================
--- cfg-common.h (revision 20811)
+++ cfg-common.h (working copy)
@@ -169,6 +169,8 @@
{"fps", &force_fps, CONF_TYPE_FLOAT, CONF_MIN, 0, 0, NULL},
{"srate", &force_srate, CONF_TYPE_INT, CONF_RANGE, 1000, 8*48000, NULL},
{"channels", &audio_output_channels, CONF_TYPE_INT, CONF_RANGE, 1, 6, NULL},
+ {"remap-channels", &remap_channels, CONF_TYPE_FLAG, 0, 0, 1, NULL},
+ {"noremap-channels", &remap_channels, CONF_TYPE_FLAG, 0, 1, 0, NULL},
{"format", &audio_output_format, CONF_TYPE_AFMT, 0, 0, 0, NULL},
{"speed", &playback_speed, CONF_TYPE_FLOAT, CONF_RANGE, 0.01, 100.0, NULL},
Index: libmpdemux/stheader.h
===================================================================
--- libmpdemux/stheader.h (revision 20811)
+++ libmpdemux/stheader.h (working copy)
@@ -19,6 +19,8 @@
int samplerate;
int samplesize;
int channels;
+ char *input_chan_map;
+ char *output_chan_map;
int o_bps; // == samplerate*samplesize*channels (uncompr. bytes/sec)
int i_bps; // == bitrate (compressed bytes/sec)
// in buffers:
Index: mencoder.c
===================================================================
--- mencoder.c (revision 20811)
+++ mencoder.c (working copy)
@@ -167,6 +167,7 @@
float playback_speed=1.0;
static int force_srate=0;
+static int remap_channels=1;
static int audio_output_format=0;
char *vobsub_out=NULL;
@@ -406,7 +407,7 @@
int new_srate=0;
unsigned int timer_start=0;
-ao_data_t ao_data = {0,0,0,0,OUTBURST,-1,0};
+ao_data_t ao_data = {0,0,0,0,OUTBURST,-1,0,NULL};
audio_encoding_params_t aparams;
audio_encoder_t *aencoder = NULL;
@@ -909,6 +910,8 @@
ao_data.samplerate = force_srate;
ao_data.channels = 0;
ao_data.format = audio_output_format;
+if (!remap_channels)
+ sh_audio->input_chan_map = sh_audio->output_chan_map = NULL;
if(!preinit_audio_filters(sh_audio,
// input:
new_srate,
@@ -926,6 +929,9 @@
aencoder = new_audio_encoder(mux_a, &aparams);
if(!aencoder)
mencoder_exit(1, NULL);
+ sh_audio->output_chan_map = aencoder->output_chan_map;
+ if (!remap_channels)
+ sh_audio->input_chan_map = sh_audio->output_chan_map = NULL;
if(!init_audio_filters(sh_audio,
new_srate, sh_audio->channels, sh_audio->sample_format,
&aparams.sample_rate, &aparams.channels, &aencoder->input_format,
Index: libaf/af.c
===================================================================
--- libaf/af.c (revision 20811)
+++ libaf/af.c (working copy)
@@ -359,12 +359,12 @@
// Check if this is the first call
if(!s->first){
+ /* remap input channels if the codec/demuxer provides a map */
+ if (s->input_chan_map)
+ if (AF_OK != af_append_channel_map(s, s->first, s->input_chan_map))
+ return -1;
// 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;
- }
- else{
+ if (s->cfg.list) {
while(s->cfg.list[i]){
if(!af_append(s,s->last,s->cfg.list[i++]))
return -1;
@@ -372,6 +372,20 @@
}
}
+ /* remap output channels if the codec/ao provides a map */
+ if (s->output_chan_map) {
+ if (AF_OK != af_append_channel_map(s, s->last, s->output_chan_map))
+ return -1;
+ /* make sure we don't append this more than once */
+ s->output_chan_map = NULL;
+ }
+
+ // To make automatic format conversion work
+ if (!s->first){
+ if(!af_append(s,s->first,"dummy"))
+ return -1;
+ }
+
// Init filters
if(AF_OK != af_reinit(s,s->first))
return -1;
@@ -711,3 +725,36 @@
{
data->bps = af_fmt2bits(data->format)/8;
}
+
+int af_append_channel_map(af_stream_t *s, af_instance_t *af_prev, char *chan_map){
+ af_control_ext_t arg;
+ af_instance_t *af = NULL;
+ int channels, route[2];
+ channels = af_prev ? af_prev->data->nch : s->input.nch;
+ /* check input */
+ if (strlen(chan_map) != 2*channels) {
+ af_msg(AF_MSG_ERROR, "channel map: num. of routes does not match num. of channels\n");
+ return AF_ERROR;
+ }
+ /* set up af_channels to route */
+ af = af_append(s, af_prev, "channels");
+ if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_ROUTER, &channels)))
+ return AF_ERROR;
+ if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_NR, &channels)))
+ return AF_ERROR;
+ /* pass each route to af_channels */
+ for (arg.ch = 0; arg.ch < channels; ++arg.ch) {
+ /* two by two... */
+ route[0] = *chan_map++ - '0';
+ route[1] = *chan_map++ - '0';
+ arg.arg = route;
+ if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS_ROUTING, &arg)))
+ return AF_ERROR;
+ }
+ /* try it! */
+ if (!af || (AF_OK != af->control(af, AF_CONTROL_CHANNELS, &channels)))
+ return AF_ERROR;
+ if (AF_OK != af_reinit(s,af))
+ return AF_ERROR;
+ return AF_OK;
+}
Index: libaf/af.h
===================================================================
--- libaf/af.h (revision 20811)
+++ libaf/af.h (working copy)
@@ -104,6 +104,8 @@
af_data_t output;
// Configuration for this stream
af_cfg_t cfg;
+ char *input_chan_map;
+ char *output_chan_map;
}af_stream_t;
/*********************************************
@@ -329,6 +331,18 @@
*/
void af_fix_parameters(af_data_t *data);
+/**
+ * \brief set up channel remapping
+ * \param af_stream
+ * \param af_instance to append this instance after
+ * \param string of channel routes
+ * \return AF_OK if ok, AF_ERROR otherwise
+ *
+ * Checks that the number of routes matches channels, and creates an instance
+ * of af_channels with the routes specified in *chan_map.
+ */
+int af_append_channel_map(af_stream_t *s, af_instance_t *af_prev, char *chan_map);
+
/** 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.