diff options
Diffstat (limited to 'audio/decode/dec_audio.c')
-rw-r--r-- | audio/decode/dec_audio.c | 188 |
1 files changed, 87 insertions, 101 deletions
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c index e381a12a3c..19b5d8bdeb 100644 --- a/audio/decode/dec_audio.c +++ b/audio/decode/dec_audio.c @@ -38,6 +38,8 @@ #include "dec_audio.h" #include "ad.h" #include "audio/format.h" +#include "audio/audio.h" +#include "audio/audio_buffer.h" #include "audio/filter/af.h" @@ -54,31 +56,29 @@ static const struct ad_functions * const ad_drivers[] = { NULL }; +// At least ad_mpg123 needs to be able to decode this many samples at once +#define DECODE_MAX_UNIT 1152 + +// At least 8192 samples, plus hack for ad_mpg123 +#define DECODE_BUFFER_SAMPLES (8192 + DECODE_MAX_UNIT) + +// Drop audio buffer and reinit it (after format change) +static void reinit_audio_buffer(sh_audio_t *sh) +{ + mp_audio_buffer_reinit_fmt(sh->decode_buffer, sh->sample_format, + &sh->channels, sh->samplerate); + mp_audio_buffer_preallocate_min(sh->decode_buffer, DECODE_BUFFER_SAMPLES); +} + static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder) { assert(!sh_audio->initialized); resync_audio_stream(sh_audio); - sh_audio->sample_format = AF_FORMAT_FLOAT_NE; - sh_audio->audio_out_minsize = 8192; // default, preinit() may change it if (!sh_audio->ad_driver->preinit(sh_audio)) { mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder preinit failed.\n"); return 0; } - const int base_size = 65536; - // At least 64 KiB plus rounding up to next decodable unit size - sh_audio->a_buffer_size = base_size + sh_audio->audio_out_minsize; - - mp_tmsg(MSGT_DECAUDIO, MSGL_V, - "dec_audio: Allocating %d + %d = %d bytes for output buffer.\n", - sh_audio->audio_out_minsize, base_size, - sh_audio->a_buffer_size); - - sh_audio->a_buffer = av_mallocz(sh_audio->a_buffer_size); - if (!sh_audio->a_buffer) - abort(); - sh_audio->a_buffer_len = 0; - if (!sh_audio->ad_driver->init(sh_audio, decoder)) { mp_tmsg(MSGT_DECAUDIO, MSGL_V, "Audio decoder init failed.\n"); uninit_audio(sh_audio); // free buffers @@ -87,13 +87,18 @@ static int init_audio_codec(sh_audio_t *sh_audio, const char *decoder) sh_audio->initialized = 1; - if (mp_chmap_is_empty(&sh_audio->channels) || !sh_audio->samplerate) { + if (mp_chmap_is_empty(&sh_audio->channels) || !sh_audio->samplerate || + !sh_audio->sample_format) + { mp_tmsg(MSGT_DECAUDIO, MSGL_ERR, "Audio decoder did not specify " "audio format!\n"); uninit_audio(sh_audio); // free buffers return 0; } + sh_audio->decode_buffer = mp_audio_buffer_create(NULL); + reinit_audio_buffer(sh_audio); + return 1; } @@ -187,7 +192,8 @@ void uninit_audio(sh_audio_t *sh_audio) } talloc_free(sh_audio->gsh->decoder_desc); sh_audio->gsh->decoder_desc = NULL; - av_freep(&sh_audio->a_buffer); + talloc_free(sh_audio->decode_buffer); + sh_audio->decode_buffer = NULL; } @@ -230,102 +236,91 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate, return 1; } -static void set_min_out_buffer_size(struct bstr *outbuf, int len) -{ - size_t oldlen = talloc_get_size(outbuf->start); - if (oldlen < len) { - assert(outbuf->start); // talloc context should be already set - mp_msg(MSGT_DECAUDIO, MSGL_V, "Increasing filtered audio buffer size " - "from %zd to %d\n", oldlen, len); - outbuf->start = talloc_realloc_size(NULL, outbuf->start, len); - } -} - -static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len) +// Filter len bytes of input, put result into outbuf. +static int filter_n_bytes(sh_audio_t *sh, struct mp_audio_buffer *outbuf, + int len) { - assert(len - 1 + sh->audio_out_minsize <= sh->a_buffer_size); - int error = 0; - // Decode more bytes if needed - int old_samplerate = sh->samplerate; - struct mp_chmap 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); - int format_change = sh->samplerate != old_samplerate - || !mp_chmap_equals(&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; + struct mp_audio config; + mp_audio_buffer_get_format(sh->decode_buffer, &config); + + while (mp_audio_buffer_samples(sh->decode_buffer) < len) { + int maxlen = mp_audio_buffer_get_write_available(sh->decode_buffer); + if (maxlen < DECODE_MAX_UNIT) + break; + struct mp_audio buffer; + mp_audio_buffer_get_write_buffer(sh->decode_buffer, maxlen, &buffer); + buffer.samples = 0; + error = sh->ad_driver->decode_audio(sh, &buffer, maxlen); + if (error < 0) + break; + // Commit the data just read as valid data + mp_audio_buffer_finish_write(sh->decode_buffer, buffer.samples); + // Format change + if (sh->samplerate != config.rate || + !mp_chmap_equals(&sh->channels, &config.channels) || + sh->sample_format != config.format) + { + // If there are still samples left in the buffer, let them drain + // first, and don't signal a format change to the caller yet. + if (mp_audio_buffer_samples(sh->decode_buffer) > 0) + break; + reinit_audio_buffer(sh); + error = -2; break; } - sh->a_buffer_len += ret; } // Filter - struct mp_audio filter_input = { - .audio = sh->a_buffer, - .len = len, - .rate = sh->samplerate, - }; - mp_audio_set_format(&filter_input, sh->sample_format); - mp_audio_set_channels(&filter_input, &sh->channels); + struct mp_audio filter_input; + mp_audio_buffer_peek(sh->decode_buffer, &filter_input); + filter_input.rate = sh->afilter->input.rate; // due to playback speed change + len = MPMIN(filter_input.samples, len); + filter_input.samples = len; struct mp_audio *filter_output = af_play(sh->afilter, &filter_input); if (!filter_output) return -1; - set_min_out_buffer_size(outbuf, outbuf->len + filter_output->len); - memcpy(outbuf->start + outbuf->len, filter_output->audio, - filter_output->len); - outbuf->len += filter_output->len; + mp_audio_buffer_append(outbuf, filter_output); // remove processed data from decoder buffer: - sh->a_buffer_len -= len; - memmove(sh->a_buffer, sh->a_buffer + len, sh->a_buffer_len); + mp_audio_buffer_skip(sh->decode_buffer, len); return error; } -/* Try to get at least minlen decoded+filtered bytes in outbuf +/* Try to get at least minsamples decoded+filtered samples in outbuf * (total length including possible existing data). * Return 0 on success, -1 on error/EOF (not distinguished). - * In the former case outbuf->len is always >= minlen on return. - * In case of EOF/error it might or might not be. - * Outbuf.start must be talloc-allocated, and will be reallocated - * if needed to fit all filter output. */ -int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen) + * In the former case outbuf has at least minsamples buffered on return. + * In case of EOF/error it might or might not be. */ +int decode_audio(sh_audio_t *sh_audio, struct mp_audio_buffer *outbuf, + int minsamples) { // Indicates that a filter seems to be buffering large amounts of data int huge_filter_buffer = 0; - // Decoded audio must be cut at boundaries of this many bytes - int bps = af_fmt2bits(sh_audio->sample_format) / 8; - int unitsize = sh_audio->channels.num * bps * 16; + // Decoded audio must be cut at boundaries of this many samples + // (Note: the reason for this is unknown, possibly a refactoring artifact) + int unitsize = 16; /* Filter output size will be about filter_multiplier times input size. * If some filter buffers audio in big blocks this might only hold * as average over time. */ double filter_multiplier = af_calc_filter_multiplier(sh_audio->afilter); - /* If the decoder set audio_out_minsize then it can do the equivalent of - * "while (output_len < target_len) output_len += audio_out_minsize;", - * so we must guarantee there is at least audio_out_minsize-1 bytes - * more space in the output buffer than the minimum length we try to - * decode. */ - int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize; - if (!unitsize) - return -1; - max_decode_len -= max_decode_len % unitsize; + int prev_buffered = -1; + while (minsamples >= 0) { + int buffered = mp_audio_buffer_samples(outbuf); + if (minsamples < buffered || buffered == prev_buffered) + break; + prev_buffered = buffered; - while (minlen >= 0 && outbuf->len < minlen) { + int decsamples = (minsamples - buffered) / filter_multiplier; // + some extra for possible filter buffering - int declen = (minlen - outbuf->len) / filter_multiplier + (unitsize << 5); - if (huge_filter_buffer) + decsamples += 1 << unitsize; + + if (huge_filter_buffer) { /* Some filter must be doing significant buffering if the estimated * input length didn't produce enough output from filters. * Feed the filters 2k bytes at a time until we have enough output. @@ -334,33 +329,24 @@ int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen) * to get audio data and buffer video frames in memory while doing * so. However the performance impact of either is probably not too * significant as long as the value is not completely insane. */ - declen = 2000; - declen -= declen % unitsize; - if (declen > max_decode_len) - declen = max_decode_len; - else - /* if this iteration does not fill buffer, we must have lots - * of buffering in filters */ - huge_filter_buffer = 1; - int res = filter_n_bytes(sh_audio, outbuf, declen); + decsamples = 2000; + } + + /* if this iteration does not fill buffer, we must have lots + * of buffering in filters */ + huge_filter_buffer = 1; + + int res = filter_n_bytes(sh_audio, outbuf, decsamples); if (res < 0) return res; } return 0; } -void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte) -{ - set_min_out_buffer_size(outbuf, outbuf->len + count); - memmove(outbuf->start + count, outbuf->start, outbuf->len); - memset(outbuf->start, byte, count); - outbuf->len += count; -} - - void resync_audio_stream(sh_audio_t *sh_audio) { sh_audio->pts = MP_NOPTS_VALUE; + sh_audio->pts_offset = 0; if (!sh_audio->initialized) return; sh_audio->ad_driver->control(sh_audio, ADCTRL_RESYNC_STREAM, NULL); |