diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e74caa0c..90d2a6de7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Makefile.am b/Makefile.am index 72ca300d3..a473ea48e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ @@ -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 \ diff --git a/README.DRM.md b/README.DRM.md new file mode 100644 index 000000000..433233849 --- /dev/null +++ b/README.DRM.md @@ -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 +() 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. diff --git a/cpp/libAACenc/include/aacenc_lib.h b/cpp/libAACenc/include/aacenc_lib.h index 902ba118f..83d82e97b 100644 --- a/cpp/libAACenc/include/aacenc_lib.h +++ b/cpp/libAACenc/include/aacenc_lib.h @@ -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 @@ -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). diff --git a/cpp/libAACenc/src/aacenc.cpp b/cpp/libAACenc/src/aacenc.cpp index 1af8a2e18..68d6c511c 100644 --- a/cpp/libAACenc/src/aacenc.cpp +++ b/cpp/libAACenc/src/aacenc.cpp @@ -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; } @@ -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); diff --git a/cpp/libAACenc/src/aacenc_hcr.cpp b/cpp/libAACenc/src/aacenc_hcr.cpp new file mode 100644 index 000000000..87f64d126 --- /dev/null +++ b/cpp/libAACenc/src/aacenc_hcr.cpp @@ -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; +} diff --git a/cpp/libAACenc/src/aacenc_hcr.h b/cpp/libAACenc/src/aacenc_hcr.h new file mode 100644 index 000000000..82fad38ca --- /dev/null +++ b/cpp/libAACenc/src/aacenc_hcr.h @@ -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 diff --git a/cpp/libAACenc/src/aacenc_lib.cpp b/cpp/libAACenc/src/aacenc_lib.cpp index ee7f91869..c5e7c45c6 100644 --- a/cpp/libAACenc/src/aacenc_lib.cpp +++ b/cpp/libAACenc/src/aacenc_lib.cpp @@ -886,7 +886,14 @@ static AACENC_ERROR FDKaacEnc_AdjustEncSettings(HANDLE_AACENCODER hAacEncoder, hAacConfig->useIS = config->userIntensity; } - hAacConfig->audioObjectType = config->userAOT; + /* DRM AOTs run on the logic of the plain AOTs (see the switch below) */ + hAacConfig->audioObjectType = + (config->userAOT == AOT_DRM_AAC) + ? AOT_AAC_LC + : ((config->userAOT == AOT_DRM_SBR) + ? AOT_SBR + : ((config->userAOT == AOT_DRM_MPEG_PS) ? AOT_PS + : config->userAOT)); hAacConfig->channelMode = GetCoreChannelMode(config->userChannelMode, hAacConfig->audioObjectType); hAacConfig->nChannels = @@ -922,6 +929,28 @@ static AACENC_ERROR FDKaacEnc_AdjustEncSettings(HANDLE_AACENCODER hAacEncoder, /* Adapt internal AOT when necessary. */ switch (config->userAOT) { + case AOT_DRM_AAC: + case AOT_DRM_SBR: + case AOT_DRM_MPEG_PS: + /* DRM AAC (ETSI ES 201 980 5.4): ER AAC scalable syntax with HCR and + VCB11, 960 transform, raw frames. The SBR / PS logic of the plain + AOTs is reused, so the internal AOT is the plain one. */ + hAacConfig->audioObjectType = + (config->userAOT == AOT_DRM_AAC) + ? AOT_AAC_LC + : ((config->userAOT == AOT_DRM_SBR) ? AOT_SBR : AOT_PS); + hAacConfig->usePns = 0; + hAacConfig->epConfig = 1; + hAacConfig->syntaxFlags |= + AC_DRM | AC_ER | AC_SCALABLE | AC_ER_VCB11 | AC_ER_HCR; + config->userTpType = TT_MP4_RAW; + hAacConfig->framelength = (config->userFramelength != (UINT)-1) + ? config->userFramelength + : 960; + if (hAacConfig->framelength != 960) { + return AACENC_INVALID_CONFIG; + } + break; case AOT_MP2_AAC_LC: case AOT_MP2_SBR: hAacConfig->usePns = 0; @@ -2102,7 +2131,7 @@ AACENC_ERROR aacEncGetLibInfo(LIB_INFO *info) { LIB_VERSION_STRING(&info[i]); /* Capability flags */ - info[i].flags = 0 | CAPF_AAC_1024 | CAPF_AAC_LC | CAPF_AAC_512 | + info[i].flags = 0 | CAPF_AAC_1024 | CAPF_AAC_LC | CAPF_AAC_960 | CAPF_AAC_512 | CAPF_AAC_480 | CAPF_AAC_DRC | CAPF_AAC_ELD_DOWNSCALE; /* End of flags */ @@ -2126,6 +2155,24 @@ AACENC_ERROR aacEncoder_SetParam(const HANDLE_AACENCODER hAacEncoder, if (settings->userAOT != (AUDIO_OBJECT_TYPE)value) { /* check if AOT matches the allocated modules */ switch (value) { + case AOT_DRM_MPEG_PS: + if (!(hAacEncoder->encoder_modis & (ENC_MODE_FLAG_PS))) { + err = AACENC_INVALID_CONFIG; + goto bail; + } + FDK_FALLTHROUGH; + case AOT_DRM_SBR: + if (!(hAacEncoder->encoder_modis & (ENC_MODE_FLAG_SBR))) { + err = AACENC_INVALID_CONFIG; + goto bail; + } + FDK_FALLTHROUGH; + case AOT_DRM_AAC: + if (!(hAacEncoder->encoder_modis & (ENC_MODE_FLAG_AAC))) { + err = AACENC_INVALID_CONFIG; + goto bail; + } + break; case AOT_PS: if (!(hAacEncoder->encoder_modis & (ENC_MODE_FLAG_PS))) { err = AACENC_INVALID_CONFIG; @@ -2258,6 +2305,7 @@ AACENC_ERROR aacEncoder_SetParam(const HANDLE_AACENCODER hAacEncoder, if (settings->userFramelength != value) { switch (value) { case 1024: + case 960: case 512: case 480: case 256: diff --git a/cpp/libAACenc/src/aacenc_tns.cpp b/cpp/libAACenc/src/aacenc_tns.cpp index 4462600c3..343615060 100644 --- a/cpp/libAACenc/src/aacenc_tns.cpp +++ b/cpp/libAACenc/src/aacenc_tns.cpp @@ -196,6 +196,22 @@ static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab1024[] = { {44100, {42, 14}}, {32000, {51, 14}}, {24000, {46, 14}}, {22050, {46, 14}}, {16000, {42, 14}}, {12000, {42, 14}}, {11025, {42, 14}}, {8000, {39, 14}}}; +static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab960[] = +{ + { 96000, { 31, 9}}, + { 88200, { 31, 9}}, + { 64000, { 34, 10}}, + { 48000, { 49, 14}}, + { 44100, { 49, 14}}, + { 32000, { 49, 14}}, + { 24000, { 46, 15}}, + { 22050, { 46, 14}}, + { 16000, { 46, 15}}, + { 12000, { 42, 15}}, + { 11025, { 42, 15}}, + { 8000, { 40, 15}} +}; + static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab120[] = { {48000, {12, -1}}, /* 48000 */ {44100, {12, -1}}, /* 44100 */ @@ -278,6 +294,9 @@ static INT getTnsMaxBands(const INT sampleRate, const INT granuleLength, switch (granuleLength) { case 960: + pMaxBandsTab = tnsMaxBandsTab960; + maxBandsTabSize = sizeof(tnsMaxBandsTab960) / sizeof(TNS_MAX_TAB_ENTRY); + break; case 1024: pMaxBandsTab = tnsMaxBandsTab1024; maxBandsTabSize = sizeof(tnsMaxBandsTab1024) / sizeof(TNS_MAX_TAB_ENTRY); diff --git a/cpp/libAACenc/src/bitenc.cpp b/cpp/libAACenc/src/bitenc.cpp index 957e8216e..b5a1ac3f8 100644 --- a/cpp/libAACenc/src/bitenc.cpp +++ b/cpp/libAACenc/src/bitenc.cpp @@ -101,6 +101,8 @@ amm-info@iis.fraunhofer.de *******************************************************************************/ #include "bitenc.h" +#include "aacenc_hcr.h" +#include "FDK_crc.h" #include "bit_cnt.h" #include "dyn_bits.h" #include "qc_data.h" @@ -244,7 +246,7 @@ static INT FDKaacEnc_encodeSectionData(SECTION_DATA *sectionData, INT sectLen; INT i; INT dbgVal = FDKgetValidBits(hBitStream); - INT sectCbBits = 4; + INT sectCbBits = useVCB11 ? 5 : 4; switch (sectionData->blockType) { case LONG_WINDOW: @@ -263,6 +265,15 @@ static INT FDKaacEnc_encodeSectionData(SECTION_DATA *sectionData, for (i = 0; i < sectionData->noOfSections; i++) { INT codeBook = sectionData->huffsection[i].codeBook; + if (useVCB11 && (codeBook == 11 || codeBook >= 16)) { + /* VCB11: escape codebook sections are one sfb long, without length */ + INT n; + for (n = 0; n < sectionData->huffsection[i].sfbCnt; n++) { + FDKwriteBits(hBitStream, codeBook, sectCbBits); + } + continue; + } + FDKwriteBits(hBitStream, codeBook, sectCbBits); { @@ -826,8 +837,22 @@ INT FDKaacEnc_writeExtensionData(HANDLE_TRANSPORTENC hTpEnc, if (syntaxFlags & (AC_SCALABLE | AC_ER)) { { - if ((syntaxFlags & AC_ELD) && ((pExtension->type == EXT_SBR_DATA) || - (pExtension->type == EXT_SBR_DATA_CRC))) { + if ((syntaxFlags & AC_DRM) && + ((pExtension->type == EXT_FILL_DATA) || + (pExtension->type == EXT_FIL))) { + /* DRM: no extension payload syntax, the stuffing is plain zero bits */ + if (hBitStream != NULL) { + INT writeBits = payloadBits; + while (writeBits > 0) { + INT n = (writeBits > 16) ? 16 : writeBits; + FDKwriteBits(hBitStream, 0, n); + writeBits -= n; + } + } + extBitsUsed += payloadBits; + } else if (((syntaxFlags & AC_ELD) || (syntaxFlags & AC_DRM)) && + ((pExtension->type == EXT_SBR_DATA) || + (pExtension->type == EXT_SBR_DATA_CRC))) { if (hBitStream != NULL) { int i, writeBits = payloadBits; UCHAR *extPayloadData = pExtension->pPayload; @@ -927,6 +952,10 @@ AAC_ENCODER_ERROR FDKaacEnc_ChannelElementWrite( int i, ch, decision_bit; INT crcReg1 = -1, crcReg2 = -1; UCHAR numberOfChannels; + FDK_CRCINFO drmCrcInfo; + INT drmCrcRegion = 0, drmCrcBitPos = 0; + static HCR_ENC_DATA hcrData[2]; /* not reentrant, like aacenc_hcr.cpp */ + INT hcrOk[2] = {0, 0}; if (hTpEnc != NULL) { /* Get bitstream handle */ @@ -940,7 +969,9 @@ AAC_ENCODER_ERROR FDKaacEnc_ChannelElementWrite( } /* Get channel element sequence table */ - list = getBitstreamElementList(aot, epConfig, numberOfChannels, 0, 0); + list = getBitstreamElementList( + (syntaxFlags & AC_DRM) ? AOT_DRM_AAC : aot, epConfig, numberOfChannels, + 0, 0); if (list == NULL) { error = AAC_ENC_UNSUPPORTED_AOT; goto bail; @@ -1102,12 +1133,36 @@ AAC_ENCODER_ERROR FDKaacEnc_ChannelElementWrite( case esc1_hcr: if (syntaxFlags & AC_ER_HCR) { - error = AAC_ENC_UNKNOWN; + /* length_of_reordered_spectral_data (14), length_of_longest_codeword (6) */ + bitDemand += 20; + if (hBitStream != NULL && qcOutChannel != NULL) { + if (FDKaacEnc_HcrEncode(&hcrData[ch], psyOutChannel[ch]->sfbOffsets, + pChSectionData, + qcOutChannel[ch]->quantSpec) != 0) { + error = AAC_ENC_UNKNOWN; + hcrData[ch].lengthReordered = 0; + hcrData[ch].longestCodeword = 0; + } else { + hcrOk[ch] = 1; + } + FDKwriteBits(hBitStream, hcrData[ch].lengthReordered, 14); + FDKwriteBits(hBitStream, hcrData[ch].longestCodeword, 6); + } } break; case spectral_data: - if (hBitStream != NULL) { + if (hBitStream != NULL && (syntaxFlags & AC_ER_HCR)) { + INT b; + if (hcrOk[ch]) { + for (b = 0; b < hcrData[ch].lengthReordered; b++) + FDKwriteBits(hBitStream, hcrData[ch].bits[b], 1); + } + if (hcrData[ch].lengthReordered != + qcOutChannel[ch]->sectionData.huffmanBits) { + return AAC_ENC_WRITE_SPEC_ERROR; + } + } else if (hBitStream != NULL) { INT spectralBits = 0; spectralBits = FDKaacEnc_encodeSpectralData( @@ -1133,19 +1188,35 @@ AAC_ENCODER_ERROR FDKaacEnc_ChannelElementWrite( } break; case adtscrc_end_reg1: - case drmcrc_end_reg: if (hTpEnc != NULL) { transportEnc_CrcEndReg(hTpEnc, crcReg1); } break; + case drmcrc_end_reg: + if (hBitStream != NULL) { + /* CRC of the region goes into the byte in front of it */ + FDK_BITSTREAM bsWriter; + FDKcrcEndReg(&drmCrcInfo, hBitStream, drmCrcRegion); + FDKinitBitStream(&bsWriter, hBitStream->hBitBuf.Buffer, + hBitStream->hBitBuf.bufSize, 0, BS_WRITER); + FDKpushFor(&bsWriter, drmCrcBitPos); + FDKwriteBits(&bsWriter, FDKcrcGetCRC(&drmCrcInfo) ^ 0xFF, 8); + FDKsyncCache(&bsWriter); + } + break; case adtscrc_end_reg2: if (hTpEnc != NULL) { transportEnc_CrcEndReg(hTpEnc, crcReg2); } break; case drmcrc_start_reg: - if (hTpEnc != NULL) { - crcReg1 = transportEnc_CrcStartReg(hTpEnc, 0); + /* 8 bit CRC in front of the protected region, filled in at the end */ + bitDemand += 8; + if (hBitStream != NULL) { + drmCrcBitPos = (INT)FDKgetValidBits(hBitStream); + FDKwriteBits(hBitStream, 0, 8); + FDKcrcInit(&drmCrcInfo, 0x001d, 0xFFFF, 8); + drmCrcRegion = FDKcrcStartReg(&drmCrcInfo, hBitStream, 0); } break; case next_channel: @@ -1300,7 +1371,46 @@ AAC_ENCODER_ERROR FDKaacEnc_WriteBitstream(HANDLE_TRANSPORTENC hTpEnc, FDKwriteBits(hBs, ID_END, EL_ID_BITS); } - if (doByteAlign) { + if (syntaxFlags & AC_DRM) { + /* DRM AAC + SBR (ETSI ES 201 980 5.4.2): the SBR payload sits at the end + of the frame in reverse bit order, the first SBR bit is the last bit of + the frame. Everything before it is the AAC data and zero stuffing. */ + INT sbrBits = 0, e, el; + for (el = 0; el < channelMapping->nElements; el++) { + for (e = 0; e < qcOut->qcElement[el]->nExtensions; e++) { + const QC_OUT_EXTENSION *ex = &qcOut->qcElement[el]->extension[e]; + if (ex->type == EXT_SBR_DATA || ex->type == EXT_SBR_DATA_CRC) + sbrBits += ex->nPayloadBits; + } + } + if (((FDKgetValidBits(hBs) - alignAnchor + qcOut->alignBits + sbrBits) & + 0x7) != 0) { + return AAC_ENC_WRITTEN_BITS_ERROR; + } + FDKaacEnc_ByteAlignment(hBs, qcOut->alignBits); + for (el = 0; el < channelMapping->nElements; el++) { + for (e = 0; e < qcOut->qcElement[el]->nExtensions; e++) { + const QC_OUT_EXTENSION *ex = &qcOut->qcElement[el]->extension[e]; + if (ex->type == EXT_SBR_DATA || ex->type == EXT_SBR_DATA_CRC) { + INT b; + /* sbr_crc_bits (8, G8, start value all ones, inverted) over all the + SBR data behind them */ + UINT crc = 0xFF; + for (b = 8; b < ex->nPayloadBits; b++) { + UINT bit = (ex->pPayload[b >> 3] >> (7 - (b & 7))) & 1; + UINT flag = ((crc >> 7) & 1) ^ bit; + crc = (crc << 1) & 0xFF; + if (flag) crc ^= 0x1d; + } + crc ^= 0xFF; + for (b = ex->nPayloadBits - 1; b >= 8; b--) + FDKwriteBits(hBs, (ex->pPayload[b >> 3] >> (7 - (b & 7))) & 1, 1); + for (b = 0; b < 8; b++) + FDKwriteBits(hBs, (crc >> b) & 1, 1); + } + } + } + } else if (doByteAlign) { /* Assure byte alignment*/ if (((FDKgetValidBits(hBs) - alignAnchor + qcOut->alignBits) & 0x7) != 0) { return AAC_ENC_WRITTEN_BITS_ERROR; diff --git a/cpp/libAACenc/src/dyn_bits.cpp b/cpp/libAACenc/src/dyn_bits.cpp index 74baeb88f..c47451b24 100644 --- a/cpp/libAACenc/src/dyn_bits.cpp +++ b/cpp/libAACenc/src/dyn_bits.cpp @@ -114,9 +114,13 @@ static INT FDKaacEnc_getSideInfoBits(const SECTION_INFO* const huffsection, const INT useHCR) { INT sideInfoBits; - if (useHCR && - ((huffsection->codeBook == 11) || (huffsection->codeBook >= 16))) { - sideInfoBits = 5; + if (useHCR) { + /* VCB11: 5 bit codebook, escape codebook sections are one sfb each */ + if ((huffsection->codeBook == 11) || (huffsection->codeBook >= 16)) { + sideInfoBits = 5 * huffsection->sfbCnt; + } else { + sideInfoBits = sideInfoTab[huffsection->sfbCnt] + 1; + } } else { sideInfoBits = sideInfoTab[huffsection->sfbCnt]; } @@ -411,11 +415,12 @@ static void FDKaacEnc_noiselessCounter( /* the sections in the sectionData are now marked with the optimal code * book */ - FDKaacEnc_findBestBook(bitLookUp[i], &(huffsection[i].codeBook), - useVCB11); + INT huffBits = FDKaacEnc_findBestBook( + bitLookUp[i], &(huffsection[i].codeBook), useVCB11); - sectionData->huffmanBits += - huffsection[i].sectionBits - + sectionData->huffmanBits += huffBits; + huffsection[i].sectionBits = + huffBits + FDKaacEnc_getSideInfoBits(&huffsection[i], sideInfoTab, useVCB11); } diff --git a/cpp/libAACenc/src/psy_configuration.cpp b/cpp/libAACenc/src/psy_configuration.cpp index eef90bc0f..b444b583a 100644 --- a/cpp/libAACenc/src/psy_configuration.cpp +++ b/cpp/libAACenc/src/psy_configuration.cpp @@ -130,6 +130,161 @@ static const SFB_INFO_TAB sfbInfoTab[] = { }; + + +const SFB_PARAM_LONG p_FDKaacEnc_8000_long_960 = { + 40, + { 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 16, + 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 24, 24, 24, 28, + 28, 32, 36, 36, 40, 44, 48, 52, 56, 60, 64, 16 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_8000_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 16, 20, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_11025_long_960 = { + 42, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_11025_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_12000_long_960 = { + 42, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_12000_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_16000_long_960 = { + 42, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 16, 16, 16, 16, 20, 20, 20, 24, + 24, 28, 28, 32, 36, 40, 40, 44, 48, 52, 56, 60, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_16000_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 12, 12, 16, 20, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_22050_long_960 = { + 46, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, + 20, 20, 24, 24, 28, 28, 32, 36, 36, 40, 44, 48, 52, 52, + 64, 64, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_22050_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_24000_long_960 = { + 46, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 16, 16, 16, + 20, 20, 24, 24, 28, 28, 32, 36, 36, 40, 44, 48, 52, 52, + 64, 64, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_24000_short_120 = { + 15, + { 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 16, 16, 12 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_32000_long_960 = { + 49, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, + 12, 16, 16, 20, 20, 24, 24, 28, 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32 } +}; + +const SFB_PARAM_SHORT p_FDKaacEnc_32000_short_120 = { + 14, + { 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 8 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_44100_long_960 = { + 49, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, + 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32 } +}; + +const SFB_PARAM_SHORT p_FDKaacEnc_44100_short_120 = { + 14, + { 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 8 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_48000_long_960 = { + 49, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, + 8, 8, 8, 12, 12, 12, 12, 16, 16, 20, 20, 24, 24, 28, + 28, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_48000_short_120 = { + 14, + { 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 8 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_64000_long_960 = { + 46, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, + 12, 16, 16, 16, 20, 24, 24, 28, 36, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 16 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_64000_short_120 = { + 12, + { 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_88200_long_960 = { + 40, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 12, + 12, 12, 12, 12, 16, 16, 24, 28, 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_88200_short_120 = { + 12, + { 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28 } +}; + +const SFB_PARAM_LONG p_FDKaacEnc_96000_long_960 = { + 40, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 12, + 12, 12, 12, 12, 16, 16, 24, 28, 36, 44, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 } +}; +const SFB_PARAM_SHORT p_FDKaacEnc_96000_short_120 = { + 12, + { 4, 4, 4, 4, 4, 4, 8, 8, 8, 16, 28, 28 } +}; + + +static const SFB_INFO_TAB sfbInfoTab960[] = { + { 8000, &p_FDKaacEnc_8000_long_960, &p_FDKaacEnc_8000_short_120}, + {11025, &p_FDKaacEnc_11025_long_960, &p_FDKaacEnc_11025_short_120}, + {12000, &p_FDKaacEnc_12000_long_960, &p_FDKaacEnc_12000_short_120}, + {16000, &p_FDKaacEnc_16000_long_960, &p_FDKaacEnc_16000_short_120}, + {22050, &p_FDKaacEnc_22050_long_960, &p_FDKaacEnc_22050_short_120}, + {24000, &p_FDKaacEnc_24000_long_960, &p_FDKaacEnc_24000_short_120}, + {32000, &p_FDKaacEnc_32000_long_960, &p_FDKaacEnc_32000_short_120}, + {44100, &p_FDKaacEnc_44100_long_960, &p_FDKaacEnc_44100_short_120}, + {48000, &p_FDKaacEnc_48000_long_960, &p_FDKaacEnc_48000_short_120}, + {64000, &p_FDKaacEnc_64000_long_960, &p_FDKaacEnc_64000_short_120}, + {88200, &p_FDKaacEnc_88200_long_960, &p_FDKaacEnc_88200_short_120}, + {96000, &p_FDKaacEnc_96000_long_960, &p_FDKaacEnc_96000_short_120}, +}; + + /* 22050 and 24000 Hz */ static const SFB_PARAM_LONG p_22050_long_512 = { 31, {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 12, 12, @@ -204,10 +359,13 @@ AAC_ENCODER_ERROR FDKaacEnc_initSfbTable(const LONG sampleRate, */ switch (granuleLength) { case 1024: - case 960: sfbInfo = sfbInfoTab; size = (INT)(sizeof(sfbInfoTab) / sizeof(SFB_INFO_TAB)); break; + case 960: + sfbInfo = sfbInfoTab960; + size = (INT)(sizeof(sfbInfoTab960)/sizeof(SFB_INFO_TAB)); + break; case 512: sfbInfo = sfbInfoTabLD512; size = sizeof(sfbInfoTabLD512); @@ -287,23 +445,31 @@ static FIXP_DBL FDKaacEnc_BarcLineValue(INT noOfLines, INT fftLine, center_freq = fftLine * samplingFreq; /* q11 or q8 */ + switch (noOfLines) { case 1024: center_freq = center_freq << 2; /* q13 */ break; + case 960: + center_freq = fMult(center_freq, INV480) << 3; + break; case 128: center_freq = center_freq << 5; /* q13 */ break; + case 120: + center_freq = fMult(center_freq, INV480) << 6; + break; case 512: - center_freq = (fftLine * samplingFreq) << 3; // q13 + center_freq = (fftLine * samplingFreq) << 3; // q13 break; case 480: - center_freq = fMult(center_freq, INV480) << 4; // q13 + center_freq = fMult(center_freq, INV480) << 4; // q13 break; default: center_freq = (FIXP_DBL)0; } + x1 = fMult(center_freq, FOURBY3EM4); /* q13 * q43 - (DFRACT_BITS-1) = q25 */ x2 = fMult(center_freq, PZZZ76) << 2; /* q13 * q41 - (DFRACT_BITS-1) + 2 = q25 */ @@ -468,6 +634,14 @@ static void FDKaacEnc_initMinSnr(const LONG bitrate, const LONG samplerate, qperwin = qperwin - 9; pePerWindow = fMult(pePerWindow, FL2FXCONST_DBL(480.f / 512.f)); break; + case 960: + pePerWindow = fMult(pePerWindow, FL2FXCONST_DBL(960.f/1024.f)); + qperwin = qperwin - 10; + break; + case 120: + pePerWindow = fMult(pePerWindow, FL2FXCONST_DBL(120.f/128.f)); + qperwin = qperwin - 7; + break; } /* for short blocks it is assumed that more bits are available */ diff --git a/cpp/libAACenc/src/psy_const.h b/cpp/libAACenc/src/psy_const.h index c2d53042c..c3f3f64a6 100644 --- a/cpp/libAACenc/src/psy_const.h +++ b/cpp/libAACenc/src/psy_const.h @@ -108,6 +108,7 @@ amm-info@iis.fraunhofer.de #define TRANS_FAC 8 /* encoder short long ratio */ +#define FRAME_LEN_LONG_960 (960) #define FRAME_MAXLEN_SHORT ((1024) / TRANS_FAC) #define FRAME_LEN_SHORT_128 ((1024) / TRANS_FAC) #define FRAME_LEN_SHORT_120 (FRAME_LEN_LONG_960 / TRANS_FAC) diff --git a/cpp/libAACenc/src/psy_main.cpp b/cpp/libAACenc/src/psy_main.cpp index f6345e4e5..717a5dacc 100644 --- a/cpp/libAACenc/src/psy_main.cpp +++ b/cpp/libAACenc/src/psy_main.cpp @@ -239,6 +239,12 @@ AAC_ENCODER_ERROR FDKaacEnc_psyInitStates(PSY_INTERNAL *hPsy, FDKaacEnc_InitBlockSwitching(&psyStatic->blockSwitchingControl, isLowDelay(audioObjectType)); + if (audioObjectType == AOT_DRM_AAC) { + /* DRM: the HCR encoder only handles long blocks */ + psyStatic->blockSwitchingControl.allowShortFrames = 0; + psyStatic->blockSwitchingControl.allowLookAhead = 0; + } + return AAC_ENC_OK; } diff --git a/cpp/libSBRenc/include/sbr_encoder.h b/cpp/libSBRenc/include/sbr_encoder.h index d979ba6c7..1e2f15017 100644 --- a/cpp/libSBRenc/include/sbr_encoder.h +++ b/cpp/libSBRenc/include/sbr_encoder.h @@ -174,6 +174,7 @@ typedef struct sbrConfiguration { INT SendHeaderDataTime; /*!< SBR header send update frequency in ms. */ INT useWaveCoding; /*!< Flag: usage of wavecoding tool. */ INT crcSbr; /*!< Flag: usage of SBR-CRC. */ + INT drmSyntax; /*!< Flag: DRM SBR syntax (8 bit CRC). */ INT dynBwSupported; /*!< Flag: support for dynamic bandwidth in this combination. */ INT parametricCoding; /*!< Flag: usage of parametric coding tool. */ diff --git a/cpp/libSBRenc/src/bit_sbr.cpp b/cpp/libSBRenc/src/bit_sbr.cpp index 5a65e98f7..0fb623c58 100644 --- a/cpp/libSBRenc/src/bit_sbr.cpp +++ b/cpp/libSBRenc/src/bit_sbr.cpp @@ -417,7 +417,7 @@ static INT encodeSbrSingleChannelElement( } } else { if (sbrSyntaxFlags & SBR_SYNTAX_SCALABLE) { - payloadBits += FDKwriteBits(hBitStream, 1, SI_SBR_COUPLING_BITS); + payloadBits += FDKwriteBits(hBitStream, 0, SI_SBR_COUPLING_BITS); } payloadBits += encodeSbrGrid(sbrEnvData, hBitStream); } diff --git a/cpp/libSBRenc/src/sbr_encoder.cpp b/cpp/libSBRenc/src/sbr_encoder.cpp index c3da07286..761651d5e 100644 --- a/cpp/libSBRenc/src/sbr_encoder.cpp +++ b/cpp/libSBRenc/src/sbr_encoder.cpp @@ -646,6 +646,7 @@ static UINT FDKsbrEnc_InitializeSbrDefaults(sbrConfigurationPtr config, config->SendHeaderDataTime = 1000; config->useWaveCoding = 0; config->crcSbr = 0; + config->drmSyntax = 0; config->dynBwSupported = 1; if (isLowDelay) config->tran_thr = 6000; @@ -1681,6 +1682,10 @@ static INT FDKsbrEnc_EnvInit(HANDLE_SBR_ELEMENT hSbrElement, if (params->crcSbr) { hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_CRC; } + if (params->drmSyntax) { + hSbrElement->sbrConfigData.sbrSyntaxFlags |= + SBR_SYNTAX_CRC | SBR_SYNTAX_DRM_CRC | SBR_SYNTAX_SCALABLE; + } hSbrElement->sbrConfigData.noQmfBands = 64 >> (2 - params->downSampleFactor); switch (hSbrElement->sbrConfigData.noQmfBands) { @@ -2193,6 +2198,10 @@ INT sbrEncoder_Init(HANDLE_SBR_ENCODER hSbrEncoder, error = 1; goto bail; } + if (syntaxFlags & AC_DRM) { + sbrConfig[el].crcSbr = 1; + sbrConfig[el].drmSyntax = 1; + } /* * Modify sbrConfig structure according to Element parameters