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 46We have a page about [customizing the network stack used by ExoPlayer]. 47 48### Caching data loaded from the network ### 49 50To temporarily cache media, or for 51[playing downloaded media]({{ site.baseurl }}/downloading-media.html#playing-downloaded-content), 52you can inject a `CacheDataSource.Factory` into the `DefaultMediaSourceFactory`: 53 54~~~ 55DataSource.Factory cacheDataSourceFactory = 56 new CacheDataSource.Factory() 57 .setCache(simpleCache) 58 .setUpstreamDataSourceFactory(httpDataSourceFactory); 59 60ExoPlayer player = new ExoPlayer.Builder(context) 61 .setMediaSourceFactory( 62 new DefaultMediaSourceFactory(cacheDataSourceFactory)) 63 .build(); 64~~~ 65{: .language-java} 66 67### Customizing server interactions ### 68 69Some apps may want to intercept HTTP requests and responses. You may want to 70inject custom request headers, read the server's response headers, modify the 71requests' URIs, etc. For example, your app may authenticate itself by injecting 72a token as a header when requesting the media segments. 73 74The following example demonstrates how to implement these behaviors by 75injecting a custom `DataSource.Factory` into the `DefaultMediaSourceFactory`: 76 77~~~ 78DataSource.Factory dataSourceFactory = () -> { 79 HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); 80 // Set a custom authentication request header. 81 dataSource.setRequestProperty("Header", "Value"); 82 return dataSource; 83}; 84 85ExoPlayer player = new ExoPlayer.Builder(context) 86 .setMediaSourceFactory(new DefaultMediaSourceFactory(dataSourceFactory)) 87 .build(); 88~~~ 89{: .language-java} 90 91In the code snippet above, the injected `HttpDataSource` includes the header 92`"Header: Value"` in every HTTP request. This behavior is *fixed* for every 93interaction with an HTTP source. 94 95For a more granular approach, you can inject just-in-time behavior using a 96`ResolvingDataSource`. The following code snippet shows how to inject 97request headers just before interacting with an HTTP source: 98 99~~~ 100DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( 101 httpDataSourceFactory, 102 // Provide just-in-time request headers. 103 dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))); 104~~~ 105{: .language-java} 106 107You may also use a `ResolvingDataSource` to perform 108just-in-time modifications of the URI, as shown in the following snippet: 109 110~~~ 111DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( 112 httpDataSourceFactory, 113 // Provide just-in-time URI resolution logic. 114 dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri))); 115~~~ 116{: .language-java} 117 118### Customizing error handling ### 119 120Implementing a custom [LoadErrorHandlingPolicy][] allows apps to customize the 121way ExoPlayer reacts to load errors. For example, an app may want to fail fast 122instead of retrying many times, or may want to customize the back-off logic that 123controls how long the player waits between each retry. The following snippet 124shows how to implement custom back-off logic: 125 126~~~ 127LoadErrorHandlingPolicy loadErrorHandlingPolicy = 128 new DefaultLoadErrorHandlingPolicy() { 129 @Override 130 public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { 131 // Implement custom back-off logic here. 132 } 133 }; 134 135ExoPlayer player = 136 new ExoPlayer.Builder(context) 137 .setMediaSourceFactory( 138 new DefaultMediaSourceFactory(context) 139 .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) 140 .build(); 141~~~ 142{: .language-java} 143 144The `LoadErrorInfo` argument contains more information about the failed load to 145customize the logic based on the error type or the failed request. 146 147### Customizing extractor flags ### 148 149Extractor flags can be used to customize how individual formats are extracted 150from progressive media. They can be set on the `DefaultExtractorsFactory` that's 151provided to the `DefaultMediaSourceFactory`. The following example passes a flag 152that enables index-based seeking for MP3 streams. 153 154~~~ 155DefaultExtractorsFactory extractorsFactory = 156 new DefaultExtractorsFactory() 157 .setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); 158 159ExoPlayer player = new ExoPlayer.Builder(context) 160 .setMediaSourceFactory( 161 new DefaultMediaSourceFactory(context, extractorsFactory)) 162 .build(); 163~~~ 164{: .language-java} 165 166### Enabling constant bitrate seeking ### 167 168For MP3, ADTS and AMR streams, you can enable approximate seeking using a 169constant bitrate assumption with `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING` flags. 170These flags can be set for individual extractors using the individual 171`DefaultExtractorsFactory.setXyzExtractorFlags` methods as described above. To 172enable constant bitrate seeking for all extractors that support it, use 173`DefaultExtractorsFactory.setConstantBitrateSeekingEnabled`. 174 175~~~ 176DefaultExtractorsFactory extractorsFactory = 177 new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true); 178~~~ 179{: .language-java} 180 181The `ExtractorsFactory` can then be injected via `DefaultMediaSourceFactory` as 182described for customizing extractor flags above. 183 184 185### Enabling asynchronous buffer queueing ### 186 187Asynchronous buffer queueing is an enhancement in ExoPlayer's rendering 188pipeline, which operates `MediaCodec` instances in [asynchronous mode][] and 189uses additional threads to schedule decoding and rendering of data. Enabling it 190can reduce dropped frames and audio underruns. 191 192Asynchronous buffer queueing is enabled by default on devices running Android 12 193and above, and can be enabled manually from Android 6. Consider enabling the 194feature for specific devices on which you observe dropped frames or audio 195underruns, particularly when playing DRM protected or high frame rate content. 196 197In the simplest case, you need to inject a `DefaultRenderersFactory` to the 198player as follows: 199 200~~~ 201DefaultRenderersFactory renderersFactory = 202 new DefaultRenderersFactory(context) 203 .forceEnableMediaCodecAsynchronousQueueing(); 204ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build(); 205~~~ 206{: .language-java} 207 208If you're instantiating renderers directly, pass a 209`AsynchronousMediaCodecAdapter.Factory` to the `MediaCodecVideoRenderer` and 210`MediaCodecAudioRenderer` constructors. 211 212## MediaSource customization ## 213 214The examples above inject customized components for use during playback of all 215`MediaItem`s that are passed to the player. Where fine-grained customization is 216required, it's also possible to inject customized components into individual 217`MediaSource` instances, which can be passed directly to the player. The example 218below shows how to customize a `ProgressiveMediaSource` to use a custom 219`DataSource.Factory`, `ExtractorsFactory` and `LoadErrorHandlingPolicy`: 220 221~~~ 222ProgressiveMediaSource mediaSource = 223 new ProgressiveMediaSource.Factory( 224 customDataSourceFactory, customExtractorsFactory) 225 .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) 226 .createMediaSource(MediaItem.fromUri(streamUri)); 227~~~ 228{: .language-java} 229 230## Creating custom components ## 231 232The library provides default implementations of the components listed at the top 233of this page for common use cases. An `ExoPlayer` can use these components, but 234may also be built to use custom implementations if non-standard behaviors are 235required. Some use cases for custom implementations are: 236 237* `Renderer` – You may want to implement a custom `Renderer` to handle a 238 media type not supported by the default implementations provided by the 239 library. 240* `TrackSelector` – Implementing a custom `TrackSelector` allows an app 241 developer to change the way in which tracks exposed by a `MediaSource` are 242 selected for consumption by each of the available `Renderer`s. 243* `LoadControl` – Implementing a custom `LoadControl` allows an app 244 developer to change the player's buffering policy. 245* `Extractor` – If you need to support a container format not currently 246 supported by the library, consider implementing a custom `Extractor` class. 247* `MediaSource` – Implementing a custom `MediaSource` class may be 248 appropriate if you wish to obtain media samples to feed to renderers in a 249 custom way, or if you wish to implement custom `MediaSource` compositing 250 behavior. 251* `MediaSource.Factory` – Implementing a custom `MediaSource.Factory` 252 allows an application to customize the way in which `MediaSource`s are created 253 from `MediaItem`s. 254* `DataSource` – ExoPlayer’s upstream package already contains a number of 255 `DataSource` implementations for different use cases. You may want to 256 implement you own `DataSource` class to load data in another way, such as over 257 a custom protocol, using a custom HTTP stack, or from a custom persistent 258 cache. 259 260When building custom components, we recommend the following: 261 262* If a custom component needs to report events back to the app, we recommend 263 that you do so using the same model as existing ExoPlayer components, for 264 example using `EventDispatcher` classes or passing a `Handler` together with 265 a listener to the constructor of the component. 266* We recommended that custom components use the same model as existing ExoPlayer 267 components to allow reconfiguration by the app during playback. To do this, 268 custom components should implement `PlayerMessage.Target` and receive 269 configuration changes in the `handleMessage` method. Application code should 270 pass configuration changes by calling ExoPlayer’s `createMessage` method, 271 configuring the message, and sending it to the component using 272 `PlayerMessage.send`. Sending messages to be delivered on the playback thread 273 ensures that they are executed in order with any other operations being 274 performed on the player. 275 276[customizing the network stack used by ExoPlayer]: {{ site.baseurl }}/network-stacks.html 277[LoadErrorHandlingPolicy]: {{ site.exo_sdk }}/upstream/LoadErrorHandlingPolicy.html 278[media source based playlist API]: {{ site.baseurl }}/media-sources.html#media-source-based-playlist-api 279[asynchronous mode]: https://developer.android.com/reference/android/media/MediaCodec#asynchronous-processing-using-buffers 280 281