继上篇Camera数据采集适配时会涉及到的YUV数据的剪切与方向感应时的旋转处理(对YUV数据不懂的可以网上搜索相关的文章),后面还有MediaCodec硬编码时,不同的手机可能还需要将NV21数据转换成NV12,I420或者YV12,这里就讲述NV21与yuv中另外三种NV12,I420,YV12的相互转换,剪切,旋转操作
android Camera(一):采集与预览尺寸适配
1:对这几种格式的转换,网上博客的写法也是很多, 单单的格式转换,或者单单旋转角度镜像旋转,或者单单剪切都还算比较简单的,但是如果要将YUV数据即要转换同时又要旋转角度镜像旋转,又要裁剪的话就是非常复杂的算法了
测试一个1280*720大小的YUV数据在旋转操作时可能就要20ms的时间(OPPO A57测试,高性能手机会非常快),如果分三部操作时就大概需要60ms的时间了, 对于一个最普通的按25帧率的采集来算,平均一秒 1000 / 25 = 40平均每40毫秒就采集一帧, 光转换旋转操作就超过了帧的时间,更何况MediaCodec编码也是要耗时的.
2:libyuv的使用
其实对于这种高效率的算法, Google已经开源了一个叫做libyuv的库(C++)专门用于YUV数据的处理,里边提供了很多YUV数据类型之间的转换,旋转,镜像,剪切算法.
这里也有个疑问,libyuv是个高效率的算法,为何里边找不到nv12或者nv21的旋转与镜像操作方法(有懂的请不吝赐教),只能根据libyuv中的NV12与I420之间的旋转源码算法另外写了一个nv12的旋转镜像操作代码, 提供nv12或者nv21所有相关方法(nv12与nv21算法大致都相似,只是UV与VU的区别)
源码入口,nv21与nv12,yuv12,i420之间的转换,裁剪,旋转,镜像并同时进行的操作https://github.com/youxiaochen/CameraMedia
直接上代码
RotateNV_UV.h,提供NV21(NV12)的UV旋转镜像操作方法, 所有YUV类型中Y数据的操作算法都是一样的
/**
* Created by you on 2018-03-06. NV21,NV12之间的UV旋转操作
* NV21与NV12区别只是UV与VU交差的区别,因此以下方法NV21,NV12可通用
* 所有_X的交叉即可用作NV12与NV21之间的UV转换
*/
#ifndef CAMERAMEDIACODEC_ROTATENV_UV_H
#define CAMERAMEDIACODEC_ROTATENV_UV_H
#include
/**
* 适合NV21或者NV12的UV数据的操作
*/
void TransposeWxH_C2(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 适合NV21或者NV12的UV数据的操作,并转换UV顺序
*/
void TransposeWxH_C2_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 镜面翻转uint8_t类型的数据,并交叉变换一个步长的顺序,适合UV与VU之间的镜面转换, 与源码稍作修改
*/
void MirrorRow_C_X(const uint8_t* src,
uint8_t* dst,
int width);
/**
* 将NV21或者NV12的UV数据旋转90
* @param width UV数据的宽
* @param height UV数据的高
*/
void RotateNV_UV90(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 将NV21或者NV12的UV数据旋转270
* @param width UV数据的宽
* @param height UV数据的高
*/
void RotateNV_UV270(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 将NV21或者NV12的UV数据旋转180
*/
void RotateNV_UV180(const uint8_t* src,
int src_stride,
uint8_t* dst,
int dst_stride,
int width,
int height);
/**
* 同时将NV21或者NV12的UV数据旋转90,并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
* @param width UV数据的宽
* @param height UV数据的高
*/
void RotateNV_UV90_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 同时将NV21或者NV12的UV数据旋转270, 并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
* @param width UV数据的宽
* @param height UV数据的高
*/
void RotateNV_UV270_X(const uint16_t* src,
int src_stride,
uint16_t* dst,
int dst_stride,
int width,
int height);
/**
* 同时将NV21或者NV12的UV数据旋转180, 并交叉转换UV顺序, 可用作NV12与NV21之间的UV互转
*/
void RotateNV_UV180_X(const uint8_t* src,
int src_stride,
uint8_t* dst,
int dst_stride,
int width,
int height);
/**
* NV21的数据拷贝
*/
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旋转
*/
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同时旋转,转换与裁剪的相关操作
/**
* Created by you on 2018-09-06. NV21,NV12之间的UV剪切并旋转操作
* NV21与NV12区别只是UV与VU交差的区别,因此以下方法NV21,NV12可通用
*/
#include "RotateNV_UV.h"
#ifndef CAMERAMEDIACODEC_CONVERTNV_H
#define CAMERAMEDIACODEC_CONVERTNV_H
/**
* 同时剪切NV21数据并旋转
*/
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转NV12并旋转
*/
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转NV12并旋转
*/
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层调用的代码
/**
* Created by you on 2018-03-12.
* 图像数据nv21,i420, nv12, yv12的一些转换裁剪旋转的相关操作
*/
public final class YuvUtils {
private YuvUtils() {}
static {
System.loadLibrary("yuv-utils");
}
/**
* 同时将NV21数据转换成I420并旋转, 不剪切
* @param nv21 camera datas
* @param i420 out datas
* @param w
* @param h
* @param orientation 旋转角度
*/
public static native void nv21ToI420Rotate(byte[] nv21,
byte[] i420,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同时剪切NV21数据转换成I420并旋转
* @param nv21
* @param i420
* @param w 相机原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋转角度
*/
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数据转换成NV12并旋转, 不剪切
* @param nv21 camera datas
* @param nv12 out datas
* @param w
* @param h
* @param orientation 旋转角度
*/
public static native void nv21ToNV12Rotate(byte[] nv21,
byte[] nv12,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同时剪切NV21数据转换成NV12并旋转
* @param nv21
* @param nv12
* @param w 相机原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w, h
* @param ch 同上
* @param left
* @param top
* @param orientation 旋转角度
*/
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数据转换成YV12并旋转, 不剪切
* @param nv21 camera datas
* @param yv12 out datas
* @param w
* @param h
* @param orientation 旋转角度
*/
public static native void nv21ToYV12Rotate(byte[] nv21,
byte[] yv12,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同时剪切NV21数据转换成YV12并旋转
* @param nv21
* @param yv12
* @param w 相机原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋转角度
*/
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数据旋转, 不剪切
* @param nv21 camera datas
* @param outs out datas
* @param w
* @param h
* @param orientation 旋转角度
*/
public static native void nv21Rotate(byte[] nv21,
byte[] outs,
int w, int h,
@Orientation.OrientationMode int orientation);
/**
* 同时剪切NV21数据并旋转
* @param nv21
* @param outs
* @param w 相机原尺寸
* @param h
* @param cw 需要裁剪后的尺寸,必须都为偶数且 <= w
* @param ch 同上 <= h
* @param left
* @param top
* @param orientation 旋转角度
*/
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);
}