From 4515a0c3f24706a43b3247b558b14d98f2b0fce2 Mon Sep 17 00:00:00 2001 From: michaelkatz Date: Mon, 23 Oct 2023 03:47:26 -0700 Subject: [PATCH] Fallback to legacy sizerate check for H264 if CDD PerfPoint check fails Some devices supporting Performance Points for decoder coverage are missing coverage over the CDD requirements for H264. For these cases ExoPlayer should fall back to legacy resolution and frame rate support checks. If there is an H264 stream evaluated as a `PerformancePointCoverageResult` of `COVERAGE_RESULT_NO`, then ExoPlayer checks for coverage of the [720p CDD requirement](https://source.android.com/docs/compatibility/10/android-10-cdd#5_3_4_h_264). Issue: google/ExoPlayer#10898 Issue: androidx/media#693 PiperOrigin-RevId: 575768836 --- RELEASENOTES.md | 4 ++ .../exoplayer/mediacodec/MediaCodecInfo.java | 70 ++++++++++++------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 045da5e0635..8a1b9da417f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -26,6 +26,10 @@ `IllegalArgumentException` ([#677](https://github.com/androidx/media/issues/677)). * Video: + * Add workaround for a device issue on Galaxy Tab S7 FE, Chromecast with + Google TV, and Lenovo M10 FHD Plus that causes 60fps AVC streams to be + marked as unsupported + ([#693](https://github.com/androidx/media/issues/693)). * Text: * Remove `ExoplayerCuesDecoder`. Text tracks with `sampleMimeType = application/x-media3-cues` are now directly handled by `TextRenderer` diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java index 9591aea2db5..ee2c1de3cd1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java @@ -522,14 +522,16 @@ public boolean isVideoSizeAndRateSupportedV21(int width, int height, double fram if (Util.SDK_INT >= 29) { @PerformancePointCoverageResult int evaluation = - Api29.areResolutionAndFrameRateCovered(videoCapabilities, width, height, frameRate); + Api29.areResolutionAndFrameRateCovered( + videoCapabilities, mimeType, width, height, frameRate); if (evaluation == COVERAGE_RESULT_YES) { return true; } else if (evaluation == COVERAGE_RESULT_NO) { logNoSupport("sizeAndRate.cover, " + width + "x" + height + "@" + frameRate); return false; } - // COVERAGE_RESULT_NO_EMPTY_LIST falls through to API 21+ code below + // If COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED then logic falls through + // to API 21+ code below. } if (!areSizeAndRateSupportedV21(videoCapabilities, width, height, frameRate)) { @@ -876,47 +878,44 @@ private static boolean needsProfileExcludedWorkaround(String mimeType, int profi && ("sailfish".equals(Util.DEVICE) || "marlin".equals(Util.DEVICE)); } - /** Whether the device is known to have wrong {@link PerformancePoint} declarations. */ - private static boolean needsIgnorePerformancePointsWorkaround() { - // See https://github.com/google/ExoPlayer/issues/10898 and [internal ref: b/267324685]. - return /* Chromecast with Google TV */ Util.DEVICE.equals("sabrina") - || Util.DEVICE.equals("boreal") - /* Lenovo Tablet M10 FHD Plus */ - || Util.MODEL.startsWith("Lenovo TB-X605") - || Util.MODEL.startsWith("Lenovo TB-X606") - || Util.MODEL.startsWith("Lenovo TB-X616"); - } - /** Possible outcomes of evaluating PerformancePoint coverage */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(TYPE_USE) - @IntDef({COVERAGE_RESULT_YES, COVERAGE_RESULT_NO, COVERAGE_RESULT_NO_EMPTY_LIST}) + @IntDef({ + COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED, + COVERAGE_RESULT_NO, + COVERAGE_RESULT_YES + }) private @interface PerformancePointCoverageResult {} - /** The decoder has a PerformancePoint that covers the resolution and frame rate */ - private static final int COVERAGE_RESULT_YES = 2; + /** + * The VideoCapabilities does not contain any PerformancePoints or its PerformancePoints do not + * cover CDD requirements. + */ + private static final int COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED = 0; /** - * The decoder has at least one PerformancePoint, but none of them cover the resolution and frame - * rate + * The decoder has at least one PerformancePoint, but none cover the resolution and frame rate. */ private static final int COVERAGE_RESULT_NO = 1; - /** The VideoCapabilities does not contain any PerformancePoints */ - private static final int COVERAGE_RESULT_NO_EMPTY_LIST = 0; + /** The decoder has a PerformancePoint that covers the resolution and frame rate. */ + private static final int COVERAGE_RESULT_YES = 2; @RequiresApi(29) private static final class Api29 { @DoNotInline public static @PerformancePointCoverageResult int areResolutionAndFrameRateCovered( - VideoCapabilities videoCapabilities, int width, int height, double frameRate) { + VideoCapabilities videoCapabilities, + String mimeType, + int width, + int height, + double frameRate) { List performancePointList = videoCapabilities.getSupportedPerformancePoints(); - if (performancePointList == null - || performancePointList.isEmpty() - || needsIgnorePerformancePointsWorkaround()) { - return COVERAGE_RESULT_NO_EMPTY_LIST; + if (performancePointList == null || performancePointList.isEmpty()) { + return COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED; } // Round frame rate down to to avoid situations where a range check in @@ -925,6 +924,27 @@ private static final class Api29 { PerformancePoint targetPerformancePoint = new PerformancePoint(width, height, (int) frameRate); + @PerformancePointCoverageResult + int performancePointCoverageResult = + evaluatePerformancePointCoverage(performancePointList, targetPerformancePoint); + + if (performancePointCoverageResult == COVERAGE_RESULT_NO + && mimeType.equals(MimeTypes.VIDEO_H264)) { + if (evaluatePerformancePointCoverage( + performancePointList, + new PerformancePoint(/* width= */ 1280, /* height= */ 720, /* frameRate= */ 60)) + != COVERAGE_RESULT_YES) { + // See https://github.com/google/ExoPlayer/issues/10898, + // https://github.com/androidx/media/issues/693 and [internal ref: b/267324685]. + return COVERAGE_RESULT_NO_PERFORMANCE_POINTS_UNSUPPORTED; + } + } + + return performancePointCoverageResult; + } + + private static @PerformancePointCoverageResult int evaluatePerformancePointCoverage( + List performancePointList, PerformancePoint targetPerformancePoint) { for (int i = 0; i < performancePointList.size(); i++) { if (performancePointList.get(i).covers(targetPerformancePoint)) { return COVERAGE_RESULT_YES;