您的位置:首页 >聚焦 >

IjkPlayer系列之JNI基础及源码目录介绍

2022-03-11 06:08:52    来源:程序员客栈

‍‍

PS:最近市场连续几天普跌,既是风险也是难得的机会。
本篇文章是阅读 IjkPlayer 播放器源码的第一篇,记得在之前的工作中也编译过 IjkPlayer,为了后续方便继续阅读其源码,下文中简单汇总下 JNI 开发的一些基础知识,本文主要内容如下:

IjkPlayer编译

IjkPlayer源码目录

NDK介绍

JNI基础知识

总结

IjkPlayer编译IjkPlayer 的编译之前单独写过一篇文章,内容还算详细,具体参考如下:如何正确编译ijkplayerIjkPlayer源码目录IjkPlayer源码目录介绍,具体详见目录后面的说明:

1├──android//android相关目录 2│├──compile-ijk.sh 3│├──contrib//ffmpeg编译目录 4││├──compile-ffmpeg.sh//ffmpeg编译脚本 5││├──compile-libsoxr.sh//libsoxr编译脚本 6││├──compile-openssl.sh//openssl编译脚本 7││├──ffmpeg-arm64 8││├──ffmpeg-armv5 9││├──ffmpeg-armv7a10││├──ffmpeg-x8611││├──ffmpeg-x86_6412│├──ijk-addr2line.sh13│├──ijk-ndk-stack.sh14│├──ijkplayer//androidijkPlayer源码目录15││├──ijkplayer-arm6416││├──ijkplayer-armv517││├──ijkplayer-armv7a18││├──ijkplayer-example//ijkPlayer使用案例19││├──ijkplayer-exo20││├──ijkplayer-java21││├──ijkplayer-x8622││├──ijkplayer-x86_6423├──compile-android-j4a.sh24├──config//ffmpeg编译脚本配置目录25│├──module-default.sh//ffmpeg默认配置脚本文件26│├──module-lite-hevc.sh//ffmpeg最小化配置添加hevc功能脚本文件27│├──module-lite.sh//ffmpeg最小化配置脚本文件28│└──module.sh//ffmpeg当前编译配置脚本文件29├──doc30│└──preflight_checklist.md31├──extra//ijkPlayer使用的开源库的下载目录32│├──ffmpeg//ffmpeg33│├──libyuv//yuv图像处理库34│└──soundtouch//音频处理库,主要是变速、变调等35├──ijkmedia//ijkPlayernative层的核心代码36│├──ijkj4a//native层和java层回调的接口层,开源项目jni4android生成37│├──ijkplayer//ijkPlayernative层代码38│├──ijksdl//ijkPlayer音视频渲染SDL库39│├──ijksoundtouch//ijk封装后的soundtouch库40│└──ijkyuv//yuv图像处理库41├──ijkprof//ijkplayer的性能调试库42├──init-android-exo.sh//初始化exoPlayer脚本43├──init-android-j4a.sh//初始化j4a脚本44├──init-android-libsoxr.sh//初始化soxr脚本45├──init-android-libyuv.sh//初始化yuv脚本46├──init-android-openssl.sh//初始化openssl脚本47├──init-android-prof.sh//初始化android-ndk-profile脚本48├──init-android.sh//初始化android平台脚本,主要拉取ffmpeg、第三方库等49├──init-android-soundtouch.sh50├──init-config.sh//ffmpeg脚本文件配置脚本51├──init-ios-openssl.sh52├──init-ios.sh53├──ios//IOS相关目录

NDK介绍对于大部分的应用开发者可能都不会接触到 NDK,但如果涉及到硬件操作的话就不得不使用 NDK 了,使用 NDK 还有一个原因就是 C/C++ 的效率比较高,因此我们可以把一些耗时操作放在 NDK 中进行实现。NDK 是 Native Development Kit 的简称,它是一个工具集,继承了 Android 的交叉编译环境,并提供了一套比较方便的 MakeFile,可以帮助开发者快速开发 C/C++ 的动态库,并自动将 so 和 java 程序打包成 apk 在 Android 中运行。JNI基础知识JNI 是 Java Native Interface 的缩写,中文为 Java 本地调用,从 Java 1.1 开始,JNI 标准成为 Java 平台的一部分,它允许 Java 代码和其他语言写的代码进行交互。JavaVM和JNIEnvJavaVM 表示 Java 虚拟机,定义在 jni.h 中,每个进程可以有多个 JavaJVM,但 Android 中只允许有一个,这个对应的 javaJVM 对象可以在进程中的各线程间共享,在使用的时候全局保存一个 JavaVM 变量即可共用,其常用的获取方式如下:

第一种:

1staticJavaVM*g_jvm; 2//第一种 3JNIEXPORTjintJNI_OnLoad(JavaVM*vm,void*reserved) 4{ 5JNIEnv*env=NULL; 6//为JavaVM指针赋值 7g_jvm=vm; 8if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_4)!=JNI_OK){ 9return-1;10}11//...12returnJNI_VERSION_1_4;13}

第二种:

1staticJavaVM*g_jvm;2JNIEXPORTjintJNICALLJava_manu_com_iptvsamples_ndk_NDKSampleActivity_sum3(JNIEnv*env,jobjectobj,jintaddend1,jintaddend2){4//为JavaVM指针赋值5env->GetJavaVM(&g_jvm);6returnaddend1+addend2;7}

此外还可以通过 JNI 函数JNI_CreateJavaVM来创建 JavaVM。JNIEnv 提供了大部分 JNI 函数,Native 函数都接收 JNIEnv 作为第一个参数,JNIEnv 用于线程本地存储,不能在线程之间共享 JNIEnv,如果一段代码没有其他方法获取它的 JNIEnv,可以使用共享 JavaVM 通过 GetEnv 来获取线程的 JNIEnv。JNI注册方式注册 JNI 函数主要有两种方式,即静态注册方式和动态注册方式,典型的比如音视频开源项目 ijkPlayer 就是动态注册的方式,后续文中再继续分析。

静态注册

静态注册方式主要就是通过定义的包含 native 方法的 .java 文件,通过 javah 相关命令生成对应的 .h 头文件。在 Activity 中定义本地方法如下:

1publicnativeintsum(intaddend1,intaddend2);

为了方便,将目录切换到项目的 java 目录下,使用如下命令生成供 C/C++ 使用的头文件:

1javah-jnicom.manu.ndksamples.MainActivity

如果执行报找不到类文件的异常,可以尝试添加 -classpath参数生成对应头文件,上述本地方法生成的头文件代码如下:

1/*DONOTEDITTHISFILE-itismachinegenerated*/ 2#include 3/*Headerforclassmanu_com_iptvsamples_ndk_NDKSampleActivity*/ 4 5#ifndef_Included_com_manu_ndksamples_MainActivity 6#define_Included_com_manu_ndksamples_MainActivity 7#ifdef__cplusplus 8extern"C"{ 9#endif10/*11*Class:com_manu_ndksamples_MainActivity12*Method:sum13*Signature:(II)I14*/15JNIEXPORTjintJNICALLJava_com_manu_ndksamples_MainActivity_sum16(JNIEnv*,jobject,jint,jint);1718#ifdef__cplusplus19}20#endif21#endif

对应的文件名是包名+类名:com_manu_ndksamples_MainActivity.h,导入头文件,使用 C/C++ 实现 Java 中定义的 native 方法,参考如下:

1#include"com_manu_ndksamples_MainActivity.h" 2 3/* 4*Class:com_manu_ndksamples_MainActivity 5*Method:sum 6*Signature:(II)I 7*/ 8extern"C"JNIEXPORTjintJNICALLJava_com_manu_ndksamples_MainActivity_sum 9(JNIEnv*env,jobjectobj,jintaddend1,jintaddend2){10returnaddend1+addend2;11}

动态注册

动态注册方式是通过 JNI 中的 JNINativeMethod的结构体来保存 native 函数与 JNI 函数之间的一一对应关系,该结构体定义如下:

1typedefstruct{2constchar*name;3constchar*signature;4void*fnPtr;5}JNINativeMethod;

上面静态注册的 sum方法也可以用动态注册的方式,如下:

1#include 2#include 3#include 4 5usingnamespacestd; 6 7#defineJNI_CLASS"com/manu/ndksamples/MainActivity" 8 9staticJavaVM*g_jvm;1011staticjintsample_sum(JNIEnv*env,jobjectthiz,jintadd1,jintadd2){12cout<<"sample_sum"<

上述代码中函数 sample_sum与 Java 中的 native 方法 sum相对应,这种对应关系是通过 RegisterNatives函数来进行注册的,其基本流程是当调用 System.loadLibrary加载库的时候会去查找 JNI_OnLoad这个函数,然后再该函数回调中进行注册,同样的在JNI_OnUnload中进行销毁操作。总结本文主要介绍了 IjkPlayer 的源码目录、IjkPlayer 的编译,以及一些必备的 JNI 相关的基础知识,下篇将正式开始 IjkPlayer 源码的阅读。推荐阅读:

OpenGL ES渲染播放视频

OpenGL ES投影和视图变换

AudioRecord采集音频数据及合成

关键词: 基础知识 对应关系 图像处理

相关阅读