diff options
author | 2011-07-02 09:22:32 +0300 | |
---|---|---|
committer | 2011-07-02 09:22:32 +0300 | |
commit | c8b3088c1831ab5f698924ce75127caa55b50dbb (patch) | |
tree | 12ca3b7d7b53ff9458b80f2c83de2cf29d522862 | |
parent | 746f9b004038e09392b69abb3ba26b1cc32a90d7 (diff) |
audio: move ready-for-ao data buffer from decoder to AO
Move the buffer storing audio data ready to be fed to the audio output
driver from the audio decoder object to the AO object. This will help
encoding code deal with end of input, and may also be useful to
improve other general gapless audio behavior (as AOs which do not
accept chunks smaller than a certain size may keep them in the buffer
while the decoder changes).
Less data may be dropped now when changing audio filters or switching
timeline parts.
-rw-r--r-- | libao2/audio_out.c | 6 | ||||
-rw-r--r-- | libao2/audio_out.h | 4 | ||||
-rw-r--r-- | libmpcodecs/dec_audio.c | 58 | ||||
-rw-r--r-- | libmpcodecs/dec_audio.h | 6 | ||||
-rw-r--r-- | libmpdemux/stheader.h | 5 | ||||
-rw-r--r-- | mplayer.c | 84 |
6 files changed, 77 insertions, 86 deletions
diff --git a/libao2/audio_out.c b/libao2/audio_out.c index 14cab9b285..bd06c8fd5f 100644 --- a/libao2/audio_out.c +++ b/libao2/audio_out.c @@ -221,8 +221,12 @@ void ao_init(struct ao *ao, char **ao_list) void ao_uninit(struct ao *ao, bool cut_audio) { + assert(ao->buffer.len >= ao->buffer_playable_size); + ao->buffer.len = ao->buffer_playable_size; if (ao->initialized) ao->driver->uninit(ao, cut_audio); + if (!cut_audio && ao->buffer.len) + mp_msg(MSGT_AO, MSGL_WARN, "Audio output truncated at end.\n"); talloc_free(ao); } @@ -254,6 +258,8 @@ int ao_get_space(struct ao *ao) void ao_reset(struct ao *ao) { + ao->buffer.len = 0; + ao->buffer_playable_size = 0; if (ao->driver->reset) ao->driver->reset(ao); } diff --git a/libao2/audio_out.h b/libao2/audio_out.h index fb62923297..628b9c3ae7 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -21,6 +21,8 @@ #include <stdbool.h> +#include "bstr.h" + typedef struct ao_info { /* driver name ("Matrox Millennium G200/G400" */ const char *name; @@ -71,6 +73,8 @@ struct ao { int outburst; int buffersize; int pts; + struct bstr buffer; + int buffer_playable_size; bool initialized; bool untimed; const struct ao_driver *driver; diff --git a/libmpcodecs/dec_audio.c b/libmpcodecs/dec_audio.c index 00c66287ed..0541947f60 100644 --- a/libmpcodecs/dec_audio.c +++ b/libmpcodecs/dec_audio.c @@ -23,6 +23,7 @@ #include "config.h" #include "mp_msg.h" +#include "bstr.h" #include "stream/stream.h" #include "libmpdemux/demuxer.h" @@ -134,10 +135,6 @@ static int init_audio_codec(sh_audio_t *sh_audio) "ID_AUDIO_BITRATE=%d\nID_AUDIO_RATE=%d\n" "ID_AUDIO_NCH=%d\n", sh_audio->i_bps * 8, sh_audio->samplerate, sh_audio->channels); - sh_audio->a_out_buffer_size = 0; - sh_audio->a_out_buffer = NULL; - sh_audio->a_out_buffer_len = 0; - return 1; } @@ -317,9 +314,6 @@ void uninit_audio(sh_audio_t *sh_audio) #endif sh_audio->initialized = 0; } - free(sh_audio->a_out_buffer); - sh_audio->a_out_buffer = NULL; - sh_audio->a_out_buffer_size = 0; av_freep(&sh_audio->a_buffer); av_freep(&sh_audio->a_in_buffer); } @@ -364,24 +358,23 @@ int init_audio_filters(sh_audio_t *sh_audio, int in_samplerate, *out_channels = afs->output.nch; *out_format = afs->output.format; - sh_audio->a_out_buffer_len = 0; - // ok! sh_audio->afilter = (void *) afs; return 1; } -static void set_min_out_buffer_size(struct sh_audio *sh, int len) +static void set_min_out_buffer_size(struct bstr *outbuf, int len) { - if (sh->a_out_buffer_size < 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 %d to %d\n", sh->a_out_buffer_size, len); - sh->a_out_buffer = realloc(sh->a_out_buffer, len); - sh->a_out_buffer_size = len; + "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, int len) +static int filter_n_bytes(sh_audio_t *sh, struct bstr *outbuf, int len) { assert(len-1 + sh->audio_out_minsize <= sh->a_buffer_size); @@ -420,10 +413,10 @@ static int filter_n_bytes(sh_audio_t *sh, int len) af_data_t *filter_output = af_play(sh->afilter, &filter_input); if (!filter_output) return -1; - set_min_out_buffer_size(sh, sh->a_out_buffer_len + filter_output->len); - memcpy(sh->a_out_buffer + sh->a_out_buffer_len, filter_output->audio, - filter_output->len); - sh->a_out_buffer_len += filter_output->len; + 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; // remove processed data from decoder buffer: sh->a_buffer_len -= len; @@ -432,13 +425,14 @@ static int filter_n_bytes(sh_audio_t *sh, int len) return error; } -/* Try to get at least minlen decoded+filtered bytes in sh_audio->a_out_buffer +/* Try to get at least minlen decoded+filtered bytes in outbuf * (total length including possible existing data). * Return 0 on success, -1 on error/EOF (not distinguished). - * In the former case sh_audio->a_out_buffer_len is always >= minlen - * on return. In case of EOF/error it might or might not be. - * Can reallocate sh_audio->a_out_buffer if needed to fit all filter output. */ -int decode_audio(sh_audio_t *sh_audio, int minlen) + * 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) { // Indicates that a filter seems to be buffering large amounts of data int huge_filter_buffer = 0; @@ -458,8 +452,8 @@ int decode_audio(sh_audio_t *sh_audio, int minlen) int max_decode_len = sh_audio->a_buffer_size - sh_audio->audio_out_minsize; max_decode_len -= max_decode_len % unitsize; - while (sh_audio->a_out_buffer_len < minlen) { - int declen = (minlen - sh_audio->a_out_buffer_len) / filter_multiplier + while (outbuf->len < minlen) { + int declen = (minlen - outbuf->len) / filter_multiplier + (unitsize << 5); // some extra for possible filter buffering if (huge_filter_buffer) /* Some filter must be doing significant buffering if the estimated @@ -478,19 +472,19 @@ 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; - int res = filter_n_bytes(sh_audio, declen); + int res = filter_n_bytes(sh_audio, outbuf, declen); if (res < 0) return res; } return 0; } -void decode_audio_prepend_bytes(struct sh_audio *sh, int count, int byte) +void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte) { - set_min_out_buffer_size(sh, sh->a_out_buffer_len + count); - memmove(sh->a_out_buffer + count, sh->a_out_buffer, sh->a_out_buffer_len); - memset(sh->a_out_buffer, byte, count); - sh->a_out_buffer_len += count; + 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; } diff --git a/libmpcodecs/dec_audio.h b/libmpcodecs/dec_audio.h index c2b92c9818..0d4baf0666 100644 --- a/libmpcodecs/dec_audio.h +++ b/libmpcodecs/dec_audio.h @@ -21,11 +21,13 @@ #include "libmpdemux/stheader.h" +struct bstr; + // dec_audio.c: void afm_help(void); int init_best_audio_codec(sh_audio_t *sh_audio, char** audio_codec_list, char** audio_fm_list); -int decode_audio(sh_audio_t *sh_audio, int minlen); -void decode_audio_prepend_bytes(struct sh_audio *sh, int count, int byte); +int decode_audio(sh_audio_t *sh_audio, struct bstr *outbuf, int minlen); +void decode_audio_prepend_bytes(struct bstr *outbuf, int count, int byte); void resync_audio_stream(sh_audio_t *sh_audio); void skip_audio_frame(sh_audio_t *sh_audio); void uninit_audio(sh_audio_t *sh_audio); diff --git a/libmpdemux/stheader.h b/libmpdemux/stheader.h index ce4db94035..1ca4acab8b 100644 --- a/libmpdemux/stheader.h +++ b/libmpdemux/stheader.h @@ -72,11 +72,6 @@ typedef struct sh_audio { char* a_buffer; int a_buffer_len; int a_buffer_size; - // output buffers: - char* a_out_buffer; - int a_out_buffer_len; - int a_out_buffer_size; -// void* audio_out; // the audio_out handle, used for this audio stream struct af_stream *afilter; // the audio filter stream const struct ad_functions *ad_driver; #ifdef CONFIG_DYNAMIC_PLUGINS @@ -1748,6 +1748,7 @@ void reinit_audio_chain(struct MPContext *mpctx) mp_tmsg(MSGT_CPLAYER,MSGL_ERR,"Could not open/initialize audio device -> no sound.\n"); goto init_error; } + ao->buffer.start = talloc_new(ao); mp_msg(MSGT_CPLAYER,MSGL_INFO,"AO: [%s] %dHz %dch %s (%d bytes per sample)\n", ao->driver->info->short_name, ao->samplerate, ao->channels, @@ -1788,7 +1789,6 @@ static double written_audio_pts(struct MPContext *mpctx) { sh_audio_t *sh_audio = mpctx->sh_audio; demux_stream_t *d_audio = mpctx->d_audio; - double buffered_output; // first calculate the end pts of audio that has been output by decoder double a_pts = sh_audio->pts; if (a_pts != MP_NOPTS_VALUE) @@ -1822,11 +1822,11 @@ static double written_audio_pts(struct MPContext *mpctx) a_pts -= sh_audio->a_buffer_len / (double)sh_audio->o_bps; // Data buffered in audio filters, measured in bytes of "missing" output - buffered_output = af_calc_delay(sh_audio->afilter); + double buffered_output = af_calc_delay(sh_audio->afilter); // Data that was ready for ao but was buffered because ao didn't fully // accept everything to internal buffers yet - buffered_output += sh_audio->a_out_buffer_len; + buffered_output += mpctx->ao->buffer.len; // Filters divide audio length by playback_speed, so multiply by it // to get the length in original units without speedup or slowdown @@ -2328,7 +2328,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) int res; // Timing info may not be set without - res = decode_audio(sh_audio, 1); + res = decode_audio(sh_audio, &ao->buffer, 1); if (res < 0) return res; @@ -2345,7 +2345,7 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) if (written_pts <= 1 && sh_audio->pts == MP_NOPTS_VALUE) { if (!did_retry) { // Try to read more data to see packets that have pts - int res = decode_audio(sh_audio, ao->bps); + int res = decode_audio(sh_audio, &ao->buffer, ao->bps); if (res < 0) return res; did_retry = true; @@ -2362,19 +2362,17 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) mpctx->syncing_audio = false; int a = FFMIN(-bytes, FFMAX(playsize, 20000)); - int res = decode_audio(sh_audio, a); - bytes += sh_audio->a_out_buffer_len; + int res = decode_audio(sh_audio, &ao->buffer, a); + bytes += ao->buffer.len; if (bytes >= 0) { - memmove(sh_audio->a_out_buffer, - sh_audio->a_out_buffer + - sh_audio->a_out_buffer_len - bytes, - bytes); - sh_audio->a_out_buffer_len = bytes; + memmove(ao->buffer.start, + ao->buffer.start + ao->buffer.len - bytes, bytes); + ao->buffer.len = bytes; if (res < 0) return res; - return decode_audio(sh_audio, playsize); + return decode_audio(sh_audio, &ao->buffer, playsize); } - sh_audio->a_out_buffer_len = 0; + ao->buffer.len = 0; if (res < 0) return res; } @@ -2394,8 +2392,8 @@ static int audio_start_sync(struct MPContext *mpctx, int playsize) return ASYNC_PLAY_DONE; } mpctx->syncing_audio = false; - decode_audio_prepend_bytes(sh_audio, bytes, fillbyte); - return decode_audio(sh_audio, playsize); + decode_audio_prepend_bytes(&ao->buffer, bytes, fillbyte); + return decode_audio(sh_audio, &ao->buffer, playsize); } static int fill_audio_out_buffers(struct MPContext *mpctx) @@ -2408,7 +2406,6 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) int playflags=0; bool audio_eof = false; bool partial_fill = false; - bool format_change = false; sh_audio_t * const sh_audio = mpctx->sh_audio; bool modifiable_audio_format = !(ao->format & AF_FORMAT_SPECIAL_MASK); int unitsize = ao->channels * af_fmt2bits(ao->format) / 8; @@ -2435,11 +2432,17 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) if (mpctx->syncing_audio && mpctx->sh_video) res = audio_start_sync(mpctx, playsize); else - res = decode_audio(sh_audio, playsize); + res = decode_audio(sh_audio, &ao->buffer, playsize); if (res < 0) { // EOF, error or format change - if (res == -2) - format_change = true; - else if (res == ASYNC_PLAY_DONE) + if (res == -2) { + /* 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. + */ + uninit_player(mpctx, INITIALIZED_AO); + reinit_audio_chain(mpctx); + return -1; + } else if (res == ASYNC_PLAY_DONE) return 0; else if (mpctx->d_audio->eof) audio_eof = true; @@ -2458,10 +2461,10 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) } } - assert(sh_audio->a_out_buffer_len % unitsize == 0); - if (playsize > sh_audio->a_out_buffer_len) { + assert(ao->buffer.len % unitsize == 0); + if (playsize > ao->buffer.len) { partial_fill = true; - playsize = sh_audio->a_out_buffer_len; + playsize = ao->buffer.len; if (audio_eof) playflags |= AOPLAY_FINAL_CHUNK; } @@ -2476,29 +2479,18 @@ static int fill_audio_out_buffers(struct MPContext *mpctx) // They're obviously badly broken in the way they handle av sync; // would not having access to this make them more broken? ao->pts = ((mpctx->sh_video?mpctx->sh_video->timer:0)+mpctx->delay)*90000.0; - playsize = ao_play(ao, sh_audio->a_out_buffer, playsize, playflags); - assert(playsize % unitsize == 0); - - if (playsize > 0) { - sh_audio->a_out_buffer_len -= playsize; - memmove(sh_audio->a_out_buffer, &sh_audio->a_out_buffer[playsize], - sh_audio->a_out_buffer_len); - mpctx->delay += opts->playback_speed*playsize/(double)ao->bps; + int played = ao_play(ao, ao->buffer.start, playsize, playflags); + assert(played % unitsize == 0); + ao->buffer_playable_size = playsize - played; + + if (played > 0) { + ao->buffer.len -= played; + memmove(ao->buffer.start, ao->buffer.start + played, ao->buffer.len); + mpctx->delay += opts->playback_speed * played / ao->bps; } else if (audio_eof && ao_get_delay(ao) < .04) { // Sanity check to avoid hanging in case current ao doesn't output // partial chunks and doesn't check for AOPLAY_FINAL_CHUNK - mp_msg(MSGT_CPLAYER, MSGL_WARN, "Audio output truncated at end.\n"); - 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; + return -2; } return -partial_fill; @@ -3047,10 +3039,9 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao) current_module = "seek_audio_reset"; resync_audio_stream(mpctx->sh_audio); if (reset_ao) - // stop audio, throwing away buffered data ao_reset(mpctx->ao); + mpctx->ao->buffer.len = mpctx->ao->buffer_playable_size; mpctx->sh_audio->a_buffer_len = 0; - mpctx->sh_audio->a_out_buffer_len = 0; if (!mpctx->sh_video) update_subtitles(mpctx, mpctx->sh_audio->pts, mpctx->video_offset, true); @@ -3155,7 +3146,6 @@ static int seek(MPContext *mpctx, struct seek_params seek, if (mpctx->sh_audio) { ao_reset(mpctx->ao); mpctx->sh_audio->a_buffer_len = 0; - mpctx->sh_audio->a_out_buffer_len = 0; } return -1; } |