0x1 系统架构
ijkplayer是由b站开源的播放器项目,底层基于ffmpeg, 支持Android和iOS。下面我们来简单介绍一下Android上的实现。
Android上的系统架构图如下。
下面分别对各个模块进行介绍。
ijkplayer-example是app的实现,主要是ui逻辑的实现,包括activity的实现,ui控件的组织,窗口的定制,数据的存储。
ijkplayer-example通过调用ijkmediaplayer,android mediaplayer, google exoplayer这三种mediaplayer来实现媒体播放。
ijkplayer-java是对底层实现的ijkmediaplayer和android mediaplayer的java封装,对ijkmediaplayer的封装是通过调用底层jni对应的java接口,对android mediaplayer的封装是调用android系统实现的默认mediaplayer接口。
ijkplayer-exo是对google exoplayer的封装,除了android默认的播放器之外,ExoPlayer是Google提供的在android平台上的另外一种播放器。
libijkplayer提供了ijkmediaplayer的jni实现ijkplayer_jni.c,然后调用封装过的ffplayer.c, 再调用底层实现的解码库libijkffmpeg和显示库libijksdl。
libijkffmpeg实现了媒体文件的demux, decode等功能。
libijksdl实现对解码后的数据进行显示。
0x2 Java层关键模块分析
0x21 三种不同的mediaplayer实现.
这三种mediaplayer的类图如下图所示。
AndroidMediaPlayer是对Andoid默认播放器的封装。
IjkMediaPlayer是基于ffmpeg的播放器实现。
IjkExoMediaPlayer是基于Goodle开源的ExoPlayer的封装。
0x22 设置不同的Render
不同Render的类图如下图所示。
SurfaceRenderView是基于SurfaceView的显示实现。
TextureRenderView是基于TextureView的显示实现。
上述两种显示实现方式都实现了接口IRenderView。
0x23 IjkMediaPlayer的JNI接口
详细的JNI接口如下图所示。
这些JNI接口提供了播放器的基本接口,包括播放准备(_setDataSource, _setVideoSurface, setVolume,_prepareAsync), 播放控制(_start, _stop, seekTo,_release, _reset)等功能。
0x3 关键流程
0x31 设置surface
设置surface的流程图如下图所示。
从上面的流程图可知,通过接口_setVideoSurface(), 把UI层的Surface对象(可以理解为显示窗口)设置给SDL显示对象,作为其显示窗口(native_window), 这样SDL有需要显示的内容可以直接在这个显示窗口上显示输出即可。
0x32 显示流程
显示的流程图如下图所示。
下面对上面的流程图简单说明。
解码线程ffp_video_thread解码完成以后,调用接口queue_picture把需要显示的buffer往SDL模块发送。
然后调用func_fill_frame()填充显示buffer, 这个时候需要判断是通过ffmpeg还是mediacodec实现的解码。
如果是ffmpeg实现的话则调用ijksdl_vout_overlay_ffmpeg.c中的函数func_fill_frame()。
否则调用ijksdl_vout_overlay_android_mediacodec.c中的func_fill_frame()。
然后显示线程video_refresh_thread就可以开始显示了。如果是GPU支持的格式则调用GPU进行输出,这个时候调用的是IJK_EGL_display,否则调用ANativeWindow_lock和ANativeWindow_unlockAndPost进行输出。