代码改变世界

Mac平台交叉编译Android/iOS FFmpeg库

2021-05-30 22:36  jiayayao  阅读(902)  评论(1编辑  收藏  举报

一、前提

    FFmpeg版本必须与系统版本(iOS)和ndk版本(Android)相匹配,否则就会有各种奇葩的编译问题。有些是ffmpeg代码与iOS系统库冲突,有些是ffmpeg代码与ndkd版本冲突,解决这些问题很浪费时间,且毫无意义。

二、iOS

    1. 版本

    FFmpeg:4.4

    macOS:11.2.2

    2. 步骤

  • github上下载ffmpeg;
  • github上下载gas-preprocessor.pl,并拷贝至/usr/local/bin下,添加777权限;
  • mac安装Command Line Tools
  • 下载编译脚本:https://github.com/kewlbear/FFmpeg-iOS-build-script
  • 修改build-ffmpeg.sh脚本种的FF_VERSION为你的版本号,修改SOURCE为你的ffmpeg路径
  • ./build-ffmpeg.sh 

三、Android

    1. 版本

    FFmpeg:4.2.1(建议无历史包袱的项目采用较新版本的FFmpeg)

    ndk:r21e

    2. 编译脚本

#!/bin/bash
# 以下路径需要修改成自己的NDK目录
TOOLCHAIN=/Users/jiayayao/Library/Android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/darwin-x86_64
# 最低支持的android sdk版本
API=21

function build_android
{
echo "Compiling FFmpeg for $CPU"
./configure \
 --prefix=$PREFIX \
 --disable-hwaccels \
 --disable-gpl \
 --disable-postproc \
 --enable-shared \
 --enable-jni \
 --disable-mediacodec \
 --enable-small \
 --enable-gpl \
 --disable-decoder=h264_mediacodec \
 --disable-static \
 --disable-doc \
 --disable-programs \
 --disable-ffmpeg \
 --disable-ffplay \
 --disable-ffprobe \
 --disable-avdevice \
 --disable-symver \
 --enable-neon \
 --disable-asm \
 --cross-prefix=$CROSS_PREFIX \
 --target-os=android \
 --arch=$ARCH \
 --cpu=$CPU \
 --cc=$CC \
 --cxx=$CXX \
 --enable-cross-compile \
 --sysroot=$SYSROOT \
 --extra-cflags="-mno-stackrealign -Os $OPTIMIZE_CFLAGS -fPIC" \
 --extra-ldflags="$ADDI_LDFLAGS" \
 $ADDITIONAL_CONFIGURE_FLAG
make clean
make -j16
make install
echo "The Compilation of FFmpeg for $CPU is completed"
}

# armv8-a
ARCH=arm64
CPU=armv8-a
# r21版本的ndk中所有的编译器都在/toolchains/llvm/prebuilt/darwin-x86_64/目录下(clang)
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
# NDK头文件环境
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
# so输出路径
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android

# 交叉编译工具目录,对应关系如下
# armv8a -> arm64 -> aarch64-linux-android-
# armv7a -> arm -> arm-linux-androideabi-
# x86 -> x86 -> i686-linux-android-
# x86_64 -> x86_64 -> x86_64-linux-android-

# CPU架构
# armv7-a
ARCH=arm
CPU=armv7-a
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
build_android

# x86
ARCH=x86
CPU=x86
CC=$TOOLCHAIN/bin/i686-linux-android$API-clang
CXX=$TOOLCHAIN/bin/i686-linux-android$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/i686-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
build_android

# x86_64
ARCH=x86_64
CPU=x86-64
CC=$TOOLCHAIN/bin/x86_64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/x86_64-linux-android$API-clang++
SYSROOT=$TOOLCHAIN/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/x86_64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU -msse4.2 -mpopcnt -m64 -mtune=intel"
# 方法调用
build_android

 3. Android Studio使用编译好的so:

a, Android Studio新建native项目,build.gradle添加如下代码: 

    defaultConfig {
        applicationId "com.example.ffmpegtest"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags '-std=c++11'
            }
        }
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
    splits {
        abi {
            enable true
            reset()
            include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
            universalApk true
        }
    }

 b, CMakeList.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.10.2)

# Declares and names the project.

project("ffmpegtest")

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

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).
             native-lib.cpp )

set(JNI_LIBS_DIR ${CMAKE_SOURCE_DIR}/../ffmpeg)

add_library(avutil
        SHARED
        IMPORTED )
set_target_properties(avutil
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavutil.so )

add_library(avresample
        SHARED
        IMPORTED )
set_target_properties(avresample
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavresample.so )

add_library(swresample
        SHARED
        IMPORTED )
set_target_properties(swresample
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libswresample.so )

add_library(swscale
        SHARED
        IMPORTED )
set_target_properties(swscale
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libswscale.so )

add_library(avcodec
        SHARED
        IMPORTED )
set_target_properties(avcodec
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavcodec.so )

add_library(avformat
        SHARED
        IMPORTED )
set_target_properties(avformat
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavformat.so )

add_library(avfilter
        SHARED
        IMPORTED )
set_target_properties(avfilter
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavfilter.so )

add_library(avdevice
        SHARED
        IMPORTED )
set_target_properties(avdevice
        PROPERTIES IMPORTED_LOCATION
        ${JNI_LIBS_DIR}/${ANDROID_ABI}/lib/libavdevice.so )

include_directories(${JNI_LIBS_DIR}/${ANDROID_ABI}/include)

target_link_libraries(native-lib
        avutil swresample swscale avcodec avformat avfilter
        ${log-lib} ${android-lib} )

  c, native-lib.cpp添加如下代码:

#include <jni.h>
#include <string>
#include <android/log.h>


extern "C" {
#include <libavformat/avformat.h>
}

void ffmpeg_log_output(void* ptr, int level, const char* fmt, va_list vl) {
    __android_log_print(ANDROID_LOG_INFO, "jiayayao", "[ffmpeg log]%s", fmt);
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ffmpegtest_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    av_log_set_callback(ffmpeg_log_output);
    AVFormatContext *pContext = avformat_alloc_context();
    avformat_open_input(&pContext, "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8", nullptr, nullptr);
    avformat_find_stream_info(pContext, nullptr);
    std::string hello = "jiayayao";
    return env->NewStringUTF(hello.c_str());
}

 d, AndroidManifest.Xml添加如下权限以联网:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

e, 日志显示ffmpeg打开url成功:

I/jiayayao: [ffmpeg log]stream %d: start_time: %0.3f duration: %0.3f [ffmpeg log]format: start_time: %0.3f duration: %0.3f bitrate=%lld kb/s 
I/jiayayao: [ffmpeg log]After avformat_find_stream_info() pos: %lld bytes read:%lld seeks:%d frames:%d

f, github链接:https://github.com/jiayayao/ffmpegtest