Ad

Play Encrypted Hls In Exoplayer Using Encrypted Keys

I am trying to play the encrypted video using .m3u8 file. I stored my video in AWS and created the .ts files and a master playlist. Aws provided me some keys for that encrypted video file. Now I have to use those keys in exoplayer. I have tried to use Aes128DataSource and DrmSessionManager but no luck.

The key types are:

Encryption Key: ####################################################################
Encryption Key MD5: ################
Encryption Initialization Vector : #############

Bellow the code I am using to play the hls video. Its plays the video smoothly without any problem. I just need to know where and how to use the keys to play the encrypted video.

            String VIDEO_URL = "https://s3.amazonaws.com/######.###.##/videos/mobiletest/mobilemaster.m3u8";

            //Create a default TrackSelector
            BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
            TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
            TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

            // Create a default LoadControl
            LoadControl loadControl = new DefaultLoadControl();
            //Bis. Create a RenderFactory
            RenderersFactory renderersFactory = new DefaultRenderersFactory(this);


            //Create the player
            player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
            simpleExoPlayerView = new SimpleExoPlayerView(this);
            simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);


            //Set media controller
            simpleExoPlayerView.setUseController(true);
            simpleExoPlayerView.requestFocus();

            // Bind the player to the view.
            simpleExoPlayerView.setPlayer(player);

            // Set the media source
            Uri mp4VideoUri = Uri.parse(VIDEO_URL);

            //Measures bandwidth during playback. Can be null if not required.
            DefaultBandwidthMeter bandwidthMeterA = new DefaultBandwidthMeter();

            //Produces DataSource instances through which media data is loaded.
            DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "PiwikVideoApp"), bandwidthMeterA);

            //Produces Extractor instances for parsing the media data.
            ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

            //FOR LIVE STREAM LINK:
            MediaSource videoSource = new HlsMediaSource(mp4VideoUri, dataSourceFactory, 1, null, null);
            final MediaSource mediaSource = videoSource;


            player.prepare(videoSource);

I have encrypted the video using AWS Elastic Transcoder, and using exoplayer version: 2.6.0

Ad

Answer

I have found a solution. The trick you need to do is to create your own custom data sources. You need to create a class that extends HttpDataSource.BaseFactory and add some code from DefaultHttpDataSourceFactory. I know it sounds crazy, but I have solved it this way. Don't worry, I am pasting here the whole code of that custom class.

import android.support.annotation.Nullable;

import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;

public class CustomDataSourcesFactory extends HttpDataSource.BaseFactory{
    private final String userAgent;
    private final @Nullable
    TransferListener listener;
    private final int connectTimeoutMillis;
    private final int readTimeoutMillis;
    private final boolean allowCrossProtocolRedirects;

    /**
     * Constructs a DefaultHttpDataSourceFactory. Sets {@link
     * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link
     * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables
     * cross-protocol redirects.
     *
     * @param userAgent The User-Agent string that should be used.
     */
    public CustomDataSourcesFactory(String userAgent) {
        this(userAgent, null);
    }

    /**
     * Constructs a DefaultHttpDataSourceFactory. Sets {@link
     * DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS} as the connection timeout, {@link
     * DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS} as the read timeout and disables
     * cross-protocol redirects.
     *
     * @param userAgent The User-Agent string that should be used.
     * @param listener An optional listener.
     */
    public CustomDataSourcesFactory(String userAgent, @Nullable TransferListener listener) {
        this(userAgent, listener, DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS,
                DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, false);
    }

    /**
     * @param userAgent The User-Agent string that should be used.
     * @param connectTimeoutMillis The connection timeout that should be used when requesting remote
     *     data, in milliseconds. A timeout of zero is interpreted as an infinite timeout.
     * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in
     *     milliseconds. A timeout of zero is interpreted as an infinite timeout.
     * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
     *     to HTTPS and vice versa) are enabled.
     */
    public CustomDataSourcesFactory(
            String userAgent,
            int connectTimeoutMillis,
            int readTimeoutMillis,
            boolean allowCrossProtocolRedirects) {
        this(
                userAgent,
                /* listener= */ null,
                connectTimeoutMillis,
                readTimeoutMillis,
                allowCrossProtocolRedirects);
    }

    /**
     * @param userAgent The User-Agent string that should be used.
     * @param listener An optional listener.
     * @param connectTimeoutMillis The connection timeout that should be used when requesting remote
     *     data, in milliseconds. A timeout of zero is interpreted as an infinite timeout.
     * @param readTimeoutMillis The read timeout that should be used when requesting remote data, in
     *     milliseconds. A timeout of zero is interpreted as an infinite timeout.
     * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
     *     to HTTPS and vice versa) are enabled.
     */
    public CustomDataSourcesFactory(
            String userAgent,
            @Nullable TransferListener listener,
            int connectTimeoutMillis,
            int readTimeoutMillis,
            boolean allowCrossProtocolRedirects) {
        this.userAgent = userAgent;
        this.listener = listener;
        this.connectTimeoutMillis = connectTimeoutMillis;
        this.readTimeoutMillis = readTimeoutMillis;
        this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
    }

    @Override
    protected HttpDataSource createDataSourceInternal(
            HttpDataSource.RequestProperties defaultRequestProperties) {
        DefaultHttpDataSource defaultHttpDataSource = new DefaultHttpDataSource(userAgent, null, listener, connectTimeoutMillis,
                readTimeoutMillis, allowCrossProtocolRedirects, defaultRequestProperties);
        defaultHttpDataSource.setRequestProperty("your header", "your token");
        return defaultHttpDataSource;
    }}

Do you see the method createDataSourceInternal? I am returning a DefaultHttpDataSource object initialised with a header key and a token defaultHttpDataSource.setRequestProperty("your header", "your token");. So now your exoplayer will hit the accusation URL with this header key and token value which will be checked by your server side in order to verify the valid request. Your hls playlist(.m3u8) contains the accusation URL. Exoplayer uses this URL automatically. All you need to do is to tell the player to use the validation keys.

So again here is the usage code of Your custom data sources:

//ExoPlayer implementation
    //Create a default TrackSelector
    BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

    // Create a default LoadControl
    LoadControl loadControl = new DefaultLoadControl();
    //Bis. Create a RenderFactory
    RenderersFactory renderersFactory = new DefaultRenderersFactory(this);


    //Create the player
    player = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl);
    simpleExoPlayerView = new SimpleExoPlayerView(this);
    simpleExoPlayerView = (SimpleExoPlayerView) findViewById(R.id.player_view);


    //Set media controller
    simpleExoPlayerView.setUseController(true);
    simpleExoPlayerView.requestFocus();

    // Bind the player to the view.
    simpleExoPlayerView.setPlayer(player);

    // Set the media source
    Uri mp4VideoUri = Uri.parse(VIDEO_URL);

    //DefaultHttpDataSource source = new DefaultHttpDataSource(Util.getUserAgent(this, "appAgent"), null);
    //source.setRequestProperty("header", "user token");

    //Measures bandwidth during playback. Can be null if not required.
    DefaultBandwidthMeter bandwidthMeterA = new DefaultBandwidthMeter();

    //DefaultDataSourceFactory o = new DefaultDataSourceFactory(this, null, new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "appAgent"), bandwidthMeterA));
    CustomDataSourcesFactory o = new CustomDataSourcesFactory("Exoplayer");

    //Produces DataSource instances through which media data is loaded.
    DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, "PiwikVideoApp"), bandwidthMeterA);

    //Produces Extractor instances for parsing the media data.
    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

    //FOR LIVE STREAM LINK:
    MediaSource videoSource = new HlsMediaSource(mp4VideoUri, o, 1, null, null);
    final MediaSource mediaSource = videoSource;


    player.prepare(videoSource);

This problem was too much critical for me because I could not found any tutorial, resources or idea to solve this. I am writing this because I don't want other noob people like me to suffer from this.

If you are not clear enough, feel free to ask here. I will assist you further. I have also the server side code in order to generate the decryption key from AWS encrypted key.

Ad
source: stackoverflow.com
Ad