mirror of
https://github.com/mpv-player/mpv
synced 2025-02-18 05:37:04 +00:00
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
This commit is contained in:
parent
6be6ba4094
commit
5ed772b9cd
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
101
mplayer.c
101
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;
|
||||
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");
|
||||
}
|
||||
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_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);
|
||||
}
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user