在android 移植 ffmpeg (一)中已经对环境进行了设置。 这一章将重点讨论怎么在应用中加入ffmpeg组件。

所有测试都将在 Android Studio工具中进行。

测试例子源地址: https://github.com/roman10/android-ffmpeg-tutorial 

本例子是在android-ffmpeg-tutorial01 基础上进行了简单调整。调整后的源码页:http://download.csdn.net/detail/net_wolf_007/9620856


一. 工程目录结构

android 移植 ffmpeg (二) 测试用例_第1张图片

目录中主要有6个地方进行变动,所以逐一说明一下。

1 assets路径

测试视频存放地, 目前测试视频为1.mp4. 通过命令 ffmpeg -i 1.mp4 得到视频信息。        视频信息为:(时长10.04秒, 比特率: 352Kb/s, 分辨率: 640*360) android 移植 ffmpeg (二) 测试用例_第2张图片

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,)                 这里就是把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配置       android 移植 ffmpeg (二) 测试用例_第3张图片

6 local.properties 文件匹配

    需要配置SDK,NDK的路径名。我的机器的配置是:          android 移植 ffmpeg (二) 测试用例_第4张图片      NDK的配置还可以通过如下方法进行设置: File->Project Structure… 快捷建(Command + ;)进行设置。       android 移植 ffmpeg (二) 测试用例_第5张图片


二. 生成代码文件

生成库文件:

需要通过命令行来生成 

       命令行运行到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的逻辑, 下一篇,将重点说明程序结构,及代码流程。




更多相关文章

  1. android 文件下载
  2. Android使用主题配置文件,去掉程序启动界面的短暂黑屏。
  3. 关于Android中的几个文件目录
  4. Android生成Xml文件
  5. Android 生成签名及APK 文件
  6. Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原
  7. Android文件系统的结构及目录用途、操作方法
  8. android文件系统

随机推荐

  1. Android:Activity生命周期深入详解
  2. Android(安卓)Studio怎样更改JDK和SDK的
  3. Android客户端与服务器端通过DES加密认证
  4. ionic4项目结合chrome在Android真机运行
  5. Mozilla将开发独立操作系统
  6. Android(安卓)OkHttp简单封装
  7. android中易错点
  8. Android(安卓)iBeacon集成开发,距离判断
  9. Android(安卓)Phonebook编写联系人UI加载
  10. android studio连接小米手机mi 5s plus真