Как преобразовать kCVPixelFormatType_420YpCbCr8BiPlanarFullRange Buffer в YUV420, используя библиотеку libyuv в ios?
Я захватил видео с помощью AVFoundation . Я установил (настройка видео) и получить в выходной буфер буфера kCVPixelFormatType_420YpCbCr8BiPlanarFullRange. Но мне нужен формат YUV420 для дальнейшей обработки.
Для этого я использую libyuv framework.
LIBYUV_API
int NV12ToI420(const uint8* src_y, int src_stride_y,
const uint8* src_uv, int src_stride_uv,
uint8* dst_y, int dst_stride_y,
uint8* dst_u, int dst_stride_u,
uint8* dst_v, int dst_stride_v,
int width, int height);
libyuv::NV12ToI420(src_yplane, inWidth ,
src_uvplane, inWidth,
dst_yplane, inWidth,
dst_vplane, inWidth / 2,
dst_uplane, inWidth / 2,
inWidth, inHeight);
Но я получаю выходной буфер полностью зеленого цвета? я сделал любую ошибку для этого процесса, пожалуйста, помогите мне?
4 ответа
Вам нужно конвертировать ваши данные в I420, я тоже обрабатываю камеру, но на Android. Я думаю, что это должно быть похоже на iOS. Android raw камера имеет формат NV21 или NV16, я конвертирую из NV21 или NV16 в YV12, I420 почти такой же, как YV12:
BYTE m_y[BIG_VIDEO_CX * BIG_VIDEO_CY],
m_u[(BIG_VIDEO_CX/2) * (BIG_VIDEO_CY/2)],
m_v[(BIG_VIDEO_CX/2) * (BIG_VIDEO_CY/2)];
void NV21_TO_YV12(BYTE *data)
{
int width = BIG_VIDEO_CX;
int height = BIG_VIDEO_CY;
m_y2=data;
data=&data[width*height];
for (uint32_t i=0; i<(width/2)*(height/2); ++i)
{
m_v[i]=*data;
m_u[i]=*(data+1);
data+=2;
}
}
void NV16_TO_YV12(BYTE *data)
{
int width = BIG_VIDEO_CX;
int height = BIG_VIDEO_CY;
m_y2=data;
const BYTE* src_uv = (const BYTE*)&data[width*height];
BYTE* dst_u = m_u;
BYTE* dst_v = m_v;
for (uint32_t y=0; y<height/2; ++y)
{
const BYTE* src_uv2 = src_uv + width;
for (uint32_t x=0; x<width/2; ++x)
{
dst_u[x]=(src_uv[0]+src_uv2[0]+1)>>1;
dst_v[x]=(src_uv[1]+src_uv2[1]+1)>>1;
src_uv+=2;
src_uv2+=2;
}
src_uv=src_uv2;
dst_u+=width/2;
dst_v+=width/2;
}
}
Выглядит правильно. Убедитесь, что ваш src_uvplane указывает на src_yplane + inWidth * inHeight
Вот как я делаю это на iOS в моем captureOutput после получения необработанного видеокадра из AVCaptureSession(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange):
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
CVImageBufferRef videoFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
CFRetain(sampleBuffer);
CVPixelBufferLockBaseAddress(videoFrame, 0);
size_t _width = CVPixelBufferGetWidth(videoFrame);
size_t _height = CVPixelBufferGetHeight(videoFrame);
const uint8* plane1 = (uint8*)CVPixelBufferGetBaseAddressOfPlane(videoFrame,0);
const uint8* plane2 = (uint8*)CVPixelBufferGetBaseAddressOfPlane(videoFrame,1);
size_t plane1_stride = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 0);
size_t plane2_stride = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 1);
size_t plane1_size = plane1_stride * CVPixelBufferGetHeightOfPlane(videoFrame, 0);
size_t plane2_size = CVPixelBufferGetBytesPerRowOfPlane (videoFrame, 1) * CVPixelBufferGetHeightOfPlane(videoFrame, 1);
size_t frame_size = plane1_size + plane2_size;
uint8* buffer = new uint8[ frame_size ];
uint8* dst_u = buffer + plane1_size;
uint8* dst_v = dst_u + plane1_size/4;
// Let libyuv convert
libyuv::NV12ToI420(/*const uint8* src_y=*/plane1, /*int src_stride_y=*/plane1_stride,
/*const uint8* src_uv=*/plane2, /*int src_stride_uv=*/plane2_stride,
/*uint8* dst_y=*/buffer, /*int dst_stride_y=*/plane1_stride,
/*uint8* dst_u=*/dst_u, /*int dst_stride_u=*/plane2_stride/2,
/*uint8* dst_v=*/dst_v, /*int dst_stride_v=*/plane2_stride/2,
_width, _height);
CVPixelBufferUnlockBaseAddress(videoFrame, 0);
CFRelease( sampleBuffer)
// TODO: call your method here with 'buffer' variable. note that you need to deallocated the buffer after using it
}
Я сделал код немного более наглядным для ясности.
Android является NV21, который libyuv поддерживает как с Arm, так и с Intel. Он также может вращаться на 90, 180 или 270, как часть преобразования, если это необходимо для ориентации. Оптимизированная версия Arm примерно в 2 раза быстрее, чем C
С
NV12ToI420_Opt (782 мс)
NV21ToI420_Opt (764 мс)
Arm (неоновая оптимизация)
NV12ToI420_Opt (398 мс)
NV21ToI420_Opt (381 мс)
Любопытно, вы используете NV16 на Android. Я бы ожидал NV61 для согласованности с NV21. Ваш код выглядит правильно, но будет приятно оптимизировать Neon, используя vrhadd.u8. Если вы хотите это увидеть, напишите вопрос libyuv. https://code.google.com/p/libyuv/issues/list