Mac 平台 Android FFmpeg 编译与集成实践

Posted 小羊子说

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mac 平台 Android FFmpeg 编译与集成实践相关的知识,希望对你有一定的参考价值。

文章目录

FFmpeg 是什么

FFmpeg 既是一款音视频编解码工具,同时也是一组音视频编解码开发套件,作为编解码开发套件,他为开发者提供了丰富的音视频处理的调用接口。

​ FFmpeg 提供了多种媒体格式的封装和解封装,包括多种音视频编码、多种协议的流媒体、多种色彩格式转换、多种采样率转换、多种码率转换等; FFmpeg 框架提供了多种丰富的插件模块,包含封装与解封转的插件、编码与解码的插件等。

官方地址:https://www.ffmpeg.org/

为什么要学 FFmpeg 开发

FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发。

FFmpeg 读作 “ef ef em peg” ,其中的 “FF” 指的是 “Fast Forward”,“mpeg” 则是 “Moving Picture Experts Group” (动态图像专家组)。

FFmpeg 项目功能复杂而庞大,基本上支持所有常见的音视频处理操作,如封装格式转换、音视频转码、音视频播放和剪辑、视频添加水印滤镜等。

尽管 FFmpeg 功能强大,但是由于其采用的是带有传染性的 LGPL/GPL 开源协议,所以一些大厂基本上都是自己独立开发类似的音视频处理库,甚至在接口和组织模块上模仿 FFmpeg 。

因此,学习 FFmpeg 不仅能够帮助你掌握音视频开发的相关知识脉络,还能让你快速适应不同的音视频处理框架。

FFmpeg 编译

FFmpeg 有六个常用的功能模块:

  • libavformat:多媒体文件或协议的封装和解封装库,如 Mp4、Flv 等文件封装格式,RTMP、RTSP 等网络协议封装格式;
  • libavcodec:音视频编解码库;
  • libavfilter:音视频、字幕滤镜库;
  • libswscale:图像格式转换库;
  • libswresample:音频重采样库;
  • libavutil:工具库;

本文主要是帮助初学者快速上手 FFmpeg 的编译。

1. 编译环境准备

  • MAC系统版本:

  • NDK版本:

    NDK 从 android Studio 中配置中直接下载

    版本号为:25.1.8937393

  • ffmpeg-5.0.1

编译前准备:

mac 环境

//1. 下载 ffmpeg-5.0.1
wget https://ffmpeg.org/releases/ffmpeg-5.0.1.tar.bz2

//2. 解压 FFmpeg  也可以直接解压
tar -jxvf ffmpeg-5.0.1.tar.bz2

遇到的问题:

在官网的链接中下载的 ndk 格式为 dmg 的 如何安装呢?(暂时没有解决,换成了其他方式下载)

https://developer.android.google.cn/ndk/downloads?hl=zh_cn

此种方式没有用,不能解压成功,如果大家能处理成功,求指导。

于是根据官方文档的提示 在Android studio中去下载

最后找到NDK的路径,后面的脚本需要用:

注意这里的路径:

刚开始我用的是 :~/Libraray/…… 这里的缩写路径,在文件夹右健进入 iterm 后出现的。不能用,需要用 pwd来找到绝对的路径。

以我的为例:

/Users/yangjun/Library/Android/sdk/ndk/25.1.8937393

2. FFmpeg 环境配置

使用 Android studio 打开configure文件,

  • 搜索CMDLINE_SET,新增cross_prefix_clang

  • 修改编译工具路径设置

  • 新建编译脚本build_android_so.sh

3. 完整的脚本

#!/bin/bash

#配置NDK路径 需要以你的为准
export NDK=/Users/xiaoyangzishuo/Library/Android/sdk/ndk/21.4.7075529
#配置toolchain路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
#配置交叉编译环境的根路径
SYSROOT=$TOOLCHAIN/sysroot

#arm64-v8a
API=30
ARCH=arm64
CPU=armv8-a
CROSS_PREFIX="$TOOLCHAIN/bin/aarch64-linux-android"
CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/aarch64-linux-android$API"

#armeabi-v7a
#API=30
#ARCH=arm
#CPU=armv7-a
#CROSS_PREFIX="$TOOLCHAIN/bin/arm-linux-androideabi"
#CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/armv7a-linux-androideabi$API"

OPTIMIZE_CFLAGS="-march=$CPU"
#配置so输出路径
OUTPUT=/Users/xiaoyangzishuo/Documents/android/FFmpeg/ffmpeg-5.0.1/out/android/$CPU

fun build

    ./configure \\
    --prefix=$OUTPUT \\
    --target-os=android \\
    --arch=$ARCH \\
    --cpu=$CPU \\
    --enable-neon \\
    --enable-cross-compile \\
    --enable-shared \\
    --enable-jni \\
    --disable-static \\
    --disable-asm \\
    --disable-doc \\
    --disable-ffplay \\
    --disable-ffprobe \\
    --disable-symver \\
    --disable-ffmpeg \\
    --disable-avdevice \\
    --disable-debug \\
    --disable-postproc \\
    --sysroot=$SYSROOT \\
    --cross-prefix=$CROSS_PREFIX- \\
    --cross_prefix_clang=$CROSS_PREFIX_CLANG- \\
    --extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS"
    
    make clean all
    make -j8
    make install


echo '编译开始...'
build
echo '编译结束...'

最后执行脚本即可

chmod +x build_android_so.sh (如果没有权限,先添加权限,之后不再需要)

./build_android_so.sh

脚本已经执行成功,这里有个提示。先忽略。

大概 5-10 分钟后,得到了 6个 so 库 编译成功。

后面可以将此 so 放到 Android Stuido中集成。

4. FQA

后面完善。

编译小结

上面编译 ffmpeg 脚本流程:shell -> configure -> make
首先我们编写 build_android_so.sh 脚本,调用 configure 配置参数,最后调用 make 命令生成动态库。

其中的 make 就是调用 makefile,makefile 是一种构建工具,类似于Gradle(构建.apk)、Maven(构建.war)
遇到最大的问题,是环境的路径的配置,脚本很简单,也有一些莫名奇妙的小问题,不过不影响编译成功。

在Android Studio 中的集成

  1. 先建立C++的项目

按上图所示例一步一步运行,配置好ndk环境。目的在于接下来的操作的文件先准备好。

  1. 将 so库及头文件 copy到项目中来

  2. 设置 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.18.1)
    
    # 支持gnu++11
    set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -std=gnu++11")
    
    # 1. 定义so库和头文件所在目录,方面后面使用
    set(ffmpeg_lib_dir $CMAKE_SOURCE_DIR/../jniLibs/$ANDROID_ABI)
    set(ffmpeg_head_dir $CMAKE_SOURCE_DIR)
    
    
    # 2. 添加头文件目录
    include_directories($ffmpeg_head_dir/include)
    
    # 3. 添加ffmpeg相关的so库
    add_library( avutil
            SHARED
            IMPORTED )
    set_target_properties( avutil
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libavutil.so )
    
    add_library(
            # 生成的库的名字
            swresample
            # 动态库
            SHARED
            # 源文件
            IMPORTED )
    set_target_properties( swresample
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libswresample.so )
    
    add_library(
            avcodec
            SHARED
            IMPORTED )
    set_target_properties(
            avcodec
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libavcodec.so )
    
    add_library(
            avfilter
            SHARED
            IMPORTED)
    set_target_properties(
            avfilter
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libavfilter.so )
    
    add_library(
            swscale
            SHARED
            IMPORTED)
    set_target_properties(
            swscale
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libswscale.so )
    
    add_library(
            avformat
            SHARED
            IMPORTED)
    set_target_properties(
            avformat
            PROPERTIES IMPORTED_LOCATION
            $ffmpeg_lib_dir/libavformat.so )
    
    # 查找代码中使用到的系统库
    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 )
    
    # 配置目标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).
            native-lib.cpp
            )
    
    # 指定编译目标库时,cmake要链接的库
    target_link_libraries(
            # 指定目标库,native-lib 是在上面 add_library 中配置的目标库
            native-lib
    
            # 4. 连接 FFmpeg 相关的库
            avutil
            swresample
            avcodec
            avfilter
            swscale
            avformat
            #        avdevice
    
            # Links the target library to the log library
            # included in the NDK.
            $log-lib )
    
  3. 设置 native-lib.cpp

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

extern "C" 
#include "include/libavutil/avutil.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_juneyang_cdemo_MainActivity_ffmpegInfo(JNIEnv *env, jobject thiz) 
// 返回 ffmpeg 配置信息
return env->NewStringUTF(avutil_configuration());

  1. 最后设置build.gradle
plugins 
  id 'com.android.application'
  id 'org.jetbrains.kotlin.android'


android 
  compileSdk 32

  defaultConfig 
    applicationId "com.juneyang.cdemo"
    minSdk 26
    targetSdk 32
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    externalNativeBuild 
      cmake 
        cppFlags '-std=c++17'
      
      // abiFilters
      ndk 
        abiFilters "arm64-v8a"
      
    
  

  buildTypes 
    release 
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    
  
  compileOptions 
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  
  kotlinOptions 
    jvmTarget = '1.8'
  
  externalNativeBuild 
    cmake 
      path file('src/main/cpp/CMakeLists.txt')
      version '3.18.1'
    
  
  buildFeatures 
    viewBinding true
  
  sourceSets 
    main 
      //将依赖库打进apk,否则可能出现找不到库
      jniLibs.srcDirs = ['libs']
    
  


dependencies 
  implementation fileTree(dir: 'libs', include: ['*.jar'])
  implementation 'androidx.core:core-ktx:1.7.0'
  implementation 'androidx.appcompat:appcompat:1.3.0'
  implementation 'com.google.android.material:material:1.4.0'
  implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
  testImplementation 'junit:junit:4.13.2'
  androidTestImplementation 'androidx.test.ext:junit:1.1.3'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

最后运行成功。

代码已上传到 GitHub

集成问题记录

目前出现一个问题,明明 NDK中有该文件,编译时却找不到。

NDK中有文件如图所示:

编译时出错。

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.18.1)

# 支持gnu++11
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -std=gnu++11")

# 1. 定义so库和头文件所在目录,方面后面使用
set(ffmpeg_lib_dir $CMAKE_SOURCE_DIR/../jniLibs/$ANDROID_ABI)
set(ffmpeg_head_dir $CMAKE_SOURCE_DIR)

# 2. 添加头文件目录
include_directories($ffmpeg_head_dir/include)

# 3. 添加ffmpeg相关的so库
add_library( avutil
        SHARED
        IMPORTED )
set_target_properties( avutil
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libavutil.so )

add_library(
        # 生成的库的名字
        swresample
        # 动态库
        SHARED
        # 源文件
        IMPORTED )
set_target_properties( swresample
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libswresample.so )

add_library(
        avcodec
        SHARED
        IMPORTED )
set_target_properties(
        avcodec
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libavcodec.so )

add_library(
        avfilter
        SHARED
        IMPORTED)
set_target_properties(
        avfilter
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libavfilter.so )

add_library(
        swscale
        SHARED
        IMPORTED)
set_target_properties(
        swscale
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libswscale.so )

add_library(
        avformat
        SHARED
        IMPORTED)
set_target_properties(
        avformat
        PROPERTIES IMPORTED_LOCATION
        $ffmpeg_lib_dir/libavformat.so )

#add_library(
#        avdevice
#        SHARED
#        IMPORTED)

#set_target_properties( avdevice
#        PROPERTIES IMPORTED_LOCATION
#        $ffmpeg_lib_dir/libavdevice.so )

# 查找代码中使用到的系统库
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 )

# 配置目标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).
        native-lib.cpp
        )

# 指定编译目标库时,cmake要链接的库
target_link_libraries(
        # 指定目标库,native-lib 是在上面 add_library 中配置的目标库
        native-lib

        # 4. 连接 FFmpeg 相关的库
        avutil
        swresample
        avcodec
        avfilter
        swscale
        avformat
#        avdevice

        # Links the target library to the log library
        # included in the NDK.
        $log-lib )

native-lib.cpp 如下:

#include <jni.h>
#include <unistd.h>
#include <cstdint>
#include <string>
extern "C" 
#include "include/libavcodec/avcodec.h"

extern "C" JNIEXPORT jstring JNICALL
Java_com_juneyang_cdemo_MainActivity_ffmpegInfo(JNIEnv *env, jobject thiz) 
  // 返回ffmpeg配置信息
  return env->NewStringUTF(avcodec_configuration());

编译时出错,最后在在 avcodec_configuration引用时选择了几个不同的头文件,并检查了 CmakeLists.txt中的头文件路径,几次尝试后解决了,这里有点困惑。

写在最后

折腾了两个晚上,记录了整个编译流程和集成的问题,如有问题,欢迎指正。

参考资料:

https://zhuanlan.zhihu.com/p/498073893

以上是关于Mac 平台 Android FFmpeg 编译与集成实践的主要内容,如果未能解决你的问题,请参考以下文章

Mac 平台 Android FFmpeg 编译与集成实践

Mac OS 编译 Android 平台 FFmpeg

Mac OS 编译 Android 平台 FFmpeg

Mac中编译FFmpeg教程(Android版)

FFmpeg4.1编译:mac+android-ndk-14b+ffmpeg4.1成功编译

FFmpeg4.1编译:mac+android-ndk-14b+ffmpeg4.1成功编译