NDK_Day11_Shell 脚本、FFmpeg 交叉编译
一、Shell 脚本
Sheell:是一个用 C 语言编写的程序
脚本:Script
后缀:.sh
Shell 脚本很多情况下就是一段命令的集合,用于完成一系列的操作,解决了每次都需要手动执行繁杂操作步骤的问题
举个栗子
写个 HelloWorld
① 创建 helloworld.sh
vim helloworld.sh
添加如下内容
#!/bin/bash
echo "Hello World"
第一行:指定脚本用到的解释器,目前应用最广的是 bash 解释器
第二行:使用 echo 输出内容
② 设置可执行权限
// Linux 中新创建的文件默认没有可执行权限,需要增加可执行权限
chmod +x helloworld.sh
③ 执行
./helloworld.sh
二、编译 Android 可用的 FFmpeg
2.1 准备
1.下载
打开 FFmpeg 官网,点击 Download 按钮
右键 Download
右键复制下载链接
到虚拟机中粘贴下载链接,使用 wget 命令下载(测试的话,下载最新的就行)
wget https://ffmpeg.org/releases/ffmpeg-4.1.3.tar.bz2
2.解压
// x:解压
// v:显示解压过程
// f:file 文件
tar -xvf ffmpeg-4.1.3.tar.bz2
看下 ffmpeg 目录下都有啥(含组件源码、文档、配置脚本等)
看下绿色的 configure 文件都有什么内容(是一个 Shell 脚本)
configure 文件(Shell 脚本)作用:生成 makefile 脚本
makefile:定义了一系列的规则来完成编译和链接,即使编译自动化,可以使用 make 指令执行 makefile 脚本,可以生成 makefile 脚本对应的静、动态库或可执行文件
2.2 常见参数简介
avformat:用于音视频封装格式的生成与解析,平时需要解析音视频时可使用
avcodec:音视频编解码
avutil:工具类,一般都需要
scale:视频缩放、色彩映射转换
avdevice:操作摄像头,但是不支持操作 Android 上的摄像头(不支持 Android 采集和播放设备),可以关闭此功能
swresample:对音频重采样,如对视频中双声道和单声道间转换
postproc:后期处理
avfilter:加字幕加水印时可用
encoders:编码,只播放可关闭此功能
muxers:混合封装,将图像和声音封装成音视频,不需封装可关闭此功能
cross-compile:交叉编译
cross-prefix:交叉编译工具前缀,就是路径
D:/android_studisdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/windows-x86_64/bin/arm-linux-androideabi-
sysroot:编译时查找头文件、库文件的目录
D:/android_studio/sdk/ndk-bundle/platforms/android-21/arch-arm
isystem:编译时查找头文件目录
D:\android_studio\sdk\ndk-bundle\sysroot\usr\include\arm-linux-androideabi
2.3 配置 FFmpeg Shell 脚本
使用到的 NDK 版本要在 16 往上,15 可能会有问题,最后测试 17 没问题
配置 NDK 环境变量
下载 NDK
- 官网下载 Linux 环境下的 NDK:https://developer.android.google.cn/ndk/downloads/
- 下载到指定目录(我们这里下载到 /home/lql 目录下),下载完成后,解压即可
打开环境变量配置文件
vim /etc/profile
在末尾添加如下内容
export ANDROID_NDK=/home/lql/android-ndk-r17c
export PATH=$ANDROID_NDK:$PATH
使配置马上生效
source /etc/profile
编写 build.sh
#!/bin/bash
# NDK_ROOT=/root/linux/ndk/android-ndk-r17c
CPU=arm-linux-androideabi
TOOLCHAIN=$NDK_ROOT/toolchains/$CPU-4.9/prebuilt/linux-x86_64
# FLAGS 除了 -isystem、-D__ANDROID_API__=21,其它都是默认的,其实网上有的并没有这么多参数,只有 -isystem、-D__ANDROID_API__=21、-fPIC 等简单的几个参数
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"
# 指定最终编译生成的文件的存放路径,随便制定一个目录也行
# 这里指定为在当前目录(pwd)下的 android/armeabi-v7a7 目录,其中 android 和 armeabi-v7a7(叫 armeabi-v7aJQK 也米问题)只是为了标识库文件是 android 平台下 armeabi-v7a 架构的库文件而已
PREFIX=`pwd`/android/armabi-v7a7
./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-postproc \
--disable-encoders \
--disable-muxers \
--enable-cross-compile \
--cross-prefix=$TOOLCHAIN/bin/$CPU- \
--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
# 编译 makefile
make
# 执行 makefile
make install
运行脚本
./build.sh
可以忽略下图的警告,不影响编译
等着吧…
如下图,不出意外的话,脚本运行完毕,生成了一个 android 文件夹,即编译成功
进入 android/armabi-v7a7,查看生成的文件
进入 lib 目录(内含生成的 .a 静态库文件)
打包 .a 静态库为 .so 动态库
$ANDROID_NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$ANDROID_NDK/platforms/android-21/arch-arm -shared -o libffmpeg.so -Wl,--whole-archive libavcodec.a libavformat.a libavutil.a libswresample.a libswscale.a libavfilter.a -Wl,--no-whole-archive
2.4 在 Android 中使用编译好的 FFmpeg 静态库
这里我们使用静态库,拿到 AndroidStudio 中使用
① 使用 AndroidStudio 创建 NDK 项目
② 在 src 目录下创建 jniLibs 目录
② 引入头文件
将 include 文件夹复制到 jniLibs 目录下
③ 引入库文件
-1 在 jniLibs 目录下创建 libs/armeabi-v7a 目录
-2 将生成的静态库放在 libs/armeabi-v7a 目录下
④ 配置 CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
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)
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)
include_directories(${CMAKE_SOURCE_DIR}/../jniLibs/include)
# 因为有 5 个静态库文件,所以这里直接使用 set() 方式链接,当然也可以用 add_library() 和 set_target_properties() 去链接,5 个静态库文件配置 5 遍
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/libs/${ANDROID_ABI}")
target_link_libraries( # Specifies the target library.
native-lib avcodec avfilter avformat avutil swresample swscale
# Links the target library to the log library
# included in the NDK.
${log-lib})
⑤ 配置 app 下的 build.gradle
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags ""
abiFilters "armeabi-v7a"
}
}
}
...
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}
dependencies {
...
}
⑥ 使用 ffmpeg 函数
#include <jni.h>
#include <string>
// extern "C":在 C++ 中使用 C 的代码
extern "C" {
#include <libavcodec/avcodec.h>
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_lql_dn_ndk_day11_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
const char *version = av_version_info(); // 使用 ffmpeg 的一个函数
return env->NewStringUTF(version);
}
⑦ 运行
o**k,模拟器显示内容和我们下载的 ffmpeg 的版本 4.1.3 一致
三、音视频
视频:一段声音 + 一段视频,以一定格式组合起来,就是视频文件
播放视频时,图像是三原色 rgb,声音是 pcm
编码:图像声音原始数据过大,需要对其进行编码(编码压缩,对原始数据进行压缩的过程),编码后的数据就是音视频数据,然后再进行封装,封装到容器(mp4、avi、flv)里面,就形成了视频文件
解码:把容器(音频+视频+字幕+…),需要解封装,抽离出音频、视频(压缩后的数据)等,然后再进行解码(解压缩),解出来图像声音的原始模型 RGB、亮度模型 YUV,拿到这些原始数据后,才能显示到屏幕上