一、FFmpeg简介
二、Linux系统下载NDK和FFmpeg压缩包
三、Linux系统下的交叉编译
1、什么是交叉编译?
在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译。这个编译过程就叫交叉编译。简单地说,就是在一个平台上生成另一个平台上的可执行代码。这里需要注意的是所谓平台,实际上包含两个概念:体系结构(Architecture)、操作系统(OperatingSystem)。同一个体系结构可以运行不同的操作系统;同样,同一个操作系统也可以在不同的体系结构上运行。举例来说,我们常说的x86 Linux平台实际上是Intel x86体系结构和Linux for x86操作系统的统称;而x86 WinNT平台实际上是Intel x86体系结构和Windows NT for x86操作系统的简称。
2、如何进行交叉编译?
(1)在刚才解压的ffmpeg里面创建一个Ubuntu_build.sh文件,并写入以下内容:
#!/bin/bash
# 首先定义一个NDK目录的变量 NDK_ROOT
NDK_ROOT=/root/Tools/android-ndk-r17c
# 此变量执行ndk中的交叉编译gcc所在目录
TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
#从as的 externalNativeBuild/xxx/build.ninja, 反正下面的配置,可以压制警告的意思
FLAGS="-isystem $NDK_ROOT/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"
INCLUDES=" -isystem $NDK_ROOT/sources/android/support/include"
# 1.定义编译后,所存放的目录
PREFIX=./android/arm
# 2.--enable-small 优化大小 非常重要,必须优化才行的哦
# 3.--disable-programs 不编译ffmpeg程序(命令行工具),我们是需要获取静态、动态库
# 4.--disable-avdevice 关闭avdevice模块,此模块在android中无用
# 5.--disable-encoders 关闭所有编码器(播放不需要编码)
# 6.--disable-muxers 关闭所有复用器(封装器),不需要生成mp4这样的文件,所有关闭
# 7.--disable-filters 关闭所有滤镜
# 8.--enable-cross-compile 开启交叉编译
# 9.--cross-prefix 看右边的值就知道是干嘛的,gcc的前缀..
# 10.disable-shared / enable-static (代表关闭动态库,开启 静态库)
# 11.--sysroot
# 12.--extra-cflags 会传给gcc的参数
# 13.--arch --target-os
./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-21/arch-arm \
--extra-cflags="$FLAGS $INCLUDES" \
--extra-cflags="-isysroot $NDK_ROOT/sysroot/" \
--arch=arm \
--target-os=android
make clean
make install
注意:NDK_ROOT必须改为自己下载的NDK存放路径,并且必须在ffmpeg里面创建一个android/arm文件侠,用来存放ffmpeg交叉编译后的文件!
(2)在以root权限在终端命令下打下ffmpeg,并输入 ./configure,再按回车,这时,如果你的系统没有安装gcc,则会报以下错误:
gcc is unable to create an executable file.
这时,我们需要安装一下gcc,可以输入以下命令安装:
yum install -y gcc
安装好后,我们再执行一下./configure命令,
我们发现还会报错,原因是我们没有关掉asm汇编相关的信息,可按提示执行./configure --disable-x86asm解决:
当输出一大堆ffmpeg相关信息的时候,就说明关闭成功了,这时我们就可以运行刚才写的Ubuntu_build.sh脚本了,即输入:sh Ubuntu_build.sh,按回车,此时,有可能会出错:
这是由于我们的linux系统在执行某些命令时缺少libtinfo.so.5导致的,所以,我们需要输入以下命令安装:
apt-get install libtinfo5
同时,sh脚本中也使用了make,所以也要安装一下make:
apt-get install make
安装完成后,再执行脚本:sh Ubuntu_build.sh,按回车键后系统就会开始编译ffmpeg,当编译完成后,打包ffmpeg目录下的android/arm文件夹,如果里面生成下面这三个文件夹,说明交叉编译成功:
四、在Android Studio中集成FFmpeg
1、创建一个Native C++工程。
2、把在Linux系统下交叉编译生成的ffmpeg库中的include复制下工程目录cpp下。
3、在cpp目录下创建一个armeabi-v7a文件侠(这个是手机CPU架构指令集,通常情况下只创建这个就可以,但个别手机可能要根据手机CPU去创建,这里不做细讲),然后把交叉编译生成的ffmpeg库中lib下面的.a文件都拷贝进去。
4、在build.grade里面的defaultConfig添加如下配置:
externalNativeBuild {
cmake {
// cppFlags "" // 这样写,默认是四大CPU架构平台
// 指定CPU架构是armeabi-v7a 【注意:这里只指定本地库到armeabi-v7a】
abiFilters "armeabi-v7a"
}
}
// TODO 指定CPU的架构 apk/lib/平台
// 下面代码不写,默认是四大CPU架构平台
ndk {
// 指定CPU架构是armeabi-v7a 【注意:这里只指定编译所有库到armeabi-v7a进apk】
abiFilters("armeabi-v7a")
}
5、在main/cpp/CMakeLists.txt文件中引用头文件以及库文件。
#引入头文件
include_directories(${CMAKE_SOURCE_DIR}/include)
#引入库文件
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/${CMAKE_ANDROID_ARCH_ABI}")
target_link_libraries(
native-lib
-Wl,--start-group # 忽略顺序引发的问题
avcodec avfilter avformat avutil swresample swscale
-Wl,--end-group
${log-lib} )
6、在native-lib.cpp里面调用头文件。
#include <jni.h>
#include <string>
extern "C"{
#include <libavutil/avutil.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_liyx_ffmdemo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "当前的FFmpeg的版本是:";
hello.append(av_version_info());
return env->NewStringUTF(hello.c_str());
}
7、MainActivity.java中调用native层代码。
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
8、activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/sample_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
9、运行。
最后,说下我的个人心得体会:
交叉编译只是NDK开发的入门基本,难点就在于我们对linux系统的不熟悉,我用的是windows电脑,需要在windows平台下安装linux系统,由于对linux系统的陌生,在安装过程中也踩了很多坑,在linux系统里编译ffmpeg坑更多,但是大多都是由于linux系统没有安装相关软件导致的,因此,我们在linux平台下执行命令的时候一定要有耐心,遇到问题不要慌,要学会根据错误信息找到原因。集成ffmpeg只是音视频开发过程中最简单最基本的一步,如果连这一步都做不好,那么在以后的开发过程中肯定会困难重重,所以,想要从事音视频开发的朋友们,一定要认认真真地做好每一步,把每个知识点弄清楚,遇到困难不要放弃,要学会冷静思考,只要坚持下去,一定会有收获的!