mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Provisioning: Fix some (admittedly quite theoretical) issues:
1. Only tell sessions that want provisioning when provisioning occurs. 2. Also propagate failure to provision to these sessions. 3. If a session responsible for provisioning is released, start provisioning using another session instead. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=168713918
This commit is contained in:
parent
a479afff5f
commit
a3a2fb506c
@ -32,7 +32,6 @@ import java.util.Arrays;
|
|||||||
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 ExoMediaDrm}.
|
* A {@link DrmSession} that supports playbacks using {@link ExoMediaDrm}.
|
||||||
@ -41,12 +40,29 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
/* package */ class DefaultDrmSession<T extends ExoMediaCrypto> implements DrmSession<T> {
|
/* package */ class DefaultDrmSession<T extends ExoMediaCrypto> implements DrmSession<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener of {@link DefaultDrmSession} events.
|
* Manages provisioning requests.
|
||||||
*/
|
*/
|
||||||
public interface EventListener {
|
public interface ProvisioningManager<T extends ExoMediaCrypto> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called each time provision is completed.
|
* Called when a session requires provisioning. The manager <em>may</em> call
|
||||||
|
* {@link #provision()} to have this session perform the provisioning operation. The manager
|
||||||
|
* <em>will</em> call {@link DefaultDrmSession#onProvisionCompleted()} when provisioning has
|
||||||
|
* completed, or {@link DefaultDrmSession#onProvisionError} if provisioning fails.
|
||||||
|
*
|
||||||
|
* @param session The session.
|
||||||
|
*/
|
||||||
|
void provisionRequired(DefaultDrmSession<T> session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a session when it fails to perform a provisioning operation.
|
||||||
|
*
|
||||||
|
* @param error The error that occurred.
|
||||||
|
*/
|
||||||
|
void onProvisionError(Exception error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by a session when it successfully completes a provisioning operation.
|
||||||
*/
|
*/
|
||||||
void onProvisionCompleted();
|
void onProvisionCompleted();
|
||||||
|
|
||||||
@ -59,14 +75,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
private static final int MAX_LICENSE_DURATION_TO_RENEW = 60;
|
private static final int MAX_LICENSE_DURATION_TO_RENEW = 60;
|
||||||
|
|
||||||
private final ExoMediaDrm<T> mediaDrm;
|
private final ExoMediaDrm<T> mediaDrm;
|
||||||
|
private final ProvisioningManager<T> provisioningManager;
|
||||||
private final byte[] initData;
|
private final byte[] initData;
|
||||||
private final String mimeType;
|
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 Handler eventHandler;
|
private final Handler eventHandler;
|
||||||
private final DefaultDrmSessionManager.EventListener eventListener;
|
private final DefaultDrmSessionManager.EventListener eventListener;
|
||||||
private final AtomicBoolean provisioningInProgress;
|
|
||||||
private final EventListener sessionEventListener;
|
|
||||||
|
|
||||||
/* package */ final MediaDrmCallback callback;
|
/* package */ final MediaDrmCallback callback;
|
||||||
/* package */ final UUID uuid;
|
/* package */ final UUID uuid;
|
||||||
@ -87,6 +102,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
*
|
*
|
||||||
* @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 initData The DRM init data.
|
* @param initData The DRM init data.
|
||||||
* @param mode The DRM mode.
|
* @param mode The DRM mode.
|
||||||
* @param offlineLicenseKeySetId The offlineLicense KeySetId.
|
* @param offlineLicenseKeySetId The offlineLicense KeySetId.
|
||||||
@ -96,13 +112,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
* @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, byte[] initData, String mimeType,
|
public DefaultDrmSession(UUID uuid, ExoMediaDrm<T> mediaDrm,
|
||||||
|
ProvisioningManager<T> provisioningManager, 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, AtomicBoolean provisioningInProgress,
|
DefaultDrmSessionManager.EventListener eventListener) {
|
||||||
EventListener sessionEventListener) {
|
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
this.provisioningManager = provisioningManager;
|
||||||
this.mediaDrm = mediaDrm;
|
this.mediaDrm = mediaDrm;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
this.offlineLicenseKeySetId = offlineLicenseKeySetId;
|
||||||
@ -111,8 +128,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
this.eventHandler = eventHandler;
|
this.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
this.provisioningInProgress = provisioningInProgress;
|
|
||||||
this.sessionEventListener = sessionEventListener;
|
|
||||||
state = STATE_OPENING;
|
state = STATE_OPENING;
|
||||||
|
|
||||||
postResponseHandler = new PostResponseHandler(playbackLooper);
|
postResponseHandler = new PostResponseHandler(playbackLooper);
|
||||||
@ -164,7 +179,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canReuse(byte[] initData) {
|
public boolean hasInitData(byte[] initData) {
|
||||||
return Arrays.equals(this.initData, initData);
|
return Arrays.equals(this.initData, initData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +187,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return Arrays.equals(this.sessionId, sessionId);
|
return Arrays.equals(this.sessionId, sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DrmSession Implementation.
|
// Provisioning implementation.
|
||||||
|
|
||||||
|
public void provision() {
|
||||||
|
ProvisionRequest request = mediaDrm.getProvisionRequest();
|
||||||
|
postRequestHandler.obtainMessage(MSG_PROVISION, request).sendToTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProvisionCompleted() {
|
||||||
|
if (openInternal(false)) {
|
||||||
|
doLicense();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProvisionError(Exception error) {
|
||||||
|
onError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrmSession implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@DrmSession.State
|
@DrmSession.State
|
||||||
@ -221,7 +253,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return true;
|
return true;
|
||||||
} catch (NotProvisionedException e) {
|
} catch (NotProvisionedException e) {
|
||||||
if (allowProvisioning) {
|
if (allowProvisioning) {
|
||||||
postProvisionRequest();
|
provisioningManager.provisionRequired(this);
|
||||||
} else {
|
} else {
|
||||||
onError(e);
|
onError(e);
|
||||||
}
|
}
|
||||||
@ -232,42 +264,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postProvisionRequest() {
|
private void onProvisionResponse(Object response) {
|
||||||
if (provisioningInProgress.getAndSet(true)) {
|
if (state != STATE_OPENING && !isOpen()) {
|
||||||
|
// This event is stale.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProvisionRequest request = mediaDrm.getProvisionRequest();
|
|
||||||
postRequestHandler.obtainMessage(MSG_PROVISION, request).sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onProvisionResponse(Object response) {
|
|
||||||
provisioningInProgress.set(false);
|
|
||||||
if (response instanceof Exception) {
|
if (response instanceof Exception) {
|
||||||
onError((Exception) response);
|
provisioningManager.onProvisionError((Exception) response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mediaDrm.provideProvisionResponse((byte[]) response);
|
mediaDrm.provideProvisionResponse((byte[]) response);
|
||||||
} catch (DeniedByServerException e) {
|
} catch (DeniedByServerException e) {
|
||||||
onError(e);
|
provisioningManager.onProvisionError(e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionEventListener != null) {
|
provisioningManager.onProvisionCompleted();
|
||||||
sessionEventListener.onProvisionCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onProvisionCompleted() {
|
|
||||||
if (state != STATE_OPENING && !isOpen()) {
|
|
||||||
// This event is stale.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (openInternal(false)) {
|
|
||||||
doLicense();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doLicense() {
|
private void doLicense() {
|
||||||
@ -405,7 +420,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
|
|
||||||
private void onKeysError(Exception e) {
|
private void onKeysError(Exception e) {
|
||||||
if (e instanceof NotProvisionedException) {
|
if (e instanceof NotProvisionedException) {
|
||||||
postProvisionRequest();
|
provisioningManager.provisionRequired(this);
|
||||||
} else {
|
} else {
|
||||||
onError(e);
|
onError(e);
|
||||||
}
|
}
|
||||||
@ -447,7 +462,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
break;
|
break;
|
||||||
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
|
case ExoMediaDrm.EVENT_PROVISION_REQUIRED:
|
||||||
state = STATE_OPENED;
|
state = STATE_OPENED;
|
||||||
postProvisionRequest();
|
provisioningManager.provisionRequired(this);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -24,6 +24,7 @@ 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.DefaultDrmSession.ProvisioningManager;
|
||||||
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.drm.ExoMediaDrm.OnEventListener;
|
||||||
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
|
import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
|
||||||
@ -36,14 +37,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
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 ExoMediaDrm}.
|
* A {@link DrmSessionManager} that supports playbacks using {@link ExoMediaDrm}.
|
||||||
*/
|
*/
|
||||||
@TargetApi(18)
|
@TargetApi(18)
|
||||||
public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSessionManager<T>,
|
public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSessionManager<T>,
|
||||||
DefaultDrmSession.EventListener {
|
ProvisioningManager<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener of {@link DefaultDrmSessionManager} events.
|
* Listener of {@link DefaultDrmSessionManager} events.
|
||||||
@ -107,7 +107,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
private final boolean multiSession;
|
private final boolean multiSession;
|
||||||
|
|
||||||
private final List<DefaultDrmSession<T>> sessions;
|
private final List<DefaultDrmSession<T>> sessions;
|
||||||
private final AtomicBoolean provisioningInProgress;
|
private final List<DefaultDrmSession<T>> provisioningSessions;
|
||||||
|
|
||||||
private Looper playbackLooper;
|
private Looper playbackLooper;
|
||||||
private int mode;
|
private int mode;
|
||||||
@ -223,7 +223,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
this.multiSession = multiSession;
|
this.multiSession = multiSession;
|
||||||
mode = MODE_PLAYBACK;
|
mode = MODE_PLAYBACK;
|
||||||
sessions = new ArrayList<>();
|
sessions = new ArrayList<>();
|
||||||
provisioningInProgress = new AtomicBoolean(false);
|
provisioningSessions = new ArrayList<>();
|
||||||
if (multiSession) {
|
if (multiSession) {
|
||||||
mediaDrm.setPropertyString("sessionSharing", "enable");
|
mediaDrm.setPropertyString("sessionSharing", "enable");
|
||||||
}
|
}
|
||||||
@ -363,17 +363,21 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (DefaultDrmSession<T> s : sessions) {
|
if (!multiSession) {
|
||||||
if (!multiSession || s.canReuse(initData)) {
|
// Look for an existing session to use.
|
||||||
session = s;
|
for (DefaultDrmSession<T> existingSession : sessions) {
|
||||||
|
if (existingSession.hasInitData(initData)) {
|
||||||
|
session = existingSession;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
session = new DefaultDrmSession<T>(uuid, mediaDrm, initData, mimeType, mode,
|
// Create a new session.
|
||||||
|
session = new DefaultDrmSession<>(uuid, mediaDrm, this, initData, mimeType, mode,
|
||||||
offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper,
|
offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper,
|
||||||
eventHandler, eventListener, provisioningInProgress, this);
|
eventHandler, eventListener);
|
||||||
sessions.add(session);
|
sessions.add(session);
|
||||||
}
|
}
|
||||||
session.acquire();
|
session.acquire();
|
||||||
@ -385,16 +389,44 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
|
|||||||
DefaultDrmSession<T> drmSession = (DefaultDrmSession<T>) session;
|
DefaultDrmSession<T> drmSession = (DefaultDrmSession<T>) session;
|
||||||
if (drmSession.release()) {
|
if (drmSession.release()) {
|
||||||
sessions.remove(drmSession);
|
sessions.remove(drmSession);
|
||||||
|
if (provisioningSessions.size() > 1 && provisioningSessions.get(0) == drmSession) {
|
||||||
|
// Other sessions were waiting for the released session to complete a provision operation.
|
||||||
|
// We need to have one of those sessions perform the provision operation instead.
|
||||||
|
provisioningSessions.get(1).provision();
|
||||||
|
}
|
||||||
|
provisioningSessions.remove(drmSession);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvisioningManager implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void provisionRequired(DefaultDrmSession<T> session) {
|
||||||
|
provisioningSessions.add(session);
|
||||||
|
if (provisioningSessions.size() == 1) {
|
||||||
|
// This is the first session requesting provisioning, so have it perform the operation.
|
||||||
|
session.provision();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProvisionCompleted() {
|
public void onProvisionCompleted() {
|
||||||
for (DefaultDrmSession<T> session : sessions) {
|
for (DefaultDrmSession<T> session : provisioningSessions) {
|
||||||
session.onProvisionCompleted();
|
session.onProvisionCompleted();
|
||||||
}
|
}
|
||||||
|
provisioningSessions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProvisionError(Exception error) {
|
||||||
|
for (DefaultDrmSession<T> session : provisioningSessions) {
|
||||||
|
session.onProvisionError(error);
|
||||||
|
}
|
||||||
|
provisioningSessions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal methods.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts {@link SchemeData} suitable for the given DRM scheme {@link UUID}.
|
* Extracts {@link SchemeData} suitable for the given DRM scheme {@link UUID}.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user