目录
背景
在iOS里,我们经常能看到 CVPixelBufferRef 这个类型,在Camera 采集返回的数据里得到一个CMSampleBufferRef,而每个CMSampleBufferRef里则包含一个 CVPixelBufferRef,在视频硬解码的返回数据里也是一个 CVPixelBufferRef(里面包好了所有的压缩的图片信息)。
一、CVPixelBufferRef简介
CVPixelBufferRef:是一种像素图片类型,由于CV开头,所以它是属于 CoreVideo 模块的。CVPixelBufferRef是Core Video框架中定义的一个类型,代表视频帧的像素数据。它不仅包含了原始图像数据,还包含了图像的格式、尺寸以及每个像素的布局信息。
通过操作`CVPixelBufferRef`,开发者可以对视频帧进行读取、修改和渲染等操作。
iOS 对象命名前面用缩写表示它属于的模块,如: CF 代表 CoreFoundation,CG代表 CoreGraphic,CM代表 CoreMedia。
它是一个C对象,而不是Objective C对象,所以它不是一个类,而是一个类似Handle的东西。从代码头文件的定义来看
二、CVPixelBufferRef的内部结构和使用
2.1、CVPixelBufferRef
CVPixelBufferRef 就是用 CVBufferRef typedef而来的,而CVBufferRef 本质上就是一个void *,至于这个void *具体指向什么数据只有系统才知道了。
所以我们看到 所有对 CVPixelBufferRef 进行操作的函数都是纯 C 函数。
比如 CVPixelBufferGetWidth, CVPixelBufferGetBytesPerRow
通过API可以看出来,CVPixelBufferRef里包含很多图片相关属性,比较重要的有 width,height,PixelFormatType等。
2.2、CVPixelBufferRef类型
由于可以有不同的PixelFormatType,说明他可以支持多种位图格式,除了常见的 RGB32以外,还可以支持比如 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,这种YUV多平面的数据格式,这个类型里 BiPlanar 表示双平面,说明它是一个 NV12的YUV,包含一个Y平面和一个UV平面。通过CVPixelBufferGetBaseAddressOfPlane可以得到每个平面的数据指针。在得到 Address之前需要调用CVPixelBufferLockBaseAddress,这说明CVPixelBufferRef的内部存储不仅是内存也可能是其它外部存储,比如现存,所以在访问前要lock下来实现地址映射,同时lock也保证了没有读写冲突。
由于是C对象,它是不受 ARC 管理的,所以需要自己手动的进行释放:可以CVPixelBufferRetain,CVPixelBufferRelease函数用来加减引用计数,其实和CFRetain和CFRelease是等效的,所以可以用 CFGetRetainCount来查看当前引用计数。
2.3、像素格式
CVPixelBufferRef支持多种像素格式,如YUV、RGB等。其中,YUV格式因为其高效的色彩表示而广泛用于视频压缩与传输。每种格式都有其特定的内存布局,了解这些布局对于直接操作像素数据非常重要。
2.4、内存布局
CVPixelBufferRef`中的像素数据可以是连续的,也可以是分散的。例如,在一些YUV格式中,Y分量和UV分量可能存储在不同的内存区域。开发者需要根据具体的像素格式,正确地计算每个分量的内存地址。
2.5、操作CVPixelBufferRef
对CVPixelBufferRef的操作主要包括创建、访问和释放等。
2.6、创建CVPixelBufferRef
CVPixelBufferRef可以通过`CVPixelBufferCreate`或CVPixelBufferPoolCreatePixelBuffer等函数创建。创建时,需要指定像素格式、尺寸以及其他可能的属性。
2.7、访问像素数据
访问CVPixelBufferRef中的像素数据前,通常需要调用CVPixelBufferLockBaseAddress函数锁定数据,以防数据在访问过程中被修改。完成数据操作后,再调用CVPixelBufferUnlockBaseAddress解锁。
2.8、释放CVPixelBufferRef
与Core Foundation中的其他对象一样,CVPixelBufferRef在使用完毕后需要释放,可以通过CFRelease函数来完成。
2.9、视频渲染
在iOS平台上,有多种方式可以实现视频渲染,包括直接在UIView上绘制、使用AVPlayerLayer播放视频流,或者通过OpenGL ES、Metal等更底层的图形API进行自定义渲染。
2.10、使用Core Graphics进行渲染
通过将CVPixelBufferRef的数据转换为CGImageRef,可以使用Core Graphics框架进行绘制。这种方法简单直观,适合不需要高性能渲染的场景。
2.11、使用OpenGL ES/Metal进行渲染
对于需要高性能的实时视频处理和渲染,可以使用OpenGL ES或Metal框架。这些框架提供了更接近硬件层的API,能够利用GPU加速视频数据的处理和渲染。在这种情况下,CVPixelBufferRef通常与纹理对象相结合使用,以实现高效的图像传输和渲染。
三、显示思路及代码实现
如果要显示 CVPixelBufferRef 里的内容,通常有以下思路。
把 CVPixelBufferRef 转换成 UIImage,就可以直接赋值给UIImageView的image属性,显示在UIImageView上,示例代码:
+(UIImage*)uiImageFromPixelBuffer:(CVPixelBufferRef)p {
CIImage* ciImage = [CIImage imageWithCVPixelBuffer : p];
CIContext* context = [CIContext contextWithOptions : @{kCIContextUseSoftwareRenderer : @(YES)
}];
CGRect rect = CGRectMake(0, 0, CVPixelBufferGetWidth(p), CVPixelBufferGetHeight(p));
CGImageRef videoImage = [context createCGImage : ciImage fromRect : rect];
UIImage* image = [UIImage imageWithCGImage : videoImage];
CGImageRelease(videoImage);
return image;
}
四、总结
CVPixelBufferRef作为iOS视频处理中的核心数据结构,其高效的数据表示和灵活的操作方法为视频渲染和处理提供了强大的支持。无论是简单的视频播放,还是复杂的视频效果处理,了解并掌握`CVPixelBufferRef`的使用都是非常重要的。希望本文能够帮助iOS开发者深入理解CVPixelBufferRef,并在实际开发中更好地应用这一强大的工具。