diff options
Diffstat (limited to 'audio/decode')
-rw-r--r-- | audio/decode/ad.h | 3 | ||||
-rw-r--r-- | audio/decode/ad_lavc.c | 82 | ||||
-rw-r--r-- | audio/decode/ad_spdif.c | 24 | ||||
-rw-r--r-- | audio/decode/dec_audio.c | 216 | ||||
-rw-r--r-- | audio/decode/dec_audio.h | 42 |
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 */ |