ijkplayer 源码分析(上)

article/2025/8/24 18:42:51

本文基于0.8.8版本的 ijkplayer ,对其源码进行剖析,涉及到不同平台下的封装接口或处理方式时,均以 Android 为例。

ijkplayer 是一款比较出众的开源 Android/IOS 跨平台播放器,基于 ffplay,API 易于集成,可定制编译控制体积。

ijkplayer 集成了三种播放器实现:

  • AndroidMediaPlayer:即安卓系统自带的播放器 MediaPlayer,基于 MediaCodec、AudioTrack 等安卓系统 API.
  • IjkExoMediaPlayer:即谷歌新推出的 ExoPlayer,同样是基于 MediaCodec、AudioTrack 等安卓系统 API,但相比 MediaPlayer 具有支持 DASH、高级 HLS、自定义扩展等优点。
  • IjkMediaPlayer:基于 FFmpeg 的 ffplay,集成了 MediaCodec 硬解码器、Opengl 渲染方式等。

一般而言, ijkplayer 就是指 IjkMediaPlayer,本文分析的对象就是 IjkMediaPlayer.

对 Android 而言,音频输出支持 AudioTrack、OpenSL ES 方式;视频输出支持 NativeWindow、OpenGL ES 方式。

对 ijkplayer 不熟悉的同学建议参看源码阅读本文。

一、初始化

ijkplayer 在 Java 层的操作封装于 IjkMediaPlayer.java ,其中包含一些与底层通信的关键方法,比如播放控制相关的 start、pause、stop 等, 这些 native 方法对应的底层实现位于 ijkpalyer_ini.c 文件中。

在 IjkMediaPlayer.java 中有一些 @CalledByNative 注解的方法,比如底层的播放状态回调,这些方法由底层主动调用,具体位于 IjkMediaPlayer.c 文件中。

ijkplayer 的初始化位于 initPlayer 方法,共做了四件事:

  1. 加载 so 库
  2. 静态初始化底层,底层其实什么都没做
  3. 初始化 Message Handler,处理底层状态信息的上报
  4. 初始化底层,这部分做的工作最多,主要逻辑位于 ijkpalyer_android 的 ijkmp_android_create 方法,如下:
1
2
3
4
5
6
7
8
// 创建底层播放器对象,设置消息处理函数
IjkMediaPlayer *mp = ijkmp_create(msg_loop, saveMode, hard_mux);// 创建图像渲染对象
mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();// 初始化视频解码器(软/硬)、音频输出设备(opensles/audioTrack)
mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);

具体是怎么选择软解码器还是硬解码器的呢,跟踪 ffpipeline_create_from_android 方法可以看到:

1
2
3
4
5
6
7
if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2)// 创建硬解码器node = ffpipenode_create_video_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout);
if (!node) {// 创建软解码器node = ffpipenode_create_video_decoder_from_ffplay(ffp);
}

ffp 的 mediacodec_all_videos 等属性是通过 IjkMediaPlayer 的成员方法 setOption() 设置的,成员方法的调用肯定是在类初始化之后的,而解码器等依赖配置项的设置却在初始化中,难道不是应该先设置,后初始化吗?代码肯定是没有问题的,初始化中其实并没有调用这些方法,只是以函数指针的方式记录下来而已。ijkplayer 几乎完全由 c 实现,函数指针随处可见,方便编程。

二、配置

初始化后 IjkMediaPlayer 后,可以对其进行一系列配置,例如:

1
2
3
4
5
// 设置硬解码
mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);// 设置 opensles 方式输出音频
mIjkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);

setOption() 会调用到底层 ff_ffplay.c 的 ffp_set_option() 方法:

1
2
3
4
5
6
7
void ffp_set_option(FFPlayer *ffp, int opt_category, const char *name, const char *value) {if (!ffp)return;AVDictionary **dict = ffp_get_opt_dict(ffp, opt_category);av_dict_set(dict, name, value, 0);
}

AVDictionary 其实就是一个键值对数组,相当于 Java 中的 HashMap,av_dict_set 就相当于 put 方法,用来存储播放器的配置信息,FFPlayer 中就有如下 AVDictionary :

1
2
3
4
5
6
AVDictionary *format_opts;      // 格式配置,解封装文件时(avformat_open_input)需传入
AVDictionary *codec_opts;       // 编解码器配置,打开编解码器时(avcodec_open2)需传入
AVDictionary *sws_dict;         // avfilter 视频相关配置
AVDictionary *player_opts;      // 播放相关配置
AVDictionary *swr_opts;         // 音频相关配置
AVDictionary *swr_preset_opts;

通过 setOption() 方法将一系列的播放器配置暂存到这些 AVDictionary 中,对于没有 map 容器的 c 语言,可以充分利用它来配置和定义播放器的参数。FFmpeg 中很多 API 就是依赖 AVDictionary 来传递参数的,比如上面提到的 avformat_open_input、avcodec_open2 接口。
FFPlayer 结构体中本身就有配置相关的成员变量的,比如控制音频输出设备的 “int opensles” 等,当我们调用一系列 setOption() 方法将播放配置存储在 player_opts 中后,该如何取出再赋值给成员变量呢?
这个情景是否似曾相似,JavaEE 开发中如何从传递的一系列键值中取出赋值给成员变量? JavaEE 的 Struts2 可以实现自动映射转换,可以将互联网上收到的 “名称”和“值”的组合自动赋值给相应名称的变量。FFmpeg 中也实现了这样一套自动映射的机制,可以实现直接将 AVDictionary 赋值给结构体,只需调用:

1
int av_opt_set_dict(void *obj, struct AVDictionary **options);

当然这个结构体需要提前做一些处理,制定映射的规则,要在结构体中添加 AVClass 成员变量,且必须为第一个成员变量,然后对其初始化,比如 FFPlayer 结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 需要映射的成员变量
static const AVOption ffp_context_options[] = {{ "mediacodec-avc",  "MediaCodec: enable H264", OPTION_OFFSET(mediacodec_avc),   OPTION_INT(0, 0, 1) },{ "opensles",        "OpenSL ES: enable",       OPTION_OFFSET(opensles),         OPTION_INT(0, 0, 1) },// 省略...
}const  AVClass  ffp_context_class = {.class_name       = "FFPlayer",                  .item_name        = ffp_context_to_name,           .option           = ffp_context_options,     // 将需要映射的成员变量记录到 AVClass 中.version          = LIBAVUTIL_VERSION_INT,.child_next       = ffp_context_child_next,.child_class_next = ffp_context_child_class_next,
};ffplayer->av_class = &ffp_context_class;    // 使结构体可自动映射

这样处理后,就可以直接对 FFplayer 这个结构体进行整体赋值了,在 ff_ffplay.c 的 ffp_prepare_async_l 方法中就有这个逻辑:

1
av_opt_set_dict(ffp, &ffp->player_opts);

扩展阅读:

FFmpeg源代码简单分析:结构体成员管理系统-AVClass

FFmpeg源代码简单分析:结构体成员管理系统-AVOption

三、播放

先来回顾下播放器的整体流程:

播放器原理.png


在 视频相关的理论知识与基础概念 中简单总结过视频播放原理,播放器必然是通过多线程同时进行解封装、解码、视频渲染等工作的,对于 Ijkplayer 来说,开辟的线程如下:


所有的播放逻辑都位于 ff_ffplay.c 中,当对播放器设置视频源路径、解码方式、输出模式等播放选项后,就可以开始播放了, 播放入口方法为 ffp_prepare_async_l,此方法中调用了比较重要的两个方法:

1
2
3
4
5
// 打开音频输出设备
ffp->aout = ffpipeline_open_audio_output(ffp->pipeline, ffp);
...
// 创建音/视频数据解码前/后队列, 创建解封装和视频渲染线程
VideoState *is = stream_open(ffp, file_name, NULL);

在初始化时 IjkPlayer 将选择视频解码器、选择音频输出设备的方法以函数指针的形式记录下来,方便后面直接调用。ffpipeline_open_audio_output 方法即选择了音频输出方式为 opensles 或 AndroidTrack ,具体实现为 ffpipeline_android.c 的 func_open_audio_output 方法。
stream_open 方法则相当重要了,梳理一下该方法中涉及到的关键方法:

stream_open (1).png

分享到


http://chatgpt.dhexx.cn/article/l5RjwOkl.shtml

相关文章

ijkplayer播放器剖析(五)视频同步与渲染机制分析

ijkplayer播放器剖析系列文章: kplayer播放器剖析(一)从应用层分析至Jni层的流程分析 ijkplayer播放器剖析(二)消息机制分析 ijkplayer播放器剖析(三)音频解码与音频输出机制分析 ijkplay…

ijkplayer播放器详解使用教程

1.认识ijkplayer 最近公司准备开发一款视频播放及直播的应用,找了许多开源的框架,大部分都是基于ffmpeg开发的。最开始准备用Vitamio框架开发的,相关的文章也比较丰富,结果对于非个人移动应用均需购买Vitamio使用授权。不过B站开…

ijkplayer播放器架构从原型到升级

ijkplayer是一款跨平台的播放器,支持Android与iOS端,核心部分基于ffmpeg,支持Android的mediacodec硬解与iOS的videotoolbox硬解,视频图像采用OpenGL进行渲染。许多主流播放器都使用ijkplayer作为播放方案。接下来我们从核心播放流…

游戏手柄改typec接口

前言 几年前买的杂牌游戏手柄坏了好久,连接十分不稳定,稍微动一下线就会断连,我推断是线的质量不好,理论上将把线换掉应该就好了。一不做二不休,我想直接改成typec接口,手柄与数据线分离,这样就…

nl80211_iftype接口类型详解

NL80211_IFTYPE_UNSPECIFIED 上层协议未指定硬件接口类型,由驱动指定 NL80211_IFTYPE_ADHOC independent BSS member,各个无线主机之间对等交换数据 NL80211_IFTYPE_STATION managed BSS member NL80211_IFTYPE_AP 接入点,通常一端通过有…

android接口和type c对比,USB Type-C究竟比3.5mm音频接口好在哪里?

随着苹果宣布将在iPhone7/7 Plus上取消传统的3.5mm音频接口,不少动作快的手机厂商已经即使跟进了,目前乐视所有新机和摩托罗拉Moto Z均使用USB Type-C接口代替3.5mm音频接口,这样做究竟有什么好处呢? 基础知识 首先我们需要知道,3.5mm接口所传输的是立体声模拟音频,这也就…

TYPE-C接口的定义诠释以及功能参数挖掘

现在的安卓手机大多都采用了Type-C接口,如华为、荣耀、小米、三星、魅族等。对于这个接口,大家不再陌生,但大多数人对它的认识还停留在 “支持正反插”、“用来充电” 的基础层面。今天就来深入挖掘一下TYPE-C发展七年至今,为何能…

Type-C接口原理图,附引脚说明

维基链接:https://zh.wikipedia.org/wiki/USB_Type-C 针名描述针名描述A1GND接地B12GND接地A2SSTXp1SuperSpeed差分信号#1,TX,正B11SSRXp1SuperSpeed差分信号#1,RX,正A3SSTXn1SuperSpeed差分信号#1,TX&…

typec接口知识

typec接口定义(24脚) typec母头 typec公头(一对D,D-,只有一个CC脚,另一个CC脚变成了VCONN) D/D-:当USB3接口不可用的时候,这些引脚为USB2信号提供信号通道。 Vbus/GND:这些引脚能够为上行数据接口提供100W的供电能力,…

深入学习USB(6)USB Type-C接口定义概念解析

文章目录 **一、Usb type c接口定义介绍****二、USB3.1 type-c接口特性****三、USB Type-C接口的优点****四、usb type-c接引脚定义****五、usb type-c接口主要功能****六、usb type-c接口工作流程**七、市场上Type-C/PD有哪几种分类呢? 一、Usb type c接口定义介绍…

lightning接口_苹果Lightning接口与Type-C接口,谁更胜一筹?

曾几何时,在苹果的强势助攻下,Lightning接口在数码界曾一度混得风生水起!然而,近年来随着智能手机的发展,手机接口快速升级,其中Type-C接口凭借更快的数据传输、充电速度等优势,越来越多的出现在大家的视线中,隐隐盖过Lightning接口的风头,就连特立独行的苹果也对其青…

Type-C接口技术(一)

前言 电子产品接口的迅速发展是电子技术迅速发展的一个缩影,随着现在电子产品对数据传输和充电功率要求越来越高的情况下,一些以前常用接口开始慢慢被淘汰,而支持更高速率和充电功率的的Type-C接口慢慢开始大放异彩,此外支持盲插…

浅谈一下Type-C接口发展历程

1996年,由英特尔、微软、ibm等多家公司联合设计的usb标准问世,键盘、鼠标、智能手机以及打印机等等大多使用usb标准来实现供电和数据传输。 usb接口从诞生之初就是为了实现通用这个目的。在usb诞生之前,键盘、鼠标多使用ps二接口,…

TYPE-C接口电路设计篇(一)

常见USB接口类型 随着USB Type-C接口的推行,USB Type-C是一种通用串行总线(USB)的硬件接口形式,USB Type-C接口在消费类产品普及程度越来越高,常见的适配器、笔电、手机等产品都兼容USB Type-C设计,接口多…

Type-C接口

TYPE-C接口 USB Type-C接口的命名原语USB Type-C连接器,这是一种坚固的易于使用的接口。支持正反插,方便快捷。支持超高速率通信功能和功率输出。最大传输速率10Gbits/S,最大输出功率100W,最大电流5A。大小8.3mm X 2.5mm。 具有…

一文带你搞清楚USB、type-C、雷电三接口之间的区别与联系

缘起 最近某些新电脑的发布,看到带了全功能的type-c接口,一直搞不懂什么type-c还有什么全功能,半功能?和雷电3又有什么区别,雷电3又有什么全速,半速。由于搞不清,所以网上看了很多资料,这里总结一下以备后续查阅 说说USB 你肯定会说,这个我知道,就是下面这种的 没…

USB Type C 接口引脚详解

1. Type C 接口特点 Type C 是一组对称的连接器,在使用的过程中不需要如同使用 USBA,MinUSB,MicroUSB 那样来辨别接口方向。其次能够承受较高的功率所以可以支持高达 100W 的功率,所以使用该接口可以更好的支持快速充电&#xff0…

Macbook技巧,Type-c接口失灵怎么办

有时Macbook Pro电脑突然出现Type-c接口失灵的情况,这该怎么解决呢?可以参考小编的操作方法 1.关机 2.按下键盘右侧的 Shift键,左侧的Control和Option键不放。此时电脑会开机,等进入显示白条的状态,不要松开手&#x…

Java Type接口 运行时获取泛型类型

一、Type接口 Type是所有类型的父接口,他有4个子接口和一个实现类。 Class比较常见,它表示的是原始类型。Class类的对象表示JVM中的一个类或接口。每个Java类在JVM里都表现为一个Class对象,可以通过“类名.class”、“对象.getClass()”、…

关于type_C接口

文章目录 概要:一、引脚定义二、六脚Type_c三、12脚Type_c四、16脚Type_c五、usb3.0 概要: type-C接口外观好看,双面插等众多优点,已经成为了现在主流的接口。 一、引脚定义 VBus:总线电源,USB PD协议可配置电压&…