簡述
平時(shí)工作中使用 CoreVideo 也不算少扶叉,但是一直沒有系統(tǒng)完整地閱讀梳理過它的官方文檔。趁著這段時(shí)間較為閑暇,就系統(tǒng)性的學(xué)習(xí)一下官方文檔积锅,并且記錄一些常見用法。
常見用法
復(fù)制 CVPixelBuffer
經(jīng)常我們需要拷貝一個(gè) CVPixelBuffer养盗,但是沒有直接拷貝的方法缚陷。這里就是一個(gè)緩沖拷貝的實(shí)現(xiàn),并且對(duì)源和目標(biāo)為不同大小的緩沖情況進(jìn)行了處理往核。
bool copyPixelBuffer(CVPixelBufferRef src, CVPixelBufferRef dst) {
bool ret = true;
CVPixelBufferLockBaseAddress(src, kCVPixelBufferLock_ReadOnly);
unsigned char* pb = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(src, 0);
int height = (int)CVPixelBufferGetHeight(src);
int stride = (int)CVPixelBufferGetBytesPerRow(src);
int size = (int)CVPixelBufferGetDataSize(src);
while (1) {
CVReturn cvRet = CVPixelBufferLockBaseAddress(dst, 0);
if (cvRet != kCVReturnSuccess) {
ret = false;
break;
}
int dst_height = (int)CVPixelBufferGetHeight(dst);
int dst_stride = (int)CVPixelBufferGetBytesPerRow(dst);
int dst_size = (int)CVPixelBufferGetDataSize(dst);
if (stride == dst_stride && dst_size == size) {
unsigned char* temp = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(dst, 0);
memcpy(temp, pb, size);
} else {
int copy_height = height > dst_height ? dst_height : height;
int copy_stride = stride > dst_stride ? dst_stride : stride;
unsigned char* offset_dst = (unsigned char*)CVPixelBufferGetBaseAddressOfPlane(dst, 0);
unsigned char* offset_src = pb;
for (int i = 0; i < copy_height; i++) {
memcpy(offset_dst, offset_src, copy_stride);
offset_src += stride;
offset_dst += dst_stride;
}
}
CVPixelBufferUnlockBaseAddress(dst, 0);
break;
}
CVPixelBufferUnlockBaseAddress(src, kCVPixelBufferLock_ReadOnly);
return ret;
}
通過 CGImage 創(chuàng)建 CVPixelBuffer
CVPixelBufferRef pixelBufferFromCGImage(CGImageRef image) {
CVPixelBufferRef pxbuffer = NULL;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t bytesPerRow = CGImageGetBytesPerRow(image);
CFDataRef dataFromImageDataProvider = CGDataProviderCopyData(CGImageGetDataProvider(image));
GLubyte *imageData = (GLubyte *)CFDataGetBytePtr(dataFromImageDataProvider);
CVPixelBufferCreateWithBytes(kCFAllocatorDefault,width,height,kCVPixelFormatType_32BGRA,imageData,bytesPerRow,NULL,NULL,(__bridge CFDictionaryRef)options,&pxbuffer);
CFRelease(dataFromImageDataProvider);
return pxbuffer;
}
CVPixelBuffer 轉(zhuǎn) CGImage
CGImageRef createCGImageFromCVPixelBuffer(CVPixelBufferRef pixels) {
CVPixelBufferLockBaseAddress(pixels, kCVPixelBufferLock_ReadOnly);
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixels];
CIContext *temporaryContext = [CIContext contextWithOptions:nil];
CGImageRef videoImage = [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(pixels), CVPixelBufferGetHeight(pixels))];
CVPixelBufferUnlockBaseAddress(pixels, kCVPixelBufferLock_ReadOnly);
return videoImage;
}
創(chuàng)建BGRA/I420/NV12 格式的 CVPixelBufferPool
+ (bool)create32BGRAPixelBufferPool:(CVPixelBufferPoolRef*)pool width:(int)width height:(int)height {
CFDictionaryRef empty; // empty value for attr value.
CFMutableDictionaryRef attrs;
empty = CFDictionaryCreate(kCFAllocatorDefault,
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionary
SInt32 cvPixelFormatTypeValue = kCVPixelFormatType_32BGRA;
CFNumberRef cfPixelFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvPixelFormatTypeValue)));
SInt32 cvWidthValue = width;
CFNumberRef cfWidth = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvWidthValue)));
SInt32 cvHeightValue = height;
CFNumberRef cfHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvHeightValue)));
attrs = CFDictionaryCreateMutable(kCFAllocatorDefault,
4,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
CFDictionarySetValue(attrs, kCVPixelBufferPixelFormatTypeKey, cfPixelFormat);
CFDictionarySetValue(attrs, kCVPixelBufferWidthKey, cfWidth);
CFDictionarySetValue(attrs, kCVPixelBufferHeightKey, cfHeight);
CVReturn ret = CVPixelBufferPoolCreate(kCFAllocatorDefault, nil, attrs, pool);
CFRelease(attrs);
CFRelease(empty);
CFRelease(cfPixelFormat);
CFRelease(cfWidth);
CFRelease(cfHeight);
if (ret != kCVReturnSuccess) {
return false;
}
return true;
}
+ (bool)createI420PixelBufferPool:(CVPixelBufferPoolRef*)pool width:(int)width height:(int)height {
CFDictionaryRef empty; // empty value for attr value.
CFMutableDictionaryRef attrs;
empty = CFDictionaryCreate(kCFAllocatorDefault,
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionary
SInt32 cvPixelFormatTypeValue = kCVPixelFormatType_420YpCbCr8PlanarFullRange;
CFNumberRef cfPixelFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvPixelFormatTypeValue)));
SInt32 cvWidthValue = width;
CFNumberRef cfWidth = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvWidthValue)));
SInt32 cvHeightValue = height;
CFNumberRef cfHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvHeightValue)));
attrs = CFDictionaryCreateMutable(kCFAllocatorDefault,
5,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
CFDictionarySetValue(attrs, kCVPixelBufferPixelFormatTypeKey, cfPixelFormat);
CFDictionarySetValue(attrs, kCVPixelBufferWidthKey, cfWidth);
CFDictionarySetValue(attrs, kCVPixelBufferHeightKey, cfHeight);
#if TARGET_OS_IOS
CFDictionarySetValue(attrs, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue);
#elif TARGET_OS_OSX
CFDictionarySetValue(attrs, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
#endif
CVReturn ret = CVPixelBufferPoolCreate(kCFAllocatorDefault, nil, attrs, pool);
CFRelease(attrs);
CFRelease(empty);
CFRelease(cfPixelFormat);
CFRelease(cfWidth);
CFRelease(cfHeight);
if (ret != kCVReturnSuccess) {
return false;
}
return true;
}
+ (bool)createNV12PixelBufferPool:(CVPixelBufferPoolRef*)pool width:(int)width height:(int)height {
CFDictionaryRef empty; // empty value for attr value.
CFMutableDictionaryRef attrs;
empty = CFDictionaryCreate(kCFAllocatorDefault,
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionary
SInt32 cvPixelFormatTypeValue = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
CFNumberRef cfPixelFormat = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvPixelFormatTypeValue)));
SInt32 cvWidthValue = width;
CFNumberRef cfWidth = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvWidthValue)));
SInt32 cvHeightValue = height;
CFNumberRef cfHeight = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, (const void*)(&(cvHeightValue)));
attrs = CFDictionaryCreateMutable(kCFAllocatorDefault,
5,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
CFDictionarySetValue(attrs, kCVPixelBufferPixelFormatTypeKey, cfPixelFormat);
CFDictionarySetValue(attrs, kCVPixelBufferWidthKey, cfWidth);
CFDictionarySetValue(attrs, kCVPixelBufferHeightKey, cfHeight);
#if TARGET_OS_IOS
CFDictionarySetValue(attrs, kCVPixelBufferOpenGLESCompatibilityKey, kCFBooleanTrue);
#elif TARGET_OS_OSX
CFDictionarySetValue(attrs, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
#endif
CVReturn ret = CVPixelBufferPoolCreate(kCFAllocatorDefault, nil, attrs, pool);
CFRelease(attrs);
CFRelease(empty);
CFRelease(cfPixelFormat);
CFRelease(cfWidth);
CFRelease(cfHeight);
if (ret != kCVReturnSuccess) {
return false;
}
return true;
}
官方文檔
DataProcessing
CVBuffer
CV 緩沖基類箫爷。派生了 CVImageBuffer。
Attachment
You can attach any Core Foundation object to a Core Video buffer to store additional information.
您可以將任何Core Foundation對(duì)象附加到 CVBuffer 以存儲(chǔ)其他信息聂儒。
提供附加信息的增刪改查及復(fù)制方法虎锚。
Retain Count
修改 CVBuffer 的引用計(jì)數(shù)值。支持傳空衩婚。
CVImageBuffer
提供管理不同類型圖像的接口窜护。
Inspect
查看 Buffer 參數(shù),如顏色空間非春、顯示大小柱徙、編碼尺寸缓屠、翻轉(zhuǎn)狀態(tài)等。
Create Color Speaces
由附件創(chuàng)建色彩空間护侮。
色彩空間由附件字典中信息生成敌完。
Data Type
CVImageBufferRef
Converting Between Strings and Integer Code Points
看上去是新添加上去的,轉(zhuǎn)換相關(guān)的方法羊初。
CVPixelBuffer
表示保存在主存的像素滨溉。
Create
支持以各種方式創(chuàng)建 PixelBuffer。
如直接初始化一塊空的像素緩沖區(qū)长赞,或者直接引用現(xiàn)有的一整段內(nèi)存將其初始化為緩沖业踏,又或者通過內(nèi)存中的幾個(gè)平面數(shù)據(jù)初始化緩沖,或者使用IOSurface(跨進(jìn)程共享)初始化一個(gè)緩沖涧卵。
那么需要注意的是勤家,后幾種方式由于是引用了一段內(nèi)存,所以緩沖釋放不會(huì)釋放引用的內(nèi)存柳恐,而使通過回調(diào)通知開發(fā)者處理緩沖釋放事件伐脖。
Inspect
查看緩沖參數(shù),如基址乐设、平面基址讼庇、寬高、每行字節(jié)數(shù)近尚,判斷緩沖類型蠕啄、平面數(shù)、平面大小等戈锻。
Modify
主要是鎖定基址和解鎖基址兩個(gè)方法歼跟。
需要在 CPU 訪問緩存前后分別調(diào)用。如果使用 GPU 訪問格遭,則不需要鎖定哈街,并且鎖定會(huì)影響性能。
Retain Count
同 CVBufferRetain
CVPixelBufferPool
緩沖池拒迅,用于優(yōu)化內(nèi)存分配的性能骚秦。
Create
創(chuàng)建緩沖池,通過緩沖池創(chuàng)建 PixelBuffer璧微。
并且可以指定創(chuàng)建出的像素緩沖默認(rèn)攜帶指定的 Attributes作箍。
Flush
釋放所有未使用的緩沖區(qū)。
Inspect
獲取緩沖池屬性字典前硫、緩沖屬性字典胞得。
Retain Count
同上
CVPixelFormatDescription
像素格式說明。在需要自定義像素格式時(shí)开瞭,才使用該說明對(duì)象懒震。
Create
創(chuàng)建格式說明。
Retrieve
檢索已知的定義的所有像素格式描述嗤详。
Time Management
Inspect Host Clock
檢查主機(jī)時(shí)鐘个扰,包括當(dāng)前系統(tǒng)時(shí)間、系統(tǒng)時(shí)間更新頻率葱色、系統(tǒng)時(shí)間最小增量递宅。
CVTime
用來表示時(shí)間的數(shù)據(jù)結(jié)構(gòu)。
typedef struct
{
int64_t timeValue;//時(shí)間分母
int32_t timeScale;//時(shí)間分子
int32_t flags;//可以表示起始時(shí)間苍狰,或者未知時(shí)間办龄、無限時(shí)間
} CVTime;
CVTimeStamp
用于定義顯示時(shí)間戳的數(shù)據(jù)結(jié)構(gòu)。
CVDisplayLink
類似 CADisplayLink淋昭。
是一個(gè)與屏幕刷新同步調(diào)用的 Timer俐填。
CVMetalTextureCache
Create
創(chuàng)建新紋理緩存、由現(xiàn)有圖片生成紋理緩存翔忽、回收/整理當(dāng)前緩存英融。
CVMetalTexture
Inspect
獲取相關(guān)屬性
OpenGL/OpenGL ES
廢棄