Skip to content

Commit 4384283

Browse files
committed
ad_spdif: recreate spdif muxer on reset
The spdif decoder uses avformat/spdifenc. Some spdifenc paths keep muxer state across packets, such as partial TrueHD MAT frames. After a seek, that state belongs to the old stream position and can cause the next packet to be muxed with stale timing or buffering state. On decoder reset, discard the current spdif muxer context. The next usable input packet will recreate the muxer through the existing initialization path. A fresh TrueHD spdif muxer needs a TrueHD major sync before it can begin muxing the stream. Startup or post-seek playback may begin on a TrueHD access unit that does not have major sync, so drop TrueHD packets until a major sync access unit is seen. This also gives the downstream decoder a clean restart point after seeks. Since the muxer is now recreated after seeks, codec parameter probing also runs after seeks. Avoid warning repeatedly for missing probe values that are not needed for the selected passthrough format. Partially addresses: https://trac.ffmpeg.org/ticket/9569 Signed-off-by: Nathan Lucas <nlucasgit@gmail.com>
1 parent 4c220ff commit 4384283

1 file changed

Lines changed: 66 additions & 7 deletions

File tree

audio/decode/ad_spdif.c

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct spdifContext {
4848
uint8_t out_buffer[OUTBUF_SIZE];
4949
bool need_close;
5050
bool use_dts_hd;
51+
int dropped_startup_packets;
5152
struct mp_aframe *fmt;
5253
int sstride;
5354
struct mp_aframe_pool *pool;
@@ -74,24 +75,47 @@ static int write_packet(void *p, const uint8_t *buf, int buf_size)
7475
return buf_size;
7576
}
7677

77-
// (called on both filter destruction _and_ if lavf fails to init)
78-
static void ad_spdif_destroy(struct mp_filter *da)
78+
static void close_lavf_context(struct spdifContext *spdif_ctx, bool write_trailer)
7979
{
80-
struct spdifContext *spdif_ctx = da->priv;
81-
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
80+
AVFormatContext *lavf_ctx = spdif_ctx->lavf_ctx;
8281

8382
if (lavf_ctx) {
84-
if (spdif_ctx->need_close)
83+
if (write_trailer && spdif_ctx->need_close)
8584
av_write_trailer(lavf_ctx);
8685
if (lavf_ctx->pb)
8786
av_freep(&lavf_ctx->pb->buffer);
8887
avio_context_free(&lavf_ctx->pb);
8988
avformat_free_context(lavf_ctx);
9089
spdif_ctx->lavf_ctx = NULL;
9190
}
91+
spdif_ctx->need_close = false;
92+
}
93+
94+
// (called on both filter destruction _and_ if lavf fails to init)
95+
static void ad_spdif_destroy(struct mp_filter *da)
96+
{
97+
struct spdifContext *spdif_ctx = da->priv;
98+
99+
close_lavf_context(spdif_ctx, true);
92100
mp_free_av_packet(&spdif_ctx->avpkt);
93101
}
94102

103+
static void ad_spdif_reset(struct mp_filter *da)
104+
{
105+
struct spdifContext *spdif_ctx = da->priv;
106+
107+
close_lavf_context(spdif_ctx, false);
108+
TA_FREEP(&spdif_ctx->fmt);
109+
}
110+
111+
// TrueHD/MLP major sync starts with f8 72 6f ba/bb after the AU header.
112+
static bool truehd_has_major_sync(AVPacket *pkt)
113+
{
114+
return pkt->size >= 8 && pkt->data[4] == 0xf8 &&
115+
pkt->data[5] == 0x72 && pkt->data[6] == 0x6f &&
116+
(pkt->data[7] & 0xfe) == 0xba;
117+
}
118+
95119
static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
96120
int *out_profile, int *out_rate)
97121
{
@@ -160,9 +184,22 @@ static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
160184
done:
161185
av_frame_free(&frame);
162186
avcodec_free_context(&ctx);
187+
}
163188

164-
if (profile == AV_PROFILE_UNKNOWN)
165-
MP_WARN(da, "Failed to parse codec profile.\n");
189+
// Some codecs do not need every probed value for spdif muxer setup.
190+
static bool codec_params_warning_needed(struct spdifContext *spdif_ctx,
191+
int profile, int rate)
192+
{
193+
switch (spdif_ctx->codec_id) {
194+
case AV_CODEC_ID_DTS:
195+
return spdif_ctx->use_dts_hd && profile == AV_PROFILE_UNKNOWN;
196+
case AV_CODEC_ID_TRUEHD:
197+
return profile == AV_PROFILE_UNKNOWN || rate <= 0;
198+
case AV_CODEC_ID_AC3:
199+
return rate <= 0;
200+
default:
201+
return false;
202+
}
166203
}
167204

168205
static int init_filter(struct mp_filter *da)
@@ -175,6 +212,8 @@ static int init_filter(struct mp_filter *da)
175212
int c_rate = 0;
176213
determine_codec_params(da, pkt, &profile, &c_rate);
177214
MP_VERBOSE(da, "In: profile=%d samplerate=%d\n", profile, c_rate);
215+
if (codec_params_warning_needed(spdif_ctx, profile, c_rate))
216+
MP_WARN(da, "Failed to parse codec parameters.\n");
178217

179218
AVFormatContext *lavf_ctx = avformat_alloc_context();
180219
if (!lavf_ctx)
@@ -318,6 +357,7 @@ static void ad_spdif_process(struct mp_filter *da)
318357
struct demux_packet *mpkt = inframe.data;
319358
struct mp_aframe *out = NULL;
320359
double pts = mpkt->pts;
360+
bool drop_packet = false;
321361

322362
if (!spdif_ctx->avpkt) {
323363
spdif_ctx->avpkt = av_packet_alloc();
@@ -326,6 +366,16 @@ static void ad_spdif_process(struct mp_filter *da)
326366
mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL);
327367
spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0;
328368
if (!spdif_ctx->lavf_ctx) {
369+
// A fresh TrueHD spdif muxer needs a major sync before it can begin
370+
// muxing the stream, and init_filter() probes codec parameters from
371+
// this packet as well, so wait for major sync before intitializing.
372+
if (spdif_ctx->codec_id == AV_CODEC_ID_TRUEHD &&
373+
!truehd_has_major_sync(spdif_ctx->avpkt))
374+
{
375+
spdif_ctx->dropped_startup_packets++;
376+
drop_packet = true;
377+
goto done;
378+
}
329379
if (init_filter(da) < 0)
330380
goto done;
331381
mp_assert(spdif_ctx->avpkt);
@@ -339,6 +389,12 @@ static void ad_spdif_process(struct mp_filter *da)
339389
MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
340390
goto done;
341391
}
392+
if (spdif_ctx->dropped_startup_packets) {
393+
MP_VERBOSE(da, "dropped %d TrueHD packet%s before major sync\n",
394+
spdif_ctx->dropped_startup_packets,
395+
spdif_ctx->dropped_startup_packets == 1 ? "" : "s");
396+
spdif_ctx->dropped_startup_packets = 0;
397+
}
342398

343399
out = mp_aframe_new_ref(spdif_ctx->fmt);
344400
int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
@@ -360,6 +416,8 @@ static void ad_spdif_process(struct mp_filter *da)
360416
talloc_free(mpkt);
361417
if (out) {
362418
mp_pin_in_write(da->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
419+
} else if (drop_packet) {
420+
mp_filter_internal_mark_progress(da);
363421
} else {
364422
mp_filter_internal_mark_failed(da);
365423
}
@@ -423,6 +481,7 @@ static const struct mp_filter_info ad_spdif_filter = {
423481
.name = "ad_spdif",
424482
.priv_size = sizeof(struct spdifContext),
425483
.process = ad_spdif_process,
484+
.reset = ad_spdif_reset,
426485
.destroy = ad_spdif_destroy,
427486
};
428487

0 commit comments

Comments
 (0)