繼上篇Camera數(shù)據(jù)采集適配時會涉及到的YUV數(shù)據(jù)的剪切與方向感應(yīng)時的旋轉(zhuǎn)處理(對YUV數(shù)據(jù)不懂的可以網(wǎng)上搜索相關(guān)的文章),后面還有MediaCodec硬編碼時,不同的手機(jī)可能還需要將NV21數(shù)據(jù)轉(zhuǎn)換成NV12,I420或者YV12,這里就講述NV21與yuv中另外三種NV12,I420,YV12的相互轉(zhuǎn)換,剪切,旋轉(zhuǎn)操作
android Camera(一):采集與預(yù)覽尺寸適配
1:對這幾種格式的轉(zhuǎn)換,網(wǎng)上博客的寫法也是很多, 單單的格式轉(zhuǎn)換,或者單單旋轉(zhuǎn)角度鏡像旋轉(zhuǎn),或者單單剪切都還算比較簡單的,但是如果要將YUV數(shù)據(jù)即要轉(zhuǎn)換同時又要旋轉(zhuǎn)角度鏡像旋轉(zhuǎn),又要裁剪的話就是非常復(fù)雜的算法了
測試一個1280*720大小的YUV數(shù)據(jù)在旋轉(zhuǎn)操作時可能就要20ms的時間(OPPO A57測試,高性能手機(jī)會非常快),如果分三部操作時就大概需要60ms的時間了, 對于一個最普通的按25幀率的采集來算,平均一秒 1000 / 25 = 40平均每40毫秒就采集一幀, 光轉(zhuǎn)換旋轉(zhuǎn)操作就超過了幀的時間,更何況MediaCodec編碼也是要耗時的.
2:libyuv的使用
其實(shí)對于這種高效率的算法, Google已經(jīng)開源了一個叫做libyuv的庫(C++)專門用于YUV數(shù)據(jù)的處理,里邊提供了很多YUV數(shù)據(jù)類型之間的轉(zhuǎn)換,旋轉(zhuǎn),鏡像,剪切算法.
這里也有個疑問,libyuv是個高效率的算法鹉勒,為何里邊找不到nv12或者nv21的旋轉(zhuǎn)與鏡像操作方法(有懂的請不吝賜教),只能根據(jù)libyuv中的NV12與I420之間的旋轉(zhuǎn)源碼算法另外寫了一個nv12的旋轉(zhuǎn)鏡像操作代碼, 提供nv12或者nv21所有相關(guān)方法(nv12與nv21算法大致都相似,只是UV與VU的區(qū)別)
源碼入口,nv21與nv12,yuv12,i420之間的轉(zhuǎn)換,裁剪,旋轉(zhuǎn),鏡像并同時進(jìn)行的操作https://github.com/youxiaochen/CameraMedia
直接上代碼
RotateNV_UV.h,提供NV21(NV12)的UV旋轉(zhuǎn)鏡像操作方法, 所有YUV類型中Y數(shù)據(jù)的操作算法都是一樣的
/**
* Created by you on 2018-03-06. NV21,NV12之間的UV旋轉(zhuǎn)操作
* NV21與NV12區(qū)別只是UV與VU交差的區(qū)別,因此以下方法NV21,NV12可通用
* 所有_X的交叉即可用作NV12與NV21之間的UV轉(zhuǎn)換
*/
#ifndef CAMERAMEDIACODEC_ROTATENV_UV_H
#define CAMERAMEDIACODEC_ROTATENV_UV_H
#include <libyuv.h>
/**
* 適合NV21或者NV12的UV數(shù)據(jù)的操作
*/
void TransposeWxH_C2(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 適合NV21或者NV12的UV數(shù)據(jù)的操作,并轉(zhuǎn)換UV順序
*/
void TransposeWxH_C2_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 鏡面翻轉(zhuǎn)uint8_t類型的數(shù)據(jù),并交叉變換一個步長的順序,適合UV與VU之間的鏡面轉(zhuǎn)換, 與源碼稍作修改
*/
void MirrorRow_C_X(const uint8_t* src,
uint8_t* dst,
int width);
/**
* 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)90
* @param width UV數(shù)據(jù)的寬
* @param height UV數(shù)據(jù)的高
*/
void RotateNV_UV90(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)270
* @param width UV數(shù)據(jù)的寬
* @param height UV數(shù)據(jù)的高
*/
void RotateNV_UV270(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)180
*/
void RotateNV_UV180(const uint8_t* src,
int src_stride,
uint8_t* dst,
int dst_stride,
int width,
int height);
/**
* 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)90,并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
* @param width UV數(shù)據(jù)的寬
* @param height UV數(shù)據(jù)的高
*/
void RotateNV_UV90_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)270, 并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
* @param width UV數(shù)據(jù)的寬
* @param height UV數(shù)據(jù)的高
*/
void RotateNV_UV270_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 同時將NV21或者NV12的UV數(shù)據(jù)旋轉(zhuǎn)180, 并交叉轉(zhuǎn)換UV順序, 可用作NV12與NV21之間的UV互轉(zhuǎn)
*/
void RotateNV_UV180_X(const uint8_t* src,
int src_stride,
uint8_t* dst,
int dst_stride,
int width,
int height);
/**
* NV21的數(shù)據(jù)拷貝
*/
void NV21Copy(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_vu,
int src_stride_vu,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_vu,
int dst_stride_vu,
int width,
int height);
/**
* NV21旋轉(zhuǎn)
*/
void NV21Rotate(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_vu,
int src_stride_vu,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_vu,
int dst_stride_vu,
int width,
int height,
enum libyuv::RotationMode mode);
#endif //CAMERAMEDIACODEC_ROTATENV_UV_H
ConvertNV.h 這里是NV12同時旋轉(zhuǎn),轉(zhuǎn)換與裁剪的相關(guān)操作
/**
* Created by you on 2018-09-06. NV21,NV12之間的UV剪切并旋轉(zhuǎn)操作
* NV21與NV12區(qū)別只是UV與VU交差的區(qū)別,因此以下方法NV21,NV12可通用
*/
#include "RotateNV_UV.h"
#ifndef CAMERAMEDIACODEC_CONVERTNV_H
#define CAMERAMEDIACODEC_CONVERTNV_H
/**
* 同時剪切NV21數(shù)據(jù)并旋轉(zhuǎn)
*/
void ConvertNV21(const uint8_t* sample,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_vu,
int dst_stride_vu,
int crop_x,
int crop_y,
int src_width,
int src_height,
int crop_width,
int crop_height,
libyuv::RotationMode mode);
/**
* 同時NV21轉(zhuǎn)NV12并旋轉(zhuǎn)
*/
void NV21ToNV12Rotate(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_uv,
int src_stride_uv,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_uv,
int dst_stride_uv,
int width,
int height,
libyuv::RotationMode mode);
/**
* 同時裁剪NV21轉(zhuǎn)NV12并旋轉(zhuǎn)
*/
void ConvertNV21ToNV12(const uint8_t* sample,
uint8_t* dst_y,
int dst_stride_y,
uint8_t* dst_uv,
int dst_stride_uv,
int crop_x,
int crop_y,
int src_width,
int src_height,
int crop_width,
int crop_height,
libyuv::RotationMode mode);
#endif //CAMERAMEDIACODEC_CONVERTNV_H
最后是JAVA層調(diào)用的代碼
/**
* Created by you on 2018-03-12.
* 圖像數(shù)據(jù)nv21,i420, nv12, yv12的一些轉(zhuǎn)換裁剪旋轉(zhuǎn)的相關(guān)操作
*/
public final class YuvUtils {
private YuvUtils() {}
static {
System.loadLibrary("yuv-utils");
}
/**
* 同時將NV21數(shù)據(jù)轉(zhuǎn)換成I420并旋轉(zhuǎn), 不剪切
* @param nv21 camera datas
* @param i420 out datas
* @param w
* @param h
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void nv21ToI420Rotate(byte[] nv21,
byte[] i420,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成I420并旋轉(zhuǎn)
* @param nv21
* @param i420
* @param w 相機(jī)原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void clipNv21ToI420Rotate(byte[] nv21,
byte[] i420,
int w, int h,
int cw, int ch,
int left, int top,
@Orientation.OrientationMode int orientation);
/**
* 同時將NV21數(shù)據(jù)轉(zhuǎn)換成NV12并旋轉(zhuǎn), 不剪切
* @param nv21 camera datas
* @param nv12 out datas
* @param w
* @param h
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void nv21ToNV12Rotate(byte[] nv21,
byte[] nv12,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成NV12并旋轉(zhuǎn)
* @param nv21
* @param nv12
* @param w 相機(jī)原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w, h
* @param ch 同上
* @param left
* @param top
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void clipNv21ToNV12Rotate(byte[] nv21,
byte[] nv12,
int w, int h,
int cw, int ch,
int left, int top,
@Orientation.OrientationMode int orientation);
/**
* 同時將NV21數(shù)據(jù)轉(zhuǎn)換成YV12并旋轉(zhuǎn), 不剪切
* @param nv21 camera datas
* @param yv12 out datas
* @param w
* @param h
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void nv21ToYV12Rotate(byte[] nv21,
byte[] yv12,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同時剪切NV21數(shù)據(jù)轉(zhuǎn)換成YV12并旋轉(zhuǎn)
* @param nv21
* @param yv12
* @param w 相機(jī)原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void clipNv21ToYV12Rotate(byte[] nv21,
byte[] yv12,
int w, int h,
int cw, int ch,
int left, int top,
@Orientation.OrientationMode int orientation);
/**
* 將NV21數(shù)據(jù)旋轉(zhuǎn), 不剪切
* @param nv21 camera datas
* @param outs out datas
* @param w
* @param h
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void nv21Rotate(byte[] nv21,
byte[] outs,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同時剪切NV21數(shù)據(jù)并旋轉(zhuǎn)
* @param nv21
* @param outs
* @param w 相機(jī)原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必須都為偶數(shù)且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋轉(zhuǎn)角度
*/
public static native void clipNv21Rotate(byte[] nv21,
byte[] outs,
int w, int h,
int cw, int ch,
int left, int top,
@Orientation.OrientationMode int orientation);
}