http://developer.android.com/guide/topics/media/exoplayer.html
Playing videos and music is a popular activity on Android devices. The Android framework provides MediaPlayer
as a quick solution for playing media with minimal code, and the MediaCodec
andMediaExtractor
classes are provided for building custom media players. The open source project, ExoPlayer, is a solution between these two options, providing a pre-built player that you can extend.
在安卓设备上,我们经常会需要播放视频和音频。Android framework 提供MediaPlayer
以便程序员能快速实现影音播放。同时,Android framework提供了MediaCodec
和MediaExtractor类,我们可以通过这两个类来实现自定义的媒体播放器。
ExoPlayer就是一个介于现有Mediaplayer和自定义媒体播放器之间的预建播放器,同时我们通过还可以获得比原有Mediaplayer更多的扩展能力。
ExoPlayer supports features not currently provided by MediaPlayer
, including Dynamic adaptive streaming over HTTP (DASH), SmoothStreaming, and persistent caching. ExoPlayer can be extended to handle additional media formats, and because you include it as part of your app code, you can update it along with your app.
ExoPlayer支持许多MediaPlayer没有的特性,包括DASH,SmoothStreaming,已经持久缓存。ExoPlayer可以通过进一步扩展来处理多种媒体格式,同时由于它是内置于你的app代码中的,所以它可以随着你的app来升级。
This guide describes how to use ExoPlayer for playing Android supported media formats, as well as DASH and SmoothStreaming playback. This guide also discusses ExoPlayer events, messages, DRM support and guidelines for customizing the player.
本文描述了怎么使用ExoPlayer播放android支持的媒体格式,以及DASH和SmoothStreaming。同时本文也探讨了ExoPlayer的事件、消息和DRM支持。最后本文还提供了定制player的教程。
Note: ExoPlayer is an open source project that is not part of the Android framework and is distributed separately from the Android SDK. The project contains a library and a demo app that shows both simple and more advanced use of ExoPlayer:
注意:ExoPlayer 是一个开源项目,它不属于Android framework ,并且是独立于Android SDK独立分发的。该项目包含一个library和一个demo,其中展示了ExoPlayer的简单应用及其高级定制。
- ExoPlayer Library — This part of the project contains the core library classes.
- Simple Demo — This part of the app demonstrates a basic use of ExoPlayer.
- Full Demo — This part of the app demonstrates more advanced features, including the ability to select between multiple audio tracks, a background audio mode, event logging and DRM protected playback.
Overview
概述
ExoPlayer is a media player built on top of the MediaExtractor
and MediaCodec
APIs released in Android 4.1 (API level 16). At the core of this library is the ExoPlayer
class. This class maintains the player’s global state, but makes few assumptions about the nature of the media being played, such as how the media data is obtained, how it is buffered or its format. You inject this functionality through ExoPlayer’s prepare()
method in the form of TrackRenderer
objects.
ExoPlayer是建立在MediaExtractor
and MediaCodec这两组api之上的播放器。
这两组api是在api 16 即Android 4.1发布的,所以
ExoPlayer的支持的最小api是api 16,但是由于ExoPlayer的build.gradle的minSdkVersion为9,所以在api《16时,也可以使用ExoPlayer的部分功能。
ExoPlayer库的核心类是ExoPlayer类。该类维护了播放器的全局状态,同时也。比如如何获取媒体数据,如何缓冲以及是怎样的编码格式。
ExoPlayer provides default TrackRenderer
implementations for audio and video, which make use of theMediaCodec
and AudioTrack
classes in the Android framework. Both renderers require a SampleSource
object, from which they obtain individual media samples for playback. Figure 1 shows the high level object model for an ExoPlayer implementation configured to play audio and video using these components.
ExoPlayer基于MediaCodec
and AudioTrack
提供了默认的音视频的TrackRenderer实现。所有的renderers都需要SampleSource对象,
ExoPlayer从SampleSource获得media samples 用于播放。图1展示了ExoPlayer是如何配置组合这些组件用于播放音视频的。
Figure 1. High level object model for an ExoPlayer configured to play audio and video using TrackRenderer
objects
TrackRenderer
A TrackRenderer
processes a component of media for playback, such as video, audio or text. The ExoPlayer class invokes methods on its TrackRenderer
instances from a single playback thread, and by doing so causes each media component to be rendered as the global playback position is advanced. The ExoPlayer library provides MediaCodecVideoTrackRenderer
as the default implementations rendering video andMediaCodecAudioTrackRenderer
for audio. Both implementations make use of MediaCodec
to decode individual media samples. They can handle all audio and video formats supported by a given Android device (see Supported Media Formats for details). The ExoPlayer library also provides an implementation for rendering text called TextTrackRenderer
.
TrackRenderer可以处理媒体文件中的音频、视频以及文本。ExoPlayer会在一个单独的播放控制线程里调用TrackRenderer实例中的方法。这一方式使得每部分媒体组件都在播放时呈现出来。ExoPlayer库默认提供了MediaCodecVideoTrackRenderer和MediaCodecAudioTrackRenderer 类来分别实现视频和音频的呈现。MediaCodecVideoTrackRenderer和MediaCodecAudioTrackRenderer 类都使用了MediaCodec
来对media samples解码。他们能够处理所有在 Supported Media Formats 列出的媒体格式。ExoPlayer还提供了TextTrackRenderer 来实现对文本的呈现。
The code example below outlines the main steps required to instantiate an ExoPlayer to play video and audio using the standard TrackRenderer
implementations.
下面的代码展示了实例化一个ExoPlayer对象并通过它播放音视频的主要步骤。其中使用的是标准的TrackRenderer实现。
// 1. Instantiate the player.
player =ExoPlayer.Factory.newInstance(RENDERER_COUNT);
// 2. Construct renderers.
MediaCodecVideoTrackRenderer videoRenderer =…
MediaCodecAudioTrackRenderer audioRenderer =...
// 3. Inject the renderers through prepare.
player.prepare(videoRenderer, audioRenderer);
// 4. Pass the surface to the video renderer.
player.sendMessage(videoRenderer,MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
surface);
// 5. Start playback.
player.setPlayWhenReady(true);
...
player.release();// Don’t forget to release when done!
For a complete example, see the SimplePlayerActivity
in the ExoPlayer demo app, which correctly manages an ExoPlayer instance with respect to both the Activity
and Surface
lifecycles.
SampleSource
采样源
A standard TrackRenderer
implementation requires a SampleSource
to be provided in its constructor. ASampleSource
object provides format information and media samples to be rendered. The ExoPlayer library provides FrameworkSampleSource
and ChunkSampleSource
. The FrameworkSampleSource
class usesMediaExtractor
to request, buffer and extract the media samples. The ChunkSampleSource
class provides adaptive playback using DASH or SmoothStreaming, and implements networking, buffering and media extraction within the ExoPlayer library.
TrackRenderer
实现需要在其构造函数中传入 SampleSource对象。
一个
SampleSource对象提供了呈现出来所需要的格式信息和采样信息。ChunkSampleSource类可以适配DASH或SmoothStreaming数据源,同时ChunkSampleSource类还实现了访问网络、缓冲播放以及媒体提取的能力。Providing media using MediaExtractor
In order to render media formats supported by the Android framework, the FrameworkSampleSource
class usesMediaExtractor
for networking, buffering and sample extraction functionality. By doing so, it supports any media container format supported by the version of Android where it is running. For more information about media formats supported by Android, see Supported Media Formats.
The diagram in Figure 2 shows the object model for an ExoPlayer implementation usingFrameworkSampleSource
.
FrameworkSampleSource
类使用MediaExtractor组件来访问网络、缓冲以及采样提取等功能。如此一来,就能支持每一版本android所能支持的媒体格式了。关于格式支持的信息详见: Supported Media Formats.Figure 2. Object model for an implementation of ExoPlayer that renders media formats supported by Android usingFrameworkSampleSource
The following code example outlines how the video and audio renderers are constructed to load the video from a specified URI.
下面的示例展示了如何从特定url构建renderer。
FrameworkSampleSource sampleSource =newFrameworkSampleSource(
activity, uri,null,2);
MediaCodecVideoTrackRenderer videoRenderer =newMediaCodecVideoTrackRenderer(
sampleSource,null,true,MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,0,
mainHandler, playerActivity,50);
MediaCodecAudioTrackRenderer audioRenderer =newMediaCodecAudioTrackRenderer(
sampleSource,null,true);
The ExoPlayer demo app provides a complete implementation of this code in DefaultRendererBuilder
. TheSimplePlaybackActivity
class uses it to play one of the videos available in the demo app. Note that in the example, video and audio are muxed, meaning they are streamed together from a single URI. TheFrameworkSampleSource
instance provides video samples to the videoRenderer
object and audio samples to the audioRenderer
object as they are extracted from the media container format. It is also possible to play demuxed media, where video and audio are streamed separately from different URIs. This functionality can be achieved by having two FrameworkSampleSource
instances instead of one.
ExoPlayer demo项目里面的DefaultRendererBuilder类中完整的包含了上面的代码。SimplePlaybackActivity
类使用其播放了一段视频。注意,在示例代码中,音频和视频是混合在一起的,这意味着视频流和音频流都来自于同一个uri。当采样数据从媒体容器中从提取出来时,FrameworkSampleSource
实例把视频采样输出到 videoRenderer 对象中,同时把音频采样输出到 audioRenderer 对象中。当然我们也可以播放音频和视频流分开的媒体,不过这需要我们建立两个FrameworkSampleSource
实例。
Providing media for adaptive playback
ExoPlayer supports adaptive streaming, which allows the quality of the media data to be adjusted during playback based on the network conditions. DASH and SmoothStreaming are examples of adaptive streaming technologies. Both these approaches load media in small chunks (typically 2 to 10 seconds in duration). Whenever a chunk of media is requested, the client selects from a number of possible formats. For example, a client may select a high quality format if network conditions are good, or a low quality format if network conditions are bad. In both techniques, video and audio are streamed separately.
ExoPlayer支持自适应的数据流,也就是说,可以在播放时根据客户端的网络带宽和CPU的执行能力的改变,随时的调整视频质量。DASH和SmoothStreaming是这类技术的典型代表。他们都是一次一小块一小块地加载数据的(一般来说大概会有2到10秒的延迟)。在获取到小块数据之后,客户端可以选择相应地格式来解析。例如,在网络环境好的时候,客户端可以选择高质量的格式,而网络环境不佳时,可以选择低质量的格式。不过,这类技术里面,其视频流和音频流是相互独立的。
ExoPlayer supports adaptive playback through use of the ChunkSampleSource
class, which loads chunks of media data from which individual samples can be extracted. Each ChunkSampleSource
requires aChunkSource
object to be injected through its constructor, which is responsible for providing media chunks from which to load and read samples. The DashMp4ChunkSource
and SmoothStreamingChunkSource
classes provide DASH and SmoothStreaming playback using the FMP4 container format. The DashWebMChunkSource
class uses the WebM container format to provide DASH playback.
ExoPlayer通过ChunkSampleSource
类来实现适应性播放功能。每个ChunkSampleSource
对象都需要在构造时注入一个ChunkSource
对象。我们通过ChunkSource
对象来加载采样数据,并输出成ChunkSampleSource
类需要的小块媒体数据。DashMp4ChunkSource
类和SmoothStreamingChunkSource
类分别提供了DASH和SmoothStreaming协议下的解决方案,不过这两个类支持的都是FMP4格式。DashWebMChunkSource类
则实现了DASH协议下使用WebM格式解析的解决方案。
All of the standard ChunkSource
implementations require a FormatEvaluator
and a DataSource
to be injected through their constructors. The FormatEvaluator
objects select from the available formats before each chunk is loaded. The DataSource
objects are responsible for actually loading the data. Finally, theChunkSampleSources
require a LoadControl
object that controls the chunk buffering policy.
所有的标准ChunkSource
实现都需要在构造器中注入一个FormatEvaluator
对象 和一个 DataSource
对象。
The object model of an ExoPlayer configured for a DASH adaptive playback is shown in the diagram below. This example uses an HttpDataSource
object to stream the media over the network. The video quality is varied at runtime using the adaptive implementation of FormatEvaluator
, while audio is played at a fixed quality level。FormatEvaluator 对象会在每个数据块被加载之前选择可用的格式。 DataSource
对象负责实际加载数据。
最后,ChunkSampleSources
需要一个LoadControl
对象来控制数据块的缓冲策略。
The object model of an ExoPlayer configured for a DASH adaptive playback is shown in the diagram below. This example uses an HttpDataSource
object to stream the media over the network. The video quality is varied at runtime using the adaptive implementation of FormatEvaluator
, while audio is played at a fixed quality level.
下图展示了如何实现DASH 自适应播放。此例中通过HttpDataSource
对象从网络获取数据流。使用FormatEvaluator 时,音频流的质量是固定的,而视频流的质量则是在运行时不断变化的。
Figure 3. Object model for a DASH adaptive playback using ExoPlayer
The following code example outlines how the video and audio renderers are constructed.
下面的代码标出了音视频的renders是如何被构造出来的。
Handler mainHandler = playerActivity.getMainHandler();
LoadControl loadControl =newDefaultLoadControl(
newBufferPool(BUFFER_SEGMENT_SIZE));
BandwidthMeter bandwidthMeter =newBandwidthMeter(); // Build the video renderer.
DataSource videoDataSource =newHttpDataSource(userAgent,
HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter);
ChunkSource videoChunkSource =newDashMp4ChunkSource(videoDataSource,
newAdaptiveEvaluator(bandwidthMeter), videoRepresentations);
ChunkSampleSource videoSampleSource =newChunkSampleSource(videoChunkSource,
loadControl, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE,true);
MediaCodecVideoTrackRenderer videoRenderer =newMediaCodecVideoTrackRenderer(
videoSampleSource,null,true,MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
0, mainHandler, playerActivity,50); // Build the audio renderer.
DataSource audioDataSource =newHttpDataSource(userAgent,
HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter);
ChunkSource audioChunkSource =newDashMp4ChunkSource(audioDataSource,
newFormatEvaluator.FixedEvaluator(), audioRepresentation);
SampleSource audioSampleSource =newChunkSampleSource(audioChunkSource,
loadControl, AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE,true);
MediaCodecAudioTrackRenderer audioRenderer =newMediaCodecAudioTrackRenderer(
audioSampleSource,null,true);
In this code, videoRepresentations
and audioRepresentation
are Representation
objects, each of which describes one of the available media streams. In the DASH model, these streams are parsed from a media presentation description (MPD) file. The ExoPlayer library provides aMediaPresentationDescriptionParser
class to obtain Representation
objects from MPD files.
在这段代码中,videoRepresentations
和 audioRepresentation
是Representation 对象。它们各自表示了一种可用的媒体流。在DASH模型中,这些数据流从MPD文件中转换而来,这一转换过程通过MediaPresentationDescriptionParser
实现。
Note: Building Representation objects from MPD files is not required. You can build Representation objects from other data sources if necessary.
注意:注意不一定要从MPD中构建Representation对象。如果必要的话,你也可以从其他数据源创建Representation对象。
The ExoPlayer demo app provides complete implementation of this code in DashVodRendererBuilder
. TheSimplePlaybackActivity
class uses this builder to construct renderers for playing DASH sample videos in the demo app. It asynchronously fetches a specified MPD file in order to construct the requiredRepresentation
objects. For an equivalent SmoothStreaming example, see theSmoothStreamingRendererBuilder
class in the demo app.
ExoPlayer demo项目在 DashVodRendererBuilder 类中完全实现了这段代码。SimplePlaybackActivity
类使用
DashVodRendererBuilder 构造了renders用以播放DASH样例视频。在demo中,异步获取了指定的MPD文件,来构建Representation
对象。同样地,demo中使用SmoothStreamingRendererBuilder
来实现相同的功能。
Format selection for adaptive playback
For DASH and SmoothStreaming playback, consider both static format selection at the start of playback and dynamic format selection during playback. Static format selection should be used to filter out formats that should not be used throughout the playback, for example formats with resolutions higher than the maximum supported by the playback device. Dynamic selection varies the selected format during playback, typically to adapt video quality in response to changes in network conditions.
使用DASH和SmoothStreaming进行自适应播放时,可以分为两种情况。一种是在播放开始时指定一个格式,这被成为静态格式选择。一种是在播放过程中动态切换播放格式,称之为动态格式选择。静态格式选择一般用于不能在播放过程中随意改变格式的情况,比如格式大于设备可以支持的最大分辨率的时候。动态格式选择可以在播放时改变格式,最典型的就是当网络环境改变时,相应地改变视频格式。
Static format selection
静态格式选择
When preparing a player, you should consider filtering out some of the available formats if they are not useable for playback. Static format selection allows you to filter out formats that cannot be used on a particular device or are not compatible with your player. For audio playback, this often means picking a single format to play and discarding the others.
当播放器进行准备操作时,我们需要把一些不用的格式给过滤出去。比如那些不被当前设备所支持或者播放器不兼容的格式。对于音频播放来说,这通常意味着选定一个格式。
For video playback, filtering formats can be more complicated. Apps should first eliminate any streams that whose resolution is too high to be played by the device. For H.264, which is normally used for DASH and SmoothStreaming playback, ExoPlayer’s MediaCodecUtil
class provides a maxH264DecodableFrameSize()
method that can be used to determine what resolution streams the device is able to handle, as shown in the following code example:
对视频播放来说,过滤格式会稍微复杂一些。首先应用要评估视频的分辨率是不是高过设备所能处理的分辨率。H.264这一格式通常在DASH和SmoothStreaming中采用,ExoPlayer的MediaCodecUtil
类专门提供了一个maxH264DecodableFrameSize()
方法。该方法用于判断设备能处理哪一分辨率的视频。示例如下:
int maxDecodableFrameSize =MediaCodecUtil.maxH264DecodableFrameSize();
Format format = representation.format;
if(format.width * format.height <= maxDecodableFrameSize){
// The device can play this stream.
videoRepresentations.add(representation);
}else{
// The device isn't capable of playing this stream.
}
This approach is used to filter Representations
in the DashVodRendererBuilder
class of the ExoPlayer demo app, and similarly to filter track indices in SmoothStreamingRendererBuilder
.
上面的代码展示了如何过滤Representations。这段代码在demo项目中的DashVodRendererBuilder 类可以看到。
在SmoothStreamingRendererBuilder 也可以看到用类似的方法过滤track。
In addition to eliminating unsupported formats, it should be noted that the ability to seamlessly switch between H.264 streams of different resolution is an optional decoder feature available in Android 4.3 (API level 16) and higher, and so is not supported by all devices. The availability of an adaptive H.264 decoder can be queried using MediaCodecUtil
, as shown in the following code example:
H.264和其他格式之间的无缝切换需要在Android 4.3 (API level 16) 及以上版本才支持。
如下示例,我们可以通过MediaCodecUtil 类来判断适配性。
boolean isAdaptive =MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264).adaptive;
The MediaCodecVideoTrackRenderer
class is still able to handle resolution changes on devices that do not have adaptive decoders, however the switch is not seamless. Typically, the switch creates a small discontinuity in visual output lasting around 50-100ms. For devices that do not provide an adaptive decoder, app developers may choose to adapt between formats at a single fixed resolution so as to avoid discontinuities. The ExoPlayer demo app implementation does not pick a fixed resolution.
13811046323
MediaCodecVideoTrackRenderer 类仍然可以在没有自适应解码器的设备上处理分辨率的变化,但是切换过程并不是无缝的。这个切换会引发50-100ms的卡顿。如果要在没有自适应解码器的设备上避免卡顿,开发者可以选择使用固定的的分辨率。不过ExoPlayer里面并没有这一示例。
Dynamic format selection
动态格式选择
During playback, you can use a FormatEvaluator
to dynamically select from the available video formats. The ExoPlayer library provides a FormatEvaluator.Adaptive
implementation for dynamically selecting between video formats based on the current network conditions.
通过使用FormatEvaluator,我们可以在播放时动态选择可用的视频格式。ExoPlayer库提供了FormatEvaluator.Adaptive的一个实现来根据当前网络状况选择格式。
This class provides a simple, general purpose reference implementation, however you are encouraged to write your own FormatEvaluator
implementation to best suit your particular needs.
这个类提供了一个简单通用的实现。但是还是建议你根据自己的需求实现你自己的FormatEvaluator
Player Events
播放器事件
During playback, your app can listen for events generated by the ExoPlayer that indicate the overall state of the player. These events are useful as triggers for updating the app user interface such as playback controls. Many ExoPlayer components also report their own component specific low level events, which can be useful for performance monitoring.
ExoPlayer提供了很多事件,便于用户监听ExoPlayer的所有状态。在播放控制时,通过这些事件可以及时的触发ui更新。许多ExoPlayer组件还会将一些底层的事件暴露出来,这在性能监控时会非常有用。
High level events
高级事件
ExoPlayer allows instances of ExoPlayer.Listener
to be added and removed using its addListener()
andremoveListener()
methods. Registered listeners are notified of changes in playback state, as well as when errors occur that cause playback to fail. For more information about the valid playback states and the possible transitions between them, see the ExoPlayer source code.
ExoPlayer中可以通过addListener()
和removeListener()
方法,添加和删除ExoPlayer.Listener
实例。被注册的监听者会接收到播放时的状态变化以及异常。如果想进一步了解播放状态的细节,还有状态之间的转换等内容,请参看ExoPlayer的源代码。
Developers who implement custom playback controls should register a listener and use it to update their controls as the player’s state changes. An app should also show an appropriate error to the user if playback fails.
开发者在实现自定义播放控制时,可以通过注册listener事件监听器的方式来更新ui控件。当然,如果播放器出现异常,app也需要将友好的错误信息呈现出来。
Low level events
低级事件
In addition to high level listeners, many of the individual components provided by the ExoPlayer library allow their own event listeners. For example, MediaCodecVideoTrackRenderer
has constructors that take aMediaCodecVideoTrackRenderer.EventListener
. In the ExoPlayer demo app, SimplePlayerActivity
acts as a listener so that it can adjust the dimensions of the target surface to have the correct height and width ratio for the video being played:
作为高级事件的补充,ExoPlayer的各个独立组件也会有他们自己的事件监听机制。比如MediaCodecVideoTrackRenderer
类就有一个接受MediaCodecVideoTrackRenderer.EventListener
的构造函数。
在demo应用中,
SimplePlayerActivity
实际上扮演了一个监听者的角色。所以它可以根据播放器的事件来调整
surface的大小以适应视频流。
@Override
publicvoid onVideoSizeChanged(int width,int height){
surfaceView.setVideoWidthHeightRatio(height ==0?1:(float) width / height);
}
The RendererBuilder
classes in the ExoPlayer demo app inject the activity as the listener, for example in theDashVodRendererBuilder
class:
在demo应用中,RendererBuilder
类注入了activity作为监听者。代码如下:
MediaCodecVideoTrackRenderer videoRenderer =newMediaCodecVideoTrackRenderer(
videoSampleSource,null,true,MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
0,mainHandler, playerActivity,50);
Note that you must pass a Handler
object to the renderer, which determines the thread on which the listener’s methods are invoked. In most cases, you should use a Handler
associated with the app’s main thread, as is the case in this example.
需要注意的是,你需要传递一个Handler
对象给renderer。这决定了监听者的方法在那个线程上调用。大部分情况下你可以直接像本例一样,传入主线程的handler。
Listening to individual components can be useful for adjusting UI based on player events, as in the example above. Listening to component events can also be helpful for logging performance metrics. For example,MediaCodecVideoTrackRenderer
notifies its listener of dropped video frames. A developer may wish to log such metrics to track playback performance in their app.
如上述实例所示,监听独立组件的事件使得根据事件调整ui会更方便。监听组件的事件有助于记录性能指标。例如,MediaCodecVideoTrackRenderer 可以发送丢弃的视频帧数给监听者。开发者可以通过记录这些指标来追踪app的性能。
Many components also notify their listeners when errors occur. Such errors may or may not cause playback to fail. If an error does not cause playback to fail, it may still result in degraded performance, and so you may wish to log all errors in order to track playback performance. Note that an ExoPlayer instance always notifies its high level listeners of errors that cause playback to fail, in addition to the listener of the individual component from which the error originated. Hence, you should display error messages to users only from high level listeners. Within individual component listeners, you should use error notifications only for informational purposes.
许多组件在发生异常时,也会发出通知,有些异常会导致播放失败,有些不会。那些不会导致播放失败的异常也很重要,很可能会影响应用的性能,所以我们也会需要记录所有的异常信息,以便追踪。
需要注意的是,ExoPlayer实例发送给高级监听者的异常,通常都是导致播放失败的异常。如果是高级监听者中接受到得异常,我们最后将错误信息告诉用户。而对于独立组件的监听者来说,我们只需要对错误信息进行记录即可。
Sending messages to components
发送消息给组件
Some ExoPlayer components allow changes in configuration during playback. By convention, you make these changes by passing asynchronous messages through the ExoPlayer to the component. This approach ensures both thread safety and that the configuration change is executed in order with any other operations being performed on the player.
一些ExoPlayer组件允许在播放过程中改变配置。为了方便起见,可以使用发送异步消息的方式变更配置。这样可以确保线程安全,同时也能保证配置的更改按顺序执行。
The most common use of messaging is passing a target surface to MediaCodecVideoTrackRenderer
:
最常见的用法就是更改视频显示的surface:
player.sendMessage(videoRenderer,MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
surface);
Note that if the surface needs to be cleared because SurfaceHolder.Callback.surfaceDestroyed()
has been invoked, then you must send this message using the blocking variant of sendMessage()
:
注意,如果SurfaceHolder.Callback.surfaceDestroyed()
被调用即surface被销毁时,我们需要发送一个阻塞消息给EXOPlayer:
player.blockingSendMessage(videoRenderer,
MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,null);
You must use a blocking message because the contract of surfaceDestroyed()
requires that the app does not attempt to access the surface after the method returns. The SimplePlayerActivity
class in the demo app demonstrates how the surface should be set and cleared.
这么做的原因是因为,surface被销毁时,需要确保MediaCodecVideoTrackRenderer 不会再将数据写入已经释放的surface。
在demo应用中,有相关代码可以展示如何正确地设置和清理surface。
Customizing ExoPlayer
One of the main benefits of ExoPlayer over MediaPlayer
is the ability to customize and extend the player to better suit the developer’s use case. The ExoPlayer library is designed specifically with this in mind, defining a number of abstract base classes and interfaces that make it possible for app developers to easily replace the default implementations provided by the library. Here are some use cases for building custom components:
使用ExoPlayer的一个主要的好处是ExoPlayer能够更加容易的扩展和定制化。ExoPlayer定义了许多朝向基类以及接口。这时代开发者能够很方便的使用自己实现来替换默认实现:
-
TrackRenderer
- You may want to implement a customTrackRenderer
to handle media types other than audio and video. TheTextTrackRenderer
class within the ExoPlayer library is an example of how to implement a custom renderer. You could use the approach it demonstrates to render custom overlays or annotations. Implementing this kind of functionality as aTrackRenderer
makes it easy to keep the overlays or annotations in sync with the other media being played. -
TrackRenderer
- 我们可能会想要定制TrackRenderer
来处理视频和音频之外的其他媒体。TextTrackRenderer
就是一个自定义TrackRenderer
-的实例。 -
SampleSource
- If you need to support a container format not already handled byMediaExtractor
or ExoPlayer, consider implementing a customSampleSource
class. -
SampleSource
- 如果你想要支持一个在MediaExtractor
或者 ExoPlayer的默认支持范围之外的容器格式。可以考虑实现自定义的SampleSource类。 -
FormatEvaluator
- The ExoPlayer library providesFormatEvaluator.Adaptive
as a simple reference implementation that switches between different quality video formats based on the available bandwidth. App developers are encouraged to develop their own adaptiveFormatEvaluator
implementations, which can be designed to suit their use specific needs. -
FormatEvaluator
- ExoPlayer library providesFormatEvaluator.Adaptive
as -
DataSource
- ExoPlayer’s upstream package already contains a number ofDataSource
implementations for different use cases, such as writing and reading to and from a persistent media cache. You may want to implement you ownDataSource
class to load data in another way, such as a custom protocol or HTTP stack for data input. -
DataSource
- ExoPlayer的上级包名下已经包含了一系列DataSource 实现,比如从媒体缓存池读写数据等。 - 你可以实现你自己的
DataSource
类,以别的方式加载数据,比如自定义的协议或者HTTP stack 。
Custom component guidelines自定义组件的一些守则
If a custom component needs to report events back to the app, we recommend that you do so using the same model as existing ExoPlayer components, where an event listener is passed together with a Handler
to the constructor of the component.
如果你得自定义组件需要有事件交互,我们推荐你使用和已有的ExoPlayer组件相同的编程模型。把 Handler
和事件监听者通过构造器传入组件中去。
We recommended that custom components use the same model as existing ExoPlayer components to allow reconfiguration by the app during playback, as described in Sending messages to components. To do this, you should implement a ExoPlayerComponent
and receive configuration changes in its handleMessage()
method. Your app should pass configuration changes by calling ExoPlayer’s sendMessage()
andblockingSendMessage()
methods.
对于组件的设置更改,我们也推荐你同已有的模型保持一致。不过这样的话,你需要实现ExoPlayerComponent
,在handleMessage()
方法中接受配置的变更。按照这种模式实现的自定义组件,就可以和默认组件一样,通过调用sendMessage()
和blockingSendMessage()
方法来变更配置了。
Digital Rights Management
数字版权管理
On Android 4.3 (API level 18) and higher, ExoPlayer supports Digital Rights Managment (DRM) protected playback. In order to play DRM protected content with ExoPlayer, your app must inject a DrmSessionManager
into the MediaCodecVideoTrackRenderer
and MediaCodecAudioTrackRenderer
constructors. ADrmSessionManager
object is responsible for providing the MediaCrypto
object required for decryption, as well as ensuring that the required decryption keys are available to the underlying DRM module being used.
在安卓4.3及以上系统中,ExoPlayer可以支持DRM技术。但是为了播放drm内容,你需要将一个DrmSessionManager
注入到MediaCodecVideoTrackRenderer
和 MediaCodecAudioTrackRenderer
构造器中。DrmSessionManager
对象提供了一个用于解密的MediaCrypto
对象,当然也要确保解密的keys是有效的。
The ExoPlayer library provides a default implementation of DrmSessionManager
, calledStreamingDrmSessionManager
, which uses MediaDrm
. The session manager supports any DRM scheme for which a modular DRM component exists on the device. All Android devices are required to support Widevine modular DRM (with L3 security, although many devices also support L1). Some devices may support additional schemes such as PlayReady.
ExoPlayer库提供了一个默认的DrmSessionManager 实现,叫做StreamingDrmSessionManager。
StreamingDrmSessionManager 有用到MediaDrm
. StreamingDrmSessionManager 支持任意一种设备支持的DRM架构。基本上,所有的安卓设备都支持Widevine modular DRM。也有一些设备能支持其他架构。
The StreamingDrmSessionManager
class requires a MediaDrmCallback
to be injected into its constructor, which is responsible for actually making provisioning and key requests. You should implement this interface to make network requests to your license server and obtain the required keys. TheWidevineTestMediaDrmCallback
class in the ExoPlayer demo app sends requests to a Widevine test server.
使用StreamingDrmSessionManager
类时,需要注入一个MediaDrmCallback
到构造器中。MediaDrmCallback 用于获取秘钥。你需要实现这个接口,并通过网络连接你的授权服务器来获取密钥。demo项目中的WidevineTestMediaDrmCallback
类就实现了通过网络连接 Widevine测试服务器以获取密钥。