FFmpeg的extract_mvs能否提取H.264子宏块(8x4/4x8/4x4)运动矢量?
解决FFmpeg extract_mvs.c无法提取H.264子宏块(8x4/4x8/4x4)运动矢量的问题
你遇到的问题是因为FFmpeg自带的extract_mvs.c默认只解析到8x8尺寸的宏块,而H.264中8x4、4x8、4x4这类子宏块的运动矢量是作为8x8宏块的子分区数据存在的,需要修改代码来递归处理这些子分区。
问题根源
从x264的输出可以确认这些子宏块确实存在:
x264 [info]: mb P I16..4: 1.9% 0.4% 0.2% P16..4: 20.1% 17.9% 28.4% 11.2% 7.2% skip:12.7%
其中P16..4的后两个数值对应8x4/4x8和4x4子宏块的占比,但extract_mvs.c的原始逻辑只处理了MB_TYPE_16x16、MB_TYPE_16x8、MB_TYPE_8x16、MB_TYPE_8x8这几种宏块类型,没有深入解析8x8块内部的子分区。
修改extract_mvs.c的具体方法
找到extract_mvs.c中处理宏块的核心逻辑(通常在decode_mb或类似函数里),添加对8x8块子分区的解析:
- 当检测到宏块类型为
MB_TYPE_8x8时,遍历该宏块下的4个8x8子块:
if (mb_type & MB_TYPE_8x8) { for (int sub_mb_idx = 0; sub_mb_idx < 4; sub_mb_idx++) { int sub_mb_type = s->current_picture.mb_type[mb_idx * 4 + sub_mb_idx]; // 处理子块的分区类型 handle_sub_mb_mvs(s, mb_idx, sub_mb_idx, sub_mb_type); } }
- 实现
handle_sub_mb_mvs函数,解析8x8子块的子分区(8x4、4x8、4x4):
static void handle_sub_mb_mvs(MpegEncContext *s, int mb_idx, int sub_mb_idx, int sub_mb_type) { int mb_x = mb_idx % s->mb_width; int mb_y = mb_idx / s->mb_width; int sub_x = (sub_mb_idx % 2) * 8; int sub_y = (sub_mb_idx / 2) * 8; // 处理8x4类型子宏块 if (sub_mb_type & MB_TYPE_8x4) { for (int i = 0; i < 2; i++) { int mv_x = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][0]; int mv_y = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][1]; printf("Sub-MB: 8x4, Pos: (%d,%d), MV: (%d,%d)\n", mb_x*16 + sub_x, mb_y*16 + sub_y + i*4, mv_x, mv_y); } } // 处理4x8类型子宏块 else if (sub_mb_type & MB_TYPE_4x8) { for (int i = 0; i < 2; i++) { int mv_x = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][0]; int mv_y = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][1]; printf("Sub-MB: 4x8, Pos: (%d,%d), MV: (%d,%d)\n", mb_x*16 + sub_x + i*4, mb_y*16 + sub_y, mv_x, mv_y); } } // 处理4x4类型子宏块 else if (sub_mb_type & MB_TYPE_4x4) { for (int i = 0; i < 4; i++) { int mv_x = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][0]; int mv_y = s->current_picture.motion_val[0][mb_idx*16 + sub_mb_idx*4 + i][1]; int px = mb_x*16 + sub_x + (i%2)*4; int py = mb_y*16 + sub_y + (i/2)*4; printf("Sub-MB: 4x4, Pos: (%d,%d), MV: (%d,%d)\n", px, py, mv_x, mv_y); } } }
- 确保代码能访问
MpegEncContext、motion_val等结构体成员,若涉及静态权限问题,可调整FFmpeg编译选项或代码的访问逻辑。
编译与测试
修改后重新编译FFmpeg,用新生成的extract_mvs工具处理目标视频,即可提取到8x4、4x8、4x4这类子宏块的运动矢量。
内容的提问来源于stack exchange,提问作者黄舒心
相关产品推荐
相关产品推荐

