Skip to content

Commit 1388a45

Browse files
nathanlucaskasper93
authored andcommitted
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. Cache codec parameters from the initial probe so recreating the muxer does not probe a partial packet after a seek or replace known values with fallbacks. 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. Warn periodically if no major sync has been found. Partially addresses: https://trac.ffmpeg.org/ticket/9569 Signed-off-by: Nathan Lucas <nlucasgit@gmail.com>
1 parent 24c1cc5 commit 1388a45

1 file changed

Lines changed: 69 additions & 9 deletions

File tree

audio/decode/ad_spdif.c

Lines changed: 69 additions & 9 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"
@@ -37,6 +38,7 @@
3738
#include "options/options.h"
3839

3940
#define OUTBUF_SIZE 65536
41+
#define TRUEHD_MAJOR_SYNC_WARN_INTERVAL 6000
4042

4143
struct spdifContext {
4244
struct mp_log *log;
@@ -48,6 +50,10 @@ struct spdifContext {
4850
uint8_t out_buffer[OUTBUF_SIZE];
4951
bool need_close;
5052
bool use_dts_hd;
53+
bool codec_params_probed;
54+
int codec_profile;
55+
int codec_rate;
56+
unsigned int dropped_startup_packets;
5157
struct mp_aframe *fmt;
5258
int sstride;
5359
struct mp_aframe_pool *pool;
@@ -74,24 +80,45 @@ static int write_packet(void *p, const uint8_t *buf, int buf_size)
7480
return buf_size;
7581
}
7682

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

8387
if (lavf_ctx) {
84-
if (spdif_ctx->need_close)
88+
if (write_trailer && spdif_ctx->need_close)
8589
av_write_trailer(lavf_ctx);
8690
if (lavf_ctx->pb)
8791
av_freep(&lavf_ctx->pb->buffer);
8892
avio_context_free(&lavf_ctx->pb);
8993
avformat_free_context(lavf_ctx);
9094
spdif_ctx->lavf_ctx = NULL;
9195
}
96+
spdif_ctx->need_close = false;
97+
TA_FREEP(&spdif_ctx->fmt);
98+
}
99+
100+
// (called on both filter destruction _and_ if lavf fails to init)
101+
static void ad_spdif_destroy(struct mp_filter *da)
102+
{
103+
struct spdifContext *spdif_ctx = da->priv;
104+
105+
close_lavf_context(spdif_ctx, true);
92106
mp_free_av_packet(&spdif_ctx->avpkt);
93107
}
94108

109+
static void ad_spdif_reset(struct mp_filter *da)
110+
{
111+
struct spdifContext *spdif_ctx = da->priv;
112+
113+
close_lavf_context(spdif_ctx, false);
114+
spdif_ctx->dropped_startup_packets = 0;
115+
}
116+
117+
static bool truehd_has_major_sync(const AVPacket *pkt)
118+
{
119+
return pkt->size >= 8 && AV_RB32(pkt->data + 4) == 0xf8726fba;
120+
}
121+
95122
static void determine_codec_params(struct mp_filter *da, AVPacket *pkt,
96123
int *out_profile, int *out_rate)
97124
{
@@ -171,10 +198,17 @@ static int init_filter(struct mp_filter *da)
171198

172199
AVPacket *pkt = spdif_ctx->avpkt;
173200

174-
int profile = AV_PROFILE_UNKNOWN;
175-
int c_rate = 0;
176-
determine_codec_params(da, pkt, &profile, &c_rate);
177-
MP_VERBOSE(da, "In: profile=%d samplerate=%d\n", profile, c_rate);
201+
if (!spdif_ctx->codec_params_probed) {
202+
spdif_ctx->codec_profile = AV_PROFILE_UNKNOWN;
203+
determine_codec_params(da, pkt, &spdif_ctx->codec_profile,
204+
&spdif_ctx->codec_rate);
205+
spdif_ctx->codec_params_probed = true;
206+
MP_VERBOSE(da, "In: profile=%d samplerate=%d\n",
207+
spdif_ctx->codec_profile, spdif_ctx->codec_rate);
208+
}
209+
210+
int profile = spdif_ctx->codec_profile;
211+
int c_rate = spdif_ctx->codec_rate;
178212

179213
AVFormatContext *lavf_ctx = avformat_alloc_context();
180214
if (!lavf_ctx)
@@ -318,6 +352,7 @@ static void ad_spdif_process(struct mp_filter *da)
318352
struct demux_packet *mpkt = inframe.data;
319353
struct mp_aframe *out = NULL;
320354
double pts = mpkt->pts;
355+
bool drop_packet = false;
321356

322357
if (!spdif_ctx->avpkt) {
323358
spdif_ctx->avpkt = av_packet_alloc();
@@ -326,6 +361,22 @@ static void ad_spdif_process(struct mp_filter *da)
326361
mp_set_av_packet(spdif_ctx->avpkt, mpkt, NULL);
327362
spdif_ctx->avpkt->pts = spdif_ctx->avpkt->dts = 0;
328363
if (!spdif_ctx->lavf_ctx) {
364+
// A fresh TrueHD spdif muxer needs a major sync to initialize its
365+
// stream parameters, so wait for major sync before initializing.
366+
if (spdif_ctx->codec_id == AV_CODEC_ID_TRUEHD &&
367+
!truehd_has_major_sync(spdif_ctx->avpkt))
368+
{
369+
spdif_ctx->dropped_startup_packets++;
370+
if (spdif_ctx->dropped_startup_packets %
371+
TRUEHD_MAJOR_SYNC_WARN_INTERVAL == 0)
372+
{
373+
MP_WARN(da, "Still waiting for TrueHD major sync after "
374+
"dropping %u packets.\n",
375+
spdif_ctx->dropped_startup_packets);
376+
}
377+
drop_packet = true;
378+
goto done;
379+
}
329380
if (init_filter(da) < 0)
330381
goto done;
331382
mp_assert(spdif_ctx->avpkt);
@@ -339,6 +390,12 @@ static void ad_spdif_process(struct mp_filter *da)
339390
MP_ERR(da, "spdif mux error: '%s'\n", mp_strerror(AVUNERROR(ret)));
340391
goto done;
341392
}
393+
if (spdif_ctx->dropped_startup_packets) {
394+
MP_VERBOSE(da, "dropped %u TrueHD packet%s before major sync\n",
395+
spdif_ctx->dropped_startup_packets,
396+
spdif_ctx->dropped_startup_packets == 1 ? "" : "s");
397+
spdif_ctx->dropped_startup_packets = 0;
398+
}
342399

343400
out = mp_aframe_new_ref(spdif_ctx->fmt);
344401
int samples = spdif_ctx->out_buffer_len / spdif_ctx->sstride;
@@ -360,6 +417,8 @@ static void ad_spdif_process(struct mp_filter *da)
360417
talloc_free(mpkt);
361418
if (out) {
362419
mp_pin_in_write(da->ppins[1], MAKE_FRAME(MP_FRAME_AUDIO, out));
420+
} else if (drop_packet) {
421+
mp_filter_internal_mark_progress(da);
363422
} else {
364423
mp_filter_internal_mark_failed(da);
365424
}
@@ -423,6 +482,7 @@ static const struct mp_filter_info ad_spdif_filter = {
423482
.name = "ad_spdif",
424483
.priv_size = sizeof(struct spdifContext),
425484
.process = ad_spdif_process,
485+
.reset = ad_spdif_reset,
426486
.destroy = ad_spdif_destroy,
427487
};
428488

0 commit comments

Comments
 (0)