From 5ed772b9cddc4c0de6762e223428b3e36eceefff Mon Sep 17 00:00:00 2001 From: reimar Date: Thu, 15 Jul 2010 17:59:46 +0000 Subject: [PATCH] audio: support parameter changes (e.g. channel count) during playback Add support for parameter changes (e.g. channel count) during playback. This makes decoding AC3 files that switch between 2 and 6 channels work reasonably well even with -channels 6 and ffac3 decoder. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@31737 b3059339-0415-0410-9bf9-f77b7e298cf2 Fix typo in error message: ACC -> AAC git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32473 b3059339-0415-0410-9bf9-f77b7e298cf2 Avoid printing AAC with SBR warning on every decode call, instead print it only after every decoder reconfiguration. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@32476 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpcodecs/ad_ffmpeg.c | 70 ++++++++++++++++++++++---------- libmpcodecs/dec_audio.c | 16 ++++++-- mplayer.c | 105 ++++++++++++++++++++++++++++-------------------- 3 files changed, 123 insertions(+), 68 deletions(-) diff --git a/libmpcodecs/ad_ffmpeg.c b/libmpcodecs/ad_ffmpeg.c index 8578314bc6..6d27a314f6 100644 --- a/libmpcodecs/ad_ffmpeg.c +++ b/libmpcodecs/ad_ffmpeg.c @@ -52,6 +52,44 @@ static int preinit(sh_audio_t *sh) return 1; } +static int setup_format(sh_audio_t *sh_audio, const AVCodecContext *lavc_context) +{ + int broken_srate = 0; + int samplerate = lavc_context->sample_rate; + int sample_format = sh_audio->sample_format; + switch (lavc_context->sample_fmt) { + case SAMPLE_FMT_U8: sample_format = AF_FORMAT_U8; break; + case SAMPLE_FMT_S16: sample_format = AF_FORMAT_S16_NE; break; + case SAMPLE_FMT_S32: sample_format = AF_FORMAT_S32_NE; break; + case SAMPLE_FMT_FLT: sample_format = AF_FORMAT_FLOAT_NE; break; + default: + mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n"); + } + if(sh_audio->wf){ + // If the decoder uses the wrong number of channels all is lost anyway. + // sh_audio->channels=sh_audio->wf->nChannels; + + if (lavc_context->codec_id == CODEC_ID_AAC && + samplerate == 2*sh_audio->wf->nSamplesPerSec) { + broken_srate = 1; + } else if (sh_audio->wf->nSamplesPerSec) + samplerate=sh_audio->wf->nSamplesPerSec; + } + if (lavc_context->channels != sh_audio->channels || + samplerate != sh_audio->samplerate || + sample_format != sh_audio->sample_format) { + sh_audio->channels=lavc_context->channels; + sh_audio->samplerate=samplerate; + sh_audio->sample_format = sample_format; + sh_audio->samplesize=af_fmt2bits(sh_audio->sample_format)/ 8; + if (broken_srate) + mp_msg(MSGT_DECAUDIO, MSGL_WARN, + "Ignoring broken container sample rate for AAC with SBR\n"); + return 1; + } + return 0; +} + static int init(sh_audio_t *sh_audio) { struct MPOpts *opts = sh_audio->opts; @@ -133,32 +171,19 @@ static int init(sh_audio_t *sh_audio) } while (x <= 0 && tries++ < 5); if(x>0) sh_audio->a_buffer_len=x; - sh_audio->channels=lavc_context->channels; - sh_audio->samplerate=lavc_context->sample_rate; sh_audio->i_bps=lavc_context->bit_rate/8; + if (sh_audio->wf && sh_audio->wf->nAvgBytesPerSec) + sh_audio->i_bps=sh_audio->wf->nAvgBytesPerSec; + switch (lavc_context->sample_fmt) { - case SAMPLE_FMT_U8: sh_audio->sample_format = AF_FORMAT_U8; break; - case SAMPLE_FMT_S16: sh_audio->sample_format = AF_FORMAT_S16_NE; break; - case SAMPLE_FMT_S32: sh_audio->sample_format = AF_FORMAT_S32_NE; break; - case SAMPLE_FMT_FLT: sh_audio->sample_format = AF_FORMAT_FLOAT_NE; break; + case SAMPLE_FMT_U8: + case SAMPLE_FMT_S16: + case SAMPLE_FMT_S32: + case SAMPLE_FMT_FLT: + break; default: - mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Unsupported sample format\n"); return 0; } - /* If the audio is AAC the container level data may be unreliable - * because of SBR handling problems (possibly half real sample rate at - * container level). Default AAC decoding with ad_faad has used codec-level - * values for a long time without generating complaints so it should be OK. - */ - if (sh_audio->wf && lavc_context->codec_id != CODEC_ID_AAC) { - // If the decoder uses the wrong number of channels all is lost anyway. - // sh_audio->channels=sh_audio->wf->nChannels; - if (sh_audio->wf->nSamplesPerSec) - sh_audio->samplerate=sh_audio->wf->nSamplesPerSec; - if (sh_audio->wf->nAvgBytesPerSec) - sh_audio->i_bps=sh_audio->wf->nAvgBytesPerSec; - } - sh_audio->samplesize=af_fmt2bits(sh_audio->sample_format)/ 8; return 1; } @@ -232,6 +257,9 @@ static int decode_audio(sh_audio_t *sh_audio,unsigned char *buf,int minlen,int m sh_audio->pts_bytes += len2; } mp_dbg(MSGT_DECAUDIO,MSGL_DBG2,"Decoded %d -> %d \n",y,len2); + + if (setup_format(sh_audio, sh_audio->context)) + break; } return len; } diff --git a/libmpcodecs/dec_audio.c b/libmpcodecs/dec_audio.c index 218203272d..ade2429dc0 100644 --- a/libmpcodecs/dec_audio.c +++ b/libmpcodecs/dec_audio.c @@ -378,13 +378,20 @@ static int filter_n_bytes(sh_audio_t *sh, int len) int error = 0; // Decode more bytes if needed + int old_samplerate = sh->samplerate; + int old_channels = sh->channels; + int old_sample_format = sh->sample_format; while (sh->a_buffer_len < len) { unsigned char *buf = sh->a_buffer + sh->a_buffer_len; int minlen = len - sh->a_buffer_len; int maxlen = sh->a_buffer_size - sh->a_buffer_len; int ret = sh->ad_driver->decode_audio(sh, buf, minlen, maxlen); - if (ret <= 0) { - error = -1; + int format_change = sh->samplerate != old_samplerate + || sh->channels != old_channels + || sh->sample_format != old_sample_format; + if (ret <= 0 || format_change) { + error = format_change ? -2 : -1; + // samples from format-changing call get discarded too len = sh->a_buffer_len; break; } @@ -467,8 +474,9 @@ int decode_audio(sh_audio_t *sh_audio, int minlen) /* if this iteration does not fill buffer, we must have lots * of buffering in filters */ huge_filter_buffer = 1; - if (filter_n_bytes(sh_audio, declen) < 0) - return -1; + int res = filter_n_bytes(sh_audio, declen); + if (res < 0) + return res; } return 0; } diff --git a/mplayer.c b/mplayer.c index 6e664e164c..87c6d7b0a2 100644 --- a/mplayer.c +++ b/mplayer.c @@ -1764,49 +1764,54 @@ void reinit_audio_chain(struct MPContext *mpctx) struct MPOpts *opts = &mpctx->opts; if (!mpctx->sh_audio) return; - current_module="init_audio_codec"; - mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); - if(!init_best_audio_codec(mpctx->sh_audio,audio_codec_list,audio_fm_list)){ - goto init_error; - } - mpctx->initialized_flags|=INITIALIZED_ACODEC; - mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); - - - current_module="af_preinit"; - ao_data.samplerate=force_srate; - ao_data.channels=0; - ao_data.format=audio_output_format; - // first init to detect best values - if(!init_audio_filters(mpctx->sh_audio, // preliminary init - // input: - mpctx->sh_audio->samplerate, - // output: - &ao_data.samplerate, &ao_data.channels, &ao_data.format)){ - mp_tmsg(MSGT_CPLAYER,MSGL_ERR, "Error at audio filter chain " - "pre-init!\n"); - exit_player(mpctx, EXIT_ERROR); + if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) { + current_module="init_audio_codec"; + mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); + if(!init_best_audio_codec(mpctx->sh_audio,audio_codec_list,audio_fm_list)){ + goto init_error; + } + mpctx->initialized_flags|=INITIALIZED_ACODEC; + mp_msg(MSGT_CPLAYER,MSGL_INFO,"==========================================================================\n"); } - current_module="ao2_init"; - mpctx->audio_out = init_best_audio_out(opts->audio_driver_list, - 0, // plugin flag - ao_data.samplerate, - ao_data.channels, - ao_data.format, 0); - if(!mpctx->audio_out){ - mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Could not open/initialize audio device -> no sound.\n"); - goto init_error; + + + if (!(mpctx->initialized_flags & INITIALIZED_AO)) { + current_module="af_preinit"; + ao_data.samplerate=force_srate; + ao_data.channels=0; + ao_data.format=audio_output_format; + // first init to detect best values + if(!init_audio_filters(mpctx->sh_audio, // preliminary init + // input: + mpctx->sh_audio->samplerate, + // output: + &ao_data.samplerate, &ao_data.channels, &ao_data.format)){ + mp_tmsg(MSGT_CPLAYER,MSGL_ERR, "Error at audio filter chain " + "pre-init!\n"); + exit_player(mpctx, EXIT_ERROR); + } + current_module="ao2_init"; + mpctx->audio_out = init_best_audio_out(opts->audio_driver_list, + 0, // plugin flag + ao_data.samplerate, + ao_data.channels, + ao_data.format, 0); + if(!mpctx->audio_out){ + mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Could not open/initialize audio device -> no sound.\n"); + goto init_error; + } + mpctx->initialized_flags|=INITIALIZED_AO; + mp_msg(MSGT_CPLAYER,MSGL_INFO,"AO: [%s] %dHz %dch %s (%d bytes per sample)\n", + mpctx->audio_out->info->short_name, + ao_data.samplerate, ao_data.channels, + af_fmt2str_short(ao_data.format), + af_fmt2bits(ao_data.format)/8 ); + mp_msg(MSGT_CPLAYER,MSGL_V,"AO: Description: %s\nAO: Author: %s\n", + mpctx->audio_out->info->name, mpctx->audio_out->info->author); + if(strlen(mpctx->audio_out->info->comment) > 0) + mp_msg(MSGT_CPLAYER,MSGL_V,"AO: Comment: %s\n", mpctx->audio_out->info->comment); } - mpctx->initialized_flags|=INITIALIZED_AO; - mp_msg(MSGT_CPLAYER,MSGL_INFO,"AO: [%s] %dHz %dch %s (%d bytes per sample)\n", - mpctx->audio_out->info->short_name, - ao_data.samplerate, ao_data.channels, - af_fmt2str_short(ao_data.format), - af_fmt2bits(ao_data.format)/8 ); - mp_msg(MSGT_CPLAYER,MSGL_V,"AO: Description: %s\nAO: Author: %s\n", - mpctx->audio_out->info->name, mpctx->audio_out->info->author); - if(strlen(mpctx->audio_out->info->comment) > 0) - mp_msg(MSGT_CPLAYER,MSGL_V,"AO: Comment: %s\n", mpctx->audio_out->info->comment); + // init audio filters: current_module="af_init"; if(!build_afilter_chain(mpctx, mpctx->sh_audio, &ao_data)) { @@ -2167,6 +2172,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) int playsize; int playflags=0; int audio_eof=0; + bool format_change = false; sh_audio_t * const sh_audio = mpctx->sh_audio; current_module="play_audio"; @@ -2192,12 +2198,16 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) // Fill buffer if needed: current_module="decode_audio"; t = GetTimer(); - if (decode_audio(sh_audio, playsize) < 0) // EOF or error - if (mpctx->d_audio->eof) { + int res = decode_audio(sh_audio, playsize); + if (res < 0) { // EOF, error or format change + if (res == -2) + format_change = true; + else if (mpctx->d_audio->eof) { audio_eof = 1; if (sh_audio->a_out_buffer_len == 0) return 0; } + } t = GetTimer() - t; tt = t*0.000001f; audio_time_usage+=tt; if (playsize > sh_audio->a_out_buffer_len) { @@ -2230,6 +2240,15 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) sh_audio->a_out_buffer_len = 0; } + /* The format change isn't handled too gracefully. A more precise + * implementation would require draining buffered old-format audio + * while displaying video, then doing the output format switch. + */ + if (format_change) { + uninit_player(mpctx, INITIALIZED_AO); + reinit_audio_chain(mpctx); + } + return 1; } -- 2.11.4.GIT