文章目录

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

Mac 平台 Android FFmpeg 编译与集成实践_音视频

  • 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​

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

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

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

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

Mac 平台 Android FFmpeg 编译与集成实践_封装_04

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

Mac 平台 Android FFmpeg 编译与集成实践_音视频_05

注意这里的路径:

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

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

以我的为例:

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

2. FFmpeg 环境配置

Mac 平台 Android FFmpeg 编译与集成实践_音视频_07

使用 Android studio 打开configure文件,

  • 搜索CMDLINE_SET,新增cross_prefix_clang

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

  • 修改编译工具路径设置
  • Mac 平台 Android FFmpeg 编译与集成实践_封装_09

  • 新建编译脚本​​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

Mac 平台 Android FFmpeg 编译与集成实践_音视频_10

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

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

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

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

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

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

  1. 将 so库及头文件 copy到项目中来
  2. Mac 平台 Android FFmpeg 编译与集成实践_macos_13

  3. 设置​​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. 设置​​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'
}

最后运行成功。

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

​代码已上传到 GitHub​

集成问题记录

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

NDK中有文件如图所示:

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

编译时出错。

Mac 平台 Android FFmpeg 编译与集成实践_封装_16

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

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​