Разработчики, занимающиеся обработкой аудио, видео или изображений, обычно более или менее контактируют с libyuv[1] Эта библиотека с открытым исходным кодом, с которой мы занимаемся разработкой аудио и видео. YUV
、RGB
Он часто используется при преобразовании, повороте и масштабировании данных в других форматах. либюв на основе C Реализацию языка можно найти в Windows、Linux、Mac OS、Android、iOS Его можно использовать на нескольких платформах, и он оптимизирован для ускорения набора команд, что приводит к повышению производительности.
Фактически, Apple также предоставляет набор инструментов под названием vImage
родной API 来支持高производительностьобработка данных изображений。vImage
да Accelerate
часть библиотеки, используя традиционные C Реализация языка, ориентированная на высокоуровневую обработку изображений, многие интерфейсы требуют ручного управления памятью, мы основе vImage
Высокая производительность также может быть достигнута RGB
и YUV
Преобразование данных.
Следующий пример кода реализует iOS BGRA
Конвертировать ARGB
,Также напечатано vImage
и libyuv
Время обработки, интерфейс и libyuv
Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.
Если вам нужно изменить формат Конвертировать на RGBA
Подождите других последовательностей и отрегулируйте permuteMap
Просто следуйте приказу.
+ (void)testBGRAToARGB:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer {
CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать vImage Данные реализации Конвертировать:
[self.class BGRAToARGB:CVPixelBufferGetBaseAddress(srcPixelBuffer) src_stride:CVPixelBufferGetBytesPerRow(srcPixelBuffer) dst:CVPixelBufferGetBaseAddress(dstPixelBuffer) dst_stride:CVPixelBufferGetBytesPerRow(dstPixelBuffer) width:CVPixelBufferGetHeight(srcPixelBuffer) height:CVPixelBufferGetWidth(srcPixelBuffer)];
NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать libyuv Данные реализации Конвертировать:
RGBAToARGB(CVPixelBufferGetBaseAddress(srcPixelBuffer),CVPixelBufferGetBytesPerRow(srcPixelBuffer),CVPixelBufferGetBaseAddress(dstPixelBuffer),CVPixelBufferGetBytesPerRow(dstPixelBuffer),CVPixelBufferGetHeight(srcPixelBuffer),CVPixelBufferGetWidth(srcPixelBuffer));
NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
NSLog(@"BGRAToARGB duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);
CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}
+ (int)BGRAToARGB:(const uint8_t *)src src_stride:(int)src_stride
dst:(uint8_t *)dst dst_stride:(int)dst_stride width:(int)width height:(int)height {
if (width == 0 || height == 0 || !src || src_stride == 0 || dst_stride == 0 || !dst) {
return -1;
}
vImage_Buffer srcBufferInfo = {
.width = width,
.height = height,
.rowBytes = src_stride,
.data = src
};
vImage_Buffer dstBufferInfo = {
.width = width,
.height = height,
.rowBytes = dst_stride,
.data = dst
};
const uint8_t permuteMap[4] = {3, 2, 1, 0}; // transfer BGRA to ARGB
return vImagePermuteChannels_ARGB8888(&srcBufferInfo, &dstBufferInfo, permuteMap, kvImageNoFlags);
}
Ниже мы провели несколько тестов и получили трудоемкие результаты:
BGRAToARGB duration: vImage=0.11, yuv=0.13
BGRAToARGB duration: vImage=0.10, yuv=0.10
BGRAToARGB duration: vImage=0.13, yuv=0.09
BGRAToARGB duration: vImage=0.09, yuv=0.10
BGRAToARGB duration: vImage=0.12, yuv=0.13
BGRAToARGB duration: vImage=0.10, yuv=0.10
BGRAToARGB duration: vImage=0.09, yuv=0.08
Анализ ключевых параметров в примере кода:
permuteMap
Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.vImagePermuteChannels_ARGB8888
Параметр исходного формата и целевой формат соответствуют vImage_Buffer
и permuteMap
,каждый vImage_Buffer
настраивать Ширина, высота, количество байтов строки, указатель адреса.CVPixelBuffer
Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress
Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress
Разблокировано.существовать iOS генерал-лейтенант NV12
Отформатированный CVPixelBuffer сохранить как UIImage Если есть такие требования, как извлечение видеокадров, сначала необходимо NV12
Конвертировать BGRA
。Пример кода ниже реализует и сравнивает vImage
и libyuv
производительность,интерфейси libyuv
Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.
Если исходные данные I420
,Метод Конвертировать можно изменить на vImageConvert_420Yp8_Cb8_Cr8ToARGB8888
。
+ (void)testNV12ToBGRA:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer {
vImage_Flags flags = kvImageNoFlags;
static vImage_YpCbCrToARGB info;
static dispatch_once_t onceTokenYUVToBGRA;
dispatch_once(&onceTokenYUVToBGRA, ^{
// vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){16, 128, 235, 240, 255, 0, 255, 1};
vImage_YpCbCrPixelRange pixelRange = {0, 128, 255, 255, 255, 1, 255, 0}; //fullrange - fullrange
vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange,
&info, kvImage420Yp8_CbCr8,
kvImageARGB8888, flags);
});
CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать vImage Данные реализации Конвертировать:
[self.class NV12ToBGRA:CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 0) src_stride_y:CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 0) src_uv:CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 1) src_stride_uv:CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 1) dst:CVPixelBufferGetBaseAddress(dstPixelBuffer) dst_stride:CVPixelBufferGetBytesPerRow(dstPixelBuffer) width:CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0) height:CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0) info:info];
NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать libyuv Данные реализации Конвертировать:
NV12ToARGB(CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 0), CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 0), CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 1), CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 1), CVPixelBufferGetBaseAddress(dstPixelBuffer), CVPixelBufferGetBytesPerRow(dstPixelBuffer), CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0), CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0));
NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
NSLog(@"NV12ToBGRA duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);
CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}
+ (int)NV12ToBGRA:(uint8_t *)src_y src_stride_y:(int)src_stride_y src_uv:(uint8_t*)src_uv src_stride_uv:(int)src_stride_uv dst:(const uint8_t *)dst dst_stride:(int)dst_stride
width:(int)width height:(int)height info:(vImage_YpCbCrToARGB)info {
if (!src_y || src_stride_y == 0 || !src_uv || src_stride_uv == 0 || !dst || dst_stride == 0 || width == 0 || height == 0) {
return -1;
}
vImage_Flags flags = kvImageNoFlags;
vImage_Buffer srcYBufferInfo = {
.width = width,
.height = height,
.rowBytes = src_stride_y,
.data = src_y};
vImage_Buffer srcUVBufferInfo = {
.width = (width + 1) >> 1,
.height = (height + 1) >> 1,
.rowBytes = src_stride_uv,
.data = src_uv};
vImage_Buffer dstBufferInfo = {
.width = width,
.height = height,
.rowBytes = dst_stride,
.data = dst};
const uint8_t permuteMap[4] = {3, 2, 1, 0}; // transfer ARGB to BGRA
return vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYBufferInfo, &srcUVBufferInfo, &dstBufferInfo, &info,
permuteMap, 255, flags);
}
Ниже мы провели несколько тестов и получили трудоемкие результаты:
NV12ToBGRA duration: vImage=0.16, yuv=0.13
NV12ToBGRA duration: vImage=0.13, yuv=0.12
NV12ToBGRA duration: vImage=0.14, yuv=0.13
NV12ToBGRA duration: vImage=0.13, yuv=0.13
NV12ToBGRA duration: vImage=0.15, yuv=0.11
NV12ToBGRA duration: vImage=0.12, yuv=0.11
NV12ToBGRA duration: vImage=0.12, yuv=0.11
NV12ToBGRA duration: vImage=0.14, yuv=0.11
NV12ToBGRA duration: vImage=0.13, yuv=0.10
Анализ ключевых параметров в примере кода:
vImageConvert_YpCbCrToARGB_GenerateConversion
需要настраивать 601
、709
,чтобы и FullRange
、VideoRange
。kvImage420Yp8_CbCr8
представлять NV12
。permuteMap
Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.vImageConvert_420Yp8_CbCr8ToARGB8888
Функция по умолчанию Конвертировать: ARGB
,Но цель Конвертироватьдля BGRA
,所к需调整настраивать permuteMap
。CVPixelBuffer
Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress
Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress
Разблокировано.srcUVBufferInfo
Иглы ширины и высоты будут округлены до нечетных чисел и выровнены. libyuv
。когда UIImage Конвертироватьдля NV12
Отформатированный Когда используется CVPixelBuffer, например, при смешивании изображений и видео, изображение Конвертировать должно быть NV12
Чтобы закодировать, вам нужно сначала BGRA
Конвертировать NV12
。Пример кода ниже реализует и сравнивает vImage
и libyuv
производительность,интерфейси libyuv
Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.
Если вам нужно Конвертировать для I420
,Метод Конвертировать можно изменить на vImageConvert_ARGB8888To420Yp8_Cb8_Cr8
。
+ (void)testBGRAToNV12:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer{
vImage_Flags flags = kvImageNoFlags;
static vImage_ARGBToYpCbCr info;
static dispatch_once_t onceTokenBGRAToNV12;
dispatch_once(&onceTokenBGRAToNV12, ^{
// vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){16, 128, 235, 240, 255, 0, 255, 1};
vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){0, 128, 255, 255, 255, 1, 255, 0};
vImageConvert_ARGBToYpCbCr_GenerateConversion(
kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2,
&pixelRange,
&info,
kvImageARGB8888,
kvImage420Yp8_CbCr8,
0);
});
CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать vImage Данные реализации Конвертировать:
[self.class BGRAToNV12:CVPixelBufferGetBaseAddress(srcPixelBuffer) src_stride:CVPixelBufferGetBytesPerRow(srcPixelBuffer) dst_y:CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0) dst_stride_y:CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 0) dst_uv:CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1) dst_stride_uv:CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 1) width:CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0) height:CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0) info:info];
NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;
// использовать libyuv Данные реализации Конвертировать:
ARGBToNV12(CVPixelBufferGetBaseAddress(srcPixelBuffer), CVPixelBufferGetBytesPerRow(srcPixelBuffer), CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0), CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 0), CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1), CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 1), CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0), CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0));
NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
NSLog(@"BGRAToNV12 duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);
CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}
+ (int)BGRAToNV12:(const uint8_t*)src src_stride:(int)src_stride
dst_y:(uint8_t*)dst_y dst_stride_y:(int)dst_stride_y dst_uv:(uint8_t*)dst_uv dst_stride_uv:(int)dst_stride_uv width:(int)width height:(int)height info:(vImage_ARGBToYpCbCr)info{
if(!src || src_stride == 0 || !dst_y || dst_stride_y == 0 || !dst_uv || dst_stride_uv == 0 || width == 0 || height == 0){
return -1;
}
vImage_Buffer srcBufferInfo = {
.width = width,
.height = height,
.rowBytes = src_stride,
.data = src};
vImage_Buffer yBufferInfo = {
.width = width,
.height = height,
.rowBytes = dst_stride_y,
.data = dst_y};
vImage_Buffer uvBufferInfo = {
.width = (width + 1) >> 1,
.height = (height + 1) >> 1,
.rowBytes = dst_stride_uv,
.data = dst_uv};
const uint8_t permuteMap[4] = { 3, 2, 1, 0}; // transfer ARGB to BGRA
return vImageConvert_ARGB8888To420Yp8_CbCr8(
&srcBufferInfo,
&yBufferInfo,
&uvBufferInfo,
&info,
permuteMap,
kvImageDoNotTile);
}
Ниже мы провели несколько тестов и получили трудоемкие результаты:
BGRAToNV12 duration: vImage=0.16, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.19
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.16, yuv=0.19
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12
Анализ ключевых параметров в примере кода:
vImageConvert_ARGBToYpCbCr_GenerateConversion
需要настраивать 601
、709
,чтобы и FullRange
、VideoRange
。kvImage420Yp8_CbCr8
представлять NV12
。permuteMap
Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.vImageConvert_ARGB8888To420Yp8_CbCr8
Функция по умолчанию Конвертировать: ARGB
,Но цель Конвертироватьдля BGRA
,所к需调整настраивать permuteMap
。CVPixelBuffer
Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress
Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress
Разблокировано.uvBufferInfo
Иглы ширины и высоты будут округлены до нечетных чисел и выровнены. libyuv
。[1]
libyuv: https://chromium.googlesource.com/libyuv/libyuv