android 移植 ffmpeg (二) 测试用例

http://blog.csdn.net/net_wolf_007/article/details/52421582

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.

  1. 172-15-70-196:main jerome$ pwd  
  2. /Users/jerome/dev/ffmpeg/android-ffmpeg-tutorial01/app/src/main  
  3. 172-15-70-196:main jerome$ /Users/jerome/dev/android-ndk-r12b/ndk-build   
  4. [armeabi] Compile thumb  : tutorial01 <= tutorial01.c  
  5. jni/tutorial01.c: In function 'naMain':  
  6. 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]  
  7.   pCodecCtx=pFormatCtx->streams[videoStream]->codec;  
  8.   ^  
  9. [armeabi] SharedLibrary  : libtutorial01.so  
  10. [armeabi] Install        : libtutorial01.so => libs/armeabi/libtutorial01.so  
  11. [armeabi] Install        : libavformat-57.so => libs/armeabi/libavformat-57.so  
  12. [armeabi] Install        : libavcodec-57.so => libs/armeabi/libavcodec-57.so  
  13. [armeabi] Install        : libswscale-4.so => libs/armeabi/libswscale-4.so  
  14. [armeabi] Install        : libswresample-2.so => libs/armeabi/libswresample-2.so  
  15. [armeabi] Install        : libavutil-55.so => libs/armeabi/libavutil-55.so  
  16. 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里面的定义,不再判断是否已经存在函数。使用重新定义
  1. //#if !HAVE_LOG2  
  2. //#undef log2  
  3. #define log2(x) (log(x) * 1.44269504088896340736)  
  4. //#endif /* HAVE_LOG2 */  
  5.   
  6. //#if !HAVE_LOG2F  
  7. //#undef log2f  
  8. #define log2f(x) ((float)log2(x))  
  9. //#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中的代码部分
  1. ifndef NDK_PROJECT_PATH  
  2.     ifneq (,$(strip $(wildcard AndroidManifest.xml)))  
  3.         NDK_PROJECT_PATH := .  
  4.     else  
  5.         ifneq (,$(strip $(wildcard jni/Android.mk)))  
  6.             NDK_PROJECT_PATH := .  
  7.         endif  
  8.     endif  
  9. endif  
  10. ifndef NDK_PROJECT_PATH  
  11.     NDK_PROJECT_PATH := $(call find-project-dir,.,jni/Android.mk)  
  12. endif  
  13. ifndef NDK_PROJECT_PATH  
  14.     NDK_PROJECT_PATH := $(call find-project-dir,.,AndroidManifest.xml)  
  15. 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 中
  1. _build_script := $(strip $(wildcard $(APP_PROJECT_PATH)/jni/Android.mk))  
  2.    ifndef _build_script  
  3.        $(call __ndk_info,There is no Android.mk under $(APP_PROJECT_PATH)/jni)  
  4.        $(call __ndk_info,If this is intentional, please define APP_BUILD_SCRIPT to point)  
  5.        $(call __ndk_info,to a valid NDK build script.)  
  6.        $(call __ndk_error,Aborting...)  
  7.    endif  
  8.    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 中)。
  1. APP_PROJECT_PATH := $(strip $(APP_PROJECT_PATH))  
  2. ifndef APP_PROJECT_PATH  
  3.     APP_PROJECT_PATH := $(NDK_PROJECT_PATH)  
  4. 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)
  1. NDK_MODULE_PATH := $(strip $(NDK_MODULE_PATH))  
  2. ifdef NDK_MODULE_PATH  
  3.   ifneq ($(words $(NDK_MODULE_PATH)),1)  
  4.     $(call __ndk_info,ERROR: You NDK_MODULE_PATH variable contains spaces)  
  5.     $(call __ndk_info,Please fix the error and start again.)  
  6.     $(call __ndk_error,Aborting)  
  7.   endif  
  8. endif  
  9.   
  10. $(call import-init)  
  11. $(foreach __path,$(subst $(HOST_DIRSEP),$(space),$(NDK_MODULE_PATH)),\  
  12.   $(call import-add-path,$(__path))\  
  13. )  
  14. $(call import-add-path-optional,$(NDK_ROOT)/sources)  
  15. $(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的逻辑, 下一篇,将重点说明程序结构,及代码流程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值