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