An extensible media player exposing traditional high-level media player functionality, such as
the ability to prepare, play, pause and seek.
Topics covered here are:
- Assumptions and player composition
- Threading model
- Player state
Assumptions and player construction
The implementation is designed make no assumptions about (and hence impose no restrictions
on) the type of the media being played, how and where it is stored, or how it is rendered.
Rather than implementing the loading and rendering of media directly,
delegates this work to one or more
TrackRenderers, which are injected when the player
is prepared. Hence
ExoPlayer is capable of loading and playing any media for which a
TrackRenderer implementation can be provided.
MediaCodecVideoTrackRenderer can be used for
the common cases of rendering audio and video. These components in turn require an
SampleSource to be injected through their constructors, where upstream
is defined to denote a component that is closer to the source of the media. This pattern of
upstream dependency injection is actively encouraged, since it means that the functionality of
the player is built up through the composition of components that can easily be exchanged for
alternate implementations. For example a
SampleSource implementation may require a
further upstream data loading component to be injected through its constructor, with different
implementations enabling the loading of data from various sources.
The figure below shows the
ExoPlayer threading model.
- It is recommended that instances are created and accessed from a single application thread.
An application's main thread is ideal. Accessing an instance from multiple threads is
discouraged, however if an application does wish to do this then it may do so provided that it
ensures accesses are synchronized.
Listeners are invoked on the thread that created the
- An internal playback thread is responsible for managing playback and invoking the
TrackRenderers in order to load and play the media.
TrackRenderer implementations (or any upstream components that they depend on) may
use additional background threads (e.g. to load data). These are implementation specific.
The components of an
ExoPlayer's state can be divided into two distinct groups. State
#getPlayWhenReady() is only ever
changed by invoking the player's methods, and are never changed as a result of operations that
have been performed asynchronously by the playback thread. In contrast, the playback state
#getPlaybackState() is only ever changed as a result of operations
completing on the playback thread, as illustrated below.
The possible playback state transitions are shown below. Transitions can be triggered either
by changes in the state of the
TrackRenderers being used, or as a result of
#release() being invoked.