Skip to content

aac-enc: DRM AAC, HE-AAC and HE-AAC v2 encoding (AOT 143, 144, 145) - #186

Open
rafael2k wants to merge 5 commits into
mstorsjo:masterfrom
rafael2k:pr/drm-encoder
Open

rafael2k wants to merge 5 commits into
mstorsjo:masterfrom
rafael2k:pr/drm-encoder

Conversation

@rafael2k

@rafael2k rafael2k commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Adds encoding for the DRM broadcasting system (ETSI ES 201 980, clause 5.4), the counterpart of the decoder's TT_DRM support. Four commits:

  1. 960 sample frame length (scalefactor band tables, TNS limits)
  2. Huffman codeword reordering (HCR) encoder
  3. DRM encoder: AOT_DRM_AAC 143, AOT_DRM_SBR 144, AOT_DRM_MPEG_PS 145 (ER AAC-SCAL syntax with HCR and VCB11, 8 bit CRC, SBR payload appended in reverse)
  4. README.DRM.md with usage and the origin of the 960 tables (OpenDigitalRadio's ODR-AudioEnc patches)

Tested against this library's decoder and in a full DRM transmitter/receiver chain (Dream, Drm-tools/dream#52): AAC, HE-AAC mono and stereo, HE-AAC v2. Not checked against other DRM receivers. HE-AAC uses long blocks only.

🤖 Generated with Claude Code

rafael2k and others added 4 commits November 4, 2026 09:08
The AAC encoder only knew the 1024 sample transform for the plain AAC object
types. The 960 transform (DRM, DAB+) needs its own scalefactor band tables for
the long and the 120 sample short blocks at the supported sampling rates and its
own TNS band limits; the frame length checks and the granule length parameter
accept it, and it is reported as a capability.

The 960 tables and the TNS limits follow the OpenDigitalRadio patches to
fdk-aac 2.0.x (ODR-AudioEnc).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
DRM AAC (ETSI ES 201 980 5.4.1) uses the error resilient HCR tool: the
spectral codewords are sorted by priority, the priority codewords are placed one
per segment and the others in the room left in the segments, set by set, with
the read direction alternating. aacenc_hcr.cpp does this for one long block
channel and returns the two side info lengths (reordered data length and longest
codeword) with the reordered bits; it mirrors the decoder
(libAACdec/src/aacdec_hcr*.cpp), codeword by codeword and bit by bit.

Not used by the encoder yet, the next commit does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds the virtual DRM object types AOT_DRM_AAC, AOT_DRM_SBR and AOT_DRM_MPEG_PS to
the encoder, the counterpart of what the decoder already does for TT_DRM (ETSI ES
201 980 clause 5.4). They run on the logic of AOT 2, 5 and 29 with the DRM
syntax: error robust AAC scalable elements (the element lists the decoder uses),
the 960 transform, long blocks only, no PNS, VCB11 section data (5 bit codebooks,
escape codebook sections one sfb long), HCR spectral data (aacenc_hcr.cpp) and
the 8 bit CRC in front of the protected region, written as the first byte of the
raw frame and patched in after the region.

With SBR, the SBR payload (8 bit CRC, header, data, PS in the extension) is
written at the end of the frame in reverse bit order after the AAC data and zero
stuffing, so that the first SBR bit is the last bit of the frame; the SBR encoder
gets the scalable syntax (the mono bs_coupling bit) and the DRM CRC flags.

Frames are returned as [crc][frame] with transport type TT_MP4_RAW. Checked
against the decoder with TT_DRM: mono and stereo AAC at 12 and 24 kHz, HE-AAC
mono and stereo, HE-AAC v2, 8 to 48 kbit/s; all frames decode with the right
spectrum and a wrong CRC byte is rejected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…les come from

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@mstorsjo

Copy link
Copy Markdown
Owner

Unfortunately, this change is out of the scope for changes I'll merge in here. As this encoder is developed by Fraunhofer, I mostly just maintain packaging of it here, plus minor tweaks on top of it. But I'm not able to commit to maintaining significant feature additions/changes on top of it.

@rafael2k

rafael2k commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the clear answer. I understand not wanting to take on a large feature, but I'd like to argue that this one is less risky than it looks. (Edited: I first overstated how much of the patch is gated on DRM; the list below is what the diff actually does.)

  • No new identifiers from us. The encoder is selected with AOT 143, 144 and 145 (AOT_DRM_AAC, AOT_DRM_SBR, AOT_DRM_MPEG_PS). They are already defined in Fraunhofer's own libSYS/include/FDK_audio.h as the virtual DRM AOTs, and the decoder already uses them with the AC_DRM syntax flag. The patch does not touch FDK_audio.h, libSYS, libAACdec, libSBRdec, libMpegTPDec or libMpegTPEnc, so there is nothing on the decoder side that could clash with a Fraunhofer update.
  • What is gated on DRM: VCB11/HCR (the new aacenc_hcr.cpp, and the check that rejects ER VCB11/HCR for everything else), the SBR DRM syntax and CRC (drmSyntax), long-blocks-only in psy_main, and the DRM paths in bitenc and psy_configuration.
  • What is not strictly gated, to be upfront: (1) a 960 granule length is now accepted and has its own TNS band table, so it applies to any non-low-delay AOT that asks for 960, not only DRM (it is opt-in via AACENC_GRANULE_LENGTH); (2) in bit_sbr.cpp the SBR coupling bit written when SBR_SYNTAX_SCALABLE is set is now 0 instead of 1; (3) dyn_bits.cpp computes the section bit count in a slightly refactored way (the HCR-specific side-info change is behind useHCR).
  • Existing output unchanged where I tested. I built current master and this branch and encoded the same test signal (mono and stereo) with AOT 2, 5, 29, 23 and 39 at 16 to 128 kbit/s: all 35 combinations that encode on both builds give byte-identical output files. I did not test the scalable-syntax AOTs (for example ER AAC scalable with SBR), which are the ones item (2) would affect.
  • Tested against a real receiver. The streams are decoded by the Dream DRM receiver (ETSI ES 201 980) in our end-to-end tests.

If the concern is maintenance, I'm happy to maintain it and keep it rebased on new releases. If it still doesn't fit here, I understand: it stays in my fork https://github.com/rafael2k/fdk-aac (used by the Dream DRM software).

@mstorsjo

Copy link
Copy Markdown
Owner

Yep, this is too big of a change to carry here. Feel free to maintain it on top in your own fork - thanks!

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.

2 participants