场景: ffmpeg -i b3e.mpeg -dct int -idct int -threads 1 -y yy.mpeg
(源码需要稍微修改下, 见杂项)
1. 编解码流程
先回顾一下我们处于哪个阶段。
pthread_slice.c:worker--> encode_thread-->
ff_set_qscale(5);
ff_mpeg1_encode_slice_header:
SLICE_MIN_START_CODE qscale 5bits
encode_mb_hq-->encode_mb-->encode_mb_internal-->
dct_quantize=ff_dct_quantize_c-->
ff_jpeg_fdct_islow_8 //DCT
bias_qmat //量化
ff_mpeg1_encode_mb-->mpeg1_encode_mb_internal-->mpeg1_encode_block -->encode_dc,vlc//熵编码
avcodec_decode_video2-->mpeg_decode_frame-->decode_chunks-->mpeg_decode_slice-->
mpeg_decode_mb-->mpeg1_decode_block_intra//熵解码
ff_MPV_decode_mb-->put_dct-->
dct_unquantize(block, i, qscale)=dct_unquantize_mpeg1_intra_c//反量化
ff_idct_put(dest, block)=ff_j_rev_dct//反DCT
2. 量化过程
DCT把一部分计算转移到量化,以减少累积误差,他们是紧耦合的!只说量化而不涉及DCT是不对的!体现在
ff_MPV_encode_init-->ff_convert_matrix(q = s->q_intra_matrix, m = ff_mpeg1_default_intra_matrix)
根据fdct做不同的初始化,其中
uint16_t ff_mpeg1_default_intra_matrix={
8, 16, 19, 22, 26, 27, 29, 34,
16, 16, 22, 24, 27, 29, 34, 37,
19, 22, 26, 27, 29, 34, 34, 38,
22, 22, 26, 27, 29, 34, 37, 40,
22, 26, 27, 29, 32, 35, 40, 48,
26, 27, 29, 32, 35, 40, 48, 58,
26, 27, 29, 34, 38, 46, 56, 69,
27, 29, 35, 38, 46, 56, 69, 83
}; 这个是加权矩阵,左上角值最小,表示权重越大。
q[scale][i] = (1 << 21)/(scale * m[i]);
q_intra_matrix 等矩阵按照6.2.3.2 Quant matrix
extension语法规定ff_write_quant_matrix到比特流里面(可选, ffmpeg没写),供解码端使用。
2.1 ff_dct_quantize_c 量化每一个系数b_大致简化_为:
c = b /(scale * m)
解码端,mpeg1_decode_sequence如果流里面有就load_matrix(m=s->intra_matrix)否则
取m = ff_mpeg1_default_intra_matrix。
dct_unquantize_mpeg1_intra/inter_c:
b = c * scale * m/8
b = (2*c+1)*scale*m/16
跟 7.4.2.3 Reconstruction formulae 不完全一样。
问题:为什么解码端多了一个除以8的因子,而编码端没有?
答案:因为标准13818-2 Annex A规定fdct输出的系数用12比特表示9比特系数值。
这个问题折磨我两天,各种胡猜,见后面的杂项。什么左移右移 CONST_BITS,
PASS1_BITS, OUT_BITS啊,夹杂在一起,就迷糊了。
2.2 clip_coeffs 对应到 7.4.3 Saturation, 把值裁剪到[min_qcoeff, max_qcoeff] = [-2047, 2047]范围内。
encode时没有做 7.4.4 Mismatch control,只是在mpeg2 decode才做的。
2.3 量化之后其实还有一个ff_block_permute:
(gdb) x /64ob scantable = ff_zigzag_direct
0x11f79a0
0 01 010 020 011 02 03 012
0x11f79a8
021 030 040 031 022 013 04 05
0x11f79b0 014 023 032 041 050 060 051 042
0x11f79b8 033 024 015 06 07 016 025 034
0x11f79c0 043 052 061 070 071 062 053 044
0x11f79c8 035 026 017 027 036 045 054 063
0x11f79d0 072 073 064 055 046 037 047 056
0x11f79d8 065 074 075 066 057 067 076 077
(gdb) p s->idsp.perm_type
$5 = FF_IDCT_PERM_LIBMPEG2
(gdb) x /64ob s->idsp.idct_permutation
0x1d99f70: 0 04 01 05 02 06 03 07
0x1d99f78: 010 014 011 015 012 016 013 017
0x1d99f80: 020 024 021 025 022 026 023 027
0x1d99f88: 030 034 031 035 032 036 033 037
0x1d99f90: 040 044 041 045 042 046 043 047
0x1d99f98: 050 054 051 055 052 056 053 057
0x1d99fa0: 060 064 061 065 062 066 063 067
0x1d99fa8: 070 074 071 075 072 076 073 077
只是列重排: [0,1,2,3,4,5,6,7]-->[0,4,1,5,2,6,3,7],我检查了-idct simple是不需要重排的。
重排存在的原因是编码的各个模块需要的数据格式和顺序不同。比如mmx输入的顺序和输出到比特流的顺序不同。
注意到老版block_permute(block);是放在量化前面的,对64个值重排;新版放在后面,只对last_non_zero个重排,
并且last_non_zero通常是0,效率自然增加了,赞一个!
TODO
====
1. 默认是 dct_quantize_ssse3(x86/mpegvideoenc_template.c),以后有空看看mmx,sse技术。
2. fixed-point math
杂项
====
技巧:
找不到这个函数哪里定义的,到gdb里面b 它, 就会出现jfdctfst.c, line 216。
到某个地方想查看函数指针什么的指向哪里,而step进不去时,disas看即可。
作为对比,ff_jpeg_fdct_islow_8,-dct 参数分别为fastint和int。
我原先为怎么指定quantize发愁呢,结果发现只需指定-dct就行了,ff_dct_quantize_c总是和ff_fdct_ifast/ff_jpeg_fdct_islow_8绑定的。
遗憾的是反量化没得参数可设定,-idct int
或simple都没有用,s->dct_unquantize_mpeg1_intra总是被ff_MPV_common_init_x86()重置,那把它放空好了。
同理也把ff_idctdsp_init_x86放空。
<-------------
#define QMAT_SHIFT 21
#define QUANT_BIAS_SHIFT 8
// (a + x * 3 / 8) / x
s->intra_quant_bias = 3 << (QUANT_BIAS_SHIFT - 3) = 96;
s->inter_quant_bias = 0;
qmat = q_intra_matrix[qscale] = (int)((UINT64_C(1) << QMAT_SHIFT) / (qscale * ff_mpeg1_default_intra_matrixj))
bias= s->intra_quant_bias<<(QMAT_SHIFT - QUANT_BIAS_SHIFT) = 96*8*1024 = 786432
threshold1= (1<<QMAT_SHIFT) - bias - 1 = 2*1024*1024 - 786433 = 1310719
threshold2= (threshold1<<1) = 2621438
level = block[j] * qmat[j];
if( level+threshold1 > threshold2)
block[j]= (bias + level)>>QMAT_SHIFT;
[96<<(QMAT_SHIFT - QUANT_BIAS_SHIFT) + b * (1<<QMAT_SHIFT)/(scale*m) ]>>QMAT_SHIFT
---->
因为编码端
BITS_IN_JSAMPLE + PASS1_BITS + 3 = 8+4+3 = 15
CONST_BITS + OUT_SHIFT = 13+4 = 17
ff_fdct_ifast里面已经DESCALE了,符合标准。
而解码端idct期望输入系数已经乘以8。
我猜可能是idct需要比较小的数,比如1*5*19/8 =
11。那dct呢?它的输入是最大是Y分量8比特不超过255。