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 f1681cfe3a..21f5677dca 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
@@ -16,7 +16,10 @@
package com.google.android.exoplayer.demo;
import com.google.android.exoplayer.AspectRatioFrameLayout;
+import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
+import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializationException;
+import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
@@ -99,9 +102,6 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private static final int MENU_GROUP_TRACKS = 1;
private static final int ID_OFFSET = 2;
- // For use when requesting permission.
- private static final String URI_FILE_SCHEME = "file";
-
private static final CookieManager defaultCookieManager;
static {
defaultCookieManager = new CookieManager();
@@ -394,13 +394,35 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
@Override
public void onError(Exception e) {
+ String errorString = null;
if (e instanceof UnsupportedDrmException) {
// Special case DRM failures.
UnsupportedDrmException unsupportedDrmException = (UnsupportedDrmException) e;
- int stringId = Util.SDK_INT < 18 ? R.string.drm_error_not_supported
+ errorString = getString(Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: unsupportedDrmException.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
- ? R.string.drm_error_unsupported_scheme : R.string.drm_error_unknown;
- Toast.makeText(getApplicationContext(), stringId, Toast.LENGTH_LONG).show();
+ ? 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);
+ } else {
+ errorString = getString(R.string.error_no_decoder,
+ decoderInitializationException.mimeType);
+ }
+ } else {
+ errorString = getString(R.string.error_instantiating_decoder,
+ decoderInitializationException.decoderName);
+ }
+ }
+ if (errorString != null) {
+ Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_LONG).show();
}
playerNeedsPrepare = true;
updateButtonVisibilities();
diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java
index 4acf3e50f1..a403c83018 100644
--- a/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java
+++ b/demo/src/main/java/com/google/android/exoplayer/demo/SampleChooserActivity.java
@@ -15,17 +15,13 @@
*/
package com.google.android.exoplayer.demo;
-import com.google.android.exoplayer.MediaCodecUtil;
-import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.demo.Samples.Sample;
-import com.google.android.exoplayer.util.MimeTypes;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -40,8 +36,6 @@ import android.widget.TextView;
*/
public class SampleChooserActivity extends Activity {
- private static final String TAG = "SampleChooserActivity";
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -52,27 +46,20 @@ public class SampleChooserActivity extends Activity {
sampleAdapter.add(new Header("YouTube DASH"));
sampleAdapter.addAll((Object[]) Samples.YOUTUBE_DASH_MP4);
- try {
- if (MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, false) != null) {
- sampleAdapter.addAll((Object[]) Samples.YOUTUBE_DASH_WEBM);
- }
- } catch (DecoderQueryException e) {
- Log.e(TAG, "Failed to query vp9 decoder", e);
- }
+ sampleAdapter.addAll((Object[]) Samples.YOUTUBE_DASH_WEBM);
sampleAdapter.add(new Header("Widevine DASH Policy Tests (GTS)"));
sampleAdapter.addAll((Object[]) Samples.WIDEVINE_GTS);
- sampleAdapter.add(new Header("Widevine DASH"));
- sampleAdapter.addAll((Object[]) Samples.WIDEVINE_DASH_MP4);
- try {
- if (MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, false) != null) {
- sampleAdapter.addAll((Object[]) Samples.WIDEVINE_VP9_WEBM_CLEAR);
- }
- if (MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, true) != null) {
- sampleAdapter.addAll((Object[]) Samples.WIDEVINE_VP9_WEBM_SECURE);
- }
- } catch (DecoderQueryException e) {
- Log.e(TAG, "Failed to query vp9 decoder", e);
- }
+ sampleAdapter.add(new Header("Widevine HDCP Capabilities Tests"));
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_HDCP);
+ sampleAdapter.add(new Header("Widevine DASH: MP4,H264"));
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_H264_MP4_CLEAR);
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_H264_MP4_SECURE);
+ sampleAdapter.add(new Header("Widevine DASH: WebM,VP9"));
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_VP9_WEBM_CLEAR);
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_VP9_WEBM_SECURE);
+ sampleAdapter.add(new Header("Widevine DASH: MP4,H265"));
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_H265_MP4_CLEAR);
+ sampleAdapter.addAll((Object[]) Samples.WIDEVINE_H265_MP4_SECURE);
sampleAdapter.add(new Header("SmoothStreaming"));
sampleAdapter.addAll((Object[]) Samples.SMOOTHSTREAMING);
sampleAdapter.add(new Header("HLS"));
diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java b/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
index badf140191..a78a77ffa1 100644
--- a/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
+++ b/demo/src/main/java/com/google/android/exoplayer/demo/Samples.java
@@ -80,7 +80,7 @@ import java.util.Locale;
};
private static final String WIDEVINE_GTS_MPD =
- "https://storage.googleapis.com/wvmedia/cenc/h264/tears.mpd";
+ "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd";
public static final Sample[] WIDEVINE_GTS = new Sample[] {
new Sample("WV: HDCP not specified", "d286538032258a1c", "widevine_test",
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
@@ -88,32 +88,122 @@ import java.util.Locale;
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
new Sample("WV: HDCP required", "e06c39f1151da3df", "widevine_test",
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
- new Sample("WV: Secure video path required", "0894c7c8719b28a0", "widevine_test",
+ new Sample("WV: Secure video path required (MP4,H264)", "0894c7c8719b28a0", "widevine_test",
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure video path required (WebM,VP9)", "0894c7c8719b28a0", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure video path required (MP4,H265)", "0894c7c8719b28a0", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
new Sample("WV: HDCP + secure video path required", "efd045b1eb61888a", "widevine_test",
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
new Sample("WV: 30s license duration (fails at ~30s)", "f9a34cab7b05881a", "widevine_test",
WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
};
- public static final Sample[] WIDEVINE_DASH_MP4 = new Sample[] {
- new Sample("WV: Clear (MP4,H264)",
- "https://storage.googleapis.com/wvmedia/cenc/clear/h264/tears.mpd",
+ public static final Sample[] WIDEVINE_HDCP = new Sample[] {
+ new Sample("WV: HDCP: None (not required)", "HDCP_None", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: HDCP: 1.0 required", "HDCP_V1", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: HDCP: 2.0 required", "HDCP_V2", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: HDCP: 2.1 required", "HDCP_V2_1", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: HDCP: 2.2 required", "HDCP_V2_2", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ new Sample("WV: HDCP: No digital output", "HDCP_NO_DIGTAL_OUTPUT", "widevine_test",
+ WIDEVINE_GTS_MPD, PlayerActivity.TYPE_DASH),
+ };
+
+ public static final Sample[] WIDEVINE_H264_MP4_CLEAR = new Sample[] {
+ new Sample("WV: Clear SD & HD (MP4,H264)",
+ "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd",
PlayerActivity.TYPE_DASH),
- new Sample("WV: Secure (MP4,H264)", "", "widevine_test",
- "https://storage.googleapis.com/wvmedia/cenc/h264/tears.mpd",
+ new Sample("WV: Clear SD (MP4,H264)",
+ "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear HD (MP4,H264)",
+ "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear UHD (MP4,H264)",
+ "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_uhd.mpd",
+ PlayerActivity.TYPE_DASH),
+ };
+
+ public static final Sample[] WIDEVINE_H264_MP4_SECURE = new Sample[] {
+ new Sample("WV: Secure SD & HD (MP4,H264)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure SD (MP4,H264)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure HD (MP4,H264)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure UHD (MP4,H264)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears_uhd.mpd",
PlayerActivity.TYPE_DASH),
};
public static final Sample[] WIDEVINE_VP9_WEBM_CLEAR = new Sample[] {
- new Sample("WV: Clear (WebM,VP9)",
- "https://storage.googleapis.com/wvmedia/clear/vp9/sintel-multicodec-4k/sintel-vp9.mpd",
+ new Sample("WV: Clear SD & HD (WebM,VP9)",
+ "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear SD (WebM,VP9)",
+ "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear HD (WebM,VP9)",
+ "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear UHD (WebM,VP9)",
+ "https://storage.googleapis.com/wvmedia/clear/vp9/tears/tears_uhd.mpd",
PlayerActivity.TYPE_DASH),
};
public static final Sample[] WIDEVINE_VP9_WEBM_SECURE = new Sample[] {
- new Sample("WV: Secure (WebM,VP9)", "01234567", "widevine_test",
- "https://storage.googleapis.com/wvmedia/cenc/vp9/sintel-multicodec-4k/sintel-vp9.mpd",
+ new Sample("WV: Secure SD & HD (WebM,VP9)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure SD (WebM,VP9)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure HD (WebM,VP9)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure UHD (WebM,VP9)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/vp9/tears/tears_uhd.mpd",
+ PlayerActivity.TYPE_DASH),
+ };
+
+ public static final Sample[] WIDEVINE_H265_MP4_CLEAR = new Sample[] {
+ new Sample("WV: Clear SD & HD (MP4,H265)",
+ "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear SD (MP4,H265)",
+ "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear HD (MP4,H265)",
+ "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Clear UHD (MP4,H265)",
+ "https://storage.googleapis.com/wvmedia/clear/hevc/tears/tears_uhd.mpd",
+ PlayerActivity.TYPE_DASH),
+ };
+
+ public static final Sample[] WIDEVINE_H265_MP4_SECURE = new Sample[] {
+ new Sample("WV: Secure SD & HD (MP4,H265)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure SD (MP4,H265)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_sd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure HD (MP4,H265)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_hd.mpd",
+ PlayerActivity.TYPE_DASH),
+ new Sample("WV: Secure UHD (MP4,H265)", "", "widevine_test",
+ "https://storage.googleapis.com/wvmedia/cenc/hevc/tears/tears_uhd.mpd",
PlayerActivity.TYPE_DASH),
};
diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml
index 2fbab91ef3..70d9f20bdb 100644
--- a/demo/src/main/res/values/strings.xml
+++ b/demo/src/main/res/values/strings.xml
@@ -37,11 +37,19 @@
[off]
- Protected content not supported on API levels below 18
+ Protected content not supported on API levels below 18
- This device does not support the required DRM scheme
+ This device does not support the required DRM scheme
- An unknown DRM error occurred
+ An unknown DRM error occurred
+
+ This device does not provide a decoder for %1$s
+
+ This device does not provide a secure decoder for %1$s
+
+ Unable to query device decoders
+
+ Unable to instantiate decoder %1$s
Permission to access storage was denied
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 77698d9f43..ff7f9027c6 100644
--- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
@@ -81,6 +81,16 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
private static final int NO_SUITABLE_DECODER_ERROR = CUSTOM_ERROR_CODE_BASE + 1;
private static final int DECODER_QUERY_ERROR = CUSTOM_ERROR_CODE_BASE + 2;
+ /**
+ * The mime type for which a decoder was being initialized.
+ */
+ public final String mimeType;
+
+ /**
+ * Whether it was required that the decoder support a secure output path.
+ */
+ public final boolean secureDecoderRequired;
+
/**
* The name of the decoder that failed to initialize. Null if no suitable decoder was found.
*/
@@ -91,15 +101,20 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
*/
public final String diagnosticInfo;
- public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause, int errorCode) {
+ public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause,
+ boolean secureDecoderRequired, int errorCode) {
super("Decoder init failed: [" + errorCode + "], " + mediaFormat, cause);
+ this.mimeType = mediaFormat.mimeType;
+ this.secureDecoderRequired = secureDecoderRequired;
this.decoderName = null;
this.diagnosticInfo = buildCustomDiagnosticInfo(errorCode);
}
public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause,
- String decoderName) {
+ boolean secureDecoderRequired, String decoderName) {
super("Decoder init failed: " + decoderName + ", " + mediaFormat, cause);
+ this.mimeType = mediaFormat.mimeType;
+ this.secureDecoderRequired = secureDecoderRequired;
this.decoderName = decoderName;
this.diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
}
@@ -313,12 +328,12 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
decoderInfo = getDecoderInfo(mimeType, requiresSecureDecoder);
} catch (DecoderQueryException e) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
- DecoderInitializationException.DECODER_QUERY_ERROR));
+ requiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR));
}
if (decoderInfo == null) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null,
- DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
+ requiresSecureDecoder, DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
}
String codecName = decoderInfo.name;
@@ -343,7 +358,8 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
inputBuffers = codec.getInputBuffers();
outputBuffers = codec.getOutputBuffers();
} catch (Exception e) {
- notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, codecName));
+ notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
+ requiresSecureDecoder, codecName));
}
codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ?
SystemClock.elapsedRealtime() : -1;