audio: restructure decode loop

Same deal as with video. Including the EOF handling.

(It would be nice if this code were not duplicated, but right now we're
not even close to unifying the audio and video code paths.)
This commit is contained in:
wm4 2017-01-11 11:58:26 +01:00
parent f8baae5854
commit cbd8abcbff
4 changed files with 85 additions and 57 deletions

View File

@ -35,8 +35,11 @@ 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 demux_packet *pkt,
struct mp_audio **out);
// Return whether or not the packet has been consumed.
bool (*send_packet)(struct dec_audio *da, struct demux_packet *pkt);
// Return whether decoding is still going on (false if EOF was reached).
// Never returns false & *out set, but can return true with !*out.
bool (*receive_frame)(struct dec_audio *da, struct mp_audio **out);
};
enum ad_ctrl {

View File

@ -45,7 +45,6 @@ struct priv {
uint32_t skip_samples, trim_samples;
bool preroll_done;
double next_pts;
bool needs_reset;
AVRational codec_timebase;
};
@ -177,14 +176,12 @@ static int control(struct dec_audio *da, int cmd, void *arg)
ctx->trim_samples = 0;
ctx->preroll_done = false;
ctx->next_pts = MP_NOPTS_VALUE;
ctx->needs_reset = false;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct mp_audio **out)
static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
@ -195,41 +192,43 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
if (mpkt && priv->next_pts == MP_NOPTS_VALUE)
priv->next_pts = mpkt->pts;
int in_len = mpkt ? mpkt->len : 0;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, &priv->codec_timebase);
int got_frame = 0;
av_frame_unref(priv->avframe);
int ret = avcodec_send_packet(avctx, mpkt ? &pkt : NULL);
if (priv->needs_reset)
control(da, ADCTRL_RESET, NULL);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return false;
int ret = avcodec_send_packet(avctx, &pkt);
if (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
if (ret >= 0 && mpkt)
mpkt->len = 0;
ret = avcodec_receive_frame(avctx, priv->avframe);
if (ret >= 0)
got_frame = 1;
if (ret == AVERROR_EOF)
priv->needs_reset = true;
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
}
if (ret < 0) {
if (ret < 0)
MP_ERR(da, "Error decoding audio.\n");
return true;
}
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
{
struct priv *priv = da->priv;
AVCodecContext *avctx = priv->avctx;
int ret = avcodec_receive_frame(avctx, priv->avframe);
if (ret == AVERROR_EOF) {
// If flushing was initialized earlier and has ended now, make it start
// over in case we get new packets at some point in the future.
control(da, ADCTRL_RESET, NULL);
return false;
} else if (ret < 0 && ret != AVERROR(EAGAIN)) {
MP_ERR(da, "Error decoding audio.\n");
return -1;
}
if (!got_frame)
return 0;
if (!priv->avframe->buf[0])
return true;
double out_pts = mp_pts_from_av(priv->avframe->pts, &priv->codec_timebase);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
return -1;
return true;
struct mp_chmap lavc_chmap = mpframe->channels;
if (lavc_chmap.num != avctx->channels)
@ -279,8 +278,8 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
av_frame_unref(priv->avframe);
MP_DBG(da, "Decoded %d -> %d samples\n", in_len, mpframe->samples);
return 0;
MP_DBG(da, "Decoded %d samples\n", mpframe->samples);
return true;
}
static void add_decoders(struct mp_decoder_list *list)
@ -294,5 +293,6 @@ const struct ad_functions ad_lavc = {
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
.send_packet = send_packet,
.receive_frame = receive_frame,
};

View File

@ -42,6 +42,8 @@ struct spdifContext {
bool use_dts_hd;
struct mp_audio fmt;
struct mp_audio_pool *pool;
bool got_eof;
struct demux_packet *queued_packet;
};
static int write_packet(void *p, uint8_t *buf, int buf_size)
@ -71,6 +73,7 @@ static void uninit(struct dec_audio *da)
av_freep(&lavf_ctx->pb->buffer);
av_freep(&lavf_ctx->pb);
avformat_free_context(lavf_ctx);
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->lavf_ctx = NULL;
}
}
@ -243,44 +246,70 @@ fail:
return -1;
}
static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
struct mp_audio **out)
static bool send_packet(struct dec_audio *da, struct demux_packet *mpkt)
{
struct spdifContext *spdif_ctx = da->priv;
spdif_ctx->out_buffer_len = 0;
if (spdif_ctx->queued_packet || spdif_ctx->got_eof)
return false;
if (!mpkt)
return 0;
spdif_ctx->queued_packet = mpkt ? demux_copy_packet(mpkt) : NULL;
spdif_ctx->got_eof = !mpkt;
return true;
}
double pts = mpkt->pts;
static bool receive_frame(struct dec_audio *da, struct mp_audio **out)
{
struct spdifContext *spdif_ctx = da->priv;
if (spdif_ctx->got_eof) {
spdif_ctx->got_eof = false;
return false;
}
if (!spdif_ctx->queued_packet)
return true;
double pts = spdif_ctx->queued_packet->pts;
AVPacket pkt;
mp_set_av_packet(&pkt, mpkt, NULL);
mpkt->len = 0; // will be fully consumed
mp_set_av_packet(&pkt, spdif_ctx->queued_packet, NULL);
pkt.pts = pkt.dts = 0;
if (!spdif_ctx->lavf_ctx) {
if (init_filter(da, &pkt) < 0)
return -1;
goto done;
}
spdif_ctx->out_buffer_len = 0;
int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);
avio_flush(spdif_ctx->lavf_ctx->pb);
if (ret < 0)
return -1;
goto done;
int samples = spdif_ctx->out_buffer_len / spdif_ctx->fmt.sstride;
*out = mp_audio_pool_get(spdif_ctx->pool, &spdif_ctx->fmt, samples);
if (!*out)
return -1;
goto done;
memcpy((*out)->planes[0], spdif_ctx->out_buffer, spdif_ctx->out_buffer_len);
(*out)->pts = pts;
return 0;
done:
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->queued_packet = NULL;
return true;
}
static int control(struct dec_audio *da, int cmd, void *arg)
{
struct spdifContext *spdif_ctx = da->priv;
switch (cmd) {
case ADCTRL_RESET:
talloc_free(spdif_ctx->queued_packet);
spdif_ctx->queued_packet = NULL;
spdif_ctx->got_eof = false;
return CONTROL_TRUE;
}
return CONTROL_UNKNOWN;
}
@ -344,5 +373,6 @@ const struct ad_functions ad_spdif = {
.init = init,
.uninit = uninit,
.control = control,
.decode_packet = decode_packet,
.send_packet = send_packet,
.receive_frame = receive_frame,
};

View File

@ -201,7 +201,7 @@ static void fix_audio_pts(struct dec_audio *da)
void audio_work(struct dec_audio *da)
{
if (da->current_frame)
if (da->current_frame || !da->ad_driver)
return;
if (!da->packet && !da->new_segment &&
@ -217,30 +217,25 @@ void audio_work(struct dec_audio *da)
da->packet = NULL;
}
bool had_input_packet = !!da->packet;
bool had_packet = da->packet || da->new_segment;
int ret = da->ad_driver->decode_packet(da, da->packet, &da->current_frame);
if (ret < 0 || (da->packet && da->packet->len == 0)) {
if (da->ad_driver->send_packet(da, da->packet)) {
talloc_free(da->packet);
da->packet = NULL;
}
bool progress = da->ad_driver->receive_frame(da, &da->current_frame);
if (da->current_frame && !mp_audio_config_valid(da->current_frame)) {
talloc_free(da->current_frame);
da->current_frame = NULL;
}
da->current_state = DATA_OK;
if (!da->current_frame) {
da->current_state = da->current_frame ? DATA_OK : DATA_AGAIN;
if (!progress)
da->current_state = DATA_EOF;
if (had_packet)
da->current_state = DATA_AGAIN;
}
fix_audio_pts(da);
bool segment_end = !da->current_frame && !had_input_packet;
bool segment_end = da->current_state == DATA_EOF;
if (da->current_frame) {
mp_audio_clip_timestamps(da->current_frame, da->start, da->end);