CVPixelBuffer的创建数据填充以及数据读取

CVPixelBuffer 在音视频编解码以及图像处理过程中应用广泛,有时需要读取内部数据,很少的时候需要自行创建并填充数据,下面简单叙述。

创建

创建时调用的方法主要是这个:

CVReturn CVPixelBufferCreate(CFAllocatorRef allocator,
size_t width,
size_t height,
OSType pixelFormatType,
CFDictionaryRef pixelBufferAttributes,
CVPixelBufferRef _Nullable *pixelBufferOut);

提供必须的参数即可,

XX pixelFormatType 常用的这几个:


/* NV12 */ kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v',
/* Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]).
baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */ kCVPixelFormatType_420YpCbCr8BiPlanarFullRange = '420f',
/* Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]).
baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct */ /* YUV420P */ kCVPixelFormatType_420YpCbCr8Planar = 'y420',
/* Planar Component Y'CbCr 8-bit 4:2:0.
baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct */

因为我想要创建NV12格式的buffer,所以没有使用那个直接提供数据的创建函数,后续提供。如果数据格式爲420P的话,直接指定数据地址也可以。

XX pixelBufferAttributes

这个参数是optinal的,提供所有额外的信息。Core Video根据提供的参数来创建合适的数据,我看到网上的代码往往是这样提供的:

NSDictionary *pixelAttributes = @{(id)kCVPixelBufferIOSurfacePropertiesKey : @{}};

说明如下:

Provide a value for this key if you want Core Video to use the
IOSurface framework to allocate the pixel buffer.
(See IOSurface.)
Provide an empty dictionary to use default IOSurface options.

数据填充

以 NV12 格式的数据填充举例说明。

在访问buffer内部裸数据的地址時(读或写都一样),需要先将其锁上,用完了再放开,如下:

CVPixelBufferLockBaseAddress(pixelBuffer, 0);

// To touch the address of pixel...

CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

Y通道(Luminance)与 UV通道(Chrominance)分开填充数据,而且需要注意后者是UV交错排列的。在填充数据時还需要考虑到数据对齐的问题,当视频帧的宽高并不是某个对齐基数的倍数時(比如16),内部具体如何分配内存是不确定的,保险的做法就是逐行数据填充。这里我放上填充Chrominance通道数据的例子:

size_t bytesPerRowChrominance = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);

long chrominanceWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
long chrominanceHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1); // Chrominance
uint8_t *uvDestPlane = (uint8_t *) CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
memset(uvDestPlane, 0x80, chrominanceHeight * bytesPerRowChrominance); for (int row = 0; row < chrominanceHeight; ++row) {
memcpy(uvDestPlane + row * bytesPerRowChrominance,
uvDataPtr + row * _outVideoWidth,
_outVideoWidth);
} free(uvDataPtr);

在逐行copy数据的时候,pixel内部地址每个循环步进 current_row * bytesPerRowChrominance 的大小,这是pixelbuffer内部的内存排列。然后我的数据来源内存排列是紧密排列不考虑内存多少位对齐的问题的,所以每次的步进是 current_row * _outVideoWidth 也就是真正的视频帧的宽度。每次copy的大小也应该是真正的宽度。对于这个通道来说,宽度和高度都是亮度通道的一半,每个元素有UV两个信息,所以这个通道每一行占用空间和亮度通道应该是一样的。也就是每一行copy数据的大小是这样算出来的:_outVideoWidth / 2 * 2.

数据读取

数据读取和数据填充正好是相反的操作,操作流程相似,先获取pixelBuffer的一些具体信息,判断信息无误后继续读取数据。

unsigned long planes = CVPixelBufferGetPlaneCount(pixelRef); 若通道数目错误显然逻辑已经错误,无需继续。同样是先锁住BaseAddress,然后获取其bytesPerRowChrominance等信息,然后按行读取数据即可。切记,仍然需要按行读取数据。

补充:从 Image 创建 PixelBuffer

直接附上可运行的代码:


size_t height;
size_t width; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pxbuffer = NULL; CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,
height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
&pxbuffer); NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
NSParameterAssert(pxdata != NULL); CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(pxdata, width,
height, 8, 4 * width, rgbColorSpace,
kCGImageAlphaNoneSkipFirst);
NSParameterAssert(context);
CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
CGImageGetHeight(image)), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context); CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

创建格式爲 kCVPixelFormatType_32ARGB 的 pixelBuffer,创建一个CGContextRef 对象,并将其内部地址设置爲pixelBuffer的内部地址。使用 CGContextDrawImage() 函数将原始图片的数据绘制到我们创建的context上面,完成。


参考资料:

大神的文章,很详细:读写CVPixelBufferRef

Create CVPixelBuffer from YUV with IOSurface backed

How to convert from YUV to CIImage for iOS

How do I export UIImage array as a movie?

最新文章

  1. Linux Mint安装jdk8
  2. iOS开发-- RunLoop的基本概念与例子分析
  3. CSS3之弹性布局
  4. 无DLL远程注入
  5. iOS开发:mac使用svn管理项目
  6. javascript-代码复用模式
  7. MVC中的路由
  8. C++中指针和引用的选择
  9. AJAX背景技术介绍
  10. 通过getResourceAsStream 获得Properties文件属性和属性值
  11. Codeforces Round #554 (Div. 2) C. Neko does Maths (简单推导)
  12. C/C++基础----标准库几个工具库tuple,bitset,正则表达式,随机数,IO库
  13. Dubbo 消费者
  14. postgresql修改配置生效方法
  15. centos 安装LAMP环境后装phpmyadmin
  16. 嵌入式Linux的web视频服务器的构建
  17. Android——用对话框做登陆界面(自定义对话框AlertDialog,多线程,进度条ProgressDialog,ListView,GridView,SharedPreferences存,读数据,存取文本,assets文件)
  18. CSS3动画库——animate.css
  19. .net组件和com组件&amp;托管代码和非托管代码
  20. JavaScript:避免代码的重复执行

热门文章

  1. 201521123073 《Java程序设计》第5周学习总结
  2. 201521123075 《Java程序设计》第3周学习总结
  3. 201521123008《Java程序设计》第二周实验总结
  4. 201521123057 《Java程序设计》第12周学习总结
  5. 201521123122 《java程序设计》第十二周学习总结
  6. 2017年9月19日 JavaScript语法操作
  7. Java实现MD5加密_字符串加密_文件加密
  8. Javascript中的noscript
  9. Ubuntu16.04下安装mysql
  10. Vi快捷操作 vim配置【shell文件格式从windows转换为linux】