一、FFMPEG-Android编译
(一)、下载FFmpeg
这里使用的是ffmpeg-3.4.6
也可以使用脚本下载指定的版本:
#库名称
source="ffmpeg-3.4.6"
#下载这个库
if [ ! -r $source ]
then
#没有下载,那么我需要执行下载操作
echo "没有FFmpeg库,我们需要下载….."
#下载:怎么下载?
#"curl"命令表示:它可以通过Http\ftp等等这样的网络方式下载和上传文件(它是一个强大网络工具)
#基本格式:curl 地址
#指定下载版本
#下载完成之后,那么我们需要解压(通过自动解压)
#"tar"命令:表示解压和压缩(打包)
#基本语法:tar options
#例如:tar xj
#options选项分为很多中类型
#-x 表示:解压文件选项
#-j 表示:是否需要解压bz2压缩包(压缩包格式类型有很多:zip、bz2等等…)
curl http://ffmpeg.org/releases/${source}.tar.bz2 | tar xj || exit 1
fi
(二)、注意事项
默认目标编译出来的.so
格式:libavcodec.so.57.2.100
存在问题:在Android
程序中无法加载动态库,编译器报错(报错信息:找不到动态库),即使手动修改了后缀名为libavcodec.so
,依然还是无法运行.
解决方案:
1.进入FFmpeg-3.4.6
开发包
2.打开configure
文件
3.修改配置
#SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
#LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
#SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
#SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
将上述的四行代码替换为:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
(三)、执行脚本./build-ffmpeg-armeabi.sh
脚本内容:
#!/bin/bash
#第一步:进入到指定目录
cd ffmpeg-3.4.6
#第二步:指定NDK路径(编译什么样的平台->采用什么样的平台编译器)
#Android平台NDK技术->做C/C++开发->编译Andrroid平台下.so动态库
#注意:放在英文目录(中文目录报错)
#修改一:修改为你自己NDK存放目录
NDK_DIR=/Users/ws/Desktop/ffmpeg/android/NDK/android-ndk-r10e
#第三步:配置Android系统版本(支持最小的版本)
#指定使用NDK Platform版本(对应系统版本)
SYSROOT=$NDK_DIR/platforms/android-18/arch-arm
#/Users/ws/Desktop/Android/android-sdk-macosx/platforms/android-18/arch-arm
第四步:指定编译工具链->(通俗:指定编译器)->CPU架构(Android手机通用的CPU架构类型):armeabi
TOOLCHAIN=$NDK_DIR/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#第五步:指定CPU平台架构类型
#指定编译后的安装目录
ARCH=arm
ADDI_CFLAGS="-marm"
#第六步:指定编译成功之后,.so动态库存放位置
#修改二:这个目录你需要修改为你自己目录
PREFIX=/Users/ws/Desktop/ffmpeg/android/android-ffmpeg
#第七步:编写执行编译脚本->调用FFmpeg进行配置
#定义了Shell脚本函数(方法)
function build_armeabi
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--enable-gpl \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-doc \
--disable-symver \
--enable-small \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=android \
--arch=$ARCH \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--enable-pic \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install
}
#第八步:执行函数->开始编译
build_armeabi
echo "Android armeabi builds finished"
脚本上已经都有标注明确的步骤,如果要使用上述脚本仅需要修改上述NDK
的路径NDK_DIR
以及动态库.so
的存放位置PREFIX
结果:
二、环境搭建
(一).使用Android Studio
创建项目:
要引入C/C++
库,需要选中如下选项:Include C++ support
需要捕获C/C++
异常,需要选中如下选项:
(二).引入.so
库
首先在app->src->main
下创建文件夹jniLibs
将.so
和头文件引入:
(三).添加、链接动态库
打开CMakeLists.txt
文件:
# FFMpeg配置
# FFmpeg配置目录
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../src/main/jniLibs)
# 编解码(最重要的库)
add_library(
avcodec
SHARED
IMPORTED)
set_target_properties(
avcodec
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libavcodec.so)
# 设备
add_library(
avdevice
SHARED
IMPORTED)
set_target_properties(
avdevice
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libavdevice.so)
# 滤镜特效处理库
add_library(
avfilter
SHARED
IMPORTED)
set_target_properties(
avfilter
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libavfilter.so)
# 封装格式处理库
add_library(
avformat
SHARED
IMPORTED)
set_target_properties(
avformat
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libavformat.so)
# 工具库(大部分库都需要这个库的支持)
add_library(
avutil
SHARED
IMPORTED)
set_target_properties(
avutil
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libavutil.so)
add_library(
postproc
SHARED
IMPORTED)
set_target_properties(
postproc
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libpostproc.so)
# 音频采样数据格式转换库
add_library(
swresample
SHARED
IMPORTED)
set_target_properties(
swresample
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libswresample.so)
# 视频像素数据格式转换
add_library(
swscale
SHARED
IMPORTED)
set_target_properties(
swscale
PROPERTIES IMPORTED_LOCATION
../../../../src/main/jniLibs/armeabi/libswscale.so)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
#配置编译的头文件
include_directories(src/main/jniLibs/include)
target_link_libraries( # Specifies the target library.
native-lib avcodec avdevice avfilter avformat avutil postproc swresample swscale
# Links the target library to the log library
# included in the NDK.
${log-lib} )
(四).配置CPU平台架构类型
build.gradle
中添加配置
在app->src->build.gradle
中添加配置:abiFilters 'armeabi'
三.编写案例&测试
案例(一):测试FFmpeg环境
1.创建类FFmpegTest
,定义Java方法
//NDK方法
public class FFmpegTest {
//加载动态库
static {
System.loadLibrary("native-lib");
}
//NDK音视频编解码:FFmpeg-测试配置
public static native void ffmpegTest();
}
注释:(1).native标记这个方法是一个特殊方法,不是普通的java方法,而是用于与NDK进行交互方法(C/C++语言交互)
(2).用native进行修饰方法:方法没有实现,具体的实现在C/C++里面.
2.定义C/C++方法(NDK方法和Java方法必须一一对应)
注意:在CMakeLists.tx
t配置native-lib.cpp
文件,将Java和C/C++进行关联
#include
#include
//当前C++兼容C语言
extern "C"{
//avcodec:编解码(最重要的库)
#include
//avformat:封装格式处理
#include "libavformat/avformat.h"
//avutil:工具库(大部分库都需要这个库的支持)
//#include "libavutil/imgutils.h"
JNIEXPORT void JNICALL Java_com_tz_dream_ffmpeg_libso_test_FFmpegTest_ffmpegTest
(JNIEnv *, jobject);
}
JNIEXPORT void JNICALL Java_com_tz_dream_ffmpeg_libso_test_FFmpegTest_ffmpegTest(
JNIEnv *env, jobject jobj) {
//(char *)表示C语言字符串
const char *configuration = avcodec_configuration();
__android_log_print(ANDROID_LOG_INFO,"FFmpeg配置信息:","%s",configuration);
}
3.调用测试
FFmpegTest.ffmpegTest();
案例(二):测试FFmpeg打开封装格式文件。
1.添加Java方法定义:
public static native void ffmpegVideoOpen(String filePath);
2.定义C/C++方法
JNIEXPORT void JNICALL Java_com_tz_dream_ffmpeg_libso_test_FFmpegTest_ffmpegVideoOpen(
JNIEnv *env, jobject jobj, jstring jinFilePath){
const char* cinputFilePath = env->GetStringUTFChars(jinFilePath,NULL);
//第一步:注册所有组件
av_register_all();
//第二步:打开视频输入文件
//参数一:封装格式上下文->AVFormatContext->包含了视频信息(视频格式、大小等等...)
AVFormatContext* avformat_context = avformat_alloc_context();
//参数二:打开文件(入口文件)->url
int avformat_open_result = avformat_open_input(&avformat_context,cinputFilePath,NULL,NULL);
if (avformat_open_result != 0){
//获取异常信息
char* error_info;
av_strerror(avformat_open_result, error_info, 1024);
__android_log_print(ANDROID_LOG_INFO,"main","异常信息:%s",error_info);
return;
}
__android_log_print(ANDROID_LOG_INFO,"main","文件打开成功");
}
3.打开SD卡访问权限
4.调用测试
String rootPath = Environment.getExternalStorageDirectory()
.getAbsolutePath();
String inFilePath = rootPath.concat("/DreamFFmpeg/Test.mov");
//文件不存在我创建一个文件
File file = new File(inFilePath);
if (file.exists()){
Log.i("日志:","存在");
}else {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FFmpegTest.ffmpegVideoOpen(inFilePath);