ExoPlayer播放音视频的使用介绍
本文翻译自: https://google.github.io/ExoPlayer/guide.html#mediasource
一、简介
在安卓设备上播放视频和音乐是很受欢迎的活动。Android框架提供了MediaPlayer作为一个快速的解决方案,可以用最少的代码来播放媒体。Android还提供低级别的媒体api框架,如MediaCodec、AudioTrack和MediaDrm,可用于构建自定义媒体播放器解决方案。
ExoPlayer是一款开源的应用级媒体播放器,基于Android的低级媒体API构建。本指南描述了ExoPlayer库及其使用。它是指ExoPlayer的主要演示应用程序中的代码,以提供具体的示例。该指南介绍了使用ExoPlayer的优缺点。它展示了如何使用ExoPlayer播放DASH,SmoothStreaming和HLS自适应流,以及MP4、M4A、FMP4、WebM、MKV、MP3、Ogg、WAV、MPEG-TS、MPEG-PS、FLV和ADTS (AAC)的格式。它还讨论了ExoPlayer事件、消息、自定义和DRM支持。
ExoPlayer是Android的应用程序级媒体播放器。 它提供了Android的MediaPlayer API的替代品,用于在本地和互联网上播放音频和视频。 ExoPlayer支持Android MediaPlayer API目前不支持的功能,包括DASH和SmoothStreaming自适应回放。 与MediaPlayer API不同,ExoPlayer易于定制和扩展,并可通过Play Store应用程序更新进行更新。
二、优点和缺点
ExoPlayer与MediaPlayer内置的Android相比具有许多优势:
- 支持HTTP动态自适应流媒体(DASH)和SmoothStreaming(这两者在MediaPlayer上都不支持)。ExoPlayer还支持许多其他格式,详细信息请参阅 ExoPlayer支持的格式页面 。
-
支持高级HLS功能,如正确处理
#EXT-X-DISCONTINUITY
标记。 - 无缝合并,连接和循环媒体的能力。
- 随着您的应用程序一起更新播放器的能力。因为ExoPlayer是一个包含在应用程序apk中的库,所以您可以控制使用哪个版本,并且可以轻松地将其更新为新版本,作为常规应用程序更新的一部分。
- 更少的设备特定问题以及不同设备和Android版本的行为差异更小。
- 在Android 4.4(API级别19)及更高版本上支持Widevine通用加密。
- 您可以根据使用情况来自定义和扩展播放器。ExoPlayer是专门为此设计的,并允许将许多组件替换为自定义实现。
- 能够使用官方扩展快速集成多个附加库。例如, IMA扩展 可以很容易地使用 交互式媒体广告(SDKInteractive Media Ads SDK) 将内容货币化,为你带来收益。
缺点:
- ExoPlayer的标准音频和视频组件依赖Android的MediaCodec API,该API在Android 4.1 (API级别16)中发布。因此,他们不支持早期版本的Android。Widevine通用加密可以在Android 4.4 (API级别19)和更高版本上使用。
三、该库的功能模块概述
ExoPlayer库的核心是
ExoPlayer
接口。
ExoPlayer
暴露了普遍使用的高级媒体播放器api功能,比如缓冲媒体、播放、暂停和拖动条的功能。实现的目的是关于对(并因此加以很少的限制)所播放的媒体类型、存储方式和存储方式、以及如何呈现的方式进行很少的假设。
ExoPlayer
实现不是直接实现媒体的加载和渲染,而是将这项工作委托给创建播放器或准备播放时注入的组件。
所有
ExoPlayer
实现的常见组件是:
-
用于定义要播放的媒体的
MediaSource
,加载media,并从中读取加载的media 。MediaSource
在播放开始时通过ExoPlayer.prepare
注入。 -
渲染媒体个别组件的
Renderers(渲染器)
。当播放器创建时,Renderers
被注入。 -
TrackSelector
用于选择由MediaSource
提供的轨道以供每个可用的Renderers
使用。在创建播放器时注入TrackSelector
。 -
LoadControl
用于控制MediaSource
何时缓冲更多media以及缓冲多少media。LoadControl
是在播放器创建时注入的。
该库为常见用例提供了这些组件的默认实现,更详细的描述请看以下介绍。
ExoPlayer
可以使用这些组件,但是如果您不想用
ExoPlayer
默认的实现方式,也可以使用自定义实现来构建。例如,可以注入自定义
LoadControl
来更改播放器的缓冲策略,可以在Android设备上将自定义
Renderer
注入到Android不支持的视频编解码器。
注入组件以实现播放器功能部件的概念存在于整个库中。上面列出的组件的默认实现可以进一步注入组件,所以许多子组件可以被自定义实现单独替换。例如,默认的
MediaSource
实现需要通过构造函数注入一个或多个
DataSource
工厂。通过提供自定义工厂,可以从非标准的源或通过不同的网络栈加载数据。
四、开始入门
使用
ExoPlayer
写一个简单的用例,主要包括以下步骤:
-
将
ExoPlayer
作为依赖添加到您的项目中。 -
创建一个
SimpleExoPlayer
实例。 - 将播放器添加到view (用于视频输出和用户输入)。
-
准备播放器与
MediaSource
播放。 - 完成后释放播放器。
这些步骤在下面更详细地概述。 有关完整示例,请参阅
主应用程序demo
中的
PlayerActivity
。
(1)将ExoPlayer添加为依赖项
入门的第一步是确保您的项目根目录中的build.gradle文件中包含JCenter和Google存储库。
repositories {
jcenter()
google()
}
接下来,在应用程序moule的build.gradle文件中添加一个依赖项。以下内容将为完整的ExoPlayer库添加一个依赖项:
implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
其中2.X.X是您的首选版本。或者,您只能依赖您实际需要的库模块。例如,以下内容将添加对Core,DASH和UI库模块的依赖关系,这可能是播放DASH内容的应用程序所需的:
implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'
下面列出了可用的库模块。向完整的ExoPlayer库添加依赖关系等效于单独添加对所有库模块的依赖关系。
-
exoplayer-core
:核心功能(必需)。 -
exoplayer-dash
:支持DASH内容。 -
exoplayer-hls
:支持HLS内容。 -
exoplayer-smoothstreaming
:支持SmoothStreaming内容。 -
exoplayer-ui
:用于ExoPlayer的UI组件和资源。
除了库模块外,
ExoPlayer
还有多个扩展模块,它们依赖于外部库来提供附加功能。这些超出了本指南的范围。有关详细信息,请浏览
扩展目录
及其各自的
README
文件。
(2)创建播放器
您可以使用
ExoPlayerFactory
创建一个
ExoPlayer
实例。
ExoPlayerFactory
提供了一系列用于创建具有不同级别定制的
ExoPlayer
实例的方法。 对绝大多数用例而言,应该使用
ExoPlayerFactory.newSimpleInstance
方法之一。 这些方法返回
SimpleExoPlayer
,它扩展了
ExoPlayer
,添加了额外的高级播放器功能。 下面的代码是创建
SimpleExoPlayer
的示例。
//1.创建默认的 TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(videoTrackSelectionFactory);
//2.创建播放器
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(context, trackSelector);
(3)将播放器附加到view
ExoPlayer
库提供了一个
PlayerView
,它封装了一个
PlayerControlView
和一个显示视频的
Surface
。可以将
PlayerView
包含在应用程序的布局xml中。将播放器绑定到view很简单,代码如下:
// 将播放器附加到view
playerView.setPlayer(player);
如果您需要对播放器控件和渲染视频的Surface进行更详细的控制,则可以分别使用
SimpleExoPlayer
的
setVideoSurfaceView
,
setVideoTextureView
,
setVideoSurfaceHolder
和
setVideoSurface
方法直接设置播放器的目标
SurfaceView
,
TextureView
,
SurfaceHolder
或
Surface
。 您可以将
PlayerControlView
作为独立组件使用,或者实现您自己的播放控件,直接与播放器交互。 可以使用
setTextOutput
和
setId3Output
在播放过程中接收字幕和ID3元数据输出。
(4)准备播放器
在
ExoPlayer
中,每个media都由
MediaSource
表示。要播放一段media,您必须先创建一个相应的
MediaSource
,然后将该对象传递给
ExoPlayer.prepare
。
ExoPlayer
库为DASH(
DashMediaSource
),SmoothStreaming(
SsMediaSource
),HLS(
HlsMediaSource
)和常规媒体文件(
ExtractorMediaSource
)提供
MediaSource
实现。 这些实现在本指南后面会有更详细的介绍。
以下代码显示了如何使用适用于播放MP4文件的
MediaSource
准备播放器。
// 在播放期间测量带宽。 如果不需要,可以为null
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// 生成用于加载媒体数据的 DataSource 实例
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
Util.getUserAgent(context, "yourApplicationName"), bandwidthMeter);
// 这是要播放媒体的MediaSource
MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory)
.createMediaSource(mp4VideoUri);
// 准备播放器的资源
player.prepare(videoSource);
(5)控制播放器
播放器准备就绪后,可以通过播放器上的调用方法来控制播放。 例如:
setPlayWhenReady
可用于开始和暂停播放
各种
seekTo
方法可用于在媒体内搜索
setRepeatMode
可用于控制媒体是否以及如何循环播放
并且
setPlaybackParameters
可用于调整播放速度和音调。
如果玩家绑定到PlayerView或PlayerControlView,则用户与这些组件的交互将导致玩家调用相应的方法。
(6)释放播放器
当播放器不再需要时释放播放器非常重要,以释放视频解码器等有限资源以供其他应用程序使用。 这可以通过调用
ExoPlayer.release
完成。
五、媒体资源(MediaSource)的使用
在ExoPlayer中,每个media都由
MediaSource
表示。
ExoPlayer
库为DASH(
DashMediaSource
),SmoothStreaming(
SsMediaSource
),HLS(
HlsMediaSource
)和常规媒体文件(
ExtractorMediaSource
)提供了
MediaSource
实现。 在
main demo app
的
PlayerActivity
中可以找到如何实例化所有四个示例。
MediaSource实例不适用于重新使用的情况。 如果您想用相同的media多次准备播放器,请每次使用新的实例。
除了上述的MediaSource实现外,ExoPlayer库还提供了
MergingMediaSource,LoopingMediaSource,ConcatenatingMediaSource和DynamicConcatenatingMediaSource
。这些
MediaSource
实现可以通过组合来实现更复杂的播放功能。下面描述了一些常见的用例。请注意,尽管在视频播放的上下文中描述了以下示例,但它们同样适用于仅播放音频,以及任何支持的媒体类型的播放的情况。
(1)从侧面加载字幕文件
给定一个视频文件和一个单独的字幕文件,可以使用 MergingMediaSource 将它们合并到单个播放源中。
// Build the video MediaSource.
MediaSource videoSource =
new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Build the subtitle MediaSource.
Format subtitleFormat = Format.createTextSampleFormat(
id, // An identifier for the track. May be null.
MimeTypes.APPLICATION_SUBRIP, // The mime type. Must be set correctly.
selectionFlags, // Selection flags for the track.
language); // The subtitle language. May be null.
MediaSource subtitleSource =
new SingleSampleMediaSource.Factory(...)
.createMediaSource(subtitleUri, subtitleFormat, C.TIME_UNSET);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
new MergingMediaSource(videoSource, subtitleSource);
(2)循环播放视频
要无限循环,通常最好使用 ExoPlayer.setRepeatMode 而不是 LoopingMediaSource 。
使用 LoopingMediaSource 可以将视频无缝地循环固定次数。 以下是播放视频两次的示例。
MediaSource source =
new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Plays the video twice.
LoopingMediaSource loopingSource = new LoopingMediaSource(source, 2);
(3)播放一系列视频
ConcatenatingMediaSource
可以连续播放两个或多个单独的
MediaSource
。 下面是按顺序播放了两个视频的例子。 数据源之间的转换是无缝的。对连接的源具有相同的格式这一点不做强制要求,您可以把两个不同格式的数据源连接起来(例如,将包含480p H264的视频文件与包含720p VP9的视频文件连接起来就可以)。 同时这些源甚至可以是不同类型的(例如,将视频与仅音频流串接也是很友好的)。
MediaSource firstSource =
new ExtractorMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =
new ExtractorMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, secondSource);
DynamicConcatenatingMediaSource
类似于
ConcatenatingMediaSource
,不同之处在于它允许在播放前和播放期间动态添加,删除和移动
MediaSource
。 DynamicConcatenatingMediaSource非常适合于播放列表的使用场景,即用户可以在播放期间修改播放列表。
MediaSource
实例不应该多次添加到
DynamicConcatenatingMediaSource
中,或者在之前被删除的情况下重新添加。 推荐创建新的实例去操作。
(4)高级组合
有可能进一步将复合MediaSources组合起来,用于更多不常见的用法。 给定两个视频A和B,以下示例显示LoopingMediaSource和ConcatenatingMediaSource如何一起使用来播放序列(A,A,B)。
MediaSource firstSource =
new ExtractorMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =
new ExtractorMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =