• 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系统版本:

    image-20220912161223454

    • 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    遇到的问题:

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

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

    image-20220912135245701

    image-20220912135043727

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

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

    image-20220912135146463

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

    image-20220912161946847

    注意这里的路径:

    image-20220912162201368

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

    以我的为例:

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

    2. FFmpeg 环境配置

    image-20220912162957090

    使用 Android studio 打开configure文件,

    • 搜索CMDLINE_SET,新增cross_prefix_clang

    image-20220912163145572

    • 修改编译工具路径设置
      image-20220912163417600

    • 新建编译脚本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 '编译结束...'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    最后执行脚本即可

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

    ./build_android_so.sh

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

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

    image-20220912163809215

    后面可以将此 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++的项目

    image-20220913225536812

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

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

      image-20220913225757384

    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} )
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
    3. 设置 native-lib.cpp

    #include 
    #include 
    
    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    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'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    最后运行成功。

    image-20220913230323299

    代码已上传到 GitHub

    集成问题记录

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

    NDK中有文件如图所示:

    image-20220912232031736

    编译时出错。

    image-20220912231720563

    image-20220912232302708

    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} )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    native-lib.cpp 如下:

    #include 
    #include 
    #include 
    #include 
    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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    写在最后

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

    参考资料:

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

  • 相关阅读:
    MySQL数据的ONLINE DDL操作测试
    进程与线程
    Moonbeam通过新的LayerZero部署扩展跨链能力
    FPGA project : usrt_rs232
    【计算机毕业设计】JAVA SpringBoot少儿编程课程管理网站
    mmsegmentation V0.27.0环境搭建(一)
    Servlet概述及接口
    从零手搓一个【消息队列】实现数据的硬盘管理和内存管理(线程安全)
    驱动初级Day03_内核模块下_参数和依赖
    Flask框架:运用Ajax轮询动态绘图
  • 原文地址:https://blog.csdn.net/jun5753/article/details/126819292