下面列出了android.media.UnsupportedSchemeException#com.google.android.exoplayer2.mediacodec.MediaCodecUtil 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
@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);
}
}
@Override
protected List<MediaCodecInfo> getDecoderInfos(
MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
throws DecoderQueryException {
@Nullable String mimeType = format.sampleMimeType;
if (mimeType == null) {
return Collections.emptyList();
}
if (allowPassthrough(format.channelCount, mimeType)) {
@Nullable
MediaCodecInfo passthroughDecoderInfo = mediaCodecSelector.getPassthroughDecoderInfo();
if (passthroughDecoderInfo != null) {
return Collections.singletonList(passthroughDecoderInfo);
}
}
List<MediaCodecInfo> decoderInfos =
mediaCodecSelector.getDecoderInfos(
mimeType, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false);
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
if (MimeTypes.AUDIO_E_AC3_JOC.equals(mimeType)) {
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
List<MediaCodecInfo> decoderInfosWithEac3 = new ArrayList<>(decoderInfos);
decoderInfosWithEac3.addAll(
mediaCodecSelector.getDecoderInfos(
MimeTypes.AUDIO_E_AC3, requiresSecureDecoder, /* requiresTunnelingDecoder= */ false));
decoderInfos = decoderInfosWithEac3;
}
return Collections.unmodifiableList(decoderInfos);
}
private static List<MediaCodecInfo> getDecoderInfos(
MediaCodecSelector mediaCodecSelector,
Format format,
boolean requiresSecureDecoder,
boolean requiresTunnelingDecoder)
throws DecoderQueryException {
@Nullable String mimeType = format.sampleMimeType;
if (mimeType == null) {
return Collections.emptyList();
}
List<MediaCodecInfo> decoderInfos =
mediaCodecSelector.getDecoderInfos(
mimeType, requiresSecureDecoder, requiresTunnelingDecoder);
decoderInfos = MediaCodecUtil.getDecoderInfosSortedByFormatSupport(decoderInfos, format);
if (MimeTypes.VIDEO_DOLBY_VISION.equals(mimeType)) {
// Fall back to H.264/AVC or H.265/HEVC for the relevant DV profiles.
@Nullable
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
if (codecProfileAndLevel != null) {
int profile = codecProfileAndLevel.first;
if (profile == CodecProfileLevel.DolbyVisionProfileDvheDtr
|| profile == CodecProfileLevel.DolbyVisionProfileDvheSt) {
decoderInfos.addAll(
mediaCodecSelector.getDecoderInfos(
MimeTypes.VIDEO_H265, requiresSecureDecoder, requiresTunnelingDecoder));
} else if (profile == CodecProfileLevel.DolbyVisionProfileDvavSe) {
decoderInfos.addAll(
mediaCodecSelector.getDecoderInfos(
MimeTypes.VIDEO_H264, requiresSecureDecoder, requiresTunnelingDecoder));
}
}
}
return Collections.unmodifiableList(decoderInfos);
}
/**
* 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 {@link 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.
*/
private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) {
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 {
try {
// 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);
}
} catch (DecoderQueryException e) {
// We tried our best. Give up!
return null;
}
}
}
return null;
}
/**
* 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;
}
/**
* 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;
}
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;
}
@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.getDecoderInfos(ANY_MIME_TYPE, CONTENT_SECURE);
ArgumentCaptor<Boolean> argumentCaptor = ArgumentCaptor.forClass(Boolean.class);
verify(internalMediaCodecUtil).getDecoderInfos(eq(ANY_MIME_TYPE), argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).isFalse();
}
@Test
public void whenContentIsInsecure_thenRequiresSecureDecoderIsFalse() throws MediaCodecUtil.DecoderQueryException {
SecurityDowngradingCodecSelector securityDowngradingCodecSelector = new SecurityDowngradingCodecSelector(internalMediaCodecUtil);
securityDowngradingCodecSelector.getDecoderInfos(ANY_MIME_TYPE, CONTENT_INSECURE);
ArgumentCaptor<Boolean> argumentCaptor = ArgumentCaptor.forClass(Boolean.class);
verify(internalMediaCodecUtil).getDecoderInfos(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) {
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 = "ExoPlaybackException type : " + e.type;
Exception ex = e;
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.codecInfo.name == 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.codecInfo.name);
}
}
}
else if (e.type == ExoPlaybackException.TYPE_SOURCE) {
errorString = getResources().getString(R.string.unrecognized_media_format);
}
eventEmitter.error(errorString, ex);
playerNeedsSource = true;
if (isBehindLiveWindow(e)) {
clearResumePosition();
initializePlayer();
} else {
updateResumePosition();
}
}
/**
* 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.
*/
private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) {
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 {
try {
// 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);
}
} catch (DecoderQueryException e) {
// We tried our best. Give up!
return null;
}
}
}
return null;
}
/**
* 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.
*/
private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) {
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 {
try {
// 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);
}
} catch (DecoderQueryException e) {
// We tried our best. Give up!
return null;
}
}
}
return null;
}
/**
* Returns the framework {@link MediaFormat} that should be used to configure the decoder.
*
* @param format The {@link Format} of media.
* @param codecMimeType The MIME type handled by the codec.
* @param codecMaxValues Codec max values that should be used when configuring the decoder.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @param deviceNeedsNoPostProcessWorkaround Whether the device is known to do post processing by
* default that isn't compatible with ExoPlayer.
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
* @return The framework {@link MediaFormat} that should be used to configure the decoder.
*/
@SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat(
Format format,
String codecMimeType,
CodecMaxValues codecMaxValues,
float codecOperatingRate,
boolean deviceNeedsNoPostProcessWorkaround,
int tunnelingAudioSessionId) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, format.width);
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, format.height);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
// Set format parameters that may be unset.
MediaFormatUtil.maybeSetFloat(mediaFormat, MediaFormat.KEY_FRAME_RATE, format.frameRate);
MediaFormatUtil.maybeSetInteger(mediaFormat, MediaFormat.KEY_ROTATION, format.rotationDegrees);
MediaFormatUtil.maybeSetColorInfo(mediaFormat, format.colorInfo);
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
// Some phones require the profile to be set on the codec.
// See https://github.com/google/ExoPlayer/pull/5438.
Pair<Integer, Integer> codecProfileAndLevel = MediaCodecUtil.getCodecProfileAndLevel(format);
if (codecProfileAndLevel != null) {
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_PROFILE, codecProfileAndLevel.first);
}
}
// Set codec max values.
mediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width);
mediaFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, codecMaxValues.height);
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, codecMaxValues.inputSize);
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
}
}
if (deviceNeedsNoPostProcessWorkaround) {
mediaFormat.setInteger("no-post-process", 1);
mediaFormat.setInteger("auto-frc", 0);
}
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
configureTunnelingV21(mediaFormat, tunnelingAudioSessionId);
}
return mediaFormat;
}
@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;
}
}
List<MediaCodecInfo> decoderInfos =
mediaCodecSelector.getDecoderInfos(format, requiresSecureDecryption);
if (decoderInfos.isEmpty()) {
return requiresSecureDecryption
&& !mediaCodecSelector
.getDecoderInfos(format, /* requiresSecureDecoder= */ false)
.isEmpty()
? FORMAT_UNSUPPORTED_DRM
: FORMAT_UNSUPPORTED_SUBTYPE;
}
if (!supportsFormatDrm(drmSessionManager, drmInitData)) {
return FORMAT_UNSUPPORTED_DRM;
}
// Check capabilities for the first decoder in the list, which takes priority.
MediaCodecInfo decoderInfo = decoderInfos.get(0);
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
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;
}
}
List<MediaCodecInfo> decoderInfos =
mediaCodecSelector.getDecoderInfos(format, requiresSecureDecryption);
if (decoderInfos.isEmpty()) {
return requiresSecureDecryption
&& !mediaCodecSelector
.getDecoderInfos(format, /* requiresSecureDecoder= */ false)
.isEmpty()
? FORMAT_UNSUPPORTED_DRM
: FORMAT_UNSUPPORTED_SUBTYPE;
}
if (!supportsFormatDrm(drmSessionManager, drmInitData)) {
return FORMAT_UNSUPPORTED_DRM;
}
// Check capabilities for the first decoder in the list, which takes priority.
MediaCodecInfo decoderInfo = decoderInfos.get(0);
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 e) {
String errorString = null;
switch (e.type) {
case ExoPlaybackException.TYPE_SOURCE:
//https://github.com/google/ExoPlayer/issues/2702
IOException ex = e.getSourceException();
String msg = ex.getMessage();
if (msg != null) {
Log.e("ExoPlayerHelper", msg);
errorString = msg;
}
break;
case ExoPlaybackException.TYPE_RENDERER:
Exception exception = e.getRendererException();
if (exception.getMessage() != null) {
Log.e("ExoPlayerHelper", exception.getMessage());
}
break;
case ExoPlaybackException.TYPE_UNEXPECTED:
RuntimeException runtimeException = e.getUnexpectedException();
Log.e("ExoPlayerHelper", runtimeException.getMessage() == null ? "Message is null" : runtimeException.getMessage());
if (runtimeException.getMessage() == null) {
runtimeException.printStackTrace();
}
errorString = runtimeException.getMessage();
break;
case ExoPlaybackException.TYPE_OUT_OF_MEMORY:
break;
case ExoPlaybackException.TYPE_REMOTE:
break;
}
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 = mContext.getString(R.string.error_querying_decoders);
} else if (decoderInitializationException.secureDecoderRequired) {
errorString = mContext.getString(R.string.error_no_secure_decoder,
decoderInitializationException.mimeType);
} else {
errorString = mContext.getString(R.string.error_no_decoder,
decoderInitializationException.mimeType);
}
} else {
errorString = mContext.getString(R.string.error_instantiating_decoder,
decoderInitializationException.decoderName);
}
}
}
if (errorString != null) {
Log.e("ExoPlayerHelper", "errorString: " + errorString);
}
if (isBehindLiveWindow(e)) {
createPlayer(true);
Log.e("ExoPlayerHelper", "isBehindLiveWindow is true");
}
if (mExoPlayerListener != null) {
mExoPlayerListener.onPlayerError(errorString);
}
}
@Override
public List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException {
return internalMediaCodecUtil.getDecoderInfos(mimeType, USE_INSECURE_DECODER);
}
@Override
public MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.DecoderQueryException {
return internalMediaCodecUtil.getPassthroughDecoderInfo();
}
List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException {
return MediaCodecUtil.getDecoderInfos(mimeType, requiresSecureDecoder);
}
MediaCodecInfo getPassthroughDecoderInfo() throws MediaCodecUtil.DecoderQueryException {
return MediaCodecUtil.getPassthroughDecoderInfo();
}
@SuppressWarnings({"PMD.StdCyclomaticComplexity", "PMD.CyclomaticComplexity", "PMD.ModifiedCyclomaticComplexity", "PMD.NPathComplexity"})
static NoPlayer.PlayerError map(Exception rendererException, String message) {
if (rendererException instanceof AudioSink.ConfigurationException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.AUDIO_SINK_CONFIGURATION_ERROR, message);
}
if (rendererException instanceof AudioSink.InitializationException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.AUDIO_SINK_INITIALISATION_ERROR, message);
}
if (rendererException instanceof AudioSink.WriteException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.AUDIO_SINK_WRITE_ERROR, message);
}
if (rendererException instanceof AudioProcessor.UnhandledFormatException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.AUDIO_UNHANDLED_FORMAT_ERROR, message);
}
if (rendererException instanceof AudioDecoderException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.AUDIO_DECODER_ERROR, message);
}
if (rendererException instanceof MediaCodecRenderer.DecoderInitializationException) {
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
(MediaCodecRenderer.DecoderInitializationException) rendererException;
String fullMessage = "decoder-name:" + decoderInitializationException.decoderName + ", "
+ "mimetype:" + decoderInitializationException.mimeType + ", "
+ "secureCodeRequired:" + decoderInitializationException.secureDecoderRequired + ", "
+ "diagnosticInfo:" + decoderInitializationException.diagnosticInfo + ", "
+ "exceptionMessage:" + message;
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.INITIALISATION_ERROR, fullMessage);
}
if (rendererException instanceof MediaCodecUtil.DecoderQueryException) {
return new NoPlayerError(PlayerErrorType.DEVICE_MEDIA_CAPABILITIES, DetailErrorType.UNKNOWN, message);
}
if (rendererException instanceof SubtitleDecoderException) {
return new NoPlayerError(PlayerErrorType.RENDERER_DECODER, DetailErrorType.DECODING_SUBTITLE_ERROR, message);
}
if (rendererException instanceof UnsupportedDrmException) {
return mapUnsupportedDrmException((UnsupportedDrmException) rendererException, message);
}
if (rendererException instanceof DefaultDrmSessionManager.MissingSchemeDataException) {
return new NoPlayerError(PlayerErrorType.DRM, DetailErrorType.CANNOT_ACQUIRE_DRM_SESSION_MISSING_SCHEME_FOR_REQUIRED_UUID_ERROR, message);
}
if (rendererException instanceof DrmSession.DrmSessionException) {
return new NoPlayerError(PlayerErrorType.DRM, DetailErrorType.DRM_SESSION_ERROR, message);
}
if (rendererException instanceof KeysExpiredException) {
return new NoPlayerError(PlayerErrorType.DRM, DetailErrorType.DRM_KEYS_EXPIRED_ERROR, message);
}
if (rendererException instanceof DecryptionException) {
return new NoPlayerError(PlayerErrorType.CONTENT_DECRYPTION, DetailErrorType.FAIL_DECRYPT_DATA_DUE_NON_PLATFORM_COMPONENT_ERROR, message);
}
if (rendererException instanceof MediaCodec.CryptoException) {
return mapCryptoException((MediaCodec.CryptoException) rendererException, message);
}
if (rendererException instanceof IllegalStateException) {
return new NoPlayerError(PlayerErrorType.DRM, DetailErrorType.MEDIA_REQUIRES_DRM_SESSION_MANAGER_ERROR, message);
}
return new NoPlayerError(PlayerErrorType.UNKNOWN, DetailErrorType.UNKNOWN, message);
}
@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;*/
}
/**
* Returns the framework {@link MediaFormat} that should be used to configure the decoder.
*
* @param format The format of media.
* @param codecMimeType The MIME type handled by the codec.
* @param codecMaxValues Codec max values that should be used when configuring the decoder.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @param deviceNeedsNoPostProcessWorkaround Whether the device is known to do post processing by
* default that isn't compatible with ExoPlayer.
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
* @return The framework {@link MediaFormat} that should be used to configure the decoder.
*/
@SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat(
Format format,
String codecMimeType,
CodecMaxValues codecMaxValues,
float codecOperatingRate,
boolean deviceNeedsNoPostProcessWorkaround,
int tunnelingAudioSessionId) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, format.width);
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, format.height);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
// Set format parameters that may be unset.
MediaFormatUtil.maybeSetFloat(mediaFormat, MediaFormat.KEY_FRAME_RATE, format.frameRate);
MediaFormatUtil.maybeSetInteger(mediaFormat, MediaFormat.KEY_ROTATION, format.rotationDegrees);
MediaFormatUtil.maybeSetColorInfo(mediaFormat, format.colorInfo);
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
// Some phones require the profile to be set on the codec.
// See https://github.com/google/ExoPlayer/pull/5438.
Pair<Integer, Integer> codecProfileAndLevel =
MediaCodecUtil.getCodecProfileAndLevel(format.codecs);
if (codecProfileAndLevel != null) {
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_PROFILE, codecProfileAndLevel.first);
}
}
// Set codec max values.
mediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width);
mediaFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, codecMaxValues.height);
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, codecMaxValues.inputSize);
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
}
}
if (deviceNeedsNoPostProcessWorkaround) {
mediaFormat.setInteger("no-post-process", 1);
mediaFormat.setInteger("auto-frc", 0);
}
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
configureTunnelingV21(mediaFormat, tunnelingAudioSessionId);
}
return mediaFormat;
}
/**
* Returns the framework {@link MediaFormat} that should be used to configure the decoder.
*
* @param format The format of media.
* @param codecMimeType The MIME type handled by the codec.
* @param codecMaxValues Codec max values that should be used when configuring the decoder.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* @param deviceNeedsNoPostProcessWorkaround Whether the device is known to do post processing by
* default that isn't compatible with ExoPlayer.
* @param tunnelingAudioSessionId The audio session id to use for tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} if tunneling should not be enabled.
* @return The framework {@link MediaFormat} that should be used to configure the decoder.
*/
@SuppressLint("InlinedApi")
protected MediaFormat getMediaFormat(
Format format,
String codecMimeType,
CodecMaxValues codecMaxValues,
float codecOperatingRate,
boolean deviceNeedsNoPostProcessWorkaround,
int tunnelingAudioSessionId) {
MediaFormat mediaFormat = new MediaFormat();
// Set format parameters that should always be set.
mediaFormat.setString(MediaFormat.KEY_MIME, codecMimeType);
mediaFormat.setInteger(MediaFormat.KEY_WIDTH, format.width);
mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, format.height);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
// Set format parameters that may be unset.
MediaFormatUtil.maybeSetFloat(mediaFormat, MediaFormat.KEY_FRAME_RATE, format.frameRate);
MediaFormatUtil.maybeSetInteger(mediaFormat, MediaFormat.KEY_ROTATION, format.rotationDegrees);
MediaFormatUtil.maybeSetColorInfo(mediaFormat, format.colorInfo);
if (MimeTypes.VIDEO_DOLBY_VISION.equals(format.sampleMimeType)) {
// Some phones require the profile to be set on the codec.
// See https://github.com/google/ExoPlayer/pull/5438.
Pair<Integer, Integer> codecProfileAndLevel =
MediaCodecUtil.getCodecProfileAndLevel(format.codecs);
if (codecProfileAndLevel != null) {
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_PROFILE, codecProfileAndLevel.first);
}
}
// Set codec max values.
mediaFormat.setInteger(MediaFormat.KEY_MAX_WIDTH, codecMaxValues.width);
mediaFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, codecMaxValues.height);
MediaFormatUtil.maybeSetInteger(
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, codecMaxValues.inputSize);
// Set codec configuration values.
if (Util.SDK_INT >= 23) {
mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, 0 /* realtime priority */);
if (codecOperatingRate != CODEC_OPERATING_RATE_UNSET) {
mediaFormat.setFloat(MediaFormat.KEY_OPERATING_RATE, codecOperatingRate);
}
}
if (deviceNeedsNoPostProcessWorkaround) {
mediaFormat.setInteger("no-post-process", 1);
mediaFormat.setInteger("auto-frc", 0);
}
if (tunnelingAudioSessionId != C.AUDIO_SESSION_ID_UNSET) {
configureTunnelingV21(mediaFormat, tunnelingAudioSessionId);
}
return mediaFormat;
}