summaryrefslogtreecommitdiffstats
path: root/audio/out/ao_lavc.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out/ao_lavc.c')
-rw-r--r--audio/out/ao_lavc.c239
1 files changed, 108 insertions, 131 deletions
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 0973d9f529..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;
@@ -148,20 +160,21 @@ static int init(struct ao *ao)
// but at least one!
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 = MPMIN(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