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 +#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 diff --git a/mplayer.c b/mplayer.c index e65afe5b0f..90cf15c8b3 100644 --- a/mplayer.c +++ b/mplayer.c @@ -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); + int played = ao_play(ao, ao->buffer.start, playsize, playflags); + assert(played % unitsize == 0); + ao->buffer_playable_size = playsize - played; - 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; + 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; }