Hi Anssi, Cc'ing you because you are listed as the maintainer for spdif*. Could you please review the PR on Forgejo and consider merging it if it looks good? There is a FATE sample attached to the PR that needs to be uploaded. I have e-mailed [email protected] but it does not appear to be uploaded yet. Once that is done, I believe the PR is ready to merge. I don't intend to make any more changes unless requested or needed. If you are no longer maintaining this code, a pointer to the appropriate reviewer would also be appreciated.
Thank you, Nathan On Sat, Jun 20, 2026 at 2:39 PM Nathan Lucas via ffmpeg-devel < [email protected]> wrote: > PR #23542 opened by Nathan Lucas (nathanlucas) > URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23542 > Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23542.patch > > ### Background > > This is fixing audio dropouts heard by some users (including myself) while > watching UHD Blu-Ray movies with TrueHD/Atmos that have been remuxed with > tools like makemkv, trueHD audio passed-through using spdifenc. I can't > say what the exact cause of the dropouts are. Downstream consumers of the > MAT frames and the audio stream seem more or less senstive to timing > depending on the setup. Ticket numbers are in the commit message. > > The main pattern I found is that the audio dropout locations are at the > uhd bluray seamless branch locations, which I could confirm looking at the > playlist on disc. > > In some movies, over several hundred truehd access units leading up to the > branch, input_timing sometimes falls behind by as much as 1800 timing units > relative to output_timing. 1800 actually shows up a lot in my traces. > After the branch, input_timing snaps back to normal (usually) and there is > a large gap in timing that is not corrected because the current sanity > check is too tight and ignores it. At the sample rate used in these > movies, 1 timing unit is 64 bytes. 1800 * 64 = 115200 bytes which is well > above the current MAT_FRAME_SIZE / 2 (30712) limit. 115200 bytes in > IEC/MAT timing is 37.5ms of IEC carrier. That lost carrier could be > related to the dropouts as well. Increasing the limit to MAT_FRAME_SIZE * > 2 is just enough to never drop padding (tested against over 280 movies). > > Related to this is that remuxes of some movie titles have truehd timing > discontinuities at the branch points. spdifenc currently only uses > input_timing to compute padding to maintain timing, but at the > discontinuities that does not work and the calculated padding is wrong. > The sanity check catches most of these but again drops padding to zero and > still does not preserve timing. I've observed that the branch > discontinuity seems to always restart with a major sync and restart header, > so output_timing from the restart header can be used to detect the > discontinuities and also to compute padding and preserve timing. Since > input_timing is relative to output_timing, padding can be determined by the > output_timing - input_timing difference before and after the > discontinuity. I have also observed the 1800 timing unit gap at > discontinuities and this method is computing sensible amounts of padding. > > Some interesting data to support the improvement/correctness: > > Below are three tables. The movies in them were remuxed using the latest > version of makemkv a few weeks ago. The tables show how many MAT frames > were expected by the end of the movie verses how many were actually > produced. The number of IEC/MAT frames emitted by spdifenc is related to > the padding that is computed so I think this is an interesting data point. > Pay attention to the expected vs. actual delta. > > ``` > // current upstream input_timing-based padding + MAT/2 cutoff > Movie AUs MATs Expected Delta > remainder_AUs padding_dropped > Wrath of Khan Theatrical 8140434 339161 339183 -22 > 18 13 > Wrath of Khan DC 8389833 349549 349575 -26 > 9 16 > Martian Theatrical 10197138 424838 424881 -43 > 18 23 > Martian Extended 10896787 453987 454033 -46 > 19 27 > Alien Romulus 8570262 357094 357094 0 > 6 0 > Avatar 11666406 486054 486100 -46 > 6 27 > Avatar Way of Water 13865005 577651 577708 -57 > 13 33 > Monsters University 7474067 311244 311417 -173 > 11 99 > ``` > > The current upstream method with the MAT_FRAME_SIZE/2 sanity check drops a > lot of padding in all but Alien Romulus which does not have that -1800 > input_timing behavior. The other movies do have that behavior. Large > amounts of padding/timing are dropped and they fall behind the expected > output. Each MAT not emitted accounts for 20ms of IEC/MAT carrier time. > > ``` > // input_timing only + MAT*2, no output_timing discontinuity handling > Movie AUs MATs Expected Delta > remainder_AUs padding_dropped > Wrath of Khan Theatrical 8140434 339184 339183 +1 > 18 0 > Wrath of Khan DC 8389833 349550 349575 -25 > 9 15 > Martian theatrical 10197138 424880 424881 -1 > 18 0 > Martian extended 10896787 453991 454033 -42 > 19 25 > Alien Romulus 8570262 357094 357094 0 > 6 0 > Avatar 11666406 486100 486100 0 > 6 0 > Avatar Way of Water 13865005 577708 577708 0 > 13 0 > Monsters University 7474067 311419 311417 +2 > 11 0 > ``` > > If I only change the sanity limit to MAT_FRAME_SIZE*2 then movies without > timing discontinuities look much better. The Martian Extended and Wrath of > Khan have a lot of discontinuities that are not resolved in this case > because input_timing-based padding does not there. > > ``` > // output_timing discontinuity handling + MAT*2 > Movie AUs MATs Expected Delta > remainder_AUs discontinuities > Wrath of Khan Theatrical 8140434 339184 339183 +1 > 18 0 > Wrath of Khan DC 8389833 349576 349575 +1 > 9 16 > Martian Theatrical 10197138 424880 424881 -1 > 18 0 > Martian Extended 10896787 454032 454033 -1 > 19 25 > Alien Romulus 8570262 357094 357094 0 > 6 0 > Avatar 11666406 486100 486100 0 > 6 0 > Avatar Way of Water 13865005 577708 577708 0 > 13 5 > Monsters University 7474067 311419 311417 +2 > 11 0 > ``` > > Using output_timing-input_timing before and after the discontinuity in > those two movies resolves them as well. > > I have uploaded a histogram of padding values over 287 movies, 31 with > output_timing discontinuities, that supports the sanity check move from > MAT_FRAME_SIZE / 2 to MAT_FRAME_SIZE * 2. The top histogram shows > input_timing-based padding in blue. Red is input_timing-based padding > values at output_timing discontinuities (i.e. red padding is probably > wrong). The bottom histogram shows input_timing-based padding in blue. > Orange is padding that was computed at a discontinuity using using the > output_timing-input_timing offset before and after the discontinuity. One > curious thing worth noting is the large number of "red" padding that still > lands just under MAT_FRAME_SIZE * 2. I've observed that sometimes the > remux either adds or removes one truehd access unit at the branch. There > technically is a discontinuity there but input_timing-based padding is > reasonable in those cases. Either way, output_timing-input_timing method > also works there. > > The last thing I want to add on this topic is that increasing the sanity > check to MAT_FRAME_SIZE * 2 will allow up to 2 MAT frames to be completed > by one AVPacket. Since likely nobody is expecting spdifenc to write more > than one IEC/MAT frame at once, these changes will still only output one > frame per AVPacket. When multiple are ready to write, they will be written > out one per AVPacket. > > ### spdifenc context reset via AVOption > > Need comments on this change. Seeking will almost always cause a > discontinuity event to get logged because the timing before and after the > seek are discontinuous by nature. In rare cases, padding may be added if > it doesn't fail the sanity check. The current MAT buffer will likely also > contain truehd access units from before the seek which doesn't make sense > to preserve after the seek. So I think players should reset the spdifenc > context on seek but the only way to do that currently is to free the > context and create a new one. I'm just trying to make the reset easier. > Is it preferable for the user to destroy/recreate the context on seek, or > should a one-shot AVOption to reset before the next AVPacket be provided? > > > >From 24c76f5a966072eaa54c0a2c12df12a5fa6f08d4 Mon Sep 17 00:00:00 2001 > From: Nathan Lucas <[email protected]> > Date: Tue, 16 Jun 2026 07:25:30 -0600 > Subject: [PATCH 1/2] avformat/spdifenc: add one-shot reset option > > Players may seek while reusing the same spdifenc contex. Some statefull > passthrough formats such as TrueHD keep state across packets. After a > seek, that state belongs to the old stream position and can cause the > next packet to use stale timing or buffering state. > > Add a runtime "reset" AVOption. When set, spdifenc resets its muxer > state before processing the next packet and then clears the option > automatically. This lets callers discard stale seek state without > tearing down and recreating the spdif muxer context. > > Signed-off-by: Nathan Lucas <[email protected]> > --- > libavformat/spdifenc.c | 25 +++++++++++++++++++++++++ > 1 file changed, 25 insertions(+) > > diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c > index ab3f73da0d..b0f96feadc 100644 > --- a/libavformat/spdifenc.c > +++ b/libavformat/spdifenc.c > @@ -88,6 +88,7 @@ typedef struct IEC61937Context { > int dtshd_fallback; > #define SPDIF_FLAG_BIGENDIAN 0x01 > int spdif_flags; > + int reset_requested; > > /// function, which generates codec dependent header information. > /// Sets data_type and pkt_offset, and length_code, out_bytes, > out_buf if necessary > @@ -99,6 +100,7 @@ static const AVOption options[] = { > { "be", "output in big-endian format (for use as s16be)", 0, > AV_OPT_TYPE_CONST, {.i64 = SPDIF_FLAG_BIGENDIAN}, 0, INT_MAX, > AV_OPT_FLAG_ENCODING_PARAM, .unit = "spdif_flags" }, > { "dtshd_rate", "mux complete DTS frames in HD mode at the specified > IEC958 rate (in Hz, default 0=disabled)", offsetof(IEC61937Context, > dtshd_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 768000, > AV_OPT_FLAG_ENCODING_PARAM }, > { "dtshd_fallback_time", "min secs to strip HD for after an overflow (-1: > till the end, default 60)", offsetof(IEC61937Context, dtshd_fallback), > AV_OPT_TYPE_INT, {.i64 = 60}, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, > +{ "reset", "request one-shot muxer state reset before processing the next > packet", offsetof(IEC61937Context, reset_requested), AV_OPT_TYPE_BOOL, > {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM }, > { NULL }, > }; > > @@ -109,6 +111,24 @@ static const AVClass spdif_class = { > .version = LIBAVUTIL_VERSION_INT, > }; > > +static void spdif_reset_state(AVFormatContext *s) > +{ > + IEC61937Context *ctx = s->priv_data; > + > + ctx->hd_buf_count = 0; > + ctx->hd_buf_filled = 0; > + ctx->hd_buf_idx = 0; > + ctx->dtshd_skip = 0; > + > + ctx->truehd_prev_time = 0; > + ctx->truehd_prev_size = 0; > + > + ctx->out_buf = NULL; > + ctx->out_bytes = 0; > + ctx->length_code = 0; > + ctx->pkt_offset = 0; > +} > + > static int spdif_header_ac3(AVFormatContext *s, AVPacket *pkt) > { > IEC61937Context *ctx = s->priv_data; > @@ -628,6 +648,11 @@ static int spdif_write_packet(struct AVFormatContext > *s, AVPacket *pkt) > IEC61937Context *ctx = s->priv_data; > int ret, padding; > > + if (ctx->reset_requested) { > + spdif_reset_state(s); > + ctx->reset_requested = 0; > + } > + > ctx->out_buf = pkt->data; > ctx->out_bytes = pkt->size; > ctx->length_code = FFALIGN(pkt->size, 2) << 3; > -- > 2.52.0 > > > >From 0cbcb55a0d562a5d043988068ac12697e934df90 Mon Sep 17 00:00:00 2001 > From: Nathan Lucas <[email protected]> > Date: Tue, 16 Jun 2026 08:05:20 -0600 > Subject: [PATCH 2/2] avformat/spdifenc: preserve TrueHD MAT padding across > branches > > Some TrueHD streams, particularly remuxes of seamless branching Blu-rays, > contain input_timing gaps that require more padding than the current > MAT_FRAME_SIZE / 2 limit allows. spdifenc treats this padding as > invalid and drops it, breaking MAT timing and IEC 61937 carrier cadence, > causing TrueHD/Atmos dropouts on some receivers. > > Allow larger valid padding gaps and queue completed MAT buffers so the > muxer can preserve the carrier cadence while still writing at most one > MAT buffer per input packet. > > Some branches also have discontinuous timing, so input_timing cannot be > directly used to compute padding at those boundaries. When output_timing > is available from a TrueHD restart header, use it to detect those > discontinuities and compute padding from the change in > output_timing - input_timing offset across the boundary instead. > > Fixes: https://trac.ffmpeg.org/ticket/9569 > Fixes: https://trac.ffmpeg.org/ticket/10948 > See also: https://github.com/mpv-player/mpv/issues/9659 > See also: https://github.com/mpv-player/mpv/issues/13943 > > Signed-off-by: Nathan Lucas <[email protected]> > --- > libavformat/spdifenc.c | 287 +++++++++++++++++++++++++++++++++++++---- > 1 file changed, 264 insertions(+), 23 deletions(-) > > diff --git a/libavformat/spdifenc.c b/libavformat/spdifenc.c > index b0f96feadc..400f09e67d 100644 > --- a/libavformat/spdifenc.c > +++ b/libavformat/spdifenc.c > @@ -54,9 +54,20 @@ > #include "libavcodec/adts_parser.h" > #include "libavcodec/dca.h" > #include "libavcodec/dca_syncwords.h" > +#include "libavcodec/get_bits.h" > #include "libavutil/mem.h" > #include "libavutil/opt.h" > > +/* > + * With padding up to MAT_FRAME_SIZE*2 allowed, TrueHD/MLP can complete > two MAT > + * buffers with one AVPacket. A third is needed for the active buffer, so > no > + * less than three are required. Since no more than one MAT can be > written per > + * AVPacket, extra headroom has been given. > + * > + * E-AC-3 and DTS-HD only need hd_buf[0]. > + */ > +#define HD_BUF_COUNT 6 > + > typedef struct IEC61937Context { > const AVClass *av_class; > enum IEC61937DataType data_type;///< burst info - reference to type > of payload of the data-burst > @@ -71,17 +82,22 @@ typedef struct IEC61937Context { > int use_preamble; ///< preamble enabled (disabled for > exactly pre-padded DTS) > int extra_bswap; ///< extra bswap for payload (for LE > DTS => standard BE DTS) > > - uint8_t *hd_buf[2]; ///< allocated buffers to concatenate > hd audio frames > + uint8_t *hd_buf[HD_BUF_COUNT]; ///< allocated buffers to concatenate > hd audio frames > int hd_buf_size; ///< size of the hd audio buffer > (eac3, dts4) > int hd_buf_count; ///< number of frames in the hd audio > buffer (eac3) > int hd_buf_filled; ///< amount of bytes in the hd audio > buffer (eac3, truehd) > int hd_buf_idx; ///< active hd buffer index (truehd) > + int hd_buf_next_ready_idx; ///< oldest completed truehd MAT > buffer ready to write (truehd) > + int hd_buf_ready_count; ///< number of completed TrueHD MAT > buffers ready to write (truehd) > > int dtshd_skip; ///< counter used for skipping DTS-HD > frames > > uint16_t truehd_prev_time; ///< input_timing from the last frame > int truehd_prev_size; ///< previous frame size in bytes, > including any MAT codes > int truehd_samples_per_frame; ///< samples per frame for padding > calculation > + uint16_t truehd_output_timing; ///< expected output_timing for > truehd restart headers > + int truehd_output_timing_valid; ///< restart header output_timing has > been read > + int truehd_oi_delta; ///< signed > (output_timing-samples_per_frame)-input_timing > > /* AVOptions: */ > int dtshd_rate; > @@ -118,10 +134,15 @@ static void spdif_reset_state(AVFormatContext *s) > ctx->hd_buf_count = 0; > ctx->hd_buf_filled = 0; > ctx->hd_buf_idx = 0; > + ctx->hd_buf_next_ready_idx = 0; > + ctx->hd_buf_ready_count = 0; > ctx->dtshd_skip = 0; > > ctx->truehd_prev_time = 0; > ctx->truehd_prev_size = 0; > + ctx->truehd_output_timing = 0; > + ctx->truehd_output_timing_valid = 0; > + ctx->truehd_oi_delta = 0; > > ctx->out_buf = NULL; > ctx->out_bytes = 0; > @@ -440,32 +461,162 @@ static const struct { > MAT_CODE(MAT_FRAME_SIZE - sizeof(mat_end_code), mat_end_code), > }; > > +/** > + * Get the next index in the MAT buffer ring. > + */ > +static int truehd_next_mat_buffer(int idx) > +{ > + return (idx + 1) % HD_BUF_COUNT; > +} > + > +/** > + * Mark the current MAT buffer ready and advance to the next free buffer. > + */ > +static int truehd_enqueue_mat(AVFormatContext *s) > +{ > + IEC61937Context *ctx = s->priv_data; > + > + if (ctx->hd_buf_ready_count >= HD_BUF_COUNT - 1) { > + av_log(s, AV_LOG_ERROR, "Too many completed TrueHD MAT frames > pending\n"); > + return AVERROR(EINVAL); > + } > + > + ctx->hd_buf_ready_count++; > + ctx->hd_buf_idx = truehd_next_mat_buffer(ctx->hd_buf_idx); > + return 0; > +} > + > +typedef struct TrueHDAccessUnitInfo { > + uint16_t input_timing; > + uint16_t output_timing; > + int has_output_timing; > + int samples_per_frame; > +} TrueHDAccessUnitInfo; > + > +static int truehd_parse_access_unit(const uint8_t *au_data, int au_size, > + TrueHDAccessUnitInfo *au) > +{ > + GetBitContext gb; > + int major_sync_size = 28; > + int ratebits; > + int num_substreams; > + int sync_word; > + int ret; > + int i; > + const uint8_t *data; > + int size; > + > + memset(au, 0, sizeof(*au)); > + > + if (au_size < 4) > + return AVERROR_INVALIDDATA; > + > + data = au_data + 4; > + size = au_size - 4; > + > + au->input_timing = AV_RB16(au_data + 2); > + > + if (size < 6 || AV_RB24(data) != 0xf8726f) > + return 0; > + > + /* major sync unit, fetch sample rate */ > + if (data[3] == 0xba) > + ratebits = data[4] >> 4; > + else if (data[3] == 0xbb) > + ratebits = data[5] >> 4; > + else > + return AVERROR_INVALIDDATA; > + au->samples_per_frame = 40 << (ratebits & 3); > + > + if (size < 27) > + return 0; > + > + if (data[3] == 0xba && data[25] & 1) { > + int ext_size = data[26] >> 4; > + major_sync_size += 2 + ext_size * 2; > + } > + > + if (major_sync_size > size) > + return 0; > + > + ret = init_get_bits8(&gb, data + major_sync_size, size - > major_sync_size); > + if (ret < 0) > + return ret; > + > + num_substreams = data[16] >> 4; > + if (num_substreams <= 0) > + return 0; > + > + for (i = 0; i < num_substreams; i++) { > + int extra_word; > + if (get_bits_left(&gb) < 16) > + return 0; > + extra_word = get_bits1(&gb); > + skip_bits_long(&gb, 15); > + if (!extra_word) > + continue; > + if (get_bits_left(&gb) < 16) > + return 0; > + skip_bits_long(&gb, 16); > + } > + > + /* output timing is always at least found in the first substream if it > + * is present at all, so only walk the first substream. > + */ > + if (get_bits_left(&gb) < 1) > + return 0; > + if (!get_bits1(&gb)) /* ! block_header_exists */ > + return 0; > + if (get_bits_left(&gb) < 1) > + return 0; > + if (!get_bits1(&gb)) /* ! restart_header_exists */ > + return 0; > + if (get_bits_left(&gb) < 13 + 1 + 16) > + return 0; > + > + sync_word = get_bits(&gb, 13); > + if (sync_word != (0x31ea >> 1)) > + return 0; > + skip_bits1(&gb); /* noise_type */ > + > + au->output_timing = get_bits(&gb, 16); > + au->has_output_timing = 1; > + > + return 0; > +} > + > +/** > + * Interpret the modulo-2^16 difference of a and b as a signed delta. > + * Only unambiguous when the true delta is < 2^15. > + */ > +static int u16_signed_delta(uint16_t a, uint16_t b) > +{ > + int delta = (uint16_t)(a - b); > + return delta >= 0x8000 ? delta - 0x10000 : delta; > +} > + > static int spdif_header_truehd(AVFormatContext *s, AVPacket *pkt) > { > IEC61937Context *ctx = s->priv_data; > uint8_t *hd_buf = ctx->hd_buf[ctx->hd_buf_idx]; > - int ratebits; > + TrueHDAccessUnitInfo au; > int padding_remaining = 0; > + int max_padding_per_packet = MAT_FRAME_SIZE * 2; > + int output_discontinuity = 0; > uint16_t input_timing; > int total_frame_size = pkt->size; > const uint8_t *dataptr = pkt->data; > int data_remaining = pkt->size; > int have_pkt = 0; > int next_code_idx; > + int ret; > > - if (pkt->size < 10) > - return AVERROR_INVALIDDATA; > + ret = truehd_parse_access_unit(pkt->data, pkt->size, &au); > + if (ret < 0) > + return ret; > > - if (AV_RB24(pkt->data + 4) == 0xf8726f) { > - /* major sync unit, fetch sample rate */ > - if (pkt->data[7] == 0xba) > - ratebits = pkt->data[8] >> 4; > - else if (pkt->data[7] == 0xbb) > - ratebits = pkt->data[9] >> 4; > - else > - return AVERROR_INVALIDDATA; > - > - ctx->truehd_samples_per_frame = 40 << (ratebits & 3); > + if (au.samples_per_frame) { > + ctx->truehd_samples_per_frame = au.samples_per_frame; > av_log(s, AV_LOG_TRACE, "TrueHD samples per frame: %d\n", > ctx->truehd_samples_per_frame); > } > @@ -473,8 +624,72 @@ static int spdif_header_truehd(AVFormatContext *s, > AVPacket *pkt) > if (!ctx->truehd_samples_per_frame) > return AVERROR_INVALIDDATA; > > - input_timing = AV_RB16(pkt->data + 2); > - if (ctx->truehd_prev_size) { > + input_timing = au.input_timing; > + > + ctx->truehd_output_timing += ctx->truehd_samples_per_frame; > + if (au.has_output_timing) { > + if (ctx->truehd_output_timing_valid && > + au.output_timing != ctx->truehd_output_timing) { > + /* > + * Each TrueHD access unit, output_timing increments by > + * samples_per_frame, and input_timing nominally increments > by the > + * same amount. However if input_timing has fallen behind > relative > + * to output_timing at a discontinuity then additional > padding can be > + * added to preserve the correct MAT timing and IEC61937 > carrier. So > + * at the discontinuity compute padding based on the > output-input > + * timing delta relative to the new output-input delta. > Negative or > + * excessive padding will be ignored and logged. > + * > + * output_timing is always ahead by one frame period, so one > period is > + * always subtracted before comparison. > + */ > + int bytes_per_sample = 2560 / ctx->truehd_samples_per_frame; > + uint16_t output_timing_minus_spf = au.output_timing - > + > ctx->truehd_samples_per_frame; > + int previous_oi_delta = ctx->truehd_oi_delta; > + int current_oi_delta = > u16_signed_delta(output_timing_minus_spf, > + input_timing); > + int prev_padding = 0; > + int discontinuity_padding = 0; > + > + output_discontinuity = 1; > + discontinuity_padding = (previous_oi_delta - > current_oi_delta) * > + bytes_per_sample; > + > + if (ctx->truehd_prev_size) > + prev_padding = 2560 - ctx->truehd_prev_size; > + > + padding_remaining = prev_padding + discontinuity_padding; > + > + if (padding_remaining < 0 || padding_remaining > > max_padding_per_packet) { > + avpriv_request_sample(s, > + "Unusual TrueHD output_timing > discontinuity: " > + "expected %"PRIu16" got %"PRIu16", " > + "timing delta %d => %d, prev > padding %d, " > + "discontinuity padding %d, " > + "total padding %d ignored", > + ctx->truehd_output_timing, > au.output_timing, > + previous_oi_delta, > current_oi_delta, prev_padding, > + discontinuity_padding, > padding_remaining); > + padding_remaining = 0; > + } else { > + av_log(s, AV_LOG_VERBOSE, > + "TrueHD output_timing discontinuity: " > + "expected %"PRIu16" got %"PRIu16", " > + "timing delta %d => %d, prev padding %d, " > + "discontinuity padding %d, " > + "total padding %d\n", > + ctx->truehd_output_timing, au.output_timing, > + previous_oi_delta, current_oi_delta, prev_padding, > + discontinuity_padding, padding_remaining); > + } > + } > + > + ctx->truehd_output_timing = au.output_timing; > + ctx->truehd_output_timing_valid = 1; > + } > + > + if (ctx->truehd_prev_size && !output_discontinuity) { > uint16_t delta_samples = input_timing - ctx->truehd_prev_time; > /* > * One multiple-of-48kHz frame is 1/1200 sec and the IEC 61937 > rate > @@ -494,13 +709,20 @@ static int spdif_header_truehd(AVFormatContext *s, > AVPacket *pkt) > delta_samples, delta_bytes); > > /* sanity check */ > - if (padding_remaining < 0 || padding_remaining >= MAT_FRAME_SIZE > / 2) { > + if (padding_remaining < 0 || padding_remaining > > max_padding_per_packet) { > avpriv_request_sample(s, "Unusual frame timing: %"PRIu16" => > %"PRIu16", %d samples/frame", > ctx->truehd_prev_time, input_timing, > ctx->truehd_samples_per_frame); > padding_remaining = 0; > } > } > > + if (ctx->truehd_output_timing_valid) { > + uint16_t output_timing_minus_spf = ctx->truehd_output_timing - > + ctx->truehd_samples_per_frame; > + ctx->truehd_oi_delta = u16_signed_delta(output_timing_minus_spf, > + input_timing); > + } > + > for (next_code_idx = 0; next_code_idx < FF_ARRAY_ELEMS(mat_codes); > next_code_idx++) > if (ctx->hd_buf_filled <= mat_codes[next_code_idx].pos) > break; > @@ -525,8 +747,10 @@ static int spdif_header_truehd(AVFormatContext *s, > AVPacket *pkt) > > /* this was the last code, move to the next MAT frame */ > have_pkt = 1; > - ctx->out_buf = hd_buf; > - ctx->hd_buf_idx ^= 1; > + ret = truehd_enqueue_mat(s); > + if (ret < 0) > + return ret; > + > hd_buf = ctx->hd_buf[ctx->hd_buf_idx]; > ctx->hd_buf_filled = 0; > > @@ -580,10 +804,6 @@ static int spdif_header_truehd(AVFormatContext *s, > AVPacket *pkt) > return 0; > } > > - ctx->data_type = IEC61937_TRUEHD; > - ctx->pkt_offset = MAT_PKT_OFFSET; > - ctx->out_bytes = MAT_FRAME_SIZE; > - ctx->length_code = MAT_FRAME_SIZE; > return 0; > } > > @@ -662,6 +882,27 @@ static int spdif_write_packet(struct AVFormatContext > *s, AVPacket *pkt) > ret = ctx->header_info(s, pkt); > if (ret < 0) > return ret; > + > + if (ctx->header_info == spdif_header_truehd) { > + /* TrueHD may complete more than one MAT buffer in one AVPacket. > At > + * most, write one completed buffer per AVPacket. > + */ > + int ready_idx; > + > + if (!ctx->hd_buf_ready_count) > + return 0; > + > + ready_idx = ctx->hd_buf_next_ready_idx; > + ctx->hd_buf_next_ready_idx = > truehd_next_mat_buffer(ctx->hd_buf_next_ready_idx); > + ctx->hd_buf_ready_count--; > + > + ctx->out_buf = ctx->hd_buf[ready_idx]; > + ctx->out_bytes = MAT_FRAME_SIZE; > + ctx->data_type = IEC61937_TRUEHD; > + ctx->length_code = MAT_FRAME_SIZE; > + ctx->pkt_offset = MAT_PKT_OFFSET; > + } > + > if (!ctx->pkt_offset) > return 0; > > -- > 2.52.0 > > _______________________________________________ > ffmpeg-devel mailing list -- [email protected] > To unsubscribe send an email to [email protected] > _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
