summaryrefslogtreecommitdiffstats
path: root/audio/decode/dec_audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/decode/dec_audio.c')
-rw-r--r--audio/decode/dec_audio.c216
1 files changed, 105 insertions, 111 deletions
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;
}