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