libYUV編譯成靜態庫以及使用

簡介
Google提供了一套Image的開源庫libyuv(git clone https://chromium.googlesource.com/libyuv/libyuv),實現對各種yuv數據之間的轉換,包括數據轉換,裁剪,縮放,旋轉。這裏由於有牆,所以可以在github中clone這個https://github.com/bilibili/libyuv.git
一、配置Android NDK編譯環境
1、配置NDK
首先下載NDK軟件包,並解壓:

1、從官網找到ndk的版本並下載:android-ndk-r17c-linux-x86_64.zip
2、解壓:unzip android-ndk-r17c-linux-x86_64.zip
3、ls android-ndk-r17c

在這裏插入圖片描述2、設置NDK的環境變量:
用root權限打開文件 :sudo vim /etc/profile,然後輸入root用戶密碼。打開在最後一行中加入如下 其中“//home/wq/libyuv/ndk/android-ndk-r17c" 這是ndk解壓出來的目錄

NDKROOT=/home/wangqi/libyuv/ndk/android-ndk-r17c
JAVA_HOME=/usr/local/java/jdk1.6.0_45
PATH=$PATH:$HOME/bin:$JAVA_HOME/bin:$NDKROOT
export JAVA_HOME
export PATH

在這裏插入圖片描述
在編輯器界面按下Esc退出編輯模式然後輸入 :qw 保存退出,輸入命令:source /etc/profile
使配置生效,然後在任意目錄下輸入ndk-build 出現如下信息則說明配置正確。

Android NDK: Could not find application project directory !    
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.    
/home/wangqi/libyuv/ndk/android-ndk-r17c/build/core/build-local.mk:151: *** Android NDK: Aborting    .  Stop.

在這裏插入圖片描述二、libYUV編譯
1、新建一個libyuv文件夾,然後再新建一個jni文件夾,l把ibyuv代碼下載到jni文件夾中,從github上面下載libyuv代碼,

git clone https://github.com/bilibili/libyuv.git

在這裏插入圖片描述2、在jni目錄下面增加Application.mk文件

APP_ABI := armeabi-v7a arm64-v8a
APP_PLATFORM := android-14

3、在libyuv目錄下,也就是與jni文件夾和ndk文件夾下面可以執行ndk-build clean,會清除編譯生成的靜態庫,否則不會生成最新的庫

ndk-build clean

在這裏插入圖片描述
4、編譯

ndk-build

如果在沒有添加Application.mk文件,就會生成所有能打出來的庫,如果編寫了Application.mk文件,就會打出指定架構的庫文件。
在這裏插入圖片描述5、靜態庫與動態庫

# This is the Android makefile for libyuv for both platform and NDK.
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_CPP_EXTENSION := .cc

LOCAL_SRC_FILES := \
    source/compare.cc           \
    source/compare_common.cc    \
    source/compare_neon64.cc    \
    source/compare_gcc.cc       \
    source/convert.cc           \
    source/convert_argb.cc      \
    source/convert_from.cc      \
    source/convert_from_argb.cc \
    source/convert_to_argb.cc   \
    source/convert_to_i420.cc   \
    source/cpu_id.cc            \
    source/planar_functions.cc  \
    source/rotate.cc            \
    source/rotate_argb.cc       \
    source/rotate_mips.cc       \
    source/rotate_neon64.cc     \
    source/row_any.cc           \
    source/row_common.cc        \
    source/row_mips.cc          \
    source/row_neon64.cc        \
    source/row_gcc.cc	        \
    source/scale.cc             \
    source/scale_any.cc         \
    source/scale_argb.cc        \
    source/scale_common.cc      \
    source/scale_mips.cc        \
    source/scale_neon64.cc      \
    source/scale_gcc.cc         \
    source/video_common.cc

# TODO(fbarchard): Enable mjpeg encoder.
#   source/mjpeg_decoder.cc
#   source/convert_jpeg.cc
#   source/mjpeg_validate.cc

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
    LOCAL_CFLAGS += -DLIBYUV_NEON
    LOCAL_SRC_FILES += \
        source/compare_neon.cc.neon    \
        source/rotate_neon.cc.neon     \
        source/row_neon.cc.neon        \
        source/scale_neon.cc.neon
endif

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
# 生成的庫名稱
LOCAL_MODULE := libyuv
LOCAL_MODULE_TAGS := optional
# 生成靜態庫還是動態庫
# BUILD_STATIC_LIBRSRY 靜態庫
# BUILD_SHARED_LIBRARY 動態庫
include $(BUILD_STATIC_LIBRARY)

如果如上所操作你只會得到.a的靜態庫,而動態庫需要在上述方法過程中修改一個文件Andorid.mk。這個文件在jni文件夾中,
三、libYUV的使用
1、JNI層代碼使用

#include <jni.h>
#include <string>
#include "libyuv.h"

//分別用來存儲1420,1420縮放,I420旋轉和鏡像的數據
static jbyte *Src_i420_data;
static jbyte *Src_i420_data_scale;
static jbyte *Src_i420_data_rotate;

JNIEXPORT void JNI_OnUnload(JavaVM *jvm, void *reserved) {
    //進行釋放
    free(Src_i420_data);
    free(Src_i420_data_scale);
    free(Src_i420_data_rotate);
}

void scaleI420(jbyte *src_i420_data, jint width, jint height, jbyte *dst_i420_data, jint dst_width,
               jint dst_height, jint mode) {

    jint src_i420_y_size = width * height;
    jint src_i420_u_size = (width >> 1) * (height >> 1);
    jbyte *src_i420_y_data = src_i420_data;
    jbyte *src_i420_u_data = src_i420_data + src_i420_y_size;
    jbyte *src_i420_v_data = src_i420_data + src_i420_y_size + src_i420_u_size;

    jint dst_i420_y_size = dst_width * dst_height;
    jint dst_i420_u_size = (dst_width >> 1) * (dst_height >> 1);
    jbyte *dst_i420_y_data = dst_i420_data;
    jbyte *dst_i420_u_data = dst_i420_data + dst_i420_y_size;
    jbyte *dst_i420_v_data = dst_i420_data + dst_i420_y_size + dst_i420_u_size;

    libyuv::I420Scale((const uint8 *) src_i420_y_data, width,
                      (const uint8 *) src_i420_u_data, width >> 1,
                      (const uint8 *) src_i420_v_data, width >> 1,
                      width, height,
                      (uint8 *) dst_i420_y_data, dst_width,
                      (uint8 *) dst_i420_u_data, dst_width >> 1,
                      (uint8 *) dst_i420_v_data, dst_width >> 1,
                      dst_width, dst_height,
                      (libyuv::FilterMode) mode);
}

void rotateI420(jbyte *src_i420_data, jint width, jint height, jbyte *dst_i420_data, jint degree) {
    jint src_i420_y_size = width * height;
    jint src_i420_u_size = (width >> 1) * (height >> 1);

    jbyte *src_i420_y_data = src_i420_data;
    jbyte *src_i420_u_data = src_i420_data + src_i420_y_size;
    jbyte *src_i420_v_data = src_i420_data + src_i420_y_size + src_i420_u_size;

    jbyte *dst_i420_y_data = dst_i420_data;
    jbyte *dst_i420_u_data = dst_i420_data + src_i420_y_size;
    jbyte *dst_i420_v_data = dst_i420_data + src_i420_y_size + src_i420_u_size;

    //要注意這裏的width和height在旋轉之後是相反的
    if (degree == libyuv::kRotate90 || degree == libyuv::kRotate270) {
        libyuv::I420Rotate((const uint8 *) src_i420_y_data, width,
                           (const uint8 *) src_i420_u_data, width >> 1,
                           (const uint8 *) src_i420_v_data, width >> 1,
                           (uint8 *) dst_i420_y_data, height,
                           (uint8 *) dst_i420_u_data, height >> 1,
                           (uint8 *) dst_i420_v_data, height >> 1,
                           width, height,
                           (libyuv::RotationMode) degree);
    }
}

void mirrorI420(jbyte *src_i420_data, jint width, jint height, jbyte *dst_i420_data) {
    jint src_i420_y_size = width * height;
    jint src_i420_u_size = (width >> 1) * (height >> 1);

    jbyte *src_i420_y_data = src_i420_data;
    jbyte *src_i420_u_data = src_i420_data + src_i420_y_size;
    jbyte *src_i420_v_data = src_i420_data + src_i420_y_size + src_i420_u_size;

    jbyte *dst_i420_y_data = dst_i420_data;
    jbyte *dst_i420_u_data = dst_i420_data + src_i420_y_size;
    jbyte *dst_i420_v_data = dst_i420_data + src_i420_y_size + src_i420_u_size;

    libyuv::I420Mirror((const uint8 *) src_i420_y_data, width,
                       (const uint8 *) src_i420_u_data, width >> 1,
                       (const uint8 *) src_i420_v_data, width >> 1,
                       (uint8 *) dst_i420_y_data, width,
                       (uint8 *) dst_i420_u_data, width >> 1,
                       (uint8 *) dst_i420_v_data, width >> 1,
                       width, height);
}


void nv21ToI420(jbyte *src_nv21_data, jint width, jint height, jbyte *src_i420_data) {
    jint src_y_size = width * height;
    jint src_u_size = (width >> 1) * (height >> 1);

    jbyte *src_nv21_y_data = src_nv21_data;
    jbyte *src_nv21_vu_data = src_nv21_data + src_y_size;

    jbyte *src_i420_y_data = src_i420_data;
    jbyte *src_i420_u_data = src_i420_data + src_y_size;
    jbyte *src_i420_v_data = src_i420_data + src_y_size + src_u_size;


    libyuv::NV21ToI420((const uint8 *) src_nv21_y_data, width,
                       (const uint8 *) src_nv21_vu_data, width,
                       (uint8 *) src_i420_y_data, width,
                       (uint8 *) src_i420_u_data, width >> 1,
                       (uint8 *) src_i420_v_data, width >> 1,
                       width, height);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_libyuv_util_YuvUtil_init(JNIEnv *env, jclass type, jint width, jint height, jint dst_width,
                                  jint dst_height) {
    Src_i420_data = (jbyte *) malloc(sizeof(jbyte) * width * height * 3 / 2);
    Src_i420_data_scale = (jbyte *) malloc(sizeof(jbyte) * dst_width * dst_height * 3 / 2);
    Src_i420_data_rotate = (jbyte *) malloc(sizeof(jbyte) * dst_width * dst_height * 3 / 2);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_libyuv_util_YuvUtil_compressYUV(JNIEnv *env, jclass type,
                                         jbyteArray src_, jint width,
                                         jint height, jbyteArray dst_,
                                         jint dst_width, jint dst_height,
                                         jint mode, jint degree,
                                         jboolean isMirror) {
    jbyte *Src_data = env->GetByteArrayElements(src_, NULL);
    jbyte *Dst_data = env->GetByteArrayElements(dst_, NULL);
    //nv21轉化爲i420
    nv21ToI420(Src_data, width, height, Src_i420_data);
    //進行縮放的操作
    scaleI420(Src_i420_data, width, height, Src_i420_data_scale, dst_width, dst_height, mode);
    if (isMirror) {
        //進行旋轉的操作
        rotateI420(Src_i420_data_scale, dst_width, dst_height, Src_i420_data_rotate, degree);
        //因爲旋轉的角度都是90和270,那後面的數據width和height是相反的
        mirrorI420(Src_i420_data_rotate, dst_height, dst_width, Dst_data);
    } else {
        rotateI420(Src_i420_data_scale, dst_width, dst_height, Dst_data, degree);
    }
    env->ReleaseByteArrayElements(dst_, Dst_data, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_libyuv_util_YuvUtil_cropYUV(JNIEnv *env, jclass type, jbyteArray src_, jint width,
                                     jint height, jbyteArray dst_, 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);
    jbyte *dst_i420_data = env->GetByteArrayElements(dst_, NULL);


    jint dst_i420_y_size = dst_width * dst_height;
    jint dst_i420_u_size = (dst_width >> 1) * (dst_height >> 1);

    jbyte *dst_i420_y_data = dst_i420_data;
    jbyte *dst_i420_u_data = dst_i420_data + dst_i420_y_size;
    jbyte *dst_i420_v_data = dst_i420_data + dst_i420_y_size + dst_i420_u_size;

    libyuv::ConvertToI420((const uint8 *) src_i420_data, src_length,
                          (uint8 *) dst_i420_y_data, dst_width,
                          (uint8 *) dst_i420_u_data, dst_width >> 1,
                          (uint8 *) dst_i420_v_data, dst_width >> 1,
                          left, top,
                          width, height,
                          dst_width, dst_height,
                          libyuv::kRotate0, libyuv::FOURCC_I420);

    env->ReleaseByteArrayElements(dst_, dst_i420_data, 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_libyuv_util_YuvUtil_yuvI420ToNV21(JNIEnv *env, jclass type, jbyteArray i420Src,
                                           jbyteArray nv21Src,
                                           jint width, jint height) {

    jbyte *src_i420_data = env->GetByteArrayElements(i420Src, NULL);
    jbyte *src_nv21_data = env->GetByteArrayElements(nv21Src, NULL);

    jint src_y_size = width * height;
    jint src_u_size = (width >> 1) * (height >> 1);

    jbyte *src_i420_y_data = src_i420_data;
    jbyte *src_i420_u_data = src_i420_data + src_y_size;
    jbyte *src_i420_v_data = src_i420_data + src_y_size + src_u_size;

    jbyte *src_nv21_y_data = src_nv21_data;
    jbyte *src_nv21_vu_data = src_nv21_data + src_y_size;


    libyuv::I420ToNV21(
            (const uint8 *) src_i420_y_data, width,
            (const uint8 *) src_i420_u_data, width >> 1,
            (const uint8 *) src_i420_v_data, width >> 1,
            (uint8 *) src_nv21_y_data, width,
            (uint8 *) src_nv21_vu_data, width,
            width, height);
}

2、java層的代碼使用

public class YuvUtil {

    static {
        System.loadLibrary("yuvutil");
    }

    /**
     * 初始化
     *
     * @param width      原始的寬
     * @param height     原始的高
     * @param dst_width  輸出的寬
     * @param dst_height 輸出的高
     **/
    public static native void init(int width, int height, int dst_width, int dst_height);


    /**
     * YUV數據的基本的處理
     *
     * @param src        原始數據
     * @param width      原始的寬
     * @param height     原始的高
     * @param dst        輸出數據
     * @param dst_width  輸出的寬
     * @param dst_height 輸出的高
     * @param mode       壓縮模式。這裏爲0,1,2,3 速度由快到慢,質量由低到高,一般用0就好了,因爲0的速度最快
     * @param degree     旋轉的角度,90,180和270三種
     * @param isMirror   是否鏡像,一般只有270的時候才需要鏡像
     **/
    public static native void compressYUV(byte[] src, int width, int height, byte[] dst, int dst_width, int dst_height, int mode, int degree, boolean isMirror);

    /**
     * yuv數據的裁剪操作
     *
     * @param src        原始數據
     * @param width      原始的寬
     * @param height     原始的高
     * @param dst        輸出數據
     * @param dst_width  輸出的寬
     * @param dst_height 輸出的高
     * @param left       裁剪的x的開始位置,必須爲偶數,否則顯示會有問題
     * @param top        裁剪的y的開始位置,必須爲偶數,否則顯示會有問題
     **/
    public static native void cropYUV(byte[] src, int width, int height, byte[] dst, int dst_width, int dst_height, int left, int top);

    /**
     * 將I420轉化爲NV21
     *
     * @param i420Src 原始I420數據
     * @param nv21Src 轉化後的NV21數據
     * @param width   輸出的寬
     * @param width   輸出的高
     **/
    public static native void yuvI420ToNV21(byte[] i420Src, byte[] nv21Src, int width, int height);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章