ijkplayer阅读学习笔记之从代码上看播放流程
看了很久的ijkplayer的视频播放,其实还是没有怎么看懂,只是个人浅浅的笔记
关键部分就是联网获取数据那部分,还没有搞定其实
从用户点击一个已有地址的网络视频开始,从源码分析播放流程。
1. // init player 加载native底层库
IjkMediaPlayer.loadLibrariesOnce(null);
IjkMediaPlayer.native_profileBegin("libijkplayer.so");
第一句话是加载三个重要的so文件
libLoader.loadLibrary("ijkffmpeg");
libLoader.loadLibrary("ijksdl");
libLoader.loadLibrary("ijkplayer");
第二句话调用了一个native方法public static native void native_profileBegin(String libName);
2. 设置uri,可以是rtmp,rtsp,http等,native ffplay代码中会根据该uri匹配不同的流媒体协议,具体参考ffplay。 支持本地和网络视频。
if (mVideoPath != null)
mVideoView.setVideoPath(mVideoPath);
else if (mVideoUri != null)
mVideoView.setVideoURI(mVideoUri);
3. 分析网络视频源:接着openVideo()
先创建播放器 mMediaPlayer = createPlayer(mSettings.getPlayer());接着是播放器的相关设置
诸如: mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
mMediaPlayer.setOnInfoListener(mInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);等
当然,还需要设置播放源,会调用
tv.danmaku.ijk.media.player.IMediaPlayer#setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String,java.lang.String>)
开始播放mVideoView.start();调用tv.danmaku.ijk.media.player.IMediaPlayer#start,这里java层就结束了,进入c底层,其实从加载native底层库开始就进入c底层了
4. 分析c底层:/ijkmedia/ijkplayer/android/ijkplayer_jni.c这个文件里 static JNINativeMethod g_methods[] 一个数组,声明了全部的java层的native方法 ,而调用native的方法集中在类 tv.danmaku.ijk.media.player.IjkMediaPlayer
5. 没想到居然是c主动调用java,通过反射生成java端所需要的函数,这个地方不是我们正常JNI的调用思路
正常调用JNI的思路:是通过javah,获取一组带签名函数,然后实现这些函数。这种方法很常用,也是官方推荐的方法。
分析一下JNI运行时机制:
当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,然后首先试图找到"JNI_OnLoad"函数,如果该函数存在,则调用它。
JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换。很显然ijk的作者是使用的这种调用方式
在 ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/android/ijkplayer_jni.c
然后在JNI_OnLoad 初始化好多内容,比如上图三个 global 型的函数
这样就可以理解作者为什么一开始就先调用三个 so 文件了
ijkplayer-sample/src/main/jni/ijkmedia/ijksdl/android/ijksdl_android_jni.c里也有JNI_OnLoad。
Ffmpeg的个人感觉是不需要,因为作者直接使用的ffmpeg的代码,只需要正常引入就好。
6. J4a里生成的两个java文件也是很重要的类
目前还不清楚作用
7. ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ff_ffplay_def.h 这个定义了ijk播放器核心数据结构体的类,作者是直接使用的ffmpeg的ijkplayer-sample/src/main/jni/ffmpeg/ffplay.c在ffmpeg的文件的基础上的扩展,里面引用了很多ffmpeg的源文件。基本上就不需要关心ffmpeg的源代码了。
8. 这里只分析libijkplayer.so
8.1 ijkmp_global_init 在 ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ijkplayer.c
void ijkmp_global_init()
{
ffp_global_init();
}
ffp_global_init() 在
ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ijkplayer_internal.h的include的
ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.h里,具体定义代码在
ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ff_ffplay.c中,主要是ffmpeg的初始化工作。
8.2 ijkmp_global_set_inject_callback(inject_callback)是在
ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ijkplayer.c的include的
ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ijkavformat/ijkavformat.h里声明,
在ijkplayer-sample/src/main/jni/ijkmedia/ijkplayer/ijkavformat/utils.c里定义
这里使用最多的是ijkavformat.h里的
typedef int (*IjkAVInjectCallback)(void *opaque, int message, void *data, size_t data_size);
这个声明引入了 IjkAVInjectCallback 类型作为函数指针的同义字,该函数有四个不同 类型的参数以及一个 int 类型的返回值