diff options
Diffstat (limited to 'audio/decode/ad_spdif.c')
-rw-r--r-- | audio/decode/ad_spdif.c | 95 |
1 files changed, 74 insertions, 21 deletions
diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c index 30c7883bf4..e38c9e5077 100644 --- a/audio/decode/ad_spdif.c +++ b/audio/decode/ad_spdif.c @@ -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; } } @@ -90,13 +93,34 @@ static int init(struct dec_audio *da, const char *decoder) return spdif_ctx->codec_id != AV_CODEC_ID_NONE; } -static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt) +static void determine_codec_params(struct dec_audio *da, AVPacket *pkt, + int *out_profile, int *out_rate) { struct spdifContext *spdif_ctx = da->priv; int profile = FF_PROFILE_UNKNOWN; AVCodecContext *ctx = NULL; AVFrame *frame = NULL; + AVCodecParserContext *parser = av_parser_init(spdif_ctx->codec_id); + if (parser) { + // Don't make it wait for the next frame. + parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; + + ctx = avcodec_alloc_context3(NULL); + + uint8_t *d = NULL; + int s = 0; + av_parser_parse2(parser, ctx, &d, &s, pkt->data, pkt->size, 0, 0, 0); + *out_profile = profile = ctx->profile; + *out_rate = ctx->sample_rate; + + av_free(ctx); + av_parser_close(parser); + } + + if (profile != FF_PROFILE_UNKNOWN || spdif_ctx->codec_id != AV_CODEC_ID_DTS) + return; + AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id); if (!codec) goto done; @@ -120,7 +144,8 @@ static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt) if (avcodec_receive_frame(ctx, frame) < 0) goto done; - profile = ctx->profile; + *out_profile = profile = ctx->profile; + *out_rate = ctx->sample_rate; done: av_frame_free(&frame); @@ -130,8 +155,6 @@ done: if (profile == FF_PROFILE_UNKNOWN) MP_WARN(da, "Failed to parse codec profile.\n"); - - return profile; } static int init_filter(struct dec_audio *da, AVPacket *pkt) @@ -139,8 +162,9 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt) struct spdifContext *spdif_ctx = da->priv; int profile = FF_PROFILE_UNKNOWN; - if (spdif_ctx->codec_id == AV_CODEC_ID_DTS) - profile = determine_codec_profile(da, pkt); + int c_rate = 0; + determine_codec_params(da, pkt, &profile, &c_rate); + MP_VERBOSE(da, "In: profile=%d samplerate=%d\n", profile, c_rate); AVFormatContext *lavf_ctx = avformat_alloc_context(); if (!lavf_ctx) @@ -186,7 +210,7 @@ static int init_filter(struct dec_audio *da, AVPacket *pkt) break; case AV_CODEC_ID_AC3: sample_format = AF_FORMAT_S_AC3; - samplerate = 48000; + samplerate = c_rate > 0 ? c_rate : 48000; num_channels = 2; break; case AV_CODEC_ID_DTS: { @@ -243,44 +267,72 @@ 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; + if (ret < 0) { + MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret))); + 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 +396,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, }; |