ExoPlayer架构详解与源码分析(6)——MediaPeriod

2023-10-24 07:15

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

系列文章目录

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


文章目录

  • 系列文章目录
  • 前言
  • MediaPeriod
  • MediaPeriod的实现
  • ProgressiveMediaPeriod
  • 总结


前言

上篇看完了MediaSource,发现其中正在发挥作用的是其中的MediaPeriod,如果MediaSource是燃料系统的外壳,那么MediaPeriod就是其外壳下的核心,媒体数据的的加载获取甚至是解析主要就靠它了。

MediaPeriod

先看下整体结构
在这里插入图片描述

MediaPeriod主要用于加载于Timeline中的一个Period对于的媒体数据。换句话说Timeline中有多少个Period就会对于多少个MediaPeriod。
MediaPeriod的所有方法都在播放器内部线程调用。MediaPeriod是在MediaSource.createPeriod创建的,MediaSource中媒体数据的加载读取解复用最终提供数据给Renderer等都通过它来实现。MediaPeriod 中每个轨道对应一个SampleStream,MediaPeriod 在同一时间可能只能为一个SampleStream提供数据,但是当前的SampleStream 可以切换(因为MediaPeriod在读取数据时是单线程的,获取到数据后会同步解析数据,通过TrackId 将其关联到不同的轨道的SampleStream 中,这个后面具体会讲到)。

看下几个重要的方法定义

  • prepare异步准备当前的MediaPeriod,因为是异步的方法入参提供了一个Callback在prepared后通知调用者,在通知调用者前搜先会调用MediaSource的onSourceInfoRefreshed通知MediaSource更新Timeline,然后完成解轨道数据的解析提供给getTrackGroups方法,目的是提前准备好Timeline,如果prepare失败就会调用MediaPeriod的maybeThrowPrepareError方法

  • maybeThrowPrepareError功能如上,这个方法只会在prepare完成前调用

  • getTrackGroups获取解析出的轨道数据,因为解析轨道是prepare时完成的,所以这个方法只能在prepared之后调用。

  • selectTracks执行轨道选择,这是一个重要的方法,就是在这个方法中将轨道数据提供给上层使用者的确切的说是Renderer,同时Renderer也通过mayRetainStreamFlags 告诉MediaPeriod使用保留当前的SampleStream。MediaPeriod如果创建了新的流或者更新了SampleStream也会通过streamResetFlags=true来告诉Renderer需要重置当前的Renderer了。另外每次调用都应该更新TrackSelections。同样这个方法肯定也只能在prepared之后调用。

      long selectTracks(@NullableType ExoTrackSelection[] selections,//TrackSelector提供的轨道选择boolean[] mayRetainStreamFlags,//需要保留的流@NullableType SampleStream[] streams,//提供的数据流boolean[] streamResetFlags,//需要替换的流long positionUs);//当前的播放位置,如果当前的period还没有播放,这个值就是起始播放位置
    
  • seekToUs Seek到指定位置。仅当至少选择一个轨道时才会调用此方法。

  • continueLoading 当需要继续加载数据时调用,MediaPeriod在prepareing或者prepared后都可以调用这个方法。

  • reevaluateBuffer 释放SampleStream的相关缓存,同样这个方法肯定也只能在prepared之后调用。

  • getStreamKeys 当MediaPeriod由多个播放源时,如HLS,这个方法返回一个多个源关于选中轨道的KeyList

MediaPeriod接口定义了数据解析的大致过程:

  1. prepare准备数据源,完成后selectTracks向外提供出SampleStream
  2. continueLoading 继续加载数据填充SampleStream
  3. 将使用完的数据通过reevaluateBuffer释放

MediaPeriod的实现

在这里插入图片描述
可以看到基本每一种ExoPlayer支持的媒体都实现了一个MediaPeriod,ProgressiveMediaPeriod相对比较基础和典型,后面也将重点解析它,剩余其他几种类型的可以先做了解,后面有时间再扩充。

ProgressiveMediaPeriod

ProgressiveMediaSource的MediaPeriod实现,主要用于渐进式媒体文件的加载,如本地或远程的单个视频文件,
先看下ProgressiveMediaPeriod整体结构
在这里插入图片描述
ProgressiveMediaPeriod主要分2部分:

  • 数据存取 管理缓存获取的数据,主要交由SampleQueue来负责,需要提供高效的缓存读写能力保证数据的持续性。
  • 数据加载解析 加载主要由DataSource负责,主要工作是获取媒体数据供解析器读取,解析器主要指的是Extractor,主要负责将获取的数据解析到不同的SampleQueue中。

上图中并没有MediaPeriod提到的SampleStream,其实ProgressiveMediaPeriod内部类中实现了SampleStream ,通过内部类将
对SampleStream 操作通过trackid关联转发给SampleQueue来实现,所以上图中的SampleQueue其实就是相当于SampleStream。

private final class SampleStreamImpl implements SampleStream {private final int track;public SampleStreamImpl(int track) {this.track = track;}@Overridepublic boolean isReady() {return ProgressiveMediaPeriod.this.isReady(track);}@Overridepublic void maybeThrowError() throws IOException {ProgressiveMediaPeriod.this.maybeThrowError(track);}@Overridepublic int readData(FormatHolder formatHolder, DecoderInputBuffer buffer, @ReadFlags int readFlags) {return ProgressiveMediaPeriod.this.readData(track, formatHolder, buffer, readFlags);}@Overridepublic int skipData(long positionUs) {return ProgressiveMediaPeriod.this.skipData(track, positionUs);}}/* package */ int readData(int sampleQueueIndex,FormatHolder formatHolder,DecoderInputBuffer buffer,@ReadFlags int readFlags) {if (suppressRead()) {return C.RESULT_NOTHING_READ;}maybeNotifyDownstreamFormat(sampleQueueIndex);int result =//sampleQueueIndex查询对应的sampleQueues读取数据sampleQueues[sampleQueueIndex].read(formatHolder, buffer, readFlags, loadingFinished);if (result == C.RESULT_NOTHING_READ) {maybeStartDeferredRetry(sampleQueueIndex);}return result;}

Extractor和DataSource那一块还没扩展开,目前就已经很复杂了,DrmSessionManager和LoadErrorHandlingPolicy不是主线任务就跳过了,重点关注Loder、SampleQueue、DataSource、Extractor。

总结

MediaPeriod接口只是定义了标准的执行逻辑,具体如何实现其中定义的每个方法其实每种MediaPeriod实现都会有很大的差异,因为媒体结构本身就千变万化。ProgressiveMediaPeriod实现了其中一种相对基础的媒体类型,但是结构也很复杂,预计下面分多篇将ProgressiveMediaPeriod的SampleQueue、Loder、DataSource、Extractor分析一遍,下篇先从SampleQueue说起。


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

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



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

相关文章

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

Redis 的 SUBSCRIBE命令详解

《Redis的SUBSCRIBE命令详解》Redis的SUBSCRIBE命令用于订阅一个或多个频道,以便接收发送到这些频道的消息,本文给大家介绍Redis的SUBSCRIBE命令,感兴趣的朋友跟随... 目录基本语法工作原理示例消息格式相关命令python 示例Redis 的 SUBSCRIBE 命令用于订

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MySQL8 密码强度评估与配置详解

《MySQL8密码强度评估与配置详解》MySQL8默认启用密码强度插件,实施MEDIUM策略(长度8、含数字/字母/特殊字符),支持动态调整与配置文件设置,推荐使用STRONG策略并定期更新密码以提... 目录一、mysql 8 密码强度评估机制1.核心插件:validate_password2.密码策略级

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

详解python pycharm与cmd中制表符不一样

《详解pythonpycharm与cmd中制表符不一样》本文主要介绍了pythonpycharm与cmd中制表符不一样,这个问题通常是因为PyCharm和命令行(CMD)使用的制表符(tab)的宽... 这个问题通常是因为PyCharm和命令行(CMD)使用的制表符(tab)的宽度不同导致的。在PyChar