Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目

使用NDK交叉编译ffmpeg

    • 前言
    • 必备基础
    • 准备工作
    • 编写ffmpeg编译脚本
    • Android 项目集成
        • 新建项目
        • 导入ffmpeg
        • 集成测试

前言

最近在学习android NDK开发相关内容,借ffmpeg练练手。ffmpeg是做音视频方面功能的基础,后面会随着个人的学习更新一系列ffmpeg博客,防止自己遗忘。
这个系列博客主要目的是基于ffmpeg通过NDK开发的方式完成一个基本的视频播放器。

本篇博客主要实现了 ffmpeg编译 以及 引入 android 项目成功调用 静态库方法显示版本号。

必备基础

  1. c、c++基础语法;Cmake基础(常用配置、常量);
  2. NDK JNI基础(熟悉java/kotlin 、c++ 互调);
  3. kotlin基础(android 项目全部使用kotlin编写);
  4. linux常用命令、shell脚本基础语法;

准备工作

编译ffmpeg时使用Linux或者Mac都可以;如果是Mac需要安装gcc,使用gcc -v输出版本号即为成功。

ffmpeg(4.0.2)
ffmpeg全版本下载地址:
http://www.ffmpeg.org/releases/

NDK(android-ndk-r17c)
NDK历史版本下载地址:
https://developer.android.google.cn/ndk/downloads/older_releases

ffmpeg下载完成后解压缩;进入ffmpeg解压后到目录

cd ./ffmpeg-4.0.2

ffmpeg编译时有很多命令参数,查看命令参数命令:

./configure --help

编写ffmpeg编译脚本

为了方便直接在 ffmpeg-4.0.2 目录里新建shell脚本

vim ./ffmpeg-4.0.2/build_ffmpeg_4.0.2.sh

输入以下内容;
注意:每一行脚本都有对应的注释,需要仔细查看,直接复制可能会编译失败,NDK工具包的相关路径要改成自己的。
NDK_SRC 和 NDK_GCC 的目录可能不同!!!
NDK_GCC 的 arm-linux-androideabi-4.9 目录根据不同的平台路径不同 本次实现仅实现了armeabi-v7a平台!!!

#!/bin/bash

# NDK工具包目录
NDK_SRC=/Users/tttell/tools/android-ndk-r17c

# 此变量执行ndk中的交叉编译gcc所在目录
NDK_GCC=$NDK_SRC/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64

# 传递给gcc的参数 [从Android Project的 externalNativeBuild/xxx/build.ninja 中复制到参数 可以减少警告信息]
FLAGS="-isystem $NDK_SRC/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=21 -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"

# 导入必需的头文件 [NDK 工具包的目录]
INCLUDES=" -isystem $NDK_SRC/sources/android/support/include"

# 编译后输出的目录 [根据自己情况自定义目录]
PREFIX=./bulid_ffmpeg_lib

./configure \
--prefix=$PREFIX \
--enable-static \
--enable-small \
--enable-cross-compile \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_SRC/sysroot/" \
--sysroot=$NDK_SRC/platforms/android-21/arch-arm \
--arch=arm \
--target-os=android

make clean

make install

# 参数注释
# --enable-static 				# 编译静态库
# --enable-small 				# 体积优化
# --enable-cross-compile 		# 开启交叉编译
# --disable-programs 			# 不编译ffmpeg命令行工具 [如果需要可以编译]
# --disable-avdevice 			# 不编译avdevice模块 [--help 提示中表示该模块不支持Android平台]
# --disable-encoders 			# 不编译编码器 [本系列仅实现播放功能 所以不需要编码]
# --disable-muxers  			# 不编译封装器 [本系列仅实现播放功能 所以不需要封装]
# --disable-filters 			# 不编译所有滤镜
# --cross-prefix 				# 指定使用NDK工具包中的gcc相关编译器
# --extra-cflags 				# gcc的参数
# --sysroot 					# 平台标准库 [具体根据android项目的平台、最低支持版本而定 本次实现仅编译了armeabi-v7a 最低支持sdk21	]
# --arch           				# 平台
# --target-os 					# 平台

开始执行脚本

sh ./ffmpeg-4.0.2/build_ffmpeg_4.0.2.sh

脚本运行时,要关注日志输出,如果出现error日志就及时停止吧 肯定会失败的 不用等到之行结束;编译过程可能会出现各种错误,具体错误可能跟环境也有关系,根据提示信息去搜索对应解决办法修改即可。

如果编译失败,输出目录中应该是空的,需要查看下终端的输出日志排查错误;
编译成功则会在你指定的输出目录下出现以下文件:
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第1张图片
include 目录是待会需要导入到Android Project中的头文件;
lib 目录中就是成功编译出的静态库文件;
share 目录中的examples是一些例子 都是c语言的例子;

Android 项目集成

新建项目

Android Studio 版本 我这应该是最新的
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第2张图片
新建一个支持Native的项目
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第3张图片
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第4张图片
然后一直下一步就可以了。

打开项目后需要在SDKManager中下载NDK和Cmake:

NDK 不用全部下载,我是为了调试不同版本全部下载了 下载21左右的就可以。
Cmake 下载的是最新版本的。

接着修改 app 目录的 build.gradle 文件:

android {

...

	defaultConfig {
		externalNativeBuild {
    		cmake {
        		abiFilters "armeabi-v7a"
    		}
		}
		ndk {
    		abiFilters("armeabi-v7a")
		}
	}
	
	# 为了开发方便不写findviewbyId 我开启了 databinding
	dataBinding {
        enabled = true
    }
...

}

下载完成后同步一下项目,运行下测试下 如果页面成功显示了 Hello from C++ 即为成功;

导入ffmpeg

将编译好的 ffmpeg 头文件目录 include 复制到 android project 的 cpp 目录下:
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第5张图片
接着开始导入静态库,在 cpp 目录下新建 libs 目录;因为上一步骤中编译的是 armeabi-v7a 的 ffmpeg 所以在libs下新建 armeabi-v7a 目录(实际开发中会打多个平台的库文件,如:x86_64需要新建x86_64的目录)。将静态库文件复制到 armeabi-v7a 目录中:
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第6张图片
导入工作完成后 开始编写 CMakeLists.txt :

cmake_minimum_required(VERSION 3.18.1)

project("studyffmepg")

# 导入 include目录的 库文件
# CMAKE_SOURCE_DIR 是常量 代表CMake文件所在目录
include_directories(${CMAKE_SOURCE_DIR}/include)

# 批量导入源文件 定义一个 SOURCE 变量
# 这样写在cpp目录下新建c++文件时不用再一个个写导入了
file(GLOB SOURCE *.cpp *.c)

# 添加动态库
add_library(
        studyffmepg # 生成库的名字
        SHARED      # SHARED 动态库 STATIC 静态库
        ${SOURCE}   # 源文件 上面定义的变量 表示全部打包进去
)

# 在NDK工具包中寻找 log 库
find_library(
        log-lib
        log
)

# 导入FFmpeg的库文件
# CMAKE_CXX_FLAGS 类似于 windows 环境变量中的 path;设置目录后会去目录下自动寻找库文件
# -L 代表追加目录
# CMAKE_ANDROID_ARCH_ABI 表示运行的平台(如:armeabi-v7a) 因为在 app:build.gradle中指定了平台所以这里可以读取到运行的平台
# 注意我这里的 -L 后的路径 /libs 对应上一步骤新建的目录 如果你的目录不同切记修改 否则会找不到依赖库
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}")

target_link_libraries(
        studyffmepg
        ${log-lib}
        # ffmpeg的静态库有依赖关系 使用 -Wl,--start-group -Wl,--end-group 方式写 可以忽略顺序 否则容易出错
        -Wl,--start-group
        libavcodec.a libavfilter.a libavformat.a libavutil.a libswresample.a libswscale.a
        -Wl,--end-group
)

完成之后同步一下。

集成测试

修改 native-lib.cpp 文件,输出一下 ffmpeg 的版本号

#include 
#include 

using namespace std;

extern "C" {
#include 
}

extern "C" JNIEXPORT jstring JNICALL
Java_top_sunhy_studyffmepg_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject jobj
) {
    //std::string hello = "Hello from C++";
    string s = "集成成功!FFmpeg Version:";
    s.append(av_version_info());

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

完成后运行一下,出现版本号即为成功:
Android ffmpeg入门(1)—— 使用NDK交叉编译ffmpeg集成到Android项目_第7张图片

你可能感兴趣的:(ANdroid,NDK,开发学习,android,音视频,ffmpeg,android,ndk,ndk)