[PATCH] audio channel remapping
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.
Hello, On Wed, Nov 08, 2006 at 10:00:47PM -0800, Corey Hickey wrote:
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.
I am fine with changing the de/encoders to do the reordering, but I am against this remapping via filter, it is IMO immensely complex and there are already without it some bugs in filter chain building (like command line parameters lost on reinit). I agree with whoever suggested on ffmpeg-devel that audio should preferably use a planar format, which each channel in a separate memory area. Although this will be a major effort, this allows reodering without any speed loss and for most decoders is closer to their original output anyway (AFAICT). Greetings, Reimar Döffinger
Reimar Döffinger wrote:
Hello, On Wed, Nov 08, 2006 at 10:00:47PM -0800, Corey Hickey wrote:
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.
I am fine with changing the de/encoders to do the reordering, but I am against this remapping via filter, it is IMO immensely complex and there are already without it some bugs in filter chain building (like command line parameters lost on reinit).
There were some other patches posted which did the reordering in each individual decoder/encoder, and, assuming the ones I tested were representative, that approach was somewhat slower than using af_channels. For example, to quote myself from an earlier mail: ======================================================================== I tested on a 6-channel wav file made from The Matrix DVD. It is 8177.3 seconds long. Each number is the user-time average of three tests, but there really wasn't much variation. The command is: $ time mplayer -ao pcm:fast:file=/dev/null -channels 6 audiodump.wav method user time % of play time ------------------------------------------------------------ original (incorrect channels): 9.099s 0.11127% using af_channels to remap: 15.057s 0.18413% reading into each channel: 30.296s 0.37048% ======================================================================== There may have been better ways to handle the reordering at the decoder; I wouldn't know. The other advantage of using af_channels is that adding remapping support to decoders/encoders is very easy, since the central code handles the actual reordering.
I agree with whoever suggested on ffmpeg-devel that audio should preferably use a planar format, which each channel in a separate memory area. Although this will be a major effort, this allows reodering without any speed loss and for most decoders is closer to their original output anyway (AFAICT).
You're probably right, and, if a patch to do that were available, it would probably be the better choice. If I had any expertise in that area, I would try it myself, but, alas, I don't. So, I think the choices are: (a) Use my patch, which is already written and runs reasonably quickly. (b) Use the patches for reordering in each encoder/decoder, which are already written and run almost as quickly. (c) Leave the bug alone and hope it motivates someone to spend a lot of time making a better solution. If anyone steps forward and says they'll try to implement your suggestions in the near future, then I have no objections to (c). Otherwise, I think we ought to apply what we have, since this is a user-visible bug that people ask about once in a while. I prefer (a), as I explained earlier, but I won't be militant about it you have good reasons to prefer (b). Either way, I'll be offline for several days starting this afternoon, so don't misinterpret my lack of responses. -Corey
Hello, On Thu, Nov 09, 2006 at 11:10:51AM -0800, Corey Hickey wrote: [...]
(a) Use my patch, which is already written and runs reasonably quickly. (b) Use the patches for reordering in each encoder/decoder, which are already written and run almost as quickly. (c) Leave the bug alone and hope it motivates someone to spend a lot of time making a better solution.
If anyone steps forward and says they'll try to implement your suggestions in the near future, then I have no objections to (c). Otherwise, I think we ought to apply what we have, since this is a user-visible bug that people ask about once in a while. I prefer (a), as I explained earlier, but I won't be militant about it you have good reasons to prefer (b).
Does anyone else have objections? I do dislike the significant added complexity for af.c of solution (a), though I did not check if it can be simplified. If it can't I'd prefer (b). Greetings, Reimar Döffinger
Reimar Döffinger wrote:
Hello, On Thu, Nov 09, 2006 at 11:10:51AM -0800, Corey Hickey wrote: [...]
(a) Use my patch, which is already written and runs reasonably quickly. (b) Use the patches for reordering in each encoder/decoder, which are already written and run almost as quickly. (c) Leave the bug alone and hope it motivates someone to spend a lot of time making a better solution.
If anyone steps forward and says they'll try to implement your suggestions in the near future, then I have no objections to (c). Otherwise, I think we ought to apply what we have, since this is a user-visible bug that people ask about once in a while. I prefer (a), as I explained earlier, but I won't be militant about it you have good reasons to prefer (b).
Does anyone else have objections? I do dislike the significant added complexity for af.c of solution (a), though I did not check if it can be simplified. If it can't I'd prefer (b).
I don't know if it can be simplified. I see a couple things that could be different, but not necessarily better. In any case, the complexity of (a) is centralized, versus being distributed into many separate files (and only slightly lessened in most cases). Adding support to an ad/ae/ao is much easier with my approach. Have you looked at the patches for (b)? I don't seem to have provided a link earlier. http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2006-October/046993.html Thanks, Corey
Hello, On Tue, Nov 14, 2006 at 04:27:59PM -0800, Corey Hickey wrote:
I don't know if it can be simplified. I see a couple things that could be different, but not necessarily better. In any case, the complexity of (a) is centralized, versus being distributed into many separate files (and only slightly lessened in most cases). Adding support to an ad/ae/ao is much easier with my approach.
well, the complexity maybe centralized with the af approach, but it is also far away from the real problem, probably making it even harder to make a real fix. I don't know. I think I mostly dislike inserting a filter, and that the fix seems so far away from the problem. I wonder how complicated it would be to instead create a reorder function that the ao/ad/aes can use. Or at least in some other was encapsulate that new code in a separate function instead of being "hidden" deep in the bowels of filter chain creation.
Have you looked at the patches for (b)? I don't seem to have provided a link earlier. http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2006-October/046993.html
Yes, I did have a look. I tend towards slightly preferring them, but I have not properly reviewed either. Greetings, Reimar Döffinger
Corey Hickey wrote:
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.
Here's an updated version of this patch in case it is of use. -Corey Index: libmpcodecs/ae_faac.c =================================================================== --- libmpcodecs/ae_faac.c (revision 25055) +++ libmpcodecs/ae_faac.c (working copy) @@ -69,6 +69,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 25055) +++ libmpcodecs/ae_pcm.c (working copy) @@ -28,6 +28,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 25055) +++ 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 25055) +++ libmpcodecs/ad_faad.c (working copy) @@ -151,6 +151,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 25055) +++ libmpcodecs/ad_dmo.c (working copy) @@ -38,6 +38,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 25055) +++ libmpcodecs/dec_audio.c (working copy) @@ -335,6 +335,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, Index: libmpcodecs/ae.h =================================================================== --- libmpcodecs/ae.h (revision 25055) +++ 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 25055) +++ libmpcodecs/ae_lavc.c (working copy) @@ -46,6 +46,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 25055) +++ libao2/ao_pcm.c (working copy) @@ -106,6 +106,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.riff = le2me_32(WAV_ID_RIFF); wavhdr.wave = le2me_32(WAV_ID_WAVE); Index: libao2/audio_out.h =================================================================== --- libao2/audio_out.h (revision 25055) +++ 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 25055) +++ DOCS/man/en/mplayer.1 (working copy) @@ -1457,6 +1457,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 25055) +++ mplayer.c (working copy) @@ -304,6 +304,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; @@ -1224,6 +1225,8 @@ 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, &ao_data->samplerate, &ao_data->channels, &ao_data->format); mpctx->mixer.afilter = sh_audio->afilter; @@ -1505,6 +1508,8 @@ ao_data.format=audio_output_format; #if 1 // first init to detect best values + if (!remap_channels) + mpctx->sh_audio->input_chan_map = mpctx->sh_audio->output_chan_map = NULL; if(!init_audio_filters(mpctx->sh_audio, // preliminary init // input: mpctx->sh_audio->samplerate, @@ -1541,6 +1546,7 @@ // init audio filters: #if 1 current_module="af_init"; + mpctx->sh_audio->output_chan_map = ao_data.output_chan_map; if(!build_afilter_chain(mpctx->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 25055) +++ cfg-common.h (working copy) @@ -173,6 +173,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 25055) +++ libmpdemux/stheader.h (working copy) @@ -18,6 +18,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 25055) +++ mencoder.c (working copy) @@ -169,6 +169,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; @@ -407,7 +408,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; @@ -914,6 +915,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(!init_audio_filters(sh_audio, // input: new_srate, @@ -930,6 +933,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, &aparams.sample_rate, &aparams.channels, &aencoder->input_format)) { Index: libaf/af.c =================================================================== --- libaf/af.c (revision 25055) +++ libaf/af.c (working copy) @@ -361,12 +361,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; @@ -374,6 +374,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; @@ -606,3 +620,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 25055) +++ libaf/af.h (working copy) @@ -100,6 +100,8 @@ af_data_t output; // Configuration for this stream af_cfg_t cfg; + char *input_chan_map; + char *output_chan_map; }af_stream_t; /********************************************* @@ -295,6 +297,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.
On Fri, Nov 16, 2007 at 10:46:57PM -0800, Corey Hickey wrote:
Corey Hickey wrote:
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.
Here's an updated version of this patch in case it is of use.
-Corey
[snip]
--- libmpcodecs/ae_lavc.c (revision 25055) +++ libmpcodecs/ae_lavc.c (working copy) @@ -46,6 +46,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
This part needs an update. It is now possible to encode aac audio with libavcodec and the reorder is different depending if audio codec is ac3 or aac. Ciao Giacomo
participants (3)
-
Corey Hickey -
Giacomo Comes -
Reimar Döffinger