如何从uint8类型I420原始视频缓冲区拆分得到YUV分量指针?
回答
可以,只要你持有的是标准连续紧凑存储、无额外行对齐填充的I420(即YUV420P)格式缓冲区,完全可以通过指针偏移计算出Copy函数需要的所有入参。
I420格式内存布局规则
标准I420是平面存储格式,三个分量按Y、U、V的顺序连续排列在缓冲区中:
- Y(亮度)平面:分辨率和帧尺寸一致,共
width * height个字节 - U(色度Cb)平面:水平、垂直方向均为Y平面的1/2采样,共
(width/2) * (height/2)个字节 - V(色度Cr)平面:尺寸和U平面完全一致,共
(width/2) * (height/2)个字节
整体缓冲区总大小固定为width * height * 3 / 2,你可以先用已知的缓冲区大小做校验,如果数值不匹配,说明缓冲区带行对齐填充、或者不是标准I420格式,不能直接按下面的方法计算。
各入参计算方式
width、height:直接传入你已知的视频帧宽度、高度即可data_y:就是你持有的原始uint8_t缓冲区首地址,Y平面位于缓冲区最开头stride_y:Y平面每行的字节跨度,紧凑无填充场景下等于widthdata_u:Y平面结束后的首地址,计算方式为原始指针 + stride_y * heightstride_u:U平面每行的字节跨度,紧凑无填充场景下等于width / 2data_v:U平面结束后的首地址,计算方式为data_u + stride_u * (height / 2)stride_v:V平面每行的字节跨度,和U平面一致,紧凑无填充场景下等于width / 2
参考实现代码
// 已知入参 uint8_t* raw_i420_buf = /* 你的I420原始缓冲区指针 */; int known_buf_size = /* 已知的缓冲区大小 */; int frame_width = /* 已知的帧宽度 */; int frame_height = /* 已知的帧高度 */; // 先校验是否为标准紧凑I420 const int expected_buf_size = frame_width * frame_height * 3 / 2; // 若known_buf_size和expected_buf_size不相等,说明存在行对齐或格式不匹配,禁止直接按以下逻辑传参 // 计算各平面参数 const uint8_t* ptr_y = raw_i420_buf; const int stride_y = frame_width; const uint8_t* ptr_u = raw_i420_buf + stride_y * frame_height; const int stride_u = frame_width / 2; const uint8_t* ptr_v = ptr_u + stride_u * (frame_height / 2); const int stride_v = frame_width / 2; // 调用第三方库接口 Copy(frame_width, frame_height, ptr_y, stride_y, ptr_u, stride_u, ptr_v, stride_v);
注意事项
- 如果你的I420数据来自硬件解码、渲染接口等场景,很可能存在行对齐要求(比如按16/32/64字节对齐行首),此时stride值会大于对应平面的行宽,不能直接用width/2作为stride,必须拿到源数据实际的行跨度才能正确计算平面偏移,否则会出现画面错位、花屏、内存越界问题。
- 如果帧宽高为奇数,色度平面尺寸按向下取整计算即可,绝大多数I420实现都遵循这个规则。
内容的提问来源于stack exchange,提问作者BuvinJ
相关产品推荐
相关产品推荐

