[FFmpeg-devel] [PATCH] Add ATRAC3+ decoder
Maxim Polijakowski
max_pole at gmx.de
Sat Oct 19 00:39:18 CEST 2013
Thank you for your suggestions!
Please find an updated patch in my next email:
[PATCH] ATRAC3+ decoder, 2nd try
Below a couple of comments...
>> +/**
>> + * Generate canonical VLC table from given descriptor.
>> + *
>> + * @param[in] cb ptr to codebook descriptor
>> + * @param[in] xlat ptr to translation table or NULL
>> + * @param[out] out_vlc ptr to vlc table to be generated
>> + */
>> +static av_cold void build_canonical_huff(const uint8_t *cb, const uint8_t *xlat,
>> + VLC *out_vlc)
>> +{
>> + int i, b;
>> + uint16_t codes[256];
>> + uint8_t bits[256];
>> + unsigned code = 0;
>> + int index = 0;
>> + int min_len = *cb++; // get shortest codeword length
>> + int max_len = *cb++; // get longest codeword length
>> +
>> + for (b = min_len; b <= max_len; b++) {
>> + for (i = *cb++; i > 0; i--) {
>> + bits[index] = b;
>> + codes[index] = code++;
>> + index++;
> index should, for saftey be checked to be within the array.
> Its not a bug as this function is never feeded with untrusted data
> but it feels safer to me to check or assert() that index doesnt
> become too large
Added an av_assert0() - based check.
>> + }
>> + code <<= 1;
>> + }
>> +
>> + ff_init_vlc_sparse(out_vlc, max_len, index, bits, 1, 1, codes, 2, 2,
>> + xlat, 1, 1, 0);
>> +}
> build_canonical_huff() could be done to all tables and the resulting
> tables be put in the source. This would avoid the need for
> build_canonical_huff() and simplify the code
> also it would allow the tables to be shared between instances of a
> loaded lib as they would be static const and not generated at runtime.
>
> making that change would likely increase the size of static const
> tables by <50% though.
> If you prefer to leave it as it is to avoid these larger tables
> thats fine too
At the time being, I would prefer to leave the tables as is. Expanding
these VLC tables is a long-term task and could be done later once the
decoder has been merged.
> [...]
>> +static int get_num_ct_values(GetBitContext *gb, Atrac3pChanUnitCtx *ctx,
>> + AVCodecContext *avctx)
>> +{
>> + int num_coded_vals;
>> +
>> + if (get_bits1(gb)) {
>> + num_coded_vals = get_bits(gb, 5);
>> + if (num_coded_vals > ctx->used_quant_units) {
>> + av_log(avctx, AV_LOG_ERROR,
>> + "Invalid number of code table indexes!\n");
> this could print the invalid value too in the errror message
Done.
> [...]
>
>> + for (i = dst[sb].num_wavs - 1, first = 1; i >= 0; i--, first = 0) {
>> + if (first)
>> + iwav[i].freq_index = get_bits(gb, 10);
>> + else {
>> + nbits = av_log2(iwav[i + 1].freq_index) + 1;
>> + iwav[i].freq_index = get_bits(gb, nbits);
>> + }
>> + }
> handling the "first" case outside the loop would avoid a check per
> loop iteration
Moved out of the loop...
> [...]
>> +static void decode_tones_phase(GetBitContext *gb, Atrac3pChanUnitCtx *ctx,
>> + int ch_num, int band_has_tones[])
>> +{
>> + int sb, i;
>> + Atrac3pWavesData *dst = ctx->channels[ch_num].tones_info;
>> +
>> + for (sb = 0; sb < ctx->waves_info->num_tone_bands; sb++) {
>> + if (!band_has_tones[sb])
>> + continue;
>> + for (i = 0; i < dst[sb].num_wavs; i++)
>> + ctx->waves_info->waves[dst[sb].start_index + i].phase_index = get_bits(gb, 5);
> that looks a bit complex for an inner loop
> something like
> phase_indexes = &ctx->waves_info->waves[dst[sb].start_index];
> for(...)
> phase_indexes[i].phase_index = get_bits(gb, 5);
>
> might be faster, same probably applies to other loops as well if they
> are speed relevant
This loop body has been simplified.
> [...]
>> +/** 3D base shape tables. The values are grouped together as follows:
>> + * [num_start_values = 8][num_shape_tables = 16][num_seg_coeffs = 9]
>> + * For each of the 8 start values there are 16 different shapes each
>> + * 9 coefficients long. */
>> +const int8_t ff_atrac3p_wl_shapes[8][16][9] = {
> some of the tables are only used from one file, these tables could
> be static, reducing the global namespace polution and might speed up
> linking in some cases on some platforms by a tiny amount
Most of these tables are only used from one file. Should I convert all
of them to static?
Should I remove the prefix "ff_atrac3p_" as well?
> [...]
>> +
>> +typedef struct ATRAC3PContext {
>> + GetBitContext gb;
>> +
>> + DECLARE_ALIGNED(32, float, samples)[2][ATRAC3P_FRAME_SAMPLES]; ///< quantized MDCT spectrum
>> + DECLARE_ALIGNED(32, float, mdct_buf)[2][ATRAC3P_FRAME_SAMPLES]; ///< output of the IMDCT
>> + DECLARE_ALIGNED(32, float, time_buf)[2][ATRAC3P_FRAME_SAMPLES]; ///< output of the gain compensation
>> + DECLARE_ALIGNED(32, float, outp_buf)[2][ATRAC3P_FRAME_SAMPLES];
>> +
>> + AtracGCContext gainc_ctx; ///< gain compensation context
>> + FFTContext mdct_ctx;
>> + FFTContext ipqf_dct_ctx; ///< IDCT context used by IPQF
>> +
>> + Atrac3pChanUnitCtx *ch_units; ///< global channel units
>> +
>> + int num_channel_blocks; ///< number of channel blocks
>> + uint8_t channel_blocks[5]; ///< channel configuration descriptor
>> + uint64_t my_channel_layout; ///< current channel layout
>> +
>> + ATRAC3PVlcTabs glob_vlcs; ///< global VLC tables
> if they are global, they should probably be static and not in a struct
> unless iam missing something
All VLC tables have been made static. The above-mentioned stuff has been
removed from the context.
>> + switch (avctx->channel_layout) {
>> + case AV_CH_LAYOUT_MONO:
>> + ctx->num_channel_blocks = 1;
>> + ctx->channel_blocks[0] = CH_UNIT_MONO;
>> + break;
>> + case AV_CH_LAYOUT_STEREO:
>> + ctx->num_channel_blocks = 1;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + break;
>> + case AV_CH_LAYOUT_SURROUND:
>> + ctx->num_channel_blocks = 2;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[1] = CH_UNIT_MONO;
>> + break;
>> + case AV_CH_LAYOUT_4POINT0:
>> + ctx->num_channel_blocks = 3;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[1] = CH_UNIT_MONO;
>> + ctx->channel_blocks[2] = CH_UNIT_MONO;
>> + break;
>> + case AV_CH_LAYOUT_5POINT1_BACK:
>> + ctx->num_channel_blocks = 4;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[1] = CH_UNIT_MONO;
>> + ctx->channel_blocks[2] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[3] = CH_UNIT_MONO;
>> + break;
>> + case AV_CH_LAYOUT_6POINT1_BACK:
>> + ctx->num_channel_blocks = 5;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[1] = CH_UNIT_MONO;
>> + ctx->channel_blocks[2] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[3] = CH_UNIT_MONO;
>> + ctx->channel_blocks[4] = CH_UNIT_MONO;
>> + break;
>> + case AV_CH_LAYOUT_7POINT1:
>> + ctx->num_channel_blocks = 5;
>> + ctx->channel_blocks[0] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[1] = CH_UNIT_MONO;
>> + ctx->channel_blocks[2] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[3] = CH_UNIT_STEREO;
>> + ctx->channel_blocks[4] = CH_UNIT_MONO;
>> + break;
> could be simplified
> for example with something like this, if you like
>
> ctx->channel_blocks[0] = CH_UNIT_MONO;
> if (channels > 1)
> ctx->channel_blocks[0] = CH_UNIT_STEREO;
> if (channels > 2)
> ctx->channel_blocks[1] = CH_UNIT_MONO;
> if (channels > 3)
> ctx->channel_blocks[2] = CH_UNIT_MONO;
> if (channels > 4) {
> ctx->channel_blocks[2] = CH_UNIT_STEREO;
> ctx->channel_blocks[3] = CH_UNIT_MONO;
> }
> if (channels > 6)
> ctx->channel_blocks[4] = CH_UNIT_MONO;
> if (channels > 7)
> ctx->channel_blocks[3] = CH_UNIT_STEREO;
It would just barely simplify the stuff because I need to estimate
number of channel blocks as well.
Moreover, it makes the code harder to read IMHO. Therefore, I'd prefer
to leave it as is...
>> + ctx->ch_units = av_mallocz(sizeof(*ctx->ch_units) *
>> + ctx->num_channel_blocks);
> looks like a memleak
Good catch! Fixed...
> [...]
>> + avpriv_float_dsp_init(&atrac3p_dsp, avctx->flags & CODEC_FLAG_BITEXACT);
> this initializes a global variable with a non global flag as input
> making the global potentially non constant
Fixed.
> [...]
>> +/* lookup table for fast modulo 23 op required for cyclic buffers of the IPQF */
>> +static int mod23_lut[26] = {
>> + 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
>> + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 0
>> +};
> should be static const
Fixed.
>> +
>> +/* First half of the 384-tap IPQF filtering coefficients. */
>> +static float ipqf_coeffs1[ATRAC3P_PQF_FIR_LEN][16] = {
> should be static const
Fixed.
> [...]
>
>> +/**
>> + * Subband synthesis filter based on the polyphase quadrature (pseudo-QMF)
>> + * filter bank.
>> + *
>> + * @param[in] dct_ctx ptr to the pre-initialized IDCT context
>> + * @param[in,out] hist ptr to the filter history
>> + * @param[in] in input data to process
>> + * @param[out] out receives processed data
>> + */
>> +void ff_atrac3p_ipqf(FFTContext *dct_ctx, Atrac3pIPQFChannelCtx *hist,
>> + float *in, float *out)
> in could be const
Done.
> you also might want to add yourself to MAINTAINERS
Ok, postponed to the next round...
More information about the ffmpeg-devel
mailing list