ExoPlayer架构详解与源码分析(11)——DataSource

系列文章目录

ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource
ExoPlayer架构详解与源码分析(6)——MediaPeriod
ExoPlayer架构详解与源码分析(7)——SampleQueue
ExoPlayer架构详解与源码分析(8)——Loader
ExoPlayer架构详解与源码分析(9)——TsExtractor
ExoPlayer架构详解与源码分析(10)——H264Reader
ExoPlayer架构详解与源码分析(11)——DataSource


文章目录

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


前言

好久不见各位,间隔了一段时间忙项目,终于有时间补上ProgressiveMediaPeriod最后一块拼图——DataSource。间隔太久先来个前情回顾。
本系列先介绍了ExoPlayer的整体架构还有些基本概念,然后围绕四大组件展开讲解,首先从MediaSource这个组件讲起,MediaSource主要由ProgressiveMediaPeriod来完整工作,先看下ProgressiveMediaPeriod整体结构:
在这里插入图片描述
之前的文章已经讲解完上图里用于解析数据的左半部分,而这些用于解析的数据就是从右半部分的DataSource里获取的。还是拿火箭来类比,MediaSource是火箭的燃料系统,那么左半边可以理解为燃油泵控制燃料的多少,右半部分就是油箱,为整个发动机提供源源不断的燃料。

DataSource

DataSource字面就是数据源的意思,用来读取URI定义的资源数据,扩展了DataReader提供了read方法供外部读取数据
看下主要方法:

  • open 打开一个数据源,读取指定的数据,传入一个DataSpec用于告诉DataSource打开源的必要信息,如URI,数据的起始位置长度等,如果DataSpec打开数据段在数据范围内,那么通过read方法可以正常读取数据,如果DataSpec的position正好等于数据的长度,则会立即返回C.RESULT_END_OF_INPUT,如果超过数据的长度,则直接抛出IOException异常。
  • read 从输入中读取最多length字节的数据,从buffer的offset位置开始填充length长度。如果readLength为0,则返回 0。如果由于已到达打开范围的末尾而没有可用数据,则返回C.RESULT_END_OF_INPUT 。否则,调用将阻塞,直到读取至少一个字节的数据并返回读取的字节数。
  • close 关闭源。即使open调用抛出IOException 时,也必须调用此方法关闭源。
  • getUri 数据源未open时返回null,当源打开时,返回从中读取数据的Uri 。返回的Uri将与open中的DataSpec的URI相同。如果发生了重定向,则返回重定向后的Uri 。
  • getResponseHeaders 当源打开时,返回与上次open调用关联的响应标头。否则,返回一个空Map。返回Map中的键不区分大小写。

DataSource的实现

DataSource的定义很简单,主要就是和数据源的相关操作,打开、读取、关闭。
它的具体实现有很多这里列出常用的一部分
在这里插入图片描述
先过下简单几个DataSource:

  • BaseDataSource 主要实现了多个TransferListener的监听分发管理。

  • PlaceholderDataSource 顾名思义一个用于占位的DataSource,不可调用其中的open,read方法,会抛出异常。

  • StatsDataSource 一个DataSource的包装,会将所有的方法调用转发给子DataSource。

  • AesCipherDataSource 也是一个DataSource的包装,会将所以的方法调用转发给子DataSource,与StatsDataSource不同的是,可以AES解密加密子的DataSource,会将子DataSource返回的数据经过AES解密返回给上层。

  • PriorityDataSource 有优先级管理的DataSource,可以使用PriorityTaskManager管理包含的子DataSource ,只有优先级足够高才可以继续调用open和read否则抛出PriorityTooLowException异常通知上层更改执行顺序。

  • DataSchemeDataSource data作为Scheme的URI的DataSource,如将数据直接转成base64存储在字符串中的方式,这类读取也很简单,直接Base64转成字节数据就行了。

  • AssetDataSource 顾名思义,用来读取Android Asset文件,通过AssetManager获取到文件流。

  • ContentDataSource 通过ContentResolver获取到文件描述,然后打开文件流。

  • RawResourceDataSource 同Resources.openRawResourceFd获取文件描述打开文件流。

  • FileDataSource 打开文件路径的DataSource,这里看下实现。

    
      @Override
      public long open(DataSpec dataSpec) throws FileDataSourceException {
        Uri uri = dataSpec.uri;
        this.uri = uri;
        transferInitializing(dataSpec);//触发监听
        this.file = openLocalFile(uri);//创建随机访问的文件流,RandomAccessFile
        try {
          file.seek(dataSpec.position);//seek到DataSpec 指定的开始位置
          bytesRemaining =//获取未读取长度
              dataSpec.length == C.LENGTH_UNSET ? file.length() - dataSpec.position : dataSpec.length;
        } catch (IOException e) {
          throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
        }
        if (bytesRemaining < 0) {//OUT_OF_RANGE
          throw new FileDataSourceException(
              /* message= */ null,
              /* cause= */ null,
              PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE);
        }
    
        opened = true;
        transferStarted(dataSpec);//触发监听
    
        return bytesRemaining;
      }
      
      @Override
      public int read(byte[] buffer, int offset, int length) throws FileDataSourceException {
        if (length == 0) {
          return 0;
        } else if (bytesRemaining == 0) {
          return C.RESULT_END_OF_INPUT;
        } else {
          int bytesRead;
          try {//在buffer 的offset处开始写入length长度的file数据
            bytesRead = castNonNull(file).read(buffer, offset, (int) min(bytesRemaining, length));
          } catch (IOException e) {
            throw new FileDataSourceException(e, PlaybackException.ERROR_CODE_IO_UNSPECIFIED);
          }
    
          if (bytesRead > 0) {
            bytesRemaining -= bytesRead;
            bytesTransferred(bytesRead);//监听
          }
    
          return bytesRead;
        }
      }
    
    
  • HttpDataSource 网络数据的基类,主要定义了网络请求相关的请求设置,EXO实现了OkHttpDataSourceDefaultHttpDataSourceCronetDataSource,对应3种网络请求库的DataSource实现,重点看下OkHttpDataSource的实现

      @Override
      public long open(DataSpec dataSpec) throws HttpDataSourceException {
        this.dataSpec = dataSpec;
        bytesRead = 0;
        bytesToRead = 0;
        transferInitializing(dataSpec);
    
        Request request = makeRequest(dataSpec);
        Response response;
        ResponseBody responseBody;
        Call call = callFactory.newCall(request);
        try {
          this.response = executeCall(call);
          response = this.response;
          responseBody = Assertions.checkNotNull(response.body());
          responseByteStream = responseBody.byteStream();//获取到数据流
        } catch (IOException e) {
          throw HttpDataSourceException.createForIOException(
              e, dataSpec, HttpDataSourceException.TYPE_OPEN);
        }
    
        int responseCode = response.code();
    
        ...
    
        long bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0;
    
       ...
        opened = true;
        transferStarted(dataSpec);
    
        try {
          skipFully(bytesToSkip, dataSpec);//从dataSpec.position位置开始
        } catch (HttpDataSourceException e) {
          closeConnectionQuietly();
          throw e;
        }
    
        return bytesToRead;
      }
      
      private int readInternal(byte[] buffer, int offset, int readLength) throws IOException {
        if (readLength == 0) {
          return 0;
        }
        if (bytesToRead != C.LENGTH_UNSET) {
          long bytesRemaining = bytesToRead - bytesRead;
          if (bytesRemaining == 0) {
            return C.RESULT_END_OF_INPUT;
          }
          readLength = (int) min(readLength, bytesRemaining);
        }
        //从上面获取的responseByteStream流中读取指定长度的数据到buffer
        int read = castNonNull(responseByteStream).read(buffer, offset, readLength);
        if (read == -1) {
          return C.RESULT_END_OF_INPUT;
        }
    
        bytesRead += read;
        bytesTransferred(read);
        return read;
      }
    
  • DefaultDataSource 播放器默认创建的DataSource,当播放的数据源不确定时,DefaultDataSource可以判断URI 的scheme动态的创建上面提到的DataSource,看下Open的源码就明了了

    public long open(DataSpec dataSpec) throws IOException {
        Assertions.checkState(dataSource == null);
        // Choose the correct source for the scheme.
        String scheme = dataSpec.uri.getScheme();
        if (Util.isLocalFileUri(dataSpec.uri)) {
          String uriPath = dataSpec.uri.getPath();
          if (uriPath != null && uriPath.startsWith("/android_asset/")) {
            dataSource = getAssetDataSource();
          } else {
            dataSource = getFileDataSource();
          }
        } else if (SCHEME_ASSET.equals(scheme)) {
          dataSource = getAssetDataSource();
        } else if (SCHEME_CONTENT.equals(scheme)) {
          dataSource = getContentDataSource();
        } else if (SCHEME_RTMP.equals(scheme)) {
          dataSource = getRtmpDataSource();
        } else if (SCHEME_UDP.equals(scheme)) {
          dataSource = getUdpDataSource();
        } else if (SCHEME_DATA.equals(scheme)) {
          dataSource = getDataSchemeDataSource();
        } else if (SCHEME_RAW.equals(scheme) || SCHEME_ANDROID_RESOURCE.equals(scheme)) {
          dataSource = getRawResourceDataSource();
        } else {
          dataSource = baseDataSource;
        }
        // Open the source and return.
        return dataSource.open(dataSpec);
      }
    

总结

到这里基本的DataSource介绍完了,对DataSource的基本功能有了一定的了解,对于简单的DataSource实现起来也很容易,但是面对复杂的数据环境这些还是远远不够的,如何保证复杂数据环境下的数据稳定输出,就是下面会重点要讲的CacheDataSourceTeeDataSource


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/480186.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jetson AGX ORIN 配置 FGVC-PIM 神经网络

Jetson AGX ORIN 配置 FGVC-PIM 神经网络 文章目录 Jetson AGX ORIN 配置 FGVC-PIM 神经网络配置 ORIN 环境创建 FGVC-PIM 虚拟环境安装 PyTorch安装 torchvision安装其他依赖包 配置 ORIN 环境 首先先配置 ORIN 的环境&#xff0c;可以参考这个链接&#xff1a; Jetson AGX …

计算机组成原理 数据通路组成实验

一、实验目的 (1)将双端口通用寄存器堆和双端口存储器模块联机; (2)进一步熟悉计算机的数据通路; (3)掌握数字逻辑电路中故障的一般规律&#xff0c;以及排除故障的一般原则和方法; (4)锻炼分析问题与解决问题的能力&#xff0c;在出现故障的情况下&#xff0c;独立分析故障…

Lamdba表达式

Lamdba表达式 Lambda是一个匿名函数&#xff0c;我们可以将Lambda表达式理解为一段可以传递的代码&#xff08;将代码像数据一样 传递&#xff09;。使用它可以写出简洁、灵活的代码。作为一种更紧凑的代码风格&#xff0c;使java语言表达能力得到提 升。 Lambda表达式在java语…

Spark-Scala语言实战(5)

在之前的文章中&#xff0c;我们学习了如何在scala中定义与使用集合和元组。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark-Scala语言实战&#xff08;…

Vscode与Cmake搭配配置opencv使用

vscode与Cmake基本使用 下载插件 CtrlShiftp打开VSCode的指令面板&#xff0c;然后输入cmake:q&#xff0c;VSCode会根据输入自动提示&#xff0c;然后选择CMake: Quick Start选择编译器根据提示输入项目名称选择可执行文件编译项目 方式一&#xff1a;执行命令cd build cmake…

数据仓库的数据处理架构Lambda和Kappa

1.数据仓库 数据仓库(Data Warehouse),简写DW。顾名思义,数据仓库是一个很大的数据存储集合,为企业分析性报告和决策支持而创建,是对多元业务数据的筛选与整合,具备一定的BI能力,主要用于企业的数据分析、数据挖掘、数据报表等方向,指导业务流程改进、监视时间、成本、…

vim编辑器和gcc/g++编辑器的使用讲解

vim编辑器 1 vim的基本概念 vim是Linux的编写代码的工具&#xff0c;是一种多模式的编辑器。 Linux中vim的常用的模式大概可以分为三种&#xff0c;分别是&#xff1a; 命令模式&#xff08;command mode&#xff09;、插入模式&#xff08;Insert mode&#xff09;和底行模式…

uniapp(vue3) H5页面连接打印机并打印

一、找到对应厂商打印机的驱动并在windows上面安装。查看是否安装完成可以在&#xff1a;控制面板->查看设备和打印机&#xff0c;找到对应打印机驱动是否安装完成 二、打印机USB连接电脑 三、运行代码调用浏览器打印&#xff0c;主要使用的是window.print()功能。下面使用…

定制 Elasticsearch 镜像

安装ik分词器 下载ik分词器 下载地址&#xff1a;https://github.com/infinilabs/analysis-ik/releases Dockerfile FROM docker.elastic.co/elasticsearch/elasticsearch:8.12.2 COPY ./elasticsearch-analysis-ik-8.12.2.zip /opt/ RUN bin/elasticsearch-plugin instal…

长三角科技盛会“2024南京国际人工智能,机器人,自动驾驶展览会”

2024南京国际人工智能,机器人,自动驾驶展览会 2024 Nanjing International Ai, Robotics, Autonomous Driving Expo 时间:2024年11月22-24日 地点:南京国际博览中心 南京&#xff0c;这座历史悠久的文化名城&#xff0c;如今正站在新一轮科技产业变革的前沿&#xff0c;以人工…

【Git】 - 版本控制

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ Git - 版本控制 Gti常用指令大全git -v :查看版…

【3DsMax】展UV记录

目录 一、概念 二、边的颜色 三、UV的连续性 四、合理的划分UV接缝 五、总结 一、概念 展uv的概念可以理解为把三维的模型铺平展成一个平面&#xff0c;然后在这个平面上去绘制图案。 二、边的颜色 我们先创建一个长方体&#xff0c;然后在修改器列表中添加“UVW展开”…

Redis中的缓存穿透

缓存穿透 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;导致这些请求直接到了数据库上&#xff0c;对数据库造成了巨大的压力&#xff0c;可能造成数据库宕机。 常见的解决方案&#xff1a; 1&#xff09;缓存无效 key 如果缓存和数据库中都查不到某…

SQLiteC/C++接口详细介绍sqlite3_stmt类(十)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;九&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十一&#xff09; 38、sqlite3_column_value sqlite3_column_valu…

【Linux 驱动基础】第一个驱动

# 前置知识 APP 打开文件时&#xff0c;可以得到一个整数&#xff0c;这个整数被称为文件句柄。对于 APP的每一个文件句柄&#xff0c;在内核里面都有一个“ struct file”与之对应。 使用open函数时&#xff0c;用户态调用 API 触发异常进入内核内核识别异常后&#xff0c;取…

【理解机器学习算法】之Clustering算法(Agglomerative Clustering)

聚合聚类(Agglomerative Clustering)是一种层次聚类算法&#xff0c;通过逐步合并或“聚集”它们来构建嵌套聚类。这种方法采用自底向上的方式构建聚类层次&#xff1a;它从将每个数据点作为单个聚类开始&#xff0c;然后迭代合并最接近的聚类对&#xff0c;直到所有数据点合并…

学习添加03(优惠卷)

1.优化卷模块的介绍 整体流程&#xff1a; 优惠卷表设计&#xff1a; 优惠卷范围表设计&#xff1a; 兑换码表设计&#xff1a;

【嵌入式——QT】Charts常见的图表的绘制

【嵌入式——QT】Charts常见的图表的绘制 柱状图QBarSetQBarSeriesQBarCategoryAxis图示 饼图堆叠柱状图百分比柱状图散点图和光滑曲线图代码示例 柱状图 QBarSet 用于创建柱状图的数据集。 主要函数 setLabel()&#xff1a;设置数据集标签 &#xff1b;setLabelBrush()&am…

解决arco-design下拉框回显id的问题

问题描述 下拉框回显选项中没有的选项&#xff0c;就会出现以下情况&#xff0c;只能把uid回显上去 解决方案 使用ui框架自带的属性fallback-option 用法 按以上操作&#xff0c;即可解决选择框回显uid问题

【数据结构】——排序之冒泡排序

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…
最新文章