[FFmpeg-devel] [PATCH 1/4] libavcodec: Implementation of AC3 fixed point decoder.
Vitor Sessak
vitor1001 at gmail.com
Sun Sep 30 17:55:38 CEST 2012
Hi Nedeljko,
On 09/25/2012 04:10 PM, Nedeljko Babic wrote:
> AC3 fixed point decoder is based on AC3 floating point
> decoder that is already part of FFmpeg.
>
> It does not use FFmpegs FFT. It uses FFT developed for
> optimization of floating point AC3 decoder and because
> of that currently some of the files that implement this
> FFT are located in libavcodec/mips folder.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 9b86f7c..cf88e48 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -80,7 +80,8 @@ OBJS-$(CONFIG_AAC_ENCODER) += aacenc.o aaccoder.o \
> mpeg4audio.o kbdwin.o \
> audio_frame_queue.o
> OBJS-$(CONFIG_AASC_DECODER) += aasc.o msrledec.o
> -OBJS-$(CONFIG_AC3_DECODER) += ac3dec.o ac3dec_data.o ac3.o kbdwin.o
> +OBJS-$(CONFIG_AC3_DECODER) += ac3dec_float.o ac3dec_data.o ac3.o kbdwin.o
> +OBJS-$(CONFIG_AC3_FIXED_DECODER) += fft_ac3_init_tab.o fft_ac3_fixed.o ac3dec_fixed.o ac3dec_data.o ac3.o kbdwin.o
> OBJS-$(CONFIG_AC3_ENCODER) += ac3enc_float.o ac3enc.o ac3tab.o \
> ac3.o kbdwin.o
> OBJS-$(CONFIG_AC3_FIXED_ENCODER) += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o
> diff --git a/libavcodec/ac3.h b/libavcodec/ac3.h
> index b9f34b9..0a1ff23 100644
> --- a/libavcodec/ac3.h
> +++ b/libavcodec/ac3.h
> @@ -27,6 +27,10 @@
> #ifndef AVCODEC_AC3_H
> #define AVCODEC_AC3_H
>
> +#ifndef CONFIG_AC3_FIXED
> +# define CONFIG_AC3_FIXED 0
> +#endif
I don't think this is needed.
> #define AC3_MAX_CODED_FRAME_SIZE 3840 /* in bytes */
> #define AC3_MAX_CHANNELS 7 /**< maximum number of channels, including coupling channel */
> #define CPL_CH 0 /**< coupling channel index */
> @@ -51,6 +55,41 @@
> #define EXP_D25 2
> #define EXP_D45 3
>
> +#if CONFIG_AC3_FIXED
> +
> +#define CONFIG_FFT_FLOAT 0
> +
> +/* pre-defined gain values */
> +#define LEVEL_PLUS_3DB 5793
> +#define LEVEL_PLUS_1POINT5DB 4871
> +#define LEVEL_MINUS_1POINT5DB 3444
> +#define LEVEL_MINUS_3DB 2896
> +#define LEVEL_MINUS_4POINT5DB 2435
> +#define LEVEL_MINUS_6DB 2048
> +#define LEVEL_MINUS_9DB 1448
> +#define LEVEL_ZERO 0
> +#define LEVEL_ONE 4096
> +
> +#define MUL_BIAS1 65536
> +#define MUL_BIAS2 2147418112
If this is just the floating-point values converted to fixed, it is
better to use a macro to define it only once. See FIXR() macro in
mpegaudiodec.c.
> +#define AC3_RENAME(x) x ## _fixed
> +#define AC3_CENTER(x) center_levels[x]
> +#define AC3_SURROUND(x) surround_levels[x]
Those two looks unused.
> +#define AC3_LEVEL(x) ((x)*23170 + 0x4000) >> 15
> +#define AC3_NORM(x,norm) ((x)<<12)/norm
> +#define AC3_DYNAMIC_RANGE(x) (x)
This looks very different from the float version, how can it work?
> +#define AC3_SPX_BLEND(x) (x)
> +#define TYPE_PREFIX(x) fixed_ ## x
Why not use sulfix everywhere, for simplicity and consistency?
> +#define AC3_DYNAMIC_RANGE1 0
Hmm, that is strange.
> +#define INTFLOAT int
> +#define SHORTFLOAT int16_t
> +
> +#define ROUND12(x) ((x)+2048)>>12
> +
> +#else
> +
> /* pre-defined gain values */
> #define LEVEL_PLUS_3DB 1.4142135623730950
> #define LEVEL_PLUS_1POINT5DB 1.1892071150027209
> @@ -62,6 +101,26 @@
> #define LEVEL_ZERO 0.0000000000000000
> #define LEVEL_ONE 1.0000000000000000
>
> +#define MUL_BIAS1 1.0f
> +#define MUL_BIAS2 32767.0f
> +
> +#define AC3_RENAME(x) x
> +#define AC3_CENTER(x) (x)
> +#define AC3_SURROUND(x) (x)
> +#define AC3_LEVEL(x) (x)*LEVEL_MINUS_3DB
> +#define AC3_NORM(x,norm) (x)*(1.0f/norm)
> +#define AC3_DYNAMIC_RANGE(x) ((dynamic_range_tab[x] - 1.0) * s->drc_scale) + 1.0
> +#define AC3_SPX_BLEND(x) (x)* (1.0f/32)
> +#define TYPE_PREFIX(x) float_ ## x
> +
> +#define AC3_DYNAMIC_RANGE1 1.0f
> +#define INTFLOAT float
> +#define SHORTFLOAT float
> +
> +#define ROUND12(x) (x)
> +
> +#endif /* CONFIG_AC3_FIXED */
> +
> /** Delta bit allocation strategy */
> typedef enum {
> DBA_REUSE = 0,
> diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c
> index c608de8..5d82e23 100644
> --- a/libavcodec/ac3dec.c
> +++ b/libavcodec/ac3dec.c
> @@ -64,7 +64,7 @@ static const uint8_t quantization_tab[16] = {
> static float dynamic_range_tab[256];
>
> /** Adjustments in dB gain */
> -static const float gain_levels[9] = {
> +static const INTFLOAT AC3_RENAME(gain_levels)[9] = {
> LEVEL_PLUS_3DB,
> LEVEL_PLUS_1POINT5DB,
> LEVEL_ONE,
> @@ -157,27 +157,32 @@ static av_cold void ac3_tables_init(void)
> /**
> * AVCodec initialization
> */
> -static av_cold int ac3_decode_init(AVCodecContext *avctx)
> +static av_cold int AC3_RENAME(ac3_decode_init)(AVCodecContext *avctx)
> {
> AC3DecodeContext *s = avctx->priv_data;
> s->avctx = avctx;
>
> ff_ac3_common_init();
> ac3_tables_init();
> - ff_mdct_init(&s->imdct_256, 8, 1, 1.0);
> - ff_mdct_init(&s->imdct_512, 9, 1, 1.0);
> - ff_kbd_window_init(s->window, 5.0, 256);
> + AC3_RENAME(ff_mdct_init)(&s->imdct_256, 8, 1, 1.0);
> + AC3_RENAME(ff_mdct_init)(&s->imdct_512, 9, 1, 1.0);\
Why the backslash?
> + AC3_RENAME(ff_kbd_window_init)(s->window, 5.0, 256);
> ff_dsputil_init(&s->dsp, avctx);
> ff_ac3dsp_init(&s->ac3dsp, avctx->flags & CODEC_FLAG_BITEXACT);
> ff_fmt_convert_init(&s->fmt_conv, avctx);
> av_lfg_init(&s->dith_state, 0);
>
> +#if CONFIG_AC3_FIXED
> + ff_ac3_fft_init_fixed(&s->imdct_256);
> + ff_ac3_fft_init_fixed(&s->imdct_512);
> +#endif
> +
> /* set scale value for float to int16 conversion */
> if (avctx->request_sample_fmt == AV_SAMPLE_FMT_FLT) {
> - s->mul_bias = 1.0f;
> + s->mul_bias = MUL_BIAS1;
> avctx->sample_fmt = AV_SAMPLE_FMT_FLT;
> } else {
> - s->mul_bias = 32767.0f;
> + s->mul_bias = MUL_BIAS2;
> avctx->sample_fmt = AV_SAMPLE_FMT_S16;
> }
>
> @@ -300,23 +305,23 @@ static int parse_frame_header(AC3DecodeContext *s)
> * Set stereo downmixing coefficients based on frame header info.
> * reference: Section 7.8.2 Downmixing Into Two Channels
> */
> -static void set_downmix_coeffs(AC3DecodeContext *s)
> +static void AC3_RENAME(set_downmix_coeffs)(AC3DecodeContext *s)
> {
> int i;
> - float cmix = gain_levels[s-> center_mix_level];
> - float smix = gain_levels[s->surround_mix_level];
> - float norm0, norm1;
> + INTFLOAT cmix = AC3_RENAME(gain_levels)[s-> center_mix_level];
> + INTFLOAT smix = AC3_RENAME(gain_levels)[s->surround_mix_level];
> + INTFLOAT norm0, norm1;
>
> for (i = 0; i < s->fbw_channels; i++) {
> - s->downmix_coeffs[i][0] = gain_levels[ac3_default_coeffs[s->channel_mode][i][0]];
> - s->downmix_coeffs[i][1] = gain_levels[ac3_default_coeffs[s->channel_mode][i][1]];
> + s->downmix_coeffs[i][0] = AC3_RENAME(gain_levels)[ac3_default_coeffs[s->channel_mode][i][0]];
> + s->downmix_coeffs[i][1] = AC3_RENAME(gain_levels)[ac3_default_coeffs[s->channel_mode][i][1]];
> }
> if (s->channel_mode > 1 && s->channel_mode & 1) {
> s->downmix_coeffs[1][0] = s->downmix_coeffs[1][1] = cmix;
> }
> if (s->channel_mode == AC3_CHMODE_2F1R || s->channel_mode == AC3_CHMODE_3F1R) {
> int nf = s->channel_mode - 2;
> - s->downmix_coeffs[nf][0] = s->downmix_coeffs[nf][1] = smix * LEVEL_MINUS_3DB;
> + s->downmix_coeffs[nf][0] = s->downmix_coeffs[nf][1] = AC3_LEVEL(smix);
> }
> if (s->channel_mode == AC3_CHMODE_2F2R || s->channel_mode == AC3_CHMODE_3F2R) {
> int nf = s->channel_mode - 4;
> @@ -324,22 +329,20 @@ static void set_downmix_coeffs(AC3DecodeContext *s)
> }
>
> /* renormalize */
> - norm0 = norm1 = 0.0;
> + norm0 = norm1 = (INTFLOAT)0.0;
norm0 = norm1 = FIXR(0.0);
so this construct can also work for other constants.
> for (i = 0; i < s->fbw_channels; i++) {
> norm0 += s->downmix_coeffs[i][0];
> norm1 += s->downmix_coeffs[i][1];
> }
> - norm0 = 1.0f / norm0;
> - norm1 = 1.0f / norm1;
> +
> for (i = 0; i < s->fbw_channels; i++) {
> - s->downmix_coeffs[i][0] *= norm0;
> - s->downmix_coeffs[i][1] *= norm1;
> + s->downmix_coeffs[i][0] = AC3_NORM(s->downmix_coeffs[i][0],norm0);
> + s->downmix_coeffs[i][1] = AC3_NORM(s->downmix_coeffs[i][1],norm1);
> }
Division is much slower than multiplication. You can do the same trick
in fixed point:
norm0 = (1 << bits) / norm0;
s->downmix_coeffs[i][0] = MUL(s->downmix_coeffs[i][0],norm0);
where
#define MUL(a,b) (((int64_t) (a)) * (b)) >> (s))
> if (s->output_mode == AC3_CHMODE_MONO) {
> for (i = 0; i < s->fbw_channels; i++)
> - s->downmix_coeffs[i][0] = (s->downmix_coeffs[i][0] +
> - s->downmix_coeffs[i][1]) * LEVEL_MINUS_3DB;
> + s->downmix_coeffs[i][0] = AC3_LEVEL(s->downmix_coeffs[i][0] + s->downmix_coeffs[i][1]);
> }
> }
Hmm, is this correct?
> @@ -602,51 +605,25 @@ static inline void do_imdct(AC3DecodeContext *s, int channels)
> for (ch = 1; ch <= channels; ch++) {
> if (s->block_switch[ch]) {
> int i;
> - float *x = s->tmp_output + 128;
> + FFTSample *x = s->tmp_output+128;
> for (i = 0; i < 128; i++)
> x[i] = s->transform_coeffs[ch][2 * i];
> - s->imdct_256.imdct_half(&s->imdct_256, s->tmp_output, x);
> - s->dsp.vector_fmul_window(s->output[ch - 1], s->delay[ch - 1],
> + s->imdct_256.AC3_RENAME(imdct_half)(&s->imdct_256, s->tmp_output, x);
> + s->dsp.AC3_RENAME(vector_fmul_window)(s->output[ch - 1], s->delay[ch - 1],
> s->tmp_output, s->window, 128);
> for (i = 0; i < 128; i++)
> x[i] = s->transform_coeffs[ch][2 * i + 1];
> - s->imdct_256.imdct_half(&s->imdct_256, s->delay[ch - 1], x);
> + s->imdct_256.AC3_RENAME(imdct_half)(&s->imdct_256, s->delay[ch - 1], x);
> } else {
> - s->imdct_512.imdct_half(&s->imdct_512, s->tmp_output, s->transform_coeffs[ch]);
> - s->dsp.vector_fmul_window(s->output[ch - 1], s->delay[ch - 1],
> + s->imdct_512.AC3_RENAME(imdct_half)(&s->imdct_512, s->tmp_output, s->transform_coeffs[ch]);
> + s->dsp.AC3_RENAME(vector_fmul_window)(s->output[ch - 1], s->delay[ch - 1],
> s->tmp_output, s->window, 128);
> - memcpy(s->delay[ch - 1], s->tmp_output + 128, 128 * sizeof(float));
> + memcpy(s->delay[ch - 1], s->tmp_output + 128, 128 * sizeof(FFTSample));
> }
> }
> }
>
> -/**
> - * Upmix delay samples from stereo to original channel layout.
> - */
> -static void ac3_upmix_delay(AC3DecodeContext *s)
> -{
> - int channel_data_size = sizeof(s->delay[0]);
> - switch (s->channel_mode) {
> - case AC3_CHMODE_DUALMONO:
> - case AC3_CHMODE_STEREO:
> - /* upmix mono to stereo */
> - memcpy(s->delay[1], s->delay[0], channel_data_size);
> - break;
> - case AC3_CHMODE_2F2R:
> - memset(s->delay[3], 0, channel_data_size);
> - case AC3_CHMODE_2F1R:
> - memset(s->delay[2], 0, channel_data_size);
> - break;
> - case AC3_CHMODE_3F2R:
> - memset(s->delay[4], 0, channel_data_size);
> - case AC3_CHMODE_3F1R:
> - memset(s->delay[3], 0, channel_data_size);
> - case AC3_CHMODE_3F:
> - memcpy(s->delay[2], s->delay[1], channel_data_size);
> - memset(s->delay[1], 0, channel_data_size);
> - break;
> - }
> -}
> +
??
> /**
> * Decode band structure for coupling, spectral extension, or enhanced coupling.
> @@ -748,10 +725,9 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
> i = !s->channel_mode;
> do {
> if (get_bits1(gbc)) {
> - s->dynamic_range[i] = ((dynamic_range_tab[get_bits(gbc, 8)] - 1.0) *
> - s->drc_scale) + 1.0;
> + s->dynamic_range[i] = AC3_DYNAMIC_RANGE(get_bits(gbc, 8));
> } else if (blk == 0) {
> - s->dynamic_range[i] = 1.0f;
> + s->dynamic_range[i] = AC3_DYNAMIC_RANGE1;
> }
> } while (i--);
>
> @@ -777,6 +753,10 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
> if (start_subband > 7)
> start_subband += start_subband - 7;
> end_subband = get_bits(gbc, 3) + 5;
> +#if CONFIG_AC3_FIXED
> + s->spx_dst_end_freq = end_freq_inv_tab[end_subband];
> + end_subband += 5;
> +#endif
> if (end_subband > 7)
> end_subband += end_subband - 7;
> dst_start_freq = dst_start_freq * 12 + 25;
> @@ -797,7 +777,9 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
>
> s->spx_dst_start_freq = dst_start_freq;
> s->spx_src_start_freq = src_start_freq;
> +#if !CONFIG_AC3_FIXED
> s->spx_dst_end_freq = dst_end_freq;
> +#endif
>
> decode_band_structure(gbc, blk, s->eac3, 0,
> start_subband, end_subband,
> @@ -817,18 +799,45 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
> for (ch = 1; ch <= fbw_channels; ch++) {
> if (s->channel_uses_spx[ch]) {
> if (s->first_spx_coords[ch] || get_bits1(gbc)) {
> - float spx_blend;
> + INTFLOAT spx_blend;
> int bin, master_spx_coord;
>
> s->first_spx_coords[ch] = 0;
> - spx_blend = get_bits(gbc, 5) * (1.0f/32);
> + spx_blend = AC3_SPX_BLEND(get_bits(gbc, 5));
> master_spx_coord = get_bits(gbc, 2) * 3;
>
> bin = s->spx_src_start_freq;
> for (bnd = 0; bnd < s->num_spx_bands; bnd++) {
> int bandsize;
> int spx_coord_exp, spx_coord_mant;
> - float nratio, sblend, nblend, spx_coord;
> + INTFLOAT nratio, sblend, nblend;
> +#if CONFIG_AC3_FIXED
> + int64_t accu;
> + /* calculate blending factors */
> + bandsize = s->spx_band_sizes[bnd];
> + accu = (long long)((bin << 23) + (bandsize << 22)) * s->spx_dst_end_freq;
> + nratio = (int)(accu >> 32);
> + nratio -= spx_blend << 18;
> +
> + if (nratio < 0)
> + {
> + nblend = 0;
> + sblend = 0x800000;
> + }
> + else if (nratio > 0x7fffff)
> + {
> + nblend = 0x800000;
> + sblend = 0;
> + }
> + else
> + {
> + nblend = ac3_fixed_sqrt(nratio);
> + accu = (long long)nblend * 1859775393;
> + nblend = (int)((accu + (1<<29)) >> 30);
> + sblend = ac3_fixed_sqrt(0x800000 - nratio);
> + }
> +#else
Indentation:
if (...) {
} else if (...) {
}
-Vitor
More information about the ffmpeg-devel
mailing list