Commit ee6ba28d by ibaker Committed by Ian Baker

Redirect exoplayer.dev pages to media3 on d.android.com

Two pages don't have media3 equivalents, so are redirected to
https://developer.android.com/media/media3/exoplayer instead:

* https://exoplayer.dev/design-documents.html
* https://exoplayer.dev/pros-and-cons.html

PiperOrigin-RevId: 621497706
(cherry picked from commit e6e7240df758b9ed6d757c77ca8855edd6aa7976)
parent d90d7290
--- ---
title: Battery consumption permalink: /battery-consumption.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/battery-consumption
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
## How important is battery consumption due to media playback? ##
Avoiding unnecessary battery consumption is an important aspect of developing a
good Android application. Media playback can be a major cause of battery drain,
however its importance for a particular app heavily depends on its usage
patterns. If an app is only used to play small amounts of media each day, then
the corresponding battery consumption will only be a small percentage of the
total consumption of the device. In such it makes sense to prioritize feature
set and reliability over optimizing for battery when selecting which player to
use. On the other hand, if an app is often used to play large amounts of media
each day, then optimizing for battery consumption should be weighted more
heavily when choosing between a number of viable options.
## How power efficient is ExoPlayer? ##
The diverse nature of the Android device and media content ecosystems means that
it’s difficult to make widely applicable statements about ExoPlayer’s battery
consumption, and in particular how it compares with Android’s MediaPlayer API.
Both absolute and relative performance vary by hardware, Android version and the
media being played. Hence the information provided below should be treated as
guidance only.
### Video playback ###
For video playback, our measurements show that ExoPlayer and MediaPlayer draw
similar amounts of power. The power required for the display and decoding the
video stream are the same in both cases, and these account for most of the power
consumed during playback.
Regardless of which media player is used, choosing between `SurfaceView` and
`TextureView` for output can have a significant impact on power consumption.
`SurfaceView` is more power efficient, with `TextureView` increasing total power
draw during video playback by as much as 30% on some devices. `SurfaceView`
should therefore be preferred where possible. Read more about choosing between
`SurfaceView` and `TextureView`
[here]({{ site.baseurl }}/ui-components.html#choosing-a-surface-type).
Below are some power consumption measurements for playing 1080p and 480p video
on Pixel 2, measured using a [Monsoon power monitor][]. As mentioned above,
these numbers should not be used to draw general conclusions about power
consumption across the Android device and media content ecosystems.
| | MediaPlayer | ExoPlayer |
|-------------------|:-----------:|:----------|
| SurfaceView 1080p | 202 mAh | 214 mAh |
| TextureView 1080p | 219 mAh | 221 mAh |
| SurfaceView 480p | 194 mAh | 207 mAh |
| TextureView 480p | 212 mAh | 215 mAh |
### Audio playback ###
For short audio playbacks or playbacks when the screen is on, using ExoPlayer
does not have a significant impact on power compared to using MediaPlayer.
For long playbacks with the screen off, ExoPlayer's audio offload mode needs to
be used or ExoPlayer may consume significantly more power than MediaPlayer.
Audio offload allows audio processing to be offloaded from the CPU to a
dedicated signal processor. It is used by default by MediaPlayer but not
ExoPlayer. ExoPlayer introduced support for audio offload in 2.12 as an
experimental feature. See `DefaultRenderersFactory.setEnableAudioOffload` and
`ExoPlayer.experimentalSetOffloadSchedulingEnabled` for more details on how
to enable it.
Due to SDK API limitations, ExoPlayer's audio offload mode is only available on
devices running Android 10 and above. MediaPlayer can use audio offload on
devices running earlier versions of Android. Whether the increased robustness,
flexibility and feature set that ExoPlayer provides over MediaPlayer is worth
the increased power consumption for audio only use cases on older devices is
something an app developer must decide, taking their requirements and app usage
patterns into account.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/battery-consumption
[Monsoon power monitor]: https://www.msoon.com/battery-configuration
--- ---
title: DASH permalink: /dash.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/dash
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
{% include_relative _page_fragments/supported-formats-dash.md %}
## Using MediaItem ##
To play a DASH stream, you need to depend on the DASH module.
~~~
implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
~~~
{: .language-gradle}
You can then create a `MediaItem` for a DASH MPD URI and pass it to the player.
~~~
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(dashUri));
// Prepare the player.
player.prepare();
~~~
{: .language-java}
If your URI doesn't end with `.mpd`, you can pass `MimeTypes.APPLICATION_MPD`
to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the
content.
ExoPlayer will automatically adapt between representations defined in the
manifest, taking into account both available bandwidth and device capabilities.
## Using DashMediaSource ##
For more customization options, you can create a `DashMediaSource` and pass it
directly to the player instead of a `MediaItem`.
~~~
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a DASH media source pointing to a DASH manifest uri.
MediaSource mediaSource =
new DashMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(dashUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(mediaSource);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Accessing the manifest ##
You can retrieve the current manifest by calling `Player.getCurrentManifest`.
For DASH you should cast the returned object to `DashManifest`. The
`onTimelineChanged` callback of `Player.Listener` is also called whenever
the manifest is loaded. This will happen once for a on-demand content, and
possibly many times for live content. The code snippet below shows how an app
can do something whenever the manifest is loaded.
~~~
player.addListener(
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
Object manifest = player.getCurrentManifest();
if (manifest != null) {
DashManifest dashManifest = (DashManifest) manifest;
// Do something with the manifest.
}
}
});
~~~
{: .language-java}
## Customizing playback ##
ExoPlayer provides multiple ways for you to tailor playback experience to your
app's needs. See the [Customization page][] for examples.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/dash
[Customization page]: {{ site.baseurl }}/customization.html
--- ---
title: Debug logging permalink: /debug-logging.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/debug-logging
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
By default ExoPlayer only logs errors. To log player events, the `EventLogger`
class can be used. The additional logging it provides can be helpful for
understanding what the player is doing, as well as for debugging playback
issues. `EventLogger` implements `AnalyticsListener`, so registering an instance
with an `ExoPlayer` is easy:
```
player.addAnalyticsListener(new EventLogger());
```
{: .language-java}
The easiest way to observe the log is using Android Studio's [logcat tab][]. You
can select your app as debuggable process by the package name (
`com.google.android.exoplayer2.demo` if using the demo app) and tell the logcat
tab to log only for that app by selecting 'show only selected application'. It's
possible to further filter the logging with the expression
`EventLogger|ExoPlayerImpl`, to get only logging from `EventLogger` and the
player itself.
An alternative to using Android Studio's logcat tab is to use the console. For
example:
~~~
adb logcat EventLogger:* ExoPlayerImpl:* *:s
~~~
{: .language-shell}
### Player information ###
The `ExoPlayerImpl` class delivers two important lines about the player version,
the device and OS the app is running on and the modules of ExoPlayer that have
been loaded:
```
ExoPlayerImpl: Release 2cd6e65 [ExoPlayerLib/2.12.0] [marlin, Pixel XL, Google, 26] [goog.exo.core, goog.exo.ui, goog.exo.dash]
ExoPlayerImpl: Init 2e5194c [ExoPlayerLib/2.12.0] [marlin, Pixel XL, Google, 26]
```
### Playback state ###
Player state changes are logged in lines like the ones below:
```
EventLogger: playWhenReady [eventTime=0.00, mediaPos=0.00, window=0, true, USER_REQUEST]
EventLogger: state [eventTime=0.01, mediaPos=0.00, window=0, BUFFERING]
EventLogger: state [eventTime=0.93, mediaPos=0.00, window=0, period=0, READY]
EventLogger: isPlaying [eventTime=0.93, mediaPos=0.00, window=0, period=0, true]
EventLogger: playWhenReady [eventTime=9.40, mediaPos=8.40, window=0, period=0, false, USER_REQUEST]
EventLogger: isPlaying [eventTime=9.40, mediaPos=8.40, window=0, period=0, false]
EventLogger: playWhenReady [eventTime=10.40, mediaPos=8.40, window=0, period=0, true, USER_REQUEST]
EventLogger: isPlaying [eventTime=10.40, mediaPos=8.40, window=0, period=0, true]
EventLogger: state [eventTime=20.40, mediaPos=18.40, window=0, period=0, ENDED]
EventLogger: isPlaying [eventTime=20.40, mediaPos=18.40, window=0, period=0, false]
```
In this example playback starts 0.93 seconds after the player is prepared. The
user pauses playback after 9.4 seconds, and resumes playback one second later at
10.4 seconds. Playback ends ten seconds later at 20.4 seconds. The common
elements within the square brackets are:
* `[eventTime=float]`: The wall clock time since player creation.
* `[mediaPos=float]`: The current playback position.
* `[window=int]`: The current window index.
* `[period=int]`: The current period in that window.
The final elements in each line indicate the value of the state being reported.
### Media tracks ###
Track information is logged when the available or selected tracks change. This
happens at least once at the start of playback. The example below shows track
logging for an adaptive stream:
```
EventLogger: tracks [eventTime=0.30, mediaPos=0.00, window=0, period=0,
EventLogger: group [
EventLogger: [X] Track:0, id=133, mimeType=video/avc, bitrate=261112, codecs=avc1.4d4015, res=426x240, fps=30.0, supported=YES
EventLogger: [X] Track:1, id=134, mimeType=video/avc, bitrate=671331, codecs=avc1.4d401e, res=640x360, fps=30.0, supported=YES
EventLogger: [X] Track:2, id=135, mimeType=video/avc, bitrate=1204535, codecs=avc1.4d401f, res=854x480, fps=30.0, supported=YES
EventLogger: [X] Track:3, id=160, mimeType=video/avc, bitrate=112329, codecs=avc1.4d400c, res=256x144, fps=30.0, supported=YES
EventLogger: [ ] Track:4, id=136, mimeType=video/avc, bitrate=2400538, codecs=avc1.4d401f, res=1280x720, fps=30.0, supported=NO_EXCEEDS_CAPABILITIES
EventLogger: ]
EventLogger: group [
EventLogger: [ ] Track:0, id=139, mimeType=audio/mp4a-latm, bitrate=48582, codecs=mp4a.40.5, channels=2, sample_rate=22050, supported=YES
EventLogger: [X] Track:1, id=140, mimeType=audio/mp4a-latm, bitrate=127868, codecs=mp4a.40.2, channels=2, sample_rate=44100, supported=YES
EventLogger: ]
EventLogger: ]
```
In this example, the player has selected four of the five available video
tracks. The fifth video track is not selected because it exceeds the
capabilities of the device, as indicated by `supported=NO_EXCEEDS_CAPABILITIES`.
The player will adapt between the selected video tracks during playback. When
the player adapts from one track to another, it's logged in a line like the one
below:
```
EventLogger: downstreamFormat [eventTime=3.64, mediaPos=3.00, window=0, period=0, id=134, mimeType=video/avc, bitrate=671331, codecs=avc1.4d401e, res=640x360, fps=30.0]
```
This log line indicates that the player switched to the 640x360 resolution video
track three seconds into the media.
### Decoder selection ###
In most cases ExoPlayer renders media using a `MediaCodec` acquired from the
underlying platform. When a decoder is initialized, this is logged in lines like
the ones below:
```
EventLogger: videoDecoderInitialized [0.77, 0.00, window=0, period=0, video, OMX.qcom.video.decoder.avc]
EventLogger: audioDecoderInitialized [0.79, 0.00, window=0, period=0, audio, OMX.google.aac.decoder]
```
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/debug-logging
[logcat tab]: https://developer.android.com/studio/debug/am-logcat
--- ---
title: Design documents permalink: /design-documents.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer
--- ---
To get early feedback from developers, we publish design documents for larger
changes on this page. Feel free to comment on the documents that are still in
"request for comments" (RFC) status. Note that we do not typically update
documents once changes are implemented.
### RFC status ###
* There are no documents in RFC status at this time
### Archive ###
* [Add support for partially fragmented MP4s][] (July 2020)
* [Audio offload][] (April 2020)
* [Sniffing order optimization][] (April 2020)
* [Masking seek state changes][] (March 2020)
* [Frame-accurate pausing][] (January 2020)
* [Index seeking in MP3 streams][] (January 2020)
* [Low-latency live playback][] (September 2019)
* [Unwrapping Nested Metadata][] (September 2019)
* [Playlist API][] (July 2019)
* [Bandwidth estimation analysis][] (July 2019)
[Add support for partially fragmented MP4s]: https://docs.google.com/document/d/1NUheADYlqIVVPT8Ch5UbV8DJHLDoxMoOD_L8mvU8tTM
[Audio offload]: https://docs.google.com/document/d/1r6wi6OtJUaI1QU8QLrLJTZieQBFTN1fyBK4U_PoPp3g
[Sniffing order optimization]: https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ
[Masking seek state changes]: https://docs.google.com/document/d/1XeOduvYus9HfwXtOtoRC185T4PK-L4u7JRmNM46Ee4w
[Frame-accurate pausing]: https://docs.google.com/document/d/1xXGvIMAYDWN4BGUNqAplNN-T7rjrW_1EAVXpyCcAqUI
[Index seeking in MP3 streams]: https://docs.google.com/document/d/1ZtQsCFvi_LiwFqhHWy20dJ1XwHLOXE4BJ5SzXWJ9a9E
[Low-latency live playback]: https://docs.google.com/document/d/1z9qwuP7ff9sf3DZboXnhEF9hzW3Ng5rfJVqlGn8N38k
[Unwrapping Nested Metadata]: https://docs.google.com/document/d/1TS13CVmexaLG1C4TdD-4NkX-BCSr_76FaHVOPo6XP1E
[Playlist API]: https://docs.google.com/document/d/11h0S91KI5TB3NNZUtsCzg0S7r6nyTnF_tDZZAtmY93g
[Bandwidth estimation analysis]: https://docs.google.com/document/d/1e3jVkZ6nxNWgCqTNibqV8uJcKo8d597XVl3nJkY7P8c
--- ---
title: Digital rights management permalink: /drm.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/drm
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
ExoPlayer uses Android's [`MediaDrm`][] API to support DRM protected playbacks.
The minimum Android versions required for different supported DRM schemes, along
with the streaming formats for which they're supported, are:
| DRM scheme | Android version number | Android API level | Supported formats |
|---------|:------------:|:------------:|:---------------------|
| Widevine "cenc" | 4.4 | 19 | DASH, HLS (FMP4 only) |
| Widevine "cbcs" | 7.1 | 25 | DASH, HLS (FMP4 only) |
| ClearKey "cenc" | 5.0 | 21 | DASH |
| PlayReady SL2000 "cenc" | AndroidTV | AndroidTV | DASH, SmoothStreaming, HLS (FMP4 only) |
In order to play DRM protected content with ExoPlayer, the UUID of the DRM
system and the license server URI should be specified
[when building a media item]({{ site.baseurl }}/media-items.html#protected-content).
The player will then use these properties to build a default implementation of
`DrmSessionManager`, called `DefaultDrmSessionManager`, that's suitable for most
use cases. For some use cases additional DRM properties may be necessary, as
outlined in the sections below.
### Key rotation ###
To play streams with rotating keys, pass `true` to
`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media
item.
### Multi-key content ###
Multi-key content consists of multiple streams, where some streams use different
keys than others. Multi-key content can be played in one of two ways, depending
on how the license server is configured.
##### Case 1: License server responds with all keys for the content #####
In this case, the license server is configured so that when it receives a
request for one key, it responds with all keys for the content. This case is
handled by ExoPlayer without the need for any special configuration. Adaptation
between streams (e.g. SD and HD video) is seamless even if they use different
keys.
Where possible, we recommend configuring your license server to behave in this
way. It's the most efficient and robust way to support playback of multikey
content, because it doesn't require the client to make multiple license requests
to access the different streams.
##### Case 2: License server responds with requested key only #####
In this case, the license server is configured to respond with only the key
specified in the request. Multi-key content can be played with this license
server configuration by passing `true` to
`MediaItem.DrmConfiguration.Builder.setMultiSession` when building the media
item.
We do not recommend configuring your license server to behave in this way. It
requires extra license requests to play multi-key content, which is less
efficient and robust than the alternative described above.
### Offline keys ###
An offline key set can be loaded by passing the key set ID to
`MediaItem.DrmConfiguration.Builder.setKeySetId` when building the media item.
This allows playback using the keys stored in the offline key set with the
specified ID.
{% include known-issue-box.html issue-id="3872" description="Only one offline
key set can be specified per playback. As a result, offline playback of
multi-key content is currently supported only when the license server is
configured as described in Case 1 above." %}
### DRM sessions for clear content ###
Use of placeholder `DrmSessions` allows `ExoPlayer` to use the same decoders for
clear content as are used when playing encrypted content. When media contains
both clear and encrypted sections, you may want to use placeholder `DrmSessions`
to avoid re-creation of decoders when transitions between clear and encrypted
sections occur. Use of placeholder `DrmSessions` for audio and video tracks can
be enabled by passing `true` to
`MediaItem.DrmConfiguration.Builder.forceSessionsForAudioAndVideoTracks` when
building the media item.
### Using a custom DrmSessionManager ###
If an app wants to customise the `DrmSessionManager` used for playback, they can
implement a `DrmSessionManagerProvider` and pass this to the
`MediaSource.Factory` which is [used when building the player]. The provider can
choose whether to instantiate a new manager instance each time or not. To always
use the same instance:
~~~
DrmSessionManager customDrmSessionManager =
new CustomDrmSessionManager(/* ... */);
// Pass a drm session manager provider to the media source factory.
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(context)
.setDrmSessionManagerProvider(mediaItem -> customDrmSessionManager);
~~~
{: .language-java}
### Improving playback performance ###
If you're experiencing video stuttering on a device running Android 6 to 11 when
playing DRM protected content, you can try [enabling asynchronous buffer
queueing].
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/drm
[main demo app]: {{ site.release_v2 }}/demos/main
[`MediaDrm`]: {{ site.android_sdk }}/android/media/MediaDrm.html
[used when building the player]: {{ site.baseurl }}/media-sources.html#customizing-media-source-creation
[enabling asynchronous buffer queueing]: {{ site.baseurl }}/customization.html#enabling-asynchronous-buffer-queueing
--- ---
title: Hello world!
redirect_from: redirect_from:
- /guide.html - /guide.html
- /guide-v1.html - /guide-v1.html
- /getting-started.html - /getting-started.html
permalink: /hello-world.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/hello-world
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
Another way to get started is to work through
[the ExoPlayer codelab](https://codelabs.developers.google.com/codelabs/exoplayer-intro/).
{:.info}
For simple use cases, getting started with `ExoPlayer` consists of implementing
the following steps:
1. Add ExoPlayer as a dependency to your project.
1. Create an `ExoPlayer` instance.
1. Attach the player to a view (for video output and user input).
1. Prepare the player with a `MediaItem` to play.
1. Release the player when done.
These steps are described in more detail below. For a complete example, refer to
`PlayerActivity` in the [main demo app][].
## Adding ExoPlayer as a dependency ##
### Add ExoPlayer modules ###
The easiest way to get started using ExoPlayer is to add it as a gradle
dependency in the `build.gradle` file of your app module. The following will add
a dependency to the full library:
~~~
implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
~~~
{: .language-gradle}
where `2.X.X` is your preferred version (the latest version can be found by
consulting the [release notes][]).
As an alternative to the full library, you can depend on only the library
modules that you actually need. For example the following will add dependencies
on the Core, DASH and UI library modules, as might be required for an app that
only plays DASH content:
~~~
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'
~~~
{: .language-gradle}
When depending on individual modules, they must all be the same version. You can
browse the list of available modules on the [Google Maven ExoPlayer page][]. The
full library includes all of the library modules prefixed with `exoplayer-`,
except for `exoplayer-transformer`.
In addition to library modules, ExoPlayer has extension modules that depend on
external libraries to provide additional functionality. Some extensions are
available from the Maven repository, whereas others must be built manually.
Browse the [extensions directory][] and their individual READMEs for details.
### Turn on Java 8 support ###
If not enabled already, you need to turn on Java 8 support in all `build.gradle`
files depending on ExoPlayer, by adding the following to the `android` section:
~~~
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
}
~~~
{: .language-gradle}
### Enable multidex ###
If your Gradle `minSdkVersion` is 20 or lower, you should
[enable multidex](https://developer.android.com/studio/build/multidex) in order
to prevent build errors.
## Creating the player ##
You can create an `ExoPlayer` instance using `ExoPlayer.Builder`, which provides
a range of customization options. The code below is the simplest example of
creating an instance.
~~~
ExoPlayer player = new ExoPlayer.Builder(context).build();
~~~
{: .language-java}
### A note on threading ###
ExoPlayer instances must be accessed from a single application thread. For the
vast majority of cases this should be the application's main thread. Using the
application's main thread is a requirement when using ExoPlayer's UI components
or the IMA extension.
The thread on which an ExoPlayer instance must be accessed can be explicitly
specified by passing a `Looper` when creating the player. If no `Looper` is
specified, then the `Looper` of the thread that the player is created on is
used, or if that thread does not have a `Looper`, the `Looper` of the
application's main thread is used. In all cases the `Looper` of the thread from
which the player must be accessed can be queried using
`Player.getApplicationLooper`.
If you see `IllegalStateException` being thrown with the message "Player is
accessed on the wrong thread", then some code in your app is accessing an
`ExoPlayer` instance on the wrong thread (the exception's stack trace shows you
where).
{:.info}
For more information about ExoPlayer's threading model, see the
["Threading model" section of the ExoPlayer Javadoc][].
## Attaching the player to a view ##
The ExoPlayer library provides a range of pre-built UI components for media
playback. These include `StyledPlayerView`, which encapsulates a
`StyledPlayerControlView`, a `SubtitleView`, and a `Surface` onto which video is
rendered. A `StyledPlayerView` can be included in your application's layout xml.
Binding the player to the view is as simple as:
~~~
// Bind the player to the view.
playerView.setPlayer(player);
~~~
{: .language-java}
You can also use `StyledPlayerControlView` as a standalone component, which is
useful for audio only use cases.
Use of ExoPlayer's pre-built UI components is optional. For video applications
that implement their own UI, the target `SurfaceView`, `TextureView`,
`SurfaceHolder` or `Surface` can be set using `ExoPlayer`'s
`setVideoSurfaceView`, `setVideoTextureView`, `setVideoSurfaceHolder` and
`setVideoSurface` methods respectively. `ExoPlayer`'s `addTextOutput` method can
be used to receive captions that should be rendered during playback.
## Populating the playlist and preparing the player ##
In ExoPlayer every piece of media is represented by a `MediaItem`. To play a
piece of media you need to build a corresponding `MediaItem`, add it to the
player, prepare the player, and call `play` to start the playback:
~~~
// Build the media item.
MediaItem mediaItem = MediaItem.fromUri(videoUri);
// Set the media item to be played.
player.setMediaItem(mediaItem);
// Prepare the player.
player.prepare();
// Start the playback.
player.play();
~~~
{: .language-java}
ExoPlayer supports playlists directly, so it's possible to prepare the player
with multiple media items to be played one after the other:
~~~
// Build the media items.
MediaItem firstItem = MediaItem.fromUri(firstVideoUri);
MediaItem secondItem = MediaItem.fromUri(secondVideoUri);
// Add the media items to be played.
player.addMediaItem(firstItem);
player.addMediaItem(secondItem);
// Prepare the player.
player.prepare();
// Start the playback.
player.play();
~~~
{: .language-java}
The playlist can be updated during playback without the need to prepare the
player again. Read more about populating and manipulating the playlist on the
[Playlists page][]. Read more about the different options available when
building media items, such as clipping and attaching subtitle files, on the
[Media items page][].
Prior to ExoPlayer 2.12, the player needed to be given a `MediaSource` rather
than media items. From 2.12 onwards, the player converts media items to the
`MediaSource` instances that it needs internally. Read more about this process
and how it can be customized on the [Media sources page][]. It's still possible
to provide `MediaSource` instances directly to the player using
`ExoPlayer.setMediaSource(s)` and `ExoPlayer.addMediaSource(s)`.
{:.info}
## Controlling the player ##
Once the player has been prepared, playback can be controlled by calling methods
on the player. Some of the most commonly used methods are listed below.
* `play` and `pause` start and pause playback.
* `seekTo` allows seeking within the media.
* `hasPrevious`, `hasNext`, `previous` and `next` allow navigating through the
playlist.
* `setRepeatMode` controls if and how media is looped.
* `setShuffleModeEnabled` controls playlist shuffling.
* `setPlaybackParameters` adjusts playback speed and audio pitch.
If the player is bound to a `StyledPlayerView` or `StyledPlayerControlView`,
then user interaction with these components will cause corresponding methods on
the player to be invoked.
## Releasing the player ##
It's important to release the player when it's no longer needed, so as to free
up limited resources such as video decoders for use by other applications. This
can be done by calling `ExoPlayer.release`.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/hello-world
[main demo app]: {{ site.release_v2 }}/demos/main/
[extensions directory]: {{ site.release_v2 }}/extensions/
[release notes]: {{ site.release_v2 }}/RELEASENOTES.md
["Threading model" section of the ExoPlayer Javadoc]: {{ site.exo_sdk }}/ExoPlayer.html
[Playlists page]: {{ site.baseurl }}/playlists.html
[Media items page]: {{ site.baseurl }}/media-items.html
[Media sources page]: {{ site.baseurl }}/media-sources.html
[Google Maven ExoPlayer page]: https://maven.google.com/web/index.html#com.google.android.exoplayer
--- ---
title: HLS permalink: /hls.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/hls
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
{% include_relative _page_fragments/supported-formats-hls.md %}
## Using MediaItem ##
To play an HLS stream, you need to depend on the HLS module.
~~~
implementation 'com.google.android.exoplayer:exoplayer-hls:2.X.X'
~~~
{: .language-gradle}
You can then create a `MediaItem` for an HLS playlist URI and pass it to the
player.
~~~
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri));
// Prepare the player.
player.prepare();
~~~
{: .language-java}
If your URI doesn't end with `.m3u8`, you can pass `MimeTypes.APPLICATION_M3U8`
to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the
content.
The URI of the media item may point to either a media playlist or a multivariant
playlist. If the URI points to a multivariant playlist that declares multiple
`#EXT-X-STREAM-INF` tags then ExoPlayer will automatically adapt between
variants, taking into account both available bandwidth and device capabilities.
## Using HlsMediaSource ##
For more customization options, you can create a `HlsMediaSource` and pass it
directly to the player instead of a `MediaItem`.
~~~
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(hlsUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(hlsMediaSource);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Accessing the manifest ##
You can retrieve the current manifest by calling `Player.getCurrentManifest`.
For HLS you should cast the returned object to `HlsManifest`. The
`onTimelineChanged` callback of `Player.Listener` is also called whenever
the manifest is loaded. This will happen once for a on-demand content, and
possibly many times for live content. The code snippet below shows how an app
can do something whenever the manifest is loaded.
~~~
player.addListener(
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
Object manifest = player.getCurrentManifest();
if (manifest != null) {
HlsManifest hlsManifest = (HlsManifest) manifest;
// Do something with the manifest.
}
}
});
~~~
{: .language-java}
## Customizing playback ##
ExoPlayer provides multiple ways for you to tailor playback experience to your
app's needs. See the [Customization page][] for examples.
### Disabling chunkless preparation ###
By default, ExoPlayer will use chunkless preparation. This means that ExoPlayer
will only use the information in the multivariant playlist to prepare the
stream, which works if the `#EXT-X-STREAM-INF` tags contain the `CODECS`
attribute.
You may need to disable this feature if your media segments contain muxed
closed-caption tracks that are not declared in the multivariant playlist with a
`#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS` tag. Otherwise, these closed-caption tracks
won't be detected and played. You can disable chunkless preparation in the
`HlsMediaSource.Factory` as shown in the following snippet. Note that this
will increase start up time as ExoPlayer needs to download a media segment to
discover these additional tracks and it is preferable to declare the
closed-caption tracks in the multivariant playlist instead.
~~~
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(false)
.createMediaSource(MediaItem.fromUri(hlsUri));
~~~
{: .language-java}
## Creating high quality HLS content ##
In order to get the most out of ExoPlayer, there are certain guidelines you can
follow to improve your HLS content. Read our [Medium post about HLS playback in
ExoPlayer][] for a full explanation. The main points are:
* Use precise segment durations.
* Use a continuous media stream; avoid changes in the media structure across
segments.
* Use the `#EXT-X-INDEPENDENT-SEGMENTS` tag.
* Prefer demuxed streams, as opposed to files that include both video and audio.
* Include all information you can in the Multivariant Playlist.
The following guidelines apply specifically for live streams:
* Use the `#EXT-X-PROGRAM-DATE-TIME` tag.
* Use the `#EXT-X-DISCONTINUITY-SEQUENCE` tag.
* Provide a long live window. One minute or more is great.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/hls
[HlsMediaSource]: {{ site.exo_sdk }}/source/hls/HlsMediaSource.html
[HTTP Live Streaming]: https://tools.ietf.org/html/rfc8216
[Customization page]: {{ site.baseurl }}/customization.html
[Medium post about HLS playback in ExoPlayer]: https://medium.com/google-exoplayer/hls-playback-in-exoplayer-a33959a47be7
--- ---
layout: article permalink: /index.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
ExoPlayer is an application level media player for Android. It provides an
alternative to Android’s MediaPlayer API for playing audio and video both
locally and over the Internet. ExoPlayer supports features not currently
supported by Android’s MediaPlayer API, including DASH and SmoothStreaming
adaptive playbacks. Unlike the MediaPlayer API, ExoPlayer is easy to customize
and extend, and can be updated through Play Store application updates.
This website provides a wealth of information to help you get started. In
addition, you can:
* Learn how to add ExoPlayer to your app by [completing the codelab][] or
reading the [Hello world][] documentation.
* Read news, hints and tips on our [developer blog][].
* Read the latest [release notes][].
* Browse the library [Javadoc][].
* Browse the source code for the [latest release][] and current [tip of tree][].
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer
[completing the codelab]: https://codelabs.developers.google.com/codelabs/exoplayer-intro/
[Hello world]: {{ site.baseurl }}/hello-world.html
[developer blog]: https://medium.com/google-exoplayer
[release notes]: {{ site.release_v2 }}/RELEASENOTES.md
[Javadoc]: {{ site.baseurl }}/doc/reference
[latest release]: {{ site.release_v2 }}
[tip of tree]: https://github.com/google/ExoPlayer/tree/dev-v2
--- ---
title: Media items permalink: /media-items.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/media-items
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
The [playlist API][] is based on `MediaItem`s, which can be conveniently built
using `MediaItem.Builder`. Inside the player, media items are converted into
playable `MediaSource`s by a `MediaSource.Factory`. Without
[custom configuration]({{ site.baseurl }}/media-sources.html#customizing-media-source-creation),
this conversion is carried out by a `DefaultMediaSourceFactory`, which is
capable of building complex media sources corresponding to the properties of the
media item. Some of the properties that can be set on media items are outlined
below.
## Simple media items ##
A media item consisting only of the stream URI can be built with the `fromUri`
convenience method:
~~~
MediaItem mediaItem = MediaItem.fromUri(videoUri);
~~~
{: .language-java}
For all other cases a `MediaItem.Builder` can be used. In the example below, a
media item is built with an ID and some attached metadata:
~~~
MediaItem mediaItem = new MediaItem.Builder()
.setUri(videoUri)
.setMediaId(mediaId)
.setTag(metadata)
.build();
~~~
{: .language-java}
Attaching metadata can be useful for
[updating your app's UI]({{ site.baseurl }}/playlists.html#detecting-when-playback-transitions-to-another-media-item)
when playlist transitions occur.
## Handling non-standard file extensions
The ExoPlayer library provides adaptive media sources for DASH, HLS and
SmoothStreaming. If the URI of such an adaptive media item ends with a standard
file extension, the corresponding media source is automatically created. If the
URI has a non-standard extension or no extension at all, then the MIME type can
be set explicitly to indicate the type of the media item:
~~~
// Use the explicit MIME type to build an HLS media item.
MediaItem mediaItem = new MediaItem.Builder()
.setUri(hlsUri)
.setMimeType(MimeTypes.APPLICATION_M3U8)
.build();
~~~
{: .language-java}
For progressive media streams a MIME type is not required.
## Protected content ##
For protected content, the media item's DRM properties should be set:
~~~
MediaItem mediaItem = new MediaItem.Builder()
.setUri(videoUri)
.setDrmConfiguration(
new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
.setLicenseUri(licenseUri)
.setMultiSession(true)
.setLicenseRequestHeaders(httpRequestHeaders)
.build())
.build();
~~~
{: .language-java}
This example builds a media item for Widevine protected content. Inside the
player, `DefaultMediaSourceFactory` will pass these properties to a
`DrmSessionManagerProvider` to obtain a `DrmSessionManager`, which is then
injected into the created `MediaSource`. DRM behaviour can be
[further customized]({{ site.baseurl }}/drm.html#using-a-custom-drmsessionmanager)
to your needs.
## Sideloading subtitle tracks ##
To sideload subtitle tracks, `MediaItem.Subtitle` instances can be added when
when building a media item:
~~~
MediaItem.SubtitleConfiguration subtitle =
new MediaItem.SubtitleConfiguration.Builder(subtitleUri)
.setMimeType(mimeType) // The correct MIME type (required).
.setLanguage(language) // The subtitle language (optional).
.setSelectionFlags(selectionFlags) // Selection flags for the track (optional).
.build();
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(videoUri)
.setSubtitleConfigurations(ImmutableList.of(subtitle))
.build();
~~~
{: .language-java}
Internally, `DefaultMediaSourceFactory` will use a `MergingMediaSource` to
combine the content media source with a `SingleSampleMediaSource` for each
subtitle track. `DefaultMediaSourceFactory` does not support sideloading
subtitles for multi-period DASH.
## Clipping a media stream ##
It's possible to clip the content referred to by a media item by setting custom
start and end positions:
~~~
MediaItem mediaItem =
new MediaItem.Builder()
.setUri(videoUri)
.setClippingConfiguration(
new ClippingConfiguration.Builder()
.setStartPositionMs(startPositionMs)
.setEndPositionMs(endPositionMs)
.build())
.build();
~~~
{: .language-java}
Internally, `DefaultMediaSourceFactory` will use a `ClippingMediaSource` to wrap
the content media source. There are additional clipping properties. See the
[`MediaItem.Builder` Javadoc][] for more details.
When clipping the start of a video file, try to align the start position with a
keyframe if possible. If the start position is not aligned with a keyframe then
the player will need to decode and discard data from the previous keyframe up to
the start position before playback can begin. This will introduce a short delay
at the start of playback, including when the player transitions to playing a
clipped media source as part of a playlist or due to looping.
{:.info}
## Ad insertion ##
To insert ads, a media item's ad tag URI property should be set:
~~~
MediaItem mediaItem = new MediaItem.Builder()
.setUri(videoUri)
.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(adTagUri).build())
.build();
~~~
{: .language-java}
Internally, `DefaultMediaSourceFactory` will wrap the content media source in an
`AdsMediaSource` to insert ads as defined by the ad tag. For this to work, the
the player also needs to have its `DefaultMediaSourceFactory`
[configured accordingly]({{ site.baseurl }}/ad-insertion.html#declarative-ad-support).
{% include media3-known-issue-box.html issue-id="185" description="Subtitles, clipping and ad insertion are only supported if you use `DefaultMediaSourceFactory`." %}
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/media-items
[playlist API]: {{ site.baseurl }}/playlists.html
[`MediaItem.Builder` Javadoc]: {{ site.exo_sdk }}/MediaItem.Builder.html
--- ---
title: Media sources permalink: /media-sources.html
redirect_from: redirect_to:
- /mediasource.html - https://developer.android.com/media/media3/exoplayer/media-sources
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
In ExoPlayer every piece of media is represented by a `MediaItem`. However
internally, the player needs `MediaSource` instances to play the content. The
player creates these from media items using a `MediaSource.Factory`.
By default the player uses a `DefaultMediaSourceFactory`, which can create
instances of the following content `MediaSource` implementations:
* `DashMediaSource` for [DASH][].
* `SsMediaSource` for [SmoothStreaming][].
* `HlsMediaSource` for [HLS][].
* `ProgressiveMediaSource` for [regular media files][].
* `RtspMediaSource` for [RTSP][].
`DefaultMediaSourceFactory` can also create more complex media sources depending
on the properties of the corresponding media items. This is described in more
detail on the [Media items page]({{ site.baseurl }}/media-items.html).
For apps that need media source setups that are not supported by the
default configuration of the player, there are several options for
customization.
## Customizing media source creation ##
When building the player, a `MediaSource.Factory` can be injected. For example,
if an app wants to insert ads and use a `CacheDataSource.Factory` to support
caching, an instance of `DefaultMediaSourceFactory` can be configured to match
these requirements and injected during player construction:
~~~
MediaSource.Factory mediaSourceFactory =
new DefaultMediaSourceFactory(context)
.setDataSourceFactory(cacheDataSourceFactory)
.setLocalAdInsertionComponents(
adsLoaderProvider, /* adViewProvider= */ playerView);
ExoPlayer player = new ExoPlayer.Builder(context)
.setMediaSourceFactory(mediaSourceFactory)
.build();
~~~
{: .language-java}
The
[`DefaultMediaSourceFactory` JavaDoc]({{ site.exo_sdk }}/source/DefaultMediaSourceFactory.html)
describes the available options in more detail.
It's also possible to inject a custom `MediaSource.Factory` implementation, for
example to support creation of a custom media source type. The factory's
`createMediaSource(MediaItem)` will be called to create a media source for each
media item that is
[added to the playlist]({{ site.baseurl }}/playlists.html).
## Media source based playlist API ##
The [`ExoPlayer`] interface defines additional playlist methods that accept
media sources rather than media items. This makes it possible to bypass the
player's internal `MediaSource.Factory` and pass media source instances to the
player directly:
~~~
// Set a list of media sources as initial playlist.
exoPlayer.setMediaSources(listOfMediaSources);
// Add a single media source.
exoPlayer.addMediaSource(anotherMediaSource);
// Can be combined with the media item API.
exoPlayer.addMediaItem(/* index= */ 3, MediaItem.fromUri(videoUri));
exoPlayer.prepare();
exoPlayer.play();
~~~
{: .language-java}
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/media-sources
[DASH]: {{ site.baseurl }}/dash.html
[SmoothStreaming]: {{ site.baseurl }}/smoothstreaming.html
[HLS]: {{ site.baseurl }}/hls.html
[RTSP]: {{ site.baseurl }}/rtsp.html
[regular media files]: {{ site.baseurl }}/progressive.html
[`ExoPlayer`]: {{ site.exo_sdk }}/ExoPlayer.html
--- ---
title: Network stacks permalink: /network-stacks.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/network-stacks
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
ExoPlayer is commonly used for streaming media over the internet. It supports
multiple network stacks for making its underlying network requests. Your choice
of network stack can have a significant impact on streaming performance.
This page outlines how to configure ExoPlayer to use your network stack of
choice, lists the available options, and provides some guidance on how to choose
a network stack for your application.
## Configuring ExoPlayer to use a specific network stack ##
ExoPlayer loads data through `DataSource` components, which it obtains from
`DataSource.Factory` instances that are injected from application code.
If your application only needs to play http(s) content, selecting a network
stack is as simple as updating any `DataSource.Factory` instances that your
application injects to be instances of the `HttpDataSource.Factory`
that corresponds to the network stack you wish to use. If your application also
needs to play non-http(s) content such as local files, use
~~~
new DefaultDataSource.Factory(
...
/* baseDataSourceFactory= */ new PreferredHttpDataSource.Factory(...));
~~~
{: .language-java}
where `PreferredHttpDataSource.Factory` is the factory corresponding to your
preferred network stack. The `DefaultDataSource.Factory` layer adds in support
for non-http(s) sources such as local files.
The example below shows how to build an `ExoPlayer` that will use the Cronet
network stack and also support playback of non-http(s) content.
~~~
// Given a CronetEngine and Executor, build a CronetDataSource.Factory.
CronetDataSource.Factory cronetDataSourceFactory =
new CronetDataSource.Factory(cronetEngine, executor);
// Wrap the CronetDataSource.Factory in a DefaultDataSource.Factory, which adds
// in support for requesting data from other sources (e.g., files, resources,
// etc).
DefaultDataSource.Factory dataSourceFactory =
new DefaultDataSource.Factory(
context,
/* baseDataSourceFactory= */ cronetDataSourceFactory);
// Inject the DefaultDataSource.Factory when creating the player.
ExoPlayer player =
new ExoPlayer.Builder(context)
.setMediaSourceFactory(
new DefaultMediaSourceFactory(context)
.setDataSourceFactory(dataSourceFactory))
.build();
~~~
{: .language-java}
## Supported network stacks ##
ExoPlayer provides direct support for Cronet, OkHttp and Android's built-in
network stack. It can also be extended to support any other network stack that
works on Android.
### Cronet ###
[Cronet](https://developer.android.com/guide/topics/connectivity/cronet) is the
Chromium network stack made available to Android apps as a library. It takes
advantage of multiple technologies that reduce the latency and increase the
throughput of the network requests that your app needs to work, including those
made by ExoPlayer. It natively supports the HTTP, HTTP/2, and HTTP/3 over QUIC
protocols. Cronet is used by some of the world's biggest streaming applications,
including YouTube.
ExoPlayer supports Cronet via its
[Cronet extension](https://github.com/google/ExoPlayer/tree/dev-v2/extensions/cronet).
Please see the extension's `README.md` for detailed instructions on how to use
it. Note that the Cronet extension is able to use three underlying Cronet
implementations:
1. **Google Play Services:** We recommend using this implementation in most
cases, and falling back to Android's built-in network stack
(i.e., `DefaultHttpDataSource`) if Google Play Services is not available.
1. **Cronet Embedded:** May be a good choice if a large percentage of your users
are in markets where Google Play Services is not widely available, or if you
want to control the exact version of the Cronet implementation being used. The
major disadvantage of Cronet Embedded is that it adds approximately 8MB to
your application.
1. **Cronet Fallback:** The fallback implementation of Cronet implements
Cronet's API as a wrapper around Android's built-in network stack. It should
not be used with ExoPlayer, since using Android's built-in network stack
directly (i.e., by using `DefaultHttpDataSource`) is more efficient.
### OkHttp ###
[OkHttp](https://square.github.io/okhttp/) is another modern network stack that
is widely used by many popular Android applications. It supports HTTP and
HTTP/2, but does not yet support HTTP/3 over QUIC.
ExoPlayer supports OkHttp via its
[OkHttp extension](https://github.com/google/ExoPlayer/tree/dev-v2/extensions/okhttp).
Please see the extension's `README.md` for detailed instructions on how to use
it. When using the OkHttp extension, the network stack is embedded within the
application. This is similar to Cronet Embedded, however OkHttp is significantly
smaller, adding under 1MB to your application.
### Android's built-in network stack ###
ExoPlayer supports use of Android's built-in network stack with
`DefaultHttpDataSource` and `DefaultHttpDataSource.Factory`, which are part of
the core ExoPlayer library.
The exact network stack implementation depends on the software running on the
underlying device. On most devices (as of 2021) only HTTP is supported (i.e.,
HTTP/2 and HTTP/3 over QUIC are not supported).
### Other network stacks ###
It's possible for applications to integrate other network stacks with ExoPlayer.
To do this, implement an `HttpDataSource` that wraps the network stack,
together with a corresponding `HttpDataSource.Factory`. ExoPlayer's Cronet and
OkHttp extensions are good examples of how to do this.
When integrating with a pure Java network stack, it's a good idea to implement a
`DataSourceContractTest` to check that your `HttpDataSource` implementation
behaves correctly. `OkHttpDataSourceContractTest` in the OkHttp extension is a
good example of how to do this.
## Choosing a network stack ##
The table below outlines the pros and cons of the network stacks supported by
ExoPlayer.
| Network stack | Protocols | APK size impact | Notes |
|:---|:--:|:--:|:---|
| Cronet (Google Play Services) | HTTP<br>HTTP/2<br>HTTP/3&nbsp;over&nbsp;QUIC | Small<br>(<100KB) | Requires Google Play Services. Cronet version updated automatically |
| Cronet (Embedded) | HTTP<br>HTTP/2<br>HTTP/3&nbsp;over&nbsp;QUIC | Large<br>(~8MB) | Cronet version controlled by app developer |
| Cronet (Fallback) | HTTP<br>(varies&nbsp;by&nbsp;device) | Small<br>(<100KB) | Not recommended for ExoPlayer |
| OkHttp | HTTP<br>HTTP/2 | Small<br>(<1MB) | Requires Kotlin runtime |
| Built-in network stack | HTTP<br>(varies&nbsp;by&nbsp;device) | None | Implementation varies by device |
The HTTP/2 and HTTP/3 over QUIC protocols can significantly improve media
streaming performance. In particular when streaming adaptive media distributed
via a content distribution network (CDN), there are cases for which use of these
protocols can allow CDNs to operate much more efficiently. For this reason,
Cronet's support for both HTTP/2 and HTTP/3 over QUIC (and OkHttp's support for
HTTP/2), is a major benefit compared to using Android's built-in network stack,
provided the servers on which the content is hosted also support these
protocols.
When considering media streaming in isolation, we recommend use of Cronet
provided by Google Play Services, falling back to `DefaultHttpDataSource` if
Google Play Services is unavailable. This recommendation strikes a good balance
between enabling use of HTTP/2 and HTTP/3 over QUIC on most devices, and
avoiding a significant increase in APK size. There are exceptions to this
recommendation. For cases where Google Play Services is likely to be unavailable
on a significant fraction of devices that will be running your application,
using Cronet Embedded or OkHttp may be more appropriate. Use of the built-in
network stack may be acceptable if APK size is a critical concern, or if media
streaming is only a minor part of your application's functionality.
Beyond just media, it's normally a good idea to choose a single network stack
for all of the networking performed by your application. This allows resources
(e.g., sockets) to be efficiently pooled and shared between ExoPlayer and other
application components.
To assist with resource sharing, it's recommended to use a single `CronetEngine`
or `OkHttpClient` instance throughout your application, when using Cronet or
OkHttp respectively.
{:.info}
Since your application will most likely need to perform networking not related
to media playback, your choice of network stack should ultimately factor in our
recommendations above for media streaming in isolation, the requirements of any
other components that perform networking, and their relative importance to your
application.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/network-stacks
--- ---
title: OEM testing permalink: /oems.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/oems
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
ExoPlayer is used by a large number of Android applications. As an OEM, it's
important to ensure that ExoPlayer works correctly both on new devices, and on
new platform builds for existing devices. This page describes compatibility
tests that we recommend running before shipping a device or platform OTA, and
some of the common failure modes encountered when running them.
## Running the tests ##
To run ExoPlayer's playback tests, first check out the latest release of
ExoPlayer from [GitHub][]. You can then run the tests from the command line or
Android Studio.
### Command line ###
From the root directory, build and install the playback tests:
~~~
./gradlew :playbacktests:installDebug
~~~
{: .language-shell}
Next, run the playback tests in the GTS package:
~~~
adb shell am instrument -w -r -e debug false \
-e package com.google.android.exoplayer2.playbacktests.gts \
com.google.android.exoplayer2.playbacktests.test/androidx.test.runner.AndroidJUnitRunner
~~~
{: .language-shell}
Test results appear in STDOUT.
### Android Studio ###
Open the ExoPlayer project, navigate to the `playbacktests` module, right click
on the `gts` folder and run the tests. Test results appear in Android Studio's
Run window.
## Common failure modes ##
Some of the common failure modes encountered when running ExoPlayer's playback
tests are described below, together with the likely root cause in each case. We
will add to this list as further failure modes are discovered.
### Unexpected video buffer presentation timestamp ###
Logcat will contain an error similar to:
~~~
Caused by: java.lang.IllegalStateException: Expected to dequeue video buffer
with presentation timestamp: 134766000. Instead got: 134733000 (Processed
buffers since last flush: 2242).
~~~
{: .language-shell}
This failure is most often caused by the video decoder under test incorrectly
discarding, inserting or re-ordering buffers. In the example above, the test
expected to dequeue a buffer with presentation timestamp `134766000` from
`MediaCodec.dequeueOutputBuffer`, but found that it dequeued a buffer with
presentation timestamp `134733000` instead. We recommend that you check the
decoder implementation when encountering this failure, in particular that it
correctly handles adaptive resolution switches without discarding any buffers.
### Too many dropped buffers ###
Logcat will contain an error similar to:
~~~
junit.framework.AssertionFailedError: Codec(DashTest:Video) was late decoding:
200 buffers. Limit: 25.
~~~
{: .language-shell}
This failure is a performance problem, where the video decoder under test was
late decoding a large number of buffers. In the example above, ExoPlayer dropped
200 buffers because they were late by the time they were dequeued, for a test
that imposes a limit of 25. The most obvious cause is that the video decoder
is too slow decoding buffers. If the failures only occur for the subset of tests
that play Widevine protected content, it's likely that the platform operations
for buffer decryption are too slow. We recommend checking the performance of
these components, and looking at whether any optimizations can be made to speed
them up.
### Native window could not be authenticated ###
Logcat will contain an error similar to:
~~~
SurfaceUtils: native window could not be authenticated
ExoPlayerImplInternal: Internal runtime error.
ExoPlayerImplInternal: android.media.MediaCodec$CodecException: Error 0xffffffff
~~~
{: .language-shell}
This failure is indicative of the platform failing to correctly set the secure
bit flag.
### Test timed out ###
Logcat will contain an error similar to:
~~~
AssertionFailedError: Test timed out after 300000 ms.
~~~
{: .language-shell}
This failure is most often caused by poor network connectivity during the test
run. If the device appears to have good network connectivity then it's possible
that the test is getting stuck calling into a platform component (e.g.
`MediaCodec`, `MediaDrm`, `AudioTrack` etc). Inspect the call stacks of the
threads in the test process to establish whether this is the case.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/oems
[GitHub]: https://github.com/google/ExoPlayer
--- ---
title: Playlists permalink: /playlists.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/playlists
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
The playlist API is defined by the `Player` interface, which is implemented by
all `ExoPlayer` implementations. It enables sequential playback of multiple
media items. The following example shows how to start playback of a playlist
containing two videos:
~~~
// Build the media items.
MediaItem firstItem = MediaItem.fromUri(firstVideoUri);
MediaItem secondItem = MediaItem.fromUri(secondVideoUri);
// Add the media items to be played.
player.addMediaItem(firstItem);
player.addMediaItem(secondItem);
// Prepare the player.
player.prepare();
// Start the playback.
player.play();
~~~
{: .language-java}
Transitions between items in a playlist are seamless. There's no requirement
that they're of the same format (e.g., it’s fine for a playlist to contain both
H264 and VP9 videos). They may even be of different types (e.g., it’s fine for a
playlist to contain both videos and audio only streams). It's allowed to use the
same `MediaItem` multiple times within a playlist.
## Modifying the playlist
It's possible to dynamically modify a playlist by adding, moving and removing
media items. This can be done both before and during playback by calling the
corresponding playlist API methods:
~~~
// Adds a media item at position 1 in the playlist.
player.addMediaItem(/* index= */ 1, MediaItem.fromUri(thirdUri));
// Moves the third media item from position 2 to the start of the playlist.
player.moveMediaItem(/* currentIndex= */ 2, /* newIndex= */ 0);
// Removes the first item from the playlist.
player.removeMediaItem(/* index= */ 0);
~~~
{: .language-java}
Replacing and clearing the entire playlist are also supported:
~~~
// Replaces the playlist with a new one.
List<MediaItem> newItems = ImmutableList.of(
MediaItem.fromUri(fourthUri),
MediaItem.fromUri(fifthUri));
player.setMediaItems(newItems, /* resetPosition= */ true);
// Clears the playlist. If prepared, the player transitions to the ended state.
player.clearMediaItems();
~~~
{: .language-java}
The player automatically handles modifications during playback in the correct
way. For example if the currently playing media item is moved, playback is not
interrupted and its new successor will be played upon completion. If the
currently playing `MediaItem` is removed, the player will automatically move to
playing the first remaining successor, or transition to the ended state if no
such successor exists.
## Querying the playlist
The playlist can be queried using `Player.getMediaItemCount` and
`Player.getMediaItemAt`. The currently playing media item can be queried
by calling `Player.getCurrentMediaItem`. There are also other convenience
methods like `Player.hasNextMediaItem` or `Player.getNextMediaItemIndex` to
simplify navigation in the playlist.
## Repeat modes
The player supports 3 repeat modes that can be set at any time with
`Player.setRepeatMode`:
* `Player.REPEAT_MODE_OFF`: The playlist isn't repeated and the player will
transition to `Player.STATE_ENDED` once the last item in the playlist has
been played.
* `Player.REPEAT_MODE_ONE`: The current item is repeated in an endless loop.
Methods like `Player.seekToNextMediaItem` will ignore this and seek to the
next item in the list, which will then be repeated in an endless loop.
* `Player.REPEAT_MODE_ALL`: The entire playlist is repeated in an endless loop.
## Shuffle mode
Shuffle mode can be enabled or disabled at any time with
`Player.setShuffleModeEnabled`. When in shuffle mode, the player will play the
playlist in a precomputed, randomized order. All items will be played once and
the shuffle mode can also be combined with `Player.REPEAT_MODE_ALL` to repeat
the same randomized order in an endless loop. When shuffle mode is turned off,
playback continues from the current item at its original position in the
playlist.
Note that the indices as returned by methods like
`Player.getCurrentMediaItemIndex` always refer to the original, unshuffled
order. Similarly, `Player.seekToNextMediaItem` will not play the item at
`player.getCurrentMediaItemIndex() + 1`, but the next item according to the
shuffle order. Inserting new items in the playlist or removing items will keep
the existing shuffled order unchanged as far as possible.
### Setting a custom shuffle order
By default the player supports shuffling by using the `DefaultShuffleOrder`.
This can be customized by providing a custom shuffle order implementation, or by
setting a custom order in the `DefaultShuffleOrder` constructor:
~~~
// Set a custom shuffle order for the 5 items currently in the playlist:
exoPlayer.setShuffleOrder(
new DefaultShuffleOrder(new int[] {3, 1, 0, 4, 2}, randomSeed));
// Enable shuffle mode.
exoPlayer.setShuffleModeEnabled(/* shuffleModeEnabled= */ true);
~~~
{: .language-java}
## Identifying playlist items
To identify playlist items, `MediaItem.mediaId` can be set when building the
item:
~~~
// Build a media item with a media ID.
MediaItem mediaItem =
new MediaItem.Builder().setUri(uri).setMediaId(mediaId).build();
~~~
{: .language-java}
If an app does not explicitly define a media ID for a media item, the string
representation of the URI is used.
## Associating app data with playlist items
In addition to an ID, each media item can also be configured with a custom tag,
which can be any app provided object. One use of custom tags is to attach
metadata to each media item:
~~~
// Build a media item with a custom tag.
MediaItem mediaItem =
new MediaItem.Builder().setUri(uri).setTag(metadata).build();
~~~
{: .language-java}
## Detecting when playback transitions to another media item
When playback transitions to another media item, or starts repeating the same
media item, `Listener.onMediaItemTransition(MediaItem,
@MediaItemTransitionReason)` is called. This callback receives the new media
item, along with a `@MediaItemTransitionReason` indicating why the transition
occurred. A common use case for `onMediaItemTransition` is to update the
application's UI for the new media item:
~~~
@Override
public void onMediaItemTransition(
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
updateUiForPlayingMediaItem(mediaItem);
}
~~~
{: .language-java}
If the metadata required to update the UI is attached to each media item using
custom tags, then an implementation might look like:
~~~
@Override
public void onMediaItemTransition(
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
@Nullable CustomMetadata metadata = null;
if (mediaItem != null && mediaItem.localConfiguration != null) {
metadata = (CustomMetadata) mediaItem.localConfiguration.tag;
}
updateUiForPlayingMediaItem(metadata);
}
~~~
{: .language-java}
## Detecting when the playlist changes
When a media item is added, removed or moved,
`Listener.onTimelineChanged(Timeline, @TimelineChangeReason)` is called
immediately with `TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`. This callback is
called even when the player has not yet been prepared.
~~~
@Override
public void onTimelineChanged(
Timeline timeline, @TimelineChangeReason int reason) {
if (reason == TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
// Update the UI according to the modified playlist (add, move or remove).
updateUiForPlaylist(timeline);
}
}
~~~
{: .language-java}
When information such as the duration of a media item in the playlist becomes
available, the `Timeline` will be updated and `onTimelineChanged` will be called
with `TIMELINE_CHANGE_REASON_SOURCE_UPDATE`. Other reasons that can cause a
timeline update include:
* A manifest becoming available after preparing an adaptive media item.
* A manifest being updated periodically during playback of a live stream.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/playlists
--- ---
title: Progressive permalink: /progressive.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/progressive
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
{% include_relative _page_fragments/supported-formats-progressive.md %}
## Using MediaItem ##
To play a progressive stream, create a `MediaItem` with the media URI and pass
it to the player.
~~~
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(progressiveUri));
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Using ProgressiveMediaSource ##
For more customization options, you can create a `ProgressiveMediaSource` and
directly to the player instead of a `MediaItem`.
~~~
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a progressive media source pointing to a stream uri.
MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(progressiveUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(mediaSource);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Customizing playback ##
ExoPlayer provides multiple ways for you to tailor playback experience to your
app's needs. See the [Customization page][] for examples.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/progressive
[Customization page]: {{ site.baseurl }}/customization.html
--- ---
title: Pros and cons permalink: /pros-and-cons.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
ExoPlayer has a number of advantages over Android's built in MediaPlayer:
* Fewer device specific issues and less variation in behavior across different
devices and versions of Android.
* The ability to update the player along with your application. Because
ExoPlayer is a library that you include in your application apk, you have
control over which version you use and you can easily update to a newer
version as part of a regular application update.
* The ability to [customize and extend the player][] to suit your use case.
ExoPlayer is designed specifically with this in mind, and allows many
components to be replaced with custom implementations.
* Support for [playlists][].
* Support for [DASH][] and [SmoothStreaming][], neither of which are supported
by MediaPlayer. Many other formats are also supported. See the [Supported
formats page][] for details.
* Support for advanced [HLS][] features, such as correct handling of
`#EXT-X-DISCONTINUITY` tags.
* Support for [Widevine common encryption][] on Android 4.4 (API level 19) and
higher.
* The ability to quickly integrate with a number of additional libraries using
official extensions. For example the [IMA extension][] makes it easy to
monetize your content using the [Interactive Media Ads SDK][].
It's important to note that there are also some disadvantages:
* For audio only playback on some devices, ExoPlayer may consume significantly
more battery than MediaPlayer. See the [Battery consumption page][] for
details.
* Including ExoPlayer in your app adds a few hundred kilobytes to the APK size.
This is likely only a concern for extremely lightweight apps. Guidance for
shrinking ExoPlayer can be found on the [APK shrinking page][].
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/pros-and-cons
[Supported formats page]: {{ site.baseurl }}/supported-formats.html
[IMA extension]: {{ site.release_v2 }}/extensions/ima
[Interactive Media Ads SDK]: https://developers.google.com/interactive-media-ads
[Battery consumption page]: {{ site.baseurl }}/battery-consumption.html
[customize and extend the player]: {{ site.baseurl }}/customization.html
[APK shrinking page]: {{ site.baseurl }}/shrinking.html
[playlists]: {{ site.baseurl }}/playlists.html
[DASH]: {{ site.baseurl }}/dash.html
[SmoothStreaming]: {{ site.baseurl }}/smoothstreaming.html
[HLS]: {{ site.baseurl }}/hls.html
[Widevine common encryption]: {{ site.baseurl }}/drm.html
--- ---
title: Retrieving metadata permalink: /retrieving-metadata.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/retrieving-metadata
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
## During playback ##
The metadata of the media can be retrieved during playback in multiple ways. The
most straightforward is to listen for the
`Player.Listener#onMediaMetadataChanged` event; this will provide a
[`MediaMetadata`][] object for use, which has fields such as `title` and
`albumArtist`. Alternatively, calling `Player#getMediaMetadata` returns the same
object.
~~~
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
if (mediaMetadata.title != null) {
handleTitle(mediaMetadata.title);
}
}
~~~
{: .language-java}
If an application needs access to specific [`Metadata.Entry`][] objects, then it
should listen to `Player.Listener#onMetadata` (for dynamic metadata delivered
during playback). Alternatively, if there is a need to look at static metadata,
this can be accessed through the `TrackSelections#getFormat`.
`Player#getMediaMetadata` is populated from both of these sources.
## Without playback ##
If playback is not needed, it is more efficient to use the
[`MetadataRetriever`][] to extract the metadata because it avoids having to
create and prepare a player.
~~~
ListenableFuture<TrackGroupArray> trackGroupsFuture =
MetadataRetriever.retrieveMetadata(context, mediaItem);
Futures.addCallback(
trackGroupsFuture,
new FutureCallback<TrackGroupArray>() {
@Override
public void onSuccess(TrackGroupArray trackGroups) {
handleMetadata(trackGroups);
}
@Override
public void onFailure(Throwable t) {
handleFailure(t);
}
},
executor);
~~~
{: .language-java}
## Motion photos ##
It is also possible to extract the metadata of a motion photo, containing the
image and video offset and length for example. The supported formats are:
* JPEG motion photos recorded by Google Pixel and Samsung camera apps. This
format is playable by ExoPlayer and the associated metadata can therefore be
retrieved with a player or using the `MetadataRetriever`.
* HEIC motion photos recorded by Google Pixel and Samsung camera apps. This
format is currently not playable by ExoPlayer and the associated metadata
should therefore be retrieved using the `MetadataRetriever`.
For motion photos, the `TrackGroupArray` obtained with the `MetadataRetriever`
contains a `TrackGroup` with a single `Format` enclosing a
[`MotionPhotoMetadata`][] metadata entry.
~~~
for (int i = 0; i < trackGroups.length; i++) {
TrackGroup trackGroup = trackGroups.get(i);
Metadata metadata = trackGroup.getFormat(0).metadata;
if (metadata != null && metadata.length() == 1) {
Metadata.Entry metadataEntry = metadata.get(0);
if (metadataEntry instanceof MotionPhotoMetadata) {
MotionPhotoMetadata motionPhotoMetadata = (MotionPhotoMetadata) metadataEntry;
handleMotionPhotoMetadata(motionPhotoMetadata);
}
}
}
~~~
{: .language-java}
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/retrieving-metadata
[`MediaMetadata`]: {{ site.exo_sdk }}/MediaMetadata.html
[`Metadata.Entry`]: {{ site.exo_sdk }}/metadata/Metadata.Entry.html
[`MetadataRetriever`]: {{ site.exo_sdk }}/MetadataRetriever.html
[`MotionPhotoMetadata`]: {{ site.exo_sdk }}/metadata/mp4/MotionPhotoMetadata.html
--- ---
title: RTSP permalink: /rtsp.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/rtsp
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
{% include_relative _page_fragments/supported-formats-rtsp.md %}
## Using MediaItem ##
To play an RTSP stream, you need to depend on the RTSP module.
~~~
implementation 'com.google.android.exoplayer:exoplayer-rtsp:2.X.X'
~~~
{: .language-gradle}
You can then create a `MediaItem` for an RTSP URI and pass it to the player.
~~~
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(rtspUri));
// Prepare the player.
player.prepare();
~~~
{: .language-java}
### Authentication ###
ExoPlayer supports playback with RTSP BASIC and DIGEST authentication. To play
protected RTSP content, the `MediaItem`'s URI must be configured with the
authentication info. Specifically, the URI should be of the form
`rtsp://<username>:<password>@<host address>`.
## Using RtspMediaSource ##
For more customization options, you can create an `RtspMediaSource` and pass it
directly to the player instead of a `MediaItem`.
~~~
// Create an RTSP media source pointing to an RTSP uri.
MediaSource mediaSource =
new RtspMediaSource.Factory()
.createMediaSource(MediaItem.fromUri(rtspUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(mediaSource);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Using RTSP behind a NAT (RTP/TCP support) ##
ExoPlayer uses UDP as the default protocol for RTP transport.
When streaming RTSP behind a NAT layer, the NAT might not be able to forward the
incoming RTP/UDP packets to the device. This occurs if the NAT lacks the
necessary UDP port mapping. If ExoPlayer detects there have not been incoming
RTP packets for a while and the playback has not started yet, ExoPlayer tears
down the current RTSP playback session, and retries playback using RTP-over-RTSP
(transmitting RTP packets using the TCP connection opened for RTSP).
The timeout for retrying with TCP can be customized by calling the method
`RtspMediaSource.Factory.setTimeoutMs()`. For example, if the timeout is set to
four seconds, the player will retry with TCP after four seconds of UDP
inactivity.
Setting the timeout also affects the end-of-stream detection logic. That is,
ExoPlayer will report the playback has ended if nothing is received for the
duration of the set timeout. Setting this value too small may lead to an early
end-of-stream signal under poor network conditions.
RTP/TCP offers better compatibility under some network setups. You can configure
ExoPlayer to use RTP/TCP by default with
`RtspMediaSource.Factory.setForceUseRtpTcp()`.
### Passing a custom SocketFactory
Custom `SocketFactory` instances can be useful when particular routing is
required (e.g. when RTSP traffic needs to pass a specific interface, or the
socket needs additional connectivity flags).
By default, `RtspMediaSource` will use Java's standard socket factory
(`SocketFactory.getDefault()`) to create connections to the remote endpoints.
This behavior can be overridden using
`RtspMediaSource.Factory.setSocketFactory()`.
~~~
// Create an RTSP media source pointing to an RTSP uri and override the socket
// factory.
MediaSource mediaSource =
new RtspMediaSource.Factory()
.setSocketFactory(...)
.createMediaSource(MediaItem.fromUri(rtspUri));
~~~
{: .language-java}
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/rtsp
--- ---
title: APK shrinking permalink: /shrinking.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/shrinking
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
Minimizing APK size is an important aspect of developing a good Android
application. This is particularly true when targeting developing markets, and
also when developing an Android Instant App. For such cases it may be desirable
to minimize the size of the ExoPlayer library that's included in the APK. This
page outlines some simple steps that can help to achieve this.
## Use modular dependencies ##
The most convenient way to use ExoPlayer is to add a dependency to the full
library:
~~~
implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
~~~
{: .language-gradle}
However this may pull in more features than your app needs. Instead, depend only
on the library modules that you actually need. For example the following will
add dependencies on the Core, DASH and UI library modules, as might be required
for an app that only plays DASH content:
~~~
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'
~~~
{: .language-gradle}
## Enable code and resource shrinking ##
You should enable code and resource shrinking for your application's release
builds. ExoPlayer is structured in a way that allows code shrinking to
effectively remove unused functionality. For example, for an application that
plays DASH content, ExoPlayer's contribution to APK size can be reduced by
approximately 40% by enabling code shrinking.
Read [Shrink, obfuscate, and optimize your app][] on the Android Developer site
to learn how to enable code and resource shrinking.
## Specify which renderers your app needs ##
By default, the player's renderers will be created using
`DefaultRenderersFactory`. `DefaultRenderersFactory` depends on all of the
`Renderer` implementations provided in the ExoPlayer library, and as a result
none of them will be removed by code shrinking. If you know that your app only
needs a subset of renderers, you can specify your own `RenderersFactory`
instead. For example, an app that only plays audio can define a factory like
this when instantiating `ExoPlayer` instances:
~~~
RenderersFactory audioOnlyRenderersFactory =
(handler, videoListener, audioListener, textOutput, metadataOutput)
-> new Renderer[] {
new MediaCodecAudioRenderer(
context, MediaCodecSelector.DEFAULT, handler, audioListener)
};
ExoPlayer player =
new ExoPlayer.Builder(context, audioOnlyRenderersFactory).build();
~~~
{: .language-java}
This will allow other `Renderer` implementations to be removed by code
shrinking. In this particular example video, text and metadata renderers are
removed.
## Specify which extractors your app needs ##
By default, the player will create `Extractor`s to play progressive media using
`DefaultExtractorsFactory`. `DefaultExtractorsFactory` depends on all of the
`Extractor` implementations provided in the ExoPlayer library, and as a result
none of them will be removed by code shrinking. If you know that your app only
needs to play a small number of container formats, or doesn't play progressive
media at all, you can specify your own `ExtractorsFactory` instead. For example,
an app that only needs to play mp4 files can provide a factory like:
~~~
ExtractorsFactory mp4ExtractorFactory =
() -> new Extractor[] {new Mp4Extractor()};
ExoPlayer player =
new ExoPlayer.Builder(
context,
new DefaultMediaSourceFactory(context, mp4ExtractorFactory))
.build();
~~~
{: .language-java}
This will allow other `Extractor` implementations to be removed by code
shrinking, which can result in a significant reduction in size.
If your app is not playing progressive content at all, you should pass
`ExtractorsFactory.EMPTY` to the `DefaultMediaSourceFactory` constructor, then
pass that `mediaSourceFactory` to the `ExoPlayer.Builder` constructor.
~~~
ExoPlayer player =
new ExoPlayer.Builder(
context,
new DefaultMediaSourceFactory(context, ExtractorsFactory.EMPTY))
.build();
~~~
{: .language-java}
## Custom MediaSource instantiation ##
If your app is using a custom `MediaSource.Factory` and you want
`DefaultMediaSourceFactory` to be removed by code stripping, you should pass
your `MediaSource.Factory` directly to the `ExoPlayer.Builder` constructor.
~~~
ExoPlayer player =
new ExoPlayer.Builder(context, customMediaSourceFactory).build();
~~~
{: .language-java}
If your app is using `MediaSource`s directly instead of `MediaItem`s you should
pass `MediaSource.Factory.UNSUPPORTED` to the `ExoPlayer.Builder` constructor,
to ensure `DefaultMediaSourceFactory` and `DefaultExtractorsFactory` can be
stripped by code shrinking.
~~~
ExoPlayer player =
new ExoPlayer.Builder(context, MediaSource.Factory.UNSUPPORTED).build();
ProgressiveMediaSource mediaSource =
new ProgressiveMediaSource.Factory(
dataSourceFactory, customExtractorsFactory)
.createMediaSource(MediaItem.fromUri(uri));
~~~
{: .language-java}
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/shrinking
[Shrink, obfuscate, and optimize your app]: https://developer.android.com/studio/build/shrink-code
--- ---
title: SmoothStreaming permalink: /smoothstreaming.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/smoothstreaming
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
{% include_relative _page_fragments/supported-formats-smoothstreaming.md %}
## Using MediaItem ##
To play a SmoothStreaming stream, you need to depend on the SmoothStreaming
module.
~~~
implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.X.X'
~~~
{: .language-gradle}
You can then create a `MediaItem` for a SmoothStreaming manifest URI and pass it
to the player.
~~~
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(ssUri));
// Prepare the player.
player.prepare();
~~~
{: .language-java}
If your URI doesn't end with `.ism/Manifest`, you can pass
`MimeTypes.APPLICATION_SS` to `setMimeType` of `MediaItem.Builder` to explicitly
indicate the type of the content.
ExoPlayer will automatically adapt between representations defined in the
manifest, taking into account both available bandwidth and device capabilities.
## Using SsMediaSource ##
For more customization options, you can create a `SsMediaSource` and pass it
directly to the player instead of a `MediaItem`.
~~~
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a SmoothStreaming media source pointing to a manifest uri.
MediaSource mediaSource =
new SsMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(ssUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media source to be played.
player.setMediaSource(mediaSource);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
## Accessing the manifest ##
You can retrieve the current manifest by calling `Player.getCurrentManifest`.
For SmoothStreaming you should cast the returned object to `SsManifest`. The
`onTimelineChanged` callback of `Player.Listener` is also called whenever
the manifest is loaded. This will happen once for a on-demand content, and
possibly many times for live content. The code snippet below shows how an app
can do something whenever the manifest is loaded.
~~~
player.addListener(
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
Object manifest = player.getCurrentManifest();
if (manifest != null) {
SsManifest ssManifest = (SsManifest) manifest;
// Do something with the manifest.
}
}
});
~~~
{: .language-java}
## Customizing playback ##
ExoPlayer provides multiple ways for you to tailor playback experience to your
app's needs. See the [Customization page][] for examples.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/smoothstreaming
[Customization page]: {{ site.baseurl }}/customization.html
--- ---
title: Supported devices permalink: /supported-devices.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/supported-devices
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
The minimum Android versions required for core ExoPlayer use cases are:
| Use case | Android version number | Android API level |
|----------|:------------:|:------------:|
| Audio playback | 4.1 | 16 |
| Video playback | 4.1 | 16 |
| DASH (no DRM) | 4.1 | 16 |
| DASH (Widevine CENC; "cenc" scheme) | 4.4 | 19 |
| DASH (Widevine CENC; "cbcs" scheme) | 7.1 | 25 |
| DASH (ClearKey; "cenc" scheme) | 5.0 | 21 |
| SmoothStreaming (no DRM) | 4.1 | 16 |
| SmoothStreaming (PlayReady SL2000; "cenc" scheme) | AndroidTV | AndroidTV |
| HLS (no DRM) | 4.1 | 16 |
| HLS (AES-128 encryption) | 4.1 | 16 |
| HLS (Widevine CENC; "cenc" scheme) | 4.4 | 19 |
| HLS (Widevine CENC; "cbcs" scheme) | 7.1 | 25 |
For a given use case, we aim to support ExoPlayer on all Android devices that
satisfy the minimum version requirement. Known device specific compatibility
issues are listed below. Device specific issues on our GitHub issue tracker can
be found
[here](https://github.com/google/ExoPlayer/labels/bug%3A%20device%20specific).
* **FireOS (version 4 and earlier)** - Whilst we endeavour to support FireOS
devices, FireOS is a fork of Android and as a result we are unable to
guarantee support. Device specific issues encountered on FireOS are normally
caused by incompatibilities in the support that FireOS provides for running
Android applications. Such issues should be reported to Amazon in the first
instance. We are aware of issues affecting FireOS version 4 and earlier. We
believe FireOS version 5 resolved these issues.
* **Nexus Player (only when using an HDMI to DVI cable)** - There is a known
issue affecting Nexus Player, only when the device is connected to a monitor
using a certain type of HDMI to DVI cable, which causes video being played too
quickly. Use of an HDMI to DVI cable is not realistic for an end user setup
because such cables cannot carry audio. Hence this issue can be safely
ignored. We suggest using a realistic end user setup (e.g., the device
connected to a TV using a standard HDMI cable) for development and testing.
* **Emulators** - Some Android emulators do not properly implement components of
Android's media stack, and as a result do not support ExoPlayer. This is an
issue with the emulator, not with ExoPlayer. Android's official emulator
("Virtual Devices" in Android Studio) supports ExoPlayer provided the system
image has an API level of at least 23. System images with earlier API levels
do not support ExoPlayer. The level of support provided by third party
emulators varies. Issues running ExoPlayer on third party emulators should be
reported to the developer of the emulator rather than to the ExoPlayer team.
Where possible, we recommend testing media applications on physical devices
rather than emulators.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/supported-devices
--- ---
title: Supported formats permalink: /supported-formats.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/supported-formats
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
When defining the formats that ExoPlayer supports, it's important to note that
"media formats" are defined at multiple levels. From the lowest level to the
highest, these are:
* The format of the individual media samples (e.g., a frame of video or a frame
of audio). These are *sample formats*. Note that a typical video file will
contain media in at least two sample formats; one for video (e.g., H.264) and
one for audio (e.g., AAC).
* The format of the container that houses the media samples and associated
metadata. These are *container formats*. A media file has a single container
format (e.g., MP4), which is commonly indicated by the file extension. Note
that for some audio only formats (e.g., MP3), the sample and container formats
may be the same.
* Adaptive streaming technologies such as DASH, SmoothStreaming and HLS. These
are not media formats as such, however it's still necessary to define what
level of support ExoPlayer provides.
The following sections define ExoPlayer's support at each level, from highest to
lowest. The last two sections describe support for standalone subtitle formats
and HDR video playback.
## Adaptive streaming ##
### DASH ###
{% include_relative _page_fragments/supported-formats-dash.md %}
### SmoothStreaming ###
{% include_relative _page_fragments/supported-formats-smoothstreaming.md %}
### HLS ###
{% include_relative _page_fragments/supported-formats-hls.md %}
## Progressive container formats ##
{% include_relative _page_fragments/supported-formats-progressive.md %}
## RTSP ##
{% include_relative _page_fragments/supported-formats-rtsp.md %}
## Sample formats ##
By default ExoPlayer uses Android's platform decoders. Hence the supported
sample formats depend on the underlying platform rather than on ExoPlayer.
Sample formats supported by Android devices are documented
[here](https://developer.android.com/guide/appendix/media-formats.html#core).
Note that individual devices may support additional formats beyond those listed.
In addition to Android's platform decoders, ExoPlayer can also make use of
software decoder extensions. These must be manually built and included in
projects that wish to make use of them. We currently provide software decoder
extensions for
[AV1]({{ site.release_v2 }}/extensions/av1),
[VP9]({{ site.release_v2 }}/extensions/vp9),
[FLAC]({{ site.release_v2 }}/extensions/flac),
[Opus]({{ site.release_v2 }}/extensions/opus) and
[FFmpeg]({{ site.release_v2 }}/extensions/ffmpeg).
### FFmpeg extension ###
The [FFmpeg extension]({{ site.release_v2 }}/extensions/ffmpeg) supports
decoding a variety of different audio sample formats. You can choose which
decoders to include when building the extension, as documented in the
extension's [README.md]({{ site.release_v2 }}/extensions/ffmpeg/README.md). The
following table provides a mapping from audio sample format to the corresponding
FFmpeg decoder name.
| Sample format | Decoder name(s) |
|---------------:|----------------------------|
| Vorbis | vorbis |
| Opus | opus |
| FLAC | flac |
| ALAC | alac |
| PCM μ-law | pcm_mulaw |
| PCM A-law | pcm_alaw |
| MP1, MP2, MP3 | mp3 |
| AMR-NB | amrnb |
| AMR-WB | amrwb |
| AAC | aac |
| AC-3 | ac3 |
| E-AC-3 | eac3 |
| DTS, DTS-HD | dca |
| TrueHD | mlp truehd |
## Standalone subtitle formats ##
ExoPlayer supports standalone subtitle files in a variety of formats. Subtitle
files can be side-loaded as described on the [media items page][].
| Container format | Supported | MIME type |
|---------------------------|:------------:|:----------|
| WebVTT | YES | MimeTypes.TEXT_VTT |
| TTML / SMPTE-TT | YES | MimeTypes.APPLICATION_TTML |
| SubRip | YES | MimeTypes.APPLICATION_SUBRIP |
| SubStationAlpha (SSA/ASS) | YES | MimeTypes.TEXT_SSA |
[media items page]: {{ site.baseurl }}/media-items.html#sideloading-subtitle-tracks
## HDR video playback ##
ExoPlayer handles extracting high dynamic range (HDR) video in various
containers, including Dolby Vision in MP4 and HDR10+ in Matroska/WebM. Decoding
and displaying HDR content depends on support from the Android platform and
device. See
[HDR Video Playback](https://source.android.com/devices/tech/display/hdr.html)
to learn about checking for HDR decoding/display capabilities and limitations of
HDR support across Android versions.
When playing an HDR stream that requires support for a particular codec profile,
ExoPlayer's default `MediaCodec` selector will pick a decoder that supports that
profile (if available), even if another decoder for the same MIME type that
doesn't support that profile appears higher up the codec list. This can result
in selecting a software decoder in cases where the stream exceeds the
capabilities of a hardware decoder for the same MIME type.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/supported-formats
--- ---
title: Track selection permalink: /track-selection.html
redirect_to:
- https://developer.android.com/media/media3/exoplayer/track-selection
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
When a media item contains multiple tracks, track selection is the process that
determines which of them are chosen for playback. The track selection process is
configured by [`TrackSelectionParameters`][], which allows many different
constraints and overrides influencing track selection to be specified.
## Querying the available tracks
You can listen to `Player.Listener.onTracksChanged` to be notified about changes
to tracks, including:
* The available tracks becoming known when preparation of the media item being
played completes. Note that the player needs to prepare a media item to know
what tracks it contains.
* The available tracks changing due to playback transitioning from one media
item to another.
* Changes to the selected tracks.
~~~
player.addListener(new Player.Listener() {
@Override
public void onTracksChanged(Tracks tracks) {
// Update UI using current tracks.
}
});
~~~
{: .language-java}
You can also query the current tracks by calling `player.getCurrentTracks()`.
The returned `Tracks` contains a list of `Track.Group`s, where tracks within a
single `Group` present the same content but in different formats.
As an example of how tracks can be grouped, consider an adaptive playback where
a main video feed is provided in five bitrates, and an alternative video feed
(e.g., a different camera angle in a sports match) is provided in two bitrates.
In this case there will be two video track groups, one corresponding to the main
video feed containing five tracks, and a second for the alternative video feed
containing two tracks.
Audio tracks whose languages differ are not grouped, because content in
different languages is not considered to be the same. Conversely, audio tracks
in the same language that only differ in properties such as bitrate, sampling
rate, channel count and so on can be grouped. This also applies to text tracks.
Each `Group` can be queried to determine which tracks are supported for
playback, which are currently selected, and what `Format` each track uses:
~~~
for (Tracks.Group trackGroup : tracks.getGroups()) {
// Group level information.
@C.TrackType int trackType = trackGroup.getTrackType();
boolean trackInGroupIsSelected = trackGroup.isSelected();
boolean trackInGroupIsSupported = trackGroup.isSupported();
for (int i = 0; i < trackGroup.length; i++) {
// Individual track information.
boolean isSupported = trackGroup.isTrackSupported(i);
boolean isSelected = trackGroup.isTrackSelected(i);
Format trackFormat = trackGroup.getTrackFormat(i);
}
}
~~~
{: .language-java}
* A track is 'supported' if the `Player` is able to decode and render its
samples. Note that even if multiple track groups of the same type (for example
multiple audio track groups) are supported, it only means that they are
supported individually and the player is not necessarily able to play them at
the same time.
* A track is 'selected' if it has been chosen for playback given the current
`TrackSelectionParameters`. If multiple tracks within one track group are
selected, the player uses these tracks for adaptive playback (for example,
multiple video tracks with different bitrates). Note that only one of these
tracks will be played at any one time.
## Modifying track selection parameters
The track selection process can be configured using
`Player.setTrackSelectionParameters`. This can be done both before and during
playback. The example below demonstrates how to obtain the current
`TrackSelectionParameters` from the player, modify them, and update the `Player`
with the modified result:
~~~
player.setTrackSelectionParameters(
player.getTrackSelectionParameters()
.buildUpon()
.setMaxVideoSizeSd()
.setPreferredAudioLanguage("hu")
.build());
~~~
{: .language-java}
### Constraint based track selection
Most options in `TrackSelectionParameters` allow you to specify constraints,
which are independent of the tracks that are actually available. Available
constraints include:
* Maximum and minimum video width, height, frame rate, and bitrate.
* Maximum audio channel count and bitrate.
* Preferred MIME types for video and audio.
* Preferred audio languages and role flags.
* Preferred text languages and role flags.
ExoPlayer uses sensible defaults for these constraints, for example restricting
video resolution to the display size and preferring the audio language that
matches the user's system Locale setting.
There are several benefits to using constraint based track selection rather than
selecting specific tracks from those that are available:
* You can specify constraints before knowing what tracks a media item provides.
This means that constraints can be specified before the player has prepared a
media item, whereas selecting specific tracks requires application code to
wait until the available tracks become known.
* Constraints are applied for all media items in a playlist, even when those
items have different available tracks. For example, a preferred audio language
constraint will be automatically applied for all media items, even if the
`Format` of the track in that language varies from one media item to the next.
This is not the case when selecting specific tracks, as described below.
### Selecting specific tracks
It's possible to select specific tracks using `TrackSelectionParameters`. First,
the player's currently available tracks should be queried using
`Player.getCurrentTracks`. Second, having identified which tracks to select,
they can be set on `TrackSelectionParameters` using a `TrackSelectionOverride`.
For example, to select the first track from a specific `audioTrackGroup`:
~~~
player.setTrackSelectionParameters(
player.getTrackSelectionParameters()
.buildUpon()
.setOverrideForType(
new TrackSelectionOverride(
audioTrackGroup.getMediaTrackGroup(),
/* trackIndex= */ 0))
.build());
~~~
{: .language-java}
A `TrackSelectionOverride` will only apply to media items that contain a
`TrackGroup` exactly matching the one specified in the override. Hence an
override may not apply to a subsequent media item if that item contains
different tracks.
### Disabling track types or groups
Track types like video, audio or text, can be disabled completely using
`TrackSelectionParameters.Builder.setTrackTypeDisabled`. A disabled track type
will be disabled for all media items:
~~~
player.setTrackSelectionParameters(
player.getTrackSelectionParameters()
.buildUpon()
.setTrackTypeDisabled(C.TRACK_TYPE_VIDEO, /* disabled= */ true)
.build());
~~~
{: .language-java}
Alternatively, it's possible to prevent the selection of tracks from a specific
`TrackGroup` by specifying an empty override for that group:
~~~
player.setTrackSelectionParameters(
player.getTrackSelectionParameters()
.buildUpon()
.addOverride(
new TrackSelectionOverride(
disabledTrackGroup.getMediaTrackGroup(),
/* trackIndices= */ ImmutableList.of()))
.build());
~~~
{: .language-java}
## Customizing the track selector
Track selection is the responsibility of a `TrackSelector`, an instance
of which can be provided whenever an `ExoPlayer` is built and later obtained
with `ExoPlayer.getTrackSelector()`.
~~~
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
ExoPlayer player =
new ExoPlayer.Builder(context)
.setTrackSelector(trackSelector)
.build();
~~~
{: .language-java}
`DefaultTrackSelector` is a flexible `TrackSelector` suitable for most use
cases. It uses the `TrackSelectionParameters` set in the `Player`, but also
provides some advanced customization options that can be specified in the
`DefaultTrackSelector.ParametersBuilder`:
~~~
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAllowVideoMixedMimeTypeAdaptiveness(true));
~~~
{: .language-java}
### Tunneling
Tunneled playback can be enabled in cases where the combination of renderers and
selected tracks supports it. This can be done by using
`DefaultTrackSelector.ParametersBuilder.setTunnelingEnabled(true)`.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/track-selection
[`TrackSelectionParameters`]: {{ site.exo_sdk }}/trackselection/TrackSelectionParameters.html
--- ---
title: Transforming media permalink: /transforming-media.html
redirect_to:
- https://developer.android.com/media/media3/transformer
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
The [Transformer API][] can be used to convert media streams. It takes an input
media stream, applies changes to it as configured by the app, and produces the
corresponding output file. The available transformations are:
* Track removal.
* Flattening of slow motion videos or, in other words, their conversion into
normal videos that retain the desired slow motion effects, but can be played
with a player that is not aware of slow motion video formats. The purpose of
this transformation is to make slow motion videos suitable for sharing with
other apps or uploading to a server.
## Starting a transformation ##
To transform media, you need to add the following dependency to your app’s
`build.gradle` file:
~~~
implementation 'com.google.android.exoplayer:exoplayer-transformer:2.X.X'
~~~
{: .language-gradle}
where `2.X.X` is your preferred ExoPlayer version.
You can then start a transformation by building a `Transformer` instance and
calling `startTransformation` on it. The code sample below starts a
transformation that removes the audio track from the input:
~~~
// Configure and create a Transformer instance.
Transformer transformer =
new Transformer.Builder(context)
.setRemoveAudio(true)
.addListener(transformerListener)
.build();
// Start the transformation.
transformer.startTransformation(inputMediaItem, outputPath);
~~~
{: .language-java}
Other parameters, such as the `MediaSource.Factory`, can be passed to the
builder.
`startTransformation` receives a `MediaItem` describing the input, and a path or
a `ParcelFileDescriptor` indicating where the output should be written. The
input can be a progressive or an adaptive stream, but the output is always a
progressive stream. For adaptive inputs, the highest resolution tracks are
always selected for the transformation. The input can be of any container format
supported by ExoPlayer (see the [Supported formats page][] for details), but the
output is always an MP4 file.
Multiple transformations can be executed sequentially with the same
`Transformer` instance, but concurrent transformations with the same instance
are not supported.
## Listening to events ##
The `startTransformation` method is asynchronous. It returns immediately and the
app is notified of events via the listener passed to the `Transformer` builder.
~~~
Transformer.Listener transformerListener =
new Transformer.Listener() {
@Override
public void onTransformationCompleted(MediaItem inputMediaItem, TransformationResult result) {
playOutput();
}
@Override
public void onTransformationError(MediaItem inputMediaItem, TransformationException exception) {
displayError(exception);
}
};
~~~
{: .language-java}
## Displaying progress updates ##
`Transformer.getProgress` can be called to query the current progress of a
transformation. The returned value indicates the progress state. If the progress
state is `PROGRESS_STATE_AVAILABLE` then the passed `ProgressHolder` will have
been updated with the current progress percentage. The snippet below
demonstrates how to periodically query the progress of a transformation, where
the `updateProgressInUi` method could be implemented to update a progress bar
displayed to the user.
~~~
transformer.startTransformation(inputMediaItem, outputPath);
ProgressHolder progressHolder = new ProgressHolder();
mainHandler.post(
new Runnable() {
@Override
public void run() {
@ProgressState int progressState = transformer.getProgress(progressHolder);
updateProgressInUi(progressState, progressHolder);
if (progressState != PROGRESS_STATE_NO_TRANSFORMATION) {
mainHandler.postDelayed(/* r= */ this, /* delayMillis= */ 500);
}
}
});
~~~
{: .language-java}
## Flattening slow motion videos ##
We define a slow motion video as a media stream whose metadata points to
sections of the stream that should be slowed during playback. Flattening is the
process of converting a slow motion video to a regular media format (for example
MP4) where the slow motion sections are played at the requested speed. The slow
motion metadata is removed, and the video and audio streams are modified so as
to produce the desired effect when the output is played with a standard player
(that is, a player that is not aware of slow motion formats).
To flatten slow motion streams, use the `setFlattenForSlowMotion` builder
method.
~~~
Transformer transformer =
new Transformer.Builder(context)
.setFlattenForSlowMotion(true)
.addListener(transformerListener)
.build();
transformer.startTransformation(inputMediaItem, outputPath);
~~~
{: .language-java}
This allows apps to support slow motion videos without having to worry about
handling these special formats. All they need to do is to store and play the
flattened version of the video instead of the original one.
Currently, Samsung's slow motion format is the only one supported.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/transforming-media
[Transformer API]: {{ site.exo_sdk }}/transformer/Transformer.html
[Supported formats page]: {{ site.baseurl }}/supported-formats.html
--- ---
title: UI components permalink: /ui-components.html
redirect_to:
- https://developer.android.com/media/media3/ui/playerview
--- ---
This documentation may be out-of-date. Please refer to the
[documentation for the latest ExoPlayer release][] on developer.android.com.
{:.info}
An app playing media requires user interface components for displaying media and
controlling playback. The ExoPlayer library includes a UI module that contains
a number of UI components. To depend on the UI module add a dependency as shown
below.
~~~
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'
~~~
{: .language-gradle}
The most important component is `StyledPlayerView`, a view for media
playbacks. It displays video, subtitles and album art during playback, as
well as playback controls.
`StyledPlayerView` has a `setPlayer` method for attaching and detaching (by
passing `null`) player instances.
## StyledPlayerView ##
`StyledPlayerView` can be used for both video and audio playbacks. It renders
video and subtitles in the case of video playback, and can display artwork
included as metadata in audio files. You can include it in your layout files
like any other UI component. For example, a `StyledPlayerView` can be included
with the following XML:
~~~
<com.google.android.exoplayer2.ui.StyledPlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:show_buffering="when_playing"
app:show_shuffle_button="true"/>
~~~
{: .language-xml}
The snippet above illustrates that `StyledPlayerView` provides several
attributes. These attributes can be used to customize the view's behavior, as
well as its look and feel. Most of these attributes have corresponding setter
methods, which can be used to customize the view at runtime. The
[`StyledPlayerView`][] Javadoc lists these attributes and setter methods in
more detail.
Once the view is declared in the layout file, it can be looked up in the
`onCreate` method of the activity:
~~~
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
playerView = findViewById(R.id.player_view);
}
~~~
{: .language-java}
When a player has been initialized, it can be attached to the view by calling
`setPlayer`:
~~~
// Instantiate the player.
player = new ExoPlayer.Builder(context).build();
// Attach player to the view.
playerView.setPlayer(player);
// Set the media item to be played.
player.setMediaItem(mediaItem);
// Prepare the player.
player.prepare();
~~~
{: .language-java}
### Choosing a surface type ###
The `surface_type` attribute of `StyledPlayerView` lets you set the type of
surface used for video playback. Besides the values `spherical_gl_surface_view`
(which is a special value for spherical video playback) and
`video_decoder_gl_surface_view` (which is for video rendering using extension
renderers), the allowed values are `surface_view`, `texture_view` and `none`. If
the view is for audio playback only, `none` should be used to avoid having to
create a surface, since doing so can be expensive.
If the view is for regular video playback then `surface_view` or `texture_view`
should be used. `SurfaceView` has a number of benefits over `TextureView` for
video playback:
* Significantly lower power consumption on many devices.
* More accurate frame timing, resulting in smoother video playback.
* Support for secure output when playing DRM protected content.
* The ability to render video content at the full resolution of the display on
Android TV devices that upscale the UI layer.
`SurfaceView` should therefore be preferred over `TextureView` where possible.
`TextureView` should be used only if `SurfaceView` does not meet your needs. One
example is where smooth animations or scrolling of the video surface is required
prior to Android N, as described below. For this case, it's preferable to use
`TextureView` only when [`SDK_INT`][] is less than 24 (Android N) and
`SurfaceView` otherwise.
`SurfaceView` rendering wasn't properly synchronized with view animations until
Android N. On earlier releases this could result in unwanted effects when a
`SurfaceView` was placed into scrolling container, or when it was subjected to
animation. Such effects included the view's contents appearing to lag slightly
behind where it should be displayed, and the view turning black when subjected
to animation. To achieve smooth animation or scrolling of video prior to Android
N, it's therefore necessary to use `TextureView` rather than `SurfaceView`.
{:.info}
Some Android TV devices run their UI layer at a resolution that's lower than the
full resolution of the display, upscaling it for presentation to the user. For
example, the UI layer may be run at 1080p on an Android TV that has a 4K
display. On such devices, `SurfaceView` must be used to render content at the
full resolution of the display. The full resolution of the display (in its
current display mode) can be queried using [`Util.getCurrentDisplayModeSize`][].
The UI layer resolution can be queried using Android's [`Display.getSize`] API.
{:.info}
### Overriding drawables ###
We don't guarantee that the customizations described in the following section
will continue to work in future versions of the library. The resource IDs may
change name, or some may be deleted entirely. This is indicated by the
[resource IDs being marked 'private'][].
{:.info}
`StyledPlayerView` uses `StyledPlayerControlView` to display the playback
controls and progress bar. The drawables used by `StyledPlayerControlView` can
be overridden by drawables with the same names defined in your application. See
the [`StyledPlayerControlView`][] Javadoc for a list of control drawables that
can be overridden.
## Further customization ##
Where customization beyond that described above is required, we expect that app
developers will implement their own UI components rather than use those provided
by ExoPlayer's UI module.
[documentation for the latest ExoPlayer release]: https://developer.android.com/guide/topics/media/exoplayer/ui-components
[`StyledPlayerView`]: {{ site.exo_sdk }}/ui/StyledPlayerView.html
[`StyledPlayerControlView`]: {{ site.exo_sdk }}/ui/StyledPlayerControlView.html
[resource IDs being marked 'private']: https://developer.android.com/studio/projects/android-library#PrivateResources
[`SDK_INT`]: {{ site.android_sdk }}/android/os/Build.VERSION.html#SDK_INT
[`Util.getCurrentDisplayModeSize`]: {{ site.exo_sdk }}/util/Util.html#getCurrentDisplayModeSize(android.content.Context)
[`Display.getSize`]: {{ site.android_sdk }}/android/view/Display.html#getSize(android.graphics.Point)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment