android 移植 ffmpeg (二) 测试用例
16lz
2021-01-23
在android 移植 ffmpeg (一)中已经对环境进行了设置。 这一章将重点讨论怎么在应用中加入ffmpeg组件。
所有测试都将在 Android Studio工具中进行。
测试例子源地址: https://github.com/roman10/android-ffmpeg-tutorial
本例子是在android-ffmpeg-tutorial01 基础上进行了简单调整。调整后的源码页:http://download.csdn.net/detail/net_wolf_007/9620856
一. 工程目录结构
目录中主要有6个地方进行变动,所以逐一说明一下。
1 assets路径
测试视频存放地, 目前测试视频为1.mp4. 通过命令 ffmpeg -i 1.mp4 得到视频信息。 视频信息为:(时长10.04秒, 比特率: 352Kb/s, 分辨率: 640*360)2 Java 文件目录
MainActivity.java 主Java程序, 后面会对程序进行说明
Utils.java 工具类。提供了文件拷贝功能,程序中主要用于把1.mp4导入到指定目录
3 NDK工作目录 jni文件夹
tutorial01.c: 测试ffmpeg源文件,主要功能,调用ffmpeg sdk分析视频,并把指定的图片上传给Java代码。后面会进一步说明。 Applicaton.mk文件: 定义了应用程序编译的基本信息, 非必要。主要告诉ndk要编译的指令集,及对应的Android平台。 Android NDK编译系统支持3种API: armeabi, armeabi-v7a和x86,分别对应 ARMv5TE, ARMv7-A和X86指令集的CPU.默认支持armeabi. Android.mk: NDK 编译命令文件,用来告诉NDK如何编译此项目的。 常用的命令在上一篇( android 移植 ffmpeg (一))文章中已经有了说明,这里仅补充没有说明的。 LOCAL_LDLIBS := -llog -ljnigraphics -lz linker flags。 可以用它来添加系统库 LOCAL_SHARED_LIBRARIES := libavformat libavcodec libswscale libswresample libavutil 要链接到本模块的动态库。 $(call import-module,ffmpeg/android) import-module: 允许寻找并inport其它modules到本Android.mk中来。 它会从NDK_MODULE_PATH寻找指定的模块名。 使用方法: $(call import-module,4 NDK生成目录 libs目录
一般NDK生成目录位于./libs/armeabi/
目录下 里面有目录 armeabi, 即生成支持ARMv5TE指令集的CPU的库。这些库可直接在ARMv5TE指令集的CPU上去行。 如果要设置成支持多个指令集的话,可在 Application.mk中进行设置,则可生成多个文件夹,每个文件夹中的文件名一样。 5 build.gradle文件配置
需要配置相应的NDK配置6 local.properties 文件匹配
需要配置SDK,NDK的路径名。我的机器的配置是: NDK的配置还可以通过如下方法进行设置: File->Project Structure… 快捷建(Command + ;)进行设置。二. 生成代码文件
生成库文件:
需要通过命令行来生成
命令行运行到main目录下,调用ndk_build.
172-15-70-196:main jerome$ pwd/Users/jerome/dev/ffmpeg/android-ffmpeg-tutorial01/app/src/main172-15-70-196:main jerome$ /Users/jerome/dev/android-ndk-r12b/ndk-build [armeabi] Compile thumb : tutorial01 <= tutorial01.cjni/tutorial01.c: In function 'naMain':jni/tutorial01.c:117:2: warning: 'codec' is deprecated (declared at /Users/jerome/dev/android-ndk-r12b/sources/ffmpeg/android/include/libavformat/avformat.h:881) [-Wdeprecated-declarations] pCodecCtx=pFormatCtx->streams[videoStream]->codec; ^[armeabi] SharedLibrary : libtutorial01.so[armeabi] Install : libtutorial01.so => libs/armeabi/libtutorial01.so[armeabi] Install : libavformat-57.so => libs/armeabi/libavformat-57.so[armeabi] Install : libavcodec-57.so => libs/armeabi/libavcodec-57.so[armeabi] Install : libswscale-4.so => libs/armeabi/libswscale-4.so[armeabi] Install : libswresample-2.so => libs/armeabi/libswresample-2.so[armeabi] Install : libavutil-55.so => libs/armeabi/libavutil-55.so172-15-70-196:main jerome$
编译运行apk
直接运行调试。这过程中会碰到如下问题, 需要修改ffmpeg源码,再重新编译ffmpeg: cannot locate symbol "log2f" referenced by "libavcodec-57.so".. cannot locate symbol "log2" referenced by "libavcodec-57.so".. 原因: 这个跟ndk与android版本有关。 解决办法: 修改 ./libavutil/libm.h里面的定义,不再判断是否已经存在函数。使用重新定义//#if !HAVE_LOG2//#undef log2#define log2(x) (log(x) * 1.44269504088896340736)//#endif /* HAVE_LOG2 *///#if !HAVE_LOG2F//#undef log2f#define log2f(x) ((float)log2(x))//#endif /* HAVE_LOG2F */
cannot locate symbol "atof" referenced by "libavformat-57.so"...
原因:android的stdlib.h中atof是内联的, 外部模块不能直接使用。跟android版本有关。 解决办法:将所有的atof改成strtod
修改完之后: 1 重新生成ffmpeg(调用 ./build_andriod_mac.sh), 2 调用ndk_build生成C库文件。 3. 运行android Java代码。
4. 点击界面的start按钮,生成图片。
至此,程序运行OK.
三. NDK开发运行流程
ndk-build 编译流程:1. 查找环境变量 NDK_PROJECT_PATH,如果用户没有设置,就根据如下流程查找! /Users/jerome/dev/android-ndk-r12b/build/core/build-local.mk中的代码部分
ifndef NDK_PROJECT_PATH ifneq (,$(strip $(wildcard AndroidManifest.xml))) NDK_PROJECT_PATH := . else ifneq (,$(strip $(wildcard jni/Android.mk))) NDK_PROJECT_PATH := . endif endifendififndef NDK_PROJECT_PATH NDK_PROJECT_PATH := $(call find-project-dir,.,jni/Android.mk)endififndef NDK_PROJECT_PATH NDK_PROJECT_PATH := $(call find-project-dir,.,AndroidManifest.xml)endif
说明:就是在当前目录及父目录中查找文件:AndroidManifest.xml 或 jni/Android.mk 文件,如果找到,就把找到的目录赋给NDK_PROJECT_PATH, 如果没找到,就直接报错。Android NDK: Could not find application project directory ! 2. 查找环境变量APP_BUILD_SCRIPT: 如果没设置,就运行如下代码。 在/Users/jerome/dev/android-ndk-r12b/build/core/add-application.mk 中
_build_script := $(strip $(wildcard $(APP_PROJECT_PATH)/jni/Android.mk)) ifndef _build_script $(call __ndk_info,There is no Android.mk under $(APP_PROJECT_PATH)/jni) $(call __ndk_info,If this is intentional, please define APP_BUILD_SCRIPT to point) $(call __ndk_info,to a valid NDK build script.) $(call __ndk_error,Aborting...) endif APP_BUILD_SCRIPT := $(_build_script)
说明: 如果没找到 APP_BUILD_SCRIPT的定义,就查找APP_PROJECT_PATH下的 jni/Android.mk, 如果找到就赋值给APP_BUILD_SCRIPT。如果没有找到,就报错。Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk 这里查找的是 APP _PROJECT_PATH, 与1中的NDK_PROJECT_PATH的关系如下(core/add-application.mk 中)。
APP_PROJECT_PATH := $(strip $(APP_PROJECT_PATH))ifndef APP_PROJECT_PATH APP_PROJECT_PATH := $(NDK_PROJECT_PATH)endif
如果定义了环境变量 APP _PROJECT_PATH, 就使用定义的值,如果没有就使用 NDK_PROJECT_PATH的值。 3. 加载其它模块Android.mk文件 在APP_BUILD_SCRIPT中查找是否有如下语句,有,则加载对应的库文件
$(call import-module,ffmpeg/android)查找逻辑是通过查找环境变量:NDK_MODULE_PATH下的对应模块的Android.mk文件, 上例中就是查找 ffmpeg/android/Android.mk文件。如果找不到就报错。Android NDK: jni/Android.mk: Cannot find module with tag 'ffmpeg/android' in import path NDK_MODULE_PATH 是可以有多个值。看脚本(core/setup-imports.mk)
NDK_MODULE_PATH := $(strip $(NDK_MODULE_PATH))ifdef NDK_MODULE_PATH ifneq ($(words $(NDK_MODULE_PATH)),1) $(call __ndk_info,ERROR: You NDK_MODULE_PATH variable contains spaces) $(call __ndk_info,Please fix the error and start again.) $(call __ndk_error,Aborting) endifendif$(call import-init)$(foreach __path,$(subst $(HOST_DIRSEP),$(space),$(NDK_MODULE_PATH)),\ $(call import-add-path,$(__path))\)$(call import-add-path-optional,$(NDK_ROOT)/sources)$(call import-add-path-optional,$(NDK_ROOT)/../development/ndk/sources)
由上脚本可知, 如果设置了NDK_MODULE_PATH,则就在NDK_MODULE_PATH中查找,如果没有设置,那就是$(NDK_ROOT)/sources目录中查找(还记得嘛,这也是我们编译ffmpeg存放的目录!),还就有是在$(NDK_ROOT)/../development/ndk/sources目录中查找! $(NDK_ROOT)目录就是ndk-build运行的目录, 如果用户不确定, 可直接设置环境变量:
1. vim ~/.bash_profile
2. 添加 export NDK_ROOT=/Users/jerome/dev/android-ndk-r12b
3. source .bash_profile
4. 编译合成后的Android.mk文件,生成指定的结果文件。
这样 ndk-build的运行逻辑就清晰了。通过设置主要环境变量, 就可以实现模块化编程。 NDK_ROOT: 代码根目录, 默认为ndk-build所在的目录。 NDK_PROJECT_PATH: 默认为ndk-build运行的目录。
APP_PROJECT_PATH: 保存脚本的路径
APP_BUILD_SCRIPT: 脚本路径
NDK_MODULE_PATH: 模块路径, 用户定义的路径
还有两个系统模块路径: $(NDK_ROOT)/sources, $(NDK_ROOT)/../development/ndk/sources。
Gradle打包流程就不分析了。
四. 后记
本篇主要让程序运行起来,并分析ndk-build的逻辑, 下一篇,将重点说明程序结构,及代码流程。
更多相关文章
- android 文件下载
- Android使用主题配置文件,去掉程序启动界面的短暂黑屏。
- 关于Android中的几个文件目录
- Android生成Xml文件
- Android 生成签名及APK 文件
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原
- Android文件系统的结构及目录用途、操作方法
- android文件系统