1---
2title: Customization
3---
4
5At the core of the ExoPlayer library is the `Player` interface. A `Player`
6exposes traditional high-level media player functionality such as the ability to
7buffer media, play, pause and seek. The default implementation `ExoPlayer` is
8designed to make few assumptions about (and hence impose few restrictions on)
9the type of media being played, how and where it is stored, and how it is
10rendered. Rather than implementing the loading and rendering of media directly,
11`ExoPlayer` implementations delegate this work to components that are injected
12when a player is created or when new media sources are passed to the player.
13Components common to all `ExoPlayer` implementations are:
14
15* `MediaSource` instances that define media to be played, load the media, and
16  from which the loaded media can be read. `MediaSource` instances are created
17  from `MediaItem`s by a `MediaSource.Factory` inside the player. They can also
18  be passed directly to the player using the [media source based playlist API].
19* A `MediaSource.Factory` that converts `MediaItem`s to `MediaSource`s. The
20  `MediaSource.Factory` is injected when the player is created.
21* `Renderer`s that render individual components of the media. `Renderer`s are
22  injected when the player is created.
23* A `TrackSelector` that selects tracks provided by the `MediaSource` to be
24  consumed by each of the available `Renderer`s. A `TrackSelector` is injected
25  when the player is created.
26* A `LoadControl` that controls when the `MediaSource` buffers more media, and
27  how much media is buffered. A `LoadControl` is injected when the player is
28  created.
29* A `LivePlaybackSpeedControl` that controls the playback speed during live
30  playbacks to allow the player to stay close to a configured live offset. A
31  `LivePlaybackSpeedControl` is injected when the player is created.
32
33The concept of injecting components that implement pieces of player
34functionality is present throughout the library. The default implementations of
35some components delegate work to further injected components. This allows many
36sub-components to be individually replaced with implementations that are
37configured in a custom way.
38
39## Player customization ##
40
41Some common examples of customizing the player by injecting components are
42described below.
43
44### Configuring the network stack ###
45
46ExoPlayer supports Android's default network stack, as well as Cronet and
47OkHttp. In each case it's possible to customize the network stack for your use
48case. The following example shows how to customize the player to use Android's
49default network stack with cross-protocol redirects enabled:
50
51~~~
52// Build a HttpDataSource.Factory with cross-protocol redirects enabled.
53HttpDataSource.Factory httpDataSourceFactory =
54    new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true);
55
56// Wrap the HttpDataSource.Factory in a DefaultDataSource.Factory, which adds in
57// support for requesting data from other sources (e.g., files, resources, etc).
58DefaultDataSource.Factory dataSourceFactory =
59    new DefaultDataSource.Factory(context, httpDataSourceFactory);
60
61// Inject the DefaultDataSourceFactory when creating the player.
62ExoPlayer player =
63    new ExoPlayer.Builder(context)
64        .setMediaSourceFactory(new DefaultMediaSourceFactory(dataSourceFactory))
65        .build();
66~~~
67{: .language-java}
68
69The same approach can be used to configure and inject `HttpDataSource.Factory`
70implementations provided by the [Cronet extension] and the [OkHttp extension],
71depending on your preferred choice of network stack.
72
73### Caching data loaded from the network ###
74
75To temporarily cache media, or for
76[playing downloaded media]({{ site.baseurl }}/downloading-media.html#playing-downloaded-content),
77you can inject a `CacheDataSource.Factory` into the `DefaultMediaSourceFactory`:
78
79~~~
80DataSource.Factory cacheDataSourceFactory =
81    new CacheDataSource.Factory()
82        .setCache(simpleCache)
83        .setUpstreamDataSourceFactory(httpDataSourceFactory);
84
85ExoPlayer player = new ExoPlayer.Builder(context)
86    .setMediaSourceFactory(
87        new DefaultMediaSourceFactory(cacheDataSourceFactory))
88    .build();
89~~~
90{: .language-java}
91
92### Customizing server interactions ###
93
94Some apps may want to intercept HTTP requests and responses. You may want to
95inject custom request headers, read the server's response headers, modify the
96requests' URIs, etc. For example, your app may authenticate itself by injecting
97a token as a header when requesting the media segments.
98
99The following example demonstrates how to implement these behaviors by
100injecting a custom `DataSource.Factory` into the `DefaultMediaSourceFactory`:
101
102~~~
103DataSource.Factory dataSourceFactory = () -> {
104  HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
105  // Set a custom authentication request header.
106  dataSource.setRequestProperty("Header", "Value");
107  return dataSource;
108};
109
110ExoPlayer player = new ExoPlayer.Builder(context)
111    .setMediaSourceFactory(new DefaultMediaSourceFactory(dataSourceFactory))
112    .build();
113~~~
114{: .language-java}
115
116In the code snippet above, the injected `HttpDataSource` includes the header
117`"Header: Value"` in every HTTP request. This behavior is *fixed* for every
118interaction with an HTTP source.
119
120For a more granular approach, you can inject just-in-time behavior using a
121`ResolvingDataSource`. The following code snippet shows how to inject
122request headers just before interacting with an HTTP source:
123
124~~~
125DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory(
126    httpDataSourceFactory,
127    // Provide just-in-time request headers.
128    dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
129~~~
130{: .language-java}
131
132You may also use a `ResolvingDataSource` to perform
133just-in-time modifications of the URI, as shown in the following snippet:
134
135~~~
136DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory(
137    httpDataSourceFactory,
138    // Provide just-in-time URI resolution logic.
139    dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
140~~~
141{: .language-java}
142
143### Customizing error handling ###
144
145Implementing a custom [LoadErrorHandlingPolicy][] allows apps to customize the
146way ExoPlayer reacts to load errors. For example, an app may want to fail fast
147instead of retrying many times, or may want to customize the back-off logic that
148controls how long the player waits between each retry. The following snippet
149shows how to implement custom back-off logic:
150
151~~~
152LoadErrorHandlingPolicy loadErrorHandlingPolicy =
153    new DefaultLoadErrorHandlingPolicy() {
154      @Override
155      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
156        // Implement custom back-off logic here.
157      }
158    };
159
160ExoPlayer player =
161    new ExoPlayer.Builder(context)
162        .setMediaSourceFactory(
163            new DefaultMediaSourceFactory(context)
164                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
165        .build();
166~~~
167{: .language-java}
168
169The `LoadErrorInfo` argument contains more information about the failed load to
170customize the logic based on the error type or the failed request.
171
172### Customizing extractor flags ###
173
174Extractor flags can be used to customize how individual formats are extracted
175from progressive media. They can be set on the `DefaultExtractorsFactory` that's
176provided to the `DefaultMediaSourceFactory`. The following example passes a flag
177that enables index-based seeking for MP3 streams.
178
179~~~
180DefaultExtractorsFactory extractorsFactory =
181    new DefaultExtractorsFactory()
182        .setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);
183
184ExoPlayer player = new ExoPlayer.Builder(context)
185    .setMediaSourceFactory(
186        new DefaultMediaSourceFactory(context, extractorsFactory))
187    .build();
188~~~
189{: .language-java}
190
191### Enabling constant bitrate seeking ###
192
193For MP3, ADTS and AMR streams, you can enable approximate seeking using a
194constant bitrate assumption with `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING` flags.
195These flags can be set for individual extractors using the individual
196`DefaultExtractorsFactory.setXyzExtractorFlags` methods as described above. To
197enable constant bitrate seeking for all extractors that support it, use
198`DefaultExtractorsFactory.setConstantBitrateSeekingEnabled`.
199
200~~~
201DefaultExtractorsFactory extractorsFactory =
202    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
203~~~
204{: .language-java}
205
206The `ExtractorsFactory` can then be injected via `DefaultMediaSourceFactory` as
207described for customizing extractor flags above.
208
209## MediaSource customization ##
210
211The examples above inject customized components for use during playback of all
212`MediaItem`s that are passed to the player. Where fine-grained customization is
213required, it's also possible to inject customized components into individual
214`MediaSource` instances, which can be passed directly to the player. The example
215below shows how to customize a `ProgressiveMediaSource` to use a custom
216`DataSource.Factory`, `ExtractorsFactory` and `LoadErrorHandlingPolicy`:
217
218~~~
219ProgressiveMediaSource mediaSource =
220    new ProgressiveMediaSource.Factory(
221            customDataSourceFactory, customExtractorsFactory)
222        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
223        .createMediaSource(MediaItem.fromUri(streamUri));
224~~~
225{: .language-java}
226
227## Creating custom components ##
228
229The library provides default implementations of the components listed at the top
230of this page for common use cases. An `ExoPlayer` can use these components, but
231may also be built to use custom implementations if non-standard behaviors are
232required. Some use cases for custom implementations are:
233
234* `Renderer` – You may want to implement a custom `Renderer` to handle a
235  media type not supported by the default implementations provided by the
236  library.
237* `TrackSelector` – Implementing a custom `TrackSelector` allows an app
238  developer to change the way in which tracks exposed by a `MediaSource` are
239  selected for consumption by each of the available `Renderer`s.
240* `LoadControl` – Implementing a custom `LoadControl` allows an app
241  developer to change the player's buffering policy.
242* `Extractor` – If you need to support a container format not currently
243  supported by the library, consider implementing a custom `Extractor` class.
244* `MediaSource` – Implementing a custom `MediaSource` class may be
245  appropriate if you wish to obtain media samples to feed to renderers in a
246  custom way, or if you wish to implement custom `MediaSource` compositing
247  behavior.
248* `MediaSource.Factory` – Implementing a custom `MediaSource.Factory`
249  allows an application to customize the way in which `MediaSource`s are created
250  from `MediaItem`s.
251* `DataSource` – ExoPlayer’s upstream package already contains a number of
252  `DataSource` implementations for different use cases. You may want to
253  implement you own `DataSource` class to load data in another way, such as over
254  a custom protocol, using a custom HTTP stack, or from a custom persistent
255  cache.
256
257When building custom components, we recommend the following:
258
259* If a custom component needs to report events back to the app, we recommend
260  that you do so using the same model as existing ExoPlayer components, for
261  example using `EventDispatcher` classes or passing a `Handler` together with
262  a listener to the constructor of the component.
263* We recommended that custom components use the same model as existing ExoPlayer
264  components to allow reconfiguration by the app during playback. To do this,
265  custom components should implement `PlayerMessage.Target` and receive
266  configuration changes in the `handleMessage` method. Application code should
267  pass configuration changes by calling ExoPlayer’s `createMessage` method,
268  configuring the message, and sending it to the component using
269  `PlayerMessage.send`. Sending messages to be delivered on the playback thread
270  ensures that they are executed in order with any other operations being
271  performed on the player.
272
273[Cronet extension]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/cronet
274[OkHttp extension]: https://github.com/google/ExoPlayer/tree/release-v2/extensions/okhttp
275[LoadErrorHandlingPolicy]: {{ site.exo_sdk }}/upstream/LoadErrorHandlingPolicy.html
276[media source based playlist API]: {{ site.baseurl }}/media-sources.html#media-source-based-playlist-api
277
278