ijkplayer剖析
ijkplayer 是一款比较出众的开源 Android/IOS 跨平台播放器,基于 ffplay,API 易于集成,可定制编译控制体积。
本文基于 0.8.8 版本的 ijkplayer ,对其源码进行剖析,涉及到不同平台下的封装接口或处理方式时,均以 Android 为例。
ijkplayer android 集成了三种播放器实现:
- AndroidMediaPlayer:即安卓系统自带的播放器 MediaPlayer,基于 MediaCodec、AudioTrack 等安卓系统 API.
- IjkExoMediaPlayer:即谷歌新推出的 ExoPlayer,同样是基于 MediaCodec、AudioTrack 等安卓系统 API,但相比 MediaPlayer 具有支持 DASH、高级 HLS、自定义扩展等优点。
- IjkMediaPlayer:基于 FFmpeg 的 ffplay,集成了 MediaCodec 硬解码器、Opengl 渲染方式等。
一般而言, ijkplayer 就是指 IjkMediaPlayer,本文分析的对象就是 IjkMediaPlayer.
目录结构
1 | ijkplayer(项目文件夹) |
功能实现的平台差异
iOS和Android平台的差异主要表现在
- 视频硬件解码
- 音频渲染
- 视频渲染
Platform | Hardware Codec | Video Render | Audio Output |
---|---|---|---|
Android | MediaCodec | OpenGL ES、MediaCodec | OpenSL ES、AudioTrack |
iOS | VideoToolBox | OpenGL ES | AudioQueue |
IjkMediaPlayer和Native交互
播放控制相关的 start、pause、stop
等,调用 对应的 native 方法
底层状态信息的上报(比如底层的播放状态回调)相关的 postEventFromNative
等,这些方法由底层主动调用(有@CalledByNative
注解)
初始化
1 | public final class extends AbstractMediaPlayer { |
initPlayer
方法,共做了四件事:
- 加载 so 库
- 静态初始化底层,底层其实什么都没做
- 初始化 Message Handler,处理底层状态信息的上报
- 初始化底层,这部分做的工作最多
初始化底层
c代码
ijkplayer/android/ijkplayer/ijkplayer-armv7a/src/main/jni/ijkmedia/ijkplayer/android/
主要逻辑位于
ijkpalyer_android.c
的 ijkmp_android_create
方法
1 | // 创建底层播放器对象,设置消息处理函数 |
软硬解码选择
跟踪 pipeline/ffpipeline_android.c
的 ffpipeline_create_from_android
方法
发现是 用函数指针记录 类 IjkMediaPlayer.setOption() 设置的属性
配置
初始化后 IjkMediaPlayer 后,可以对其进行一系列配置,例如:
1 | // 设置硬解码 |
setOption() 会调用到底层 ff_ffplay.c 的 ffp_set_option() 方法
播放
播放器必然是通过多线程同时进行解封装、解码、视频渲染等工作的,对于 Ijkplayer 来说,开辟的线程如下:
当对播放器设置视频源路径、解码方式、输出模式等播放选项后,就可以开始播放了, 播放入口方法为 ffp_prepare_async_l
,此方法中调用了比较重要的两个方法:
1 | // 打开音频输出设备 |
stream_open 方法则相当重要了,梳理一下该方法中涉及到的关键方法:
音视频同步
对于播放器来说,音视频同步是一个关键点,同时也是一个难点,同步效果的好坏,直接决定着播放器的质量。
通常音视频同步的解决方案就是选择一个参考时钟,播放时读取音视频帧上的时间戳,同时参考当前时钟参考时钟上的时间来安排播放。如下图所示:
如果音视频帧的播放时间大于当前参考时钟上的时间,则不急于播放该帧,直到参考时钟达到该帧的时间戳;如果音视频帧的时间戳小于当前参考时钟上的时间,则需要“尽快”播放该帧或丢弃,以便播放进度追上参考时钟。
参考时钟的选择
有多种方式:
- 选取视频时间戳作为参考时钟源
- 选取音频时间戳作为参考时钟源
- 选取外部时间作为参考时钟源
考虑人对视频、和音频的敏感度,在存在音频的情况下,优先选择音频作为主时钟源。
ijkplayer在默认情况下也是使用音频作为参考时钟源
事件处理
在播放过程中,某些行为的完成或者变化,如prepare完成,开始渲染等,需要以事件形式通知到外部,以便上层作出具体的业务处理。
播放器底层上报事件时,实际上就是将待发送的消息放入消息队列,另外有一个线程会不断从队列中取出消息,上报给外部
参考&扩展
原文:大专栏 ijkplayer剖析
原文地址:https://www.cnblogs.com/sanxiandoupi/p/11642317.html
- IoC+AOP的简单实现
- 使用了继承、多态还有工厂模式和反射,但是还是没有OO的感觉。[已经增加了实现的代码]
- OO——从不知到知道一点,从迷茫到豁然开朗 (迟来的我的2002到2007)
- 只在UnitTest和WebHost中的出现的关于LogicalCallContext的严重问题
- TEST LAB V8在线渗透实验室教程(三)
- CMQ请求域名
- 在Entity Framework中使用存储过程(一):实现存储过程的自动映射
- 在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?
- 表单控件的副产品——查询控件
- 表单控件续(1)——应用接口来简化和分散代码
- 通过自定义配置实现插件式设计
- 让IoC动态解析自定义配置(提供基于Unity的实现)
- 如何让ASP.NET默认的资源编程方式支持非.ResX资源存储
- 在VS中通过建立依赖关系使文件结构更清晰
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 哇,ElasticSearch多字段权重排序居然可以这么玩
- Python 自动化,Appium 凭什么使用 UiAutomator2?
- 我用几行 Python 自动化脚本完美解决掉了小姐姐的微信焦虑感
- 【设计模式】692- TypeScript 设计模式之发布-订阅模式
- 强网杯-upload
- 基于暗通道去雾算法
- 全套 | 人脸检测 & 人脸关键点检测 & 人脸卡通化
- 使用Jenkins Dashboard插件可视化部署
- 全面综述:图像特征提取与匹配技术
- opencv+python制作硬核七夕礼物
- opencv+python制作硬核七夕礼物
- 七夕节也要学起来,哈希哈希哈希!
- 目标检测器性能评估工具包
- istio 1.7发布
- AkShare-中国宏观-工业品出厂价格指数