Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

用 CMake 为 ijkplayer 增加 android native debug 支持 #5

Closed
befovy opened this issue Jun 30, 2019 · 4 comments
Closed

用 CMake 为 ijkplayer 增加 android native debug 支持 #5

befovy opened this issue Jun 30, 2019 · 4 comments

Comments

@befovy
Copy link
Owner

@befovy befovy commented Jun 30, 2019

给 ijkplayer 增加了 cmake 构建方式,支持在 android studio 中进行 c 代码断点调试。
直接上手请前往 https://github.com/befovy/ijkplayer#build-android-via-cmake

如果想了解一下整个过程请继续阅读本文。

@befovy

This comment has been minimized.

Copy link
Owner Author

@befovy befovy commented Jul 3, 2019

ijkplayer 简介

ijkplayer 是由 B 站开源的一款移动端播放器。基于 ffmpeg 中 ffplay 开发,并添加 android MediaCodec、iOS VideotTolbox 视频硬解码支持以及 opengles、NativeWindow(android) 渲染,在当前的移动直播热潮中,被大量使用。
但使用 ijkplayer 进行 android 播放器开发的过程中,调试起来稍微有些麻烦,官方提供了ndk 调试的几个patch 文件,并且有一段描述,是基于 ndk-build android.mk 工具链构建的。说实话由于没有做成开箱即用,我也没认真看过这部分内容。 由于我对 cmake 的了解更多一些,加之 google 官方也逐渐将 ndk 的工具链由 ndk-build 转向 cmake,并在 android studio 中对 cmake 提供了更好的支持。同时 cmake 也是跨平台的构建工具,方便后续将 ijkplayer 扩展至其它平台,所以我用 cmake 重新实现了 ijkplayer 的构建过程。

只要不是修改 ffmpeg 的源代码,其它内容包括 ijkplayer 的 c 代码修改, java 层修改,都可以在 android studio 中一键运行、调试。

关于 ijkplayer 内部核心代码的分析,多个线程的工作内容,解码部分,音视频同步控制等部分的更深入的介绍,可以参考金山云视频的分析文章 https://www.jianshu.com/p/daf0a61cc1e0 , 我最开始也是从这一篇文章入手逐渐读懂 ijkplayer 源代码的。

题外话:ijkplayer 在 github 上的提交记录自 k0.8.8 版本之后就再也没有了,好多人评论说项目已经放弃维护了,但是细心寻找会发现在另一个项目 Bilibili/ffmpeg 中,还不断有提交记录。说明项目还是在不断开发,只是重心放在实现新的 ffmpeg protocol 上,没有及时维护 ijkplayer 项目上的 issues。

下面具体说说一些具体的修改内容。

@befovy

This comment has been minimized.

Copy link
Owner Author

@befovy befovy commented Jul 4, 2019

android 工程结构调整

ijkplayer 的项目本身具有多个 android module。

ijkplayer-arm64
ijkplayer-armv5
ijkplayer-armv7a
ijkplayer-x86
ijkplayer-x86_64   # 上面几个都是一个module 壳,shell 命令编译好 so 库放在指定目录中
ijkplayer-example  # 示例APP项目
ijkplayer-exo      # 对 google exoplayer 的一层封装适,配合ijkplayer 定义的 mediaplyer 接口
ijkplayer-java     # ijkplayer java 层代码,对接 native 层代码,以及硬解码器的选择

项目中为不同的 cpu 架构都单独设置了 module,这几个 module 最大的区别就是在 Application.mk 中 APP_ABIAPP_PLATFORM 的值不同,其中 APP_PLATFORM 取值和 build.gradle 中定义的 minSdkVersion 相同。
ijkplayer 最低竟然支持 sdk version 9,确实感谢 B 站项目组初期在这方面的辛苦付出。

这种 module 确实方便采用 ndk-build 工具的项目管理,但是我在使用过程中还是觉得有些不太方便之处:

  1. 在 android studio 中搜索 c 代码,搜索结果中重复出现多次
  2. android studio 中的 c 代码没有语法高亮。

对于问题1,我找到的解决方案是:

//NOTE:: run `./gradlew ideaModule` to apply exclude dirs
apply plugin: 'idea'

idea.module {
    excludeDirs += file('ijkplayer-armv7a/src/')
    excludeDirs += file('ijkplayer-arm64/src/')
    excludeDirs += file('ijkplayer-armv5/src/')
    excludeDirs += file('ijkplayer-x86/src/')
    excludeDirs += file('ijkplayer-x86_64/src/')
}

在工程根 build.gradle 中增加这样的配置,并执行 ./gradlew ideaModule, 就会在搜索结果中排除来自这几个文件夹的内容。

对于问题2,我当然是用 cmake 解决了,新建一个 module, 取名 fijkplayer-full。换个名字方便和原项目进行区分,同时 f 也表示full, 我在这个 module 中囊括了原来 6 个 module 提供的内容。 f 还有别的含义,有机会在其它地方会提到。
在 fijkplayer-full 的 build.gradel 中增加 cmake 的配置以及引入 ijkplayer-java module 的 java 源代码。

android {
    .......
    .......
    sourceSets.main {
        java.srcDirs = ["$rootProject.rootDir/ijkplayer-java/src/main/java/"]
        jni.srcDirs = [] // This prevents the auto generation of Android.mk
    }

    externalNativeBuild {
        cmake {
            path 'src/main/CMakeLists.txt'
        }
    }
}

修改后,fijkplayer-full 就成了 "六神合体" 了。

对于不同cpu 架构区分不同的 minSdkVersion 可以通过 productFlavors 实现,这一块我还没有具体考虑充分,目前是统一设置了大家用得最多的 minSdkVersion 16。

此部分修改的完整内容点我查看, github.com/befovy/ijkplayer

@befovy

This comment has been minimized.

Copy link
Owner Author

@befovy befovy commented Jul 4, 2019

CMakeLists.txt

编写 CMakeLists.txt 完全是个翻译工作,将 Android.mk 中的规则翻译过来就行。完成后通过 CMake 方式最终生成的 aar 和原始方式生成的aar 文件大小几乎没什么差别,见下图。
fijkplayer-full 只编译了 armv7a 架构,和 ijkplayer-armv7a 模块编译结果的对比。
fijkplayer-full 中 classes.jar 较大是因为包含了 ijkplayer-java module 中的内容。
还可以调整CMake 参数进一步减小生成库的大小。
图片

转到 CMake 工具链的过程还遇到下面几个问题。

libffmpeg.so 找不到

libffmpeg.so 是事先通过 shell 命令编译成的,在 ffmpeg build 文件夹中,并且几乎不会改变,除非是要修改 ffmpeg。
在 CMake 中通过

add_library(ijkffmpeg SHARED IMPORTED)
set_target_properties( # Specifies the target library.
    ijkffmpeg
    PROPERTIES
    IMPORTED_LOCATION ${FFMPAG_SHARED_DIR}/libijkffmpeg.so
)

引入预编译好的 ijkffmpeg,在编译 ijkplayer 的过程可以顺利完成链接。但是打包运行找不到 libijkffmpeg.so。分析最终的 apk 以及 fijkplayer-full.aar ,发现其中都没有 libijkffmpeg.so 文件,所以是打包 aar 的时候没有将这个文件打包进去。查看工程目录结构,发现编译好的 libijkplayer.so 和 libijksdl.so 都在文件夹 /build/intermediates/cmake/debug/obj/armeabi-v7a 中,所以还需要修改 CMakeLists.txt 在每次编译的时候将 libijkffmpeg.so 复制过去。最后的解决方案是

add_library(ijkffmpeg SHARED IMPORTED)
set_target_properties( # Specifies the target library.
    ijkffmpeg
    PROPERTIES
    IMPORTED_LOCATION ${FFMPAG_SHARED_DIR}/libijkffmpeg.so
    )

add_custom_target(cpffmpeg
    COMMAND mkdir -p ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
    COMMAND cp ${FFMPAG_SHARED_DIR}/libijkffmpeg.so ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
)

add_dependencies(ijkffmpeg cpffmpeg)

没有找到一条命令能够解决的,所以麻烦了一点多写了点,用两个cmake 命令解决。

cpufeatures 编译出错

原项目中用到了 ndk 中的 cpufeatures,我在 CMake 文件中也引入并编译了 cpufeatures,但在编译 x86 以及 x86_64 架构的时候却出现了编译错误,具体是一处未识别的汇编指令。
最后发现是 ndk r15c 版本中 文件 $ANDROID_NDK/sources/android/cpufeatures/cpu-features.c 中少了几个换行符,补上之后(下面内容)编译就没问题了。

static __inline__ void x86_cpuid(int func, int values[4])
{
    int a, b, c, d;
    /* We need to preserve ebx since we're compiling PIC code */
    /* this means we can't use "=b" for the second output register */
    __asm__ __volatile__ ( \
      "push %%ebx"
      "cpuid\n" \
      "mov %%ebx, %1\n"
      "pop %%ebx\n"
      : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
      : "a" (func) \
    );
    values[0] = a;
    values[1] = b;
    values[2] = c;
    values[3] = d;
}

意外的是我最终发现 ijkplayer 项目中没有用到 cpufeatures 的地方,可能是 gpl 协议的 android-ndk-profiler 部分用了吧,但项目中默认使用了一个假的 android-ndk-profiler,此部分没有去深究,性能分析使用 simpleperf 就足够了。
最后还是去掉 CMake 中 cpufeatures 的部分。

此部分 CMakeLists.txt 的完整内容请看
https://github.com/befovy/ijkplayer/blob/master/android/ijkplayer/fijkplayer-full/src/main/CMakeLists.txt
https://github.com/befovy/ijkplayer/blob/master/ijkmedia/CMakeLists.txt
https://github.com/befovy/ijkplayer/blob/master/ijkmedia/ijkplayer/CMakeLists.txt
https://github.com/befovy/ijkplayer/blob/master/ijkmedia/ijksdl/CMakeLists.txt

@befovy

This comment has been minimized.

Copy link
Owner Author

@befovy befovy commented Jul 4, 2019

结束语

😄 附一张 NDK 断点调试的大图 👏

刚开始写博客,可能有些啰嗦,多多包涵。或是没有说明白的地方,请大方的前往原始地址进行评论。

最近对 Flutter 产生了兴趣,并打算用 ijkplayer 开发一个 Flutter 的播放器 Plugin, 祝我早日完成。

@befovy befovy closed this Jul 4, 2019
@befovy befovy changed the title 为 ijkplayer 增加 android native debug 支持 用 CMake 为 ijkplayer 增加 android native debug 支持 Jul 4, 2019
@befovy befovy transferred this issue from another repository Aug 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.