ijkplayer 播放器:在Android端進(jìn)行native調(diào)試

前言

ijkplayer是B站開源的基于FFmpeg的輕量級Android/iOS視頻播放器,強(qiáng)烈建議在定制的播放器的時(shí)候以ijkplayer為基礎(chǔ)進(jìn)行二次開發(fā)。
對于二次開發(fā)時(shí)代碼的調(diào)試時(shí)一件重中之重的事情;在iOS平臺,ijkplayer可以直接在Xcode進(jìn)行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進(jìn)行調(diào)試,很開心的是官方提供了這么一個(gè)流程:

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,幾乎所有人都會遇到這一個(gè)問題,當(dāng)你執(zhí)行這句命令的時(shí)候:

sh android/patch-debugging-with-lldb.sh armv7a

恭喜你,一般你都會發(fā)現(xiàn)如下錯(cuò)誤:

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?這個(gè)腳本竟然是有問題的!
當(dāng)然這是因?yàn)檫@個(gè)腳本是用git一些修改patch進(jìn)行代碼還原,但是但是,由于這個(gè)腳本已經(jīng)太久沒有更新了,而ijkplayer的一些代碼結(jié)構(gòu)又有調(diào)整導(dǎo)致腳本無法從patch文件附帶的這些信息把代碼正確還原回去。


解決思路

我們通過查看patch-debugging-with-lldb.sh,可以發(fā)現(xiàn)我們只需要按照以下幾個(gè)patch文件做對應(yīng)源碼修改就可以達(dá)到調(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

例如隨便打開里面一個(gè)文件例如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的同學(xué)應(yīng)該就知道,這里應(yīng)該是要?jiǎng)h除掉

classpath 'com.android.tools.build:gradle:2.1.3'

然后把這一行添加上去

classpath 'com.android.tools.build:gradle:2.2.0-rc1'

其他文件也是以此類推,當(dāng)然ijkplayer的源碼不斷的迭代,可能有些修改已經(jīng)對不上了,并且有些修改也并不需必須的,所以這里我們就不完整對patch里面的每一行修改都進(jìn)行修改,而是分析出這些patch的主要更改點(diǎn)在什么地方。


解決重點(diǎn)

通過分析這幾個(gè)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)容:添加兩個(gè)本地庫-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)庫導(dǎo)入,所以這里并不需要加載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進(jìn)行編譯,所以我們要指定本機(jī)的ndk目錄地址,并且確保這個(gè)ndk版本必須在r12到r14之間的版本,千萬不要用到sdk里面的那個(gè)ndk。

ndk.dir=<你的ndk路徑>

關(guān)閉編譯優(yōu)化方便調(diào)試

修改文件:ijkplayer/ijkplayer-armv7a/src/main/jni/Application.mk
修改內(nèi)容:c代碼在編譯的時(shí)候可能過度優(yōu)化導(dǎo)致調(diào)試的時(shí)候看不到變量的值,我們可以將-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

可能會遇到的錯(cuò)誤

如果發(fā)現(xiàn)這個(gè)報(bào)錯(cuò)

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 {
    #這個(gè)值需要與工程下的ijkplayer/build.gradle中的versionCode相同
    flavorDimensions "800800"
  }
}

如果發(fā)現(xiàn)這個(gè)報(bào)錯(cuò)

AAPT2 error: check logs for details

ijkplayer/gradle.properties中添加

android.enableAapt2=false

結(jié)語

這篇文章簡單介紹了怎么在Android Studio中對ijkplayer工程進(jìn)行源碼調(diào)試的必要修改步驟,這也是對于分析源碼或者二次開發(fā)都有非常有用的幫助。

本文發(fā)布于簡書

End!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容