利用libav庫中的解析swf_adpcm部分來解析swf_adpcm
阿新 • • 發佈:2019-01-27
在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資料了