前言
ijkplayer是B站开源的基于FFmpeg的轻量级Android/iOS视频播放器,强烈建议在定制的播放器的时候以ijkplayer为基础进行二次开发。
对于二次开发时代码的调试时一件重中之重的事情;在iOS平台,ijkplayer可以直接在Xcode进行c/c++源码的debug调试工作,而Android平台的demo工程依赖的是ijkplayer编译完毕的so文件,而不是直接关联到ijkplayer的Android.mk编译脚本文件,所以要对c/c++这些native源码的调试的话就需要多折腾一些工作。
可能遇到的问题
如果我们要在Android Studio里面对ijkplayer的native进行调试,很开心的是官方提供了这么一个流程:
If you want to enable debugging ijkplayer(native modules) on Android Studio 2.2+: (experimental) sh android/patch-debugging-with-lldb.sh armv7a Install Android Studio 2.2(+) Preference -> Android SDK -> SDK Tools Select (LLDB, NDK, Android SDK Build-tools,Cmake) and install Open an existing Android Studio project Select android/ijkplayer Sync Project with Gradle Files Run -> Edit Configurations -> Debugger -> Symbol Directories Add "ijkplayer-armv7a/.externalNativeBuild/ndkBuild/release/obj/local/armeabi-v7a" to Symbol Directories Run -> Debug 'ijkplayer-example' if you want to reverse patches: sh patch-debugging-with-lldb.sh reverse armv7a
清楚明了对吧,but,几乎所有人都会遇到这一个问题,当你执行这句命令的时候:
sh android/patch-debugging-with-lldb.sh armv7a
恭喜你,一般你都会发现如下错误:
patch apply ==> armv7a git apply ==> patches/0001-gitignore-ignore-.externalNativeBuild.patch git apply ==> patches/0002-gradle-upgrade-build-tool-to-2.2.0-beta2.patch error: patch failed: android/ijkplayer/ijkplayer-example/build.gradle:44 error: android/ijkplayer/ijkplayer-example/build.gradle: patch does not apply git apply ==> patches/0003-armv7a-enable-debugging-with-LLDB.patch error: patch failed: ijkmedia/ijkplayer/Android.mk:59 error: ijkmedia/ijkplayer/Android.mk: patch does not apply error: patch failed: ijkmedia/ijksdl/Android.mk:70 error: ijkmedia/ijksdl/Android.mk: patch does not apply git apply ==> patches/0004-armv7a-link-prebuilt-staic-libraries-of-ffmepg.patch
what?这个脚本竟然是有问题的!
当然这是因为这个脚本是用git一些修改patch进行代码还原,但是但是,由于这个脚本已经太久没有更新了,而ijkplayer的一些代码结构又有调整导致脚本无法从patch文件附带的这些信息把代码正确还原回去。
解决思路
我们通过查看patch-debugging-with-lldb.sh,可以发现我们只需要按照以下几个patch文件做对应源码修改就可以达到调试的目的:
android/patches/0001-gitignore-ignore-.externalNativeBuild.patch android/patches/0002-gradle-upgrade-build-tool-to-2.2.0-beta2.patch android/patches/0003-$PARAM_TARGET-enable-debugging-with-LLDB.patch android/patches/0004-$PARAM_TARGET-link-prebuilt-staic-libraries-of-ffmepg.patch
例如随便打开里面一个文件例如0002-gradle-upgrade-build-tool-to-2.2.0-beta2.patch的内容如下:
From 5d70fa0496f9ebfbcfa3786d85c74c690d66781e Mon Sep 17 00:00:00 2001 From: ctiao <calmer91@gmail.com> Date: Mon, 29 Aug 2016 14:50:34 +0800 Subject: [PATCH 2/2] gradle: upgrade build-tool to 2.2.0-rc1 --- android/ijkplayer/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/ijkplayer/build.gradle b/android/ijkplayer/build.gradle index 0de03ec..6132c1d 100644 --- a/android/ijkplayer/build.gradle +++ b/android/ijkplayer/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.3' + classpath 'com.android.tools.build:gradle:2.2.0-rc1' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' -- 2.7.4 (Apple Git-66)
留意这些+和-这些所在的行的内容,熟悉git的同学应该就知道,这里应该是要删除掉
classpath 'com.android.tools.build:gradle:2.1.3'
然后把这一行添加上去
classpath 'com.android.tools.build:gradle:2.2.0-rc1'
其他文件也是以此类推,当然ijkplayer的源码不断的迭代,可能有些修改已经对不上了,并且有些修改也并不需必须的,所以这里我们就不完整对patch里面的每一行修改都进行修改,而是分析出这些patch的主要更改点在什么地方。
解决重点
通过分析这几个patch文件,我们可以明确得出主要的修改如下:
ijkplayer-xxxabi工程只保留armv7a
修改文件:ijkplayer/setting.gradle
修改内容:将其他的非armv7a的cpu架构的库删掉或者注释
//删除armv5、x86-64、x86、arm64这些工程 //include ':ijkplayer-armv5', ':ijkplayer-x86_64' include ':ijkplayer-armv7a' //include ':ijkplayer-arm64' //include ':ijkplayer-x86'
升级工程的gradle版本
修改文件:ijkplayer/build.gradle
修改内容:将gradle版本升级到2.2以上
dependencies { //classpath 'com.android.tools.build:gradle:2.1.3' classpath 'com.android.tools.build:gradle:3.0.1' }
ijkplayer-armv7a工程关联Android.mk编译脚本
修改文件:ijkplayer/ijkplayer-armv7a/build.gradle
修改内容:不要指定jni的lib库文件夹位置
android { //删除以下内容 //sourceSets.main { // jniLibs.srcDirs 'src/main/libs' // jni.srcDirs = [] // This prevents the auto generation of Android.mk //} }
修改内容:添加native代码主编译脚本Android.mk的文件路径
android { //添加以下内容 externalNativeBuild { ndkBuild { path "src/main/jni/Android.mk" } } }
修改内容:设置编译脚本的参数
android { defaultConfig { //添加以下内容 externalNativeBuild { ndkBuild { arguments "NDK_APPLICATION:=src/main/jni/Application.mk" abiFilters "armeabi-v7a" } } } }
修改内容:开启工程的debug模式
android { buildTypes { //添加以下内容 debug { debuggable true jniDebuggable true ndk { debuggable true } } } }
修改ffmpeg的编译脚本
修改文件:ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk
修改内容:将ffmpeg的链接方式由动态库方式改为静态库方式
LOCAL_PATH := $(call my-dir) #删除ffmpeg动态库相关 #include $(CLEAR_VARS) #LOCAL_MODULE := ijkffmpeg #LOCAL_SRC_FILES := #$(MY_APP_FFMPEG_OUTPUT_PATH)/libijkffmpeg.so #Include $(PREBUILT_SHARED_LIBRARY) #添加ffmpeg静态库相关如下全部 include $(CLEAR_VARS) LOCAL_MODULE := avcodec LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libavcodec.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := avformat LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libavformat.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := swscale LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libswscale.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_OUTPUT_PATH)/include LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := avutil LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libavutil.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := avfilter LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libavfilter.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY) LOCAL_MODULE := swresample LOCAL_SRC_FILES := $(MY_APP_FFMPEG_OUTPUT_PATH)/lib/libswresample.a LOCAL_EXPORT_C_INCLUDES := $(MY_APP_FFMPEG_INCLUDE_PATH) LOCAL_C_INCLUDES += $(MY_APP_FFMPEG_INCLUDE_PATH) include $(PREBUILT_STATIC_LIBRARY)
修改ijkplayer的编译脚本
修改文件:ijkplayer/ijkmedia/ijkplayer/Android.mk
修改内容:添加两个本地库-lm -lz
LOCAL_CFLAGS += -std=c99 #LOCAL_LDLIBS += -llog -landroid LOCAL_LDLIBS += -llog -landroid -lm -lz
修改内容:将ffmpeg由动态链接方式改为静态链接方式
#LOCAL_SHARED_LIBRARIES := ijkffmpeg ijksdl #LOCAL_STATIC_LIBRARIES := android-ndk-profiler ijksoundtouch LOCAL_SHARED_LIBRARIES := ijksdl LOCAL_STATIC_LIBRARIES := android-ndk-profiler ijksoundtouch avformat avcodec swscale swresample avfilter avutil
修改ijksdl编译脚本
修改文件:ijkplayer/ijkmedia/ijksdl/Android.mk
修改内容:将ffmpeg由动态链接方式改为静态链接方式
#LOCAL_SHARED_LIBRARIES := ijkffmpeg #LOCAL_STATIC_LIBRARIES := cpufeatures yuv_static ijkj4a LOCAL_STATIC_LIBRARIES := cpufeatures yuv_static ijkj4a avformat avcodec swscale swresample avfilter avutil
屏蔽掉java层对于ffmpeg动态库的加载
修改文件:ijkplayer/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
修改内容:由于ffmpeg采用了静态库导入,所以这里并不需要加载ffmpeg的动态库。
public static void loadLibrariesOnce(IjkLibLoader libLoader) { synchronized (IjkMediaPlayer.class) { if (!mIsLibLoaded) { if (libLoader == null) libLoader = sLocalLibLoader; //这里屏蔽掉ijkffmpeg库的加载 //libLoader.loadLibrary("ijkffmpeg"); libLoader.loadLibrary("ijksdl"); libLoader.loadLibrary("ijkplayer"); mIsLibLoaded = true; } } }
example工程同步修改
修改文件:ijkplayer/ijkplayer-example/build.gradle
修改内容:将cpu的类型指定为armv7a,并且开启debug模式
android { //指定armeabi-v7a defaultConfig { externalNativeBuild { ndkBuild { abiFilters "armeabi-v7a" } } } buildTypes { //添加debug模式的指定 debug { jniDebuggable true } } }
修改内容:删除掉非armeabi-v7a工程的依赖
dependencies { //all32Compile project(':ijkplayer-armv5') all32Compile project(':ijkplayer-armv7a') //all32Compile project(':ijkplayer-x86') //all64Compile project(':ijkplayer-armv5') all64Compile project(':ijkplayer-armv7a') //all64Compile project(':ijkplayer-arm64') //all64Compile project(':ijkplayer-x86') //all64Compile project(':ijkplayer-x86_64') }
ndk库的关联
修改文件:ijkplayer/local.properties
修改内容:ijkplayer由于需要依赖ndk进行编译,所以我们要指定本机的ndk目录地址,并且确保这个ndk版本必须在r12到r14之间的版本,千万不要用到sdk里面的那个ndk。
ndk.dir=<你的ndk路径>
关闭编译优化方便调试
修改文件:ijkplayer/ijkplayer-armv7a/src/main/jni/Application.mk
修改内容:c代码在编译的时候可能过度优化导致调试的时候看不到变量的值,我们可以将-O3的优化级别改为-O0
APP_CFLAGS := #将O3的优化级别降低为O0 #-O3 -Wall -pipe \ -O0 -Wall -pipe \ -ffast-math \ -fstrict-aliasing -Werror=strict-aliasing \ -Wno-psabi -Wa,--noexecstack \ -DANDROID -DNDEBUG
可能会遇到的错误
如果发现这个报错
All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html
在ijkplayer/ijkplayer-example/build.gradle中添加
android { defaultConfig { #这个值需要与工程下的ijkplayer/build.gradle中的versionCode相同 flavorDimensions "800800" } }
如果发现这个报错
AAPT2 error: check logs for details
在ijkplayer/gradle.properties中添加
android.enableAapt2=false
结语
这篇文章简单介绍了怎么在Android Studio中对ijkplayer工程进行源码调试的必要修改步骤,这也是对于分析源码或者二次开发都有非常有用的帮助。
作者:码农叔叔
链接:https://www.jianshu.com/p/2f6bf5fbc4a2
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。