Exoplayer简介

这篇具有很好参考价值的文章主要介绍了Exoplayer简介。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

Exoplayer是一个android平台的播放器,支持格式比android系统的mediaplayer更好,确定性更好,mediaplayer是可以进行厂家定制的,各平台一致性比较差,这里简单介绍一下Exoplayer的最基础的使用接口,方便之后阅读源码

正文

播放器一般分为三部分,获取DataSource,解码以及视输出。因为exoplayer解码基本上是通过android系统mediacode实现的,或者一些分必要的插件实现的,代码比较少,输出包括声音输出以及视频渲染,相对来说业务也不是很复杂,所以资源输入进行了多次抽象。主要分成三层

  1. MediaItem是对输入项的抽象,主要代表一个可播放的资源,包括文件路径,网络地址,android的asset文件和file,
  2. DataSource主要是读取MediaItem内容,
  3. Extractor是解封装,解封装的。
  4. MediaSource主要是对于DataSource进一封装,主要是为了解决HLS对片段的支持。
    一个最简单的MediaSource构建如下:
Uri uri = Uri.parse("/sdcard/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv")
DataSource.Factory dataSourceFactory = new FileDataSource.Factory();
mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory,MatroskaExtractor.FACTORY).
		createMediaSource(MediaItem.fromUri(uri));

大概就是传入文件地址,得到mediaitem,然后通过FileDataSource.Factory(),生成FileDataSource去可以读取文件内容,这里的复杂操作都是通过ProgressiveMediaSource通过类的聚合,把FileDataSource、Extractor和MediaItem统一进行调度,实现文件的读取,注意这里ProgressiveMediaSource是针对单个文件的定制类,如果hls或者dash需要特定的mediaSource。
播放的话,直接调用如下:

    ExoPlayer player = new ExoPlayer.Builder(getApplicationContext()).build();
    player.setMediaSource(mediaSource);
    player.prepare();
    player.play();
    player.setVideoSurface(surface);
    

大概是这样

在这里我们简单的分析一下ProgressiveMediaSource和player的主要流程,
MediaSource最核心的功能有两个

  1. 提供一个Timeline,主要是描述一个多媒体的列表,或者一个片段。是对多媒体管理
  2. 提供MediaPeriod,给播放器读取多媒体资源
    最关键的接口是两个,一个是prepareSource获取TImeLine,createPeriod获取MediaPeriod。我们主要关注createPeriod。
  @Override
  public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
    DataSource dataSource = dataSourceFactory.createDataSource();
    if (transferListener != null) {
      dataSource.addTransferListener(transferListener);
    }
    return new ProgressiveMediaPeriod(
        localConfiguration.uri,
        dataSource,
        progressiveMediaExtractorFactory.createProgressiveMediaExtractor(),
        drmSessionManager,
        createDrmEventDispatcher(id),
        loadableLoadErrorHandlingPolicy,
        createEventDispatcher(id),
        this,
        allocator,
        localConfiguration.customCacheKey,
        continueLoadingCheckIntervalBytes);
  }

这里只是提供了调用接口,最终交给player获取MediaPeriod,因为这里面包含了MediaExtractor以及DataSource,完全可以解封装出我们需要的东西,这里我们记录一下,这就是mkv的MediaExtractor以及FileDataSource。关于mediasource的阅读到此为止,下部分分析Player相关流程。
Player构造比较简单,主要是通过builder模式实现的

    public Builder(Context context) {
      this(
          context,
          () -> new DefaultRenderersFactory(context),
          () -> new DefaultMediaSourceFactory(context, new DefaultExtractorsFactory()));
    }
   
   private Builder(
        Context context,
        Supplier<RenderersFactory> renderersFactorySupplier,
        Supplier<MediaSourceFactory> mediaSourceFactorySupplier) {
      this(
          context,
          renderersFactorySupplier,
          mediaSourceFactorySupplier,
          () -> new DefaultTrackSelector(context),
          DefaultLoadControl::new,
          () -> DefaultBandwidthMeter.getSingletonInstance(context),
          /* analyticsCollectorSupplier= */ null);
    }

    private Builder(
        Context context,
        Supplier<RenderersFactory> renderersFactorySupplier,
        Supplier<MediaSourceFactory> mediaSourceFactorySupplier,
        Supplier<TrackSelector> trackSelectorSupplier,
        Supplier<LoadControl> loadControlSupplier,
        Supplier<BandwidthMeter> bandwidthMeterSupplier,
        @Nullable Supplier<AnalyticsCollector> analyticsCollectorSupplier) {
      this.context = context;
      this.renderersFactorySupplier = renderersFactorySupplier;
      this.mediaSourceFactorySupplier = mediaSourceFactorySupplier;
      this.trackSelectorSupplier = trackSelectorSupplier;
      this.loadControlSupplier = loadControlSupplier;
      this.bandwidthMeterSupplier = bandwidthMeterSupplier;
      this.analyticsCollectorSupplier =
          analyticsCollectorSupplier != null
              ? analyticsCollectorSupplier
              : () -> new AnalyticsCollector(checkNotNull(clock));
      looper = Util.getCurrentOrMainLooper();
      audioAttributes = AudioAttributes.DEFAULT;
      wakeMode = C.WAKE_MODE_NONE;
      videoScalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
      videoChangeFrameRateStrategy = C.VIDEO_CHANGE_FRAME_RATE_STRATEGY_ONLY_IF_SEAMLESS;
      useLazyPreparation = true;
      seekParameters = SeekParameters.DEFAULT;
      seekBackIncrementMs = C.DEFAULT_SEEK_BACK_INCREMENT_MS;
      seekForwardIncrementMs = C.DEFAULT_SEEK_FORWARD_INCREMENT_MS;
      livePlaybackSpeedControl = new DefaultLivePlaybackSpeedControl.Builder().build();
      clock = Clock.DEFAULT;
      releaseTimeoutMs = DEFAULT_RELEASE_TIMEOUT_MS;
      detachSurfaceTimeoutMs = DEFAULT_DETACH_SURFACE_TIMEOUT_MS;
    }
    
        public ExoPlayer build() {
      return buildSimpleExoPlayer();
    }

    /* package */ SimpleExoPlayer buildSimpleExoPlayer() {
      checkState(!buildCalled);
      buildCalled = true;
      return new SimpleExoPlayer(/* builder= */ this);
    }


player =
          new ExoPlayerImpl(
              renderers,
              builder.trackSelectorSupplier.get(),
              builder.mediaSourceFactorySupplier.get(),
              builder.loadControlSupplier.get(),
              builder.bandwidthMeterSupplier.get(),
              analyticsCollector,
              builder.useLazyPreparation,
              builder.seekParameters,
              builder.seekBackIncrementMs,
              builder.seekForwardIncrementMs,
              builder.livePlaybackSpeedControl,
              builder.releaseTimeoutMs,
              builder.pauseAtEndOfMediaItems,
              builder.clock,
              builder.looper,
              /* wrappingPlayer= */ this,
              additionalPermanentAvailableCommands);
              
  }

注意这里是通过builder初始化了比较多的一个default的factory类,主要解决类型定制的问题,这里主要注意一下:

  1. DefaultRenderersFactory
    这是提供解码器的核心工具类,我们自己定义个简单的MyRenderersFactory
static class MyRenderersFactory implements RenderersFactory{

    private final Context context;

    MyRenderersFactory(Context context){
      this.context = context;
    }
    @Override
    public Renderer[] createRenderers(Handler eventHandler, VideoRendererEventListener videoRendererEventListener,
                                      AudioRendererEventListener audioRendererEventListener, TextOutput textRendererOutput, MetadataOutput metadataRendererOutput) {
      MediaCodecVideoRenderer videoRenderer =
              new MediaCodecVideoRenderer(
                      context,
                      new DefaultMediaCodecAdapterFactory(),
                      MediaCodecSelector.DEFAULT,
                      5000,
                      false,
                      eventHandler,
                      videoRendererEventListener,
                      50);

      @Nullable
      AudioSink audioSink =
              new DefaultAudioSink(AudioCapabilities.getCapabilities(context), new DefaultAudioSink.DefaultAudioProcessorChain(),
                      true, true, DefaultAudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED);

      MediaCodecAudioRenderer audioRenderer =
              new MediaCodecAudioRenderer(
                      context,
                      new DefaultMediaCodecAdapterFactory(),
                      MediaCodecSelector.DEFAULT,
                      false,
                      eventHandler,
                      audioRendererEventListener,
                      audioSink);
      Renderer[] renderers = {videoRenderer,audioRenderer};
      return renderers;
    }
  }

这里比较明显的就是提供一个render列表。
2. 核心是:MediaSourceList
3. 核心是: MediaPeriodQueue

setMediaSource
  public void setMediaSources(
      List<MediaSourceList.MediaSourceHolder> mediaSources,
      int windowIndex,
      long positionUs,
      ShuffleOrder shuffleOrder) {
    handler
        .obtainMessage(
            MSG_SET_MEDIA_SOURCES,
            new MediaSourceListUpdateMessage(mediaSources, shuffleOrder, windowIndex, positionUs))
        .sendToTarget();
  }

其实就是通过handler处理消息,以及把mediasource,传入
关于如何读取data。以及如何解封装,相关调用比较复杂,不过在我们场景下,就是通过ProgressiveMediaPeriod获取解封装后的数据,解码器中,大概代码如下:

  /* package */ int readData(
      int sampleQueueIndex,
      FormatHolder formatHolder,
      DecoderInputBuffer buffer,
      @ReadFlags int readFlags) {

    int result =
        sampleQueues[sampleQueueIndex].read(formatHolder, buffer, readFlags, loadingFinished);
    return result;
  }

然后通知解码器去处理解码,

其实整体是有两个线程,一个是mediasource负责读取解封装,一个是负责解码渲染。我们大概介绍一下.核心类就是MediaDataSource。具体类图如下:
Exoplayer简介
其核心就是被ExoPlayerImplInternal控制,进行必要的初始化,然后在调用prepare后,启动loader,进行自治,实现自动加载内容,最终把数据缓存在SampleQueue中,被render读取,大概流程
就是ExoPlayerImplInternal调用MediaPerid中的startloading。启动一个loader。然后自治加载解封装内容,大概流程如下:
Exoplayer简介
关于externalLoadable的加载其实也相对比较复杂,但是大概内容就是通过dataSource加载内容,通过Extractor解封装,传入ExtractorOutput,最终保存到sampleQueues中,具体流程这里就不在详细介绍。
我们把目光关注render中的解码以及输出,

后记

这里大概介绍了一下exoplayer的架构,文中跳过不少过程细节,以及状态判断,但是整体流程基本保留,有机会重新整理一下。文章来源地址https://www.toymoban.com/news/detail-414232.html

到了这里,关于Exoplayer简介的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【免费】【前言】Stable Diffusion WebUI 课程简介

    欢迎来到  聚梦小课堂  的 Stable Diffusion系统图文课堂。 在这里,会详细介绍 免费的本地版AI绘画软件 —— Stable Diffusion(WebUI+Fooocus) 的方方面面,包括但不限于:如何安装,如何使用,参数含义,常见问题,使用技巧、综合商业案例... 使用中若发现有错误或者任何建议,

    2024年02月02日
    浏览(45)
  • 深入理解Android音视频同步机制(一)ExoPlayer的avsync逻辑

    对于此前没有了解过ExoPlayer的朋友,我们在这里先用下面的时序图简单介绍一下ExoPlayer在音视频同步这块的基本流程: 图中 ExoPlayerImplInternal是Exoplayer的主loop所在处,这个大loop不停的循环运转,将下载、解封装的数据送给AudioTrack和MediaCodec去播放。 MediaCodecAudioRenderer和MediaC

    2023年04月12日
    浏览(56)
  • 【Android入门到项目实战-- 11.4】—— ExoPlayer视频播放器框架的详细使用

    目录 什么是ExoPlayer 一、基本使用  1、添加依赖项  2、布局 3、Activity 二、自定义播放暂停 1、首先如何隐藏默认的开始暂停和快进? 2、自定义 三、控制视频画面旋转和比例调整 四、全屏放大和缩小 1、双击视频放大缩小 2、按钮放大缩小 五、完整的实现代码 XML Activity   

    2024年02月11日
    浏览(57)
  • Android ExoPlayer2播放m3u8视频设置Cookie

    支持m3u8格式视频,同时服务端做了加密防止视频被盗。 服务端需要客户端这边再请求.m3u8地址时带上3个Cookie参数,分别是CloudFront-Policy,CloudFront-Signature,CloudFront-Key-Pair-Id。 本地使用了GSYVideoPlayer作为视频播放器,如何设置Cookie是个问题,原本以为直接将待传入的三个Cookie直接

    2024年01月16日
    浏览(56)
  • 安卓手机APP开发使用Media3 ExoPlayer创建一个基本的播放app

    安卓手机APP开发使用Media3 ExoPlayer创建一个基本的播放app 目录 概述  现在开始 创建一个媒体播放器 创建一个ExoPlayer 准备播放器 控制播放器 释放播放器 管理回放使用一个媒体会话 创建一个媒体会话 给其它的客户端的授权 在后台上播放媒体

    2024年04月11日
    浏览(42)
  • NetMock 简介:简化 Java、Android 和 Kotlin 多平台中的 HTTP 请求测试

    NetMock可让我们摆脱在测试环境中模拟请求和响应的复杂性。 NetMock 是一个功能强大、用户友好的库,旨在简化模拟HTTP请求和响应的过程。 对开发者来说,测试HTTP请求经常会带来一些挑战,因为要在测试环境中模拟请求和响应的复杂性很高。这样就会增加手动测试的时间和精

    2024年02月11日
    浏览(49)
  • Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放

    1. 实现效果    效果图中,视频没有铺满 是因为使用了ExoPlayer的RESIZE_MODE_FIT模式, 虽然使用RESIZE_MODE_FILL模式可以填充整个父布局,但是本Demo中使用的视频源本身就不适合全屏,会把视频拉伸,效果不好。 抖音上的视频源应该都有严格的宽高尺寸,才能做到全屏有很好的效

    2023年04月09日
    浏览(41)
  • Android System Property讲解前言

    我们在开发过程中经常会使用到系统属性,例如获取系统软件版本,获取设备名名称,boardid等;有时也需要内置自己的属性,系统属性简单来说是用来存储系统中某些键值对数据,具有全局性、存取灵活方便的特点。 setprop 可以给属性设置int,bool,string等基本类型 在java代码中

    2024年02月07日
    浏览(42)
  • exoplayer的使用-1

    exoplayer是谷歌出品的,不同版本的api变化不少.我用的是2.18.7版本的.因为flutter插件用到这个播放器也是这个版本的. 基于exoplayer的flutter播放器有video_player,betterplayer,这两个比较有名的.先从flutter播放器说起. 如果只是做一个播放示例,这些播放器功能就满足了.通常自己添加亮度

    2024年02月16日
    浏览(33)
  • 【ARM 裸机】硬件平台简介

    硬件平台采用的是正点原子的 I.MX6ULL-MINI 开发板,分为底板和核心板; 正点原子 Mini 开发板的外形尺寸为 100mm*130mm,I.MX6U-Mini 开发板底板板载资源如下: ◆ 1 个核心板接口,支持 I.MX6ULL 核心板。 ◆ 1 个电源指示灯(蓝色)。 ◆ 1 个状态指示灯(红色)。 ◆ 1 路 CAN 接口,

    2024年04月14日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包