Skip to content

Commit f1863fb

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. Partially addresses: https://trac.ffmpeg.org/ticket/9569 Signed-off-by: Nathan Lucas <nlucasgit@gmail.com>
1 parent 390ebe2 commit f1863fb

1 file changed

Lines changed: 48 additions & 5 deletions

File tree

audio/decode/ad_spdif.c

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <libavformat/avformat.h>
2424
#include <libavcodec/avcodec.h>
25+
#include <libavutil/intreadwrite.h>
2526
#include <libavutil/opt.h>
2627

2728
#include "audio/aframe.h"
@@ -48,6 +49,7 @@ struct spdifContext {
4849
uint8_t out_buffer[OUTBUF_SIZE];
4950
bool need_close;
5051
bool use_dts_hd;
52+
int dropped_startup_packets;
5153
struct mp_aframe *fmt;
5254
int sstride;
5355
struct mp_aframe_pool *pool;
@@ -74,24 +76,45 @@ static int write_packet(void *p, const uint8_t *buf, int buf_size)
7476
return buf_size;
7577
}
7678

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

8383
if (lavf_ctx) {
84-
if (spdif_ctx->need_close)
84+
if (write_trailer && spdif_ctx->need_close)
8585
av_write_trailer(lavf_ctx);
8686
if (lavf_ctx->pb)
8787
av_freep(&lavf_ctx->pb->buffer);
8888
avio_context_free(&lavf_ctx->pb);
8989
avformat_free_context(lavf_ctx);
9090
spdif_ctx->lavf_ctx = NULL;
9191
}
92+
spdif_ctx->need_close = false;
93+
}
94+
95+
// (called on both filter destruction _and_ if lavf fails to init)
96+
static void ad_spdif_destroy(struct mp_filter *da)
97+
{
98+
struct spdifContext *spdif_ctx = da->priv;
99+
100+
close_lavf_context(spdif_ctx, true);
92101
mp_free_av_packet(&spdif_ctx->avpkt);
93102
}
94103

104+
static void ad_spdif_reset(struct mp_filter *da)
105+
{
106+
struct spdifContext *spdif_ctx = da->priv;
107+
108+
close_lavf_context(spdif_ctx, false);
109+
spdif_ctx->dropped_startup_packets = 0;
110+
TA_FREEP(&spdif_ctx->fmt);
111+
}
112+
113+
static bool truehd_has_major_sync(AVPacket *pkt)
114+
{
115+
return pkt->size >= 8 && AV_RB32(pkt->data + 4) == 0xf8726fba;
116+
}
117+
95118
static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
96119
int *out_profile, int *out_rate)
97120
{
@@ -318,6 +341,7 @@ static void ad_spdif_process(struct mp_filter *da)
318341
struct demux_packet *mpkt = inframe.data;
319342
struct mp_aframe *out = NULL;
320343
double pts = mpkt->pts;
344+
bool drop_packet = false;
321345

322346
if (!spdif_ctx->avpkt) {
323347
spdif_ctx->avpkt = av_packet_alloc();
@@ -326,6 +350,16 @@ static void ad_spdif_process(struct mp_filter *da)
326350
mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL);
327351
spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0;
328352
if (!spdif_ctx->lavf_ctx) {
353+
// A fresh TrueHD spdif muxer needs a major sync before it can begin
354+
// muxing the stream, and init_filter() probes codec parameters from
355+
// this packet as well, so wait for major sync before initializing.
356+
if (spdif_ctx->codec_id == AV_CODEC_ID_TRUEHD &&
357+
!truehd_has_major_sync(spdif_ctx->avpkt))
358+
{
359+
spdif_ctx->dropped_startup_packets++;
360+
drop_packet = true;
361+
goto done;
362+
}
329363
if (init_filter(da) < 0)
330364
goto done;
331365
mp_assert(spdif_ctx->avpkt);
@@ -339,6 +373,12 @@ static void ad_spdif_process(struct mp_filter *da)
339373
MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
340374
goto done;
341375
}
376+
if (spdif_ctx->dropped_startup_packets) {
377+
MP_VERBOSE(da, "dropped %d TrueHD packet%s before major sync\n",
378+
spdif_ctx->dropped_startup_packets,
379+
spdif_ctx->dropped_startup_packets == 1 ? "" : "s");
380+
spdif_ctx->dropped_startup_packets = 0;
381+
}
342382

343383
out = mp_aframe_new_ref(spdif_ctx->fmt);
344384
int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
@@ -360,6 +400,8 @@ static void ad_spdif_process(struct mp_filter *da)
360400
talloc_free(mpkt);
361401
if (out) {
362402
mp_pin_in_write(da->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
403+
} else if (drop_packet) {
404+
mp_filter_internal_mark_progress(da);
363405
} else {
364406
mp_filter_internal_mark_failed(da);
365407
}
@@ -423,6 +465,7 @@ static const struct mp_filter_info ad_spdif_filter = {
423465
.name = "ad_spdif",
424466
.priv_size = sizeof(struct spdifContext),
425467
.process = ad_spdif_process,
468+
.reset = ad_spdif_reset,
426469
.destroy = ad_spdif_destroy,
427470
};
428471

0 commit comments

Comments
 (0)