Skip to content

Overriding media format selection logic

How EnigmaPlayer selects a media format

An asset present in the Red Bee Media OTT backend may have several available media formats. In this context a media format is the combination of a stream format (DASH/HLS/etc) and a drm technology (widevine/fairplay/playready/none).

When calling enigmaPlayer.play(playRequest) with an AssetPlayable the following steps are taken to decide which media format will be played. 1. EnigmaPlayer makes a "play-call" to the exposure backend. The JSON-response contains a set of available media formats for the asset. 1. EnigmaPlayer queries it's current player implementation about which of these formats are supported. 1. EnigmaPlayer uses a default IMediaFormatSelector to make an initial decision on which format to use. This tentative selection is called the "prospect" and may be changed by other IMediaFormatSelectors further down the chain. 1. If a custom media format selector has been provided for the EnigmaPlayer (either by EnigmaPlayer#setMediaFormatSelector or EnigmaPlayer#setMediaFormatPreference) it is also queried and allowed to change the prospect. 1. If the PlaybackProperties of the PlayRequest has a IMediaFormatSelector set this is also queried. 1. After this the prospect is marked as the selection and the corresponding media source is sent to the player implementation to be loaded.

Supplying a custom media format selector

We can supply a custom IMediaFormatSelector to EnigmaPlayer by calling EnigmaPlayer#setMediaFormatSelector(IMediaFormatSelector). This is typically done during setup of the player.

We can also use the convenience method EnigmaPlayer#setMediaFormatPreference(EnigmaMediaFormat...) and send in a preference order of media formats. Calling this method is equivalent to calling

enigmaPlayer.setMediaFormatSelector(new SimpleMediaFormatSelector(EnigmaMediaFormat...));

Simple example using EnigmaPlayer#setMediaFormatPreference(EnigmaMediaFormat...)

This will cause EnigmaPlayer to select HLS (preferring drm-protected) if it exists for an asset and the current player implementation supports it.

...
    private IEnigmaPlayer createEnigmaPlayer(ISession session, IPlayerImplementation playerImplementation) {
        EnigmaPlayer enigmaPlayer = new EnigmaPlayer(session, playerImplementation);
        ...
        //Prefer HLS
        enigmaPlayer.setMediaFormatPreference(
            EnigmaMediaFormat.HLS().fairplay(),
            EnigmaMediaFormat.HLS().unenc()
        );
        return enigmaPlayer;
    }
...

Advanced example using EnigmaPlayer#setMediaFormatSelector(IMediaFormatSelector)

Using EnigmaPlayer#setMediaFormatSelector(IMediaFormatSelector) we can supply a custom IMediaFormatSelector-implementation and have more advanced logic. In this example we will implement a IMediaFormatSelector that preferes unencrypted (not drm-protected) streams over encrypted streams.

...
    private IEnigmaPlayer createEnigmaPlayer(ISession session, IPlayerImplementation playerImplementation) {
        EnigmaPlayer enigmaPlayer = new EnigmaPlayer(session, playerImplementation);
        ...
        //Prefer unencrypted
        enigmaPlayer.setMediaFormatSelector(new UnencryptedMediaFormatSelector());
        return enigmaPlayer;
    }
...
private static class UnencryptedMediaFormatSelector implements IMediaFormatSelector {
    @Override
    public EnigmaMediaFormat select(EnigmaMediaFormat prospect, Collection<EnigmaMediaFormat> available) {
        if(prospect != null) {
            //If try to select corresponding unencrypted format for prospect
            if(prospect.getDrmTechnology() != EnigmaMediaFormat.DrmTechnology.NONE) {
                EnigmaMediaFormat unencrypted = new EnigmaMediaFormat(prospect.getStreamFormat(), EnigmaMediaFormat.DrmTechnology.NONE);
                if(available.contains(unencrypted)) {
                    return unencrypted;
                }
            }
            return prospect;
        } else {
            for(EnigmaMediaFormat enigmaMediaFormat : available) {
                if(enigmaMediaFormat.getDrmTechnology() == EnigmaMediaFormat.DrmTechnology.NONE) {
                    return enigmaMediaFormat;
                }
            }
            return null;
        }
    }
}
...

Overriding the media format selection logic for a single play request

We can also provide a IMediaFormatSelector to be applied for a specific PlayRequest. This is done by calling

PlaybackProperties#setMediaFormatSelector(IMediaFormatSelector)

or the convenience method

PlaybackProperties#setMediaFormatPreference(EnigmaMediaFormat...)

on the PlaybackProperties object passed to the PlayRequest.

...
private IPlaybackProperties getPlaybackProperties(EnigmaMediaFormat... preferredFormats) {
    PlaybackProperties playbackProperties = new PlaybackProperties();
    playbackProperties.setMediaFormatPreference(preferredFormats);
    return playbackProperties;
}
...
private void playInEnigmaPlayer(IPlayable playable) {
    IPlaybackProperties playbackProperties = getPlaybackProperties(
        EnigmaMediaFormat.DASH().widevine(),
        EnigmaMediaFormat.HLS().unenc(),
    );
    enigmaPlayer.play(new PlayRequest(playable, playbackProperties, ,,,));
}
...

Overriding the media format selection logic for downloads

In the Downloads and offline playback series we learn how to use the download API.

Similarly to playback we can provide a custom IMediaFormatSelector to select media format for download. This can either be set on the EnigmaDownloadContext during initialization (global for downloads), or it can be provided for a single DownloadStartRequest (similar to "Overriding the media format selection logic for a single play request").

Setting the default media format selection logic for downloads

Setting the default media format selector for downloads should be done when initializing the SDK.

public class MyApplication extends Application {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        ...
        EnigmaRiverContext.EnigmaRiverContextInitialization initialization = new EnigmaRiverContext.EnigmaRiverContextInitialization(exposureBaseUrl);
        ...
        initialization.forModule(EnigmaDownloadContext.MODULE_INFO).setDefaultDownloadFormatSelector(new IMediaFormatSelector() {
            @Override
            public EnigmaMediaFormat select(EnigmaMediaFormat prospect, Collection<EnigmaMediaFormat> available) {
                // ... select media format ... //
            }
        });
        ...
        EnigmaRiverContext.initialize(this, initialization);
    }
    ...
}

Overriding the default media format selection logic for a single DownloadStartRequest

We can provide a IMediaFormatSelector to be applied for a specific DownloadStartRequest. This is done by calling

DownloadStartRequest#setMediaFormatSelector(IMediaFormatSelector)

IEnigmaDownload enigmaDownload = new EnigmaDownload(businessUnit);
...
DownloadStartRequest downloadStartRequest = new DownloadStartRequest(assetId, session);
downloadStartRequest.setMediaFormatSelector(new IMediaFormatSelector() {
    @Override
    public EnigmaMediaFormat select(EnigmaMediaFormat prospect, Collection<EnigmaMediaFormat> available) {
        // ... select media format ... //
    }
});
enigmaDownload.startAssetDownload(context downloadStartRequest, resultHandler);

Forcing DRM L3 securityLevel

ExoPlayerTech initialization provides an option that would allow you to force L3 DRM when creating the player (under the hood we would use ExoPlayer FrameworkMediaDrm setPropertyString("securityLevel", "L3") ). But that should only be done on devices where this is really necessary, so app-developer would have to maintain such a list and you need to implement some logic around that in the frontend.

boolean useDrmSecurityLevelL3 = true;
new ExoPlayerTech(this, getString(R.string.app_name), useDrmSecurityLevelL3)

Table of Contents