diff options
Diffstat (limited to 'audio/out/ao_lavc.c')
-rw-r--r-- | audio/out/ao_lavc.c | 241 |
1 files changed, 109 insertions, 132 deletions
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c index 974d9d0b63..4bae4389e8 100644 --- a/audio/out/ao_lavc.c +++ b/audio/out/ao_lavc.c @@ -26,12 +26,17 @@ #include <limits.h> #include <libavutil/common.h> +#include <libavutil/samplefmt.h> #include "config.h" #include "options/options.h" #include "common/common.h" +#include "audio/aframe.h" +#include "audio/chmap_avchannel.h" #include "audio/format.h" #include "audio/fmt-conversion.h" +#include "filters/filter_internal.h" +#include "filters/f_utils.h" #include "mpv_talloc.h" #include "ao.h" #include "internal.h" @@ -44,20 +49,19 @@ struct priv { int pcmhack; int aframesize; - int aframecount; - int64_t savepts; int framecount; int64_t lastpts; int sample_size; - const void *sample_padding; double expected_next_pts; + struct mp_filter *filter_root; + struct mp_filter *fix_frame_size; AVRational worst_time_base; bool shutdown; }; -static void encode(struct ao *ao, double apts, void **data); +static bool write_frame(struct ao *ao, struct mp_frame frame); static bool supports_format(const AVCodec *codec, int format) { @@ -87,6 +91,9 @@ static void select_format(struct ao *ao, const AVCodec *codec) static void on_ready(void *ptr) { struct ao *ao = ptr; + struct priv *ac = ao->priv; + + ac->worst_time_base = encoder_get_mux_timebase_unlocked(ac->enc); ao_add_events(ao, AO_EVENT_INITIAL_UNBLOCK); } @@ -119,8 +126,13 @@ static int init(struct ao *ao) if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false)) goto fail; mp_chmap_reorder_to_lavc(&ao->channels); + +#if !HAVE_AV_CHANNEL_LAYOUT encoder->channels = ao->channels.num; encoder->channel_layout = mp_chmap_to_lavc(&ao->channels); +#else + mp_chmap_to_av_layout(&encoder->ch_layout, &ao->channels); +#endif encoder->sample_fmt = AV_SAMPLE_FMT_NONE; @@ -146,22 +158,23 @@ static int init(struct ao *ao) // enough frames for at least 0.25 seconds ac->framecount = ceil(ao->samplerate * 0.25 / ac->aframesize); // but at least one! - ac->framecount = FFMAX(ac->framecount, 1); + ac->framecount = MPMAX(ac->framecount, 1); - ac->savepts = AV_NOPTS_VALUE; ac->lastpts = AV_NOPTS_VALUE; ao->untimed = true; - ao->period_size = ac->aframesize * ac->framecount; + ao->device_buffer = ac->aframesize * ac->framecount; - if (ao->channels.num > AV_NUM_DATA_POINTERS) - goto fail; + ac->filter_root = mp_filter_create_root(ao->global); + ac->fix_frame_size = mp_fixed_aframe_size_create(ac->filter_root, + ac->aframesize, true); + MP_HANDLE_OOM(ac->fix_frame_size); return 0; fail: - pthread_mutex_unlock(&ao->encode_lavc_ctx->lock); + mp_mutex_unlock(&ao->encode_lavc_ctx->lock); ac->shutdown = true; return -1; } @@ -170,117 +183,88 @@ fail: static void uninit(struct ao *ao) { struct priv *ac = ao->priv; - struct encode_lavc_context *ectx = ao->encode_lavc_ctx; if (!ac->shutdown) { - double outpts = ac->expected_next_pts; - - pthread_mutex_lock(&ectx->lock); - if (!ac->enc->options->rawts) - outpts += ectx->discontinuity_pts_offset; - pthread_mutex_unlock(&ectx->lock); - - outpts += encoder_get_offset(ac->enc); - encode(ao, outpts, NULL); + if (!write_frame(ao, MP_EOF_FRAME)) + MP_WARN(ao, "could not flush last frame\n"); + encoder_encode(ac->enc, NULL); } + + talloc_free(ac->filter_root); } -// return: how many samples can be played without blocking -static int get_space(struct ao *ao) +// must get exactly ac->aframesize amount of data +static void encode(struct ao *ao, struct mp_aframe *af) { struct priv *ac = ao->priv; + AVCodecContext *encoder = ac->enc->encoder; + double outpts = mp_aframe_get_pts(af); + + AVFrame *frame = mp_aframe_to_avframe(af); + MP_HANDLE_OOM(frame); - return ac->aframesize * ac->framecount; + frame->pts = rint(outpts * av_q2d(av_inv_q(encoder->time_base))); + + int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base, + ac->worst_time_base); + if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) { + // whatever the fuck this code does? + MP_WARN(ao, "audio frame pts went backwards (%d <- %d), autofixed\n", + (int)frame->pts, (int)ac->lastpts); + frame_pts = ac->lastpts + 1; + ac->lastpts = frame_pts; + frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, + encoder->time_base); + frame_pts = av_rescale_q(frame->pts, encoder->time_base, + ac->worst_time_base); + } + ac->lastpts = frame_pts; + + frame->quality = encoder->global_quality; + encoder_encode(ac->enc, frame); + av_frame_free(&frame); } -// must get exactly ac->aframesize amount of data -static void encode(struct ao *ao, double apts, void **data) +static bool write_frame(struct ao *ao, struct mp_frame frame) { struct priv *ac = ao->priv; - struct encode_lavc_context *ectx = ao->encode_lavc_ctx; - AVCodecContext *encoder = ac->enc->encoder; - double realapts = ac->aframecount * (double) ac->aframesize / - ao->samplerate; - - ac->aframecount++; - - pthread_mutex_lock(&ectx->lock); - if (data) - ectx->audio_pts_offset = realapts - apts; - pthread_mutex_unlock(&ectx->lock); - - if(data) { - AVFrame *frame = av_frame_alloc(); - frame->format = af_to_avformat(ao->format); - frame->nb_samples = ac->aframesize; - - size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1; - assert(num_planes <= AV_NUM_DATA_POINTERS); - for (int n = 0; n < num_planes; n++) - frame->extended_data[n] = data[n]; - - frame->linesize[0] = frame->nb_samples * ao->sstride; - - frame->pts = rint(apts * av_q2d(av_inv_q(encoder->time_base))); - - int64_t frame_pts = av_rescale_q(frame->pts, encoder->time_base, - ac->worst_time_base); - if (ac->lastpts != AV_NOPTS_VALUE && frame_pts <= ac->lastpts) { - // this indicates broken video - // (video pts failing to increase fast enough to match audio) - MP_WARN(ao, "audio frame pts went backwards (%d <- %d), autofixed\n", - (int)frame->pts, (int)ac->lastpts); - frame_pts = ac->lastpts + 1; - frame->pts = av_rescale_q(frame_pts, ac->worst_time_base, - encoder->time_base); - } - ac->lastpts = frame_pts; - frame->quality = encoder->global_quality; - encoder_encode(ac->enc, frame); - av_frame_free(&frame); - } else { - encoder_encode(ac->enc, NULL); + // Can't push in frame if it doesn't want it output one. + mp_pin_out_request_data(ac->fix_frame_size->pins[1]); + + if (!mp_pin_in_write(ac->fix_frame_size->pins[0], frame)) + return false; // shouldn't happen™ + + while (1) { + struct mp_frame fr = mp_pin_out_read(ac->fix_frame_size->pins[1]); + if (!fr.type) + break; + if (fr.type != MP_FRAME_AUDIO) + continue; + struct mp_aframe *af = fr.data; + encode(ao, af); + mp_frame_unref(&fr); } + + return true; } -// this should round samples down to frame sizes -// return: number of samples played -static int play(struct ao *ao, void **data, int samples, int flags) +static bool audio_write(struct ao *ao, void **data, int samples) { struct priv *ac = ao->priv; - struct encoder_context *enc = ac->enc; struct encode_lavc_context *ectx = ao->encode_lavc_ctx; - int bufpos = 0; + + // See ao_driver.write_frames. + struct mp_aframe *af = mp_aframe_new_ref(*(struct mp_aframe **)data); + double nextpts; - int orig_samples = samples; + double pts = mp_aframe_get_pts(af); + double outpts = pts; // for ectx PTS fields - pthread_mutex_lock(&ectx->lock); - - double pts = ectx->last_audio_in_pts; - pts += ectx->samples_since_last_pts / (double)ao->samplerate; - - size_t num_planes = af_fmt_is_planar(ao->format) ? ao->channels.num : 1; - - void *tempdata = NULL; - void *padded[MP_NUM_CHANNELS]; - - if ((flags & AOPLAY_FINAL_CHUNK) && (samples % ac->aframesize)) { - tempdata = talloc_new(NULL); - size_t bytelen = samples * ao->sstride; - size_t extralen = (ac->aframesize - 1) * ao->sstride; - for (int n = 0; n < num_planes; n++) { - padded[n] = talloc_size(tempdata, bytelen + extralen); - memcpy(padded[n], data[n], bytelen); - af_fill_silence((char *)padded[n] + bytelen, extralen, ao->format); - } - data = padded; - samples = (bytelen + extralen) / ao->sstride; - } + mp_mutex_lock(&ectx->lock); - double outpts = pts; - if (!enc->options->rawts) { + if (!ectx->options->rawts) { // Fix and apply the discontinuity pts offset. nextpts = pts; if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) { @@ -297,52 +281,42 @@ static int play(struct ao *ao, void **data, int samples, int flags) outpts = pts + ectx->discontinuity_pts_offset; } - pthread_mutex_unlock(&ectx->lock); - - // Shift pts by the pts offset first. - outpts += encoder_get_offset(enc); - - while (samples - bufpos >= ac->aframesize) { - void *start[MP_NUM_CHANNELS] = {0}; - for (int n = 0; n < num_planes; n++) - start[n] = (char *)data[n] + bufpos * ao->sstride; - encode(ao, outpts + bufpos / (double) ao->samplerate, start); - bufpos += ac->aframesize; - } - // Calculate expected pts of next audio frame (input side). - ac->expected_next_pts = pts + bufpos / (double) ao->samplerate; - - pthread_mutex_lock(&ectx->lock); + ac->expected_next_pts = pts + mp_aframe_get_size(af) / (double) ao->samplerate; // Set next allowed input pts value (input side). - if (!enc->options->rawts) { + if (!ectx->options->rawts) { nextpts = ac->expected_next_pts + ectx->discontinuity_pts_offset; if (nextpts > ectx->next_in_pts) ectx->next_in_pts = nextpts; } - talloc_free(tempdata); + mp_mutex_unlock(&ectx->lock); - int taken = FFMIN(bufpos, orig_samples); - ectx->samples_since_last_pts += taken; + mp_aframe_set_pts(af, outpts); - pthread_mutex_unlock(&ectx->lock); + return write_frame(ao, MAKE_FRAME(MP_FRAME_AUDIO, af)); +} - if (flags & AOPLAY_FINAL_CHUNK) { - if (bufpos < orig_samples) - MP_ERR(ao, "did not write enough data at the end\n"); - } else { - if (bufpos > orig_samples) - MP_ERR(ao, "audio buffer overflow (should never happen)\n"); - } +static void get_state(struct ao *ao, struct mp_pcm_state *state) +{ + state->free_samples = 1; + state->queued_samples = 0; + state->delay = 0; +} - return taken; +static bool set_pause(struct ao *ao, bool paused) +{ + return true; // signal support so common code doesn't write silence +} + +static void start(struct ao *ao) +{ + // we use data immediately } -static void drain(struct ao *ao) +static void reset(struct ao *ao) { - // pretend we support it, so generic code doesn't force a wait } const struct ao_driver audio_out_lavc = { @@ -350,12 +324,15 @@ const struct ao_driver audio_out_lavc = { .description = "audio encoding using libavcodec", .name = "lavc", .initially_blocked = true, + .write_frames = true, .priv_size = sizeof(struct priv), .init = init, .uninit = uninit, - .get_space = get_space, - .play = play, - .drain = drain, + .get_state = get_state, + .set_pause = set_pause, + .write = audio_write, + .start = start, + .reset = reset, }; // vim: sw=4 ts=4 et tw=80 |