前言
本文总结了笔者在 Android 音视频采集与软编码中的一些经验与技巧,包括移植 FFmpeg、YUV 视频帧处理、最新的 JNI 编写技巧、 ndk 开发技巧等,为了不扯太远本文不会对音视频编码的一些原理性东西进行剖析,也不会大量贴源码,更注重使用方法与流程的讲解。 文章最后将展示一个实现了音视频采集功能与本地视频压缩功能的完整项目。
采用软编码利弊
众所周知, Android API 中有个 MediaCodec
的类,利用 MediaCodec
可实现硬编码,高效且方便,Android API 中还有个叫 MediaMuxer
的类,可以实现音频与视频的合成,但是 MediaCodec
使用的 API 最低要求是 16,MediaMuxer
则是 18,除此之外如果对视频想做更多的处理还需要做比较多的开发,当然这点纯粹是偷懒。如果使用软编码,那么 API 限制将不再那么苛刻,笔者开发时调到了 14,然后由于我用的是 FFmpeg 且移植了它的命令行工具,那么对视频很多的处理操作可以使用简单命令即可完成。视频编码我为 FFmpeg 配置的是 x264 ,其可编码为 H.264 视频格式,除此之外比较出名的还有下一代编码标准 HEVC、VP9。音频我为 FFmpeg 配置的是 libfdk-aac ,其 libfaac 在新版 FFmpeg 中已经被抛弃了。x264 现在在算法上基本达到了瓶颈,而软编效率很大程度上依赖算法与 cpu ,所以 cpu 就成了决定效率的关键,经过我实验,虽然其跟硬编码效率还是有差距,但是它在普通 64 位的 cpu 上表现还是不错的,处理 480P、帧率 30 、比特率 1000000 的视频帧基本没有延迟。
本地依赖库的准备
需要准备的依赖库有 FFmpeg
、libx264
、libfdk-aac
等,它们都是不能直接在 Android 上使用的,需要修改些东西并且需要用 NDK 编译成动态链接库或者静态链接库,修改的东西其实主要是一些命名上的东西,很多平台识别的格式不太一样,如 Android 上不能识别 ffmpeg.so.01 这样的库,对于 FFmpeg 的编译网上有非常多的脚本,但是如果你需求不是和他一模一样,建议还是自己定制下,其实定制无非是对一些功能的开开关关而已,其他平台可能无所谓,大不了功能全开呗,但是对于 Android 来说,编译个全功能的 FFmpeg 可不是什么好事,apk 将会随之变大很多,这是不能接受的。这里有一个项目里面包含了 FFmpeg
、libx264
、libfdk-aac
全平台的编译脚本与 Android 下使用的简单 Demo,https://github.com/mabeijianxi/FFmpeg4Android ,你只需要简单修改脚本即可定制自己的 FFmpeg 了。
CMake 脚本编写
如果你 Android Studio 版本大于 2.2 ,且在新建项目时如果勾选上了 Incude C++ Support
,那么 AS 将会默认使用 CMake 插件,我们一般情况下只需要对 CMakeLists.txt
进行编写即可完成 Native 编译。下面展示一段 CMakeLists.txt
编写的实用列子:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
jxffmpegrun
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/cmdutils.c
src/main/cpp/ffmpeg.c
src/main/cpp/ffmpeg_filter.c
src/main/cpp/ffmpeg_opt.c
src/main/cpp/jx_ffmpeg_cmd_run.c
)
add_library(
avcodec
SHARED
IMPORTED
)
if(${ANDROID_ABI} STREQUAL "armeabi")
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi/libavcodec.so
)
endif(${ANDROID_ABI} STREQUAL "armeabi")
if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libavcodec.so
)
endif(${ANDROID_ABI} STREQUAL "armeabi-v7a")
if(${ANDROID_ABI} STREQUAL "arm64-v8a")
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/arm64-v8a/libavcodec.so
)
endif(${ANDROID_ABI} STREQUAL "arm64-v8a")
include_directories(
${CMAKE_SOURCE_DIR}/ffmpeg-3.2.5
)
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 )
target_link_libraries( # Specifies the target library.
jxffmpegrun
avcodec
# Links the target library to the log library
# included in the NDK.
${log-lib} )
简单分析下上面的脚本:
cmake_minimum_required
命令的作用是指定 CMake 使用的最小版本。add_library
命令的作用是添加一个库,首先指定的是库名,如 jxffmpegrun,根据规范最后会生成