在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,<name>)
这里就是把ffmpeg/android的模块导入进来,即把ffmpeg/android/下的Android.mk导入进本模块来。
这样上面 LOCAL_SHARED_LIBRARIES指字的库名才能找到。
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/main 172-15-70-196:main jerome$ /Users/jerome/dev/android-ndk-r12b/ndk-build [armeabi] Compile thumb : tutorial01 <= tutorial01.c jni/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.so 172-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 endif endif ifndef NDK_PROJECT_PATH NDK_PROJECT_PATH := $(call find-project-dir,.,jni/Android.mk) endif ifndef 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) endif endif $(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的逻辑,下一篇,将重点说明程序结构,及代码流程。
作者:net_wolf_007 发表于2016/9/3 17:24:02 原文链接
阅读:91 评论:0 查看评论