Add license server URL to SchemeData
Allows DrmInitData to carry a license server URL when the media declares one. Issue:#3393 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=199643743
This commit is contained in:
parent
f7ed789fc3
commit
5f79aa253b
@ -1,5 +1,11 @@
|
|||||||
# Release notes #
|
# Release notes #
|
||||||
|
|
||||||
|
### 2.8.3 ###
|
||||||
|
|
||||||
|
* DRM:
|
||||||
|
* Allow DrmInitData to carry a license server URL
|
||||||
|
([#3393](https://github.com/google/ExoPlayer/issues/3393)).
|
||||||
|
|
||||||
### 2.8.2 ###
|
### 2.8.2 ###
|
||||||
|
|
||||||
* IMA: Don't advertise support for video/mpeg ad media, as we don't have an
|
* IMA: Don't advertise support for video/mpeg ad media, as we don't have an
|
||||||
|
@ -22,11 +22,12 @@ import android.os.Handler;
|
|||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
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.DefaultDrmSessionEventListener.EventDispatcher;
|
import com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.DefaultKeyRequest;
|
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.ProvisionRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -77,8 +78,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
private final ExoMediaDrm<T> mediaDrm;
|
private final ExoMediaDrm<T> mediaDrm;
|
||||||
private final ProvisioningManager<T> provisioningManager;
|
private final ProvisioningManager<T> provisioningManager;
|
||||||
private final byte[] initData;
|
private final SchemeData schemeData;
|
||||||
private final String mimeType;
|
|
||||||
private final @DefaultDrmSessionManager.Mode int mode;
|
private final @DefaultDrmSessionManager.Mode int mode;
|
||||||
private final HashMap<String, String> optionalKeyRequestParameters;
|
private final HashMap<String, String> optionalKeyRequestParameters;
|
||||||
private final EventDispatcher eventDispatcher;
|
private final EventDispatcher eventDispatcher;
|
||||||
@ -103,9 +103,11 @@ import java.util.UUID;
|
|||||||
* @param uuid The UUID of the drm scheme.
|
* @param uuid The UUID of the drm scheme.
|
||||||
* @param mediaDrm The media DRM.
|
* @param mediaDrm The media DRM.
|
||||||
* @param provisioningManager The manager for provisioning.
|
* @param provisioningManager The manager for provisioning.
|
||||||
* @param initData The DRM init data.
|
* @param schemeData The DRM data for this session, or null if a {@code offlineLicenseKeySetId} is
|
||||||
|
* provided.
|
||||||
* @param mode The DRM mode.
|
* @param mode The DRM mode.
|
||||||
* @param offlineLicenseKeySetId The offlineLicense KeySetId.
|
* @param offlineLicenseKeySetId The offline license key set identifier, or null when not using
|
||||||
|
* offline keys.
|
||||||
* @param optionalKeyRequestParameters The optional key request parameters.
|
* @param optionalKeyRequestParameters The optional key request parameters.
|
||||||
* @param callback The media DRM callback.
|
* @param callback The media DRM callback.
|
||||||
* @param playbackLooper The playback looper.
|
* @param playbackLooper The playback looper.
|
||||||
@ -117,10 +119,9 @@ import java.util.UUID;
|
|||||||
UUID uuid,
|
UUID uuid,
|
||||||
ExoMediaDrm<T> mediaDrm,
|
ExoMediaDrm<T> mediaDrm,
|
||||||
ProvisioningManager<T> provisioningManager,
|
ProvisioningManager<T> provisioningManager,
|
||||||
byte[] initData,
|
@Nullable SchemeData schemeData,
|
||||||
String mimeType,
|
|
||||||
@DefaultDrmSessionManager.Mode int mode,
|
@DefaultDrmSessionManager.Mode int mode,
|
||||||
byte[] offlineLicenseKeySetId,
|
@Nullable byte[] offlineLicenseKeySetId,
|
||||||
HashMap<String, String> optionalKeyRequestParameters,
|
HashMap<String, String> optionalKeyRequestParameters,
|
||||||
MediaDrmCallback callback,
|
MediaDrmCallback callback,
|
||||||
Looper playbackLooper,
|
Looper playbackLooper,
|
||||||
@ -131,6 +132,7 @@ import java.util.UUID;
|
|||||||
this.mediaDrm = mediaDrm;
|
this.mediaDrm = mediaDrm;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
||||||
|
this.schemeData = offlineLicenseKeySetId == null ? schemeData : null;
|
||||||
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
|
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
|
this.initialDrmRequestRetryCount = initialDrmRequestRetryCount;
|
||||||
@ -141,14 +143,6 @@ import java.util.UUID;
|
|||||||
requestHandlerThread = new HandlerThread("DrmRequestHandler");
|
requestHandlerThread = new HandlerThread("DrmRequestHandler");
|
||||||
requestHandlerThread.start();
|
requestHandlerThread.start();
|
||||||
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
|
postRequestHandler = new PostRequestHandler(requestHandlerThread.getLooper());
|
||||||
|
|
||||||
if (offlineLicenseKeySetId == null) {
|
|
||||||
this.initData = initData;
|
|
||||||
this.mimeType = mimeType;
|
|
||||||
} else {
|
|
||||||
this.initData = null;
|
|
||||||
this.mimeType = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Life cycle.
|
// Life cycle.
|
||||||
@ -187,13 +181,37 @@ import java.util.UUID;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasInitData(byte[] initData) {
|
public boolean hasInitData(byte[] initData) {
|
||||||
return Arrays.equals(this.initData, initData);
|
return Arrays.equals(schemeData != null ? schemeData.data : null, initData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSessionId(byte[] sessionId) {
|
public boolean hasSessionId(byte[] sessionId) {
|
||||||
return Arrays.equals(this.sessionId, sessionId);
|
return Arrays.equals(this.sessionId, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public void onMediaDrmEvent(int what) {
|
||||||
|
if (!isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (what) {
|
||||||
|
case ExoMediaDrm.EVENT_KEY_REQUIRED:
|
||||||
|
doLicense(false);
|
||||||
|
break;
|
||||||
|
case ExoMediaDrm.EVENT_KEY_EXPIRED:
|
||||||
|
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
|
||||||
|
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
|
||||||
|
// waiting for key response.
|
||||||
|
onKeysExpired();
|
||||||
|
break;
|
||||||
|
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
|
||||||
|
state = STATE_OPENED;
|
||||||
|
provisioningManager.provisionRequired(this);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Provisioning implementation.
|
// Provisioning implementation.
|
||||||
|
|
||||||
public void provision() {
|
public void provision() {
|
||||||
@ -356,14 +374,19 @@ import java.util.UUID;
|
|||||||
|
|
||||||
private void postKeyRequest(int type, boolean allowRetry) {
|
private void postKeyRequest(int type, boolean allowRetry) {
|
||||||
byte[] scope = type == ExoMediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
|
byte[] scope = type == ExoMediaDrm.KEY_TYPE_RELEASE ? offlineLicenseKeySetId : sessionId;
|
||||||
try {
|
byte[] initData = null;
|
||||||
KeyRequest request = mediaDrm.getKeyRequest(scope, initData, mimeType, type,
|
String mimeType = null;
|
||||||
optionalKeyRequestParameters);
|
String licenseServerUrl = null;
|
||||||
if (C.CLEARKEY_UUID.equals(uuid)) {
|
if (schemeData != null) {
|
||||||
request = new DefaultKeyRequest(ClearKeyUtil.adjustRequestData(request.getData()),
|
initData = schemeData.data;
|
||||||
request.getDefaultUrl());
|
mimeType = schemeData.mimeType;
|
||||||
|
licenseServerUrl = schemeData.licenseServerUrl;
|
||||||
}
|
}
|
||||||
postRequestHandler.obtainMessage(MSG_KEYS, request, allowRetry).sendToTarget();
|
try {
|
||||||
|
KeyRequest request =
|
||||||
|
mediaDrm.getKeyRequest(scope, initData, mimeType, type, optionalKeyRequestParameters);
|
||||||
|
Pair<KeyRequest, String> arguments = Pair.create(request, licenseServerUrl);
|
||||||
|
postRequestHandler.obtainMessage(MSG_KEYS, arguments, allowRetry).sendToTarget();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
onKeysError(e);
|
onKeysError(e);
|
||||||
}
|
}
|
||||||
@ -382,9 +405,6 @@ import java.util.UUID;
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] responseData = (byte[]) response;
|
byte[] responseData = (byte[]) response;
|
||||||
if (C.CLEARKEY_UUID.equals(uuid)) {
|
|
||||||
responseData = ClearKeyUtil.adjustResponseData(responseData);
|
|
||||||
}
|
|
||||||
if (mode == DefaultDrmSessionManager.MODE_RELEASE) {
|
if (mode == DefaultDrmSessionManager.MODE_RELEASE) {
|
||||||
mediaDrm.provideKeyResponse(offlineLicenseKeySetId, responseData);
|
mediaDrm.provideKeyResponse(offlineLicenseKeySetId, responseData);
|
||||||
eventDispatcher.drmKeysRemoved();
|
eventDispatcher.drmKeysRemoved();
|
||||||
@ -430,30 +450,7 @@ import java.util.UUID;
|
|||||||
return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS;
|
return state == STATE_OPENED || state == STATE_OPENED_WITH_KEYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
// Internal classes.
|
||||||
public void onMediaDrmEvent(int what) {
|
|
||||||
if (!isOpen()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (what) {
|
|
||||||
case ExoMediaDrm.EVENT_KEY_REQUIRED:
|
|
||||||
doLicense(false);
|
|
||||||
break;
|
|
||||||
case ExoMediaDrm.EVENT_KEY_EXPIRED:
|
|
||||||
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
|
|
||||||
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
|
|
||||||
// waiting for key response.
|
|
||||||
onKeysExpired();
|
|
||||||
break;
|
|
||||||
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
|
|
||||||
state = STATE_OPENED;
|
|
||||||
provisioningManager.provisionRequired(this);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("HandlerLeak")
|
@SuppressLint("HandlerLeak")
|
||||||
private class PostResponseHandler extends Handler {
|
private class PostResponseHandler extends Handler {
|
||||||
@ -492,6 +489,7 @@ import java.util.UUID;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
Object response;
|
Object response;
|
||||||
try {
|
try {
|
||||||
@ -500,7 +498,8 @@ import java.util.UUID;
|
|||||||
response = callback.executeProvisionRequest(uuid, (ProvisionRequest) msg.obj);
|
response = callback.executeProvisionRequest(uuid, (ProvisionRequest) msg.obj);
|
||||||
break;
|
break;
|
||||||
case MSG_KEYS:
|
case MSG_KEYS:
|
||||||
response = callback.executeKeyRequest(uuid, (KeyRequest) msg.obj);
|
Pair<KeyRequest, String> arguments = (Pair<KeyRequest, String>) msg.obj;
|
||||||
|
response = callback.executeKeyRequest(uuid, arguments.first, arguments.second);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
@ -534,5 +533,4 @@ import java.util.UUID;
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
|
|||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener;
|
||||||
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
|
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;
|
||||||
@ -89,7 +88,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
public static final int INITIAL_DRM_REQUEST_RETRY_COUNT = 3;
|
public static final int INITIAL_DRM_REQUEST_RETRY_COUNT = 3;
|
||||||
|
|
||||||
private static final String TAG = "DefaultDrmSessionMgr";
|
private static final String TAG = "DefaultDrmSessionMgr";
|
||||||
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
|
|
||||||
|
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final ExoMediaDrm<T> mediaDrm;
|
private final ExoMediaDrm<T> mediaDrm;
|
||||||
@ -509,17 +507,14 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] initData = null;
|
SchemeData schemeData = null;
|
||||||
String mimeType = null;
|
|
||||||
if (offlineLicenseKeySetId == null) {
|
if (offlineLicenseKeySetId == null) {
|
||||||
SchemeData data = getSchemeData(drmInitData, uuid, false);
|
schemeData = getSchemeData(drmInitData, uuid, false);
|
||||||
if (data == null) {
|
if (schemeData == null) {
|
||||||
final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
|
final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
|
||||||
eventDispatcher.drmSessionManagerError(error);
|
eventDispatcher.drmSessionManagerError(error);
|
||||||
return new ErrorStateDrmSession<>(new DrmSessionException(error));
|
return new ErrorStateDrmSession<>(new DrmSessionException(error));
|
||||||
}
|
}
|
||||||
initData = getSchemeInitData(data, uuid);
|
|
||||||
mimeType = getSchemeMimeType(data, uuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultDrmSession<T> session;
|
DefaultDrmSession<T> session;
|
||||||
@ -528,6 +523,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
} else {
|
} else {
|
||||||
// Only use an existing session if it has matching init data.
|
// Only use an existing session if it has matching init data.
|
||||||
session = null;
|
session = null;
|
||||||
|
byte[] initData = schemeData != null ? schemeData.data : null;
|
||||||
for (DefaultDrmSession<T> existingSession : sessions) {
|
for (DefaultDrmSession<T> existingSession : sessions) {
|
||||||
if (existingSession.hasInitData(initData)) {
|
if (existingSession.hasInitData(initData)) {
|
||||||
session = existingSession;
|
session = existingSession;
|
||||||
@ -543,8 +539,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
uuid,
|
uuid,
|
||||||
mediaDrm,
|
mediaDrm,
|
||||||
this,
|
this,
|
||||||
initData,
|
schemeData,
|
||||||
mimeType,
|
|
||||||
mode,
|
mode,
|
||||||
offlineLicenseKeySetId,
|
offlineLicenseKeySetId,
|
||||||
optionalKeyRequestParameters,
|
optionalKeyRequestParameters,
|
||||||
@ -650,31 +645,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
return matchingSchemeDatas.get(0);
|
return matchingSchemeDatas.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
@SuppressLint("HandlerLeak")
|
||||||
private class MediaDrmHandler extends Handler {
|
private class MediaDrmHandler extends Handler {
|
||||||
|
|
||||||
|
@ -266,9 +266,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
* applies to all schemes).
|
* applies to all schemes).
|
||||||
*/
|
*/
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
/**
|
/** The URL of the server to which license requests should be made. May be null if unknown. */
|
||||||
* The mimeType of {@link #data}.
|
public final @Nullable String licenseServerUrl;
|
||||||
*/
|
/** The mimeType of {@link #data}. */
|
||||||
public final String mimeType;
|
public final String mimeType;
|
||||||
/**
|
/**
|
||||||
* The initialization data. May be null for scheme support checks only.
|
* The initialization data. May be null for scheme support checks only.
|
||||||
@ -297,7 +297,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
|
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
|
||||||
*/
|
*/
|
||||||
public SchemeData(UUID uuid, String mimeType, byte[] data, boolean requiresSecureDecryption) {
|
public SchemeData(UUID uuid, String mimeType, byte[] data, boolean requiresSecureDecryption) {
|
||||||
|
this(uuid, /* licenseServerUrl= */ null, mimeType, data, requiresSecureDecryption);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
|
||||||
|
* universal (i.e. applies to all schemes).
|
||||||
|
* @param licenseServerUrl See {@link #licenseServerUrl}.
|
||||||
|
* @param mimeType See {@link #mimeType}.
|
||||||
|
* @param data See {@link #data}.
|
||||||
|
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
|
||||||
|
*/
|
||||||
|
public SchemeData(
|
||||||
|
UUID uuid,
|
||||||
|
@Nullable String licenseServerUrl,
|
||||||
|
String mimeType,
|
||||||
|
byte[] data,
|
||||||
|
boolean requiresSecureDecryption) {
|
||||||
this.uuid = Assertions.checkNotNull(uuid);
|
this.uuid = Assertions.checkNotNull(uuid);
|
||||||
|
this.licenseServerUrl = licenseServerUrl;
|
||||||
this.mimeType = Assertions.checkNotNull(mimeType);
|
this.mimeType = Assertions.checkNotNull(mimeType);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.requiresSecureDecryption = requiresSecureDecryption;
|
this.requiresSecureDecryption = requiresSecureDecryption;
|
||||||
@ -305,6 +323,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
|
|
||||||
/* package */ SchemeData(Parcel in) {
|
/* package */ SchemeData(Parcel in) {
|
||||||
uuid = new UUID(in.readLong(), in.readLong());
|
uuid = new UUID(in.readLong(), in.readLong());
|
||||||
|
licenseServerUrl = in.readString();
|
||||||
mimeType = in.readString();
|
mimeType = in.readString();
|
||||||
data = in.createByteArray();
|
data = in.createByteArray();
|
||||||
requiresSecureDecryption = in.readByte() != 0;
|
requiresSecureDecryption = in.readByte() != 0;
|
||||||
@ -346,7 +365,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
SchemeData other = (SchemeData) obj;
|
SchemeData other = (SchemeData) obj;
|
||||||
return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid)
|
return Util.areEqual(licenseServerUrl, other.licenseServerUrl)
|
||||||
|
&& Util.areEqual(mimeType, other.mimeType)
|
||||||
|
&& Util.areEqual(uuid, other.uuid)
|
||||||
&& Arrays.equals(data, other.data);
|
&& Arrays.equals(data, other.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,6 +375,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
if (hashCode == 0) {
|
if (hashCode == 0) {
|
||||||
int result = uuid.hashCode();
|
int result = uuid.hashCode();
|
||||||
|
result = 31 * result + (licenseServerUrl == null ? 0 : licenseServerUrl.hashCode());
|
||||||
result = 31 * result + mimeType.hashCode();
|
result = 31 * result + mimeType.hashCode();
|
||||||
result = 31 * result + Arrays.hashCode(data);
|
result = 31 * result + Arrays.hashCode(data);
|
||||||
hashCode = result;
|
hashCode = result;
|
||||||
@ -372,6 +394,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||||||
public void writeToParcel(Parcel dest, int flags) {
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
dest.writeLong(uuid.getMostSignificantBits());
|
dest.writeLong(uuid.getMostSignificantBits());
|
||||||
dest.writeLong(uuid.getLeastSignificantBits());
|
dest.writeLong(uuid.getLeastSignificantBits());
|
||||||
|
dest.writeString(licenseServerUrl);
|
||||||
dest.writeString(mimeType);
|
dest.writeString(mimeType);
|
||||||
dest.writeByteArray(data);
|
dest.writeByteArray(data);
|
||||||
dest.writeByte((byte) (requiresSecureDecryption ? 1 : 0));
|
dest.writeByte((byte) (requiresSecureDecryption ? 1 : 0));
|
||||||
|
@ -26,7 +26,9 @@ import android.media.UnsupportedSchemeException;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
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.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -40,6 +42,8 @@ import java.util.UUID;
|
|||||||
@TargetApi(23)
|
@TargetApi(23)
|
||||||
public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto> {
|
public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto> {
|
||||||
|
|
||||||
|
private static final String CENC_SCHEME_MIME_TYPE = "cenc";
|
||||||
|
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final MediaDrm mediaDrm;
|
private final MediaDrm mediaDrm;
|
||||||
|
|
||||||
@ -116,14 +120,43 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
|
|||||||
@Override
|
@Override
|
||||||
public KeyRequest getKeyRequest(byte[] scope, byte[] init, String mimeType, int keyType,
|
public KeyRequest getKeyRequest(byte[] scope, byte[] init, String mimeType, int keyType,
|
||||||
HashMap<String, String> optionalParameters) throws NotProvisionedException {
|
HashMap<String, String> optionalParameters) throws NotProvisionedException {
|
||||||
|
|
||||||
|
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
|
||||||
|
if (Util.SDK_INT < 21 && C.WIDEVINE_UUID.equals(uuid)) {
|
||||||
|
byte[] psshData = PsshAtomUtil.parseSchemeSpecificData(init, uuid);
|
||||||
|
if (psshData == null) {
|
||||||
|
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
|
||||||
|
} else {
|
||||||
|
init = psshData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
|
||||||
|
if (Util.SDK_INT < 26
|
||||||
|
&& C.CLEARKEY_UUID.equals(uuid)
|
||||||
|
&& (MimeTypes.VIDEO_MP4.equals(mimeType) || MimeTypes.AUDIO_MP4.equals(mimeType))) {
|
||||||
|
mimeType = CENC_SCHEME_MIME_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
final MediaDrm.KeyRequest request = mediaDrm.getKeyRequest(scope, init, mimeType, keyType,
|
final MediaDrm.KeyRequest request = mediaDrm.getKeyRequest(scope, init, mimeType, keyType,
|
||||||
optionalParameters);
|
optionalParameters);
|
||||||
return new DefaultKeyRequest(request.getData(), request.getDefaultUrl());
|
|
||||||
|
byte[] requestData = request.getData();
|
||||||
|
if (C.CLEARKEY_UUID.equals(uuid)) {
|
||||||
|
requestData = ClearKeyUtil.adjustRequestData(requestData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultKeyRequest(requestData, request.getDefaultUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] provideKeyResponse(byte[] scope, byte[] response)
|
public byte[] provideKeyResponse(byte[] scope, byte[] response)
|
||||||
throws NotProvisionedException, DeniedByServerException {
|
throws NotProvisionedException, DeniedByServerException {
|
||||||
|
|
||||||
|
if (C.CLEARKEY_UUID.equals(uuid)) {
|
||||||
|
response = ClearKeyUtil.adjustResponseData(response);
|
||||||
|
}
|
||||||
|
|
||||||
return mediaDrm.provideKeyResponse(scope, response);
|
return mediaDrm.provideKeyResponse(scope, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
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.ExoMediaDrm.KeyRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
||||||
@ -114,8 +115,13 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception {
|
public byte[] executeKeyRequest(
|
||||||
|
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
|
||||||
|
throws Exception {
|
||||||
String url = request.getDefaultUrl();
|
String url = request.getDefaultUrl();
|
||||||
|
if (TextUtils.isEmpty(url)) {
|
||||||
|
url = mediaProvidedLicenseServerUrl;
|
||||||
|
}
|
||||||
if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {
|
if (forceDefaultLicenseUrl || TextUtils.isEmpty(url)) {
|
||||||
url = defaultLicenseUrl;
|
url = defaultLicenseUrl;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.drm;
|
package com.google.android.exoplayer2.drm;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
@ -44,7 +45,9 @@ public final class LocalMediaDrmCallback implements MediaDrmCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception {
|
public byte[] executeKeyRequest(
|
||||||
|
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
|
||||||
|
throws Exception {
|
||||||
return keyResponse;
|
return keyResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.drm;
|
package com.google.android.exoplayer2.drm;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest;
|
||||||
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -38,10 +39,13 @@ public interface MediaDrmCallback {
|
|||||||
* Executes a key request.
|
* Executes a key request.
|
||||||
*
|
*
|
||||||
* @param uuid The UUID of the content protection scheme.
|
* @param uuid The UUID of the content protection scheme.
|
||||||
* @param request The request.
|
* @param request The request generated by the content decryption module.
|
||||||
|
* @param mediaProvidedLicenseServerUrl A license server URL provided by the media, or null if the
|
||||||
|
* media does not include any license server URL.
|
||||||
* @return The response data.
|
* @return The response data.
|
||||||
* @throws Exception If an error occurred executing the request.
|
* @throws Exception If an error occurred executing the request.
|
||||||
*/
|
*/
|
||||||
byte[] executeKeyRequest(UUID uuid, KeyRequest request) throws Exception;
|
byte[] executeKeyRequest(
|
||||||
|
UUID uuid, KeyRequest request, @Nullable String mediaProvidedLicenseServerUrl)
|
||||||
|
throws Exception;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user