Mac交叉编译Android FFmpeg

318 阅读2分钟

最近在学习Android上的FFmpeg开发。记录下在Mac上交叉编译和使用Android FFmpeg库的过程。

1. 交叉编译

# 1.下载ffmpeg-4.2.2源码
wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
# 2. 解压
# 3. 运行configure脚本配置项目
./configure --disable-x86asm

在当前文件夹下创建编译脚本build_android_arm64-v8a_clang.sh:

#!/bin/zsh

export NDK=~/Library/Android/android-ndk-r20b #这里配置先你的 NDK 路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

#arm64-v8a
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"

#armv7-a
#ARCH=arm
#CPU=armv7-a
#API=21
#CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
#CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
#SYSROOT=$NDK/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
#CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
#PREFIX=$(pwd)/android/$CPU
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "

function build_android {

	./configure \
		--prefix=$PREFIX \
		--enable-neon \
		--enable-hwaccels \
		--enable-gpl \
		--disable-postproc \
		--disable-debug \
		--enable-small \
		--enable-jni \
		--enable-mediacodec \
		--enable-decoder=h264_mediacodec \
		--enable-static \
		--enable-shared \
		--disable-doc \
		--enable-ffmpeg \
		--disable-ffplay \
		--disable-ffprobe \
		--disable-avdevice \
		--disable-doc \
		--disable-symver \
		--cross-prefix=$CROSS_PREFIX \
		--target-os=android \
		--arch=$ARCH \
		--cpu=$CPU \
		--cc=$CC \
		--cxx=$CXX \
		--enable-cross-compile \
		--sysroot=$SYSROOT \
		--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
		--extra-ldflags="$ADDI_LDFLAGS"

	  make clean
	  make -j16
          make install
	
	echo "============================ build android arm64-v8a success =========================="

}

build_android

运行脚本编译,之后会在android/armv8-a/lib/下生成动态和静态库。

# 修改 build_android_arm64-v8a_clang.sh 可执行权限  
chmod +x build_android_arm64-v8a_clang.sh  
# 运行编译脚本  
./build_android_arm64-v8a_clang.sh

2. Android上集成测试

新建Android项目。

2.1 JNI接口

  1. 将生成目录的头文件和库文件放入下面目录。注意库文件要放在app/src/jniLibs/arm64-v8a目录下。 image.png
  2. 创建FFMediaPlayer.java,作为java层接口。
package com.zzakafool.androidffmpeg;

public class FFMediaPlayer {
    static {
        System.loadLibrary("learn-ffmpeg");   // 这里会加载对应的cmake编译的库
    }

    public static String GetFFmpegVersion() {
        return native_GetFFmpegVersion();
    }

    private static native String native_GetFFmpegVersion();
}
  1. app/src/main/cpp/下添加对应native实现FFMediaPlayer.cpp
#include <cstdio>
#include <cstring>
//#include "util/LogUtil.h"
#include "jni.h"

//由于 FFmpeg 库是 C 语言实现的,告诉编译器按照 C 的规则进行编译
extern "C" {
#include <libavcodec/version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <libavfilter/version.h>
#include <libswresample/version.h>
#include <libswscale/version.h>
};

extern "C"
JNIEXPORT jstring JNICALL
Java_com_zzakafool_androidffmpeg_FFMediaPlayer_native_1GetFFmpegVersion(JNIEnv *env, jclass clazz) {
    // TODO: implement native_GetFFmpegVersion()
    char strBuffer[1024 * 4] = {0};
    strcat(strBuffer, "libavcodec : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
    strcat(strBuffer, "\nlibavformat : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
    strcat(strBuffer, "\nlibavutil : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
    strcat(strBuffer, "\nlibavfilter : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
    strcat(strBuffer, "\nlibswresample : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
    strcat(strBuffer, "\nlibswscale : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
    strcat(strBuffer, "\navcodec_configure : \n");
    strcat(strBuffer, avcodec_configuration());
    strcat(strBuffer, "\navcodec_license : ");
    strcat(strBuffer, avcodec_license());
//    LOGCATE("GetFFmpegVersion\n%s", strBuffer);
    return env->NewStringUTF(strBuffer);
}
  1. app/src/main/cpp/下添加CMakeLists.txt
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

set(jnilibs ${CMAKE_SOURCE_DIR}/../jniLibs)
set(libname learn-ffmpeg)

include_directories(
        include
        ${CMAKE_SOURCE_DIR}/util
)

link_directories(
        ${jnilibs}/${ANDROID_ABI})

file(GLOB src-files
        ${CMAKE_SOURCE_DIR}/*.cpp)

add_library( # Sets the name of the library.
        ${libname}

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        ${src-files}
        )

set(third-party-libs
        avformat
        avcodec
        avfilter
        swresample
        swscale
        avutil
        )

set(native-libs
        android
        EGL
        GLESv3
        OpenSLES
        log
        m
        z
        )

target_link_libraries( # Specifies the target library.
        ${libname}

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib}
        ${third-party-libs}
        ${native-libs}
        )
  1. 在build.gradle.kts中添加cmakelist.txt
android {
    namespace = "com.zzakafool.ffmpeglearning"
    compileSdk = 33
    
    // 省略...
    
    externalNativeBuild {
        cmake {
            path = file("src/main/cpp/CMakeLists.txt")
            version = "3.22.1"
        }
    }
    
    // 仅构建arm64-v8a的包
    ndk {
        abiFilters.add("arm64-v8a")
    }
}
  1. 在Activity中调用对应的接口。
import static com.zzakafool.androidffmpeg.R.id.*;

import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.zzakafool.androidffmpeg.R.id;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView)findViewById(text_view)).setText(FFMediaPlayer.GetFFmpegVersion());
    }
}
  1. 运行App

image.png

参考资料

FFmpeg 开发(01):FFmpeg 编译和集成