summaryrefslogtreecommitdiffstats
path: root/audio/decode
diff options
context:
space:
mode:
Diffstat (limited to 'audio/decode')
-rw-r--r--audio/decode/ad.h3
-rw-r--r--audio/decode/ad_lavc.c82
-rw-r--r--audio/decode/ad_spdif.c24
-rw-r--r--audio/decode/dec_audio.c216
-rw-r--r--audio/decode/dec_audio.h42
5 files changed, 181 insertions, 186 deletions
diff --git a/audio/decode/ad.h b/audio/decode/ad.h
index 05139549b1..771ceb7e88 100644
--- a/audio/decode/ad.h
+++ b/audio/decode/ad.h
@@ -35,7 +35,8 @@ struct ad_functions {
int (*init)(struct dec_audio *da, const char *decoder);
void (*uninit)(struct dec_audio *da);
int (*control)(struct dec_audio *da, int cmd, void *arg);
- int (*decode_packet)(struct dec_audio *da, struct mp_audio **out);
+ int (*decode_packet)(struct dec_audio *da, struct demux_packet *pkt,
+ struct mp_audio **out);
};
enum ad_ctrl {
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index 7e5c8d5aa5..c30aff7fd5 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -42,8 +42,9 @@ struct priv {
AVFrame *avframe;
struct mp_audio frame;
bool force_channel_map;
- struct demux_packet *packet;
- uint32_t skip_samples;
+ uint32_t skip_samples, trim_samples;
+ bool preroll_done;
+ double next_pts;
};
static void uninit(struct dec_audio *da);
@@ -60,7 +61,7 @@ const struct m_sub_options ad_lavc_conf = {
.opts = (const m_option_t[]) {
OPT_FLOATRANGE("ac3drc", ac3drc, 0, 0, 6),
OPT_FLAG("downmix", downmix, 0),
- OPT_INTRANGE("threads", threads, 0, 1, 16),
+ OPT_INTRANGE("threads", threads, 0, 0, 16),
OPT_KEYVALUELIST("o", avopts, 0),
{0}
},
@@ -78,8 +79,7 @@ static int init(struct dec_audio *da, const char *decoder)
struct ad_lavc_params *opts = mpopts->ad_lavc_params;
AVCodecContext *lavc_context;
AVCodec *lavc_codec;
- struct sh_stream *sh = da->header;
- struct mp_codec_params *c = sh->codec;
+ struct mp_codec_params *c = da->codec;
struct priv *ctx = talloc_zero(NULL, struct priv);
da->priv = ctx;
@@ -140,6 +140,8 @@ static int init(struct dec_audio *da, const char *decoder)
return 0;
}
+ ctx->next_pts = MP_NOPTS_VALUE;
+
return 1;
}
@@ -165,27 +167,21 @@ static int control(struct dec_audio *da, int cmd, void *arg)
switch (cmd) {
case ADCTRL_RESET:
avcodec_flush_buffers(ctx->avctx);
- talloc_free(ctx->packet);
- ctx->packet = NULL;
ctx->skip_samples = 0;
+ ctx->trim_samples = 0;
+ ctx->preroll_done = false;
+ ctx->next_pts = MP_NOPTS_VALUE;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
-static int decode_packet(struct dec_audio *da, struct mp_audio **out)
+static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
+ struct mp_audio **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
- struct demux_packet *mpkt = priv->packet;
- if (!mpkt) {
- if (demux_read_packet_async(da->header, &mpkt) == 0)
- return AD_WAIT;
- }
-
- priv->packet = talloc_steal(priv, mpkt);
-
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
@@ -203,57 +199,69 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out)
mpkt->len -= ret;
mpkt->pts = MP_NOPTS_VALUE; // don't reset PTS next time
}
- if (mpkt->len == 0 || ret < 0) {
- talloc_free(mpkt);
- priv->packet = NULL;
- }
// LATM may need many packets to find mux info
- if (ret == AVERROR(EAGAIN))
- return AD_OK;
+ if (ret == AVERROR(EAGAIN)) {
+ mpkt->len = 0;
+ return 0;
+ }
}
if (ret < 0) {
MP_ERR(da, "Error decoding audio.\n");
- return AD_ERR;
+ return -1;
}
if (!got_frame)
- return mpkt ? AD_OK : AD_EOF;
+ return 0;
double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, NULL);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
- return AD_ERR;
+ return -1;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
mp_chmap_from_channels(&lavc_chmap, avctx->channels);
if (priv->force_channel_map) {
- if (lavc_chmap.num == da->header->codec->channels.num)
- lavc_chmap = da->header->codec->channels;
+ if (lavc_chmap.num == da->codec->channels.num)
+ lavc_chmap = da->codec->channels;
}
mp_audio_set_channels(mpframe, &lavc_chmap);
mpframe->pts = out_pts;
+ if (mpframe->pts == MP_NOPTS_VALUE)
+ mpframe->pts = priv->next_pts;
+ if (mpframe->pts != MP_NOPTS_VALUE)
+ priv->next_pts = mpframe->pts + mpframe->samples / (double)mpframe->rate;
+
#if HAVE_AVFRAME_SKIP_SAMPLES
AVFrameSideData *sd =
av_frame_get_side_data(priv->avframe, AV_FRAME_DATA_SKIP_SAMPLES);
if (sd && sd->size >= 10) {
char *d = sd->data;
priv->skip_samples += AV_RL32(d + 0);
- uint32_t pad = AV_RL32(d + 4);
- uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
- if (skip) {
- mp_audio_skip_samples(mpframe, skip);
- if (mpframe->pts != MP_NOPTS_VALUE)
- mpframe->pts += skip / (double)mpframe->rate;
- priv->skip_samples -= skip;
- }
- if (pad <= mpframe->samples)
- mpframe->samples -= pad;
+ priv->trim_samples += AV_RL32(d + 4);
}
#endif
+ if (!priv->preroll_done) {
+ // Skip only if this isn't already handled by AV_FRAME_DATA_SKIP_SAMPLES.
+ if (!priv->skip_samples)
+ priv->skip_samples = avctx->delay;
+ priv->preroll_done = true;
+ }
+
+ uint32_t skip = MPMIN(priv->skip_samples, mpframe->samples);
+ if (skip) {
+ mp_audio_skip_samples(mpframe, skip);
+ priv->skip_samples -= skip;
+ }
+ uint32_t trim = MPMIN(priv->trim_samples, mpframe->samples);
+ if (trim) {
+ mpframe->samples -= trim;
+ priv->trim_samples -= trim;
+ }
+
*out = mpframe;
av_frame_unref(priv->avframe);
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c
index 5e9dcf1c4f..7298d9e7d7 100644
--- a/audio/decode/ad_spdif.c
+++ b/audio/decode/ad_spdif.c
@@ -41,6 +41,7 @@ struct spdifContext {
bool need_close;
bool use_dts_hd;
struct mp_audio fmt;
+ struct mp_audio_pool *pool;
};
static int write_packet(void *p, uint8_t *buf, int buf_size)
@@ -79,6 +80,7 @@ static int init(struct dec_audio *da, const char *decoder)
da->priv = spdif_ctx;
spdif_ctx->log = da->log;
spdif_ctx->use_dts_hd = da->opts->dtshd;
+ spdif_ctx->pool = mp_audio_pool_create(spdif_ctx);
if (strcmp(decoder, "dts-hd") == 0) {
decoder = "dts";
@@ -189,7 +191,8 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt)
break;
case AV_CODEC_ID_DTS: {
bool is_hd = profile == FF_PROFILE_DTS_HD_HRA ||
- profile == FF_PROFILE_DTS_HD_MA;
+ profile == FF_PROFILE_DTS_HD_MA ||
+ profile == FF_PROFILE_UNKNOWN;
if (spdif_ctx->use_dts_hd && is_hd) {
av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000
sample_format = AF_FORMAT_S_DTSHD;
@@ -240,38 +243,35 @@ fail:
return -1;
}
-static int decode_packet(struct dec_audio *da, struct mp_audio **out)
+static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
+ struct mp_audio **out)
{
struct spdifContext *spdif_ctx = da->priv;
spdif_ctx->out_buffer_len = 0;
- struct demux_packet *mpkt;
- if (demux_read_packet_async(da->header, &mpkt) == 0)
- return AD_WAIT;
-
if (!mpkt)
- return AD_EOF;
+ return 0;
double pts = mpkt->pts;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
+ mpkt->len = 0; // will be fully consumed
pkt.pts = pkt.dts = 0;
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
- return AD_ERR;
+ return -1;
}
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
- talloc_free(mpkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
if (ret < 0)
- return AD_ERR;
+ return -1;
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
- *out = mp_audio_pool_get(da->pool, &spdif_ctx->fmt, samples);
+ *out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
if (!*out)
- return AD_ERR;
+ return -1;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
(*out)->pts = pts;
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index f774ed1abd..e60ebe370f 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -61,8 +61,6 @@ static void uninit_decoder(struct dec_audio *d_audio)
d_audio->ad_driver = NULL;
talloc_free(d_audio->priv);
d_audio->priv = NULL;
- d_audio->afilter->initialized = -1;
- d_audio->decode_format = (struct mp_audio){0};
}
static int init_audio_codec(struct dec_audio *d_audio, const char *decoder)
@@ -88,12 +86,12 @@ struct mp_decoder_list *audio_decoder_list(void)
static struct mp_decoder_list *audio_select_decoders(struct dec_audio *d_audio)
{
struct MPOpts *opts = d_audio->opts;
- const char *codec = d_audio->header->codec->codec;
+ const char *codec = d_audio->codec->codec;
struct mp_decoder_list *list = audio_decoder_list();
struct mp_decoder_list *new =
mp_select_decoders(list, codec, opts->audio_decoders);
- if (d_audio->spdif_passthrough) {
+ if (d_audio->try_spdif) {
struct mp_decoder_list *spdif =
mp_select_decoder_list(list, codec, "spdif", opts->audio_spdif);
mp_append_decoders(spdif, new);
@@ -146,7 +144,7 @@ int audio_init_best_codec(struct dec_audio *d_audio)
MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc);
} else {
MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n",
- d_audio->header->codec->codec);
+ d_audio->codec->codec);
}
talloc_free(list);
@@ -157,136 +155,132 @@ void audio_uninit(struct dec_audio *d_audio)
{
if (!d_audio)
return;
- MP_VERBOSE(d_audio, "Uninit audio filters...\n");
uninit_decoder(d_audio);
- af_destroy(d_audio->afilter);
- talloc_free(d_audio->waiting);
talloc_free(d_audio);
}
-static int decode_new_frame(struct dec_audio *da)
+void audio_reset_decoding(struct dec_audio *d_audio)
{
- while (!da->waiting) {
- int ret = da->ad_driver->decode_packet(da, &da->waiting);
- if (ret < 0)
- return ret;
-
- if (da->waiting) {
- if (da->waiting->pts != MP_NOPTS_VALUE) {
- if (da->pts != MP_NOPTS_VALUE) {
- da->pts += da->pts_offset / (double)da->waiting->rate;
- da->pts_offset = 0;
- }
- double newpts = da->waiting->pts;
- // Keep the interpolated timestamp if it doesn't deviate more
- // than 1 ms from the real one. (MKV rounded timestamps.)
- if (da->pts == MP_NOPTS_VALUE || da->pts_offset != 0 ||
- fabs(da->pts - newpts) > 0.001)
- {
- // Attempt to detect jumps in PTS. Even for the lowest
- // sample rates and with worst container rounded timestamp,
- // this should be a margin more than enough.
- if (da->pts != MP_NOPTS_VALUE && fabs(newpts - da->pts) > 0.1)
- {
- MP_WARN(da, "Invalid audio PTS: %f -> %f\n",
- da->pts, newpts);
- da->pts_reset = true;
- }
- da->pts = da->waiting->pts;
- da->pts_offset = 0;
- }
- }
- da->pts_offset += da->waiting->samples;
- da->decode_format = *da->waiting;
- mp_audio_set_null_data(&da->decode_format);
- }
-
- if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
- da->pts = 0;
- }
- return mp_audio_config_valid(da->waiting) ? AD_OK : AD_ERR;
+ if (d_audio->ad_driver)
+ d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
+ d_audio->pts = MP_NOPTS_VALUE;
+ talloc_free(d_audio->current_frame);
+ d_audio->current_frame = NULL;
+ talloc_free(d_audio->packet);
+ d_audio->packet = NULL;
+ talloc_free(d_audio->new_segment);
+ d_audio->new_segment = NULL;
+ d_audio->start = d_audio->end = MP_NOPTS_VALUE;
}
-/* Decode packets until we know the audio format. Then reinit the buffer.
- * Returns AD_OK on success, negative AD_* code otherwise.
- * Also returns AD_OK if already initialized (and does nothing).
- */
-int initial_audio_decode(struct dec_audio *da)
+static void fix_audio_pts(struct dec_audio *da)
{
- return decode_new_frame(da);
-}
+ if (!da->current_frame)
+ return;
-static bool copy_output(struct af_stream *afs, struct mp_audio_buffer *outbuf,
- int minsamples, bool eof)
-{
- while (mp_audio_buffer_samples(outbuf) < minsamples) {
- if (af_output_frame(afs, eof) < 0)
- return true; // error, stop doing stuff
- struct mp_audio *mpa = af_read_output_frame(afs);
- if (!mpa)
- return false; // out of data
- mp_audio_buffer_append(outbuf, mpa);
- talloc_free(mpa);
+ if (da->current_frame->pts != MP_NOPTS_VALUE) {
+ double newpts = da->current_frame->pts;
+ // Keep the interpolated timestamp if it doesn't deviate more
+ // than 1 ms from the real one. (MKV rounded timestamps.)
+ if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
+ da->pts = da->current_frame->pts;
}
- return true;
+
+ if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
+ da->pts = 0;
+
+ da->current_frame->pts = da->pts;
+
+ if (da->pts != MP_NOPTS_VALUE)
+ da->pts += da->current_frame->samples / (double)da->current_frame->rate;
}
-/* Try to get at least minsamples decoded+filtered samples in outbuf
- * (total length including possible existing data).
- * Return 0 on success, or negative AD_* error code.
- * In the former case outbuf has at least minsamples buffered on return.
- * In case of EOF/error it might or might not be. */
-int audio_decode(struct dec_audio *da, struct mp_audio_buffer *outbuf,
- int minsamples)
+void audio_work(struct dec_audio *da)
{
- struct af_stream *afs = da->afilter;
- if (afs->initialized < 1)
- return AD_ERR;
+ if (da->current_frame)
+ return;
- MP_STATS(da, "start audio");
+ if (!da->packet && demux_read_packet_async(da->header, &da->packet) == 0) {
+ da->current_state = DATA_WAIT;
+ return;
+ }
- int res;
- while (1) {
- res = 0;
+ if (da->packet && da->packet->new_segment) {
+ assert(!da->new_segment);
+ da->new_segment = da->packet;
+ da->packet = NULL;
+ }
- if (copy_output(afs, outbuf, minsamples, false))
- break;
+ bool had_packet = da->packet || da->new_segment;
- res = decode_new_frame(da);
- if (res < 0) {
- // drain filters first (especially for true EOF case)
- copy_output(afs, outbuf, minsamples, true);
- break;
- }
+ int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
+ if (ret < 0 || (da->packet && da->packet->len == 0)) {
+ talloc_free(da->packet);
+ da->packet = NULL;
+ }
- // On format change, make sure to drain the filter chain.
- if (!mp_audio_config_equals(&afs->input, da->waiting)) {
- copy_output(afs, outbuf, minsamples, true);
- res = AD_NEW_FMT;
- break;
- }
+ if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
+ talloc_free(da->current_frame);
+ da->current_frame = NULL;
+ }
- struct mp_audio *mpa = da->waiting;
- da->waiting = NULL;
- if (af_filter_frame(afs, mpa) < 0)
- return AD_ERR;
+ da->current_state = DATA_OK;
+ if (!da->current_frame) {
+ da->current_state = DATA_EOF;
+ if (had_packet)
+ da->current_state = DATA_AGAIN;
}
- MP_STATS(da, "end audio");
+ fix_audio_pts(da);
+
+ bool segment_end = true;
+
+ if (da->current_frame) {
+ mp_audio_clip_timestamps(da->current_frame, da->start, da->end);
+ if (da->current_frame->pts != MP_NOPTS_VALUE && da->start != MP_NOPTS_VALUE)
+ segment_end = da->current_frame->pts >= da->start;
+ if (da->current_frame->samples == 0) {
+ talloc_free(da->current_frame);
+ da->current_frame = NULL;
+ }
+ }
- return res;
+ // If there's a new segment, start it as soon as we're drained/finished.
+ if (segment_end && da->new_segment) {
+ struct demux_packet *new_segment = da->new_segment;
+ da->new_segment = NULL;
+
+ // Could avoid decoder reinit; would still need flush.
+ da->codec = new_segment->codec;
+ if (da->ad_driver)
+ da->ad_driver->uninit(da);
+ da->ad_driver = NULL;
+ audio_init_best_codec(da);
+
+ da->start = new_segment->start;
+ da->end = new_segment->end;
+
+ new_segment->new_segment = false;
+
+ da->packet = new_segment;
+ da->current_state = DATA_AGAIN;
+ }
}
-void audio_reset_decoding(struct dec_audio *d_audio)
+// Fetch an audio frame decoded with audio_work(). Returns one of:
+// DATA_OK: *out_frame is set to a new image
+// DATA_WAIT: waiting for demuxer; will receive a wakeup signal
+// DATA_EOF: end of file, no more frames to be expected
+// DATA_AGAIN: dropped frame or something similar
+int audio_get_frame(struct dec_audio *da, struct mp_audio **out_frame)
{
- if (d_audio->ad_driver)
- d_audio->ad_driver->control(d_audio, ADCTRL_RESET, NULL);
- af_seek_reset(d_audio->afilter);
- d_audio->pts = MP_NOPTS_VALUE;
- d_audio->pts_offset = 0;
- d_audio->pts_reset = false;
- if (d_audio->waiting) {
- talloc_free(d_audio->waiting);
- d_audio->waiting = NULL;
+ *out_frame = NULL;
+ if (da->current_frame) {
+ *out_frame = da->current_frame;
+ da->current_frame = NULL;
+ return DATA_OK;
}
+ if (da->current_state == DATA_OK)
+ return DATA_AGAIN;
+ return da->current_state;
}
diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h
index a8c66fa67e..7bc8b00b0f 100644
--- a/audio/decode/dec_audio.h
+++ b/audio/decode/dec_audio.h
@@ -30,41 +30,33 @@ struct dec_audio {
struct mp_log *log;
struct MPOpts *opts;
struct mpv_global *global;
- bool spdif_passthrough, spdif_failed;
const struct ad_functions *ad_driver;
struct sh_stream *header;
- struct af_stream *afilter;
+ struct mp_codec_params *codec;
char *decoder_desc;
- int init_retries;
- struct mp_audio_pool *pool;
- struct mp_audio decode_format;
- struct mp_audio *waiting; // used on format-change
- // set by decoder
- int bitrate; // input bitrate, can change with VBR sources
- // last known pts value in output from decoder
- double pts;
- // number of samples output by decoder after last known pts
- int pts_offset;
- // set every time a jump in timestamps is encountered
- bool pts_reset;
+
+ bool try_spdif;
+
// For free use by the ad_driver
void *priv;
-};
-enum {
- AD_OK = 0,
- AD_ERR = -1,
- AD_EOF = -2,
- AD_NEW_FMT = -3,
- AD_WAIT = -4,
+ // Strictly internal (dec_audio.c).
+
+ double pts; // endpts of previous frame
+ double start, end;
+ struct demux_packet *packet;
+ struct demux_packet *new_segment;
+ struct mp_audio *current_frame;
+ int current_state;
};
struct mp_decoder_list *audio_decoder_list(void);
int audio_init_best_codec(struct dec_audio *d_audio);
-int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf,
- int minsamples);
-int initial_audio_decode(struct dec_audio *d_audio);
-void audio_reset_decoding(struct dec_audio *d_audio);
void audio_uninit(struct dec_audio *d_audio);
+void audio_work(struct dec_audio *d_audio);
+int audio_get_frame(struct dec_audio *d_audio, struct mp_audio **out_frame);
+
+void audio_reset_decoding(struct dec_audio *d_audio);
+
#endif /* MPLAYER_DEC_AUDIO_H */