Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ set(AACENC_SRC
libAACenc/src/aacEnc_ram.cpp
libAACenc/src/aacEnc_rom.cpp
libAACenc/src/aacenc.cpp
libAACenc/src/aacenc_hcr.cpp
libAACenc/src/aacenc_lib.cpp
libAACenc/src/aacenc_pns.cpp
libAACenc/src/aacenc_tns.cpp
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ AACENC_SRC = \
cpp/libAACenc/src/aacEnc_ram.cpp \
cpp/libAACenc/src/aacEnc_rom.cpp \
cpp/libAACenc/src/aacenc.cpp \
cpp/libAACenc/src/aacenc_hcr.cpp \
cpp/libAACenc/src/aacenc_lib.cpp \
cpp/libAACenc/src/aacenc_pns.cpp \
cpp/libAACenc/src/aacenc_tns.cpp \
Expand Down Expand Up @@ -624,6 +625,7 @@ EXTRA_DIST = \
$(top_srcdir)/autogen.sh \
$(top_srcdir)/MODULE_LICENSE_FRAUNHOFER \
$(top_srcdir)/NOTICE \
$(top_srcdir)/README.DRM.md \
$(top_srcdir)/OWNERS \
$(top_srcdir)/Android.bp \
$(top_srcdir)/fdk-aac.def \
Expand Down
39 changes: 39 additions & 0 deletions README.DRM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# DRM AAC encoding

The encoder can produce the audio of the DRM broadcasting system (ETSI ES 201 980,
clause 5.4) through three virtual audio object types, the counterparts of what the
decoder reads with `TT_DRM`:

| `AACENC_AOT` | value | content |
|-------------------------|-------|-----------------------------------------------------|
| `AOT_DRM_AAC` | 143 | AAC, mono or stereo |
| `AOT_DRM_SBR` | 144 | AAC with SBR (HE-AAC), mono or stereo |
| `AOT_DRM_MPEG_PS` | 145 | AAC with SBR and parametric stereo (HE-AAC v2) |

Use them with `AACENC_TRANSMUX = TT_MP4_RAW` and an input sampling rate of 12 or 24 kHz
(the AAC rate of the DRM service), twice that with SBR. Stereo and parametric stereo
need 24 kHz. One call to `aacEncEncode()` returns one raw frame: a CRC byte followed by
the frame, which is what a DRM audio super frame carries per audio frame.

What the DRM syntax needs, and this adds to the encoder:

* the 960 sample transform (scalefactor band tables and TNS limits for it),
* the error robust AAC scalable element syntax with HCR (`aacenc_hcr.cpp`) and VCB11,
long blocks only, no PNS,
* the 8 bit CRC over the side information,
* the SBR payload (with its own 8 bit CRC) at the end of the frame in reverse bit
order.

## Origin

The 960 sample tables and TNS limits follow the patches of OpenDigitalRadio
([ODR-AudioEnc](https://github.com/Opendigitalradio/ODR-AudioEnc)) to fdk-aac 2.0.x; the
DRM encoding was written for the Dream DRM software
(<https://github.com/Drm-tools/dream>) and tested against the decoder of this library
and in a complete DRM transmitter / receiver chain. It has not been checked against
other DRM receivers.

## Licence

The Fraunhofer FDK AAC Codec licence in `NOTICE` applies to the whole library including
these additions; no patent licence is granted, see the licence text.
10 changes: 10 additions & 0 deletions cpp/libAACenc/include/aacenc_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,14 @@ typedef enum {
- 129: MPEG-2 AAC Low Complexity.
- 132: MPEG-2 AAC Low Complexity with Spectral Band
Replication (HE-AAC).
- 143, 144, 145: Virtual AOTs for DRM (ETSI ES 201 980 clause
5.4): AAC, AAC with SBR and AAC with SBR and Parametric Stereo
(::AOT_DRM_AAC, ::AOT_DRM_SBR, ::AOT_DRM_MPEG_PS). Error robust
AAC scalable syntax with HCR and VCB11, the 960 transform (long
blocks only), raw frames (::TT_MP4_RAW) of one CRC byte followed
by the frame, the SBR data at the end of the frame in reverse bit
order. The input rate is the AAC rate (12 or 24 kHz), twice that
with SBR; stereo and parametric stereo need 24 kHz.

Please note that the virtual MPEG-2 AOT's basically disables
non-existing Perceptual Noise Substitution tool in AAC encoder
Expand Down Expand Up @@ -1318,6 +1326,8 @@ typedef enum {
AACENC_GRANULE_LENGTH =
0x0105, /*!< Core encoder (AAC) audio frame length in samples:
- 1024: Default configuration.
- 960: Length for DRM (and DAB+) with the AAC-LC, HE-AAC and
HE-AAC v2 object types.
- 512: Default length in LD/ELD configuration.
- 480: Length in LD/ELD configuration.
- 256: Length for ELD reduced delay mode (x2).
Expand Down
15 changes: 11 additions & 4 deletions cpp/libAACenc/src/aacenc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,16 +485,18 @@ AAC_ENCODER_ERROR FDKaacEnc_Initialize(
return AAC_ENC_UNSUPPORTED_BITRATE;
}

if (config->syntaxFlags & AC_ER_VCB11) {
/* VCB11 (section data syntax) and HCR (aacenc_hcr.cpp) are supported for DRM only */
if ((config->syntaxFlags & AC_ER_VCB11) && !(config->syntaxFlags & AC_DRM)) {
return AAC_ENC_UNSUPPORTED_ER_FORMAT;
}
if (config->syntaxFlags & AC_ER_HCR) {
if ((config->syntaxFlags & AC_ER_HCR) && !(config->syntaxFlags & AC_DRM)) {
return AAC_ENC_UNSUPPORTED_ER_FORMAT;
}

/* check frame length */
switch (config->framelength) {
case 1024:
case 960:
if (isLowDelay(config->audioObjectType)) {
return AAC_ENC_INVALID_FRAME_LENGTH;
}
Expand Down Expand Up @@ -562,12 +564,17 @@ AAC_ENCODER_ERROR FDKaacEnc_Initialize(
* full reset requested. */
ErrorStatus = FDKaacEnc_psyInit(hAacEnc->psyKernel, hAacEnc->psyOut,
hAacEnc->maxFrames, hAacEnc->maxChannels,
config->audioObjectType, cm);
(config->syntaxFlags & AC_DRM)
? AOT_DRM_AAC
: config->audioObjectType,
cm);
if (ErrorStatus != AAC_ENC_OK) goto bail;
}

ErrorStatus = FDKaacEnc_psyMainInit(
hAacEnc->psyKernel, config->audioObjectType, cm, config->sampleRate,
hAacEnc->psyKernel,
(config->syntaxFlags & AC_DRM) ? AOT_DRM_AAC : config->audioObjectType,
cm, config->sampleRate,
config->framelength, psyBitrate, tnsMask, hAacEnc->bandwidth90dB,
config->usePns, config->useIS, config->useMS, config->syntaxFlags,
initFlags);
Expand Down
180 changes: 180 additions & 0 deletions cpp/libAACenc/src/aacenc_hcr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/* Huffman codeword reordering (HCR) for the DRM AAC encoder, ISO/IEC 14496-3
4.5.2.3 and ETSI ES 201 980 clause 5.4.1.

Written for the Dream DRM software (https://github.com/Drm-tools/dream) as the
encoder side of libAACdec/src/aacdec_hcr*.cpp, and distributed under the terms
of the Fraunhofer FDK AAC Codec licence of this library (see NOTICE). */

#include "aacenc_hcr.h"
#include "bit_cnt.h"
#include "FDK_bitstream.h"
#include "aacEnc_rom.h"

#define HCR_MAX_CB 32
#define HCR_HI_LTAB(a) ((a) >> 16)
#define HCR_LO_LTAB(a) ((a) & 0xffff)

/* FDKaacEnc_codeValues() writes codebooks 5 and 6 two codewords at a time; HCR
needs every pair on its own */
static void hcrCodePair56(const SHORT *v, INT cb, FDK_BITSTREAM *bs) {
const INT t0 = v[0] + 4, t1 = v[1] + 4;
if (cb == 5)
FDKwriteBits(bs, FDKaacEnc_huff_ctab5[t0][t1],
HCR_HI_LTAB(FDKaacEnc_huff_ltab5_6[t0][t1]));
else
FDKwriteBits(bs, FDKaacEnc_huff_ctab6[t0][t1],
HCR_LO_LTAB(FDKaacEnc_huff_ltab5_6[t0][t1]));
}

/* the same tables as in the decoder (libAACdec/src/aac_rom.cpp) */
static const UCHAR hcrMaxCwLen[HCR_MAX_CB] = {
0, 11, 9, 20, 16, 13, 11, 14, 12, 17, 14, 49, 0, 0, 0, 0,
14, 17, 21, 21, 25, 25, 29, 29, 29, 29, 33, 33, 33, 37, 37, 41};
static const UCHAR hcrDimShift[HCR_MAX_CB] = {1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
static const UCHAR hcrPriority[HCR_MAX_CB] = {
0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 22, 0, 0, 0, 0,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};

typedef struct {
UCHAR cb;
UCHAR len;
UCHAR done;
UCHAR pos; /* bits already placed */
UCHAR bit[64]; /* the codeword: body, sign bits, escape sequences */
} HCR_CW;

typedef struct {
INT left, right; /* next free bit from the left / from the right */
INT start; /* first bit of the segment in the reordered data */
INT width;
INT remaining;
} HCR_SEG;

/* The codeword arrays are large: keep them off the stack */
static HCR_CW hcrCw[HCR_MAX_CODEWORDS];
static HCR_SEG hcrSeg[HCR_MAX_CODEWORDS];

INT FDKaacEnc_HcrEncode(HCR_ENC_DATA *hcr, const INT *sfbOffset,
const SECTION_DATA *sd, SHORT *quantSpectrum) {
INT nCw = 0;
INT total = 0;
INT longest = 0;
INT prio, i, k, s, t;

if (sd->blockType == SHORT_WINDOW) return 1; /* not supported */

/* codewords of all sections, ordered by priority (stable inside a priority) */
for (prio = 22; prio >= 1; prio--) {
for (i = 0; i < sd->noOfSections; i++) {
const INT cb = sd->huffsection[i].codeBook;
if (cb < 0 || cb >= HCR_MAX_CB || hcrPriority[cb] != prio) continue;
{
const INT sfbStart = sd->huffsection[i].sfbStart;
const INT sfbEnd = sfbStart + sd->huffsection[i].sfbCnt;
const INT line0 = sfbOffset[sfbStart];
const INT lines = sfbOffset[sfbEnd] - line0;
const INT dim = 1 << hcrDimShift[cb];
for (k = 0; k + dim <= lines; k += dim) {
UCHAR buf[64];
FDK_BITSTREAM bs;
INT len, b;
if (nCw >= HCR_MAX_CODEWORDS) return 2;
FDKinitBitStream(&bs, buf, sizeof(buf), 0, BS_WRITER);
if (cb == 5 || cb == 6)
hcrCodePair56(quantSpectrum + line0 + k, cb, &bs);
else
FDKaacEnc_codeValues(quantSpectrum + line0 + k, dim, cb, &bs);
len = (INT)FDKgetValidBits(&bs);
FDKsyncCache(&bs);
if (len <= 0 || len > 49) return 3;
hcrCw[nCw].cb = (UCHAR)cb;
hcrCw[nCw].len = (UCHAR)len;
hcrCw[nCw].done = 0;
hcrCw[nCw].pos = 0;
for (b = 0; b < len; b++)
hcrCw[nCw].bit[b] = (buf[b >> 3] >> (7 - (b & 7))) & 1;
total += len;
if (len > longest) longest = len;
nCw++;
}
}
}
}

hcr->lengthReordered = total;
hcr->longestCodeword = longest;
if (nCw == 0) return 0;
if (total > HCR_MAX_BITS || longest > 49) return 4;

/* segments: one per codeword while they fit, the last one takes the rest */
{
INT nSeg = 0, start = 0;
for (k = 0; k < nCw; k++) {
INT w = hcrMaxCwLen[hcrCw[k].cb];
if (w > longest) w = longest;
if (start + w <= total) {
hcrSeg[nSeg].start = start;
hcrSeg[nSeg].width = w;
start += w;
nSeg++;
} else {
if (nSeg == 0) return 5;
hcrSeg[nSeg - 1].width = total - hcrSeg[nSeg - 1].start;
break;
}
}
for (s = 0; s < nSeg; s++) {
hcrSeg[s].left = 0;
hcrSeg[s].right = hcrSeg[s].width - 1;
hcrSeg[s].remaining = hcrSeg[s].width;
}

/* priority codewords: codeword s in segment s, from the left */
for (s = 0; s < nSeg; s++) {
HCR_CW *c = &hcrCw[s];
if (c->len > hcrSeg[s].width) return 6;
for (i = 0; i < c->len; i++)
hcr->bits[hcrSeg[s].start + hcrSeg[s].left++] = c->bit[i];
hcrSeg[s].remaining -= c->len;
c->done = 1;
}

/* the other codewords, set by set: first set from the right, then
alternating. Trial t gives segment s the codeword (s - t) mod nSeg. */
{
INT first = nSeg;
INT set = 1;
while (first < nCw) {
const INT cnt = (nCw - first < nSeg) ? (nCw - first) : nSeg;
const INT fromRight = (set & 1);
if (set >= 14) return 9; /* the decoder handles at most 14 sets */
for (t = 0; t < nSeg; t++) {
for (s = 0; s < nSeg; s++) {
INT c = (s - t) % nSeg;
HCR_CW *cw;
if (c < 0) c += nSeg;
if (c >= cnt || hcrSeg[s].remaining <= 0) continue;
cw = &hcrCw[first + c];
while (!cw->done && hcrSeg[s].remaining > 0) {
const INT p = fromRight ? hcrSeg[s].right-- : hcrSeg[s].left++;
hcr->bits[hcrSeg[s].start + p] = cw->bit[cw->pos++];
hcrSeg[s].remaining--;
if (cw->pos == cw->len) cw->done = 1;
}
}
}
first += cnt;
set++;
}
}

/* everything has to be in place */
for (k = 0; k < nCw; k++)
if (!hcrCw[k].done) return 7;
for (s = 0; s < nSeg; s++)
if (hcrSeg[s].remaining != 0) return 8;
}
return 0;
}
30 changes: 30 additions & 0 deletions cpp/libAACenc/src/aacenc_hcr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* Huffman codeword reordering (HCR) for the DRM AAC encoder, ISO/IEC 14496-3
4.5.2.3 and ETSI ES 201 980 clause 5.4.1.

Written for the Dream DRM software (https://github.com/Drm-tools/dream) as the
encoder side of libAACdec/src/aacdec_hcr*.cpp, and distributed under the terms
of the Fraunhofer FDK AAC Codec licence of this library (see NOTICE). */

/* The codewords of one channel are sorted by priority, placed one per segment
(priority codewords) and the others in the space left in the segments, set by
set, trial by trial, exactly as the decoder takes them out again. */
#ifndef AACENC_HCR_H
#define AACENC_HCR_H

#include "common_fix.h"
#include "dyn_bits.h"

#define HCR_MAX_CODEWORDS 512
#define HCR_MAX_BITS 16383 /* 14 bit length field */

typedef struct {
INT lengthReordered; /* length_of_reordered_spectral_data */
INT longestCodeword; /* length_of_longest_codeword */
UCHAR bits[HCR_MAX_BITS + 1]; /* the reordered spectral data, one bit per entry */
} HCR_ENC_DATA;

/* Returns 0 on success. Long blocks only. */
INT FDKaacEnc_HcrEncode(HCR_ENC_DATA *hcr, const INT *sfbOffset,
const SECTION_DATA *sectionData, SHORT *quantSpectrum);

#endif
Loading