0%

[原创] Android 编译并使用 libyuv 对 YUV 数据进行格式转换、缩放、旋转、镜像、裁剪

编译环境

  • 操作系统:macOS 11.6
  • NDK:22.1.7171670
  • libyuv:Version: 1815 (截止 2022/03/24)
  • cmake: 3.20.3
  • gcc:
    1
    2
    3
    4
    5
    Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
    Apple clang version 13.0.0 (clang-1300.0.29.3)
    Target: x86_64-apple-darwin20.6.0
    Thread model: posix
    InstalledDir: /Library/Developer/CommandLineTools/usr/bin

编译 libyuv

下载 libyuv

由于是编译 Android 中使用的 libyuv,所以需要创建一个 Android 工程。读者可以创建自己的示例工程,会使用下文中作者的开源工程

说明:为了能够让 Android 编译 Native 代码,因此需要将下载的 libyuv 放到 jni 目录中。

(可以从官网下载 libyuv 库,若读者无法下载,也可直接使用作者已经下载好的源代码 libyuv-20220324.tar.gz 。为了减少文件大小,该压缩文件中仅移除了 .git 文件夹。)

1
2
3
4
5
6
% cd /Users/yhz61010/AndroidStudioProjects/LeoAndroidBaseUtilProject-Kotlin/  
% mkdir -p libyuv/jni
% cd libyuv/jni
% git clone https://chromium.googlesource.com/libyuv/libyuv .
// or unzip `libyuv-20220324.tar.gz` file
% tar xvzf ../../yuv-sdk/libyuv-20220324.tar.gz --strip-components 1

修改 Android.mk 文件

注意:由于此文的目的不需要 JPEG 功能,因此编译 libyuv 时禁用了相关的 JPEG 依赖。
修改 ./libyuv/jni/Android.mk 文件,禁用 JPEG 依赖:
LOCAL_CPP_EXTENSION := .cc 之前,添加如下常量:

1
LIBYUV_DISABLE_JPEG := "yes"  

之后,将原代码(86 至 87 行) :

1
2
LOCAL_STATIC_LIBRARIES := libyuv_static  
LOCAL_SHARED_LIBRARIES := libjpeg

替换成:

1
2
3
4
LOCAL_STATIC_LIBRARIES := libyuv_static  
ifneq ($(LIBYUV_DISABLE_JPEG), "yes")
LOCAL_SHARED_LIBRARIES := libjpeg
endif

编译

在如下路径执行 ndk-build 命令,开始编译源代码:

1
2
% cd /Users/yhz61010/AndroidStudioProjects/LeoAndroidBaseUtilProject-Kotlin/libyuv/jni  
% ndk-build

编译成功后,可以在 /Users/yhz61010/AndroidStudioProjects/LeoAndroidBaseUtilProject-Kotlin/libyuv/libs 目录中找到生成的各个平台所需的 libyuv.so 文件。

使用 libyuv

将源代码中的 libyuv/jni/include 文件夹,拷贝到需要使用 libyuv 的项目中,例如作者用到的 yuv-sdk 模块。需要将 include 文件夹,拷贝到 yuv-sdk/main/cpp 文件夹中。并将刚刚生成的不同平台的 so 文件,拷贝到 yuv-sdk/libs 中。

创建 CMakeLists.txt 文件,并修改 build.gradle 文件。

下面就可以开始在自己的 Native 代码中,使用 libyuv 库了。具体代码详见我的开源项目

写完自己的 Native 代码后,就可以开始编译自己的 libyuv 库了。

  • 使用 gradlew 命令编译

    1
    2
    % cd /Users/yhz61010/AndroidStudioProjects/LeoAndroidBaseUtilProject-Kotlin/ 
    % ./gradlew yuv-sdk:assemble
  • 使用 Gradle 侧边栏编译
    在 Android Studio 中,点击右侧的侧边栏 Gradle -> LeoAndroidBaseUtil -> yuv-sdk -> build -> assemble

  • 使用 Build 菜单编译
    在 Android Studio 中,选中 yuv-sdk 模块,然后点击 Build 菜单:Build -> Make Module ‘LeoAndroidBaseUtil.yuv-sdk’
    注意:可以在 Build Variants 中修改编译选项。

编译完自己的 libyuv 后,使用起来就非常简单了,具体的可以查看 YuvUtil 工具类。下面是一些示例:

1
2
3
4
5
val nv21Bytes = YuvUtil.i420ToNv21(i420Bytes, width, height)
val nv12Bytes = YuvUtil.i420ToNv12(i420Bytes, width, height)
val rotateI420Bytes = YuvUtil.rotateI420(i420Bytes, width, height, YuvUtil.Rotate_90)
val mirrorI420Bytes = YuvUtil.mirrorI420(i420Bytes, width, height)
val convertToI420Bytes = YuvUtil.convertToI420(yuvData, YuvUtil.I420, width, height, verticallyFlip = true, YuvUtil.Rotate_270)

性能分析

以下是使用 libyuv 库与使用 Kotlin 直接对 YUV420 数据进行操作的性能分析:

视频尺寸(1920x1080) 旋转 镜像 同时进行旋转与镜像
libyuv 5.5ms 4.6ms 6.9ms
Kotlin 26.9ms 17.3ms 35.7ms

参考文献

坚持原创及高品质技术分享,您的支持将鼓励我继续创作!