From 1919f1e05be38d7c28c9430efaee7edf15fc21b3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 19 May 2015 21:35:43 +0200 Subject: [PATCH] ad_spdif: use DTS-HD passthrough only if the audio is really DTS-HD Apparently some A/V receivers do not behave well if "normal" DTS is passed through using the high bitrate spdif format normally used for DTS-HD (other receivers are fine with it). Parse the first packet passed to ad_spdif by decoding it with libavcodec in order to get the profile. Ignore the --ad-spdif-dtshd if it's not DTS-HD. (If the codec profile changes midstream, the user is out of luck. But this is probably an insignificant corner case.) I thought about parsing the bitstream, but let's not. While it probably wouldn't be that much effort, we are trying to keep it down on codec details here - otherwise we could just do our own spdif framing instead of using libavformat's spdif pseudo-muxer. Another possibility, using the codec parameters signalled by libavformat, is disregarded. Our builtin Matroska decoder doesn't do this, and also we do not want on the demuxer having to decode some packets in order to retrieve codec params (as libavformat does). Fixes #1949. --- audio/decode/ad_spdif.c | 58 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c index 4f763a1086..e126520fb1 100644 --- a/audio/decode/ad_spdif.c +++ b/audio/decode/ad_spdif.c @@ -82,9 +82,56 @@ static int init(struct dec_audio *da, const char *decoder) return spdif_ctx->codec_id != AV_CODEC_ID_NONE; } -static int init_filter(struct dec_audio *da) +static int determine_codec_profile(struct dec_audio *da, AVPacket *pkt) { struct spdifContext *spdif_ctx = da->priv; + int profile = FF_PROFILE_UNKNOWN; + AVCodecContext *ctx = NULL; + AVFrame *frame = NULL; + + AVCodec *codec = avcodec_find_decoder(spdif_ctx->codec_id); + if (!codec) + goto done; + + frame = av_frame_alloc(); + if (!frame) + goto done; + + ctx = avcodec_alloc_context3(codec); + if (!ctx) + goto done; + + if (avcodec_open2(ctx, codec, NULL) < 0) { + av_free(ctx); // don't attempt to avcodec_close() an unopened ctx + ctx = NULL; + goto done; + } + + int got_frame = 0; + if (avcodec_decode_audio4(ctx, frame, &got_frame, pkt) < 1 || !got_frame) + goto done; + + profile = ctx->profile; + +done: + av_frame_free(&frame); + if (ctx) + avcodec_close(ctx); + avcodec_free_context(&ctx); + + 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) +{ + 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); AVFormatContext *lavf_ctx = avformat_alloc_context(); if (!lavf_ctx) @@ -133,8 +180,10 @@ static int init_filter(struct dec_audio *da) samplerate = 48000; num_channels = 2; break; - case AV_CODEC_ID_DTS: - if (da->opts->dtshd) { + case AV_CODEC_ID_DTS: { + bool is_hd = profile == FF_PROFILE_DTS_HD_HRA || + profile == FF_PROFILE_DTS_HD_MA; + if (da->opts->dtshd && is_hd) { av_dict_set(&format_opts, "dtshd_rate", "768000", 0); // 4*192000 sample_format = AF_FORMAT_S_DTSHD; samplerate = 192000; @@ -145,6 +194,7 @@ static int init_filter(struct dec_audio *da) num_channels = 2; } break; + } case AV_CODEC_ID_EAC3: sample_format = AF_FORMAT_S_EAC3; samplerate = 192000; @@ -204,7 +254,7 @@ static int decode_packet(struct dec_audio *da, struct mp_audio **out) da->pts_offset = 0; } if (!spdif_ctx->lavf_ctx) { - if (init_filter(da) < 0) + if (init_filter(da, &pkt) < 0) return AD_ERR; } int ret = av_write_frame(spdif_ctx->lavf_ctx, &pkt);