Commit 7d229003 by olly Committed by Oliver Woodman

Add flexibility to DrmSessionManager.

DrmSessionManager is now an actual manager. For each session
request it may return a shared session (as things work now,
and as is suitable for Widevine VOD) or a separate instance
(e.g. for PlayReady where audio and video are protected with
different keys).
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=123203664
parent da2cfd60
......@@ -16,6 +16,7 @@
package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.drm.DrmSession;
import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.NalUnitUtil;
......@@ -155,6 +156,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private Format format;
private MediaCodec codec;
private DrmSession drmSession;
private boolean codecIsAdaptive;
private boolean codecNeedsDiscardToSpsWorkaround;
private boolean codecNeedsFlushWorkaround;
......@@ -167,7 +169,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private int inputIndex;
private int outputIndex;
private boolean shouldSkipOutputBuffer;
private boolean openedDrmSession;
private boolean codecReconfigured;
private int codecReconfigurationState;
private int codecReinitializationState;
......@@ -266,20 +267,17 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
if (drmSessionManager == null) {
throw ExoPlaybackException.createForRenderer(
new IllegalStateException("Media requires a DrmSessionManager"), getIndex());
} else if (drmSessionManager.getState() == DrmSessionManager.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
}
if (!openedDrmSession) {
drmSessionManager.open(Looper.myLooper(), format.drmInitData);
openedDrmSession = true;
if (drmSession == null) {
drmSession = drmSessionManager.acquireSession(Looper.myLooper(), format.drmInitData);
}
int drmSessionState = drmSessionManager.getState();
if (drmSessionState == DrmSessionManager.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
} else if (drmSessionState == DrmSessionManager.STATE_OPENED
|| drmSessionState == DrmSessionManager.STATE_OPENED_WITH_KEYS) {
mediaCrypto = drmSessionManager.getMediaCrypto();
requiresSecureDecoder = drmSessionManager.requiresSecureDecoderComponent(mimeType);
int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} else if (drmSessionState == DrmSession.STATE_OPENED
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
mediaCrypto = drmSession.getMediaCrypto();
requiresSecureDecoder = drmSession.requiresSecureDecoderComponent(mimeType);
} else {
// The drm session isn't open yet.
return;
......@@ -349,9 +347,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
releaseCodec();
} finally {
try {
if (openedDrmSession) {
drmSessionManager.close();
openedDrmSession = false;
if (drmSession != null) {
drmSessionManager.releaseSession(drmSession);
drmSession = null;
}
} finally {
super.onDisabled();
......@@ -612,14 +610,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
}
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException {
if (!openedDrmSession) {
if (drmSession == null) {
return false;
}
int drmManagerState = drmSessionManager.getState();
if (drmManagerState == DrmSessionManager.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
}
return drmManagerState != DrmSessionManager.STATE_OPENED_WITH_KEYS
return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
}
......
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.drm;
import android.annotation.TargetApi;
import android.media.MediaCrypto;
/**
* A DRM session.
*/
@TargetApi(16)
public interface DrmSession {
/**
* The session is in an error state. {@link #getError()} can be used to retrieve the cause.
*/
int STATE_ERROR = 0;
/**
* The session is closed.
*/
int STATE_CLOSED = 1;
/**
* The session is being opened.
*/
int STATE_OPENING = 2;
/**
* The session is open, but does not yet have the keys required for decryption.
*/
int STATE_OPENED = 3;
/**
* The session is open and has the keys required for decryption.
*/
int STATE_OPENED_WITH_KEYS = 3;
/**
* Gets the current state of the session.
*
* @return One of {@link #STATE_ERROR}, {@link #STATE_CLOSED}, {@link #STATE_OPENING},
* {@link #STATE_OPENED} and {@link #STATE_OPENED_WITH_KEYS}.
*/
int getState();
/**
* Gets a {@link MediaCrypto} for the open session.
* <p>
* This method may be called when the manager is in the following states:
* {@link #STATE_OPENED}, {@link #STATE_OPENED_WITH_KEYS}
*
* @return A {@link MediaCrypto} for the open session.
* @throws IllegalStateException If called when a session isn't opened.
*/
MediaCrypto getMediaCrypto();
/**
* Whether the session requires a secure decoder for the specified mime type.
* <p>
* Normally this method should return {@link MediaCrypto#requiresSecureDecoderComponent(String)},
* however in some cases implementations may wish to modify the return value (i.e. to force a
* secure decoder even when one is not required).
* <p>
* This method may be called when the manager is in the following states:
* {@link #STATE_OPENED}, {@link #STATE_OPENED_WITH_KEYS}
*
* @return Whether the open session requires a secure decoder for the specified mime type.
* @throws IllegalStateException If called when a session isn't opened.
*/
boolean requiresSecureDecoderComponent(String mimeType);
/**
* Gets the cause of the error state.
* <p>
* This method may be called when the manager is in any state.
*
* @return An exception if the state is {@link #STATE_ERROR}. Null otherwise.
*/
Exception getError();
}
......@@ -16,7 +16,6 @@
package com.google.android.exoplayer.drm;
import android.annotation.TargetApi;
import android.media.MediaCrypto;
import android.os.Looper;
/**
......@@ -26,81 +25,20 @@ import android.os.Looper;
public interface DrmSessionManager {
/**
* The error state. {@link #getError()} can be used to retrieve the cause.
*/
int STATE_ERROR = 0;
/**
* The session is closed.
*/
int STATE_CLOSED = 1;
/**
* The session is being opened (i.e. {@link #open(Looper, DrmInitData)} has been called, but the
* session is not yet open).
*/
int STATE_OPENING = 2;
/**
* The session is open, but does not yet have the keys required for decryption.
*/
int STATE_OPENED = 3;
/**
* The session is open and has the keys required for decryption.
*/
int STATE_OPENED_WITH_KEYS = 4;
/**
* Opens the session, possibly asynchronously.
* Acquires a {@link DrmSession} for the specified {@link DrmInitData}.
* <p>
* The {@link DrmSession} must be returned to {@link #releaseSession(DrmSession)} when it is no
* longer required
*
* @param playbackLooper The looper associated with the media playback thread.
* @param drmInitData DRM initialization data.
* @return The DRM session.
*/
void open(Looper playbackLooper, DrmInitData drmInitData);
/**
* Closes the session.
*/
void close();
/**
* Gets the current state of the session.
*
* @return One of {@link #STATE_ERROR}, {@link #STATE_CLOSED}, {@link #STATE_OPENING},
* {@link #STATE_OPENED} and {@link #STATE_OPENED_WITH_KEYS}.
*/
int getState();
/**
* Gets a {@link MediaCrypto} for the open session.
* <p>
* This method may be called when the manager is in the following states:
* {@link #STATE_OPENED}, {@link #STATE_OPENED_WITH_KEYS}
*
* @return A {@link MediaCrypto} for the open session.
* @throws IllegalStateException If called when a session isn't opened.
*/
MediaCrypto getMediaCrypto();
DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitData);
/**
* Whether the session requires a secure decoder for the specified mime type.
* <p>
* Normally this method should return {@link MediaCrypto#requiresSecureDecoderComponent(String)},
* however in some cases implementations may wish to modify the return value (i.e. to force a
* secure decoder even when one is not required).
* <p>
* This method may be called when the manager is in the following states:
* {@link #STATE_OPENED}, {@link #STATE_OPENED_WITH_KEYS}
*
* @return Whether the open session requires a secure decoder for the specified mime type.
* @throws IllegalStateException If called when a session isn't opened.
*/
boolean requiresSecureDecoderComponent(String mimeType);
/**
* Gets the cause of the error state.
* <p>
* This method may be called when the manager is in any state.
*
* @return An exception if the state is {@link #STATE_ERROR}. Null otherwise.
* Releases a {@link DrmSession}.
*/
Exception getError();
void releaseSession(DrmSession drmSession);
}
......@@ -41,11 +41,10 @@ import java.util.HashMap;
import java.util.UUID;
/**
* A base class for {@link DrmSessionManager} implementations that support streaming playbacks
* using {@link MediaDrm}.
* A {@link DrmSessionManager} that supports streaming playbacks using {@link MediaDrm}.
*/
@TargetApi(18)
public class StreamingDrmSessionManager implements DrmSessionManager {
public class StreamingDrmSessionManager implements DrmSessionManager, DrmSession {
/**
* Interface definition for a callback to be notified of {@link StreamingDrmSessionManager}
......@@ -172,32 +171,6 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
state = STATE_CLOSED;
}
@Override
public final int getState() {
return state;
}
@Override
public final MediaCrypto getMediaCrypto() {
if (state != STATE_OPENED && state != STATE_OPENED_WITH_KEYS) {
throw new IllegalStateException();
}
return mediaCrypto;
}
@Override
public boolean requiresSecureDecoderComponent(String mimeType) {
if (state != STATE_OPENED && state != STATE_OPENED_WITH_KEYS) {
throw new IllegalStateException();
}
return mediaCrypto.requiresSecureDecoderComponent(mimeType);
}
@Override
public final Exception getError() {
return state == STATE_ERROR ? lastException : null;
}
/**
* Provides access to {@link MediaDrm#getPropertyString(String)}.
* <p>
......@@ -246,11 +219,13 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
mediaDrm.setPropertyByteArray(key, value);
}
// DrmSessionManager implementation.
@Override
public void open(Looper playbackLooper, DrmInitData drmInitData) {
public DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
if (++openCount != 1) {
return;
return this;
}
if (this.playbackLooper == null) {
......@@ -266,7 +241,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
schemeData = drmInitData.get(uuid);
if (schemeData == null) {
onError(new IllegalStateException("Media does not support uuid: " + uuid));
return;
return this;
}
if (Util.SDK_INT < 21) {
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
......@@ -279,10 +254,11 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
}
state = STATE_OPENING;
openInternal(true);
return this;
}
@Override
public void close() {
public void releaseSession(DrmSession session) {
if (--openCount != 0) {
return;
}
......@@ -303,6 +279,36 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
}
}
// DrmSession implementation.
@Override
public final int getState() {
return state;
}
@Override
public final MediaCrypto getMediaCrypto() {
if (state != STATE_OPENED && state != STATE_OPENED_WITH_KEYS) {
throw new IllegalStateException();
}
return mediaCrypto;
}
@Override
public boolean requiresSecureDecoderComponent(String mimeType) {
if (state != STATE_OPENED && state != STATE_OPENED_WITH_KEYS) {
throw new IllegalStateException();
}
return mediaCrypto.requiresSecureDecoderComponent(mimeType);
}
@Override
public final Exception getError() {
return state == STATE_ERROR ? lastException : null;
}
// Internal methods.
private void openInternal(boolean allowProvisioning) {
try {
sessionId = mediaDrm.openSession();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment