Skip to content
On this page

ExoPlayer version 2.16.x to version 2.18.x

1. Require

Some general requirement is here

Prerequisite

  • Add a dependency in the build.gradle file of your app module. The following will add a dependency to the full library:
java
implementation 'com.google.android.exoplayer:exoplayer:2.16.0'
  // FIXME: If you don't use feature license encrypt, please comment line below
  // implementation 'com.sigma.packer:2.16.x:1.0.3'

2. Integrate MediaDrmCallback

Please see at MediaCallback

3. Create DrmSessionManagerProvider

java
import static com.google.android.exoplayer2.drm.DefaultDrmSessionManager.MODE_PLAYBACK;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;

import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.DrmSessionManagerProvider;
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.Util;
import com.google.common.primitives.Ints;
// FIXME: If you use license encrypt feature please uncomment line below
// import com.sigma.packer.SigmaMediaDrm;

import java.util.Map;

public class SmDrmSessionManagerProvider implements DrmSessionManagerProvider {
  private final Object lock;

  @GuardedBy("lock")
  private MediaItem.DrmConfiguration drmConfiguration;

  @GuardedBy("lock")
  private DrmSessionManager manager;

  @Nullable
  private DataSource.Factory drmHttpDataSourceFactory;
  @Nullable private String userAgent;

  public SmDrmSessionManagerProvider() {
    lock = new Object();
  }

  /**
   * Sets the {@link DataSource.Factory} which is used to create {@link HttpMediaDrmCallback}
   * instances. If {@code null} is passed a {@link DefaultHttpDataSource.Factory} is used.
   *
   * @param drmDataSourceFactory The data source factory or {@code null} to use {@link
   *     DefaultHttpDataSource.Factory}.
   */
  public void setDrmHttpDataSourceFactory(@Nullable DataSource.Factory drmDataSourceFactory) {
    this.drmHttpDataSourceFactory = drmDataSourceFactory;
  }

  /**
   * @deprecated Pass a custom {@link DataSource.Factory} to {@link
   *     #setDrmHttpDataSourceFactory(DataSource.Factory)} which sets the desired user agent on
   *     outgoing requests.
   */
  @Deprecated
  public void setDrmUserAgent(@Nullable String userAgent) {
    this.userAgent = userAgent;
  }

  @Override
  public DrmSessionManager get(MediaItem mediaItem) {
    checkNotNull(mediaItem.localConfiguration);
    @Nullable
    MediaItem.DrmConfiguration drmConfiguration = mediaItem.localConfiguration.drmConfiguration;
    if (drmConfiguration == null || Util.SDK_INT < 18) {
      return DrmSessionManager.DRM_UNSUPPORTED;
    }

    synchronized (lock) {
      if (!Util.areEqual(drmConfiguration, this.drmConfiguration)) {
        this.drmConfiguration = drmConfiguration;
        this.manager = createManager(drmConfiguration);
      }
      return checkNotNull(this.manager);
    }
  }

  @RequiresApi(18)
  private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
    DataSource.Factory dataSourceFactory =
      drmHttpDataSourceFactory != null
          ? drmHttpDataSourceFactory
          : new DefaultHttpDataSource.Factory().setUserAgent(userAgent);
    WidevineMediaDrmCallback httpDrmCallback =
      new WidevineMediaDrmCallback(
        drmConfiguration.licenseUri == null ? null : drmConfiguration.licenseUri.toString(),
        drmConfiguration.forceDefaultLicenseUri,
        (HttpDataSource.Factory)dataSourceFactory);
    for (Map.Entry<String, String> entry : drmConfiguration.licenseRequestHeaders.entrySet()) {
      httpDrmCallback.setKeyRequestProperty(entry.getKey(), entry.getValue());
    }
    DefaultDrmSessionManager drmSessionManager =
      new DefaultDrmSessionManager.Builder()
        // FIXME: If you use license encrypt feature please uncomment line below
        // .setUuidAndExoMediaDrmProvider(drmSchemeUuid, SigmaMediaDrm.DEFAULT_PROVIDER)
        .setUuidAndExoMediaDrmProvider(
            drmConfiguration.scheme, FrameworkMediaDrm.DEFAULT_PROVIDER)
        .setMultiSession(drmConfiguration.multiSession)
        .setPlayClearSamplesWithoutKeys(drmConfiguration.playClearContentWithoutKey)
        .setUseDrmSessionsForClearContent(
            Ints.toArray(drmConfiguration.forcedSessionTrackTypes))
        .build(httpDrmCallback);
    drmSessionManager.setMode(MODE_PLAYBACK, drmConfiguration.getKeySetId());
    return drmSessionManager;
  }
}

4. Create PlayerActivity using WidevineMediaDrmCallback

If you have experience with Exoplayer, then please avoid this chapter.

4.1. Create Media Item

java
private static List<MediaItem> createMediaItems(String mediaUrl) {
  List<MediaItem> mediaItems = new ArrayList<>();
  mediaItems.add(createMediaItem(mediaUrl));
  return mediaItems;
}

private static MediaItem createMediaItem(String mediaUrl) {
  MediaItem.Builder mediaItem = new MediaItem.Builder();
  mediaItem.setUri(Uri.parse(mediaUrl));
  mediaItem.setDrmConfiguration(
    new MediaItem.DrmConfiguration.Builder(Util.getDrmUuid("widevine"))
      .setMultiSession(true)
      .setLicenseUri(LICENSE_URL)
      .build()
  );
  return mediaItem.build();
}

4.2. Build Factory

java
private MediaSource.Factory createMediaSourceFactory() {
  SmDrmSessionManagerProvider drmSessionManagerProvider =
    new SmDrmSessionManagerProvider();
  drmSessionManagerProvider.setDrmHttpDataSourceFactory(
    buildHttpDataSourceFactory());
  return new DefaultMediaSourceFactory(/* context= */ this)
    .setDrmSessionManagerProvider(drmSessionManagerProvider);
}
public HttpDataSource.Factory buildHttpDataSourceFactory() {
  return new DefaultHttpDataSourceFactory(userAgent);
}

private DataSource.Factory buildDataSourceFactory() {
  DefaultDataSourceFactory upstreamFactory =
    new DefaultDataSourceFactory(this, buildHttpDataSourceFactory());
  return buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache());
}

private RenderersFactory buildRenderersFactory(boolean preferExtensionDecoders) {
  int extensionRendererMode =
    useExtensionRenderers()
      ? (preferExtensionRenderer
      ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
      : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
      : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF;
  return new DefaultRenderersFactory(/* context= */ this)
      .setExtensionRendererMode(extensionRendererMode);
}

3.5. Init Player with MediaSource

java
protected boolean initializePlayer() {
  if (player == null) {
    mediaItems = createMediaItems(MEDIA_URL);
    if (mediaItems.isEmpty()) {
        return false;
    }
    ExoPlayer.Builder playerBuilder =
        new ExoPlayer.Builder(/* context= */ this)
            .setMediaSourceFactory(createMediaSourceFactory());
    playerBuilder.setRenderersFactory(buildRenderersFactory(true));

    player = playerBuilder.build();
    player.setTrackSelectionParameters(new TrackSelectionParameters.Builder(/* context= */ this).build());
    player.setAudioAttributes(AudioAttributes.DEFAULT, /* handleAudioFocus= */ true);
    player.setPlayWhenReady(true);
    playerView.setPlayer(player);
  }
  player.setMediaItems(mediaItems, true);
  player.prepare();
  return true;
}

5. Sample Code

Sample source code