[FFmpeg-devel] [PATCH] QCELP decoder
Michael Niedermayer
michaelni
Thu Oct 16 00:09:26 CEST 2008
cOn Tue, Oct 14, 2008 at 06:59:34PM -0700, Kenan Gillet wrote:
> Thank you for all your feedback.
>
> Here is a summary of the round 4 patch:
> - add documentation
> - grammar / spelling fixes
> - cosmetics
> - split lsp2lpc code into its own file qcelp_lsp.c
> - small optimizations
> - bug fixes
>
> For info, postfilter and final gain control are still missing.
[...]
> Index: libavcodec/qcelp.h
> ===================================================================
> --- libavcodec/qcelp.h (revision 0)
> +++ libavcodec/qcelp.h (revision 0)
[...]
> +#ifndef AVCODEC_QCELP_H
> +#define AVCODEC_QCELP_H
> +
> +/**
> + * @file qcelpdata.h
> + * QCELP decoder
hmm
> + * @author Reynaldo H. Verdejo Pinochet
> + */
> +
> +typedef enum
> +{
> + SILENCE = 0,
> + RATE_OCTAVE,
> + RATE_QUARTER,
> + RATE_HALF,
> + RATE_FULL,
> + I_F_Q, /*!< insufficient frame quality */
> + RATE_UNKNOWN
> +} qcelp_packet_rate;
> +
> +/**
> + * Reconstructs LPC coefficients from the line spectral pair frequencies.
> + *
> + * TIA/EIA/IS-733 2.4.3.3.5
> + */
> +void qcelp_lspf2lpc(const float *lspf, float *lpc);
where is the file that contains this? it seems not in the patch ...
[...]
> +static const QCELPBitmap qcelp_rate_full_bitmap[] = {
> + {62,2},{62,1},{62,0},{61,6},{61,5},{61,4},{61,3},{61,2},
> + {61,1},{61,0},{60,5},{60,4},{60,3},{60,2},{60,1},{60,0},
> + {64,5},{64,4},{64,3},{64,2},{64,1},{64,0},{63,5},{63,4},
> + {63,3},{63,2},{63,1},{63,0},{62,6},{62,5},{62,4},{62,3},
> + { 0,0},{16,3},{16,2},{16,1},{16,0},{52,0},{48,6},{48,5},
> + {48,4},{48,3},{48,2},{48,1},{48,0},{56,2},{56,1},{56,0},
> + {33,3},{33,2},{33,1},{33,0},{ 1,0},{17,3},{17,2},{17,1},
> + {17,0},{32,6},{32,5},{32,4},{32,3},{32,2},{32,1},{32,0},
> + {19,0},{34,6},{34,5},{34,4},{34,3},{34,2},{34,1},{34,0},
> + { 2,0},{18,3},{18,2},{18,1},{18,0},{33,6},{33,5},{33,4},
> + {49,2},{49,1},{49,0},{57,2},{57,1},{57,0},{35,6},{35,5},
> + {35,4},{35,3},{35,2},{35,1},{35,0},{ 3,0},{19,2},{19,1},
> + {36,5},{36,4},{36,3},{36,2},{36,1},{36,0},{ 4,0},{20,3},
> + {20,2},{20,1},{20,0},{53,0},{49,6},{49,5},{49,4},{49,3},
> + {22,2},{22,1},{22,0},{37,6},{37,5},{37,4},{37,3},{37,2},
> + {37,1},{37,0},{ 5,0},{21,3},{21,2},{21,1},{21,0},{36,6},
> + {39,2},{39,1},{39,0},{ 7,0},{23,2},{23,1},{23,0},{38,6},
> + {38,5},{38,4},{38,3},{38,2},{38,1},{38,0},{ 6,0},{22,3},
> + {24,0},{54,0},{50,6},{50,5},{50,4},{50,3},{50,2},{50,1},
> + {50,0},{58,2},{58,1},{58,0},{39,6},{39,5},{39,4},{39,3},
> + { 9,0},{25,3},{25,2},{25,1},{25,0},{40,6},{40,5},{40,4},
> + {40,3},{40,2},{40,1},{40,0},{ 8,0},{24,3},{24,2},{24,1},
> + {42,3},{42,2},{42,1},{42,0},{10,0},{26,3},{26,2},{26,1},
> + {26,0},{41,6},{41,5},{41,4},{41,3},{41,2},{41,1},{41,0},
> + {59,1},{59,0},{43,6},{43,5},{43,4},{43,3},{43,2},{43,1},
> + {43,0},{11,0},{27,2},{27,1},{27,0},{42,6},{42,5},{42,4},
> + {44,1},{44,0},{12,0},{28,3},{28,2},{28,1},{28,0},{55,0},
> + {51,6},{51,5},{51,4},{51,3},{51,2},{51,1},{51,0},{59,2},
> + {45,5},{45,4},{45,3},{45,2},{45,1},{45,0},{13,0},{29,3},
> + {29,2},{29,1},{29,0},{44,6},{44,5},{44,4},{44,3},{44,2},
> + {31,2},{31,1},{31,0},{46,6},{46,5},{46,4},{46,3},{46,2},
> + {46,1},{46,0},{14,0},{30,3},{30,2},{30,1},{30,0},{45,6},
> + {70,1},{70,0},{47,6},{47,5},{47,4},{47,3},{47,2},{47,1},
> + {47,0},{15,0}
> +};
> +
> +static const QCELPBitmap qcelp_rate_half_bitmap[] = {
> + {62,2},{62,1},{62,0},{61,6},{61,5},{61,4},{61,3},{61,2},
> + {61,1},{61,0},{60,5},{60,4},{60,3},{60,2},{60,1},{60,0},
> + {64,5},{64,4},{64,3},{64,2},{64,1},{64,0},{63,5},{63,4},
> + {63,3},{63,2},{63,1},{63,0},{62,6},{62,5},{62,4},{62,3},
> + { 0,0},{16,3},{16,2},{16,1},{16,0},{52,0},{48,6},{48,5},
> + {48,4},{48,3},{48,2},{48,1},{48,0},{56,2},{56,1},{56,0},
> + {49,5},{49,4},{49,3},{49,2},{49,1},{49,0},{57,2},{57,1},
> + {57,0},{32,6},{32,5},{32,4},{32,3},{32,2},{32,1},{32,0},
> + {58,1},{58,0},{33,6},{33,5},{33,4},{33,3},{33,2},{33,1},
> + {33,0},{ 1,0},{17,3},{17,2},{17,1},{17,0},{53,0},{49,6},
> + {34,1},{34,0},{ 2,0},{18,3},{18,2},{18,1},{18,0},{54,0},
> + {50,6},{50,5},{50,4},{50,3},{50,2},{50,1},{50,0},{58,2},
> + {55,0},{51,6},{51,5},{51,4},{51,3},{51,2},{51,1},{51,0},
> + {59,2},{59,1},{59,0},{34,6},{34,5},{34,4},{34,3},{34,2},
> + {35,6},{35,5},{35,4},{35,3},{35,2},{35,1},{35,0},{ 3,0},
> + {19,3},{19,2},{19,1},{19,0}
> +};
> +static const QCELPBitmap qcelp_rate_4thr_bitmap[] = {
> + {62,2},{62,1},{62,0},{61,6},{61,5},{61,4},{61,3},{61,2},
> + {61,1},{61,0},{60,5},{60,4},{60,3},{60,2},{60,1},{60,0},
> + {64,5},{64,4},{64,3},{64,2},{64,1},{64,0},{63,5},{63,4},
> + {63,3},{63,2},{63,1},{63,0},{62,6},{62,5},{62,4},{62,3},
> + {19,3},{19,2},{19,1},{19,0},{18,3},{18,2},{18,1},{18,0},
> + {17,3},{17,2},{17,1},{17,0},{16,3},{16,2},{16,1},{16,0},
> + {70,1},{70,0},{20,3},{20,2},{20,1},{20,0}
> +};
> +
> +static const QCELPBitmap qcelp_rate_8thr_bitmap[] = {
> + {71,3},{60,0},{61,0},{62,0},{71,2},{63,0},{64,0},{65,0},
> + {71,1},{66,0},{67,0},{68,0},{71,0},{69,0},{16,1},{16,0},
> + {70,3},{70,2},{70,1},{70,0}
> +};
i think these would be more readable with offsetof() or some macro using
that
[...]
> +/**
> + * circular codebook for rate 1 frames
> + * TIA/EIA/IS-733 2.4.6.1-2
> + */
> +static const int16_t qcelp_fullrate_ccodebook[128] = {
> + 10, -65, - 59, 12, 110, 34, -134, 157,
> + 104, -84, -34, -115, 23, -101, 3, 45,
> + -101, -16, -59, 28, -45, 134, -67, 22,
> + 61, -29, 226, -26, -55, -179, 157, -51,
> + -220, -93, -37, 60, 118, 74, -48, -95,
> + -181, 111, 36, -52, -215, 78, -112, 39,
> + -17, -47, -223, 19, 12, -98, -142, 130,
> + 54, -127, 21, -12, 39, -48, 12, 128,
> + 6, -167, 82, -102, -79, 55, -44, 48,
> + -20, -53, 8, -61, 11, -70, -157, -168,
> + 20, -56, -74, 78, 33, -63, -173, -2,
> + -75, -53, -146, 77, 66, -29, 9, -75,
> + 65, 119, -43, 76, 233, 98, 125, -156,
> + -27, 78, -9, 170, 176, 143, -148, -7,
> + 27, -136, 5, 27, 18, 139, 204, 7,
> + -184, -197, 52, -3, 78, -189, 8, -65
> +};
> +#define QCELP_FULLRATE_CODEBOOK(i) qcelp_fullrate_ccodebook[i] * .01
i think a constant is clearer than a opaque macro(i)
[...]
> +typedef struct {
> + GetBitContext gb;
> + qcelp_packet_rate framerate;
> +
> +// beginning of unpacked data
> + uint8_t cbsign[16];
> + uint8_t cbgain[16];
> + uint8_t cindex[16];
> + uint8_t plag[4];
> + uint8_t pfrac[4];
> + uint8_t pgain[4];
> + uint8_t lspv[10]; /*!< LSP for RATE_OCTAVE, LSPV for other rates */
> + uint8_t reserved; /*!< on all but rate 1/2 packets */
> + uint8_t cbseed; /*!< only in rate 1/8 packets */
unused
> +// end of unpacked data
> +
> + uint8_t erasure_count;
> + uint8_t octave_count; /*!< count the consecutive RATE_OCTAVE frames */
> + float prev_lspf[10];
> + float predictor_lspf[10]; /*!< LSP predictor,
> + only use for RATE_OCTAVE and I_F_Q */
> + float pitch_synthesis_filter_mem[143];
> + float pitch_pre_filter_mem[143];
> + float formant_mem[10];
> + float last_codebook_gain;
> + int prev_g1[2];
> + int prev_framerate;
> + float prev_pitch_gain[4];
> + uint8_t prev_pitch_lag[4];
> + uint16_t first16bits;
> + int frame_num;
unused
[...]
> +/**
> + * Decodes the 10 quantized LSP frequencies from the LSPV/LSP
> + * transmission codes of any framerate and check for badly received packets.
> + *
> + * @return 0 on success, -1 if the packet is badly received
> + *
> + * TIA/EIA/IS-733 2.4.3.2.6.2-2, 2.4.8.7.3
> + */
> +static int decode_lspf(QCELPContext *q, float *lspf) {
> + int i;
> + float tmp_lspf;
> +
> + if (q->framerate == RATE_OCTAVE ||
> + q->framerate == I_F_Q) {
> + float smooth;
> + float erasure_coeff = 1.0;
> + const float *predictors = (q->prev_framerate != RATE_OCTAVE ||
> + q->prev_framerate != I_F_Q ? q->prev_lspf
> + : q->predictor_lspf);
> +
> + if (q->framerate == RATE_OCTAVE) {
> + q->octave_count++;
> + smooth = (q->octave_count < 10 ? .875 : 0.1);
> + } else {
> + if (q->erasure_count > 1)
> + erasure_coeff = (q->erasure_count < 4 ? 0.9 : 0.7);
> + smooth = 0.125;
> + }
> +
> + for (i = 0; i < 10; i++) {
> + lspf[i] = (i + 1) / 11.;
> + if (q->framerate == RATE_OCTAVE) {
> + q->predictor_lspf[i] =
> + lspf[i] += (q->lspv[i] ? QCELP_LSP_SPREAD_FACTOR
> + : -QCELP_LSP_SPREAD_FACTOR)
> + + (predictors[i] - lspf[i]) * QCELP_LSP_OCTAVE_PREDICTOR;
> + } else {
> + q->predictor_lspf[i] = (predictors[i] - lspf[i]) * erasure_coeff;
> + lspf[i] += QCELP_LSP_OCTAVE_PREDICTOR * q->predictor_lspf[i];
> + }
> + }
> +
> + // Check the stability of the LSP frequencies.
> + if (lspf[0] < QCELP_LSP_SPREAD_FACTOR)
> + lspf[0] = QCELP_LSP_SPREAD_FACTOR;
> + for (i = 1; i < 10; i++)
> + if (lspf[i] < lspf[i-1] + QCELP_LSP_SPREAD_FACTOR)
> + lspf[i] = lspf[i-1] + QCELP_LSP_SPREAD_FACTOR;
> +
> + if (lspf[9] > 1.0 - QCELP_LSP_SPREAD_FACTOR)
> + lspf[9] = 1.0 - QCELP_LSP_SPREAD_FACTOR;
> + for (i = 9; i > 0; i--)
> + if (lspf[i-1] > lspf[i] - QCELP_LSP_SPREAD_FACTOR)
> + lspf[i-1] = lspf[i] - QCELP_LSP_SPREAD_FACTOR;
i think these can be simplified with FFMIN/FFMAX
> +
> + // Low-pass filter the LSP frequencies
> + interpolate_lspf(lspf, smooth, lspf, q->prev_lspf);
> + } else {
> + q->octave_count = 0;
> +
> + tmp_lspf = 0.;
> + for (i = 0; i < 5 ; i++) {
> + lspf[2*i+0] = tmp_lspf += qcelp_lspvq[i][q->lspv[i]][0] * 0.0001;
> + lspf[2*i+1] = tmp_lspf += qcelp_lspvq[i][q->lspv[i]][1] * 0.0001;
> + }
> +
> + // Check for badly received packets
> + if (q->framerate != RATE_QUARTER) {
> + if (lspf[9] <= .66 || lspf[9] >= .985)
> + return -1;
> + for (i = 4; i < 10; i++)
> + if (FFABS(lspf[i] - lspf[i-4]) < .0931)
> + return -1;
> + } else {
> + if (lspf[9] <= .70 || lspf[9] >= .97)
> + return -1;
> + for (i = 3; i < 10; i++)
> + if (FFABS(lspf[i] - lspf[i-2]) < .08)
> + return -1;
> + }
i think this would be slightly more readable as
if(q->framerate == RATE_QUARTER)
else
> + }
> + return 0;
> +}
> +
> +/**
> + * Converts codebook transmission codes to GAIN and INDEX.
> + *
> + * TIA/EIA/IS-733 2.4.6.2
> + */
> +static int decode_gain_and_index(QCELPContext *q, float *gain) {
> + int i, g1[16];
> + float ga[16], gain_memory, smooth_coef;
> +
> + switch (q->framerate) {
> + case RATE_FULL:
> + case RATE_HALF:
> + for (i = 0; i < 16; i++) {
> + if (q->framerate == RATE_HALF && i>=4)
> + break;
this may be more readable as
int something=
for(i=0; i<something; i++
> +
> + g1[i] = 4 * q->cbgain[i];
> + if (q->framerate == RATE_FULL && !((i+1) & 3)) {
> + g1[i] += av_clip((g1[i-1] + g1[i-2] + g1[i-3]) / 3, 6, 38) - 6;
> + if (g1[i] > 60)
> + g1[i] = 60;
> + }
> +
> + gain[i] = qcelp_g12ga[g1[i]];
> +
> + if (q->cbsign[i]) {
> + gain[i] = -gain[i];
> + q->cindex[i] = (q->cindex[i]-89) & 127;
> + }
> + }
> +
> + q->prev_g1[0] = g1[i-2];
> + q->prev_g1[1] = g1[i-1];
> + q->last_codebook_gain = gain[i-1];
> +
> + break;
> + case RATE_QUARTER:
> + for (i = 0; i < 5; i++) {
> + g1[i] = 4 * q->cbgain[i];
> +
> + if (i>0 && FFABS(g1[i] - g1[i-1]) > 40)
> + return -1;
> + if (i >= 2 && FFABS(g1[i] - 2*g1[i-1] + g1[i-2]) > 48)
> + return -1;
> + ga[i] = qcelp_g12ga[g1[i]];
> + }
> +
> + q->prev_g1[0] = g1[3];
> + q->prev_g1[1] = g1[4];
> + q->last_codebook_gain = ga[4];
this code looks like it can be factorized with the other 2cases
[...]
> +static float compute_subframe_energy(const float *vector, const int subframeno) {
> + int i;
> + float energy = 0;
> +
> + vector += 40 * subframeno;
> +
> + for (i = 0; i < 40; i++)
> + energy += vector[i] * vector[i];
> +
> + return energy;
> +}
IMHO its cleaner not to add subframeno inside the function.
a simple dot_product(vector, len) should be more readable
also such a function has a much better chance to be shareable
between codec, and especially when things like that are optimized its
better if it isnt needed to be redone for each len and other obscure variant
> +
> +/**
> + * Apply generic gain control.
> + *
> + * @param in vector to control gain of
> + * @param out gain-controlled output vector
> + *
> + * TIA/EIA/IS-733 2.4.8.3-4/5, 2.4.8.6
> + */
> +static void apply_gain_ctrl(const float *in, float *out) {
> + int i;
> + float scalefactors[4];
> +
> + for (i = 0; i < 4; i++)
> + scalefactors[i] = sqrt(compute_subframe_energy(in , i) /
> + compute_subframe_energy(out, i));
is there something that gurantees "energy" wont be 0 ?
> +
> + for (i = 0; i < 160; i++)
> + out[i] = scalefactors[i/40] * out[i];
the /40 is avoidable
the scalefactors array unneeded
> +
> +}
> +
> +/**
> + * Apply filter in pitch-subframe steps.
> + *
> + * @param memory a buffer for the previous state of the filter
> + * @param gain array of gain per subframe, each element is between 0.0 and 2.0
> + * @param lag array of lag per subframe, each element is
> + * between 16 and 143 if its corresponding pfrac is 0,
> + * between 16 and 139 otherwise
> + * @param pfrac array of boolean per subframe, 1 if the lag is fractional, 0 otherwise
> + * @param v_out the output vector of the filter
> + * @param v_in the input vector of the filter
> + */
> +static void do_pitchfilter(float *memory,
> + const float gain[4], const uint8_t *lag, const uint8_t pfrac[4],
> + float v_out[160], const float v_in[160]) {
> + int i, j, k, index;
> + float hamm_tmp;
> +
> + memory += 143; // Lookup must be done starting at the end of the buffer.
> +
> + for (i = 0; i < 4; i++)
> + if (gain[i] != 0.0) {
the != 0.0 is superflous
> + index = 40 * i - lag[i];
> + for (k = 0; k < 40; k++) {
> + if (pfrac[i]) { // If is a fractional lag...
> + hamm_tmp = 0.0;
> + for (j = -4; j < 4; j++) {
> + hamm_tmp += qcelp_hammsinc_table[j + 4]
> + * (index + j < 0 ? memory[index + j] : v_out [index + j]);
> + }
> + } else
> + hamm_tmp = index < 0 ? memory[index] : v_out[index];
ive looked a little more at the code and i see no problem with removing
the checks, also i dont see why yet another memcpy() would be needed.
we need to carefully review if the existing memcpies can be avoided ...
[...]
> +/**
> + * Apply pitch synthesis filter and pitch pre-filter to the scaled codebook vector.
> + * TIA/EIA/IS-733 2.4.5.2
> + *
> + * @param q the context
> + * @param cdn_vector the scaled codebook vector
> + *
> + * @return 0 if everything goes well, -1 if frame should be erased
> + */
> +static int apply_pitch_filters(QCELPContext *q, float *cdn_vector) {
> + int i;
> + float gain[4];
> + float v_buf[160];
> +
> + if (q->framerate == RATE_FULL ||
> + q->framerate == RATE_HALF ||
> + (q->framerate == I_F_Q && (q->prev_framerate == RATE_FULL ||
> + q->prev_framerate == RATE_HALF))) {
> +
> + if (q->framerate == RATE_FULL ||
> + q->framerate == RATE_HALF) {
> +
> + // Compute Gain & Lag for the whole frame
> + for (i = 0; i < 4; i++) {
> + gain[i] = q->plag[i] ? (q->pgain[i] + 1) / 4.0 : 0.0;
> +
> + q->plag[i] += 16;
> +
> + if (q->pfrac[i] && q->plag[i] >= 140) return -1;
> + }
> + memcpy(q->prev_pitch_lag, q->plag, 4 * sizeof(*q->plag));
are the many spaces intended?
> + } else {
> + gain[3] = q->erasure_count < 3 ? 0.9 - 0.3 * (q->erasure_count - 1)
> + : 0.0;
> + for (i = 0; i < 4; i++)
> + gain[i] = FFMIN(q->prev_pitch_gain[i], gain[3]);
> +
> + memset(q->pfrac, 0, 4 *sizeof(*q->pfrac));
ive not checked but shouldnt this be zero alraedy?
[...]
> +/**
> + * formant synthesis filter
> + *
> + * TIA/EIA/IS-733 2.4.3.1 (NOOOOT)
> + *
> + * @param vector in/out vector of the formant filter
> + * @param lpc linear predictive coding coefficients
> + * @param memory the previous state of the filter
> + */
> +static void do_formant(float *vector, const float *lpc, float *memory) {
> + int i,j;
> +
> + for (i = 0; i < 40;i++)
> + for (j = 0; j < 10; j++)
> + vector[i] += lpc[j] * (i > j ? vector[ i-j-1]
> + : memory[10 + i-j-1]);
please get rid of this check as well, you are alraedy doing a memcpy of the
array.
also IMHO do_formant() is a very poor name
see ff_acelp_lp_synthesis_filter()
also this belongs in a file seperate from the qcelp decoder so it can be
shared with other decoders.
[...]
> + case SILENCE:
> + av_log_missing_feature(avctx, "Blank frame", 1);
> + default:
> + q->framerate = I_F_Q;
is it intended that SILENCE sets IFQ ?
[...]
> +static int qcelp_decode_frame(AVCodecContext *avctx,
qcelp_ prefixes shouldnt be needed for static functions unless you/Reynaldo
want them
> + void *data, int *data_size,
> + uint8_t *buf, const int buf_size) {
> + QCELPContext *q = avctx->priv_data;
> + const QCELPBitmap *order = NULL;
> + float *outbuffer = data;
> + int i, bits;
> + float qtzd_lspf[10], lpc[10];
> + float *formant_mem;
> +
> + if (determine_framerate(avctx, q, buf_size, &buf) < 0) {
> + av_log(avctx, AV_LOG_ERROR, "frame #%d: unknown framerate, unsupported size: %d\n",
> + q->frame_num++, buf_size);
> + return -1;
> + }
> +
> + if (q->framerate == RATE_OCTAVE &&
> + (q->first16bits = (buf[0] << 8) + buf[1]) == 0xFFFF) {
> + warn_insufficient_frame_quality(avctx, "framerate is 1/8 and first 16 bits are on.");
> + goto erasure;
> + }
> +
> + order = qcelp_unpacking_bitmpap_per_rate[q->framerate];
> + if (order) {
> + bits = qcelp_bits_per_rate[q->framerate];
> +
> + init_get_bits(&q->gb, buf, 8 * buf_size);
> +
> + memset(q->cbsign, 0, 72);
iam not sure if C gurantees that these variables will be packed in 72 byte
[...]
> + if (q->framerate == I_F_Q) {
> +erasure:
> + q->framerate = I_F_Q;
> + q->erasure_count++;
> + decode_scaled_codebook_vector(q, outbuffer);
> + decode_lspf(q, qtzd_lspf);
> + apply_pitch_filters(q, outbuffer);
> + }
> +
> + formant_mem = q->formant_mem;
> + for (i = 0; i < 4; i++) {
> + interpolate_lpc(q, qtzd_lspf, lpc, i);
> +
> + do_formant(outbuffer + i * 40, lpc, formant_mem);
> +
> + // WIP Adaptive postfilter should be here
> +
> + formant_mem = outbuffer + i * 40 + 30;
> + }
> + memcpy(q->formant_mem, outbuffer + 150, 10 * sizeof(float));
> +
> + if (q->framerate != I_F_Q)
> + q->erasure_count = 0;
cant this be a else above?
> +
> + for (i = 0; i < 160; i++)
> + outbuffer[i] = av_clipf(outbuffer[i], -8192., 8191.75) / 8192.;
cant this scale factor be merged into some tables?
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
In a rich man's house there is no place to spit but his face.
-- Diogenes of Sinope
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: Digital signature
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20081016/2b49d3fd/attachment.pgp>
More information about the ffmpeg-devel
mailing list