ExoPlayer架构详解与源码分析(5)——MediaSource

2023-10-18 12:30

本文主要是介绍ExoPlayer架构详解与源码分析(5)——MediaSource,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource


文章目录

  • 系列文章目录
  • 前言
  • MediaSource
  • MediaSource的实现
    • BaseMediaSource
    • CompositeMediaSource
    • WrappingMediaSource
    • MaskingMediaSource
    • ProgressiveMediaSource
  • 总结


前言

上篇说完整体架构,这里开始分析其中的各个组件,先从MediaSource看起,继续拿运载火箭做对比,MediaSource在整个运载火箭中的角色就类似于燃料系统,确保火箭顺利升空,燃料系统是其中重要的一环,需要能在运行过程从持续稳定的提供燃料。ExoPlayer也一样,为了保证能够持续的渲染出媒体内容,就得保证MediaSource持续稳定提供需要的数据。

MediaSource

继续扩充下我们的版图
在这里插入图片描述
MediaSource定义了媒体信息以及提供媒体数据给播放器,主要有2个职责:

  • 为播放器提供定义其媒体时序结构的Timeline,并在媒体时序结构发生变化时提供新的Timeline。初始化是提供一个PlaceholdTimeline,当prepareSource 完成时一般就能获取到真实的Timeline,然后调用传递给prepareSource 的MediaSourceCallers 上的onSourceInfoRefreshed 来更新这些新的Timeline。
  • 为其Timeline中的Period提供 MediaPeriod 实例。 MediaPeriods是通过调用createPeriod获得的,并为播放器提供加载和读取媒体的方式。

应用代码不应该直接调用MediaSource 里的方法,而应该让ExoPlayer在合适的时间调用。
MediaSource实例可以重复使用,但只能同时用于一个 ExoPlayer 实例。
不同MediaSource 方法只能在应用程序线程或内部播放线程其中一个上调用。每个方法文档上都明确了可以调用的线程。

看下几个重要的方法定义

  • getInitialTimeline主线程调用,当真实Timeline未知时立即返回初始PlaceholderTimeline,或者为返回null 让播放器创建初始Timeline。
  • getMediaItem主线程调用,返回当前的MediaItem,可以看到MediaSource里也可能保存了MediaItem。
  • prepareSource内部播放线程调用,注册 MediaSourceCaller,主要用来为播放器提供一个回调,获取最新的Timeline。另外,在播放某些播放资源需要先获取真实的媒体源时,这里会提前解析媒体资源(如播放HLS时这个时候会去获取解析M3U8文件),prepareSource完成后会立即刷新Timeline。
  void prepareSource(MediaSourceCaller caller,@Nullable TransferListener mediaTransferListener,PlayerId playerId);interface MediaSourceCaller {void onSourceInfoRefreshed(MediaSource source, Timeline timeline);}
  • createPeriod在内部播放线程调用,返回一个由periodId区分的新的MediaPerods对象,只能在真实的源已经准备好后再调用,也就是上面的prepareSource确保源已经准备完成,参数id就是MediaPerods唯一区分,startPositionUs想要播放的开始位置,allocator是一个缓存分配器这个后面讲MediaPerods会提到,MediaPerods创建完成后也会perpare,完成后一般就可以获取媒体的基本数据,如时长、轨道等,这个时候会反过来通知MediaSource刷新Timeline。
  MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs);

MeidiaSource大致工作流程就是创建时初始化出一个Timeline,然后prepareSource准备数据源,之后createPeriod创建Period,然后讲工作交给Period,整个过程都在刷新Timeline。

MediaSource的实现

在这里插入图片描述

BaseMediaSource

MediaSource虚函数实现,主要用于多个MediaSourceEventListener的处理分发,触发多个MediaSourceCaller的onSourceInfoRefreshed,还保存上一次的Timeline。

CompositeMediaSource

由多个子MediaSource组成的复合MediaSource,将所有方法调用转发给各个子的MediaSource

WrappingMediaSource

继承自CompositeMediaSource,实现只包含了一个子MediaSource的MediaSource 。

MaskingMediaSource

一个MediaSource ,主要作用是当实际媒体结果未知时,用一个PlaceholderTimeline来表示Timeline ,当获取实际的媒体结构时采用实际的Timeline替换PlaceholderTimeline。

public MaskingMediaSource(MediaSource mediaSource, boolean useLazyPreparation) {super(mediaSource);this.useLazyPreparation = useLazyPreparation && mediaSource.isSingleWindow();window = new Timeline.Window();period = new Timeline.Period();@Nullable Timeline initialTimeline = mediaSource.getInitialTimeline();if (initialTimeline != null) {timeline =MaskingTimeline.createWithRealTimeline(initialTimeline, /* firstWindowUid= */ null, /* firstPeriodUid= */ null);hasRealTimeline = true;} else {timeline = MaskingTimeline.createWithPlaceholderTimeline(mediaSource.getMediaItem());}}

ProgressiveMediaSource

继承自BaseMediaSource,主要用于渐进式媒体文件的播放,如本地或远程的单个视频文件

  @Override//prepareprotected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {transferListener = mediaTransferListener;drmSessionManager.setPlayer(/* playbackLooper= */ checkNotNull(Looper.myLooper()), getPlayerId());drmSessionManager.prepare();notifySourceInfoRefreshed();}@Override//ProgressiveMediaPeriod在获取到Timeline相关信息后会回调更新Timelinepublic void onSourceInfoRefreshed(long durationUs, boolean isSeekable, boolean isLive) {// 优先实现之前的durationUs durationUs = durationUs == C.TIME_UNSET ? timelineDurationUs : durationUs;if (!timelineIsPlaceholder&& timelineDurationUs == durationUs&& timelineIsSeekable == isSeekable&& timelineIsLive == isLive) {// 没有发生变更return;}timelineDurationUs = durationUs;timelineIsSeekable = isSeekable;timelineIsLive = isLive;timelineIsPlaceholder = false;notifySourceInfoRefreshed();}//刷新TimeLineprivate void notifySourceInfoRefreshed() {Timeline timeline =new SinglePeriodTimeline(timelineDurationUs,timelineIsSeekable,/* isDynamic= */ false,/* useLiveConfiguration= */ timelineIsLive,/* manifest= */ null,mediaItem);if (timelineIsPlaceholder) {timeline =new ForwardingTimeline(timeline) {@Overridepublic Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {super.getWindow(windowIndex, window, defaultPositionProjectionUs);window.isPlaceholder = true;return window;}@Overridepublic Period getPeriod(int periodIndex, Period period, boolean setIds) {super.getPeriod(periodIndex, period, setIds);period.isPlaceholder = true;return period;}};}//触发监听refreshSourceInfo(timeline);}

总结

没了就这么多,燃料系统这么简陋的吗?当然不会,因为它把除了Timeline的管理维护之外的几乎所有的工作都交给别人来完成了,它就是下面要重点讲的MediaPeriod,MediaSource只管创建出就好了,ExoPlayer也是主要通过MediaSource关联的MediaPeriod控制媒体的加载释放等。


版权声明 ©
本文为CSDN作者山雨楼原创文章
转载请注明出处
原创不易,觉得有用的话,收藏转发点赞支持

这篇关于ExoPlayer架构详解与源码分析(5)——MediaSource的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/232577

相关文章

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Redis 基本数据类型和使用详解

《Redis基本数据类型和使用详解》String是Redis最基本的数据类型,一个键对应一个值,它的功能十分强大,可以存储字符串、整数、浮点数等多种数据格式,本文给大家介绍Redis基本数据类型和... 目录一、Redis 入门介绍二、Redis 的五大基本数据类型2.1 String 类型2.2 Hash