ijkplayer 播放器:在Android端进行native调试

前言

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工程进行源码调试的必要修改步骤,这也是对于分析源码或者二次开发都有非常有用的帮助。

本文发布于简书

End!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容