前言
ios調(diào)用系統(tǒng)框架采集出的視頻YUV格式為NV12.
為滿足不同業(yè)務(wù)需求,我們需要把nv12轉(zhuǎn)換為i420或者rgba等格式.
libYUV庫和ffmpeg都可以幫助我們輕松搞定.(推薦libyuv庫,性能比ffmpeg高出很多).
libyuv
libyuv是Google開源的實現(xiàn)各種YUV與RGB之間相互轉(zhuǎn)換挖炬、旋轉(zhuǎn)、縮放的庫。它是跨平臺的,可在Windows综膀、Linux抓歼、Mac萄喳、Android等操作系統(tǒng),x86、x64茵休、arm架構(gòu)上進行編譯運行,支持SSE、AVX、NEON等SIMD指令加速.
使用libyuv
libyuv下載和編譯網(wǎng)上教程較多,可去官網(wǎng)下載
我們來看一下NV12轉(zhuǎn)換為i420的接口
// Convert NV12 to I420.
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);
src_ 為我們需要轉(zhuǎn)換的NV12格式數(shù)據(jù),dst_ 為轉(zhuǎn)換后的i420數(shù)據(jù)
那么stride代表啥???
跨距-stride
我們都知道現(xiàn)在計算機的cpu都是32位或者64位的cpu,他們一次最少讀取4、8個字節(jié)阎毅,如果少于這些蒲拉,反而要做一些額外的工作燃领,會花更長的時間曼库。所有會有一個概念叫做內(nèi)存對齊,將結(jié)構(gòu)體的長度設(shè)為4、8的倍數(shù)。
跨距也是因為同樣的理由出現(xiàn)的。因為圖像的操作通常按行操作的稠歉,如果圖像的所有數(shù)據(jù)都緊密排列横媚,那么會發(fā)生非常多次的讀取非對齊內(nèi)存孝宗。會影響效率婚被。而圖像的處理本就是一個分秒必爭的操作,所以為了性能的提高就引入了跨距這個概念。
跨距就是指圖像中的一行圖像數(shù)據(jù)所占的存儲空間的長度驶睦,它是一個大于等于圖像寬度的內(nèi)存對齊的長度旗闽。
這樣每次以行為基準讀取數(shù)據(jù)的時候就能內(nèi)存對齊,雖然可能會有一點內(nèi)存浪費,但是在內(nèi)存充裕的今天已經(jīng)無所謂了。
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
//表示開始操作數(shù)據(jù)
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
//圖像寬度(像素)
size_t pixelWidth = CVPixelBufferGetWidth(pixelBuffer);
//圖像高度(像素)
size_t pixelHeight = CVPixelBufferGetHeight(pixelBuffer);
//獲取CVImageBufferRef中的y數(shù)據(jù)
uint8_t *y_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
//獲取CMVImageBufferRef中的uv數(shù)據(jù)
uint8_t *uv_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
//y stride
size_t plane1_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 0);
//uv stride
size_t plane2_stride = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 1);
//y_size
size_t plane1_size = plane1_stride * CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
//uv_size
size_t plane2_size = CVPixelBufferGetBytesPerRowOfPlane (pixelBuffer, 1) * CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
//yuv_size
size_t frame_size = plane1_size + plane2_size;
uint8* buffer = malloc(frame_size);
uint8* dst_u = buffer + plane1_size;
uint8* dst_v = dst_u + plane1_size/4;
// Let libyuv convert
NV12ToI420(/*const uint8* src_y=*/y_frame, /*int src_stride_y=*/plane1_stride,
/*const uint8* src_uv=*/uv_frame, /*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,
pixelWidth, pixelHeight);
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
free(buffer);
以上為NV12轉(zhuǎn)化為i420的所有代碼.