Progressive

Streams in the following container formats can be played directly by ExoPlayer. The contained audio and video sample formats must also be supported (see the sample formats section for details).

Container format Supported Comment
MP4 YES  
M4A YES  
FMP4 YES  
WebM YES  
Matroska YES  
MP3 YES Some streams only seekable using constant bitrate seeking**
Ogg YES Containing Vorbis, Opus and FLAC
WAV YES  
MPEG-TS YES  
MPEG-PS YES  
FLV YES Not seekable*
ADTS (AAC) YES Only seekable using constant bitrate seeking**
FLAC YES Using the FLAC extension or the FLAC extractor in the core library***
AMR YES Only seekable using constant bitrate seeking**

* Seeking is unsupported because the container does not provide metadata (e.g., a sample index) to allow a media player to perform a seek in an efficient way. If seeking is required, we suggest using a more appropriate container format.

** These extractors have FLAG_ENABLE_CONSTANT_BITRATE_SEEKING flags for enabling approximate seeking using a constant bitrate assumption. This functionality is not enabled by default. The simplest way to enable this functionality for all extractors that support it is to use DefaultExtractorsFactory.setConstantBitrateSeekingEnabled, as described here.

*** The FLAC extension extractor outputs raw audio, which can be handled by the framework on all API levels. The core library FLAC extractor outputs FLAC audio frames and so relies on having a FLAC decoder (e.g., a MediaCodec decoder that handles FLAC (required from API level 27), or the FFmpeg extension with FLAC enabled). The DefaultExtractorsFactory uses the extension extractor if the application was built with the FLAC extension. Otherwise, it uses the core library extractor.

Creating a MediaSource

To play a progressive stream, create a ProgressiveMediaSource and prepare the player with it as usual.

// Create a data source factory.
DataSource.Factory dataSourceFactory =
    new DefaultHttpDataSourceFactory(Util.getUserAgent(context, "app-name"));
// Create a progressive media source pointing to a stream uri.
MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory)
    .createMediaSource(progressiveUri);
// Create a player instance.
SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build();
// Prepare the player with the media source.
player.prepare(mediaSource);

Customizing progressive playbacks

ExoPlayer provides multiple ways for you to tailor playback experience to your app’s needs. The following sections briefly document some of the customization options available when building a ProgressiveMediaSource. See the Customization page for more general customization options.

Setting extractor flags

Extractor flags can be used to control how individual formats are extracted. They can be set on a DefaultExtractorsFactory, which can then be used when instantiating a ProgressiveMediaSource.Factory. The following example passes a flag which disables edit list parsing for the MP4 streams.

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory()
        .setMp4ExtractorFlags(Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS);
ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory)
        .createMediaSource(progressiveUri);

Enabling constant bitrate seeking

For MP3, ADTS and AMR streams, you can enable approximate seeking using a constant bitrate assumption with FLAG_ENABLE_CONSTANT_BITRATE_SEEKING flags. These flags can be set for individual extractors using the approach described above. To enable constant bitrate seeking for all extractors that support it, use DefaultExtractorsFactory.setConstantBitrateSeekingEnabled.

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory)
        .createMediaSource(progressiveUri);

Customizing server interactions

Some apps may want to intercept HTTP requests and responses. You may want to inject custom request headers, read the server’s response headers, modify the requests’ URIs, etc. For example, your app may authenticate itself by injecting a token as a header when requesting the media segments.

The following example demonstrates how to implement these behaviors by injecting custom HttpDataSources into a ProgressiveMediaSource:

ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(
            () -> {
              HttpDataSource dataSource =
                  new DefaultHttpDataSource(userAgent);
              // Set a custom authentication request header.
              dataSource.setRequestProperty("Header", "Value");
              return dataSource;
            })
        .createMediaSource(progressiveUri);

In the code snippet above, the injected HttpDataSource includes the header "Header: Value" in every HTTP request triggered by progressiveMediaSource. This behavior is fixed for every interaction of progressiveMediaSource with an HTTP source.

For a more granular approach, you can inject just-in-time behavior using a ResolvingDataSource. The following code snippet shows how to inject request headers just before interacting with an HTTP source:

ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(
        new ResolvingDataSource.Factory(
            new DefaultHttpDataSourceFactory(userAgent),
            // Provide just-in-time request headers.
            (DataSpec dataSpec) ->
                dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))))
        .createMediaSource(customSchemeUri);

You may also use a ResolvingDataSource to perform just-in-time modifications of the URI, as shown in the following snippet:

ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(
        new ResolvingDataSource.Factory(
            new DefaultHttpDataSourceFactory(userAgent),
            // Provide just-in-time URI resolution logic.
            (DataSpec dataSpec)-> dataSpec.withUri(resolveUri(dataSpec.uri))))
        .createMediaSource(customSchemeUri);

Customizing error handling

Implementing a custom LoadErrorHandlingPolicy allows apps to customize the way ExoPlayer reacts to load errors. For example, an app may want fail fast instead of retrying many times, or may want to customize the back-off logic that controls how long the player waits between each retry. The following snippet shows how to implement custom back-off logic when creating a ProgressiveMediaSource:

ProgressiveMediaSource progressiveMediaSource =
    new ProgressiveMediaSource.Factory(dataSourceFactory)
        .setLoadErrorHandlingPolicy(
            new DefaultLoadErrorHandlingPolicy() {
              @Override
              public long getRetryDelayMsFor(...) {
                // Implement custom back-off logic here.
              }
            })
        .createMediaSource(progressiveUri);

You will find more information in our Medium post about error handling.