前言
ijkplayer是B站開源的基于FFmpeg的輕量級Android/iOS視頻播放器茸俭,強烈建議在定制的播放器的時候以ijkplayer為基礎(chǔ)進行二次開發(fā)粒梦。
對于二次開發(fā)時代碼的調(diào)試時一件重中之重的事情;在iOS平臺,ijkplayer可以直接在Xcode進行c/c++源碼的debug調(diào)試工作,而Android平臺的demo工程依賴的是ijkplayer編譯完畢的so文件硼补,而不是直接關(guān)聯(lián)到ijkplayer的Android.mk編譯腳本文件,所以要對c/c++這些native源碼的調(diào)試的話就需要多折騰一些工作熏矿。
可能遇到的問題
如果我們要在Android Studio里面對ijkplayer的native進行調(diào)試已骇,很開心的是官方提供了這么一個流程:
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票编,幾乎所有人都會遇到這一個問題褪储,當你執(zhí)行這句命令的時候:
sh android/patch-debugging-with-lldb.sh armv7a
恭喜你,一般你都會發(fā)現(xiàn)如下錯誤:
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進行代碼還原,但是但是吊趾,由于這個腳本已經(jīng)太久沒有更新了宛裕,而ijkplayer的一些代碼結(jié)構(gòu)又有調(diào)整導致腳本無法從patch文件附帶的這些信息把代碼正確還原回去瑟啃。
解決思路
我們通過查看patch-debugging-with-lldb.sh
,可以發(fā)現(xiàn)我們只需要按照以下幾個patch文件做對應源碼修改就可以達到調(diào)試的目的:
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
的內(nèi)容如下:
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)
留意這些+
和-
這些所在的行的內(nèi)容揩尸,熟悉git的同學應該就知道蛹屿,這里應該是要刪除掉
classpath 'com.android.tools.build:gradle:2.1.3'
然后把這一行添加上去
classpath 'com.android.tools.build:gradle:2.2.0-rc1'
其他文件也是以此類推,當然ijkplayer的源碼不斷的迭代岩榆,可能有些修改已經(jīng)對不上了错负,并且有些修改也并不需必須的,所以這里我們就不完整對patch里面的每一行修改都進行修改勇边,而是分析出這些patch的主要更改點在什么地方犹撒。
解決重點
通過分析這幾個patch文件,我們可以明確得出主要的修改如下:
ijkplayer-xxxabi工程只保留armv7a
修改文件:ijkplayer/setting.gradle
修改內(nèi)容:將其他的非armv7a的cpu架構(gòu)的庫刪掉或者注釋
//刪除armv5粒褒、x86-64识颊、x86、arm64這些工程
//include ':ijkplayer-armv5', ':ijkplayer-x86_64'
include ':ijkplayer-armv7a'
//include ':ijkplayer-arm64'
//include ':ijkplayer-x86'
升級工程的gradle版本
修改文件:ijkplayer/build.gradle
修改內(nèi)容:將gradle版本升級到2.2以上
dependencies {
//classpath 'com.android.tools.build:gradle:2.1.3'
classpath 'com.android.tools.build:gradle:3.0.1'
}
ijkplayer-armv7a工程關(guān)聯(lián)Android.mk編譯腳本
修改文件:ijkplayer/ijkplayer-armv7a/build.gradle
修改內(nèi)容:不要指定jni的lib庫文件夾位置
android {
//刪除以下內(nèi)容
//sourceSets.main {
// jniLibs.srcDirs 'src/main/libs'
// jni.srcDirs = [] // This prevents the auto generation of Android.mk
//}
}
修改內(nèi)容:添加native代碼主編譯腳本Android.mk的文件路徑
android {
//添加以下內(nèi)容
externalNativeBuild {
ndkBuild {
path "src/main/jni/Android.mk"
}
}
}
修改內(nèi)容:設(shè)置編譯腳本的參數(shù)
android {
defaultConfig {
//添加以下內(nèi)容
externalNativeBuild {
ndkBuild {
arguments "NDK_APPLICATION:=src/main/jni/Application.mk"
abiFilters "armeabi-v7a"
}
}
}
}
修改內(nèi)容:開啟工程的debug模式
android {
buildTypes {
//添加以下內(nèi)容
debug {
debuggable true
jniDebuggable true
ndk {
debuggable true
}
}
}
}
修改ffmpeg的編譯腳本
修改文件:ijkplayer/ijkplayer-armv7a/src/main/jni/ffmpeg/Android.mk
修改內(nèi)容:將ffmpeg的鏈接方式由動態(tài)庫方式改為靜態(tài)庫方式
LOCAL_PATH := $(call my-dir)
#刪除ffmpeg動態(tài)庫相關(guān)
#include $(CLEAR_VARS)
#LOCAL_MODULE := ijkffmpeg
#LOCAL_SRC_FILES := #$(MY_APP_FFMPEG_OUTPUT_PATH)/libijkffmpeg.so
#Include $(PREBUILT_SHARED_LIBRARY)
#添加ffmpeg靜態(tài)庫相關(guān)如下全部
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
修改內(nèi)容:添加兩個本地庫-lm -lz
LOCAL_CFLAGS += -std=c99
#LOCAL_LDLIBS += -llog -landroid
LOCAL_LDLIBS += -llog -landroid -lm -lz
修改內(nèi)容:將ffmpeg由動態(tài)鏈接方式改為靜態(tài)鏈接方式
#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
修改內(nèi)容:將ffmpeg由動態(tài)鏈接方式改為靜態(tài)鏈接方式
#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動態(tài)庫的加載
修改文件:ijkplayer/ijkplayer-java/src/main/java/tv/danmaku/ijk/media/player/IjkMediaPlayer.java
修改內(nèi)容:由于ffmpeg采用了靜態(tài)庫導入奕坟,所以這里并不需要加載ffmpeg的動態(tài)庫祥款。
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
修改內(nèi)容:將cpu的類型指定為armv7a,并且開啟debug模式
android {
//指定armeabi-v7a
defaultConfig {
externalNativeBuild {
ndkBuild {
abiFilters "armeabi-v7a"
}
}
}
buildTypes {
//添加debug模式的指定
debug {
jniDebuggable true
}
}
}
修改內(nèi)容:刪除掉非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庫的關(guān)聯(lián)
修改文件:ijkplayer/local.properties
修改內(nèi)容:ijkplayer由于需要依賴ndk進行編譯月杉,所以我們要指定本機的ndk目錄地址刃跛,并且確保這個ndk版本必須在r12到r14之間的版本,千萬不要用到sdk里面的那個ndk苛萎。
ndk.dir=<你的ndk路徑>
關(guān)閉編譯優(yōu)化方便調(diào)試
修改文件:ijkplayer/ijkplayer-armv7a/src/main/jni/Application.mk
修改內(nèi)容:c代碼在編譯的時候可能過度優(yōu)化導致調(diào)試的時候看不到變量的值桨昙,我們可以將-O3的優(yōu)化級別改為-O0
APP_CFLAGS :=
#將O3的優(yōu)化級別降低為O0
#-O3 -Wall -pipe \
-O0 -Wall -pipe \
-ffast-math \
-fstrict-aliasing -Werror=strict-aliasing \
-Wno-psabi -Wa,--noexecstack \
-DANDROID -DNDEBUG
可能會遇到的錯誤
如果發(fā)現(xiàn)這個報錯
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"
}
}
如果發(fā)現(xiàn)這個報錯
AAPT2 error: check logs for details
在ijkplayer/gradle.properties
中添加
android.enableAapt2=false
結(jié)語
這篇文章簡單介紹了怎么在Android Studio中對ijkplayer工程進行源碼調(diào)試的必要修改步驟,這也是對于分析源碼或者二次開發(fā)都有非常有用的幫助腌歉。
本文發(fā)布于簡書
End!