[关闭]
@qidiandasheng 2020-07-01T11:12:42.000000Z 字数 4121 阅读 2889

视频YUV格式简析

音视频


YUV是什么

视频是由一帧一帧的数据连接而成,而一帧视频数据其实就是一张图片。

yuv是一种图片储存格式,跟RGB格式类似。

RGB格式的图片很好理解,计算机中的大多数图片,都是以RGB格式存储的。

yuv中,y表示亮度,单独只有y数据就可以形成一张图片,只不过这张图片是灰色的。u和v表示色差(u和v也被称为:Cb-蓝色差,Cr-红色差),

YUV格式有两大类:planar和packed。

为什么要用YUV

有一定历史原因,最早的电视信号,为了兼容黑白电视,采用的就是yuv格式。

一张yuv的图像,去掉uv,只保留y,这张图片就是黑白的。

而且yuv可以通过抛弃色差来进行带宽优化。

比如YUV420格式图像相比RGB来说,要节省一半的字节大小,抛弃相邻的色差对于人眼来说,差别不大。

从下图可以看到一个U分量为Y分量的四分之一,V分量为Y分量的四分之一。也就是说4个像素yuv420格式为6个字节,而RGB为12个字节。
v2-8683d2005b49bec3cd76697b40b3b54b_1440w (1).jpg-16.6kB

将一张图片的Y、U、V数据单独显示就会如下图所示:

导出图片Tue Apr 21 2020 21_26_13 GMT+0800 (中国标准时间).png-428.4kB

YUV的格式

YUV的存储格式其实与其采样的方式密切相关,主流的采样方式有三种,YUV4:4:4,YUV4:2:2,YUV4:2:0。

以黑点表示采样该像素点的Y分量,以空心圆圈表示采用该像素点的UV分量

YUV444

导出图片Tue Apr 21 2020 21_37_46 GMT+0800 (中国标准时间).png-76.4kB

4:4:4表示完全取样,每个像素点都采样单独的 YUV 分量信息,包含了最全面的 YUV 信息。占用字节数跟RGB一样。

YUV422

导出图片Tue Apr 21 2020 21_41_06 GMT+0800 (中国标准时间).png-54.1kB

在 YUV444 编码的基础上采用 2 * 1 的矩阵进行二次取样,也就是在水平方向上隔一列采样一次 UV 信息,在垂直方向上进行完全取样,每两个Y共用一组UV分量。

占用字节数为(width * height + (width * height) / 2 + (width * height) / 2) = (width * height) * 2,是RGB的三分之二。

YUV422根据数据排列分为以下几种格式:

YUV420

导出图片Tue Apr 21 2020 21_41_29 GMT+0800 (中国标准时间).png-59.1kB

YUV420分为两种:YUV420pYUV420sp:

格式转换

搜索PixelFormatType的类型里420能发现以下几种格式中:

根据表面意思,可以看出,可以分为两类:planar(平面420p)和 BiPlanar(双平面420sp)。iOS只支持NV12,也就是双平面kCVPixelFormatType_420YpCbCr8BiPlanarVideoRangekCVPixelFormatType_420YpCbCr8BiPlanarFullRange。所以在iOS里我们就需要进行一个格式的转换把三平面转换成双平面来渲染显示。

I420转NV12

主要是三个planar(平面)转为两个planar(平面)

使用iOS三方库libyuv

  1. + (CVPixelBufferRef)convertVideoSmapleBufferToNV12:(CVPixelBufferRef)pixelBuffer{
  2. CVPixelBufferLockBaseAddress(pixelBuffer, 0);
  3. //图像宽度(像素)
  4. size_t pixelWidth = CVPixelBufferGetWidth(pixelBuffer);
  5. //图像高度(像素)
  6. size_t pixelHeight = CVPixelBufferGetHeight(pixelBuffer);
  7. //获取CVImageBufferRef中的y数据
  8. uint8_t *y_frame = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
  9. //获取CMVImageBufferRef中的u数据
  10. uint8_t *u_frame =(unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
  11. //获取CMVImageBufferRef中的v数据
  12. uint8_t *v_frame =(unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 2);
  13. //y stride
  14. size_t plane1_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 0);
  15. //u stride
  16. size_t plane2_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 1);
  17. //v stride
  18. size_t plane3_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 2);
  19. size_t plane1_size = plane1_stride * CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
  20. size_t plane2_size = plane2_stride * CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
  21. size_t plane3_size = plane3_stride * CVPixelBufferGetHeightOfPlane(pixelBuffer, 2);
  22. size_t frame_size = plane1_size + plane2_size + plane3_size;
  23. uint8* buffer = (unsigned char *)malloc(frame_size);
  24. uint8* dst_uv = buffer + plane1_size;
  25. libyuv::I420ToNV12(y_frame, plane1_stride,
  26. u_frame, plane2_stride,
  27. v_frame, plane3_stride,
  28. buffer, plane1_stride,
  29. dst_uv, plane2_stride+plane3_stride,
  30. pixelWidth, pixelHeight);
  31. CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
  32. //转化
  33. NSDictionary *pixelAttributes = @{(id)kCVPixelBufferIOSurfacePropertiesKey : @{}};
  34. CVPixelBufferRef pixelBuffer1 = NULL;
  35. CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,
  36. pixelWidth,pixelHeight,kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
  37. (__bridge CFDictionaryRef)pixelAttributes,&pixelBuffer1);
  38. CVPixelBufferLockBaseAddress(pixelBuffer1, 0);
  39. uint8_t *yDestPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer1, 0);
  40. memcpy(yDestPlane, buffer, pixelWidth * pixelHeight);
  41. uint8_t *uvDestPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer1, 1);
  42. memcpy(uvDestPlane, dst_uv, pixelWidth * pixelHeight/2);
  43. if (result != kCVReturnSuccess) {
  44. NSLog(@"Unable to create cvpixelbuffer %d", result);
  45. }
  46. CVPixelBufferUnlockBaseAddress(pixelBuffer1, 0);
  47. free(buffer);
  48. return pixelBuffer1;
  49. }

参考

yuv、pcm数据的介绍和获取
YUV数据格式与YUV_420_888

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注