在android项目上集成libyuv库以及使用libyuv库完成camera的缩放,旋转,翻转,裁剪操作

时间: 2023-07-10 admin IT培训

在android项目上集成libyuv库以及使用libyuv库完成camera的缩放,旋转,翻转,裁剪操作

在android项目上集成libyuv库以及使用libyuv库完成camera的缩放,旋转,翻转,裁剪操作

目录

一、下拉google官方的libyuv库代码

二、在android项目中集成libyuv库

1.环境配置

2.拷贝libyuv源码文件

​编辑3.配置cmake libyuv相关的链接编译等 

三、使用libyuv库

1.libyuv库完成camera的旋转

2.libyuv库实现翻转

3.libyuv库实现缩放

4.libyuv库实现裁剪


一、下拉google官方的libyuv库代码

官方地址 

如果打不开打不开,可以去 github 上下载:

下拉完成后目录如下所示:

主要我们用到的是include和source目录内容

二、在android项目中集成libyuv库

1.环境配置

首先配置方面要支持ndk,所以需要下载cmake和ndk配置,如下:

然后下面的集成流程是根据Android Studio Electric Eel | 2022.1.1 Patch 2版本进行的,不同版本可以稍有差异

2.拷贝libyuv源码文件

将include的文件和source文件拷贝到项目中,我这边是专门在cpp目录下建了一个libyuv文件夹,然后将include文件夹和source文件夹拷贝进来,如下:


3.配置cmake libyuv相关的链接编译等 

首先我的cmake路径和libyuv是在同一目录下,这个老版本android studio和新版本有所差异,注意分辨,和下面的cmake中配置libyuv路径有关,然后我这边是以同一目录下进行配置的,配置文件如下所示:

# For more information about using CMake with Android Studio, read the
# documentation: .html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.6.0)# Declares and names the project.project("xxx")# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.set(baseCppPath "libyuv")SET(ly_src_dir ${baseCppPath})
FILE(GLOB_RECURSE ly_source_files ${ly_src_dir}/*.cc)
LIST(SORT ly_source_files)add_library( # Sets the name of the library.liveassistant# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).${ly_source_files}native-lib.cpp)include_directories(${baseCppPath}/include)# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log)# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.xxx# Links the target library to the log library# included in the NDK.jnigraphics${log-lib})如果想详细了解上面cmake配置的具体内容可参考:

三、使用libyuv库

1.libyuv库完成camera的旋转

首先在jni中的cpp文件中添加头文件,如下:

头文件

#include <jni.h>

#include <string>

#include <libyuv.h>

#include <android/bitmap.h>

然后是cpp文件的具体实现

extern "C"

JNIEXPORT void JNICALL

Java_xxx_xxx_xxx_xxx_YuvNativeUtil_rotate(JNIEnv *env, jclass thiz, jobject y,

                                                          jobject u,

                                                          jobject v, jint yStride, jint uStride,

                                                          jint vStride,

                                                          jobject yOut, jobject uOut, jobject vOut,

                                                          jint yOutStride, jint uOutStride,

                                                          jint vOutStride,

                                                          jint width, jint height,

                                                          jint rotationMode) {

    uint8_t *yNative = (uint8_t *) env->GetDirectBufferAddress(y);

    uint8_t *uNative = (uint8_t *) env->GetDirectBufferAddress(u);

    uint8_t *vNative = (uint8_t *) env->GetDirectBufferAddress(v);

    uint8_t *yOutNative = (uint8_t *) env->GetDirectBufferAddress(yOut);

    uint8_t *uOutNative = (uint8_t *) env->GetDirectBufferAddress(uOut);

    uint8_t *vOutNative = (uint8_t *) env->GetDirectBufferAddress(vOut);

    libyuv::I420Rotate(yNative, yStride,

                       uNative, uStride,

                       vNative, vStride,

                       yOutNative, yOutStride,

                       uOutNative, uOutStride,

                       vOutNative, vOutStride,

                       width, height,

                       libyuv::RotationMode(rotationMode));

}

上面xxx对应自己的路径名

然后是java层代码实现,先加载so包:

static {

        System.loadLibrary("xxx");

    }

然后实现java代码,如下:

   public static YuvFrame rotate(Image image, int rotationMode) {

       YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(image.getWidth(),

               image.getHeight(), rotationMode);

       rotate(image.getPlanes()[0].getBuffer(),

               image.getPlanes()[1].getBuffer(),

               image.getPlanes()[2].getBuffer(),

               image.getPlanes()[0].getRowStride(),

               image.getPlanes()[1].getRowStride(),

               image.getPlanes()[2].getRowStride(),

               outFrame.getY(),

               outFrame.getU(),

               outFrame.getV(),

               outFrame.getYStride(),

               outFrame.getUStride(),

               outFrame.getVStride(),

               image.getWidth(),

               image.getHeight(),

               rotationMode);

       return outFrame;

   }

   public static YuvFrame rotate(YuvFrame yuvFrame, int rotationMode) {

       YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(yuvFrame.getWidth(),

               yuvFrame.getHeight(), rotationMode);

       rotate(yuvFrame.getY(),

               yuvFrame.getU(),

               yuvFrame.getV(),

               yuvFrame.getYStride(),

               yuvFrame.getUStride(),

               yuvFrame.getVStride(),

               outFrame.getY(),

               outFrame.getU(),

               outFrame.getV(),

               outFrame.getYStride(),

               outFrame.getUStride(),

               outFrame.getVStride(),

               yuvFrame.getWidth(),

               yuvFrame.getHeight(),

               rotationMode);

       return outFrame;

   }

   private static native void rotate(ByteBuffer y,

                                     ByteBuffer u,

                                     ByteBuffer v,

                                     int yStride,

                                     int uStride,

                                     int vStride,

                                     ByteBuffer yOut,

                                     ByteBuffer uOut,

                                     ByteBuffer vOut,

                                     int yOutStride,

                                     int uOutStride,

                                     int vOutStride,

                                     int width,

                                     int height,

                                     int rotationMode);

上面两种实现一是直接传入camera2中通过OnImageAvailableListener回调获取的Image,部分代码如下:

另一种方式是传递中间数据YuvFrame,然后写存储数据的YuvFrame类,如下,注意是kotlin方式写的

class YuvFrame {

    lateinit var y: ByteBuffer; private set

    lateinit var u: ByteBuffer; private set

    lateinit var v: ByteBuffer; private set

    var yStride: Int = 0private set

    var uStride: Int = 0private set

    var vStride: Int = 0private set

    var width: Int = 0private set

    var height: Int = 0private set

    fun fill(

        y: ByteBuffer,

        u: ByteBuffer,

        v: ByteBuffer,

        yStride: Int,

        uStride: Int,

        vStride: Int,

        width: Int,

        height: Int

    ) {

        this.y = y

        this.u = u

        this.v = v

        this.yStride = yStride

        this.uStride = uStride

        this.vStride = vStride

        this.width = width

        this.height = height

    }

    fun fill(image: Image) {

        for (i in 0 until 3) {

            when (i) {

                0 -> {

                    y = image.planes[i].buffer

                    yStride = image.planes[i].rowStride

                }

                1 -> {

                    u = image.planes[i].buffer

                    uStride = image.planes[i].rowStride

                }

                2 -> {

                    v = image.planes[i].buffer

                    vStride = image.planes[i].rowStride

                }

            }

        }

        width = image.width

        height = image.height

    }

    /** experimental method */

    fun fill(width: Int, height: Int, data: ByteArray) {

        this.width = width

        this.height = height

        val yArr = ByteArray(width * height)

        val uArr = ByteArray(width * height / 4)

        val vArr = ByteArray(width * height / 4)

        System.arraycopy(data, 0, yArr, 0, yArr.size)

        System.arraycopy(data, yArr.size, uArr, 0, uArr.size)

        System.arraycopy(data, yArr.size + uArr.size, vArr, 0, uArr.size)

        y = ByteBuffer.allocateDirect(yArr.size).put(yArr)

        u = ByteBuffer.allocateDirect(uArr.size).put(uArr)

        v = ByteBuffer.allocateDirect(vArr.size).put(vArr)

        y.position(0)

        u.position(0)

        v.position(0)

    }

    fun asArray(): ByteArray {

        var array: ByteArray

        val yPos = y.position()

        val uPos = u.position()

        val vPos = v.position()

        try {

            array =

                ByteBuffer.allocate(y.capacity() + u.capacity() + v.capacity()).put(y).put(u).put(v)

                    .array()

            y.position(yPos)

            u.position(uPos)

            v.position(vPos)

        catch (e: Exception) {

            array = ByteArray(size())

            y.get(array, 0, y.remaining())

            y.position(yPos)

            u.get(array, y.remaining(), u.remaining())

            u.position(uPos)

            v.get(array, y.remaining() + u.remaining(), v.remaining())

            v.position(vPos)

        }

        return array

    }

    fun size() = y.remaining() + u.remaining() + v.remaining()

    fun free() {

        y = ByteBuffer.allocate(1)

        u = ByteBuffer.allocate(1)

        v = ByteBuffer.allocate(1)

        yStride = 0

        uStride = 0

        vStride = 0

        width = 0

        height = 0

    }

}

2.libyuv库实现翻转

翻转有水平翻转和垂直翻转,垂直翻转可以直接通过旋转实现,水平翻转需要重新调用libyuv库,代码如下所示:

extern "C"

JNIEXPORT void JNICALL

Java_xxx_xxx_xxx_utils_YuvNativeUtil_mirrorH(JNIEnv *env, jclass thiz, jobject y,

                                                           jobject u,

                                                           jobject v, jint yStride, jint uStride,

                                                           jint vStride,

                                                           jobject yOut, jobject uOut, jobject vOut,

                                                           jint yOutStride, jint uOutStride,

                                                           jint vOutStride,

                                                           jint width, jint height) {

    uint8_t *yNative = (uint8_t *) env->GetDirectBufferAddress(y);

    uint8_t *uNative = (uint8_t *) env->GetDirectBufferAddress(u);

    uint8_t *vNative = (uint8_t *) env->GetDirectBufferAddress(v);

    uint8_t *yOutNative = (uint8_t *) env->GetDirectBufferAddress(yOut);

    uint8_t *uOutNative = (uint8_t *) env->GetDirectBufferAddress(uOut);

    uint8_t *vOutNative = (uint8_t *) env->GetDirectBufferAddress(vOut);

    libyuv::I420Mirror(yNative, yStride,

                       uNative, uStride,

                       vNative, vStride,

                       yOutNative, yOutStride,

                       uOutNative, uOutStride,

                       vOutNative, vOutStride,

                       width, height);

}

public static YuvFrame mirrorH(Image image) {

        YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(image.getWidth(), image.getHeight());

        mirrorH(image.getPlanes()[0].getBuffer(),

                image.getPlanes()[1].getBuffer(),

                image.getPlanes()[2].getBuffer(),

                image.getPlanes()[0].getRowStride(),

                image.getPlanes()[1].getRowStride(),

                image.getPlanes()[2].getRowStride(),

                outFrame.getY(),

                outFrame.getU(),

                outFrame.getV(),

                outFrame.getYStride(),

                outFrame.getUStride(),

                outFrame.getVStride(),

                image.getWidth(),

                image.getHeight());

        return outFrame;

    }

    public static YuvFrame mirrorH(YuvFrame yuvFrame) {

        YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(yuvFrame.getWidth(),

                yuvFrame.getHeight());

        mirrorH(yuvFrame.getY(),

                yuvFrame.getU(),

                yuvFrame.getV(),

                yuvFrame.getYStride(),

                yuvFrame.getUStride(),

                yuvFrame.getVStride(),

                outFrame.getY(),

                outFrame.getU(),

                outFrame.getV(),

                outFrame.getYStride(),

                outFrame.getUStride(),

                outFrame.getVStride(),

                yuvFrame.getWidth(),

                yuvFrame.getHeight());

        return outFrame;

    }

    public static YuvFrame mirrorV(Image image) {

        YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(image.getWidth(), image.getHeight());

        rotate(image.getPlanes()[0].getBuffer(),

                image.getPlanes()[1].getBuffer(),

                image.getPlanes()[2].getBuffer(),

                image.getPlanes()[0].getRowStride(),

                image.getPlanes()[1].getRowStride(),

                image.getPlanes()[2].getRowStride(),

                outFrame.getY(),

                outFrame.getU(),

                outFrame.getV(),

                outFrame.getYStride(),

                outFrame.getUStride(),

                outFrame.getVStride(),

                image.getWidth(),

                -image.getHeight(),

                0);

        return outFrame;

    }

    public static YuvFrame mirrorV(YuvFrame yuvFrame) {

        YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(yuvFrame.getWidth(),

                yuvFrame.getHeight());

        rotate(yuvFrame.getY(),

                yuvFrame.getU(),

                yuvFrame.getV(),

                yuvFrame.getYStride(),

                yuvFrame.getUStride(),

                yuvFrame.getVStride(),

                outFrame.getY(),

                outFrame.getU(),

                outFrame.getV(),

                outFrame.getYStride(),

                outFrame.getUStride(),

                outFrame.getVStride(),

                yuvFrame.getWidth(),

                -yuvFrame.getHeight(),

                0);

        return outFrame;

    }

    private static native void mirrorH(ByteBuffer y,

                                       ByteBuffer u,

                                       ByteBuffer v,

                                       int yStride,

                                       int uStride,

                                       int vStride,

                                       ByteBuffer yOut,

                                       ByteBuffer uOut,

                                       ByteBuffer vOut,

                                       int yOutStride,

                                       int uOutStride,

                                       int vOutStride,

                                       int width,

                                       int height);

3.libyuv库实现缩放

注意宽高必须为偶数,不然会出现花屏,代码如下所示:

extern "C"

JNIEXPORT void JNICALL

Java_xxx_xxx_xxx_utils_YuvNativeUtil_scale(

        JNIEnv *env, jclass thiz, jobject y, jobject u, jobject v,

        jint yStride, jint uStride, jint vStride,

        jobject yOut, jobject uOut, jobject vOut,

        jint yOutStride, jint uOutStride, jint vOutStride,

        jint srcWidth, jint srcHeight, jint dstWidth, jint dstHeight, jint filterMode) {

    uint8_t *yNative = (uint8_t *) env->GetDirectBufferAddress(y);

    uint8_t *uNative = (uint8_t *) env->GetDirectBufferAddress(u);

    uint8_t *vNative = (uint8_t *) env->GetDirectBufferAddress(v);

    uint8_t *yOutNative = (uint8_t *) env->GetDirectBufferAddress(yOut);

    uint8_t *uOutNative = (uint8_t *) env->GetDirectBufferAddress(uOut);

    uint8_t *vOutNative = (uint8_t *) env->GetDirectBufferAddress(vOut);

    libyuv::I420Scale(yNative, yStride,

                      uNative, uStride,

                      vNative, vStride,

                      srcWidth, srcHeight,

                      yOutNative, yOutStride,

                      uOutNative, uOutStride,

                      vOutNative, vOutStride,

                      dstWidth, dstHeight,

                      libyuv::FilterMode(filterMode));

}

public static YuvFrame scale(Image image, int dstWidth, int dstHeight, int filerMode) {

    YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(dstWidth, dstHeight);

    scale(image.getPlanes()[0].getBuffer(),

            image.getPlanes()[1].getBuffer(),

            image.getPlanes()[2].getBuffer(),

            image.getPlanes()[0].getRowStride(),

            image.getPlanes()[1].getRowStride(),

            image.getPlanes()[2].getRowStride(),

            outFrame.getY(),

            outFrame.getU(),

            outFrame.getV(),

            outFrame.getYStride(),

            outFrame.getUStride(),

            outFrame.getVStride(),

            image.getWidth(),

            image.getHeight(),

            dstWidth,

            dstHeight,

            filerMode);

    return outFrame;

}

public static YuvFrame scale(YuvFrame yuvFrame, int dstWidth, int dstHeight, int filerMode) {

    if (dstHeight <= 0 || dstWidth <= 0) {

        return null;

    }

    YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(dstWidth, dstHeight);

    scale(yuvFrame.getY(),

            yuvFrame.getU(),

            yuvFrame.getV(),

            yuvFrame.getYStride(),

            yuvFrame.getUStride(),

            yuvFrame.getVStride(),

            outFrame.getY(),

            outFrame.getU(),

            outFrame.getV(),

            outFrame.getYStride(),

            outFrame.getUStride(),

            outFrame.getVStride(),

            yuvFrame.getWidth(),

            yuvFrame.getHeight(),

            dstWidth,

            dstHeight,

            filerMode);

    return outFrame;

}

private static native void scale(ByteBuffer y,

                                 ByteBuffer u,

                                 ByteBuffer v,

                                 int yStride,

                                 int uStride,

                                 int vStride,

                                 ByteBuffer yOut,

                                 ByteBuffer uOut,

                                 ByteBuffer vOut,

                                 int yOutStride,

                                 int uOutStride,

                                 int vOutStride,

                                 int srcWidth,

                                 int srcHeight,

                                 int dstWidth,

                                 int dstHeight,

                                 int filterMode);

4.libyuv库实现裁剪

注意宽高和位置必须为偶数,不然会出现花屏,代码如下:

extern "C"

JNIEXPORT void JNICALL

Java_xxx_xxx_xxx_utils_YuvNativeUtil_crop(JNIEnv *env, jclass thiz, jbyteArray src_,

                                                        jobject yOut, jobject uOut, jobject vOut,

                                                        jint yOutStride, jint uOutStride,

                                                        jint vOutStride,

                                                        jint width, jint height, jint dst_width,

                                                        jint dst_height, jint left,

                                                        jint top) {

    //裁剪的区域大小不对

    if (left + dst_width > width || top + dst_height > height) {

        return;

    }

    //left和top必须为偶数,否则显示会有问题

    if (left % 2 != 0 || top % 2 != 0) {

        return;

    }

    jint src_length = env->GetArrayLength(src_);

    jbyte *src_i420_data = env->GetByteArrayElements(src_, NULL);

    uint8_t *yOutNative = (uint8_t *) env->GetDirectBufferAddress(yOut);

    uint8_t *uOutNative = (uint8_t *) env->GetDirectBufferAddress(uOut);

    uint8_t *vOutNative = (uint8_t *) env->GetDirectBufferAddress(vOut);

    libyuv::ConvertToI420((const uint8_t *) src_i420_data, width*height*3/2,

                          (uint8_t *) yOutNative, yOutStride,

                          (uint8_t *) uOutNative, uOutStride,

                          (uint8_t *) vOutNative, vOutStride,

                          left, top,

                          width, height,

                          dst_width, dst_height,

                          libyuv::kRotate0, libyuv::FOURCC_NV21);

}

/**

 * 裁剪,下面必须为偶数 不然画面会有问题

 *

 * @param src

 * @param image

 * @param cropWidth  必须为偶数

 * @param cropHeight 必须为偶数

 * @param left       必须为偶数

 * @param top        必须为偶数

 * @return

 */

public static YuvFrame crop(byte[] src, Image image, int cropWidth, int cropHeight, int left,

                            int top) {

    YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(cropWidth, cropHeight);

    crop(src,

            outFrame.getY(),

            outFrame.getU(),

            outFrame.getV(),

            outFrame.getYStride(),

            outFrame.getUStride(),

            outFrame.getVStride(),

            image.getWidth(),

            image.getHeight(),

            outFrame.getWidth(),

            outFrame.getHeight(),

            left, top);

    return outFrame;

}

public static YuvFrame crop(byte[] src, YuvFrame yuvFrame, int cropWidth, int cropHeight,

                            int left,

                            int top) {

    YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(cropWidth, cropHeight);

    crop(src,

            outFrame.getY(),

            outFrame.getU(),

            outFrame.getV(),

            outFrame.getYStride(),

            outFrame.getUStride(),

            outFrame.getVStride(),

            yuvFrame.getWidth(),

            yuvFrame.getHeight(),

            outFrame.getWidth(),

            outFrame.getHeight(),

            left, top);

    return outFrame;

}

/**

 * yuv数据的裁剪操作

 *

 * @param src        原始数据

 * @param width      原始的宽

 * @param height     原始的高

 * @param dst_width  输出的宽

 * @param dst_height 输出的高

 * @param left       裁剪的x的开始位置,必须为偶数,否则显示会有问题

 * @param top        裁剪的y的开始位置,必须为偶数,否则显示会有问题

 **/

public static native void crop(byte[] src, ByteBuffer yOut,

                               ByteBuffer uOut,

                               ByteBuffer vOut,

                               int yOutStride,

                               int uOutStride,

                               int vOutStride,

                               int width,

                               int height, int dst_width, int dst_height, int left, int top);

上面的裁剪需要传入一个原始数据的数组,得到原始数据的代码如下:

/**

     * q: 为什么要转换成NV21

     * a: 因为NV21是android系统的默认格式,而且NV21是YUV420SP格式,这种格式的特点是占用空间小,传输和存储都比较方便,适合移动设备

     * <p>

     * q: 为什么要转换成YUV420SP

     * a: 因为YUV420SP是NV21的一种变换格式,YUV420SP的特点是可以直接用于MediaCodec编码,而NV21不行

     * <p>

     * q: 为什么要转换成YUV420P

     * a: 因为YUV420P是YUV420SP的一种变换格式,YUV420P的特点是可以直接用于FFmpeg编码,而YUV420SP不行

     *

     * @param image

     * @return

     */

    public static byte[] getNV21FromImage(Image image) {

        long time1 = System.currentTimeMillis();

        int w = image.getWidth(), h = image.getHeight();

        int i420Size = w * h * 3 2;

        int picel1 = ImageFormat.getBitsPerPixel(ImageFormat.NV21);

        int picel2 = ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888);

        Image.Plane[] planes = image.getPlanes();

        //remaining0 = rowStride*(h-1)+w => 27632= 192*143+176

        int remaining0 = planes[0].getBuffer().remaining();

        int remaining1 = planes[1].getBuffer().remaining();

        //remaining2 = rowStride*(h/2-1)+w-1 =>  13807=  192*71+176-1

        int remaining2 = planes[2].getBuffer().remaining();

        //获取pixelStride,可能跟width相等,可能不相等

        int pixelStride = planes[2].getPixelStride();

        int rowOffest = planes[2].getRowStride();

        byte[] nv21 = new byte[i420Size];

        byte[] yRawSrcBytes = new byte[remaining0];

        byte[] uRawSrcBytes = new byte[remaining1];

        byte[] vRawSrcBytes = new byte[remaining2];

        planes[0].getBuffer().get(yRawSrcBytes);

        planes[1].getBuffer().get(uRawSrcBytes);

        planes[2].getBuffer().get(vRawSrcBytes);

        if (pixelStride == w) {

            //两者相等,说明每个YUV块紧密相连,可以直接拷贝

            System.arraycopy(yRawSrcBytes, 0, nv21, 0, rowOffest * h);

            System.arraycopy(vRawSrcBytes, 0, nv21, rowOffest * h, rowOffest * h / 2 1);

        else {

            byte[] ySrcBytes = new byte[w * h];

            byte[] uSrcBytes = new byte[w * h / 2 1];

            byte[] vSrcBytes = new byte[w * h / 2 1];

            for (int row = 0; row < h; row++) {

                //源数组每隔 rowOffest 个bytes 拷贝 w 个bytes到目标数组

                System.arraycopy(yRawSrcBytes, rowOffest * row, ySrcBytes, w * row, w);

                //y执行两次,uv执行一次

                if (row % 2 == 0) {

                    //最后一行需要减一

                    if (row == h - 2) {

                        System.arraycopy(vRawSrcBytes, rowOffest * row / 2, vSrcBytes, w * row / 2, w - 1);

                    else {

                        System.arraycopy(vRawSrcBytes, rowOffest * row / 2, vSrcBytes, w * row / 2, w);

                    }

                }

            }

            System.arraycopy(ySrcBytes, 0, nv21, 0, w * h);

            System.arraycopy(vSrcBytes, 0, nv21, w * h, w * h / 2 1);

        }

        return nv21;

    }

如上就是使用libyuv的旋转、翻转、缩放、裁剪的核心操作,如果需要将image转换成YuvFrame,可以如下操作,代码:

extern "C"

JNIEXPORT void JNICALL

Java_com_example_liveassistant_utils_YuvNativeUtil_convertToI420(JNIEnv *env, jclass thiz,

                                                                 jobject y, jobject u,

                                                                 jobject v, jint yStride,

                                                                 jint uStride, jint vStride,

                                                                 jint srcPixelStrideUv,

                                                                 jobject yOut, jobject uOut,

                                                                 jobject vOut,

                                                                 jint yOutStride, jint uOutStride,

                                                                 jint vOutStride,

                                                                 jint width, jint height) {

    uint8_t *yNative = (uint8_t *) env->GetDirectBufferAddress(y);

    uint8_t *uNative = (uint8_t *) env->GetDirectBufferAddress(u);

    uint8_t *vNative = (uint8_t *) env->GetDirectBufferAddress(v);

    uint8_t *yOutNative = (uint8_t *) env->GetDirectBufferAddress(yOut);

    uint8_t *uOutNative = (uint8_t *) env->GetDirectBufferAddress(uOut);

    uint8_t *vOutNative = (uint8_t *) env->GetDirectBufferAddress(vOut);

    libyuv::Android420ToI420(yNative, yStride,

                             uNative, uStride,

                             vNative, vStride,

                             srcPixelStrideUv,

                             yOutNative, yOutStride,

                             uOutNative, uOutStride,

                             vOutNative, vOutStride,

                             width, height);

}

public static YuvFrame convertToI420(Image image) {

    YuvFrame outFrame = FramesFactory.INSTANCE.instanceYuv(image.getWidth(), image.getHeight());

    convertToI420(image.getPlanes()[0].getBuffer(),

            image.getPlanes()[1].getBuffer(),

            image.getPlanes()[2].getBuffer(),

            image.getPlanes()[0].getRowStride(),

            image.getPlanes()[1].getRowStride(),

            image.getPlanes()[2].getRowStride(),

            image.getPlanes()[2].getPixelStride(),

            outFrame.getY(),

            outFrame.getU(),

            outFrame.getV(),

            outFrame.getYStride(),

            outFrame.getUStride(),

            outFrame.getVStride(),

            image.getWidth(),

            image.getHeight());

    return outFrame;

}

private static native void convertToI420(ByteBuffer y,

                                         ByteBuffer u,

                                         ByteBuffer v,

                                         int yStride,

                                         int uStride,

                                         int vStride,

                                         int srcPixelStrideUv,

                                         ByteBuffer yOut,

                                         ByteBuffer uOut,

                                         ByteBuffer vOut,

                                         int yOutStride,

                                         int uOutStride,

                                         int vOutStride,

                                         int width,

                                         int height);