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