内容
播放视频和音乐是Android设备上的热门活动。 Android框架提供MediaPlayer
作为使用最少代码播放媒体的快速解决方案。 它还提供低级媒体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支持。
与Android内置的MediaPlayer相比,ExoPlayer具有许多优势:
#EXT-X-DISCONTINUITY
标签。值得注意的是,还有一些缺点:
ExoPlayer库的核心是ExoPlayer
接口。 ExoPlayer
公开了传统的高级媒体播放器功能,例如缓冲媒体,播放,暂停和搜索的功能。 实现旨在对正在播放的媒体类型,存储方式和位置以及如何呈现的媒体类型做出一些假设(并因此施加很少的限制)。ExoPlayer
实现不是直接实现媒体的加载和呈现,而是将此工作委托给在创建播放器或准备播放时注入的组件。 所有ExoPlayer
实现的通用组件是:
MediaSource
,用于定义要播放的媒体,加载媒体,以及从中读取加载的媒体。 在播放开始时通过ExoPlayer.prepare
注入MediaSource
。Renderer
渲染媒体的各个组件。 创建播放器时会注入Renderer
。TrackSelector
,用于选择MediaSource
提供的曲目,供每个可用的Renderer
。 创建播放器时会注入TrackSelector
。LoadControl
,用于控制MediaSource
何时缓冲更多媒体,以及缓冲多少媒体。 创建播放器时会注入LoadControl
。该库为常见用例提供了这些组件的默认实现,如下面更详细地描述的。 ExoPlayer
可以使用这些组件,但如果需要非标准行为,也可以使用自定义实现构建。 例如,可以注入自定义LoadControl
来更改播放器的缓冲策略,或者可以注入自定义Renderer
以使用Android本身不支持的视频编解码器。
注入实现播放器功能的组件的概念贯穿整个库。 上面列出的组件的默认实现将工作委托给进一步注入的组件。 这允许使用自定义实现单独替换许多子组件。 例如,默认的MediaSource
实现需要通过其构造函数注入一个或多个DataSource
工厂。 通过提供自定义工厂,可以从非标准源或通过不同的网络堆栈加载数据。
对于简单的用例, ExoPlayer
入门包括实现以下步骤:
SimpleExoPlayer
实例。MediaSource
准备播放器。以下更详细地概述了这些步骤。 有关完整示例,请参阅主演示应用程序中的PlayerActivity
。
入门的第一步是确保您的项目根目录中的build.gradle
文件中包含JCenter和Google存储库。
repositories { jcenter () google () }
接下来,在app模块的build.gradle
文件中添加依赖项。 以下内容将为完整的ExoPlayer库添加依赖项:
implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
其中2.XX
是您的首选版本。 或者,您只能依赖实际需要的库模块。 例如,以下内容将添加对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
:支持exoplayer-smoothstreaming
内容。exoplayer-ui
:用于ExoPlayer的UI组件和资源。除了库模块,ExoPlayer还有多个扩展模块,这些模块依赖于外部库来提供附加功能。 这些超出了本指南的范围。 浏览扩展目录及其各自的README以获取详细信息。
您可以使用ExoPlayerFactory
创建ExoPlayer
实例。 工厂提供了一系列方法来创建具有不同级别自定义的ExoPlayer
实例。对于绝大多数用例,应使用ExoPlayerFactory.newSimpleInstance
方法之一。 这些方法返回SimpleExoPlayer
,它扩展了ExoPlayer
以添加额外的高级播放器功能。 下面的代码是创建SimpleExoPlayer
的示例。
// 1. Create a default TrackSelector Handler mainHandler = new Handler (); BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter (); TrackSelection . Factory videoTrackSelectionFactory = new AdaptiveTrackSelection . Factory ( bandwidthMeter ); DefaultTrackSelector trackSelector = new DefaultTrackSelector ( videoTrackSelectionFactory ); // 2. Create the player SimpleExoPlayer player = ExoPlayerFactory . newSimpleInstance ( context , trackSelector );
必须从单个应用程序线程访问ExoPlayer实例。 如果该线程具有{@link Looper},则必须是创建该播放器的线程,否则该应用程序的主线程。
ExoPlayer库提供了一个PlayerView
,它封装了一个PlayerControlView
和一个呈现视频的Surface
。 PlayerView
可以包含在应用程序的布局xml中。 将播放器绑定到视图非常简单:
// Bind the player to the view. playerView . setPlayer ( player );
如果您需要对播放器控件和渲染视频的Surface
进行细粒度控制,则可以分别使用SimpleExoPlayer
的setVideoSurfaceView
, setVideoTextureView
, setVideoSurfaceHolder
和setVideoSurface
方法直接设置播放器的目标SurfaceView
, TextureView
, SurfaceHolder
或Surface
。 您可以将PlayerControlView
用作独立组件,或实现您自己的与播放器直接交互的播放控件。 setTextOutput
和setId3Output
可用于在播放期间接收标题和ID3元数据输出。
在ExoPlayer中,每个媒体都由MediaSource
代表。 要播放介质,必须首先创建相应的MediaSource
,然后将此对象传递给ExoPlayer.prepare
。 ExoPlayer库为DASH( DashMediaSource
), SsMediaSource
( SsMediaSource
),HLS( HlsMediaSource
)和常规媒体文件( ExtractorMediaSource
)提供MediaSource
实现。 本指南后面将详细介绍这些实现。以下代码显示如何使用适合播放MP4文件的MediaSource
准备播放器。
// 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 ( context , Util . getUserAgent ( context , "yourApplicationName" ), bandwidthMeter ); // This is the MediaSource representing the media to be played. MediaSource videoSource = new ExtractorMediaSource . Factory ( dataSourceFactory ) . createMediaSource ( mp4VideoUri ); // Prepare the player with the source. player . prepare ( videoSource );
一旦准备好了播放器,就可以通过调用播放器上的方法来控制播放。 例如, setPlayWhenReady
启动并暂停播放,各种seekTo
方法在媒体内搜索, setRepeatMode
控制媒体是否以及如何循环, setShuffleModeEnabled
控制播放列表改组, setPlaybackParameters
调整播放速度和音高。
如果播放器绑定到PlayerView
或PlayerControlView
则用户与这些组件的交互将导致调用播放器上的相应方法。
状态更改和回放错误等事件将报告给已注册的Player.EventListener
实例。 注册监听器以接收此类事件很容易:
// Add a listener to receive events from the player. player . addListener ( eventListener );
如果您只对事件的子集感兴趣,则扩展Player.DefaultEventListener
而不是实现Player.EventListener
允许您仅实现您感兴趣的方法。
使用SimpleExoPlayer
,可以在播放器上设置其他侦听器。 addVideoListener
方法允许您接收与视频渲染相关的事件,这些事件可能对调整UI很有用(例如,正在渲染视频的Surface
的宽高比)。 addAnalyticsListener
方法允许您接收详细事件,这可能对分析有用。
在不再需要播放器时释放播放器非常重要,这样可以释放有限的资源,例如视频解码器,供其他应用程序使用。 这可以通过调用ExoPlayer.release
来完成。
在ExoPlayer中,每个媒体都由MediaSource
代表。 ExoPlayer库为DASH( DashMediaSource
), SsMediaSource
( SsMediaSource
),HLS( HlsMediaSource
)和常规媒体文件( ExtractorMediaSource
)提供MediaSource
实现。 可以在主演示应用程序的 PlayerActivity
中找到如何实例化所有四个示例 。
除了上面描述的MediaSource实现之外,ExoPlayer库还提供了ConcatenatingMediaSource
, ClippingMediaSource
, LoopingMediaSource
和MergingMediaSource
。 这些MediaSource
实现通过组合实现更复杂的回放功能。 一些常见用例如下所述。 请注意,尽管在视频播放的上下文中描述了以下示例,但它们同样适用于仅音频播放,并且实际上适用于任何支持的媒体类型的播放。
使用ConcatenatingMediaSource
支持播放列表,可以顺序播放多个MediaSource
。 以下示例表示由两个视频组成的播放列表。
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 );
连接源之间的转换是无缝的。 不要求它们具有相同的格式(例如,将包含480p H264的视频文件与包含720p VP9的视频文件连接起来就可以了)。 它们甚至可能是不同类型的(例如,将视频与仅音频流连接起来很好)。 允许在串联中多次使用单个MediaSource
。
可以通过在ConcatenatingMediaSource
添加,删除和移动MediaSource
来动态修改播放列表。 这可以通过调用相应的ConcatenatingMediaSource
方法在回放之前和回放期间完成。 播放器以正确的方式自动处理播放期间的修改。 例如,如果移动当前播放的MediaSource
,则不会中断播放,并且将在完成时播放其新的后继播放器。 如果当前播放的MediaSource
被移除,则播放器将自动移动到播放第一个剩余的后继者,或者如果不存在这样的后继者则转换到结束状态。
ClippingMediaSource
可用于剪辑MediaSource
以便只播放其中的一部分。 以下示例将视频播放剪辑为以5秒开始并以10秒结束。
MediaSource videoSource = new ExtractorMediaSource . Factory (...). createMediaSource ( videoUri ); // Clip to start at 5 seconds and end at 10 seconds. ClippingMediaSource clippingSource = new ClippingMediaSource ( videoSource , /* startPositionUs= */ 5_000_000 , /* endPositionUs= */ 10_000_000 );
要仅剪辑源的开头,可以将C.TIME_END_OF_SOURCE
设置为C.TIME_END_OF_SOURCE
。 要仅剪切到特定的持续时间,有一个构造函数接受durationUs
参数。
剪切视频文件的开头时,尽可能将起始位置与关键帧对齐。 如果开始位置未与关键帧对齐,则播放器将需要解码并丢弃从先前关键帧到开始位置的数据,然后才能开始播放。 这将在播放开始时引入短暂的延迟,包括播放器转换为播放ClippingMediaSource
作为播放列表的一部分或由于循环播放。
要无限循环,通常最好使用ExoPlayer.setRepeatMode
而不是LoopingMediaSource
。
使用LoopingMediaSource
可以将视频无缝循环固定次数。 以下示例播放视频两次。
MediaSource source = new ExtractorMediaSource . Factory (...). createMediaSource ( videoUri ); // Plays the video twice. LoopingMediaSource loopingSource = new LoopingMediaSource ( source , 2 );
给定视频文件和单独的字幕文件,可以使用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 );
可以将复合MediaSource
进一步组合用于更多不寻常的用例。 给定两个视频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 = new ConcatenatingMediaSource ( firstSourceTwice , secondSource );
以下示例是等效的,表明可以有多种方法来实现相同的结果。
MediaSource firstSource = new ExtractorMediaSource . Builder ( firstVideoUri , ...). build (); MediaSource secondSource = new ExtractorMediaSource . Builder ( secondVideoUri , ...). build (); // Plays the first video twice, then the second video. ConcatenatingMediaSource concatenatedSource = new ConcatenatingMediaSource ( firstSource , firstSource , secondSource );
曲目选择确定播放器的Renderer
播放哪些可用的媒体曲目。 轨道选择是TrackSelector
的责任,每当构建ExoPlayer
时,必须提供其实例。
DefaultTrackSelector
是一个灵活的TrackSelector
适用于大多数用例。 使用DefaultTrackSelector
,可以通过修改其Parameters
来控制它选择的轨道。 这可以在播放之前或播放期间完成。 例如,以下代码告诉选择器将视频轨道选择限制为SD,并选择德语音轨(如果有):
trackSelector . setParameters ( trackSelector . buildUponParameters () . setMaxVideoSizeSd () . setPreferredAudioLanguage ( "deu" ));
这是基于约束的轨道选择的示例,其中在不知道实际可用的轨道的情况下指定约束。 可以使用Parameters
指定许多不同类型的约束。 Parameters
还可用于从可用的轨道中选择特定轨道。 有关更多详细信息,请参阅DefaultTrackSelector
, Parameters
和ParametersBuilder
文档。
可以向ExoPlayer组件发送消息。 这些可以使用createMessage
创建,然后使用PlayerMessage.send
发送。 默认情况下,消息尽快在回放线程上传递,但可以通过设置另一个回调线程(使用PlayerMessage.setHandler
)或指定传递回放位置(使用PlayerMessage.setPosition
)来自定义。 通过ExoPlayer
发送消息可确保按顺序执行操作,同时在播放器上执行任何其他操作。
大多数ExoPlayer的开箱即用渲染器都支持允许在播放期间更改其配置的消息。 例如,音频渲染器接受消息来设置音量,视频渲染器接受消息来设置曲面。 这些消息应在回放线程上传递,以确保线程安全。
与Android的MediaPlayer
,ExoPlayer的主要优势之一是能够自定义和扩展播放器以更好地适应开发人员的使用案例。ExoPlayer库专门为此而设计,定义了许多接口和抽象基类,使应用程序开发人员可以轻松替换库提供的默认实现。 以下是构建自定义组件的一些用例:
Renderer
- 您可能希望实现自定义Renderer
来处理库提供的默认实现不支持的媒体类型。TrackSelector
- 实现自定义TrackSelector
允许应用程序开发人员更改每个可用Renderer
选择供MediaSource
公开的轨道使用的方式。LoadControl
- 实现自定义LoadControl
允许应用程序开发人员更改播放器的缓冲策略。Extractor
- 如果需要支持库当前不支持的容器格式,请考虑实现自定义Extractor
类,然后可以将其与ExtractorMediaSource
一起用于播放该类型的媒体。MediaSource
- 如果您希望获得以自定义方式提供给渲染器的媒体示例,或者您希望实现自定义MediaSource
合成行为,则实现自定义MediaSource
类可能是合适的。DataSource
- ExoPlayer的上游包已经包含了许多针对不同用例的DataSource
实现。 您可能希望实现自己的DataSource
类以另一种方式加载数据,例如通过自定义协议,使用自定义HTTP堆栈或自定义持久缓存。构建自定义组件时,建议执行以下操作:
Handler
一起传递给组件的构造函数。ExoPlayerComponent
并在其handleMessage
方法中接收配置更改。 您的应用应通过调用ExoPlayer的sendMessages
和blockingSendMessages
方法来传递配置更改。ExoPlayer支持Android 4.4(API级别19)的受数字版权管理(DRM)保护的播放。 有关更多详细信息,请参阅DRM页面 。
有关使用ExoPlayer时电池消耗的信息,请参阅电池消耗页面 。
有关最小化ExoPlayer库大小的建议,请参阅Shrinking ExoPlayer页面 。