DrmSession cleanup

These changes are in part related to handling playback of mixed clear
and encrypted content, where we might want to use a secure decoder
throughout, but only have drm init data and only care about the state
of the DrmSession during playback of encrypted parts.

- requiresSecureDecoderComponent became unnecessary when we added
  ExoMediaCrypto, which provides a layer in which requiresSecureDecoderComponent
  can be overridden.
- Relaxed requirements for obtaining the MediaCrypto. It's helpful
  to allow retrieval in the error state, since it can be used to
  instantiate a decoder and play clear samples.
- Deferred throwing of errors in renderer implementations. As long as
  we can get a MediaCrypto, we should init the codec. We can also
  play clear samples without failing if playClearSamplesWithoutKeys is
  true, regardless of the errors state.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160536365
This commit is contained in:
olly 2017-06-29 09:41:36 -07:00 committed by Oliver Woodman
parent beb0734107
commit db177db6d9
9 changed files with 124 additions and 142 deletions

View file

@ -30,6 +30,7 @@ import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -185,42 +186,21 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
} }
} }
// We have a format. // If we don't have a decoder yet, we need to instantiate one.
drmSession = pendingDrmSession; maybeInitDecoder();
ExoMediaCrypto mediaCrypto = null;
if (drmSession != null) { if (decoder != null) {
int drmSessionState = drmSession.getState(); try {
if (drmSessionState == DrmSession.STATE_ERROR) { // Rendering loop.
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); TraceUtil.beginSection("drainAndFeed");
} else if (drmSessionState == DrmSession.STATE_OPENED while (drainOutputBuffer(positionUs)) {}
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) { while (feedInputBuffer()) {}
mediaCrypto = drmSession.getMediaCrypto();
} else {
// The drm session isn't open yet.
return;
}
}
try {
if (decoder == null) {
// If we don't have a decoder yet, we need to instantiate one.
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
TraceUtil.beginSection("createVpxDecoder");
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto);
decoder.setOutputMode(outputMode);
TraceUtil.endSection(); TraceUtil.endSection();
long codecInitializedTimestamp = SystemClock.elapsedRealtime(); } catch (VpxDecoderException e) {
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp, throw ExoPlaybackException.createForRenderer(e, getIndex());
codecInitializedTimestamp - codecInitializingTimestamp);
decoderCounters.decoderInitCount++;
} }
TraceUtil.beginSection("drainAndFeed"); decoderCounters.ensureUpdated();
while (drainOutputBuffer(positionUs)) {}
while (feedInputBuffer()) {}
TraceUtil.endSection();
} catch (VpxDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
} }
decoderCounters.ensureUpdated();
} }
private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException { private boolean drainOutputBuffer(long positionUs) throws VpxDecoderException {
@ -399,15 +379,14 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
} }
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException { private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException {
if (drmSession == null) { if (drmSession == null || (!bufferEncrypted && playClearSamplesWithoutKeys)) {
return false; return false;
} }
int drmSessionState = drmSession.getState(); @DrmSession.State int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) { if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} }
return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS;
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
} }
private void flushDecoder() { private void flushDecoder() {
@ -516,6 +495,40 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
} }
} }
private void maybeInitDecoder() throws ExoPlaybackException {
if (decoder != null) {
return;
}
drmSession = pendingDrmSession;
ExoMediaCrypto mediaCrypto = null;
if (drmSession != null) {
mediaCrypto = drmSession.getMediaCrypto();
if (mediaCrypto == null) {
DrmSessionException drmError = drmSession.getError();
if (drmError != null) {
throw ExoPlaybackException.createForRenderer(drmError, getIndex());
}
// The drm session isn't open yet.
return;
}
}
try {
long codecInitializingTimestamp = SystemClock.elapsedRealtime();
TraceUtil.beginSection("createVpxDecoder");
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE, mediaCrypto);
decoder.setOutputMode(outputMode);
TraceUtil.endSection();
long codecInitializedTimestamp = SystemClock.elapsedRealtime();
eventDispatcher.decoderInitialized(decoder.getName(), codecInitializedTimestamp,
codecInitializedTimestamp - codecInitializingTimestamp);
decoderCounters.decoderInitCount++;
} catch (VpxDecoderException e) {
throw ExoPlaybackException.createForRenderer(e, getIndex());
}
}
private void releaseDecoder() { private void releaseDecoder() {
if (decoder != null) { if (decoder != null) {
decoder.release(); decoder.release();

View file

@ -1463,7 +1463,7 @@ public final class AudioTrack {
@TargetApi(21) @TargetApi(21)
private int writeNonBlockingWithAvSyncV21(android.media.AudioTrack audioTrack, private int writeNonBlockingWithAvSyncV21(android.media.AudioTrack audioTrack,
ByteBuffer buffer, int size, long presentationTimeUs) { ByteBuffer buffer, int size, long presentationTimeUs) {
// TODO: Uncomment this when [Internal ref b/33627517] is clarified or fixed. // TODO: Uncomment this when [Internal ref: b/33627517] is clarified or fixed.
// if (Util.SDK_INT >= 23) { // if (Util.SDK_INT >= 23) {
// // The underlying platform AudioTrack writes AV sync headers directly. // // The underlying platform AudioTrack writes AV sync headers directly.
// return audioTrack.write(buffer, size, WRITE_NON_BLOCKING, presentationTimeUs * 1000); // return audioTrack.write(buffer, size, WRITE_NON_BLOCKING, presentationTimeUs * 1000);

View file

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.SimpleDecoder; import com.google.android.exoplayer2.decoder.SimpleDecoder;
import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; import com.google.android.exoplayer2.decoder.SimpleOutputBuffer;
import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.drm.ExoMediaCrypto;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
@ -376,15 +377,14 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
} }
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException { private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException {
if (drmSession == null) { if (drmSession == null || (!bufferEncrypted && playClearSamplesWithoutKeys)) {
return false; return false;
} }
@DrmSession.State int drmSessionState = drmSession.getState(); @DrmSession.State int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) { if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} }
return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS;
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
} }
private void processEndOfStream() throws ExoPlaybackException { private void processEndOfStream() throws ExoPlaybackException {
@ -514,13 +514,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
drmSession = pendingDrmSession; drmSession = pendingDrmSession;
ExoMediaCrypto mediaCrypto = null; ExoMediaCrypto mediaCrypto = null;
if (drmSession != null) { if (drmSession != null) {
@DrmSession.State int drmSessionState = drmSession.getState(); mediaCrypto = drmSession.getMediaCrypto();
if (drmSessionState == DrmSession.STATE_ERROR) { if (mediaCrypto == null) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); DrmSessionException drmError = drmSession.getError();
} else if (drmSessionState == DrmSession.STATE_OPENED if (drmError != null) {
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) { throw ExoPlaybackException.createForRenderer(drmError, getIndex());
mediaCrypto = drmSession.getMediaCrypto(); }
} else {
// The drm session isn't open yet. // The drm session isn't open yet.
return; return;
} }

View file

@ -222,7 +222,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
mediaDrm.setOnEventListener(new MediaDrmEventListener()); mediaDrm.setOnEventListener(new MediaDrmEventListener());
state = STATE_CLOSED;
mode = MODE_PLAYBACK; mode = MODE_PLAYBACK;
} }
@ -358,7 +357,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
if (--openCount != 0) { if (--openCount != 0) {
return; return;
} }
state = STATE_CLOSED; state = STATE_RELEASED;
provisioningInProgress = false; provisioningInProgress = false;
mediaDrmHandler.removeCallbacksAndMessages(null); mediaDrmHandler.removeCallbacksAndMessages(null);
postResponseHandler.removeCallbacksAndMessages(null); postResponseHandler.removeCallbacksAndMessages(null);
@ -384,35 +383,19 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
return state; return state;
} }
@Override
public final T 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 @Override
public final DrmSessionException getError() { public final DrmSessionException getError() {
return state == STATE_ERROR ? lastException : null; return state == STATE_ERROR ? lastException : null;
} }
@Override
public final T getMediaCrypto() {
return mediaCrypto;
}
@Override @Override
public Map<String, String> queryKeyStatus() { public Map<String, String> queryKeyStatus() {
// User may call this method rightfully even if state == STATE_ERROR. So only check if there is return sessionId == null ? null : mediaDrm.queryKeyStatus(sessionId);
// a sessionId
if (sessionId == null) {
throw new IllegalStateException();
}
return mediaDrm.queryKeyStatus(sessionId);
} }
@Override @Override

View file

@ -41,16 +41,16 @@ public interface DrmSession<T extends ExoMediaCrypto> {
* The state of the DRM session. * The state of the DRM session.
*/ */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_ERROR, STATE_CLOSED, STATE_OPENING, STATE_OPENED, STATE_OPENED_WITH_KEYS}) @IntDef({STATE_RELEASED, STATE_ERROR, STATE_OPENING, STATE_OPENED, STATE_OPENED_WITH_KEYS})
@interface State {} @interface State {}
/**
* The session has been released.
*/
int STATE_RELEASED = 0;
/** /**
* The session has encountered an error. {@link #getError()} can be used to retrieve the cause. * The session has encountered an error. {@link #getError()} can be used to retrieve the cause.
*/ */
int STATE_ERROR = 0; int STATE_ERROR = 1;
/**
* The session is closed.
*/
int STATE_CLOSED = 1;
/** /**
* The session is being opened. * The session is being opened.
*/ */
@ -65,66 +65,40 @@ public interface DrmSession<T extends ExoMediaCrypto> {
int STATE_OPENED_WITH_KEYS = 4; int STATE_OPENED_WITH_KEYS = 4;
/** /**
* Returns the current state of the session. * Returns the current state of the session, which is one of {@link #STATE_ERROR},
* * {@link #STATE_RELEASED}, {@link #STATE_OPENING}, {@link #STATE_OPENED} and
* @return One of {@link #STATE_ERROR}, {@link #STATE_CLOSED}, {@link #STATE_OPENING}, * {@link #STATE_OPENED_WITH_KEYS}.
* {@link #STATE_OPENED} and {@link #STATE_OPENED_WITH_KEYS}.
*/ */
@State int getState(); @State int getState();
/**
* Returns a {@link ExoMediaCrypto} for the open session.
* <p>
* This method may be called when the session is in the following states:
* {@link #STATE_OPENED}, {@link #STATE_OPENED_WITH_KEYS}
*
* @return A {@link ExoMediaCrypto} for the open session.
* @throws IllegalStateException If called when a session isn't opened.
*/
T getMediaCrypto();
/**
* Whether the session requires a secure decoder for the specified mime type.
* <p>
* Normally this method should return
* {@link ExoMediaCrypto#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 session 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);
/** /**
* Returns the cause of the error state. * Returns the cause of the error state.
* <p>
* This method may be called when the session is in any state.
*
* @return An exception if the state is {@link #STATE_ERROR}. Null otherwise.
*/ */
DrmSessionException getError(); DrmSessionException getError();
/** /**
* Returns an informative description of the key status for the session. The status is in the form * Returns a {@link ExoMediaCrypto} for the open session, or null if called before the session has
* of {name, value} pairs. * been opened or after it's been released.
* */
* <p>Since DRM license policies vary by vendor, the specific status field names are determined by T getMediaCrypto();
/**
* Returns a map describing the key status for the session, or null if called before the session
* has been opened or after it's been released.
* <p>
* Since DRM license policies vary by vendor, the specific status field names are determined by
* each DRM vendor. Refer to your DRM provider documentation for definitions of the field names * each DRM vendor. Refer to your DRM provider documentation for definitions of the field names
* for a particular DRM engine plugin. * for a particular DRM engine plugin.
* *
* @return A map of key status. * @return A map describing the key status for the session, or null if called before the session
* @throws IllegalStateException If called when the session isn't opened. * has been opened or after it's been released.
* @see MediaDrm#queryKeyStatus(byte[]) * @see MediaDrm#queryKeyStatus(byte[])
*/ */
Map<String, String> queryKeyStatus(); Map<String, String> queryKeyStatus();
/** /**
* Returns the key set id of the offline license loaded into this session, if there is one. Null * Returns the key set id of the offline license loaded into this session, or null if there isn't
* otherwise. * one.
*/ */
byte[] getOfflineLicenseKeySetId(); byte[] getOfflineLicenseKeySetId();

View file

@ -26,9 +26,12 @@ import com.google.android.exoplayer2.util.Assertions;
public final class FrameworkMediaCrypto implements ExoMediaCrypto { public final class FrameworkMediaCrypto implements ExoMediaCrypto {
private final MediaCrypto mediaCrypto; private final MediaCrypto mediaCrypto;
private final boolean forceAllowInsecureDecoderComponents;
/* package */ FrameworkMediaCrypto(MediaCrypto mediaCrypto) { /* package */ FrameworkMediaCrypto(MediaCrypto mediaCrypto,
boolean forceAllowInsecureDecoderComponents) {
this.mediaCrypto = Assertions.checkNotNull(mediaCrypto); this.mediaCrypto = Assertions.checkNotNull(mediaCrypto);
this.forceAllowInsecureDecoderComponents = forceAllowInsecureDecoderComponents;
} }
public MediaCrypto getWrappedMediaCrypto() { public MediaCrypto getWrappedMediaCrypto() {
@ -37,7 +40,8 @@ public final class FrameworkMediaCrypto implements ExoMediaCrypto {
@Override @Override
public boolean requiresSecureDecoderComponent(String mimeType) { public boolean requiresSecureDecoderComponent(String mimeType) {
return mediaCrypto.requiresSecureDecoderComponent(mimeType); return !forceAllowInsecureDecoderComponents
&& mediaCrypto.requiresSecureDecoderComponent(mimeType);
} }
} }

View file

@ -24,7 +24,9 @@ import android.media.NotProvisionedException;
import android.media.ResourceBusyException; import android.media.ResourceBusyException;
import android.media.UnsupportedSchemeException; import android.media.UnsupportedSchemeException;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -163,7 +165,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
@Override @Override
public FrameworkMediaCrypto createMediaCrypto(UUID uuid, byte[] initData) public FrameworkMediaCrypto createMediaCrypto(UUID uuid, byte[] initData)
throws MediaCryptoException { throws MediaCryptoException {
return new FrameworkMediaCrypto(new MediaCrypto(uuid, initData)); // Work around a bug prior to Lollipop where L1 Widevine forced into L3 mode would still
// indicate that it required secure video decoders [Internal ref: b/11428937].
boolean forceAllowInsecureDecoderComponents = Util.SDK_INT < 21
&& C.WIDEVINE_UUID.equals(uuid) && "L3".equals(getPropertyString("securityLevel"));
return new FrameworkMediaCrypto(new MediaCrypto(uuid, initData),
forceAllowInsecureDecoderComponents);
} }
} }

View file

@ -34,14 +34,16 @@ public final class WidevineUtil {
/** /**
* Returns license and playback durations remaining in seconds. * Returns license and playback durations remaining in seconds.
* *
* @return A {@link Pair} consisting of the remaining license and playback durations in seconds. * @param drmSession The drm session to query.
* @throws IllegalStateException If called when a session isn't opened. * @return A {@link Pair} consisting of the remaining license and playback durations in seconds,
* @param drmSession * or null if called before the session has been opened or after it's been released.
*/ */
public static Pair<Long, Long> getLicenseDurationRemainingSec(DrmSession<?> drmSession) { public static Pair<Long, Long> getLicenseDurationRemainingSec(DrmSession<?> drmSession) {
Map<String, String> keyStatus = drmSession.queryKeyStatus(); Map<String, String> keyStatus = drmSession.queryKeyStatus();
return new Pair<>( if (keyStatus == null) {
getDurationRemainingSec(keyStatus, PROPERTY_LICENSE_DURATION_REMAINING), return null;
}
return new Pair<>(getDurationRemainingSec(keyStatus, PROPERTY_LICENSE_DURATION_REMAINING),
getDurationRemainingSec(keyStatus, PROPERTY_PLAYBACK_DURATION_REMAINING)); getDurationRemainingSec(keyStatus, PROPERTY_PLAYBACK_DURATION_REMAINING));
} }

View file

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.drm.DrmSession;
import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException;
@ -298,20 +299,20 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
drmSession = pendingDrmSession; drmSession = pendingDrmSession;
String mimeType = format.sampleMimeType; String mimeType = format.sampleMimeType;
MediaCrypto mediaCrypto = null; MediaCrypto wrappedMediaCrypto = null;
boolean drmSessionRequiresSecureDecoder = false; boolean drmSessionRequiresSecureDecoder = false;
if (drmSession != null) { if (drmSession != null) {
@DrmSession.State int drmSessionState = drmSession.getState(); FrameworkMediaCrypto mediaCrypto = drmSession.getMediaCrypto();
if (drmSessionState == DrmSession.STATE_ERROR) { if (mediaCrypto == null) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); DrmSessionException drmError = drmSession.getError();
} else if (drmSessionState == DrmSession.STATE_OPENED if (drmError != null) {
|| drmSessionState == DrmSession.STATE_OPENED_WITH_KEYS) { throw ExoPlaybackException.createForRenderer(drmError, getIndex());
mediaCrypto = drmSession.getMediaCrypto().getWrappedMediaCrypto(); }
drmSessionRequiresSecureDecoder = drmSession.requiresSecureDecoderComponent(mimeType);
} else {
// The drm session isn't open yet. // The drm session isn't open yet.
return; return;
} }
wrappedMediaCrypto = mediaCrypto.getWrappedMediaCrypto();
drmSessionRequiresSecureDecoder = mediaCrypto.requiresSecureDecoderComponent(mimeType);
} }
if (codecInfo == null) { if (codecInfo == null) {
@ -358,7 +359,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codec = MediaCodec.createByCodecName(codecName); codec = MediaCodec.createByCodecName(codecName);
TraceUtil.endSection(); TraceUtil.endSection();
TraceUtil.beginSection("configureCodec"); TraceUtil.beginSection("configureCodec");
configureCodec(codecInfo, codec, format, mediaCrypto); configureCodec(codecInfo, codec, format, wrappedMediaCrypto);
TraceUtil.endSection(); TraceUtil.endSection();
TraceUtil.beginSection("startCodec"); TraceUtil.beginSection("startCodec");
codec.start(); codec.start();
@ -736,15 +737,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
} }
private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException { private boolean shouldWaitForKeys(boolean bufferEncrypted) throws ExoPlaybackException {
if (drmSession == null) { if (drmSession == null || (!bufferEncrypted && playClearSamplesWithoutKeys)) {
return false; return false;
} }
@DrmSession.State int drmSessionState = drmSession.getState(); @DrmSession.State int drmSessionState = drmSession.getState();
if (drmSessionState == DrmSession.STATE_ERROR) { if (drmSessionState == DrmSession.STATE_ERROR) {
throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex()); throw ExoPlaybackException.createForRenderer(drmSession.getError(), getIndex());
} }
return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS return drmSessionState != DrmSession.STATE_OPENED_WITH_KEYS;
&& (bufferEncrypted || !playClearSamplesWithoutKeys);
} }
/** /**