ExoPlayer 播放器开发指南(译)

本文是译者第一次翻译英文文档,如有不足请多包涵

欢迎转载,转载请注明出处。http://blog.csdn.net/coder_giser/article/details/52870382

英文原文链接:https://google.github.io/ExoPlayer/guide.html
github source:https://github.com/google/ExoPlayer

序文

这个文档主要介绍 ExoPlayer 2.x。如果你仍然在使用1.x,你可以在这个链接中找到旧版本的开发文档

这是个在Android设备上为播放视频和音乐的主流库,这个库只需要使用少量的代码并使用MediaPlayer来播放多媒体,同时支持较低的API来自定义媒体播放器,比如 MediaCodec, AudioTrackMediaDrm

ExoPlayer在github上面是开源的,并且应用的媒体播放器是建立在Android低等级的媒体APIs上,在开源代码中包括ExoPlaye library和一个demo 程序。

这篇文档介绍了ExoPlayer库和如何使用它,主要是在demo中演示代码,已提供具体的例子,文档介绍了ExoPlayer的利弊,展示了如何使用ExoPlayer去播放DASH,SmoothStreaming 和HLS自适应流,以及其他格式,比如 MP4, M4A, FMP4, WebM, MKV, MP3, Ogg, WAV, MPEG-TS, MPEG-PS, FLV 和 ADTS (AAC)。文章也讨论了对ExoPlayer 的事件 ,消息,定制和DRM的支持。

利弊

ExoPlayer 相比在Android上直接使用MediaPlayer有很多优势。

  • 支持动态自适应HTTP流(DASH)和SmoothStreaming,其他的格式也支持,详情请查看支持的格式页面。
  • 支持更高级的HLS 要素,比如可以处理#EXT-X-DISCONTINUITY标签。
  • 与多媒体无缝连接,合并和循环播放。
  • 支持自定义播放器,或者继承播放器的类来满足你的需求。ExoPlayer 有明确的设计思想,即支持更多的组件可以被自定义。
  • 播放器可以很容易的和你的应用一起更新。因为ExoPlayer只是一个库并被你的应用所引用,你可以控制任何你想使用的版本,你可以使用更新的版本,来配合你的应用日常更新。
  • 很少的设备会有问题。
  • 在4.3(API 18)或者更高版本的设备上支持,Google推出的Widevine 方式的常规加密。

这里也有一些缺点
- ExoPlayer的标准视频和音频是依赖Android的MediaCodec API构建的,该API是于Android 4.1 (API 16)发布的,因此,在低于Android 4.1 (API 16)是无法使用的,即ExoPlayer最低支持到Android4.1,Widevine 方式的加密最低支持Android4.3(API 18)。

ExoPlayer库概述


ExoPlayer库的核心是ExoPlayer接口,ExoPlayer 拥有传统高级播 放器功能,比如支持缓冲,播放,暂停和快进。这样设计的目的是对媒体播放有更多的思考,比如怎么缓存,缓存在哪里,怎么渲染UI,而不是直接实现多媒体的加载和UI渲染,当一个播放器被创建或者当它准备重放的时候,ExoPlayer 来完成这些播放组件。ExoPlayer 实现了以下功能:

  • MediaSource 定义了加载、播放媒体,并且可以读取已经加载的媒体,使用ExoPlayer.prepare 可在播放开始传入MediaSource

  • Renderers 是一个渲染媒体的独立组件,当播放器被创建时,Renderers 会被注入。

  • TrackSelector 用来选择磁道,由MediaSource 所提供,并会被任意的可用的Renderers 所使用,当播放器被创建时,TrackSelector 会被注入。

  • LoadControl 用来控制缓存的,当媒体需要更多缓存的时候,LoadControl 来控制缓存的数量,当播放器被创建时,LoadControl 会被注入。

这个库提供了一些组件的默认实现的常见例子,下面我们详介绍下。

ExoPlayer 可以用在这些组件上,如果有特殊需求也可以自定义实现,例如自定义LoadControl 可以改变缓冲需求,或者自定义Renderer 可以改变Android原生不支持的视频编解码器。

注入组件的概念在播放器库中多处使用,允许很多子组件去替换默认的实现,例如,需要一个默认的MediaSource实现,或者更多的DataSource 通过他们的构造方法去注入,通过提供一个自定义的factory 可以从非标准的数据源或网络加载数据。

入门指南

一个简单的例子,使用ExoPlayer 需要执行以下步骤:


  1. 添加ExoPlayer库的依赖;
  2. 创建一个SimpleExoPlayer 实例
  3. 把player渲染在View上,为了展示播放器内容或者响应用户交互;
  4. 使用MediaSource 准备播放;
  5. 使用完成后,释放资源。

下面详细描述这些步骤,如果需要完成的例子,可参考在ExoPlayer demo中的PlayerActivity

添加ExoPlayer依赖

确定你项目根目录下面的build.gradle 是使用的jcenter仓库。

repositories {
    jcenter()
}

然后添加ExoPlayer库的依赖在你的app module下面的build.gradle

compile 'com.google.android.exoplayer:exoplayer:r2.X.X'

这里的r2.X.X 可以选择你喜欢的版本,如果想看最新版本,可以查看这里已经发布的。更多详细信息,请查看在Bintray上面的项目。

创建一个播放器

现在你可以使用ExoPlayerFactory 创建一个ExoPlayer 实例,ExoPlayerFactory 提供了一系列方法,可以在不同程度上创建实例。大不部分情况下,使用默认的Renderer 即可满足需求,当ExoPlayerFactory.newSimpleInstance 方法被使用的时候,会返回一个SimpleExoPlayerSimpleExoPlayer 继承自ExoPlayer 并添加了很多高级功能,(子类添加其他方法)。下面有一个创建SimpleExoPlayer 的例子:

// 1. Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
    new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
    new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory);

// 2. Create a default LoadControl
LoadControl loadControl = new DefaultLoadControl();

// 3. Create the player
SimpleExoPlayer player =
    ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl);

把player渲染在View上

ExoPlayer库提供了一个SimpleExoPlayerView 类,封装了PlaybackControlViewSurface 用于展示视频,SimpleExoPlayerView 可以写在你的layout文件加下面的xml文件中,然后绑定player给这个view。

// Bind the player to the view.
simpleExoPlayerView.setPlayer(player);

如果你需要在播放视频时高精度的控制player,你可以分别使用SimpleExoPlayersetVideoSurfaceView, setVideoTextureView, setVideoSurfaceHoldersetVideoSurface 方法,设置playe绑定的目标组件为SurfaceView, TextureView, SurfaceHolder 或者Surface

你可以使用PlaybackControlView 作为一个独立的组件,用来实现你自定义的回放控件,已达到交互目的。

setTextOutputsetId3Output 可以在播放期间,获取到字幕和ID3的元数据。

准备播放

ExoPlayer 每帧是通过MediaSource 展示的,播放的每一帧必须先创建对应的MediaSource 然后通过这个对象调用ExoPlayer.prepare ,ExoPlayer 库提供提供的MediaSource 的实现支持 DASH (DashMediaSource), SmoothStreaming (SsMediaSource), HLS (HlsMediaSource) 和 普通的媒体文件(ExtractorMediaSource),这些具体的实现会在本文最后介绍。下面的代码展示了使用MediaSource 播放一个mp4文件是如何准备player的。

// Measures bandwidth during playback. Can be null if not required.

DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
    Util.getUserAgent(this, "yourApplicationName"), bandwidthMeter);
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource(mp4VideoUri,
    dataSourceFactory, extractorsFactory, null, null);
// Prepare the player with the source.
player.prepare(videoSource);

只要player已经进入准备,播放就可以接收到播放器的回调方法,例如,setPlayWhenReady 可以被用来执行开始和暂停操作,不同类型的seekTo 方法可以在媒体中执行快进操作,如果这个player已经绑定了SimpleExoPlayerViewPlaybackControlView ,那么用户就可以调用对应的方法来做交互了。

释放player

当player不在需要被使用时,一定要去释放player资源,比如视频解码器。这里可以调用ExoPlayer.release 完成操作。

MediaSource


在ExoPlayer库中每一帧都是通过MediaSource 来显示的,ExoPlayer 库提供的MediaSource 支持 DASH (DashMediaSource), SmoothStreaming (SsMediaSource), HLS (HlsMediaSource) 和 普通的媒体文件(ExtractorMediaSource)。在ExoPlayer demo中例举了4个PlayerActivity ,用户可自行查看。

除了MediaSource 基础类的描述外,ExoPlayer library 也提供了MergingMediaSource, LoopingMediaSourceConcatenatingMediaSource ,这些MediaSource通过继承实现了更多的复杂播放功能,下面将介绍一些常见的情况,下面的例子虽然只是介绍了视频播放的情况,他们同样适用于音频播放,实际上,媒体播放支持任何类型。

外挂字幕文件

假如我们有一个视频文件和一个单独的字幕文件,MergingMediaSource 可以合并他们并且播放。

MediaSource videoSource = new ExtractorMediaSource(videoUri, ...);
MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, ...);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
    new MergingMediaSource(videoSource, subtitleSource);

无缝循环播放视频

使用LoopingMediaSource 可以无缝循环播放一个视频,下面的代码展示了无限循环播放一个视频,也可以在创建LoopingMediaSource 的时候制定循环次数。

MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Loops the video indefinitely.
LoopingMediaSource loopingSource = new LoopingMediaSource(source);

无缝播放一系列视频

ConcatenatingMediaSource可以用来无缝的播放2个或者更多的单独视频文件,下面展示的是无缝播放2个视频,并且2个文件不需要是相同的格式,也可以无缝播放如480p H264和720p VP9的视频文件,播放的媒体文件甚至可以是不同类型的文件,比如一个是视频,一个是音频。

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, secondSource);

高级使用

我们再次更进一步的使用MediaSources 来满足一些特殊的需求。加入有2个视频文件A和B,下面的例子演示了如何使用LoopingMediaSourceConcatenatingMediaSource 来共同完成无限有序的循环,播放顺序为AAB。

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSourceTwice, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);

下面的例子演示了和上面例子相同的效果,只是为了展示给大家我们如何使用不同的方法来满足同一个需求。

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, firstSource, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);

以下黑体内容相当重要,请认真阅读:

在开发过程中,请避免使用同一个MediaSource 实例多次,除非本文档有明确的允许这么做, 只有上面演示的这一种情况,firstSource对象才会被使用两次,因为Javadoc 明确的指出了ConcatenatingMediaSource 允许重复使用。在开发中是允许使用多个MediaSource 实例的。

Player events


正在播放时,可以监听到ExoPlayer播放的全部状态事件。这些事件对于更新UI是非常有用的,比如播放按钮,更多的ExoPlayer 组件也回调了他们自己特殊的低级事件,可以用来做性能监控。

High level events(高级事件)

ExoPlayer 支持使用addListenerremoveListener 方法来添加和移除ExoPlayer.EventListener 实例。注册的监听会在播放状态被改变时发生回调,播放错误时同样会接受到播放失败的回调。

开发人员可以自定义一个播放按钮并注册监听,可以在播放状态改变时更新按钮状态,一个app应该在播放失败时展示一个错误提示给用户。

当使用SimpleExoPlayer 时,可以注册额外的监听给player,特别是setVideoListener 当可以接受到绑定视频展示的事件时,更方便更新UI状态,其他监听可以作用于SimpleExoPlayer用来接收调试信息 ,例如setVideoDebugListener 和setAudioDebugListener 的回调。

Low level events(低级事件)

除了高级监听外,ExoPlayer library 也允许一些独立组件提供他们自己的监听。你通常需要通过Handler 对象去处理这些组件,确定在哪个线程中调用了监听的方法。实际上,很多时候我们都是在主线程中创建的Handler

发送消息给组件


在播放时,一些ExoPlayer 组件允许改变其配置属性。根据这条约定,使用sendMessages 或者blockingSendMessages方法 ,我们可以发送消息给这些组件,在使用这个方法之前我们首先要确定是线程安全的,这些配置会配合其他操作在播放视频时改变代码的执行顺序。

定制化

ExoPlayer 的好处之一就是其定制化的功能,其继承自MediaPlayer 并更好的适应了开发需求。ExoPlayer library在设计时专门考虑到了这一点,定义了一系列的借口和抽象类来尽可能帮助开发者替换该库默认的实现方式。这里有一些定制化的建议:

  • Renderer — 你可以自定义Renderer 来处理该库默认实现方式所不支持的媒体格式。
  • Extractor— 如果你需要支持一个目前不受支持的格式,可以自定义Extractor的实现,然后配合ExtractorMediaSource 共同来播放需要的格式。
  • MediaSource— 自定义MediaSource可以实现适合播放器的渲染器。
  • DataSource— ExoPlayer的底层包已经包含了很多不同情况下使用的DataSource ,你可以自定义DataSource 加载数据,比如自定义协议,自定义HTTP栈,和通过持久化缓存。

自定义引导

  • 如果一个自定义组件需要发送事件给app,建议使用同一个model 作为已经存在的ExoPlayer组件,事件会通过Handler 一起传递给组件的构造函数。
  • 建议自定义组件使用同一个model 作为已经存在的ExoPlayer组件,并支持在播放时可以动态配置属性,应该实现一个ExoPlayerComponent 并在他的handleMessage方法中接受配置信息的改变 ,app应该支持调用sendMessagesblockingSendMessages方法发送配置信息的改变信息。

数字版权管理


在Android4.3或者更高版本上,ExoPlayer 支持数字版权管理(DRM)来保护播放器,为了支持数字版权管理内容,当你实例化player时,你的APP必须注入一个DrmSessionManagerExoPlayerFactory 提供了一个工厂方法用来支持该操作。DrmSessionManager对象负责提供DrmSession实例,该实例提供MediaCrypto对象解密,以确保需要解密的密匙和DRM所使用的是匹配的。

ExoPlayer library提供了默认的DrmSessionManager ,即StreamingDrmSessionManager ,在Android设备上,这个session管理支持任何DRM方式来模块化已经存在的DRM 组件,所有的Android设备都需要支持Widevine modular DRM(即L3安全,尽管一些设备支持L1),一些设备还支持其他方式,比如PlayReady。所有的Android TV设备都支持PlayReady。

在ExoPlayer demo 中的PlayerActivity 演示了当player在实例化时,DrmSessionManager 如何创建和注入的。

本文是译者第一次翻译英文文档,如有不足请多包涵

欢迎转载,转载请注明出处。http://blog.csdn.net/coder_giser/article/details/52870382

猜你喜欢

转载自blog.csdn.net/u010259369/article/details/52870382