AV Foundation ⑯ 视频处理与CMSampleBuf
视频处理
使用 AVCaptureMovieFileOutput
虽然可以便捷的捕捉视频数据,但是它无法同视频数据进行交互,而交互又是许多场景所需要的功能。当需要使用底层控制时,就会用到框架提供的最底层的视频捕捉输出 AVCaptureVideoDataOutput
。
AVCaptureVideoDataOutput
是一个 AVCaptureOutput
的子类,可以直接访问摄像头传感器捕捉到的视频帧。这是一个强大的功能,因为这样我们就完全控制了视频数据的格式、时间和元数据,可以按照需求操作视频内容。大部分情况下,处理过程都是使用 OpenGL ES 或 Core Image,有时, Quartz 也可以满足一些简单的处理要求。
使用 AVCaptureVideoDataOutput
输出的对象需要通过 AVCapatureVideoDataOutputSampleBufferDelegate
协议包含视频数据,其定义了下面两个方法
-
captureOutput:didOutputSampleBuffer:fromConnection:
每当有一个新的视频帧写入时该方法就会被调用。数据会基于视频数据输出的
videoSettings
属性进行解码或重新编码 -
captureOutput:didDropSampleBuffer:fromConnection:
每当有一个迟到的视频帧被丢弃时该方法就会被调用。通常是因为在
didOutputSampleBuffer
调用中消耗了太多处理时间就会调用该方法。
这两个方法最重要的参数与 sample buffer 相关。 sample buffer 以 CMSampleBuffer
对象的形式提供。
CMSampleBuffer
CMSampleBuffer
是一个由 Core Media 框架提供的 Core Foundation 风格的框架,用于在媒体管道中传输数字样板。 CMSampleBuffer
的角色是将基础的样本数据进行封装并提供格式和时间信息,还会加上所有在转换和处理数据时用到的元数据。
样本数据
CMSampleBuffer
中会包含一个 CVPixelBuffer
,它是带有单个视频帧原始像素数据的 Core Video 对象。下面示例展示了如何直接操作 CVPixelBuffer
的内容为捕捉到的图片 buffer
应用一个灰度效果:
const int BYTES_PER_PIXEL = 4;
CMSampleBuffer sampleBuffer1 = <#sample buffer#>
//获取基本的 CVPixelBuffer,其保存的是像素数据
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
//获取相应内存块的锁
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
//确定宽高
size_t bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
size_t bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
// 获取基址指针
unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);
unsigned char grayPixel;
//遍历像素点
for(int row = 0; row < bufferHeight; row++){
for(int column = 0; column < bufferWidth; column++){
//执行灰度平均
grayPixel = (pixel[0] + pixel[1] + pixel[2]) / 3;
pixel[0] = pixel[1] = pixel[2] = grayPixel;
pixel += BYTES_PER_PIXEL;
}
}
//释放锁
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
格式描述
除了原始媒体样本外, CMSampleBuffer
还提供了以 CMFormatDescription
对象的形式来访问样本的格式信息。 CMFormatDescription.h
定义了大量函数用于访问媒体样本的更多细节。在头文件中带有 CMFormatDescription
前缀的函数一般可以用于所有的媒体类型,还有前缀为 CMVideoFormaDescription
和 CMAuidoFormatDescription
的函数分别适用于获取视频和音频的细节。下面是使用 CMFormatDescription
来区别音频和视频数据的一个示例:
CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer);
CMMediaType mediaType = CMFormatDescriptionGetMediaType(formatDescription);
if(mediaType == kCMMediaType_Video){
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
}else if(mediaType == kCMMediaType_Video){
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
}
时间信息
CMSampleBuffer
还定义了关于媒体样本的时间信息。可以分别使用 CMSampleBufferGetPresentationTimeStamp
函数和 CMSampleBufferGetDecodeTimeStamp
函数提取时间信息来得到原始的表示时间戳和解码时间戳。
附加的元数据
Core Media 还在 CMAttachment.h
中定义了一个 CMAttachment
形式的元数据协议。 其提供了读取和写入底层元数据的基础架构,比如可交互图片文件格式标签。