mirror of
https://github.com/samsonjs/media.git
synced 2026-04-15 12:55:46 +00:00
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
This commit is contained in:
parent
da2cfd601d
commit
7d229003ad
4 changed files with 157 additions and 124 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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);
|
||||
DrmSession acquireSession(Looper playbackLooper, DrmInitData drmInitData);
|
||||
|
||||
/**
|
||||
* Closes the session.
|
||||
* Releases a {@link DrmSession}.
|
||||
*/
|
||||
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.
|
||||
* <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;
|
|||
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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue