转自:https://www.jianshu.com/p/84151c863c72

上层java代码

  • IjkMediaPlayer.java

构造方法

step 1:
IjkMediaPlayer()|--this(sLocalLibLoader);|--initPlayer(libLoader);|--loadLibrariesOnce(libLoader); //加载所需要的库|   |--libLoader.loadLibrary("ijkffmpeg");|   |--libLoader.loadLibrary("ijksdl");|   |--libLoader.loadLibrary("ijkplayer"); // step 2: |--initNativeOnce(); |   |--native_init(); //初始化 native || |--native_setup(new WeakReference<IjkMediaPlayer>(this)); //step 3:

底层c代码

IjkMediaPlayer.java --> ijkplayer_jni.c

step 2.5:
native_init() --> IjkMediaPlayer_native_init
//底层是个空实现
IjkMediaPlayer_native_init(JNIEnv *env)
{MPTRACE("%s\n", __func__);
}step 3:
native_setup --> IjkMediaPlayer_native_setupIjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{IjkMediaPlayer *mp = ijkmp_android_create(message_loop); //创建一个 player 结构体jni_set_media_player(env, thiz, mp); //保存当前player结构体ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));LABEL_RETURN:ijkmp_dec_ref_p(&mp);
}

加载class的宏定义

j4a_allclasses.loader.h  //头文件中包含加载class的宏定义J4A_LOAD_CLASS(java_nio_Buffer);
J4A_LOAD_CLASS(java_nio_ByteBuffer);
J4A_LOAD_CLASS(java_util_ArrayList);
J4A_LOAD_CLASS(android_media_AudioTrack);
J4A_LOAD_CLASS(android_media_MediaCodec);
J4A_LOAD_CLASS(android_media_MediaFormat);
J4A_LOAD_CLASS(android_media_PlaybackParams);
J4A_LOAD_CLASS(android_os_Build);
J4A_LOAD_CLASS(android_os_Bundle);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_misc_IMediaDataSource);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_misc_IIjkIOHttp);
J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_IjkMediaPlayer);
#define J4A_LOAD_CLASS(class__)  --> J4A_loadClass__J4AC_##class__(env)
--> J4A_loadClass__J4AC_IjkMediaPlayer
or
--> J4A_loadClass__J4AC_MediaCodec

class初始化流程

step 2:
Ijksdl_android_jni.c
JNI_OnLoad(JavaVM *vm, void *reserved)|--J4A_LoadAll__catchAll(env);
J4a_allclasses.c
J4A_LoadAll__catchAll(JNIEnv *env)  //调用头文件中的宏定义函数|--#include "j4a/j4a_allclasses.loader.h"|--J4A_LOAD_CLASS(tv_danmaku_ijk_media_player_IjkMediaPlayer);|--J4A_loadClass__J4AC_##class__(env);|--J4A_loadClass__J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer(env)|--class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;|--name     = "mNativeMediaPlayer";|--sign     = "J";|--class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.field_mNativeMediaPlayer = J4A_GetFieldID__catchAll(env, class_id, name, sign);

IjkMediaPlayer结构体

struct IjkMediaPlayer {volatile int ref_count;pthread_mutex_t mutex;FFPlayer *ffplayer;int (*msg_loop)(void*);SDL_Thread *msg_thread;SDL_Thread _msg_thread;int mp_state;char *data_source;void *weak_thiz;int restart;int restart_from_beginning;int seek_req;long seek_msec;
};

step 3的创建player过程

SDL_Class结构体变量

typedef struct SDL_Class {const char *name;
} SDL_Class;static SDL_Class g_class = {.name = "EGL",
};static SDL_Class g_nativewindow_class = {.name = "ANativeWindow_Vout",
};static SDL_Class g_pipeline_class = {.name = "ffpipeline_ffplay",
};

SDL_Vout结构体

struct SDL_Vout {SDL_mutex *mutex;SDL_Class       *opaque_class;SDL_Vout_Opaque *opaque;SDL_VoutOverlay *(*create_overlay)(int width, int height, int frame_format, SDL_Vout *vout);void (*free_l)(SDL_Vout *vout);int (*display_overlay)(SDL_Vout *vout, SDL_VoutOverlay *overlay);Uint32 overlay_format;
};

SDL_Vout_Opaque结构体

typedef struct SDL_Vout_Opaque {ANativeWindow   *native_window;SDL_AMediaCodec *acodec;int              null_native_window_warned; // reduce log for null windowint              next_buffer_id;ISDL_Array       overlay_manager;ISDL_Array       overlay_pool;IJK_EGL         *egl;
} SDL_Vout_Opaque;

IJKFF_Pipeline结构体

struct IJKFF_Pipeline {SDL_Class             *opaque_class;IJKFF_Pipeline_Opaque *opaque;void            (*func_destroy)             (IJKFF_Pipeline *pipeline);IJKFF_Pipenode *(*func_open_video_decoder)  (IJKFF_Pipeline *pipeline, FFPlayer *ffp);SDL_Aout       *(*func_open_audio_output)   (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
};
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)|--IjkMediaPlayer *mp = ijkmp_android_create(message_loop);|--IjkMediaPlayer *mp = ijkmp_create(msg_loop);|--mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface(); //创建surface用于视频输出|   |--SDL_VoutAndroid_CreateForANativeWindow();|       |--SDL_Vout_CreateInternal(size_t opaque_size)|       |   |--SDL_Vout *vout = (SDL_Vout*) calloc(1, sizeof(SDL_Vout));|       ||       |--SDL_Vout_Opaque *opaque = vout->opaque;|       |--opaque->native_window = NULL;|       |--opaque->egl = IJK_EGL_create();创建egl结构体|       |       |--IJK_EGL *egl = (IJK_EGL*) mallocz(sizeof(IJK_EGL));|       |       |--egl->opaque_class = &g_class;|       |       |--egl->opaque = mallocz(sizeof(IJK_EGL_Opaque));|       ||       |--vout->opaque_class    = &g_nativewindow_class;|       |--vout->create_overlay  = func_create_overlay;|       |--vout->free_l          = func_free_l;|       |--vout->display_overlay = func_display_overlay|       ||--mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer); //创建ffplay管线|   |--IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque));|   |--IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;|   |--opaque->ffp                   = ffp;|   |--opaque->surface_mutex         = SDL_CreateMutex();|   |--opaque->left_volume           = 1.0f;|   |--opaque->right_volume          = 1.0f;|   |--pipeline->func_destroy            = func_destroy;|   |--pipeline->func_open_video_decoder = func_open_video_decoder;|   |--pipeline->func_open_audio_output  = func_open_audio_output;|||--ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout); //将管线和视频输出关联|--IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;|--opaque->weak_vout = vout;

初始化总结

step 1中进行IjkMediaPlayer实例的构造,构造过程中完成的step 2加载so并在加载过程中将底层的一些信息保存到IjkMediaPlayer实例中
step 3将IjkMediaPlayer实例对象设置到底层IjkMediaPlayer结构体中,创建底层IjkMediaPlayer结构体。在创建结构体的同时设置message_loop回调函数实现将底层消息传递到上层的作用

初始化后个结构体的关联图

结构体关联图.jpg

到此完成了IJKPlayer的初始化

IjkMediaPlayer使用前的设置

IjkMediaPlayer被封装到了IjkVideoView中,在IjkVideoView实例化的过程中会完成上面的初始化过程。同时在构造函数中会将surface设置到底层,下面是初始化图像渲染环境的代码分析

以设置surfaceView为例:

step 1:
initVideoView()|--initRenders();|--setRender(mCurrentRender);|--renderView = new SurfaceRenderView(getContext());|   |--getHolder().addCallback(mSurfaceCallback); //当SurfaceRenderView被添加到屏幕显示时会回调 mSurfaceCallback 中的 surfaceCreated 方法,在 surfaceCreated 方法中又会调用注册到 renderView 中的 mSHCallback 实例的 onSurfaceCreated 方法|--setRenderView(renderView);|--mRenderView.addRenderCallback(mSHCallback);mSHCallback = new IRenderView.IRenderCallback() //在 mSHCallback 的 onSurfaceCreated 中将 mSurfaceHolder 赋值|--onSurfaceCreated()|--mSurfaceHolder = holder;                 step 2:setVideoPath()|--setVideoURI()|--openVideo();|--mMediaPlayer = createPlayer(mSettings.getPlayer());|--bindSurfaceHolder(mMediaPlayer, mSurfaceHolder); //完成了 mSurfaceHolder 赋值后就将它和 mMediaPlayer 进行绑定|--holder.bindToMediaPlayer(mp); 即 mSurfaceHolder.bindToMediaPlayer(mMediaPlayer)|--InternalSurfaceHolder.bindToMediaPlayer()|--mp.setDisplay(mSurfaceHolder); //即 mMediaPlayer.setDisplay(mSurfaceHolder)|--surface = sh.getSurface();|-- _setVideoSurface(surface); //native 方法设置surface到native

go to native _setVideoSurface

ijkplayer_jni.c

IjkMediaPlayer_setVideoSurface|--ijkmp_android_set_surface //go to Ijkplayer_android.c

Ijkplayer_android.c

ijkmp_android_set_surface(JNIEnv *env, jobject thiz, jobject jsurface)|--ijkmp_android_set_surface_l(env, mp, android_surface);|--SDL_VoutAndroid_SetAndroidSurface(env, mp->ffplayer->vout, android_surface); //Ijksdl_vout_android_surface.c|--ffpipeline_set_surface(env, mp->ffplayer->pipeline, android_surface);

Ijksdl_vout_android_surface.c

SDL_VoutAndroid_SetAndroidSurface(env, mp->ffplayer->vout, android_surface)|--native_window = ANativeWindow_fromSurface(env, android_surface);|--SDL_VoutAndroid_SetNativeWindow(vout, native_window); //Ijksdl_vout_android_nativewindow.c|--SDL_VoutAndroid_SetNativeWindow_l(vout, native_window);|--IJK_EGL_terminate(opaque->egl);|--SDL_VoutAndroid_invalidateAllBuffers_l(vout);|--if (opaque->native_window)|       |--ANativeWindow_release(opaque->native_window);|--if (native_window)|       |--ANativeWindow_acquire(native_window);|--opaque->native_window = native_window;

在上面的初始化的过程中就已经通过IjkMediaPlayer_native_setup函数对SDL_Vout结构中的回调函数,和egl信息进行了初始化。而IjkMediaPlayer_setVideoSurface则将native_window的地址保存到SDL_Vout->opaque->native_window中。到目前为止所有关于显示的信息都保存的了SDL_Vout结构体中.以便在ff_player.c文件中进行调用

下面展示了调用过程:

IjkMediaPlayer_prepareAsync //Ijkplayer_jni.c|--ijkmp_prepare_async|--ijkmp_prepare_async_l(IjkMediaPlayer *mp) //Ijkplayer.c|--ffp_prepare_async_l(FFPlayer *ffp, const char *file_name) //Ff_ffplay.c|--stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)|--SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");|   |--video_refresh_thread(void *arg)|       |--video_refresh(FFPlayer *opaque, double *remaining_time|           |--video_display2(FFPlayer *ffp)|               |--video_image_display2(FFPlayer *ffp)|                   |--SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp);|                       |--vout->display_overlay(vout, overlay);  // vout->display_overlay = func_display_overlay;在SDL_VoutAndroid_CreateForANativeWindow中设置了回调函数|                           |--func_display_overlay|                               |--func_display_overlay_l(vout, overlay);|                                   |--IJK_EGL_display(opaque->egl, native_window, overlay);|                                   |   |--IJK_EGL_makeCurrent(egl, window)|                                   |   |   |--eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context)|                                   |   |--IJK_EGL_display_internal(egl, window, overlay);|                                   |   |   |--IJK_EGL_prepareRenderer(egl, overlay)|                                   |   |   |--IJK_GLES2_Renderer_renderOverlay(opaque->renderer, overlay)|                                   |   |   |--eglSwapBuffers(egl->display, egl->surface);|                                   |   ||                                   |   |--eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);|                                   |--eglReleaseThread(); // FIXME: call at thread exit|--SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read"); //创建视频文件读取线程


著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

ijkplayer源码分析之surface与opengl关联初始化相关推荐

  1. ijkplayer 源码分析(1):初始化流程

    一.ijkplayer 初始化流程 本文是基于 A4ijkplayer 项目进行 ijkplayer 源码分析,该项目是将 ijkplayer 改成基于 CMake 编译,可导入 Android St ...

  2. Spring源码分析【1】-Tomcat的初始化

    org.apache.catalina.startup.ContextConfig.configureStart() org.apache.catalina.startup.ContextConfig ...

  3. 从源码分析tomcat如何调用Servlet的初始化

    引言 上一篇博客我们将tomcat源码在本地成功运行了,所以在本篇博客中我们从源码层面分析,tomcat在启动的过程中,是如何初始化servlet容器的.我们平常都是将我们的服务部署到 tomcat中 ...

  4. ijkplayer源码分析 视频渲染流程

    前言 本系列如下: 整体概述 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是流程分析的第一篇,分析ijkP ...

  5. MyBatis源码分析(三):MyBatis初始化(配置文件读取和解析)

    一. 介绍MyBatis初始化过程 项目是简单的Mybatis应用,编写SQL Mapper,还有编写的SqlSessionFactoryUtil里面用了Mybatis的IO包里面的Resources ...

  6. ijkplayer源码分析 音频解码流程

    前言 本系列如下: 整体概述 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是流程分析的第四篇,分析ijkP ...

  7. ijkplayer源码分析 视频向音频同步

    本系列如下: 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是流程分析的第六篇,分析ijkPlayer中的音 ...

  8. ijkplayer源码分析 start流程和buffering缓冲策略

    前言 本系列如下: 整体概述 视频渲染流程 音频播放流程 read线程流程 音频解码流程 视频解码流程 视频向音频同步 start流程和buffering缓冲策略 本文是分析ijkPlayer中的st ...

  9. Spring源码分析4 — spring bean创建和初始化

    1 介绍 创建并初始化spring容器中,refresh()方法中解析xml配置文件,注册容器后处理器,bean后处理器,初始化MessageSource,ApplicationEventMultic ...

最新文章

  1. 将baidu地图中的baidu logo去掉
  2. [导入]XmlReader 读取器读取内存流 MemoryStream 的注意事项
  3. html打地鼠游戏设计报告,有趣的Axure案例:打地鼠游戏的设计
  4. boost::gil模块沿 x 轴计算梯度的方法的示例
  5. 今天聊聊Oracle Database 21c 十“小”新特性
  6. python3基本数据类型02——列表、元组
  7. Collectors.collectingAndThen()
  8. Dynamics CRM2013 业务规则的新建、激活与删除
  9. 思维导图让你掌握《有效沟通》
  10. tcp协议服务器如何做物联网平台,物联网IoT终端设备如何选择接入协议——(TCP、UDP、MQTT、CoAP、LwM2M哪一个更适合?)...
  11. 月活8.89亿背后:微信工程师细数兼容测试经验
  12. 微信公众平台开发-入门教程
  13. matlab输入数据作方程,用MATLAB函数编写并求解微分方程
  14. win7计算机个性化,win7系统桌面右击不显示个性化选项的解决方法
  15. PNAS:大脑区域间耦合的增加和减少会相应增加和减少人类大脑中的振荡活动
  16. C++ - 文件读写(fstream)
  17. android移动支付——银联支付
  18. php 上上级目录的表示
  19. jsp与servlet的关系
  20. ADB和Fastboot最新版的谷歌官方下载链接

热门文章

  1. 函数载入的html触发onload,JS IFRAME动态加载触发onload事件解析
  2. 上海迪士尼持续火爆,原来是大数据背后撑腰
  3. Electron-Vue3-Vadmin后台系统|vite2+electron桌面端权限管理系统
  4. 怎么启动计算机前面u盘插槽,如何设置顶星主板u盘启动【设置法法】
  5. Google 的工程师文化
  6. 如何才能成为一名医生?
  7. 一些数学几何知识和小技巧
  8. 两大技巧教你轻松应对IB数学
  9. 制作GIF神器(小于2MB,操作简单)
  10. Failed to find configured root that contains 安装apk报错