FFmpeg: mac下手動編譯android上使用的FFmpeg(支持x86、armeabi-v7a、arm64-v8a)


之前一直在linux下編譯FFmpeg,最近換電腦了,嘗試了下在mac下編譯ffmpeg,特記錄之。

一. 准備工作

1. 下載FFmpeg。(http://ffmpeg.org/download.html#releases),看了下最新的是4.1.4,我用的是3.4.6。

2. 下載mac上使用的NDK。(https://developer.android.google.cn/ndk/downloads/index.html), 我用的是android-ndk-r14b-darwin-x86_64.zip,不推薦使用ndk-bundle,有些頭文件在ndk-bundle里沒有,遇到過一次。

3. 修改FFmpeg3.4.6目錄下的configure文件。如果不修改的話之后編譯生成的版本號會加在so的后面,會導致android不能識別,切記。

修改的內容如下,將:

SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'

修改成:

SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'

4. 編寫編譯用的腳本文件。這個網上有很多,我找到了兩個可以用的:

用於編譯 armeabi-v7a、arm64-v8a、x86_64版本的:

#!/bin/bash

echo "進入FFmpeg編譯腳本"

# NDK環境    
export NDK=/Users/haiyuegao/Library/Android/android-ndk-r14b
export SYSROOT=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
PREFIX=android-build

COMMON_OPTIONS="\
    --prefix=android/ \
    --target-os=android \
    --enable-shared \
    --enable-runtime-cpudetect \
    --enable-small \
    --disable-static \
    --disable-debug \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-doc \
    --disable-symver \
    --disable-asm \
    --disable-stripping \
    --disable-armv5te \
    "

function build_android {

    echo "開始編譯FFmpeg..."

    # armeabi
    echo "開始編譯FFmpeg(armeabi)"
    ./configure \
    --libdir=${PREFIX}/libs/armeabi \
    --incdir=${PREFIX}/include/armeabi \
    --pkgconfigdir=${PREFIX}/pkgconfig/armeabi \
    --arch=arm \
    --cpu=armv6 \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$SYSROOT \
    --extra-ldexeflags=-pie \
    ${COMMON_OPTIONS}
    make clean
    make -j8 && make install
    echo "結束編譯FFmpeg(armeabi)"

    # armeabi-v7a
    echo "開始編譯FFmpeg(armeabi-v7a)"
    ./configure \
    --libdir=${PREFIX}/libs/armeabi-v7a \
    --incdir=${PREFIX}/include/armeabi-v7a \
    --pkgconfigdir=${PREFIX}/pkgconfig/armeabi-v7a \
    --arch=arm \
    --cpu=armv7-a \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$SYSROOT \
    --extra-cflags="-march=armv7-a -mfloat-abi=softfp -mfpu=neon" \
    --extra-ldexeflags=-pie \
    ${COMMON_OPTIONS}
    make clean
    make -j8 && make install
    echo "結束編譯FFmpeg(armeabi-v7a)"

    # arm64-v8a
    echo "開始編譯FFmpeg(arm64-v8a)"
    ./configure \
    --libdir=${PREFIX}/libs/arm64-v8a \
    --incdir=${PREFIX}/include/arm64-v8a \
    --pkgconfigdir=${PREFIX}/pkgconfig/arm64-v8a \
    --arch=aarch64 \
    --cpu=armv8-a \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$SYSROOT \
    --extra-ldexeflags=-pie \
    ${COMMON_OPTIONS} 
    make clean
    make -j8 && make install
    echo "結束編譯FFmpeg(arm64-v8a)"

    # x86_64
    echo "開始編譯FFmpeg(x86_64)"
    ./configure \
    --libdir=${PREFIX}/libs/x86_64 \
    --incdir=${PREFIX}/include/x86_64 \
    --pkgconfigdir=${PREFIX}/pkgconfig/x86_64 \
    --arch=x86_64 \
    --cpu=x86_64 \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$SYSROOT \
    --extra-ldexeflags=-pie \
    ${COMMON_OPTIONS}
    make clean
    make -j8 && make install
    echo "結束編譯FFmpeg(x86_64)"

    echo "編譯結束"

};
build_android
macos_build_android.sh

用於編譯x86版本的

#!/bin/bash

echo "進入FFmpeg編譯腳本"

# NDK環境    
export NDK=/Users/haiyuegao/Library/Android/android-ndk-r14b
export SYSROOT=$NDK/platforms/android-21/arch-x86
export TOOLCHAIN=$NDK/toolchains/x86-4.9/prebuilt/darwin-x86_64
PREFIX=android-build

COMMON_OPTIONS="\
    --prefix=android/ \
    --target-os=android \
    --enable-shared \
    --enable-runtime-cpudetect \
    --enable-small \
    --disable-static \
    --disable-debug \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-doc \
    --disable-symver \
    --disable-asm \
    --disable-stripping \
    --disable-armv5te \
    "

function build_android {

    echo "開始編譯FFmpeg..."

    # x86
    echo "開始編譯FFmpeg(x86)"
    ./configure \
    --libdir=${PREFIX}/libs/x86 \
    --incdir=${PREFIX}/include/x86 \
    --pkgconfigdir=${PREFIX}/pkgconfig/x86 \
    --arch=x86 \
    --cpu=i686 \
    --cross-prefix=$TOOLCHAIN/bin/i686-linux-android- \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32" \
    --extra-ldexeflags=-pie \
    ${COMMON_OPTIONS} 
    make clean
    make -j8 && make install
    echo "結束編譯FFmpeg(x86)"


    echo "編譯結束"

};
build_android
macos_build_android_x86.sh

需要注意的是:要根據自己電腦的實際環境修改下上面的NDK目錄地址,我的NDK地址是:

5. 修改腳本的執行權限。將腳本放置到ffmpeg的根目錄下,為其添加執行權限:

然后打開終端,cd到ffmpeg那級目錄,分別執行以下兩條命令(如果要編譯x86版本,就把shell腳本的名字換成x86版本的那個):

chmod 777 macos_build_android.sh 
./macos_build_android.sh

6. 開始編譯。視電腦的配置不同編譯的時間也不同,我電腦用了大約5分鍾左右完成了編譯。

等待編譯完成,稍后ffmpeg那級目錄會出現一個android-build文件夾,里面包含了我們會用到的庫文件和頭文件。

二. 集成FFmpeg到項目中

1. 新建一個支持C++的Android Studio 項目。

2. 拷貝ffmpeg庫文件和頭文件。將編譯生成的libs文件夾和include文件夾分別拷貝到項目的app目錄下:

3. 更新配置文件。

修改app目錄下的build.gradle文件:

添加下列代碼到gradle文件:

    externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
            ndk {
                abiFilters "armeabi-v7a","x86"
            }
        }
        sourceSets {
            main {
                jniLibs.srcDirs=['libs']
            }
        }
externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

最終文件如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.yongdaimi.android.ffapitest"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
            ndk {
                abiFilters "armeabi-v7a","x86"
            }
        }
        sourceSets {
            main {
                jniLibs.srcDirs=['libs']
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
build.gradle

修改app目錄下的CMakeLists.txt文件:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# 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.


# 添加頭文件路徑
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/${ANDROID_ABI})


# 設置FFmpeg庫所在路徑
set(FF ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI})

# 打印當前位置信息
message("Current souce file positon: " ${CMAKE_CURRENT_SOURCE_DIR})


# avcodec
add_library(avcodec SHARED IMPORTED)
set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${FF}/libavcodec.so)


add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp)

# 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.
        native-lib
        avcodec

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})
CMakeLists.txt

最終目錄結構如下:

這里一定要注意CMakeLists.txt這個文件的位置,我在Windows下創建AS項目時這個文件在app目錄下,在Mac上創建AS項目時這個文件卻在app/src/main/cpp目錄下,個人為了保持習慣,便將這個文件仍然移動到了app目錄下,若使用Mac上默認的位置,則上述CMakeLists.txt和build.gradle中聲明文件的位置部分也需要修改,切記。

4. 添加測試代碼。打開native-lib.cpp,修改已有的JNI方法:

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


extern "C" {
#include <libavcodec/avcodec.h>
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_yongdaimi_android_ffapitest_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

    hello += avcodec_configuration();


    return env->NewStringUTF(hello.c_str());
} 

由於FFmpeg是使用純C編寫的,因此在導入其頭文件的時候要注意使用extern "C" 大括號包裹。

之后運行項目,如果能出現相關的配置信息就說明FFmpeg已經加載成功了。

三. 常見錯誤說明

1. Error computing CMake server result.

若編譯出現

Error computing CMake server result.
Check for working C compiler: D:/sdk/sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/bin/clang.exe
Check for working C compiler: D:/sdk/sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/bin/clang.exe -- works
Detecting C compiler ABI info
Detecting C compiler ABI info - done
Detecting C compile features
Detecting C compile features - done
Check for working CXX compiler: D:/sdk/sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/bin/clang++.exe
Check for working CXX compiler: D:/sdk/sdk/ndk-bundle/toolchains/llvm/prebuilt/windows-x86_64/bin/clang++.exe -- works
Detecting CXX compiler ABI info
Detecting CXX compiler ABI info - done
Detecting CXX compile features
Detecting CXX compile features - done
Boost version: 1.70.0
Configuring for JNI
Configuring done

則打開Build的Toggle View按鈕,根據Log分析出錯的詳細原因,看看是native-lib.cpp的文件找不到或者是其它問題。

2. ninja error needed by missing and no known rule to make it

Error:error: '../../../../src/main/jniLibs/x86/libstblur_preview_api.so', 
needed by '../../../../build/intermediates/cmake/debug/obj/x86/libjnistblur_preview_api.so', missing and no known rule to make it

這種錯誤一般是缺失了某個平台的庫導致的,首先查看具體缺失的是哪個平台的so庫,如果該平台的庫不需要,可以在app下的build.gradle中聲明不加載:

     ndk {
            // 聲明只使用這兩個版本的庫,否則默認的可是4個平台
            abiFilters 'armeabi-v7a','arm64-v8a'
        }

或者是檢查下CMakeLists.txt中FFmpeg庫的路徑聲明是否正確:

比如我這里聲明的庫路徑就是app/libs/具體的平台。

 

參考鏈接:

1.  FFmpeg編譯For Android(Mac)

2. 在Mac中編譯Android平台的FFmpeg( arm和x86

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM