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 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; 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.drm.DrmSessionManager;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.NalUnitUtil; import com.google.android.exoplayer.util.NalUnitUtil;
...@@ -155,6 +156,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -155,6 +156,7 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private Format format; private Format format;
private MediaCodec codec; private MediaCodec codec;
private DrmSession drmSession;
private boolean codecIsAdaptive; private boolean codecIsAdaptive;
private boolean codecNeedsDiscardToSpsWorkaround; private boolean codecNeedsDiscardToSpsWorkaround;
private boolean codecNeedsFlushWorkaround; private boolean codecNeedsFlushWorkaround;
...@@ -167,7 +169,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -167,7 +169,6 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
private int inputIndex; private int inputIndex;
private int outputIndex; private int outputIndex;
private boolean shouldSkipOutputBuffer; private boolean shouldSkipOutputBuffer;
private boolean openedDrmSession;
private boolean codecReconfigured; private boolean codecReconfigured;
private int codecReconfigurationState; private int codecReconfigurationState;
private int codecReinitializationState; private int codecReinitializationState;
...@@ -266,20 +267,17 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -266,20 +267,17 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
if (drmSessionManager == null) { if (drmSessionManager == null) {
throw ExoPlaybackException.createForRenderer( throw ExoPlaybackException.createForRenderer(
new IllegalStateException("Media requires a DrmSessionManager"), getIndex()); new IllegalStateException("Media requires a DrmSessionManager"), getIndex());
} else if (drmSessionManager.getState() == DrmSessionManager.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
} }
if (!openedDrmSession) { if (drmSession == null) {
drmSessionManager.open(Looper.myLooper(), format.drmInitData); drmSession = drmSessionManager.acquireSession(Looper.myLooper(), format.drmInitData);
openedDrmSession = true;
} }
int drmSessionState = drmSessionManager.getState(); int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSessionManager.STATE_ERROR) { if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex()); throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} else if (drmSessionState == DrmSessionManager.STATE_OPENED } else if (drmSessionState == DrmSession.STATE_OPENED
|| drmSessionState == DrmSessionManager.STATE_OPENED_WITH_KEYS) { || drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) {
mediaCrypto = drmSessionManager.getMediaCrypto(); mediaCrypto = drmSession.getMediaCrypto();
requiresSecureDecoder = drmSessionManager.requiresSecureDecoderComponent(mimeType); requiresSecureDecoder = drmSession.requiresSecureDecoderComponent(mimeType);
} else { } else {
// The drm session isn't open yet. // The drm session isn't open yet.
return; return;
...@@ -349,9 +347,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -349,9 +347,9 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
releaseCodec(); releaseCodec();
} finally { } finally {
try { try {
if (openedDrmSession) { if (drmSession != null) {
drmSessionManager.close(); drmSessionManager.releaseSession(drmSession);
openedDrmSession = false; drmSession = null;
} }
} finally { } finally {
super.onDisabled(); super.onDisabled();
...@@ -612,14 +610,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer { ...@@ -612,14 +610,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
} }
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException { private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException {
if (!openedDrmSession) { if (drmSession == null) {
return false; return false;
} }
int drmManagerState = drmSessionManager.getState(); int drmSessionState = drmSession.getState();
if (drmManagerState == DrmSessionManager.STATE_ERROR) { if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex()); throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} }
return drmManagerState != DrmSessionManager.STATE_OPENED_WITH_KEYS return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS
&& (bufferEncrypted || !playClearSamplesWithoutKeys); && (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 @@ ...@@ -16,7 +16,6 @@
package com.google.android.exoplayer.drm; package com.google.android.exoplayer.drm;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.media.MediaCrypto;
import android.os.Looper; import android.os.Looper;
/** /**
...@@ -26,81 +25,20 @@ import android.os.Looper; ...@@ -26,81 +25,20 @@ import android.os.Looper;
public interface DrmSessionManager { public interface DrmSessionManager {
/** /**
* The error state. {@link #getError()} can be used to retrieve the cause. * Acquires a {@link DrmSession} for the specified {@link DrmInitData}.
*/ * <p>
int STATE_ERROR = 0; * The {@link DrmSession} must be returned to {@link #releaseSession(DrmSession)} when it is no
/** * longer required
* 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.
* *
* @param playbackLooper The looper associated with the media playback thread. * @param playbackLooper The looper associated with the media playback thread.
* @param drmInitData DRM initialization data. * @param drmInitData DRM initialization data.
* @return The DRM session.
*/ */
void open(Looper playbackLooper, DrmInitData drmInitData); DrmSession acquireSession(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();
/** /**
* Whether the session requires a secure decoder for the specified mime type. * Releases a {@link DrmSession}.
* <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(); void releaseSession(DrmSession drmSession);
} }
...@@ -41,11 +41,10 @@ import java.util.HashMap; ...@@ -41,11 +41,10 @@ import java.util.HashMap;
import java.util.UUID; import java.util.UUID;
/** /**
* A base class for {@link DrmSessionManager} implementations that support streaming playbacks * A {@link DrmSessionManager} that supports streaming playbacks using {@link MediaDrm}.
* using {@link MediaDrm}.
*/ */
@TargetApi(18) @TargetApi(18)
public class StreamingDrmSessionManager implements DrmSessionManager { public class StreamingDrmSessionManager implements DrmSessionManager, DrmSession {
/** /**
* Interface definition for a callback to be notified of {@link StreamingDrmSessionManager} * Interface definition for a callback to be notified of {@link StreamingDrmSessionManager}
...@@ -172,32 +171,6 @@ public class StreamingDrmSessionManager implements DrmSessionManager { ...@@ -172,32 +171,6 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
state = STATE_CLOSED; 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)}. * Provides access to {@link MediaDrm#getPropertyString(String)}.
* <p> * <p>
...@@ -246,11 +219,13 @@ public class StreamingDrmSessionManager implements DrmSessionManager { ...@@ -246,11 +219,13 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
mediaDrm.setPropertyByteArray(key, value); mediaDrm.setPropertyByteArray(key, value);
} }
// DrmSessionManager implementation.
@Override @Override
public void open(Looper playbackLooper, DrmInitData drmInitData) { public DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper); Assertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
if (++openCount != 1) { if (++openCount != 1) {
return; return this;
} }
if (this.playbackLooper == null) { if (this.playbackLooper == null) {
...@@ -266,7 +241,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager { ...@@ -266,7 +241,7 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
schemeData = drmInitData.get(uuid); schemeData = drmInitData.get(uuid);
if (schemeData == null) { if (schemeData == null) {
onError(new IllegalStateException("Media does not support uuid: " + uuid)); onError(new IllegalStateException("Media does not support uuid: " + uuid));
return; return this;
} }
if (Util.SDK_INT < 21) { if (Util.SDK_INT < 21) {
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom. // Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
...@@ -279,10 +254,11 @@ public class StreamingDrmSessionManager implements DrmSessionManager { ...@@ -279,10 +254,11 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
} }
state = STATE_OPENING; state = STATE_OPENING;
openInternal(true); openInternal(true);
return this;
} }
@Override @Override
public void close() { public void releaseSession(DrmSession session) {
if (--openCount != 0) { if (--openCount != 0) {
return; return;
} }
...@@ -303,6 +279,36 @@ public class StreamingDrmSessionManager implements DrmSessionManager { ...@@ -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) { private void openInternal(boolean allowProvisioning) {
try { try {
sessionId = mediaDrm.openSession(); 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