Add additional Widevine samples + improve errors.

* Add additional Widevine samples.
* Improve error messaging in demo app around decoders.
* Display toasts for playback errors related to missing insecure
  decoders, missing secure decoders, decoder instantiation failure
  and decoder query failure.
* Remove checks from SampleChooserActivity, since the above largely
  covers off this problem.
This commit is contained in:
Oliver Woodman 2015-11-27 16:38:30 +00:00
parent c2df814b58
commit a6bfe02d24
5 changed files with 173 additions and 50 deletions

View File

@ -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();

View File

@ -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"));

View File

@ -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),
};

View File

@ -37,11 +37,19 @@
<string name="off">[off]</string>
<string name="drm_error_not_supported">Protected content not supported on API levels below 18</string>
<string name="error_drm_not_supported">Protected content not supported on API levels below 18</string>
<string name="drm_error_unsupported_scheme">This device does not support the required DRM scheme</string>
<string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
<string name="drm_error_unknown">An unknown DRM error occurred</string>
<string name="error_drm_unknown">An unknown DRM error occurred</string>
<string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_no_secure_decoder">This device does not provide a secure decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
<string name="error_querying_decoders">Unable to query device decoders</string>
<string name="error_instantiating_decoder">Unable to instantiate decoder <xliff:g id="decoder_name">%1$s</xliff:g></string>
<string name="storage_permission_denied">Permission to access storage was denied</string>

View File

@ -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;