做过流媒体开发的人应该对YUV数据不陌生,YUV将视频帧编码成Y(亮度)、U(蓝色)、V(红色)三个分量,在内存中,Y分量长度为w*h,U、V分量长度一般为w/2 * h/2。基本上从摄像头采集到的视频数据都属于这种格式。在此主要介绍下YUV的一些格式间相互转换和视频帧旋转的实现。
基于三个分量在内存中的排列方式,YUV又分为多种不同的格式。在工作中我遇到过最多的,有如下四种格式:
- NV21
- YUV420SP
- YV12
- YUV420P
常见的几种YUV格式及三分量排列方式:
名称 | 排列方式 | 说明 |
---|---|---|
NV21 | YYY…VUVU…. | Y分量在前,VU分量交替排在后面 |
YUV420SP | YYY…UVUV…. | Y分量在前,UV分量交替排在后面 |
YV12 | YYY…VV..UU.. | Y分量在前,其次是V分量,其次是U分量 |
YUV420P | YYY…UU..VV.. | Y分量在前,其次是U分量,其次是V分量 |
是不是绕晕了?其实有规律的,都是Y在前面,然后名称里带‘S’的,表示UV分量交错呈现;不带‘S’,表示UV分量分开呈现;最后U、V的顺序就要自己查资料了。
实际应用中,我们可能需要将YUV数据进行转换、旋转,比方说安卓硬编码目前不支持NV21和YV12,支持420P和420SP,那我们就需要将摄像头采集到的视频数据先转换成420P或420SP;还有,摄像头竖屏采集的时候,为了播放端能正常显示,我们需要将视频帧旋转90度。
理解了这几种格式的分量的排列方式,将它们相互之间进行转换也就不难了。下面我们做几个转换和旋转的示例。假设视频帧的宽、高分别为width、height
NV21转420SP:
// 跳过Y分量,将UV‘点’相互颠倒一下
int uLen = width*height*0.5;
int len = width*height;
for (int i=len; i< len + uLen; i++)
{
jbyte temp = *(pData +i);
*(pData + i) = *(pData + i +1);
*(pData + i +1) = temp;
++i;
}
YV12转420P:
// 也是跳过Y分量,将UV‘块’颠倒一下
int