@TargetApi(18) @SuppressWarnings("ResourceType") private static boolean isL1WidevineAvailable(String videoMimeType) { try { // Force L3 if secure decoder is not available. if (MediaCodecUtil.getDecoderInfo(videoMimeType, true) == null) { return false; } MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID); String securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY); mediaDrm.release(); return WIDEVINE_SECURITY_LEVEL_1.equals(securityProperty); } catch (DecoderQueryException | UnsupportedSchemeException e) { throw new IllegalStateException(e); } }
@SuppressWarnings("ResourceType") public static boolean isL1WidevineAvailable(String mimeType) { if (Util.SDK_INT >= 18) { try { // Force L3 if secure decoder is not available. if (MediaCodecUtil.getDecoderInfo(mimeType, true) == null) { return false; } MediaDrm mediaDrm = MediaDrmBuilder.build(); String securityProperty = mediaDrm.getPropertyString(SECURITY_LEVEL_PROPERTY); mediaDrm.release(); return WIDEVINE_SECURITY_LEVEL_1.equals(securityProperty); } catch (MediaCodecUtil.DecoderQueryException e) { throw new IllegalStateException(e); } } return false; }
@Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { errorString = getResources().getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { errorString = getResources().getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { errorString = getResources().getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { errorString = getResources().getString(R.string.error_instantiating_decoder, decoderInitializationException.decoderName); } } } if (errorString != null) { eventEmitter.error(errorString, e); } playerNeedsSource = true; }
private static boolean shouldSkipAdaptiveTest(String mimeType) throws DecoderQueryException { MediaCodecInfo decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, false); assertNotNull(decoderInfo); if (decoderInfo.adaptive) { return false; } assertTrue(Util.SDK_INT < 21); return true; }
/** * Called when an error occurs. The playback state will transition to {@link ExoPlayer#STATE_IDLE} * immediately after this method is called. The player instance can still be used, and * {@link #release()} must still be called on the player should it no longer be required. * * @param e The error. */ @Override public void onPlayerError(ExoPlaybackException e) { MediaError error = null; if (e == null) { error = new MediaError(MediaError.ERROR_UNKNOWN); } else { if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { error = new MediaError(MediaError.EXO_ERROR_QUERYING_DECODERS); } else if (decoderInitializationException.secureDecoderRequired) { error = new MediaError(MediaError.EXO_ERROR_NO_SECURE_DECODER); } else { error = new MediaError(MediaError.EXO_ERROR_NO_DECODER); } } else { error = new MediaError(MediaError.EXO_ERROR_INSTANTIATING_DECODER); } } } } if (error == null) { error = new MediaError(MediaError.ERROR_UNKNOWN); } notifyOnError(error); PlayerLog.d(TAG, "onPlayerError " + error.toString()); }
@Override protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { return FORMAT_UNSUPPORTED_TYPE; } boolean requiresSecureDecryption = false; DrmInitData drmInitData = format.drmInitData; if (drmInitData != null) { for (int i = 0; i < drmInitData.schemeDataCount; i++) { requiresSecureDecryption |= drmInitData.get(i).requiresSecureDecryption; } } MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, requiresSecureDecryption); if (decoderInfo == null) { return FORMAT_UNSUPPORTED_SUBTYPE; } boolean decoderCapable = decoderInfo.isCodecSupported(format.codecs); if (decoderCapable && format.width > 0 && format.height > 0) { if (Util.SDK_INT >= 21) { decoderCapable = decoderInfo.isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate); } else { decoderCapable = format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize(); if (!decoderCapable) { Log.d(TAG, "FalseCheck [legacyFrameSize, " + format.width + "x" + format.height + "] [" + Util.DEVICE_DEBUG_INFO + "]"); } } } int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS; int tunnelingSupport = decoderInfo.tunneling ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED; int formatSupport = decoderCapable ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES; return adaptiveSupport | tunnelingSupport | formatSupport; }
/** * Returns a maximum video size to use when configuring a codec for {@code format} in a way * that will allow possible adaptation to other compatible formats that are expected to have the * same aspect ratio, but whose sizes are unknown. * * @param codecInfo Information about the {@link MediaCodec} being configured. * @param format The format for which the codec is being configured. * @return The maximum video size to use, or null if the size of {@code format} should be used. * @throws DecoderQueryException If an error occurs querying {@code codecInfo}. */ private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) throws DecoderQueryException { boolean isVerticalVideo = format.height > format.width; int formatLongEdgePx = isVerticalVideo ? format.height : format.width; int formatShortEdgePx = isVerticalVideo ? format.width : format.height; float aspectRatio = (float) formatShortEdgePx / formatLongEdgePx; for (int longEdgePx : STANDARD_LONG_EDGE_VIDEO_PX) { int shortEdgePx = (int) (longEdgePx * aspectRatio); if (longEdgePx <= formatLongEdgePx || shortEdgePx <= formatShortEdgePx) { // Don't return a size not larger than the format for which the codec is being configured. return null; } else if (Util.SDK_INT >= 21) { Point alignedSize = codecInfo.alignVideoSizeV21(isVerticalVideo ? shortEdgePx : longEdgePx, isVerticalVideo ? longEdgePx : shortEdgePx); float frameRate = format.frameRate; if (codecInfo.isVideoSizeAndRateSupportedV21(alignedSize.x, alignedSize.y, frameRate)) { return alignedSize; } } else { // Conservatively assume the codec requires 16px width and height alignment. longEdgePx = Util.ceilDivide(longEdgePx, 16) * 16; shortEdgePx = Util.ceilDivide(shortEdgePx, 16) * 16; if (longEdgePx * shortEdgePx <= MediaCodecUtil.maxH264DecodableFrameSize()) { return new Point(isVerticalVideo ? shortEdgePx : longEdgePx, isVerticalVideo ? longEdgePx : shortEdgePx); } } } return null; }
@Override public void onPlayerError(ExoPlaybackException e) { String errorString = null; if (e.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = e.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) { errorString = context.getString(R.string.error_querying_decoders); } else if (decoderInitializationException.secureDecoderRequired) { errorString = context.getString(R.string.error_no_secure_decoder, decoderInitializationException.mimeType); } else { errorString = context.getString(R.string.error_no_decoder, decoderInitializationException.mimeType); } } else { errorString = context.getString(R.string.error_instantiating_decoder, decoderInitializationException.decoderName); } } } if (errorString != null) { showToast(errorString); } if (isBehindLiveWindow(e)) { clearResumePosition(); initializePlayer(); } else { updateResumePosition(); } }
@Test public void whenContentIsSecure_thenRequiresSecureDecoderIsFalse() throws MediaCodecUtil.DecoderQueryException { SecurityDowngradingCodecSelector securityDowngradingCodecSelector = new SecurityDowngradingCodecSelector(internalMediaCodecUtil); securityDowngradingCodecSelector.getDecoderInfo(ANY_MIME_TYPE, CONTENT_SECURE); ArgumentCaptor<Boolean> argumentCaptor = ArgumentCaptor.forClass(Boolean.class); verify(internalMediaCodecUtil).getDecoderInfo(eq(ANY_MIME_TYPE), argumentCaptor.capture()); assertThat(argumentCaptor.getValue()).isFalse(); }
@Test public void whenContentIsInsecure_thenRequiresSecureDecoderIsFalse() throws MediaCodecUtil.DecoderQueryException { SecurityDowngradingCodecSelector securityDowngradingCodecSelector = new SecurityDowngradingCodecSelector(internalMediaCodecUtil); securityDowngradingCodecSelector.getDecoderInfo(ANY_MIME_TYPE, CONTENT_INSECURE); ArgumentCaptor<Boolean> argumentCaptor = ArgumentCaptor.forClass(Boolean.class); verify(internalMediaCodecUtil).getDecoderInfo(eq(ANY_MIME_TYPE), argumentCaptor.capture()); assertThat(argumentCaptor.getValue()).isFalse(); }
@Test public void whenGettingPassthroughDecoderInfo_thenDelegates() throws MediaCodecUtil.DecoderQueryException { SecurityDowngradingCodecSelector securityDowngradingCodecSelector = new SecurityDowngradingCodecSelector(internalMediaCodecUtil); securityDowngradingCodecSelector.getPassthroughDecoderInfo(); verify(internalMediaCodecUtil).getPassthroughDecoderInfo(); }
@Override protected int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { return FORMAT_UNSUPPORTED_TYPE; } boolean requiresSecureDecryption = false; DrmInitData drmInitData = format.drmInitData; if (drmInitData != null) { for (int i = 0; i < drmInitData.schemeDataCount; i++) { requiresSecureDecryption |= drmInitData.get(i).requiresSecureDecryption; } } MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, requiresSecureDecryption); if (decoderInfo == null) { return FORMAT_UNSUPPORTED_SUBTYPE; } boolean decoderCapable = decoderInfo.isCodecSupported(format.codecs); if (decoderCapable && format.width > 0 && format.height > 0) { if (Util.SDK_INT >= 21) { if (format.frameRate > 0) { decoderCapable = decoderInfo.isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate); } else { decoderCapable = decoderInfo.isVideoSizeSupportedV21(format.width, format.height); } } else { decoderCapable = format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize(); } } int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS; int formatSupport = decoderCapable ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES; return adaptiveSupport | formatSupport; }
public void testDecoderInfoH264() throws DecoderQueryException { if (Util.SDK_INT < 16) { // Pass. return; } MediaCodecInfo decoderInfo = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264, false); assertNotNull(decoderInfo); assertTrue(Util.SDK_INT < 21 || decoderInfo.adaptive); }
public void testDecoderInfoH265V24() throws DecoderQueryException { if (Util.SDK_INT < 24) { // Pass. return; } assertTrue(MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H265, false).adaptive); }
public void testDecoderInfoVP9V24() throws DecoderQueryException { if (Util.SDK_INT < 24) { // Pass. return; } assertTrue(MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_VP9, false).adaptive); }
@Override public MediaCodecInfo getDecoderInfo(String mimeType, boolean contentRequiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException { return internalMediaCodecUtil.getDecoderInfo(mimeType, USE_INSECURE_DECODER); }
@Override public MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.DecoderQueryException { return internalMediaCodecUtil.getPassthroughDecoderInfo(); }
MediaCodecInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException { return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder); }
MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.DecoderQueryException { return MediaCodecUtil.getPassthroughDecoderInfo(); }
private static boolean shouldSkipAdaptiveTest(String mimeType) throws DecoderQueryException { MediaCodecInfo decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, false); return decoderInfo == null || !decoderInfo.adaptive; }
@Override protected int supportsFormat(MediaCodecSelector mediaCodecSelector, DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, Format format) throws DecoderQueryException { String mimeType = format.sampleMimeType; if (!MimeTypes.isVideo(mimeType)) { return FORMAT_UNSUPPORTED_TYPE; } boolean requiresSecureDecryption = false; DrmInitData drmInitData = format.drmInitData; if (drmInitData != null) { for (int i = 0; i < drmInitData.schemeDataCount; i++) { requiresSecureDecryption |= drmInitData.get(i).requiresSecureDecryption; } } MediaCodecInfo decoderInfo = mediaCodecSelector.getDecoderInfo(mimeType, requiresSecureDecryption); if (decoderInfo == null) { return requiresSecureDecryption && mediaCodecSelector.getDecoderInfo(mimeType, false) != null ? FORMAT_UNSUPPORTED_DRM : FORMAT_UNSUPPORTED_SUBTYPE; } if (!supportsFormatDrm(drmSessionManager, drmInitData)) { return FORMAT_UNSUPPORTED_DRM; } boolean decoderCapable = decoderInfo.isCodecSupported(format.codecs); if (decoderCapable && format.width > 0 && format.height > 0) { if (Util.SDK_INT >= 21) { decoderCapable = decoderInfo.isVideoSizeAndRateSupportedV21(format.width, format.height, format.frameRate); } else { decoderCapable = format.width * format.height <= MediaCodecUtil.maxH264DecodableFrameSize(); if (!decoderCapable) { Log.d(TAG, "FalseCheck [legacyFrameSize, " + format.width + "x" + format.height + "] [" + Util.DEVICE_DEBUG_INFO + "]"); } } } int adaptiveSupport = decoderInfo.adaptive ? ADAPTIVE_SEAMLESS : ADAPTIVE_NOT_SEAMLESS; int tunnelingSupport = decoderInfo.tunneling ? TUNNELING_SUPPORTED : TUNNELING_NOT_SUPPORTED; int formatSupport = decoderCapable ? FORMAT_HANDLED : FORMAT_EXCEEDS_CAPABILITIES; return adaptiveSupport | tunnelingSupport | formatSupport; }
@Override public void onPlayerError(ExoPlaybackException error) { String errorString = null; if (error.type == ExoPlaybackException.TYPE_RENDERER) { Exception cause = error.getRendererException(); if (cause instanceof MediaCodecRenderer.DecoderInitializationException) { // Special case for decoder initialization failures. MediaCodecRenderer.DecoderInitializationException decoderInitializationException = (MediaCodecRenderer.DecoderInitializationException) cause; if (decoderInitializationException.decoderName == null) { if (decoderInitializationException.getCause() instanceof MediaCodecUtil.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) { Log.e(TAG, errorString); } if (isBehindLiveWindow(error)) { clearResumePosition(); preparePlayer(); } else { Log.e("VIDEO FAILED","VIDEO FAILED LOADING NEW ONE."); updateResumePosition(); onVideoLoadFailed(); } /* Log.e(TAG, "onError"); onVideoLoadFailed(); playerNeedsPrepare = true;*/ }