要使用封装优化ijk就必须先了解ffmpeg,然后看ijk对ffmpeg的C层封装!
这是我看ijk源码时候的笔记,比较散乱。不喜勿喷~

ijk源码简析:

1.ijkplayer_jni.c 封装的播放器JNI层 API,对应java层调用。

2.ijkplayer.c 封装的播放器API,对应给JNI层调用

3.ff_ffplay_options.c 参数设置options

4.ijkplayer_android.c 创建播放器,对应Android系统的音视频

5..ff_ffplay.c ffmpeg主要封装类 含整个解码流程

首先入口是 JNI_OnLoad()加载.so库,各种初始化。RegisterNatives注册g_methods中的native方法,注册后,相当于java层声明为native方法的函数与c层有一个映射关系;举个栗子,当我们java里面调用_start()方法的时候,其实调用的是c层中的IjkMediaPlayer_start()方法;对应地,作者在java层声明了一个修饰java方法的annotation,这些函数也会在这里注册,实现后面在C层也能调用java层的函数。

接着还调用了几个init方法:
ffp_global_init() –>主要是ffmpeg的初始化工作, 前面说过ijkplayer是基于ffmpeg的。
FFmpegApi_global_init(env)–>初始化FFmpegApi#av_base64_encode函数。

后面调用的IjkMediaPlayer.native_profileBegin(“libijkplayer.so”);发现并没有什么作用的样子,注释了还是能成功运行。未解?

IjkMediaPlayer初始化过程:调用了native方法native_setup(new WeakReference(this));对应C语言的IjkMediaPlayer_native_setup()方法,也在ijkplayer_jni.c文件里面。

然后就是ijkmp_android_create(message_loop);/ijkplayer_android.c : 很重要
创建一个IJKMediaPlayer类型的player。
接下来就是对ijkplayer结构体中的ffplayer进行设置,刚刚通过SDL_VoutAndroid_CreateForAndroidSurface()创建了输出设备vout,后面在ffpipeline_set_vout()里面:mp->ffplayer->opaque->weak_vout = vout。
然后再jni_set_media_player(),这里基本就结束了。
SDL_VoutAndroid_CreateForAndroidSurface()创建视频输出设备的时候,跟踪函数跳转发现,硬解用的mediacidec,软解用的ffmpeg。还有渲染视频设备用的ANativeWindow,对接surface等。

prepareAsync()流程:IjkMediaPlayer_prepareAsync/ijkplayer_jni.c———->ijkmp_prepare_async_l()—–>ffp_prepare_async_l()—–>stream_open—->
video_refresh_thread为入口的视频显示线程——->read_thread为入口的网络数据或者本地文件的数据读取线程.
其实在read_thread里面还向msg_queue发送了一个消息,那就是ffp_notify_msg1(ffp, FFP_MSG_PREPARED);,消息循环线程收到这个消息后,会调用java层的函数,向handler发送个MEDIA_PREPARED消息,然后在handler的handleMessage函数里面会处理这个消息:

    case MEDIA_PREPARED:player.notifyOnPrepared();return;
//最终会调用:
videoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(IMediaPlayer mp) {videoView.start();}});

ijkplayer_jni.c message_loop_n消息循环线程
然后 post_event发送到ijkj4a文件夹中IjkMediaPlayer.c
在ijkj4a文件夹中IjkMediaPlayer.c中实现postEventFromNative 然后调用ijkj4a文件夹Java层postEventFromNative。然后ijkplayer-java的IjkMediaPlayer.java具体实现了postEventFromNative。
C到Java ——>(CallStaticVoidMethod)

IJK音频解码源码:

audio_thread()/ff_ffplayer.c
一开始就进入循环,然后调用decoder_decode_frame()进行解码,解码后的帧存放到frame中,然后调用frame_queue_peek_writable()判断是否能把刚刚解码的frame写入is->sampq中,因为is->sampq是音频解码帧列表,然而播放线程直接从这里面读取数据,然后播放出来。最后 av_frame_move_ref(af->frame, frame);把frame放入到sampq相应位置。由于前面af = frame_queue_peek_writable(&is->sampq),af就是指向这一帧frame应该放的位置的指针,所以直接把值赋值给它的结构体里面的frame就行了。
然后frame_queue_push(&is->sampq);里面是一个唤醒线程的操作,如查音频播放线程因为sampq队列为空而阻塞,这里可以唤醒它。
在decoder_decode_frame()里面是调用传进去的codec的codec->decode()方法解码。
在frame_queue_peek_writable()里面会判断sampq队列是否满了,如果没位置放我们的frame的话,会调用pthread_cond_wait()方法阻塞队列。如果有位置放frame的话,就会返回frame应该放置的位置的地址。

IJK音频播放源码:

ijkmp_android_create()/ijkplayer_android.c
在ffpipeline_create_from_android()里面有一句
pipeline->func_open_audio_output = func_open_audio_output;
接着我们看看func_open_audio_output()/ffpipeline_android.c:
可以看出,音频播放也分为:opensles,audiotrack

看看audiotrack吧.
aout_open_audio()/ijksdl_aout_android_audiotrack.c。
接着会调用aout_open_audio_n()/ijksdl_aout_android_audiotrack.c
SDL_CreateThreadEx(&opaque->_audio_tid, aout_thread, aout, “ff_aout_android”);这里创建的线程就是播放线程。
接着我们看看入口函数aout_thread,在这个函数内部会调用aout_thread_n()/ijksdl_aout_android_audiotrack.c:
这个函数的开始有很多SDL_Android_AudioTrack_set_xxx(),主要是设置播放器相关的配置,比如播放速度,声音大小等。

IJK视频解码源码:

SDL_CreateThreadEx(&is->_video_tid, video_thread, ffp, “ff_video_dec”);频解码线程的创建
然后在创建之后,有一个死循环:

for (;;) {if (is->abort_request)break;//ignore audio partret = av_read_frame(ic, pkt);packet_queue_put(&is->videoq, pkt);   //将获取到的视频包推入videoq队列中}

这里循环把解析到的frame放入is->videoq中,那么我们在后面解码的时候,肯定是从这里面读取帧,然后再解码。
现在我们来看看解码线程:
ffp->node_vdec->func_run_sync(node);
是直接运行的ffplayer结构体里面的函数,这里肯定和音频播放一样,在之前初始化的时候给这个函数指针赋值过,那么我们回过头看看到底在哪里为它赋值的:
ijkmp_android_create()调用ffpipeline_create_from_android(),然后里面有一句:pipeline->func_open_video_decoder = func_open_video_decoder;
然后我们再返回看到在stream_component_open()/ff_ffplayer.c里面有一句:
decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
点进去ffpipeline_open_video_decoder(ffp->pipeline, ffp);:
IJKFF_Pipenode* ffpipeline_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
return pipeline->func_open_video_decoder(pipeline, ffp);
}
在stream_component_open()/ff_ffplayer.c里面其实运行了func_open_video_decoder(),然后把返回值赋值给ffp->node_vdec;继续跟着流程到func_open_video_decoder(),最终ffp->node_vdec =node。
前面调用的ffp->node_vdec->func_run_sync(node);现在看来就是调用的func_run_sync()函数
然后在视频解码流程里面又创建了一个线程
opaque->enqueue_thread = SDL_CreateThreadEx(&opaque->_enqueue_thread, enqueue_thread_func, node, “amediacodec_input_thread”);
点进去feed_input_buffer()/ffpipenode_android_mediacodec_vdec.c:
这个enqueue_thread_func也就只是把每一帧数据送到解码器去解码而已。

再看到之前解码线程,发现真正的解码是重新开一个线程解码,那么这个线程是干嘛的呢? 从硬解码器中获得解码后的数据。

IJK视频播放源码:

显示的线程非常简单,就是从is->pictq读取数据,然后直接推送给硬件设备,完成渲染。
入口函数video_refresh_thread()/ff_ffplayer.c开始
video_refresh_thread(void *arg)
|(调用)
video_refresh(ffp, &remaining_time);
|(调用)
video_display2(ffp);
|(调用)
video_image_display2(ffp);
SDL_VoutDisplayYUVOverlay()从pictq列表中获取视频frame数据,然后再写入nativewindows的视频缓冲中进行渲染。

ffmpeg实战教程(十三)iJKPlayer源码简析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. django源码简析——后台程序入口

    django源码简析--后台程序入口 这一年一直在用云笔记,平时记录一些tips或者问题很方便,所以也就不再用博客进行记录,还是想把最近学习到的一些东西和大家作以分享,也能够对自己做一个总结.工作中主 ...

  3. (Ajax)axios源码简析(三)——请求与取消请求

    传送门: axios源码简析(一)--axios入口文件 axios源码简析(二)--Axios类与拦截器 axios源码简析(三)--请求与取消请求 请求过程 在Axios.prototype.re ...

  4. java ArrayList 概述 与源码简析

    ArrayList 概述 与源码简析 1 ArrayList 创建 ArrayList<String> list = new ArrayList<>(); //构造一个初始容量 ...

  5. Spring Boot源码简析 @EnableTransactionManagement

    相关阅读 Spring Boot源码简析 事务管理 Spring Boot源码简析 @EnableAspectJAutoProxy Spring Boot源码简析 @EnableAsync Sprin ...

  6. 【Android项目】本地FM收音机开发及源码简析

    [Android项目]本地FM收音机开发及源码简析 目录 1.概述 2.收音机的基本原理 3.收音机其他信息 RDS功能 4.Android开发FM收音机源码解析 5.App层如何设计本地FM应用 6 ...

  7. Log-Pilot 源码简析

    Log-Pilot 源码简析 简单介绍 源码简析 Pilot结构体 Piloter接口 main函数 Pilot.Run Pilot.New Pilot.watch Pilot.processEven ...

  8. Lottie动画框架入门及源码简析

    现在越来越多的APP中添加动画来提升用户体验,下面简单介绍下Airbnb开源的动画框架Lottie的使用 一.基本使用 首先添加依赖 compile 'com.airbnb.android:lotti ...

  9. Spring Boot源码简析 @Qualifier

    源码 @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementT ...

最新文章

  1. 2021-2027年中国玩具行业市场研究及前瞻分析报告
  2. 关于java中普通代码块、构造代码块与静态代码块
  3. FS,FT,DFT,DFS和DTFT的关系
  4. 【学习求职必备】百度AI和它的7大AI黑科技
  5. linux下MySQL与jdk安装
  6. /dev/zero和/dev/null的区别
  7. Virtualbox 无缝整合linux和windows双系统
  8. dedecms更改php目录名称,dedecms修改专题目录名称(路径)
  9. centos网络隔一段时间就断_计算机网络总结
  10. 云端资源,“掌”握手中 ——关于 阿里云 App你不知道的五件事
  11. C语言 数组内存溢出 - C语言零基础入门教程
  12. vba put 语名 delphi 用什么语句_将VBA数组写入工作表时转置(Transpose)的利用
  13. linux下时间编程,Linux下的时间操作编程
  14. 将Go的main包拆分为多个文件
  15. 2022年下半年软考考哪个科目?看完就懂了
  16. Installing vipm-17.0.2018-linux
  17. 使用 Dva 开发复杂 SPA
  18. 聊天室页面问题和解决方案
  19. 百度红包架构分析与推测
  20. 3D重建中的相机雷达融合

热门文章

  1. 一份曝光文件透露解放军核武库:谁也不敢惹中国
  2. python Selenium浏览器爬虫指南 --未完成版本
  3. python二维数组按照第一列排序_Python中二维数组按照某行或列排序的实现方法
  4. init-history
  5. HTML+CSS+JavaScript做一个简约的浏览器主页
  6. [知识库分享系列] 二、.NET(ASP.NET)
  7. IPv5 发生了什么?为什么有 IPv4、IPv6 但没有 IPv5?
  8. 除了IPv4,还有IPv5、IPv6、IPv7和IPv9,IPv10
  9. 国家lpv9服务器包括哪些项目,ipv6与ipv9什么关系
  10. 真正的外企风范——毕博