Skip to content

# DTS core decoded nothing from streams other encoders wrote - #201

Merged
Hawkynt merged 1 commit into
mainfrom
fix/dts-sync
Sep 6, 2026
Merged

# DTS core decoded nothing from streams other encoders wrote#201
Hawkynt merged 1 commit into
mainfrom
fix/dts-sync

Conversation

@Hawkynt

@Hawkynt Hawkynt commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Every libavcodec DTS stream returned zero bytes — 14 of 14. Our own encoder's output decoded, which is why nothing caught it.

Faults

The one that returned nothing: DtsFrameDecoder sized the code-book-selector and scale-factor-adjust arrays at 11 — the number of code-book groups — while the subframe header allows a bit-allocation index up to 26. The first wide-band subband indexed past the end and the frame was abandoned. That single bound was the entire symptom.

Behind it, three more, none of which could surface while the decoder returned nothing:

  • The half-IMDCT had the wrong modulation phase — a 997 Hz tone came out as full-amplitude images at 2.0, 2.5 and 3.5 kHz.
  • The filterbank output was left in the subband domain, so every sample clipped.
  • Quantiser steps were stored as short decimals. The finest six are 0.14% off the defined Q22 integers (0.00008 against 336/2²²). That was exactly the 1.00136 level error measured before the cause was found.
  • The LFE interpolation FIR reaches back past the frame's first decimated sample, but the buffer was cleared every frame.

Output is also now in ITU/WAVE channel order rather than the bit stream's own.

Measured, against ffmpeg's decode of the same bytes

before after
44 streams 0 bytes on all 14 100% coverage, peak ≤ 1 LSB, 99–100% of samples identical

Tests

DtsForeignStreamTests embeds ffmpeg-produced streams and ffmpeg's decode of them, asserting sample by sample. Five original faults were re-introduced one at a time and each is caught: the abits array size, the IMDCT phase, the quantiser step table, the channel map, and the LFE history.

45 passed, 0 failed for ~Dts. Full suite on the source tree: 29030 passed, 0 failed, 1355 skipped.

Flagged, not fixed

Joint-intensity coding is parsed but not reconstructed, and the DYNF dynamic-range byte is not skipped — a latent desync on any stream that sets it. Neither is exercised by ffmpeg-encoded material, so both were left alone rather than changed on speculation.

DtsCodec's doc claimed it was "a faithful managed port of the FFmpeg reference decoder" without the vendoring records AGENTS.md requires; the wording is corrected in the files touched here. DtsCodec.Encode still carries a similar claim about dcaenc.c and deserves the same look.

Every libavcodec-encoded core stream came back empty. The per-channel arrays
holding the sample code-book selector and the scale-factor adjustment were
sized to the ten code-book groups, but the subframe header allows a
bit-allocation index up to 26, so the first wide-band subband indexed past the
end and the frame was abandoned. Our own encoder only ever writes small
indices, which is why nothing caught it.

Behind that sat three more faults, each of which the reader had been hiding:

- The QMF synthesis fed the filterbank a half-IMDCT with the wrong modulation
  phase, so a 997 Hz tone came out as full-amplitude images at 2.0, 2.5 and
  3.5 kHz. The stage is the second half of a 64-point IMDCT over the 32 subband
  values; the kernel now says so.
- The filterbank output was left in the subband domain instead of being scaled
  to the +/-1 range the caller quantizes from, so every sample clipped.
- The quantizer step sizes were stored as short decimals. The finest six steps
  are 0.14 percent away from the values the format defines, which put a level
  error on anything coded at high resolution; they are now the exact Q22
  integers.
- The LFE interpolation FIR reaches back past the first decimated sample of a
  frame, but the buffer was cleared each frame, restarting the filter from
  silence once per frame.

Decoded PCM now comes out in the ITU/WAVE interleave order rather than the bit
stream's centre-first AMODE order.

Verified against ffmpeg n9.0.1 over 44 streams (mono through 5.1, 32/44.1/48
kHz, 384-1411 kbit/s, tones, noise, chirps, squares, transients, silence): all
decode at full length, no sample differs by more than one LSB, and 99 to 100
percent of samples are identical.
@Hawkynt
Hawkynt merged commit d0e75f0 into main Sep 6, 2026
4 checks passed
@Hawkynt
Hawkynt deleted the fix/dts-sync branch September 6, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant