Make DRM work in ExoPlayer V2.

Bigger changes will be coming, but this gets DRM back
up and working again.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122719062
This commit is contained in:
olly 2016-05-19 04:19:00 -07:00 committed by Oliver Woodman
parent 80f67d908a
commit 7d4c786e21
8 changed files with 127 additions and 126 deletions

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.TrackGroup;
@ -65,7 +66,7 @@ public class EventLogger implements DemoPlayer.Listener, DemoPlayer.InfoListener
}
@Override
public void onError(Exception e) {
public void onError(ExoPlaybackException e) {
Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
}

View File

@ -27,6 +27,7 @@ import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.dash.DashSampleSource;
import com.google.android.exoplayer.demo.player.DemoPlayer;
import com.google.android.exoplayer.demo.ui.TrackSelectionHelper;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.hls.HlsSampleSource;
@ -261,39 +262,21 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
return Util.SDK_INT >= 23
&& Util.isLocalFileUri(uri)
&& checkSelfPermission(permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED;
!= PackageManager.PERMISSION_GRANTED;
}
// Internal methods
@SuppressWarnings("unused")
private SampleSource buildSource(Uri uri, String id, String provider, int type) {
switch (type) {
case Util.TYPE_SS:
// TODO: Bring back DRM support. new SmoothStreamingTestMediaDrmCallback()
return new SmoothStreamingSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_DASH:
// TODO: Bring back DRM support. new WidevineTestMediaDrmCallback(id, provider)
return new DashSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_HLS:
return new HlsSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_OTHER:
Allocator allocator = new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE);
DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter());
return new ExtractorSampleSource(uri, dataSource, allocator, C.DEFAULT_MUXED_BUFFER_SIZE,
player.getMainHandler(), player, 0, ExtractorSampleSource.newDefaultExtractors());
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
private void initializePlayer() {
Intent intent = getIntent();
Uri uri = intent.getData();
int type = intent.getIntExtra(CONTENT_TYPE_EXTRA,
inferContentType(uri, intent.getStringExtra(CONTENT_EXT_EXTRA)));
if (player == null) {
boolean useExtensionDecoders = getIntent().getBooleanExtra(USE_EXTENSION_DECODERS, false);
player = new DemoPlayer(this, useExtensionDecoders);
String id = intent.getStringExtra(CONTENT_ID_EXTRA);
String provider = intent.getStringExtra(PROVIDER_EXTRA);
boolean useExtensionDecoders = intent.getBooleanExtra(USE_EXTENSION_DECODERS, false);
player = new DemoPlayer(this, buildDrmCallback(type, id, provider), useExtensionDecoders);
player.addListener(this);
player.setCaptionListener(this);
player.setMetadataListener(this);
@ -313,22 +296,51 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
debugViewHelper.start();
}
if (playerNeedsSource) {
Intent intent = getIntent();
Uri uri = intent.getData();
if (maybeRequestPermission(uri)) {
// The player will be reinitialized if permission is granted.
return;
}
String id = intent.getStringExtra(CONTENT_ID_EXTRA);
String provider = intent.getStringExtra(PROVIDER_EXTRA);
int type = intent.getIntExtra(CONTENT_TYPE_EXTRA,
inferContentType(uri, intent.getStringExtra(CONTENT_EXT_EXTRA)));
player.setSource(buildSource(uri, id, provider, type));
player.setSource(buildSource(type, uri));
playerNeedsSource = false;
updateButtonVisibilities();
}
}
private MediaDrmCallback buildDrmCallback(int type, String id, String provider) {
if (Util.SDK_INT < 18) {
return null;
}
switch (type) {
case Util.TYPE_SS:
return new SmoothStreamingTestMediaDrmCallback();
case Util.TYPE_DASH:
return new WidevineTestMediaDrmCallback(id, provider);
default:
return null;
}
}
private SampleSource buildSource(int type, Uri uri) {
switch (type) {
case Util.TYPE_SS:
return new SmoothStreamingSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_DASH:
return new DashSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_HLS:
return new HlsSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_OTHER:
Allocator allocator = new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE);
DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter());
return new ExtractorSampleSource(uri, dataSource, allocator, C.DEFAULT_MUXED_BUFFER_SIZE,
player.getMainHandler(), player, 0, ExtractorSampleSource.newDefaultExtractors());
default:
throw new IllegalStateException("Unsupported type: " + type);
}
}
private void releasePlayer() {
if (player != null) {
shutterView.setVisibility(View.VISIBLE);
@ -377,32 +389,34 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
}
@Override
public void onError(Exception e) {
public void onError(ExoPlaybackException e) {
String errorString = null;
if (e instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e;
errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
} else if (e instanceof ExoPlaybackException
&& e.getCause() instanceof DecoderInitializationException) {
// Special case for decoder initialization failures.
DecoderInitializationException decoderInitializationException =
(DecoderInitializationException) e.getCause();
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof DecoderQueryException) {
errorString = getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
if (e.type == ExoPlaybackException.TYPE_RENDERER) {
Exception cause = e.getRendererException();
if (cause instanceof DecoderInitializationException) {
// Special case for decoder initialization failures.
DecoderInitializationException decoderInitializationException =
(DecoderInitializationException) cause;
if (decoderInitializationException.decoderName == null) {
if (decoderInitializationException.getCause() instanceof DecoderQueryException) {
errorString = getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorString = getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
errorString = getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
}
} else {
errorString = getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
} else if (cause instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) cause;
errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
}
}
if (errorString != null) {

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.util.Util;
@ -33,7 +34,7 @@ import java.util.UUID;
* Demo {@link StreamingDrmSessionManager} for smooth streaming test content.
*/
@TargetApi(18)
public class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback {
public final class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback {
private static final String PLAYREADY_TEST_DEFAULT_URI =
"http://playready.directtaps.net/pr/svc/rightsmanager.asmx";
@ -46,6 +47,11 @@ public class SmoothStreamingTestMediaDrmCallback implements MediaDrmCallback {
KEY_REQUEST_PROPERTIES = keyRequestProperties;
}
@Override
public UUID getUuid() {
return C.PLAYREADY_UUID;
}
@Override
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException {
String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());

View File

@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.util.Util;
@ -30,7 +31,7 @@ import java.util.UUID;
* A {@link MediaDrmCallback} for Widevine test content.
*/
@TargetApi(18)
public class WidevineTestMediaDrmCallback implements MediaDrmCallback {
public final class WidevineTestMediaDrmCallback implements MediaDrmCallback {
private static final String WIDEVINE_GTS_DEFAULT_BASE_URI =
"https://proxy.uat.widevine.com/proxy";
@ -42,6 +43,11 @@ public class WidevineTestMediaDrmCallback implements MediaDrmCallback {
defaultUri = WIDEVINE_GTS_DEFAULT_BASE_URI + params;
}
@Override
public UUID getUuid() {
return C.WIDEVINE_UUID;
}
@Override
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException {
String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());

View File

@ -33,6 +33,8 @@ import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.VideoTrackRendererEventListener;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
@ -75,7 +77,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
*/
public interface Listener {
void onStateChanged(boolean playWhenReady, int playbackState);
void onError(Exception e);
void onError(ExoPlaybackException e);
void onTracksChanged(TrackInfo trackInfo);
void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
float pixelWidthHeightRatio);
@ -86,8 +88,8 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
* <p>
* These errors are not visible to the user, and hence this listener is provided for
* informational purposes only. Note however that an internal error may cause a fatal
* error if the player fails to recover. If this happens, {@link Listener#onError(Exception)}
* will be invoked.
* error if the player fails to recover. If this happens,
* {@link Listener#onError(ExoPlaybackException)} will be invoked.
*/
public interface InternalErrorListener {
void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs);
@ -153,7 +155,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
private InfoListener infoListener;
private CodecCounters videoCodecCounters;
public DemoPlayer(Context context, boolean useExtensionDecoders) {
public DemoPlayer(Context context, MediaDrmCallback drmCallback, boolean useExtensionDecoders) {
mainHandler = new Handler();
bandwidthMeter = new DefaultBandwidthMeter();
listeners = new CopyOnWriteArrayList<>();
@ -163,7 +165,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
if (useExtensionDecoders) {
buildExtensionRenderers(renderersList);
}
buildRenderers(context, renderersList);
buildRenderers(context, drmCallback, renderersList);
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
// Build the player and associated objects.
@ -460,14 +462,18 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
}
}
private void buildRenderers(Context context, ArrayList<TrackRenderer> renderersList) {
MediaCodecVideoTrackRenderer videoRenderer =
new MediaCodecVideoTrackRenderer(context, MediaCodecSelector.DEFAULT,
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, this, 50);
private void buildRenderers(Context context, MediaDrmCallback drmCallback,
ArrayList<TrackRenderer> renderersList) {
DrmSessionManager drmSessionManager = drmCallback == null ? null
: new StreamingDrmSessionManager(drmCallback, null, mainHandler, this);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
drmSessionManager, false, mainHandler, this, 50);
renderersList.add(videoRenderer);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT, null,
true, mainHandler, this, AudioCapabilities.getCapabilities(context),
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(MediaCodecSelector.DEFAULT,
drmSessionManager, true, mainHandler, this, AudioCapabilities.getCapabilities(context),
AudioManager.STREAM_MUSIC);
renderersList.add(audioRenderer);

View File

@ -266,6 +266,8 @@ 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);

View File

@ -26,6 +26,13 @@ import java.util.UUID;
@TargetApi(18)
public interface MediaDrmCallback {
/**
* Returns the {@link UUID} of the DRM scheme that this callback supports.
*
* @return The DRM scheme {@link UUID}.
*/
UUID getUuid();
/**
* Executes a provisioning request.
*

View File

@ -35,7 +35,6 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import java.util.HashMap;
import java.util.UUID;
@ -99,77 +98,37 @@ public class StreamingDrmSessionManager implements DrmSessionManager {
private byte[] sessionId;
/**
* Instantiates a new instance using the Widevine scheme.
*
* @param callback Performs key and provisioning requests.
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
* to {@link MediaDrm#getKeyRequest(byte[], byte[], String, int, HashMap)}. May be null.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @throws UnsupportedDrmException If the specified DRM scheme is not supported.
*/
public static StreamingDrmSessionManager newWidevineInstance(MediaDrmCallback callback,
public StreamingDrmSessionManager(MediaDrmCallback callback,
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
EventListener eventListener) throws UnsupportedDrmException {
return new StreamingDrmSessionManager(C.WIDEVINE_UUID, callback, optionalKeyRequestParameters,
eventHandler, eventListener);
}
/**
* Instantiates a new instance using the PlayReady scheme.
* <p>
* Note that PlayReady is unsupported by most Android devices, with the exception of Android TV
* devices, which do provide support.
*
* @param callback Performs key and provisioning requests.
* @param customData Optional custom data to include in requests generated by the instance.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @throws UnsupportedDrmException If the specified DRM scheme is not supported.
*/
public static StreamingDrmSessionManager newPlayReadyInstance(MediaDrmCallback callback,
String customData, Handler eventHandler, EventListener eventListener)
throws UnsupportedDrmException {
HashMap<String, String> optionalKeyRequestParameters;
if (!TextUtils.isEmpty(customData)) {
optionalKeyRequestParameters = new HashMap<>();
optionalKeyRequestParameters.put(PLAYREADY_CUSTOM_DATA_KEY, customData);
} else {
optionalKeyRequestParameters = null;
}
return new StreamingDrmSessionManager(C.PLAYREADY_UUID, callback, optionalKeyRequestParameters,
eventHandler, eventListener);
}
/**
* @param uuid The UUID of the drm scheme.
* @param callback Performs key and provisioning requests.
* @param optionalKeyRequestParameters An optional map of parameters to pass as the last argument
* to {@link MediaDrm#getKeyRequest(byte[], byte[], String, int, HashMap)}. May be null.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @throws UnsupportedDrmException If the specified DRM scheme is not supported.
*/
public StreamingDrmSessionManager(UUID uuid, MediaDrmCallback callback,
HashMap<String, String> optionalKeyRequestParameters, Handler eventHandler,
EventListener eventListener) throws UnsupportedDrmException {
this.uuid = uuid;
EventListener eventListener) {
this.callback = callback;
this.optionalKeyRequestParameters = optionalKeyRequestParameters;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
uuid = callback.getUuid();
MediaDrm mediaDrm = null;
try {
mediaDrm = new MediaDrm(uuid);
mediaDrm.setOnEventListener(new MediaDrmEventListener());
state = STATE_CLOSED;
} catch (UnsupportedSchemeException e) {
throw new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME, e);
lastException = new UnsupportedDrmException(
UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME, e);
state = STATE_ERROR;
} catch (Exception e) {
throw new UnsupportedDrmException(UnsupportedDrmException.REASON_INSTANTIATION_ERROR, e);
lastException = new UnsupportedDrmException(
UnsupportedDrmException.REASON_INSTANTIATION_ERROR, e);
state = STATE_ERROR;
} finally {
this.mediaDrm = mediaDrm;
}
mediaDrm.setOnEventListener(new MediaDrmEventListener());
state = STATE_CLOSED;
}
@Override