整章目录:Android------- IjkPlayer 源码学习目录
在Android ----- ijkplayer源码阅读Java层(二)中欠了一篇细说加载Ijkplayer动态库时到底干了写啥???那我们就来聊聊上图红色圈起来部分。
VideoActivity.onCreate中调用
native_profileBegin这个是加载----性能分析工具android-ndk-profiler的动态库。这方面没用过,不说。
loadLibrariesOnce:
加载三个动态库
ffmpeg:音视频编解码的核心库
ijksdl:在native层创建一些java类对象,并存放在native层全局引用
ijkplayer : 播放器的核心库,Java层所有native方法的实现库。
由于介绍的是Ijkplayer框架所以ffmpeg不是我们的重点,后面只提ffmpeg基本的执行流程。
所以只聊加载ijksdl和ijkplayer动态库的那些事
加载ijksdl:
由于本人在模拟器运行,所以找ijkplayer-x86/src/main/jni/ijkmedia/ijksdl/android/ijksdl_android_jni.h
入口:
ijksdl_android_jni.JNI_OnLoad
j4a_allclasses.J4A_LoadAll__catchAll:
上图:加载用到的大部分Java类
下面已加载Android_os_Build举例
j4a_base.J4A_LOAD_CLASS:宏定义
其中 \ 的作用----连接多行。即在头文件中换行前,还需要连接后面的代码时,使用该符合。例如:
#define HELLO "hello \
the world"
printf("HELLO is %s\n", HELLO);
输出结果:hello the world
和 # 运算符一样,## 运算符用在替换文本。所以上面的例子我们就可以看成如下:
class__=android_os_Build;
J4A_loadClass__J4AC_##class__(env);
===》J4A_loadClass__J4AC_android_os_Build(env);
于是全局搜索下J4A_loadClass__J4AC_android_os_Build这个函数的实现,在ijkplayer-x86/src/main/jni/ijkmedia/ijkj4a/j4a/class/android/os/Build.c中找到我们的函数。
上图添加了相应的备注,两个箭头都是去创建相应的Java类,所以介绍第一个红箭头
Build.J4A_FindClass__asGlobalRef__catchAll:
J4A_FindClass__catchAll:
J4A_NewGlobalRef__catchAll:
这些都是jni开发的基础知识,不多细讲,其目的就是创建系统中的android/os/Build.java类。
OK,第一个动态库就这么加载完成了。
加载ijkplayer:
找到其入口:ijkplayer-x86/src/main/jni/ijkmedia/ijkplayer/android/ijkplayer_jni.c
ijkplayer_jni.JNI_OnLoad
红一:创建IjkMediaPlayer.java,前面讲过,不多说
红二:动态注册了IjkMediaPlayer.java的native方法(红五也是一样的)
接下来就是播放器正式的初始化了
红三:ijkmp_global_init最终回去调用ff_ffplay.ffp_global_init
红框圈着的为ffmpeg基本操作
FFMPeg一般流程:
1、av_register_all();//注册所有文件格式和编解码库
2、avformat_network_init();//打开网络视频流
3、av_open_input_file();//读取文件头部把信息保存到AVFormatContext结构体
4、av_find_stream_info();//为pFormatCtx->streams填充上正确的信息
5、CODEC_TYPE_VIDEO;//通过判断得到视频流类型
6、avcodec_find_decoder();//查找解码器
7、avcodec_open();//打开编解码器
8、avcodec_alloc_frame();//分配空间保存帧数据
9、av_read_frame();//不断从流中提取帧数据
10、avcodec_decode_video();//解码视频流
11、avcodec_close();//关闭解码器
12、avformat_close_input_file();//关闭输入文件
红四:ijkmp_global_set_inject_callback(inject_callback)
设置一个回调函数,作用将jni数据通过Java的bundle类封装后,传到Java层使用
inject_callback:
上面做了什么事???我们先看一段Java代码:
Bundle jbundle=new Bundle();
jbundle.putString("url",real_data->url);
jbundle.putString("segment_index",real_data->segment_index);
jbundle.putString("retry_counter",real_data->retry_counter);
weak_thiz.onNativeInvoke(jbundle);
是不是很熟悉,而inject_callback也是做的同样的事情,只是它是用c语言实现的。
重要步骤:
1. jobject weak_thiz = (jobject) opaque;
2. jbundle = J4AC_Bundle__Bundle__catchAll(env);
3. J4AC_Bundle__putString__withCString__catchAll(env, jbundle, "url", real_data->url);
4. real_data->is_handled = J4AC_IjkMediaPlayer__onNativeInvoke(env, weak_thiz, what, jbundle);
1. 在Java层创建IjkMediaPlayer时,将自己闯入native层,最后存放在native层的IjkMediaPlayer结构体的weak_thiz中。
2、3、4 都一样,以4为例说明:
J4AC_IjkMediaPlayer__onNativeInvoke:
在IjkMediaPlayer.h头文件中定义的别名
然后在IjkMediaPlayer.c中去找其实现方法:
只调用了一个方法:
jboolean J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__onNativeInvoke(JNIEnv *env, jobject weakThiz, jint what, jobject args)
{
return (*env)->CallStaticBooleanMethod(env, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_onNativeInvoke, weakThiz, what, args);
}
其中
class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id参数:需要用的Java类
class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_onNativeInvoke参数:需要调用的函数名称
在IjkMediaPlayer.c中的J4A_loadClass__J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer方法,明确注册了这两个参数。
OK,Ijkplayer 加载动态库的故事讲完了。