diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java b/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java index a19e7b9e2e..a41a76a750 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/EventLogger.java @@ -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); } diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 27ec5bb6d1..575b0e7d7e 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -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) { diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java b/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java index 257b0cac2e..139279e890 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/SmoothStreamingTestMediaDrmCallback.java @@ -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()); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java b/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java index 4d9fcf22b8..faf2cf8846 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/WidevineTestMediaDrmCallback.java @@ -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()); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java index b1fa7d5763..7616e9208e 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/DemoPlayer.java @@ -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 *

* 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 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 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); diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java index 833fbd7e52..75a56fbf1e 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java @@ -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); diff --git a/library/src/main/java/com/google/android/exoplayer/drm/MediaDrmCallback.java b/library/src/main/java/com/google/android/exoplayer/drm/MediaDrmCallback.java index 30757e75b4..0ffa2ac92c 100644 --- a/library/src/main/java/com/google/android/exoplayer/drm/MediaDrmCallback.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/MediaDrmCallback.java @@ -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. * diff --git a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java index 044e2a95f4..10941825cf 100644 --- a/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java +++ b/library/src/main/java/com/google/android/exoplayer/drm/StreamingDrmSessionManager.java @@ -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 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. - *

- * 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 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 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