1. 程式人生 > >利用libav庫中的解析swf_adpcm部分來解析swf_adpcm

利用libav庫中的解析swf_adpcm部分來解析swf_adpcm

在libav庫的libavcodec資料夾中,有adpcm.c這個檔案,就是用來解析adpcm音訊資料,其中有一個方法是用來解析swf_adpcm資料,方法名為adpcm_swf_decode

這個方法的程式碼段如下

1.需要用到的資料結構:

ff_adpcm_step_table【89】

/**
 * This is the step table. Note that many programs use slight deviations from
 * this table, but such deviations are negligible:
 */
const int16_t ff_adpcm_step_table[89] = {
        7,     8,     9,    10,    11,    12,    13,    14,    16,    17,
       19,    21,    23,    25,    28,    31,    34,    37,    41,    45,
       50,    55,    60,    66,    73,    80,    88,    97,   107,   118,
      130,   143,   157,   173,   190,   209,   230,   253,   279,   307,
      337,   371,   408,   449,   494,   544,   598,   658,   724,   796,
      876,   963,  1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,
     2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,
     5894,  6484,  7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899,
    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};

swf_index_tables[4][16]
// padded to zero where table size is less then 16
static const int swf_index_tables[4][16] = {
    /*2*/ { -1, 2 },
    /*3*/ { -1, -1, 2, 4 },
    /*4*/ { -1, -1, -1, -1, 2, 4, 6, 8 },
    /*5*/ { -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16 }
};
解析adpcm資料的程式碼為
static void adpcm_swf_decode(AVCodecContext *avctx, const uint8_t *buf, int buf_size, int16_t *samples)
{
    ADPCMDecodeContext *c = avctx->priv_data;
    GetBitContext gb;
    const int *table;
    int k0, signmask, nb_bits, count;
    int size = buf_size*8;
    int i;

    init_get_bits(&gb, buf, size);

    //read bits & initial values
    nb_bits = get_bits(&gb, 2)+2;
    table = swf_index_tables[nb_bits-2];
    k0 = 1 << (nb_bits-2);
    signmask = 1 << (nb_bits-1);

    while (get_bits_count(&gb) <= size - 22*avctx->channels) {
        for (i = 0; i < avctx->channels; i++) {
            *samples++ = c->status[i].predictor = get_sbits(&gb, 16);
            c->status[i].step_index = get_bits(&gb, 6);
        }

        for (count = 0; get_bits_count(&gb) <= size - nb_bits*avctx->channels && count < 4095; count++) {
            int i;

            for (i = 0; i < avctx->channels; i++) {
                // similar to IMA adpcm
                int delta = get_bits(&gb, nb_bits);
                int step = ff_adpcm_step_table[c->status[i].step_index];
                long vpdiff = 0; // vpdiff = (delta+0.5)*step/4
                int k = k0;

                do {
                    if (delta & k)
                        vpdiff += step;
                    step >>= 1;
                    k >>= 1;
                } while(k);
                vpdiff += step;

                if (delta & signmask)
                    c->status[i].predictor -= vpdiff;
                else
                    c->status[i].predictor += vpdiff;

                c->status[i].step_index += table[delta & (~signmask)];

                c->status[i].step_index = av_clip(c->status[i].step_index, 0, 88);
                c->status[i].predictor = av_clip_int16(c->status[i].predictor);

                *samples++ = c->status[i].predictor;
            }
        }
    }
}
考慮到庫中的結構繁多,而且很多也沒必要用到,所以需要對程式碼進行裁剪好提取出來。

1.先要將AVCodecContext *avctx去除掉,而對於AVCodecContext結構來說,需要用到的部分為status。

故可以將這個結構分割出來,用

struct adpcm_state {
	short	valprev;	/* Previous output value */
	char	index;		/* Index into stepsize table */
};
來表示。

avctx->channels

所以這個方法的初步程式碼為

adpcm_swf_decode(const uint8_t *buf, int buf_size, int16_t *samples, int channels)
{
	GetBitContext gb;
	const int *table;
	int k0, signmask, nb_bits, count;
	int size = buf_size*8;
	int i;
	int lengthCounts;
	struct adpcm_state adpcmState;

	init_get_bits(&gb, buf, size);

	//read bits & initial values
	nb_bits = get_bits(&gb, 2)+2;
	table = swf_index_tables[nb_bits-2];
	k0 = 1 << (nb_bits-2);
	signmask = 1 << (nb_bits-1);
	lengthCounts = 0;
	while (get_bits_count(&gb) <= size - 22*channels) {
		for (i = 0; i < channels; i++) {
			*samples++ = adpcmState.valprev = get_sbits(&gb, 16);
			adpcmState.index = get_bits(&gb, 6);
		}

		for (count = 0; get_bits_count(&gb) <= size - nb_bits*channels && count < 4095; count++) {
			int i;
			for (i = 0; i < channels; i++) {
				// similar to IMA adpcm
				int delta = get_bits(&gb, nb_bits);
				int step = stepsizeTable[adpcmState.index];
				long vpdiff = 0; // vpdiff = (delta+0.5)*step/4
				int k = k0;

				do {
					if (delta & k)
						vpdiff += step;
					step >>= 1;
					k >>= 1;
				} while(k);
				vpdiff += step;

				if (delta & signmask)
					adpcmState.valprev -= vpdiff;
				else
					adpcmState.valprev += vpdiff;

				adpcmState.index += table[delta & (~signmask)];

				adpcmState.index = av_clip(adpcmState.index, 0, 88);
				adpcmState.valprev = av_clip_int16(adpcmState.valprev);

				*samples++ = adpcmState.valprev;
				lengthCounts++;
			}
		}
	}
	*outSize = lengthCounts;
	
	return 0;
}

最後,由於這個方法採用了大量的行內函數和巨集,所以我從libav的官方win32版本中拿出了需要用到的幾個標頭檔案,點選此處下載

好了,這樣,通過這個函式,就能將swf的adpcm資料轉化成pcm資料了