Implement multi session to support DRM key rotation.
Spec: https://storage.googleapis.com/wvdocs/Widevine_DRM_Android_Using_Key_Rotation.pdf 1. Implement multisession to support drm key rotation 2. Put MediaDrmEventListener back to manager since this is a per mediaDrm thing. 3. It seems diffrenciate between single/multi session is unnecessary. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=167602965
This commit is contained in:
parent
e16610a82c
commit
b62eab63a4
@ -89,6 +89,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi
|
|||||||
public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
|
public static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
|
||||||
public static final String DRM_LICENSE_URL = "drm_license_url";
|
public static final String DRM_LICENSE_URL = "drm_license_url";
|
||||||
public static final String DRM_KEY_REQUEST_PROPERTIES = "drm_key_request_properties";
|
public static final String DRM_KEY_REQUEST_PROPERTIES = "drm_key_request_properties";
|
||||||
|
public static final String DRM_MULTI_SESSION = "drm_multi_session";
|
||||||
public static final String PREFER_EXTENSION_DECODERS = "prefer_extension_decoders";
|
public static final String PREFER_EXTENSION_DECODERS = "prefer_extension_decoders";
|
||||||
|
|
||||||
public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
|
public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
|
||||||
@ -264,13 +265,14 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi
|
|||||||
if (drmSchemeUuid != null) {
|
if (drmSchemeUuid != null) {
|
||||||
String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL);
|
String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL);
|
||||||
String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES);
|
String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES);
|
||||||
|
boolean multiSession = intent.getBooleanExtra(DRM_MULTI_SESSION, false);
|
||||||
int errorStringId = R.string.error_drm_unknown;
|
int errorStringId = R.string.error_drm_unknown;
|
||||||
if (Util.SDK_INT < 18) {
|
if (Util.SDK_INT < 18) {
|
||||||
errorStringId = R.string.error_drm_not_supported;
|
errorStringId = R.string.error_drm_not_supported;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
drmSessionManager = buildDrmSessionManagerV18(drmSchemeUuid, drmLicenseUrl,
|
drmSessionManager = buildDrmSessionManagerV18(drmSchemeUuid, drmLicenseUrl,
|
||||||
keyRequestPropertiesArray);
|
keyRequestPropertiesArray, multiSession);
|
||||||
} catch (UnsupportedDrmException e) {
|
} catch (UnsupportedDrmException e) {
|
||||||
errorStringId = e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
|
errorStringId = e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
|
||||||
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown;
|
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown;
|
||||||
@ -379,7 +381,8 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18(UUID uuid,
|
private DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18(UUID uuid,
|
||||||
String licenseUrl, String[] keyRequestPropertiesArray) throws UnsupportedDrmException {
|
String licenseUrl, String[] keyRequestPropertiesArray, boolean multiSession)
|
||||||
|
throws UnsupportedDrmException {
|
||||||
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl,
|
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl,
|
||||||
buildHttpDataSourceFactory(false));
|
buildHttpDataSourceFactory(false));
|
||||||
if (keyRequestPropertiesArray != null) {
|
if (keyRequestPropertiesArray != null) {
|
||||||
@ -389,7 +392,7 @@ public class PlayerActivity extends Activity implements OnClickListener, EventLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback,
|
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback,
|
||||||
null, mainHandler, eventLogger);
|
null, mainHandler, eventLogger, multiSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releasePlayer() {
|
private void releasePlayer() {
|
||||||
|
@ -182,6 +182,7 @@ public class SampleChooserActivity extends Activity {
|
|||||||
UUID drmUuid = null;
|
UUID drmUuid = null;
|
||||||
String drmLicenseUrl = null;
|
String drmLicenseUrl = null;
|
||||||
String[] drmKeyRequestProperties = null;
|
String[] drmKeyRequestProperties = null;
|
||||||
|
boolean drmMultiSession = false;
|
||||||
boolean preferExtensionDecoders = false;
|
boolean preferExtensionDecoders = false;
|
||||||
ArrayList<UriSample> playlistSamples = null;
|
ArrayList<UriSample> playlistSamples = null;
|
||||||
String adTagUri = null;
|
String adTagUri = null;
|
||||||
@ -220,6 +221,9 @@ public class SampleChooserActivity extends Activity {
|
|||||||
reader.endObject();
|
reader.endObject();
|
||||||
drmKeyRequestProperties = drmKeyRequestPropertiesList.toArray(new String[0]);
|
drmKeyRequestProperties = drmKeyRequestPropertiesList.toArray(new String[0]);
|
||||||
break;
|
break;
|
||||||
|
case "drm_multi_session":
|
||||||
|
drmMultiSession = reader.nextBoolean();
|
||||||
|
break;
|
||||||
case "prefer_extension_decoders":
|
case "prefer_extension_decoders":
|
||||||
Assertions.checkState(!insidePlaylist,
|
Assertions.checkState(!insidePlaylist,
|
||||||
"Invalid attribute on nested item: prefer_extension_decoders");
|
"Invalid attribute on nested item: prefer_extension_decoders");
|
||||||
@ -242,15 +246,16 @@ public class SampleChooserActivity extends Activity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
reader.endObject();
|
reader.endObject();
|
||||||
|
DrmInfo drmInfo = drmUuid == null ? null : new DrmInfo(drmUuid, drmLicenseUrl,
|
||||||
|
drmKeyRequestProperties, drmMultiSession);
|
||||||
if (playlistSamples != null) {
|
if (playlistSamples != null) {
|
||||||
UriSample[] playlistSamplesArray = playlistSamples.toArray(
|
UriSample[] playlistSamplesArray = playlistSamples.toArray(
|
||||||
new UriSample[playlistSamples.size()]);
|
new UriSample[playlistSamples.size()]);
|
||||||
return new PlaylistSample(sampleName, drmUuid, drmLicenseUrl, drmKeyRequestProperties,
|
return new PlaylistSample(sampleName, preferExtensionDecoders, drmInfo,
|
||||||
preferExtensionDecoders, playlistSamplesArray);
|
playlistSamplesArray);
|
||||||
} else {
|
} else {
|
||||||
return new UriSample(sampleName, drmUuid, drmLicenseUrl, drmKeyRequestProperties,
|
return new UriSample(sampleName, preferExtensionDecoders, drmInfo, uri, extension,
|
||||||
preferExtensionDecoders, uri, extension, adTagUri);
|
adTagUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,31 +377,47 @@ public class SampleChooserActivity extends Activity {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class Sample {
|
private static final class DrmInfo {
|
||||||
|
|
||||||
public final String name;
|
|
||||||
public final boolean preferExtensionDecoders;
|
|
||||||
public final UUID drmSchemeUuid;
|
public final UUID drmSchemeUuid;
|
||||||
public final String drmLicenseUrl;
|
public final String drmLicenseUrl;
|
||||||
public final String[] drmKeyRequestProperties;
|
public final String[] drmKeyRequestProperties;
|
||||||
|
public final boolean drmMultiSession;
|
||||||
|
|
||||||
public Sample(String name, UUID drmSchemeUuid, String drmLicenseUrl,
|
public DrmInfo(UUID drmSchemeUuid, String drmLicenseUrl,
|
||||||
String[] drmKeyRequestProperties, boolean preferExtensionDecoders) {
|
String[] drmKeyRequestProperties, boolean drmMultiSession) {
|
||||||
this.name = name;
|
|
||||||
this.drmSchemeUuid = drmSchemeUuid;
|
this.drmSchemeUuid = drmSchemeUuid;
|
||||||
this.drmLicenseUrl = drmLicenseUrl;
|
this.drmLicenseUrl = drmLicenseUrl;
|
||||||
this.drmKeyRequestProperties = drmKeyRequestProperties;
|
this.drmKeyRequestProperties = drmKeyRequestProperties;
|
||||||
|
this.drmMultiSession = drmMultiSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateIntent(Intent intent) {
|
||||||
|
Assertions.checkNotNull(intent);
|
||||||
|
intent.putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drmSchemeUuid.toString());
|
||||||
|
intent.putExtra(PlayerActivity.DRM_LICENSE_URL, drmLicenseUrl);
|
||||||
|
intent.putExtra(PlayerActivity.DRM_KEY_REQUEST_PROPERTIES, drmKeyRequestProperties);
|
||||||
|
intent.putExtra(PlayerActivity.DRM_MULTI_SESSION, drmMultiSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private abstract static class Sample {
|
||||||
|
public final String name;
|
||||||
|
public final boolean preferExtensionDecoders;
|
||||||
|
public final DrmInfo drmInfo;
|
||||||
|
|
||||||
|
public Sample(String name, boolean preferExtensionDecoders, DrmInfo drmInfo) {
|
||||||
|
this.name = name;
|
||||||
this.preferExtensionDecoders = preferExtensionDecoders;
|
this.preferExtensionDecoders = preferExtensionDecoders;
|
||||||
|
this.drmInfo = drmInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Intent buildIntent(Context context) {
|
public Intent buildIntent(Context context) {
|
||||||
Intent intent = new Intent(context, PlayerActivity.class);
|
Intent intent = new Intent(context, PlayerActivity.class);
|
||||||
intent.putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, preferExtensionDecoders);
|
intent.putExtra(PlayerActivity.PREFER_EXTENSION_DECODERS, preferExtensionDecoders);
|
||||||
if (drmSchemeUuid != null) {
|
if (drmInfo != null) {
|
||||||
intent.putExtra(PlayerActivity.DRM_SCHEME_UUID_EXTRA, drmSchemeUuid.toString());
|
drmInfo.updateIntent(intent);
|
||||||
intent.putExtra(PlayerActivity.DRM_LICENSE_URL, drmLicenseUrl);
|
|
||||||
intent.putExtra(PlayerActivity.DRM_KEY_REQUEST_PROPERTIES, drmKeyRequestProperties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,10 +429,9 @@ public class SampleChooserActivity extends Activity {
|
|||||||
public final String extension;
|
public final String extension;
|
||||||
public final String adTagUri;
|
public final String adTagUri;
|
||||||
|
|
||||||
public UriSample(String name, UUID drmSchemeUuid, String drmLicenseUrl,
|
public UriSample(String name, boolean preferExtensionDecoders, DrmInfo drmInfo, String uri,
|
||||||
String[] drmKeyRequestProperties, boolean preferExtensionDecoders, String uri,
|
|
||||||
String extension, String adTagUri) {
|
String extension, String adTagUri) {
|
||||||
super(name, drmSchemeUuid, drmLicenseUrl, drmKeyRequestProperties, preferExtensionDecoders);
|
super(name, preferExtensionDecoders, drmInfo);
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
this.adTagUri = adTagUri;
|
this.adTagUri = adTagUri;
|
||||||
@ -432,10 +452,9 @@ public class SampleChooserActivity extends Activity {
|
|||||||
|
|
||||||
public final UriSample[] children;
|
public final UriSample[] children;
|
||||||
|
|
||||||
public PlaylistSample(String name, UUID drmSchemeUuid, String drmLicenseUrl,
|
public PlaylistSample(String name, boolean preferExtensionDecoders, DrmInfo drmInfo,
|
||||||
String[] drmKeyRequestProperties, boolean preferExtensionDecoders,
|
|
||||||
UriSample... children) {
|
UriSample... children) {
|
||||||
super(name, drmSchemeUuid, drmLicenseUrl, drmKeyRequestProperties, preferExtensionDecoders);
|
super(name, preferExtensionDecoders, drmInfo);
|
||||||
this.children = children;
|
this.children = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,29 +27,36 @@ import android.os.Message;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener;
|
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
||||||
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
|
import java.util.Arrays;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer2.util.Util;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DrmSession} that supports playbacks using {@link MediaDrm}.
|
* A {@link DrmSession} that supports playbacks using {@link MediaDrm}.
|
||||||
*/
|
*/
|
||||||
@TargetApi(18)
|
@TargetApi(18)
|
||||||
/* package */ class DefaultDrmSession<T extends ExoMediaCrypto> implements DrmSession<T> {
|
/* package */ class DefaultDrmSession<T extends ExoMediaCrypto> implements DrmSession<T> {
|
||||||
private static final String TAG = "DefaultDrmSession";
|
|
||||||
|
|
||||||
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
|
/**
|
||||||
|
* Listener of {@link DefaultDrmSession} events.
|
||||||
|
*/
|
||||||
|
public interface EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called each time provision is completed.
|
||||||
|
*/
|
||||||
|
void onProvisionCompleted();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "DefaultDrmSession";
|
||||||
|
|
||||||
private static final int MSG_PROVISION = 0;
|
private static final int MSG_PROVISION = 0;
|
||||||
private static final int MSG_KEYS = 1;
|
private static final int MSG_KEYS = 1;
|
||||||
|
|
||||||
private static final int MAX_LICENSE_DURATION_TO_RENEW = 60;
|
private static final int MAX_LICENSE_DURATION_TO_RENEW = 60;
|
||||||
|
|
||||||
private final Handler eventHandler;
|
private final Handler eventHandler;
|
||||||
@ -58,7 +65,6 @@ import java.util.UUID;
|
|||||||
private final HashMap<String, String> optionalKeyRequestParameters;
|
private final HashMap<String, String> optionalKeyRequestParameters;
|
||||||
/* package */ final MediaDrmCallback callback;
|
/* package */ final MediaDrmCallback callback;
|
||||||
/* package */ final UUID uuid;
|
/* package */ final UUID uuid;
|
||||||
/* package */ MediaDrmHandler mediaDrmHandler;
|
|
||||||
/* package */ PostResponseHandler postResponseHandler;
|
/* package */ PostResponseHandler postResponseHandler;
|
||||||
private HandlerThread requestHandlerThread;
|
private HandlerThread requestHandlerThread;
|
||||||
private Handler postRequestHandler;
|
private Handler postRequestHandler;
|
||||||
@ -66,13 +72,14 @@ import java.util.UUID;
|
|||||||
@DefaultDrmSessionManager.Mode
|
@DefaultDrmSessionManager.Mode
|
||||||
private final int mode;
|
private final int mode;
|
||||||
private int openCount;
|
private int openCount;
|
||||||
private boolean provisioningInProgress;
|
private final AtomicBoolean provisioningInProgress;
|
||||||
|
private final EventListener sessionEventListener;
|
||||||
@DrmSession.State
|
@DrmSession.State
|
||||||
private int state;
|
private int state;
|
||||||
private T mediaCrypto;
|
private T mediaCrypto;
|
||||||
private DrmSessionException lastException;
|
private DrmSessionException lastException;
|
||||||
private final byte[] schemeInitData;
|
private final byte[] initData;
|
||||||
private final String schemeMimeType;
|
private final String mimeType;
|
||||||
private byte[] sessionId;
|
private byte[] sessionId;
|
||||||
private byte[] offlineLicenseKeySetId;
|
private byte[] offlineLicenseKeySetId;
|
||||||
|
|
||||||
@ -90,11 +97,12 @@ import java.util.UUID;
|
|||||||
* @param eventHandler The handler to post listener events.
|
* @param eventHandler The handler to post listener events.
|
||||||
* @param eventListener The DRM session manager event listener.
|
* @param eventListener The DRM session manager event listener.
|
||||||
*/
|
*/
|
||||||
public DefaultDrmSession(UUID uuid, ExoMediaDrm<T> mediaDrm, DrmInitData initData,
|
public DefaultDrmSession(UUID uuid, ExoMediaDrm<T> mediaDrm, byte[] initData, String mimeType,
|
||||||
@DefaultDrmSessionManager.Mode int mode, byte[] offlineLicenseKeySetId,
|
@DefaultDrmSessionManager.Mode int mode, byte[] offlineLicenseKeySetId,
|
||||||
HashMap<String, String> optionalKeyRequestParameters, MediaDrmCallback callback,
|
HashMap<String, String> optionalKeyRequestParameters, MediaDrmCallback callback,
|
||||||
Looper playbackLooper, Handler eventHandler,
|
Looper playbackLooper, Handler eventHandler,
|
||||||
DefaultDrmSessionManager.EventListener eventListener) {
|
DefaultDrmSessionManager.EventListener eventListener, AtomicBoolean provisioningInProgress,
|
||||||
|
EventListener sessionEventListener) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.mediaDrm = mediaDrm;
|
this.mediaDrm = mediaDrm;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
@ -104,45 +112,23 @@ import java.util.UUID;
|
|||||||
|
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
|
this.provisioningInProgress = provisioningInProgress;
|
||||||
|
this.sessionEventListener = sessionEventListener;
|
||||||
state = STATE_OPENING;
|
state = STATE_OPENING;
|
||||||
|
|
||||||
mediaDrmHandler = new MediaDrmHandler(playbackLooper);
|
|
||||||
mediaDrm.setOnEventListener(new MediaDrmEventListener());
|
|
||||||
postResponseHandler = new PostResponseHandler(playbackLooper);
|
postResponseHandler = new PostResponseHandler(playbackLooper);
|
||||||
requestHandlerThread = new HandlerThread("DrmRequestHandler");
|
requestHandlerThread = new HandlerThread("DrmRequestHandler");
|
||||||
requestHandlerThread.start();
|
requestHandlerThread.start();
|
||||||
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
|
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
|
||||||
|
|
||||||
// Parse init data.
|
|
||||||
byte[] schemeInitData = null;
|
|
||||||
String schemeMimeType = null;
|
|
||||||
if (offlineLicenseKeySetId == null) {
|
if (offlineLicenseKeySetId == null) {
|
||||||
SchemeData data = getSchemeData(initData, uuid);
|
this.initData = initData;
|
||||||
if (data == null) {
|
this.mimeType = mimeType;
|
||||||
onError(new IllegalStateException("Media does not support uuid: " + uuid));
|
|
||||||
} else {
|
} else {
|
||||||
schemeInitData = data.data;
|
this.initData = null;
|
||||||
schemeMimeType = data.mimeType;
|
this.mimeType = null;
|
||||||
if (Util.SDK_INT < 21) {
|
|
||||||
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
|
|
||||||
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData, uuid);
|
|
||||||
if (psshData == null) {
|
|
||||||
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
|
|
||||||
} else {
|
|
||||||
schemeInitData = psshData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Util.SDK_INT < 26 && C.CLEARKEY_UUID.equals(uuid)
|
|
||||||
&& (MimeTypes.VIDEO_MP4.equals(schemeMimeType)
|
|
||||||
|| MimeTypes.AUDIO_MP4.equals(schemeMimeType))) {
|
|
||||||
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
|
|
||||||
schemeMimeType = CENC_SCHEME_MIME_TYPE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.schemeInitData = schemeInitData;
|
|
||||||
this.schemeMimeType = schemeMimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Life cycle.
|
// Life cycle.
|
||||||
|
|
||||||
@ -163,9 +149,6 @@ import java.util.UUID;
|
|||||||
public boolean release() {
|
public boolean release() {
|
||||||
if (--openCount == 0) {
|
if (--openCount == 0) {
|
||||||
state = STATE_RELEASED;
|
state = STATE_RELEASED;
|
||||||
provisioningInProgress = false;
|
|
||||||
mediaDrmHandler.removeCallbacksAndMessages(null);
|
|
||||||
mediaDrmHandler = null;
|
|
||||||
postResponseHandler.removeCallbacksAndMessages(null);
|
postResponseHandler.removeCallbacksAndMessages(null);
|
||||||
postRequestHandler.removeCallbacksAndMessages(null);
|
postRequestHandler.removeCallbacksAndMessages(null);
|
||||||
postRequestHandler = null;
|
postRequestHandler = null;
|
||||||
@ -182,6 +165,14 @@ import java.util.UUID;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canReuse(byte[] initData) {
|
||||||
|
return Arrays.equals(this.initData, initData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSessionId(byte[] sessionId) {
|
||||||
|
return Arrays.equals(this.sessionId, sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
// DrmSession Implementation.
|
// DrmSession Implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -245,21 +236,15 @@ import java.util.UUID;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void postProvisionRequest() {
|
private void postProvisionRequest() {
|
||||||
if (provisioningInProgress) {
|
if (provisioningInProgress.getAndSet(true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
provisioningInProgress = true;
|
|
||||||
ProvisionRequest request = mediaDrm.getProvisionRequest();
|
ProvisionRequest request = mediaDrm.getProvisionRequest();
|
||||||
postRequestHandler.obtainMessage(MSG_PROVISION, request).sendToTarget();
|
postRequestHandler.obtainMessage(MSG_PROVISION, request).sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onProvisionResponse(Object response) {
|
private void onProvisionResponse(Object response) {
|
||||||
provisioningInProgress = false;
|
provisioningInProgress.set(false);
|
||||||
if (state != STATE_OPENING && !isOpen()) {
|
|
||||||
// This event is stale.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response instanceof Exception) {
|
if (response instanceof Exception) {
|
||||||
onError((Exception) response);
|
onError((Exception) response);
|
||||||
return;
|
return;
|
||||||
@ -267,11 +252,24 @@ import java.util.UUID;
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
mediaDrm.provideProvisionResponse((byte[]) response);
|
mediaDrm.provideProvisionResponse((byte[]) response);
|
||||||
if (openInternal(false)) {
|
|
||||||
doLicense();
|
|
||||||
}
|
|
||||||
} catch (DeniedByServerException e) {
|
} catch (DeniedByServerException e) {
|
||||||
onError(e);
|
onError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionEventListener != null) {
|
||||||
|
sessionEventListener.onProvisionCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProvisionCompleted() {
|
||||||
|
if (state != STATE_OPENING && !isOpen()) {
|
||||||
|
// This event is stale.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openInternal(false)) {
|
||||||
|
doLicense();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,6 +320,8 @@ import java.util.UUID;
|
|||||||
postKeyRequest(MediaDrm.KEY_TYPE_RELEASE);
|
postKeyRequest(MediaDrm.KEY_TYPE_RELEASE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ import java.util.UUID;
|
|||||||
private void postKeyRequest(int type) {
|
private void postKeyRequest(int type) {
|
||||||
byte[] scope = type == MediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
|
byte[] scope = type == MediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
|
||||||
try {
|
try {
|
||||||
KeyRequest request = mediaDrm.getKeyRequest(scope, schemeInitData, schemeMimeType, type,
|
KeyRequest request = mediaDrm.getKeyRequest(scope, initData, mimeType, type,
|
||||||
optionalKeyRequestParameters);
|
optionalKeyRequestParameters);
|
||||||
postRequestHandler.obtainMessage(MSG_KEYS, request).sendToTarget();
|
postRequestHandler.obtainMessage(MSG_KEYS, request).sendToTarget();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -433,20 +433,12 @@ import java.util.UUID;
|
|||||||
return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS;
|
return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("HandlerLeak")
|
|
||||||
private class MediaDrmHandler extends Handler {
|
|
||||||
|
|
||||||
public MediaDrmHandler(Looper looper) {
|
|
||||||
super(looper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
public void onMediaDrmEvent(int what) {
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
if (!isOpen()) {
|
if (!isOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (msg.what) {
|
switch (what) {
|
||||||
case MediaDrm.EVENT_KEY_REQUIRED:
|
case MediaDrm.EVENT_KEY_REQUIRED:
|
||||||
doLicense();
|
doLicense();
|
||||||
break;
|
break;
|
||||||
@ -460,19 +452,8 @@ import java.util.UUID;
|
|||||||
state = STATE_OPENED;
|
state = STATE_OPENED;
|
||||||
postProvisionRequest();
|
postProvisionRequest();
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
}
|
break;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MediaDrmEventListener implements OnEventListener<T> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEvent(ExoMediaDrm<? extends T> md, byte[] sessionId, int event, int extra,
|
|
||||||
byte[] data) {
|
|
||||||
if (mode == DefaultDrmSessionManager.MODE_PLAYBACK) {
|
|
||||||
mediaDrmHandler.sendEmptyMessage(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -493,6 +474,9 @@ import java.util.UUID;
|
|||||||
case MSG_KEYS:
|
case MSG_KEYS:
|
||||||
onKeyResponse(msg.obj);
|
onKeyResponse(msg.obj);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,20 +511,4 @@ import java.util.UUID;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts {@link SchemeData} suitable for the given DRM scheme {@link UUID}.
|
|
||||||
*
|
|
||||||
* @param drmInitData The {@link DrmInitData} from which to extract the {@link SchemeData}.
|
|
||||||
* @param uuid The UUID of the scheme.
|
|
||||||
* @return The extracted {@link SchemeData}, or null if no suitable data is present.
|
|
||||||
*/
|
|
||||||
public static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid) {
|
|
||||||
SchemeData schemeData = drmInitData.get(uuid);
|
|
||||||
if (schemeData == null && C.CLEARKEY_UUID.equals(uuid)) {
|
|
||||||
// If present, the Common PSSH box should be used for ClearKey.
|
|
||||||
schemeData = drmInitData.get(C.COMMON_PSSH_UUID);
|
|
||||||
}
|
|
||||||
return schemeData;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,27 +15,36 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.drm;
|
package com.google.android.exoplayer2.drm;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.media.MediaDrm;
|
import android.media.MediaDrm;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||||
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener;
|
||||||
|
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link DrmSessionManager} that supports playbacks using {@link MediaDrm}.
|
* A {@link DrmSessionManager} that supports playbacks using {@link MediaDrm}.
|
||||||
*/
|
*/
|
||||||
@TargetApi(18)
|
@TargetApi(18)
|
||||||
public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSessionManager<T> {
|
public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSessionManager<T>,
|
||||||
|
DefaultDrmSession.EventListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener of {@link DefaultDrmSessionManager} events.
|
* Listener of {@link DefaultDrmSessionManager} events.
|
||||||
@ -70,6 +79,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
* The key to use when passing CustomData to a PlayReady instance in an optional parameter map.
|
* The key to use when passing CustomData to a PlayReady instance in an optional parameter map.
|
||||||
*/
|
*/
|
||||||
public static final String PLAYREADY_CUSTOM_DATA_KEY = "PRCustomData";
|
public static final String PLAYREADY_CUSTOM_DATA_KEY = "PRCustomData";
|
||||||
|
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
|
||||||
|
|
||||||
/** Determines the action to be done after a session acquired. */
|
/** Determines the action to be done after a session acquired. */
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@ -93,14 +103,17 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
private final EventListener eventListener;
|
private final EventListener eventListener;
|
||||||
private final ExoMediaDrm<T> mediaDrm;
|
private final ExoMediaDrm<T> mediaDrm;
|
||||||
private final HashMap<String, String> optionalKeyRequestParameters;
|
private final HashMap<String, String> optionalKeyRequestParameters;
|
||||||
|
|
||||||
private final MediaDrmCallback callback;
|
private final MediaDrmCallback callback;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
|
private final boolean multiSession;
|
||||||
|
|
||||||
private Looper playbackLooper;
|
private Looper playbackLooper;
|
||||||
private int mode;
|
private int mode;
|
||||||
private byte[] offlineLicenseKeySetId;
|
private byte[] offlineLicenseKeySetId;
|
||||||
private DefaultDrmSession<T> session;
|
|
||||||
|
private final List<DefaultDrmSession<T>> sessions;
|
||||||
|
private final AtomicBoolean provisioningInProgress;
|
||||||
|
/* package */ MediaDrmHandler mediaDrmHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new instance using the Widevine scheme.
|
* Instantiates a new instance using the Widevine scheme.
|
||||||
@ -163,7 +176,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
UUID uuid, MediaDrmCallback callback, HashMap<String, String> optionalKeyRequestParameters,
|
UUID uuid, MediaDrmCallback callback, HashMap<String, String> optionalKeyRequestParameters,
|
||||||
Handler eventHandler, EventListener eventListener) throws UnsupportedDrmException {
|
Handler eventHandler, EventListener eventListener) throws UnsupportedDrmException {
|
||||||
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), callback,
|
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), callback,
|
||||||
optionalKeyRequestParameters, eventHandler, eventListener);
|
optionalKeyRequestParameters, eventHandler, eventListener, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,7 +192,27 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback,
|
public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback,
|
||||||
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
|
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
|
||||||
EventListener eventListener) {
|
EventListener eventListener) {
|
||||||
|
this(uuid, mediaDrm, callback, optionalKeyRequestParameters, eventHandler, eventListener,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uuid The UUID of the drm scheme.
|
||||||
|
* @param mediaDrm An underlying {@link ExoMediaDrm} for use by the manager.
|
||||||
|
* @param callback Performs key and provisioning requests.
|
||||||
|
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
|
||||||
|
* to {@link MediaDrm#getKeyRequest(byte[], byte[], String, int, HashMap)}. May be null.
|
||||||
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
|
* null if delivery of events is not required.
|
||||||
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
|
* @param multiSession A boolean that specify whether multiple key session support is enabled.
|
||||||
|
* Default is false.
|
||||||
|
*/
|
||||||
|
public DefaultDrmSessionManager(UUID uuid, ExoMediaDrm<T> mediaDrm, MediaDrmCallback callback,
|
||||||
|
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
|
||||||
|
EventListener eventListener, boolean multiSession) {
|
||||||
Assertions.checkNotNull(uuid);
|
Assertions.checkNotNull(uuid);
|
||||||
|
Assertions.checkNotNull(mediaDrm);
|
||||||
Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead");
|
Assertions.checkArgument(!C.COMMON_PSSH_UUID.equals(uuid), "Use C.CLEARKEY_UUID instead");
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.mediaDrm = mediaDrm;
|
this.mediaDrm = mediaDrm;
|
||||||
@ -187,7 +220,13 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
|
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
|
this.multiSession = multiSession;
|
||||||
mode = MODE_PLAYBACK;
|
mode = MODE_PLAYBACK;
|
||||||
|
sessions = new ArrayList<>();
|
||||||
|
provisioningInProgress = new AtomicBoolean(false);
|
||||||
|
if (multiSession) {
|
||||||
|
mediaDrm.setPropertyString("sessionSharing", "enable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,7 +300,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
* @param offlineLicenseKeySetId The key set id of the license to be used with the given mode.
|
* @param offlineLicenseKeySetId The key set id of the license to be used with the given mode.
|
||||||
*/
|
*/
|
||||||
public void setMode(@Mode int mode, byte[] offlineLicenseKeySetId) {
|
public void setMode(@Mode int mode, byte[] offlineLicenseKeySetId) {
|
||||||
Assertions.checkState(session == null);
|
Assertions.checkState(sessions.isEmpty());
|
||||||
if (mode == MODE_QUERY || mode == MODE_RELEASE) {
|
if (mode == MODE_QUERY || mode == MODE_RELEASE) {
|
||||||
Assertions.checkNotNull(offlineLicenseKeySetId);
|
Assertions.checkNotNull(offlineLicenseKeySetId);
|
||||||
}
|
}
|
||||||
@ -273,7 +312,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canAcquireSession(@NonNull DrmInitData drmInitData) {
|
public boolean canAcquireSession(@NonNull DrmInitData drmInitData) {
|
||||||
SchemeData schemeData = DefaultDrmSession.getSchemeData(drmInitData, uuid);
|
SchemeData schemeData = getSchemeData(drmInitData, uuid);
|
||||||
if (schemeData == null) {
|
if (schemeData == null) {
|
||||||
// No data for this manager's scheme.
|
// No data for this manager's scheme.
|
||||||
return false;
|
return false;
|
||||||
@ -294,22 +333,144 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
@Override
|
@Override
|
||||||
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
|
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
|
||||||
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
|
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
|
||||||
if (session == null) {
|
if (sessions.isEmpty()) {
|
||||||
this.playbackLooper = playbackLooper;
|
this.playbackLooper = playbackLooper;
|
||||||
session = new DefaultDrmSession<T>(uuid, mediaDrm, drmInitData, mode, offlineLicenseKeySetId,
|
mediaDrmHandler = new MediaDrmHandler(playbackLooper);
|
||||||
optionalKeyRequestParameters, callback, playbackLooper, eventHandler, eventListener);
|
mediaDrm.setOnEventListener(new MediaDrmEventListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DefaultDrmSession<T> session = null;
|
||||||
|
byte[] initData = null;
|
||||||
|
String mimeType = null;
|
||||||
|
|
||||||
|
if (offlineLicenseKeySetId == null) {
|
||||||
|
SchemeData data = getSchemeData(drmInitData, uuid);
|
||||||
|
if (data == null) {
|
||||||
|
if (eventHandler != null && eventListener != null) {
|
||||||
|
eventHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
eventListener.onDrmSessionManagerError(new IllegalStateException(
|
||||||
|
"Media does not support uuid: " + uuid));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initData = getSchemeInitData(data, uuid);
|
||||||
|
mimeType = getSchemeMimeType(data, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (DefaultDrmSession<T> s : sessions) {
|
||||||
|
if (!multiSession || s.canReuse(initData)) {
|
||||||
|
session = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session == null) {
|
||||||
|
session = new DefaultDrmSession<T>(uuid, mediaDrm, initData, mimeType, mode,
|
||||||
|
offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper,
|
||||||
|
eventHandler, eventListener, provisioningInProgress, this);
|
||||||
|
sessions.add(session);
|
||||||
|
}
|
||||||
session.acquire();
|
session.acquire();
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSession(DrmSession<T> session) {
|
public void releaseSession(DrmSession<T> session) {
|
||||||
Assertions.checkState(session == this.session);
|
DefaultDrmSession<T> drmSession = (DefaultDrmSession<T>) session;
|
||||||
if (this.session.release()) {
|
if (drmSession.release()) {
|
||||||
this.session = null;
|
sessions.remove(drmSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessions.isEmpty()) {
|
||||||
|
mediaDrm.setOnEventListener(null);
|
||||||
|
mediaDrmHandler.removeCallbacksAndMessages(null);
|
||||||
|
mediaDrmHandler = null;
|
||||||
|
playbackLooper = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProvisionCompleted() {
|
||||||
|
for (DefaultDrmSession<T> session : sessions) {
|
||||||
|
session.onProvisionCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts {@link SchemeData} suitable for the given DRM scheme {@link UUID}.
|
||||||
|
*
|
||||||
|
* @param drmInitData The {@link DrmInitData} from which to extract the {@link SchemeData}.
|
||||||
|
* @param uuid The UUID.
|
||||||
|
* @return The extracted {@link SchemeData}, or null if no suitable data is present.
|
||||||
|
*/
|
||||||
|
private static SchemeData getSchemeData(DrmInitData drmInitData, UUID uuid) {
|
||||||
|
SchemeData schemeData = drmInitData.get(uuid);
|
||||||
|
if (schemeData == null && C.CLEARKEY_UUID.equals(uuid)) {
|
||||||
|
// If present, the Common PSSH box should be used for ClearKey.
|
||||||
|
schemeData = drmInitData.get(C.COMMON_PSSH_UUID);
|
||||||
|
}
|
||||||
|
return schemeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getSchemeInitData(SchemeData data, UUID uuid) {
|
||||||
|
byte[] schemeInitData = data.data;
|
||||||
|
if (Util.SDK_INT < 21) {
|
||||||
|
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
|
||||||
|
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(schemeInitData, uuid);
|
||||||
|
if (psshData == null) {
|
||||||
|
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
|
||||||
|
} else {
|
||||||
|
schemeInitData = psshData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return schemeInitData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getSchemeMimeType(SchemeData data, UUID uuid) {
|
||||||
|
String schemeMimeType = data.mimeType;
|
||||||
|
if (Util.SDK_INT < 26 && C.CLEARKEY_UUID.equals(uuid)
|
||||||
|
&& (MimeTypes.VIDEO_MP4.equals(schemeMimeType)
|
||||||
|
|| MimeTypes.AUDIO_MP4.equals(schemeMimeType))) {
|
||||||
|
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
|
||||||
|
schemeMimeType = CENC_SCHEME_MIME_TYPE;
|
||||||
|
}
|
||||||
|
return schemeMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("HandlerLeak")
|
||||||
|
private class MediaDrmHandler extends Handler {
|
||||||
|
|
||||||
|
public MediaDrmHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
byte[] sessionId = (byte[]) msg.obj;
|
||||||
|
for (DefaultDrmSession<T> session : sessions) {
|
||||||
|
if (session.hasSessionId(sessionId)) {
|
||||||
|
session.onMediaDrmEvent(msg.what);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MediaDrmEventListener implements OnEventListener<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(ExoMediaDrm<? extends T> md, byte[] sessionId, int event, int extra,
|
||||||
|
byte[] data) {
|
||||||
|
if (mode == DefaultDrmSessionManager.MODE_PLAYBACK) {
|
||||||
|
mediaDrmHandler.obtainMessage(event, sessionId).sendToTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user