PR #21668 opened by ShortKatz URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21668 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21668.patch
Add support for E-AC-3 channel layouts beyond 5.1 by encoding extra channels (back surround, top height) in a dependent substream. Supported layouts: 7.1, 5.1.2, 5.1.4, 7.1.2. Add FATE framecrc tests for all four layouts using self-generated samples. >From f32ba6711cb3929d07ea7df3382281965352cb1b Mon Sep 17 00:00:00 2001 From: Nomis101 <[email protected]> Date: Fri, 6 Feb 2026 23:28:33 +0100 Subject: [PATCH] avcodec/eac3enc: add dependent substream encoding for multichannel layouts --- libavcodec/ac3enc.c | 830 ++++++++++++++++++++++++++++++- libavcodec/ac3enc.h | 30 ++ libavcodec/eac3enc.c | 160 +++++- libavcodec/eac3enc.h | 8 + tests/fate/ac3.mak | 27 +- tests/ref/fate/eac3-encode-5.1.2 | 13 + tests/ref/fate/eac3-encode-5.1.4 | 13 + tests/ref/fate/eac3-encode-7.1 | 13 + tests/ref/fate/eac3-encode-7.1.2 | 13 + 9 files changed, 1104 insertions(+), 3 deletions(-) create mode 100644 tests/ref/fate/eac3-encode-5.1.2 create mode 100644 tests/ref/fate/eac3-encode-5.1.4 create mode 100644 tests/ref/fate/eac3-encode-7.1 create mode 100644 tests/ref/fate/eac3-encode-7.1.2 diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c index 5a1a3ab63a..a662084fe2 100644 --- a/libavcodec/ac3enc.c +++ b/libavcodec/ac3enc.c @@ -32,6 +32,7 @@ #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/crc.h" +#include "libavutil/float_dsp.h" #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/mem_internal.h" @@ -1978,11 +1979,652 @@ static void ac3_output_frame(AC3EncodeContext *s, unsigned char *frame) output_frame_end(s, &pb); } +/** + * Extract exponents from MDCT coefficients for dependent channels. + */ +static void dep_extract_exponents(AC3EncodeContext *s) +{ + int ch; + int chan_size = AC3_MAX_COEFS * s->num_blocks; + + for (ch = 0; ch < s->dependent_channels; ch++) { + s->ac3dsp.extract_exponents(s->dep_blocks[0].exp[ch], + s->dep_blocks[0].fixed_coef[ch], + chan_size); + } +} + +/** + * Encode exponents for a single channel in the dependent stream. + * Applies differential encoding and constrains deltas to [-2, 2]. + */ +static void dep_encode_exponents_blk_ch(uint8_t *exp, int nb_coefs, int exp_strategy) +{ + int nb_groups, i, k; + + /* Multiply by 3 because each grouped value represents 3 individual exponents */ + nb_groups = exponent_group_tab[0][exp_strategy-1][nb_coefs] * 3; + + /* for D25 and D45 modes, average and expand the exponents */ + switch (exp_strategy) { + case EXP_D25: + for (i = 1, k = 1; i <= nb_groups; i++) { + uint8_t exp_min = FFMIN(exp[k], exp[k+1]); + exp[i] = exp_min; + k += 2; + } + break; + case EXP_D45: + for (i = 1, k = 1; i <= nb_groups; i++) { + uint8_t exp_min = FFMIN(FFMIN(exp[k], exp[k+1]), FFMIN(exp[k+2], exp[k+3])); + exp[i] = exp_min; + k += 4; + } + break; + } + + /* DC exponent constraint */ + if (exp[0] > 15) + exp[0] = 15; + + /* constrain deltas to [-2, 2] */ + for (i = 1; i <= nb_groups; i++) + exp[i] = FFMIN(exp[i], exp[i-1] + 2); + i--; + while (--i >= 0) + exp[i] = FFMIN(exp[i], exp[i+1] + 2); + + /* expand exponents to decoder values */ + switch (exp_strategy) { + case EXP_D25: + for (i = nb_groups, k = (nb_groups * 2); i > 0; i--) { + uint8_t exp1 = exp[i]; + exp[k--] = exp1; + exp[k--] = exp1; + } + break; + case EXP_D45: + for (i = nb_groups, k = (nb_groups * 4); i > 0; i--) { + exp[k] = exp[k-1] = exp[k-2] = exp[k-3] = exp[i]; + k -= 4; + } + break; + } +} + +/** + * Encode exponents for all dependent channels. + */ +static void dep_encode_exponents(AC3EncodeContext *s) +{ + int blk, blk1, ch; + uint8_t *exp; + int nb_coefs, num_reuse_blocks; + + for (ch = 0; ch < s->dependent_channels; ch++) { + exp = s->dep_blocks[0].exp[ch]; + + blk = 0; + while (blk < s->num_blocks) { + AC3Block *block = &s->dep_blocks[blk]; + nb_coefs = block->end_freq[ch]; + blk1 = blk + 1; + + /* set reference blocks for EXP_REUSE */ + s->dep_exp_ref_block[ch][blk] = blk; + while (blk1 < s->num_blocks && s->dep_exp_strategy[ch][blk1] == EXP_REUSE) { + s->dep_exp_ref_block[ch][blk1] = blk; + blk1++; + } + num_reuse_blocks = blk1 - blk - 1; + + /* for EXP_REUSE, select minimum exponents */ + s->ac3dsp.ac3_exponent_min(exp, num_reuse_blocks, AC3_MAX_COEFS); + + dep_encode_exponents_blk_ch(exp, nb_coefs, s->dep_exp_strategy[ch][blk]); + + exp += AC3_MAX_COEFS * (num_reuse_blocks + 1); + blk = blk1; + } + } +} + + +/** + * Group exponents for dependent channels. + */ +static void dep_group_exponents(AC3EncodeContext *s) +{ + int blk, ch, i; + int group_size, nb_groups; + uint8_t *p; + int delta0, delta1, delta2; + int exp0, exp1; + + for (blk = 0; blk < s->num_blocks; blk++) { + for (ch = 0; ch < s->dependent_channels; ch++) { + int exp_strategy = s->dep_exp_strategy[ch][blk]; + AC3Block *block = &s->dep_blocks[blk]; + + if (exp_strategy == EXP_REUSE) + continue; + + group_size = exp_strategy + (exp_strategy == EXP_D45); + nb_groups = exponent_group_tab[0][exp_strategy-1][block->end_freq[ch]]; + p = block->exp[ch]; + + /* DC exponent */ + exp1 = *p++; + block->grouped_exp[ch][0] = exp1; + + /* remaining exponents are delta encoded */ + for (i = 1; i <= nb_groups; i++) { + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta0 = exp1 - exp0 + 2; + av_assert2(delta0 >= 0 && delta0 <= 4); + + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta1 = exp1 - exp0 + 2; + av_assert2(delta1 >= 0 && delta1 <= 4); + + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta2 = exp1 - exp0 + 2; + av_assert2(delta2 >= 0 && delta2 <= 4); + + block->grouped_exp[ch][i] = ((delta0 * 5 + delta1) * 5) + delta2; + } + } + } +} + +/** + * Count mantissa bits for dependent channels with given SNR offset. + */ +static int dep_count_mantissa_bits(AC3EncodeContext *s, int snr_offset) +{ + int blk, ch; + LOCAL_ALIGNED_16(uint16_t, mant_cnt, [AC3_MAX_BLOCKS], [16]); + int end_freq; + + /* Initialize mantissa counts */ + for (blk = 0; blk < AC3_MAX_BLOCKS; blk++) { + memset(mant_cnt[blk], 0, sizeof(mant_cnt[blk])); + mant_cnt[blk][1] = mant_cnt[blk][2] = 2; + mant_cnt[blk][4] = 1; + } + + /* Calculate bap values and count bits */ + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + for (ch = 0; ch < s->dependent_channels; ch++) { + int ref_blk = s->dep_exp_ref_block[ch][blk]; + end_freq = block->end_freq[ch]; + + /* Calculate bap for this channel/block */ + s->ac3dsp.bit_alloc_calc_bap(s->dep_blocks[ref_blk].mask[ch], + s->dep_blocks[ref_blk].psd[ch], + 0, end_freq, + snr_offset, s->bit_alloc.floor, + ff_ac3_bap_tab, + s->dep_ref_bap[ch][blk]); + + /* Update mantissa counts */ + s->ac3dsp.update_bap_counts(mant_cnt[blk], + s->dep_ref_bap[ch][blk], + end_freq); + } + } + + return s->ac3dsp.compute_mantissa_size(mant_cnt); +} + +/** + * Compute bit allocation for dependent channels with rate control. + * Calculate psd, mask, and bap for each channel, ensuring bits fit in frame. + */ +static int dep_compute_bit_allocation(AC3EncodeContext *s) +{ + int blk, ch; + int snr_offset, snr_incr; + int bits_left, mantissa_bits; + int header_bits, exponent_bits; + int end_freq; + + /* Initialize ref_bap pointers */ + for (ch = 0; ch < s->dependent_channels; ch++) { + for (blk = 0; blk < s->num_blocks; blk++) { + s->dep_ref_bap[ch][blk] = &s->dep_bap_buffer[AC3_MAX_COEFS * (s->num_blocks * ch + blk)]; + } + } + + end_freq = s->dep_blocks[0].end_freq[0]; + + /* Calculate psd and mask for all channels/blocks */ + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + for (ch = 0; ch < s->dependent_channels; ch++) { + if (s->dep_exp_strategy[ch][blk] != EXP_REUSE) { + /* Calculate psd */ + ff_ac3_bit_alloc_calc_psd(block->exp[ch], 0, + block->end_freq[ch], block->psd[ch], + block->band_psd[ch]); + + /* Calculate mask */ + ff_ac3_bit_alloc_calc_mask(&s->bit_alloc, block->band_psd[ch], + 0, block->end_freq[ch], + ff_ac3_fast_gain_tab[s->dep_fast_gain_code[ch]], + 0, /* not LFE */ + DBA_NONE, 0, NULL, NULL, NULL, + block->mask[ch]); + } + } + } + + /* Calculate header bits for dependent frame: + * sync word (16) + BSI (~80) + per-block overhead (~20 each) + * + aux/CRC (18) */ + header_bits = 114 + 20 * s->num_blocks; + + /* Calculate exponent bits: + * DC exp (4 bits) + grouped exponents (7 bits each) per channel per non-reuse block */ + exponent_bits = 0; + for (ch = 0; ch < s->dependent_channels; ch++) { + for (blk = 0; blk < s->num_blocks; blk++) { + if (s->dep_exp_strategy[ch][blk] != EXP_REUSE) { + int nb_groups = exponent_group_tab[0][s->dep_exp_strategy[ch][blk]-1][end_freq]; + exponent_bits += 4 + 7 * nb_groups + 2; /* DC exp + grouped exps + gain range */ + } + } + } + + /* Calculate bits available for mantissas */ + bits_left = s->dependent_frame_size * 8 - header_bits - exponent_bits; + if (bits_left < 0) + bits_left = 0; + + /* Try bit allocation at max SNR offset first. If it doesn't fit, + * reduce bandwidth until it does. This handles the case where even + * at maximum SNR offset, the floor term in bap calculation causes + * high-energy bins to require too many mantissa bits. */ + mantissa_bits = dep_count_mantissa_bits(s, (1023 - 240) * 4); + while (mantissa_bits > bits_left && end_freq > 76) { + /* Reduce bandwidth by 3 (one bandwidth code step) */ + end_freq -= 3; + for (blk = 0; blk < s->num_blocks; blk++) + for (ch = 0; ch < s->dependent_channels; ch++) + s->dep_blocks[blk].end_freq[ch] = end_freq; + + /* Recalculate exponent bits with new bandwidth */ + exponent_bits = 0; + for (ch = 0; ch < s->dependent_channels; ch++) { + for (blk = 0; blk < s->num_blocks; blk++) { + if (s->dep_exp_strategy[ch][blk] != EXP_REUSE) { + int nb_groups = exponent_group_tab[0][s->dep_exp_strategy[ch][blk]-1][end_freq]; + exponent_bits += 4 + 7 * nb_groups + 2; + } + } + } + bits_left = s->dependent_frame_size * 8 - header_bits - exponent_bits; + if (bits_left < 0) + bits_left = 0; + + /* Recompute psd/mask with reduced bandwidth */ + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + for (ch = 0; ch < s->dependent_channels; ch++) { + if (s->dep_exp_strategy[ch][blk] != EXP_REUSE) { + ff_ac3_bit_alloc_calc_psd(block->exp[ch], 0, + end_freq, block->psd[ch], + block->band_psd[ch]); + ff_ac3_bit_alloc_calc_mask(&s->bit_alloc, block->band_psd[ch], + 0, end_freq, + ff_ac3_fast_gain_tab[s->dep_fast_gain_code[ch]], + 0, DBA_NONE, 0, NULL, NULL, NULL, + block->mask[ch]); + } + } + } + + mantissa_bits = dep_count_mantissa_bits(s, (1023 - 240) * 4); + } + + /* Start SNR search from main stream's coarse offset */ + snr_offset = s->coarse_snr_offset << 4; + + /* Find lowest SNR offset where bits fit */ + while (snr_offset <= 1023) { + mantissa_bits = dep_count_mantissa_bits(s, (snr_offset - 240) * 4); + if (mantissa_bits <= bits_left) + break; + snr_offset += 64; + } + + if (snr_offset > 1023) + snr_offset = 1023; + + /* Refine: binary search for lowest offset that fits. + * Don't go below 1 because snr_offset=0 converted to (0-240)*4=-960 + * triggers a special case in bit_alloc_calc_bap that sets all bap=0. */ + for (snr_incr = 32; snr_incr > 0; snr_incr >>= 1) { + while (snr_offset - snr_incr >= 1) { + mantissa_bits = dep_count_mantissa_bits(s, (snr_offset - snr_incr - 240) * 4); + if (mantissa_bits <= bits_left) { + snr_offset -= snr_incr; + } else { + break; + } + } + } + + /* Final pass: calculate bap values with chosen SNR offset */ + snr_offset = (snr_offset - 240) * 4; + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + for (ch = 0; ch < s->dependent_channels; ch++) { + int ref_blk = s->dep_exp_ref_block[ch][blk]; + s->ac3dsp.bit_alloc_calc_bap(s->dep_blocks[ref_blk].mask[ch], + s->dep_blocks[ref_blk].psd[ch], + 0, block->end_freq[ch], + snr_offset, s->bit_alloc.floor, + ff_ac3_bap_tab, + s->dep_ref_bap[ch][blk]); + } + } + + /* Store coarse and fine SNR offsets for header */ + { + int combined = snr_offset / 4 + 240; + s->dep_coarse_snr_offset = combined >> 4; + s->dep_fine_snr_offset[0] = combined & 0xF; + } + + return 0; +} + +/** + * Quantize mantissas for dependent channels. + */ +static void dep_quantize_mantissas(AC3EncodeContext *s) +{ + int blk, ch; + + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + AC3Mant m = { 0 }; + + for (ch = 0; ch < s->dependent_channels; ch++) { + int ref_blk = s->dep_exp_ref_block[ch][blk]; + quantize_mantissas_blk_ch(&m, block->fixed_coef[ch], + s->dep_blocks[ref_blk].exp[ch], + s->dep_ref_bap[ch][blk], block->qmant[ch], + 0, block->end_freq[ch]); + } + } +} + +/** + * Process the dependent substream for extended channel layouts. + * This performs full MDCT transformation, exponent encoding, bit allocation, + * and mantissa quantization for the extra channels (e.g., back surrounds in 7.1). + * + * @param s AC-3 encoder private context + * @param samples input sample data (all channels) + */ +static void process_dependent_substream(AC3EncodeContext *s, uint8_t * const *samples) +{ + int blk, ch; + const unsigned sampletype_size = SAMPLETYPE_SIZE(s); + + int end_freq = s->bandwidth_code * 3 + 73; /* Same bandwidth as independent stream */ + + /* Initialize end_freq for all dependent blocks and channels */ + for (blk = 0; blk < s->num_blocks; blk++) { + for (ch = 0; ch < s->dependent_channels; ch++) { + s->dep_blocks[blk].end_freq[ch] = end_freq; + } + } + + /* Set exponent strategies for dependent channels */ + for (ch = 0; ch < s->dependent_channels; ch++) { + /* Use EXP_D15 for first block, EXP_REUSE for rest */ + s->dep_exp_strategy[ch][0] = EXP_D15; + for (blk = 1; blk < s->num_blocks; blk++) + s->dep_exp_strategy[ch][blk] = EXP_REUSE; + } + + /* Apply MDCT to dependent channels + * Note: EAC3 is always float, so we use the float path exclusively. + * The fdsp is actually AVFloatDSPContext* at runtime for EAC3. */ + for (ch = 0; ch < s->dependent_channels; ch++) { + int input_ch_idx = s->channel_map_dep[ch]; + const uint8_t *input_samples0 = s->dep_samples[ch]; + const uint8_t *input_samples1 = samples[input_ch_idx]; + + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + /* Float path - EAC3 dependent streams are always float */ + float *windowed = s->windowed_samples_float; + const float *in0 = (const float *)input_samples0; + const float *in1 = (const float *)input_samples1; + + /* Apply window using fdsp (cast to float dsp context) */ + AVFloatDSPContext *fdsp = (AVFloatDSPContext *)s->fdsp; + fdsp->vector_fmul(windowed, in0, s->mdct_window_float, AC3_BLOCK_SIZE); + fdsp->vector_fmul_reverse(windowed + AC3_BLOCK_SIZE, in1, + s->mdct_window_float, AC3_BLOCK_SIZE); + + /* Apply MDCT */ + s->tx_fn(s->tx, block->mdct_coef[ch], windowed, sizeof(float)); + + /* Advance input pointers */ + input_samples0 = input_samples1; + input_samples1 = input_samples1 + AC3_BLOCK_SIZE * sampletype_size; + } + + /* Store last 256 samples for next frame */ + memcpy(s->dep_samples[ch], input_samples0, AC3_BLOCK_SIZE * sampletype_size); + } + + /* Scale float coefficients to fixed-point */ + if (!s->fixed_point) { + int chan_size = AC3_MAX_COEFS * s->num_blocks; + for (ch = 0; ch < s->dependent_channels; ch++) { + s->ac3dsp.float_to_fixed24(s->dep_blocks[0].fixed_coef[ch], + (const float *)s->dep_blocks[0].mdct_coef[ch], + chan_size); + } + } + + /* Extract exponents from MDCT coefficients */ + dep_extract_exponents(s); + + /* Encode exponents */ + dep_encode_exponents(s); + + /* Compute bit allocation */ + dep_compute_bit_allocation(s); + + /* Group exponents */ + dep_group_exponents(s); + + /* Quantize mantissas */ + dep_quantize_mantissas(s); +} + +/** + * Output mantissas for dependent channels in one block. + */ +static void dep_output_mantissas(AC3EncodeContext *s, PutBitContext *pb, int blk) +{ + int ch, i; + AC3Block *block = &s->dep_blocks[blk]; + + for (ch = 0; ch < s->dependent_channels; ch++) { + int end_freq = block->end_freq[ch]; + uint8_t *bap = s->dep_ref_bap[ch][blk]; + uint16_t *qmant = block->qmant[ch]; + + for (i = 0; i < end_freq; i++) { + int b = bap[i]; + int q = qmant[i]; + + switch (b) { + case 0: + break; + case 1: + /* 3 values grouped in 5 bits, 128 means already output */ + if (q != 128) + put_bits(pb, 5, q); + break; + case 2: + /* 3 values grouped in 7 bits */ + if (q != 128) + put_bits(pb, 7, q); + break; + case 3: + /* 3-bit signed mantissa */ + put_sbits(pb, 3, q); + break; + case 4: + /* 2 values grouped in 7 bits */ + if (q != 128) + put_bits(pb, 7, q); + break; + case 14: + /* 14-bit signed mantissa */ + put_sbits(pb, 14, q); + break; + case 15: + /* 16-bit signed mantissa */ + put_sbits(pb, 16, q); + break; + default: + /* bap 5-13: (bap-1) bit signed mantissa */ + put_sbits(pb, b - 1, q); + break; + } + } + } +} + +/** + * Encode and output the dependent substream for extended channel layouts. + * This outputs a fully encoded E-AC-3 dependent frame with real audio data. + */ +static void eac3_output_dependent_frame(AC3EncodeContext *s, unsigned char *frame) +{ + PutBitContext pb; + int blk, ch, i; + const AVCRC *crc_ctx = av_crc_get_table(AV_CRC_16_ANSI); + int pad_bytes; + uint16_t crc2; + + init_put_bits(&pb, frame, s->dependent_frame_size); + + /* Output dependent frame header */ + ff_eac3_output_dep_frame_header(s, &pb); + + /* Output audio blocks for dependent stream */ + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + + /* dynamic range - 1 bit for stereo (channel_mode != 0) */ + put_bits(&pb, 1, 0); + + /* spectral extension - for E-AC3 block 0, output spx_in_use directly */ + put_bits(&pb, 1, 0); + + /* No coupling in dependent stream - already signaled in header */ + + /* stereo rematrixing - required for stereo mode (acmod=2) */ + if (s->dependent_channels == 2) { + if (blk == 0) { + /* Block 0: output rematrixing flags (4 bands, no strategy bit) */ + put_bits(&pb, 1, 0); /* band 0 rematrixing flag */ + put_bits(&pb, 1, 0); /* band 1 rematrixing flag */ + put_bits(&pb, 1, 0); /* band 2 rematrixing flag */ + put_bits(&pb, 1, 0); /* band 3 rematrixing flag */ + } else { + /* Blocks 1-5: output new_rematrixing_strategy bit */ + put_bits(&pb, 1, 0); /* no new rematrixing strategy */ + } + } + + /* bandwidth - only when not reusing exponents */ + for (ch = 0; ch < s->dependent_channels; ch++) { + if (s->dep_exp_strategy[ch][blk] != EXP_REUSE) { + /* Calculate bandwidth code from end_freq */ + int bw_code = (block->end_freq[ch] - 73) / 3; + bw_code = av_clip(bw_code, 0, 60); + put_bits(&pb, 6, bw_code); + } + } + + /* exponents */ + for (ch = 0; ch < s->dependent_channels; ch++) { + int nb_groups; + int exp_strategy = s->dep_exp_strategy[ch][blk]; + + if (exp_strategy == EXP_REUSE) + continue; + + /* DC exponent */ + put_bits(&pb, 4, block->grouped_exp[ch][0]); + + /* exponent groups */ + nb_groups = exponent_group_tab[0][exp_strategy-1][block->end_freq[ch]]; + for (i = 1; i <= nb_groups; i++) + put_bits(&pb, 7, block->grouped_exp[ch][i]); + + /* gain range info */ + put_bits(&pb, 2, 0); + } + + /* Note: E-AC-3 to AC-3 converter SNR offset is NOT output for dependent frames. + * It's only read for independent frames (frame_type == 0). */ + + /* mantissas */ + dep_output_mantissas(s, &pb, blk); + } + + /* auxiliary data */ + put_bits(&pb, 1, 0); + + /* CRC */ + put_bits(&pb, 1, 0); /* crcrsv */ + + /* Pad and compute CRC */ + flush_put_bits(&pb); + + pad_bytes = s->dependent_frame_size - (put_bits_ptr(&pb) - frame) - 2; + if (pad_bytes < 0) { + av_log(s->avctx, AV_LOG_WARNING, "Dependent frame overflow by %d bytes\n", -pad_bytes); + pad_bytes = 0; + } + if (pad_bytes > 0) + memset(put_bits_ptr(&pb), 0, pad_bytes); + + /* compute crc2 for E-AC-3 */ + crc2 = av_bswap16(av_crc(crc_ctx, 0, frame + 2, s->dependent_frame_size - 4)); + if (crc2 == 0x0B77) { + frame[s->dependent_frame_size - 3] ^= 0x1; + crc2 ^= 0x8005; + } + AV_WB16(frame + s->dependent_frame_size - 2, crc2); +} + int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr) { AC3EncodeContext *const s = avctx->priv_data; int ret; + int total_frame_size; if (s->options.allow_per_frame_metadata) { ret = ac3_validate_metadata(s); @@ -1993,6 +2635,37 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (s->bit_alloc.sr_code == 1 || s->eac3) ac3_adjust_frame_size(s); + /* For extended channel layouts, calculate dependent frame size */ + if (s->eac3_dependent_enabled) { + /* Dependent stream bitrate scales with number of extra channels + * Use ~96kbps per dependent channel for full audio encoding */ + int dep_bitrate = s->dependent_channels * 96000; + int frame_samples = AC3_BLOCK_SIZE * s->num_blocks; + int min_frame_size; + int end_freq = s->bandwidth_code * 3 + 73; + + s->dependent_frame_size = ((dep_bitrate / 16) * frame_samples / s->sample_rate) * 2; + + /* Minimum frame size accounting for real audio encoding: + * - Header: ~80 bytes + * - Per block: exponents (~end_freq/3 * 7 bits) + mantissas (~end_freq * 6 bits avg) + * - 6 blocks, per channel */ + min_frame_size = 80 + (s->num_blocks * end_freq * s->dependent_channels * 8 / 8); + if (s->dependent_frame_size < min_frame_size) + s->dependent_frame_size = min_frame_size; + + /* E-AC-3 max frame size is 4096 bytes (frmsiz is 11 bits: (4096/2-1) = 2047) + * Cap at 3840 to leave some margin */ + if (s->dependent_frame_size > 3840) + s->dependent_frame_size = 3840; + + /* Ensure frame size is even and word-aligned */ + s->dependent_frame_size = (s->dependent_frame_size + 3) & ~3; + total_frame_size = s->frame_size + s->dependent_frame_size; + } else { + total_frame_size = s->frame_size; + } + s->encode_frame(s, frame->extended_data); ac3_apply_rematrixing(s); @@ -2009,11 +2682,21 @@ int ff_ac3_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ac3_quantize_mantissas(s); - ret = ff_get_encode_buffer(avctx, avpkt, s->frame_size, 0); + /* For extended channel layouts, process the dependent stream with full audio encoding */ + if (s->eac3_dependent_enabled) + process_dependent_substream(s, frame->extended_data); + + ret = ff_get_encode_buffer(avctx, avpkt, total_frame_size, 0); if (ret < 0) return ret; + + /* Output independent frame */ ac3_output_frame(s, avpkt->data); + /* Output dependent frame mode */ + if (s->eac3_dependent_enabled) + eac3_output_dependent_frame(s, avpkt->data + s->frame_size); + if (frame->pts != AV_NOPTS_VALUE) avpkt->pts = frame->pts - ff_samples_to_time_base(avctx, avctx->initial_padding); @@ -2170,12 +2853,50 @@ av_cold int ff_ac3_encode_close(AVCodecContext *avctx) av_freep(&s->cpl_coord_buffer); av_freep(&s->fdsp); + /* Free dependent substream buffers */ + if (s->eac3_dependent_enabled) { + for (int ch = 0; ch < s->dependent_channels; ch++) + av_freep(&s->dep_samples[ch]); + av_freep(&s->dep_bap_buffer); + av_freep(&s->dep_mdct_coef_buffer); + av_freep(&s->dep_fixed_coef_buffer); + av_freep(&s->dep_exp_buffer); + av_freep(&s->dep_grouped_exp_buffer); + av_freep(&s->dep_psd_buffer); + av_freep(&s->dep_band_psd_buffer); + av_freep(&s->dep_mask_buffer); + av_freep(&s->dep_qmant_buffer); + } + av_tx_uninit(&s->tx); return 0; } +/** + * E-AC-3 dependent substream channel definitions. + * Maps channel mask bits to E-AC-3 custom channel map bit positions. + * The custom channel map is a 16-bit field where bits indicate extra channels. + */ +static const struct { + uint64_t av_mask; ///< FFmpeg channel mask + int map_bit; ///< E-AC-3 custom channel map bit position (0-15, from MSB) + int num_channels; ///< Number of channels this entry represents +} eac3_dep_channel_map[] = { + { AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER, 5, 2 }, + { AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT, 6, 2 }, + { AV_CH_BACK_CENTER, 7, 1 }, + { AV_CH_TOP_CENTER, 8, 1 }, + { AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT, 9, 2 }, + { AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT, 10, 2 }, + { AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT, 11, 2 }, + { AV_CH_TOP_FRONT_CENTER, 12, 1 }, + { AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT, 13, 2 }, + { AV_CH_LOW_FREQUENCY_2, 14, 1 }, + { 0, 0, 0 } /* terminator */ +}; + /* * Set channel information during initialization. */ @@ -2185,6 +2906,49 @@ static av_cold void set_channel_info(AVCodecContext *avctx) uint64_t mask = av_channel_layout_subset(&avctx->ch_layout, ~(uint64_t)0); int channels = avctx->ch_layout.nb_channels; + /* Base 5.1 channel mask (what goes in independent stream) */ + const uint64_t base_mask = AV_CH_FRONT_LEFT | AV_CH_FRONT_CENTER | AV_CH_FRONT_RIGHT | + AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT | AV_CH_LOW_FREQUENCY; + + /* Initialize dependent substream settings */ + s->eac3_dependent_enabled = 0; + s->dependent_channel_map = 0; + s->dependent_channels = 0; + s->input_channels = channels; + + /* Check for channels beyond 5.1 - requires E-AC-3 */ + if (s->eac3 && channels > 6) { + uint64_t dep_mask = mask & ~base_mask; + int dep_ch_idx = 0; + + /* Check each possible dependent channel type */ + for (int i = 0; eac3_dep_channel_map[i].av_mask != 0; i++) { + uint64_t ch_mask = eac3_dep_channel_map[i].av_mask; + if ((dep_mask & ch_mask) == ch_mask) { + /* This channel group is present - add to dependent stream */ + s->dependent_channel_map |= (1 << (15 - eac3_dep_channel_map[i].map_bit)); + + /* Find input channel indices for this channel group */ + for (int ch = 0; ch < avctx->ch_layout.nb_channels && dep_ch_idx < 16; ch++) { + enum AVChannel channel = av_channel_layout_channel_from_index(&avctx->ch_layout, ch); + uint64_t ch_bit = (channel != AV_CHAN_NONE && channel < 64) ? (1ULL << channel) : 0; + if (ch_bit && (ch_mask & ch_bit)) { + s->channel_map_dep[dep_ch_idx++] = ch; + } + } + + /* Remove from main mask */ + mask &= ~ch_mask; + } + } + + if (dep_ch_idx > 0) { + s->eac3_dependent_enabled = 1; + s->dependent_channels = dep_ch_idx; + channels = avctx->ch_layout.nb_channels - dep_ch_idx; + } + } + s->lfe_on = !!(mask & AV_CH_LOW_FREQUENCY); s->channels = channels; s->fbw_channels = channels - s->lfe_on; @@ -2234,6 +2998,9 @@ static av_cold int validate_options(AC3EncodeContext *s) case 4: avctx->bit_rate = 384000; break; case 5: avctx->bit_rate = 448000; break; } + /* Add extra bitrate for dependent stream (~64kbps per channel) */ + if (s->eac3_dependent_enabled) + avctx->bit_rate += s->dependent_channels * 64000; } /* validate bit rate */ @@ -2465,6 +3232,67 @@ static av_cold int allocate_buffers(AC3EncodeContext *s) } } + /* Allocate buffers for dependent substream (7.1, 5.1.2, 5.1.4, 7.1.2, etc.) */ + if (s->eac3_dependent_enabled) { + int dep_channels = s->dependent_channels; + int dep_channel_blocks = dep_channels * s->num_blocks; + int dep_total_coefs = AC3_MAX_COEFS * dep_channel_blocks; + + /* Sample buffers for dependent channels */ + for (ch = 0; ch < dep_channels; ch++) { + s->dep_samples[ch] = av_mallocz((AC3_FRAME_SIZE + AC3_BLOCK_SIZE) * sampletype_size); + if (!s->dep_samples[ch]) + return AVERROR(ENOMEM); + } + + if (!FF_ALLOC_TYPED_ARRAY(s->dep_bap_buffer, dep_total_coefs) || + !FF_ALLOCZ_TYPED_ARRAY(s->dep_mdct_coef_buffer, dep_total_coefs) || + !FF_ALLOC_TYPED_ARRAY(s->dep_exp_buffer, dep_total_coefs) || + !FF_ALLOC_TYPED_ARRAY(s->dep_grouped_exp_buffer, dep_channel_blocks * 128) || + !FF_ALLOC_TYPED_ARRAY(s->dep_psd_buffer, dep_total_coefs) || + !FF_ALLOC_TYPED_ARRAY(s->dep_band_psd_buffer, dep_channel_blocks * 64) || + !FF_ALLOC_TYPED_ARRAY(s->dep_mask_buffer, dep_channel_blocks * 64) || + !FF_ALLOC_TYPED_ARRAY(s->dep_qmant_buffer, dep_total_coefs)) + return AVERROR(ENOMEM); + + if (!s->fixed_point) { + if (!FF_ALLOCZ_TYPED_ARRAY(s->dep_fixed_coef_buffer, dep_total_coefs)) + return AVERROR(ENOMEM); + } + + /* Initialize dependent block pointers */ + for (blk = 0; blk < s->num_blocks; blk++) { + AC3Block *block = &s->dep_blocks[blk]; + + for (ch = 0; ch < dep_channels; ch++) { + /* arrangement: block, channel, coeff */ + block->grouped_exp[ch] = &s->dep_grouped_exp_buffer[128 * (blk * dep_channels + ch)]; + block->psd[ch] = &s->dep_psd_buffer [AC3_MAX_COEFS * (blk * dep_channels + ch)]; + block->band_psd[ch] = &s->dep_band_psd_buffer [64 * (blk * dep_channels + ch)]; + block->mask[ch] = &s->dep_mask_buffer [64 * (blk * dep_channels + ch)]; + block->qmant[ch] = &s->dep_qmant_buffer [AC3_MAX_COEFS * (blk * dep_channels + ch)]; + + /* arrangement: channel, block, coeff */ + block->exp[ch] = &s->dep_exp_buffer [AC3_MAX_COEFS * (s->num_blocks * ch + blk)]; + block->mdct_coef[ch] = &s->dep_mdct_coef_buffer [AC3_MAX_COEFS * (s->num_blocks * ch + blk)]; + if (s->fixed_point) + block->fixed_coef[ch] = (int32_t *)block->mdct_coef[ch]; + else + block->fixed_coef[ch] = &s->dep_fixed_coef_buffer[AC3_MAX_COEFS * (s->num_blocks * ch + blk)]; + + /* Set bandwidth for dependent channels - use same as main stream */ + block->end_freq[ch] = s->bandwidth_code * 3 + 73; + } + } + + /* Initialize SNR offsets for dependent stream */ + s->dep_coarse_snr_offset = 40; + for (ch = 0; ch < dep_channels; ch++) { + s->dep_fast_gain_code[ch] = 4; + s->dep_fine_snr_offset[ch] = 0; + } + } + return 0; } diff --git a/libavcodec/ac3enc.h b/libavcodec/ac3enc.h index 5e98ad188b..8c9cc9d0bd 100644 --- a/libavcodec/ac3enc.h +++ b/libavcodec/ac3enc.h @@ -266,9 +266,39 @@ typedef struct AC3EncodeContext { DECLARE_ALIGNED(32, float, windowed_samples_float)[AC3_WINDOW_SIZE]; DECLARE_ALIGNED(32, int32_t, windowed_samples_fixed)[AC3_WINDOW_SIZE]; }; + + /* E-AC-3 dependent substream support (7.1, 5.1.2, 5.1.4, 7.1.2, etc.) */ + int eac3_dependent_enabled; ///< dependent substream enabled + uint16_t dependent_channel_map; ///< 16-bit custom channel map for dependent + int dependent_channels; ///< number of channels in dependent stream + int dependent_frame_size; ///< frame size for dependent substream + + /* Channel mapping for dependent substream */ + int input_channels; ///< total channels from input + uint8_t channel_map_dep[16]; ///< input channel indices for dependent stream + + /* Separate buffers for dependent substream */ + uint8_t *dep_samples[16]; ///< sample buffers for dependent channels + CoefType *dep_mdct_coef_buffer; + int32_t *dep_fixed_coef_buffer; + uint8_t *dep_exp_buffer; + uint8_t *dep_grouped_exp_buffer; + uint8_t *dep_bap_buffer; + int16_t *dep_psd_buffer; + int16_t *dep_band_psd_buffer; + int16_t *dep_mask_buffer; + uint16_t *dep_qmant_buffer; + AC3Block dep_blocks[AC3_MAX_BLOCKS]; ///< blocks for dependent substream + uint8_t dep_exp_strategy[16][AC3_MAX_BLOCKS]; ///< exponent strategies for dependent + uint8_t dep_exp_ref_block[16][AC3_MAX_BLOCKS]; ///< reference blocks for EXP_REUSE + uint8_t *dep_ref_bap[16][AC3_MAX_BLOCKS]; ///< bit allocation pointers for dependent + int dep_coarse_snr_offset; ///< coarse SNR offset for dependent + int dep_fine_snr_offset[16]; ///< fine SNR offsets for dependent + int dep_fast_gain_code[16]; ///< fast gain codes for dependent } AC3EncodeContext; extern const AVChannelLayout ff_ac3_ch_layouts[19]; +extern const AVChannelLayout ff_eac3_ch_layouts[]; extern const AVOption ff_ac3_enc_options[]; extern const AVClass ff_ac3enc_class; extern const FFCodecDefault ff_ac3_enc_defaults[]; diff --git a/libavcodec/eac3enc.c b/libavcodec/eac3enc.c index 10b1ab337c..537109115c 100644 --- a/libavcodec/eac3enc.c +++ b/libavcodec/eac3enc.c @@ -27,6 +27,7 @@ #define AC3ENC_FLOAT 1 #include "libavutil/attributes.h" +#include "libavutil/channel_layout.h" #include "libavutil/thread.h" #include "ac3enc.h" #include "codec_internal.h" @@ -42,6 +43,63 @@ static const AVClass eac3enc_class = { .version = LIBAVUTIL_VERSION_INT, }; +/** + * List of supported channel layouts for E-AC-3. + * Includes extended layouts (7.1, 5.1.2, etc.) not supported by AC-3. + * These use the dependent substream mechanism for extra channels. + * Layouts with more than 5 dependent channels (e.g., 7.1.4) would require + * multiple dependent substreams and are not yet supported. + */ +const AVChannelLayout ff_eac3_ch_layouts[] = { + AV_CHANNEL_LAYOUT_MONO, + AV_CHANNEL_LAYOUT_STEREO, + AV_CHANNEL_LAYOUT_2_1, + AV_CHANNEL_LAYOUT_SURROUND, + AV_CHANNEL_LAYOUT_2_2, + AV_CHANNEL_LAYOUT_QUAD, + AV_CHANNEL_LAYOUT_4POINT0, + AV_CHANNEL_LAYOUT_5POINT0, + AV_CHANNEL_LAYOUT_5POINT0_BACK, + { + .nb_channels = 2, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_MONO | AV_CH_LOW_FREQUENCY, + }, + { + .nb_channels = 3, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_STEREO | AV_CH_LOW_FREQUENCY, + }, + { + .nb_channels = 4, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_2_1 | AV_CH_LOW_FREQUENCY, + }, + { + .nb_channels = 4, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_SURROUND | AV_CH_LOW_FREQUENCY, + }, + { + .nb_channels = 5, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_4POINT0 | AV_CH_LOW_FREQUENCY, + }, + AV_CHANNEL_LAYOUT_5POINT1, + AV_CHANNEL_LAYOUT_5POINT1_BACK, + /* E-AC-3 extended layouts using dependent substream */ + AV_CHANNEL_LAYOUT_7POINT1, /* 5.1 + Lb/Rb */ + AV_CHANNEL_LAYOUT_5POINT1POINT2_BACK,/* 5.1.2 - 5.1 + TBL/TBR */ + { /* 5.1.2 with top front instead of top back */ + .nb_channels = 8, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_5POINT1 | AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT, + }, + AV_CHANNEL_LAYOUT_5POINT1POINT4_BACK,/* 5.1.4 - 5.1 + TFL/TFR + TBL/TBR */ + AV_CHANNEL_LAYOUT_7POINT1POINT2, /* 7.1.2 - 7.1 + TFL/TFR */ + { 0 }, +}; + /** * LUT for finding a matching frame exponent strategy index from a set of * exponent strategies for a single channel across all 6 blocks. @@ -127,6 +185,106 @@ void ff_eac3_set_cpl_states(AC3EncodeContext *s) } } + +/** + * Get the acmod value for the dependent substream based on channel count. + * The dependent stream uses standard acmod values for its channel configuration. + */ +static int get_dep_acmod(int dep_channels) +{ + switch (dep_channels) { + case 1: return AC3_CHMODE_MONO; /* 1/0 */ + case 2: return AC3_CHMODE_STEREO; /* 2/0 */ + case 3: return AC3_CHMODE_3F; /* 3/0 */ + case 4: return AC3_CHMODE_2F2R; /* 2/2 */ + case 5: return AC3_CHMODE_3F2R; /* 3/2 */ + default: return AC3_CHMODE_STEREO; /* fallback to stereo */ + } +} + +/** + * Write the E-AC-3 dependent substream frame header to the output bitstream. + * Used for extended channel layouts (7.1, 5.1.2, 5.1.4, 7.1.2, etc.) where extra + * channels are encoded in a dependent substream. + */ +void ff_eac3_output_dep_frame_header(AC3EncodeContext *s, PutBitContext *pb) +{ + int blk, ch; + AC3EncOptions *opt = &s->options; + int dep_acmod = get_dep_acmod(s->dependent_channels); + int frmsiz = (s->dependent_frame_size / 2) - 1; + + put_bits_assume_flushed(pb); + + put_bits(pb, 16, 0x0b77); /* sync word */ + + /* BSI header for dependent substream */ + put_bits(pb, 2, 1); /* stream type = dependent */ + put_bits(pb, 3, 0); /* substream id = 0 */ + put_bits(pb, 11, frmsiz); /* frame size */ + put_bits(pb, 2, s->bit_alloc.sr_code); /* sample rate code */ + put_bits(pb, 2, s->num_blks_code); /* number of blocks */ + put_bits(pb, 3, dep_acmod); /* acmod based on dep channels */ + put_bits(pb, 1, 0); /* LFE off in dependent */ + put_bits(pb, 5, s->bitstream_id); /* bitstream id (16 for EAC3) */ + put_bits(pb, 5, -opt->dialogue_level); /* dialogue normalization */ + put_bits(pb, 1, 0); /* no compression gain */ + + /* Custom channel map - critical for dependent streams */ + put_bits(pb, 1, 1); /* channel map exists */ + put_bits(pb, 16, s->dependent_channel_map); /* 16-bit channel map */ + + /* No mixing metadata for dependent stream */ + put_bits(pb, 1, 0); + + /* No info metadata for dependent stream */ + put_bits(pb, 1, 0); + + /* Note: converter sync flag is NOT output for dependent frames. + * It's only used for independent frames with num_blocks != 6. */ + + put_bits(pb, 1, 0); /* no additional bit stream info */ + + /* Frame header */ + if (s->num_blocks == 6) { + put_bits(pb, 1, 1); /* use block exponent strategy */ + put_bits(pb, 1, 0); /* aht enabled = no */ + } + put_bits(pb, 2, 0); /* snr offset strategy = 1 */ + put_bits(pb, 1, 0); /* transient pre-noise processing enabled = no */ + put_bits(pb, 1, 0); /* block switch syntax enabled = no */ + put_bits(pb, 1, 0); /* dither flag syntax enabled = no */ + put_bits(pb, 1, 0); /* bit allocation model syntax enabled = no */ + put_bits(pb, 1, 0); /* fast gain codes enabled = no */ + put_bits(pb, 1, 0); /* dba syntax enabled = no */ + put_bits(pb, 1, 0); /* skip field syntax enabled = no */ + put_bits(pb, 1, 0); /* spx enabled = no */ + + /* Coupling strategy - no coupling for dependent stream */ + if (dep_acmod > AC3_CHMODE_MONO) { + put_bits(pb, 1, 0); /* blk 0: coupling not in use */ + for (blk = 1; blk < s->num_blocks; blk++) + put_bits(pb, 1, 0); /* blk N: no new coupling strategy */ + } + + /* Exponent strategy - block-based: EXP_D15 for blk0, EXP_REUSE for rest */ + for (blk = 0; blk < s->num_blocks; blk++) + for (ch = 0; ch < s->dependent_channels; ch++) + put_bits(pb, 2, s->dep_exp_strategy[ch][blk]); + + /* E-AC-3 to AC-3 converter exponent strategy - NOT used for dependent frames + * The decoder only reads this for independent frames (frame_type == 0). + * For dependent frames (frame_type == 1), skip this entirely. */ + + /* SNR offsets */ + put_bits(pb, 6, s->dep_coarse_snr_offset); + put_bits(pb, 4, s->dep_fine_snr_offset[0]); + + /* Block start info */ + if (s->num_blocks > 1) + put_bits(pb, 1, 0); +} + /** * Write the E-AC-3 frame header to the output bitstream. */ @@ -278,7 +436,7 @@ const FFCodec ff_eac3_encoder = { CODEC_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP), .p.priv_class = &eac3enc_class, CODEC_SAMPLERATES_ARRAY(ff_ac3_sample_rate_tab), - CODEC_CH_LAYOUTS_ARRAY(ff_ac3_ch_layouts), + CODEC_CH_LAYOUTS_ARRAY(ff_eac3_ch_layouts), .defaults = ff_ac3_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/eac3enc.h b/libavcodec/eac3enc.h index 0523de411b..490dd8b7c6 100644 --- a/libavcodec/eac3enc.h +++ b/libavcodec/eac3enc.h @@ -29,6 +29,8 @@ #include "ac3enc.h" +struct PutBitContext; + /** * Determine frame exponent strategy use and indices. */ @@ -41,4 +43,10 @@ void ff_eac3_get_frame_exp_strategy(AC3EncodeContext *s); */ void ff_eac3_set_cpl_states(AC3EncodeContext *s); +/** + * Write the E-AC-3 dependent substream frame header. + * Used for encodings where extra channels are in a dependent substream. + */ +void ff_eac3_output_dep_frame_header(AC3EncodeContext *s, struct PutBitContext *pb); + #endif /* AVCODEC_EAC3ENC_H */ diff --git a/tests/fate/ac3.mak b/tests/fate/ac3.mak index 9f655c3ff4..0b64fd5476 100644 --- a/tests/fate/ac3.mak +++ b/tests/fate/ac3.mak @@ -105,4 +105,29 @@ fate-eac3-core-bsf: REF = b704bf851e99b7442e9bed368b60e6ca FATE_SAMPLES_AVCONV += $(FATE_AC3-yes) $(FATE_EAC3-yes) -fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes) +# E-AC-3 dependent substream encoding tests. +# These test encoding multichannel layouts that use dependent substreams +# for channels beyond 5.1. Self-generated samples, no external SAMPLES needed. +FATE_EAC3_DEP_ENC-$(call FRAMECRC, WAV, PCM_S16LE, ARESAMPLE_FILTER EAC3_ENCODER) += fate-eac3-encode-7.1 +fate-eac3-encode-7.1: tests/data/asynth-44100-8.wav +fate-eac3-encode-7.1: SRC = $(TARGET_PATH)/tests/data/asynth-44100-8.wav +fate-eac3-encode-7.1: CMD = framecrc -i $(SRC) -c:a eac3 -b:a 640k -frames:a 6 -af aresample + +FATE_EAC3_DEP_ENC-$(call FRAMECRC, WAV, PCM_S16LE, ARESAMPLE_FILTER EAC3_ENCODER) += fate-eac3-encode-5.1.2 +fate-eac3-encode-5.1.2: tests/data/asynth-44100-8.wav +fate-eac3-encode-5.1.2: SRC = $(TARGET_PATH)/tests/data/asynth-44100-8.wav +fate-eac3-encode-5.1.2: CMD = framecrc -i $(SRC) -c:a eac3 -b:a 640k -frames:a 6 -af "aresample=ochl=5.1.2" + +FATE_EAC3_DEP_ENC-$(call FRAMECRC, WAV, PCM_S16LE, ARESAMPLE_FILTER EAC3_ENCODER) += fate-eac3-encode-5.1.4 +fate-eac3-encode-5.1.4: tests/data/asynth-44100-10.wav +fate-eac3-encode-5.1.4: SRC = $(TARGET_PATH)/tests/data/asynth-44100-10.wav +fate-eac3-encode-5.1.4: CMD = framecrc -i $(SRC) -c:a eac3 -b:a 640k -frames:a 6 -af "aresample=ochl=5.1.4" + +FATE_EAC3_DEP_ENC-$(call FRAMECRC, WAV, PCM_S16LE, ARESAMPLE_FILTER EAC3_ENCODER) += fate-eac3-encode-7.1.2 +fate-eac3-encode-7.1.2: tests/data/asynth-44100-10.wav +fate-eac3-encode-7.1.2: SRC = $(TARGET_PATH)/tests/data/asynth-44100-10.wav +fate-eac3-encode-7.1.2: CMD = framecrc -i $(SRC) -c:a eac3 -b:a 640k -frames:a 6 -af "aresample=ochl=7.1.2" + +FATE_AVCONV += $(FATE_EAC3_DEP_ENC-yes) + +fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes) $(FATE_EAC3_DEP_ENC-yes) diff --git a/tests/ref/fate/eac3-encode-5.1.2 b/tests/ref/fate/eac3-encode-5.1.2 new file mode 100644 index 0000000000..3e11bf9d82 --- /dev/null +++ b/tests/ref/fate/eac3-encode-5.1.2 @@ -0,0 +1,13 @@ +#tb 0: 1/44100 +#media_type 0: audio +#codec_id 0: eac3 +#sample_rate 0: 44100 +#channel_layout_name 0: 5.1.2 +0, -256, -256, 1536, 5758, 0x6d4d644b +0, 1280, 1280, 1536, 5760, 0x165f9d38 +0, 2816, 2816, 1536, 5758, 0x5164aecf +0, 4352, 4352, 1536, 5758, 0x5983c3e0 +0, 5888, 5888, 1536, 5758, 0x94a16fe0 +0, 7424, 7424, 1536, 5758, 0x67806895 +0, 8960, 8960, 1536, 5760, 0x9eb75cd6 +0, 10496, 10496, 1536, 5758, 0x5ac6a122 diff --git a/tests/ref/fate/eac3-encode-5.1.4 b/tests/ref/fate/eac3-encode-5.1.4 new file mode 100644 index 0000000000..b83f1d0332 --- /dev/null +++ b/tests/ref/fate/eac3-encode-5.1.4 @@ -0,0 +1,13 @@ +#tb 0: 1/44100 +#media_type 0: audio +#codec_id 0: eac3 +#sample_rate 0: 44100 +#channel_layout_name 0: 5.1.4 +0, -256, -256, 1536, 6626, 0xdefa60a6 +0, 1280, 1280, 1536, 6628, 0xf50f8daa +0, 2816, 2816, 1536, 6626, 0x4d50af63 +0, 4352, 4352, 1536, 6626, 0x1ff9a2c4 +0, 5888, 5888, 1536, 6626, 0x524a57d7 +0, 7424, 7424, 1536, 6626, 0xbb3c8e9a +0, 8960, 8960, 1536, 6628, 0x52f5588d +0, 10496, 10496, 1536, 6626, 0x99abae61 diff --git a/tests/ref/fate/eac3-encode-7.1 b/tests/ref/fate/eac3-encode-7.1 new file mode 100644 index 0000000000..6da16cbd25 --- /dev/null +++ b/tests/ref/fate/eac3-encode-7.1 @@ -0,0 +1,13 @@ +#tb 0: 1/44100 +#media_type 0: audio +#codec_id 0: eac3 +#sample_rate 0: 44100 +#channel_layout_name 0: 7.1 +0, -256, -256, 1536, 5758, 0x87ae4e00 +0, 1280, 1280, 1536, 5760, 0x11378061 +0, 2816, 2816, 1536, 5758, 0xb5fea1d0 +0, 4352, 4352, 1536, 5758, 0xaa44938c +0, 5888, 5888, 1536, 5758, 0x2f434897 +0, 7424, 7424, 1536, 5758, 0x9eaf81d5 +0, 8960, 8960, 1536, 5760, 0xd4644744 +0, 10496, 10496, 1536, 5758, 0x3e5e9a7d diff --git a/tests/ref/fate/eac3-encode-7.1.2 b/tests/ref/fate/eac3-encode-7.1.2 new file mode 100644 index 0000000000..7dfae38187 --- /dev/null +++ b/tests/ref/fate/eac3-encode-7.1.2 @@ -0,0 +1,13 @@ +#tb 0: 1/44100 +#media_type 0: audio +#codec_id 0: eac3 +#sample_rate 0: 44100 +#channel_layout_name 0: 7.1.2 +0, -256, -256, 1536, 6626, 0xd968dead +0, 1280, 1280, 1536, 6628, 0x6e301b19 +0, 2816, 2816, 1536, 6626, 0xd075ef71 +0, 4352, 4352, 1536, 6626, 0x90ed1ec3 +0, 5888, 5888, 1536, 6626, 0x175be815 +0, 7424, 7424, 1536, 6626, 0xe9b0e572 +0, 8960, 8960, 1536, 6628, 0x3911082a +0, 10496, 10496, 1536, 6626, 0x6bcc0375 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
