android Camera(二): NV21高效处理与libyuv使用

继上篇Camera数据采集适配时会涉及到的YUV数据的剪切与方向感应时的旋转处理(对YUV数据不懂的可以网上搜索相关的文章),后面还有MediaCodec硬编码时,不同的手机可能还需要将NV21数据转换成NV12,I420或者YV12,这里就讲述NV21与yuv中另外三种NV12,I420,YV12的相互转换,剪切,旋转操作

android Camera(一):采集与预览尺寸适配
1:对这几种格式的转换,网上博客的写法也是很多, 单单的格式转换,或者单单旋转角度镜像旋转,或者单单剪切都还算比较简单的,但是如果要将YUV数据即要转换同时又要旋转角度镜像旋转,又要裁剪的话就是非常复杂的算法了

\color{red}{如果分三部操作,先剪切,再旋转,再转换会三次循环遍历YUV数据非常慢 (可以说看到这种分多部操作的就一定行不通)}
测试一个1280*720大小的YUV数据在旋转操作时可能就要20ms的时间(OPPO A57测试,高性能手机会非常快),如果分三部操作时就大概需要60ms的时间了, 对于一个最普通的按25帧率的采集来算,平均一秒 1000 / 25 = 40平均每40毫秒就采集一帧, 光转换旋转操作就超过了帧的时间,更何况MediaCodec编码也是要耗时的.
\color{red}{因此对于这种情况就需要高效率的算法,尽量在一次遍历YUV数据时就完成剪切,旋转,并转换同时进行的操作}

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);

}

C++实现和使用方法示例代码中都有,这里不再过多介绍
附上源码https://github.com/youxiaochen/CameraMedia

你可能感兴趣的:(android Camera(二): NV21高效处理与libyuv使用)