Skip to content

Commit

Permalink
Fallback to legacy sizerate check for H264 if CDD PerfPoint check fails
Browse files Browse the repository at this point in the history
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: #693
PiperOrigin-RevId: 575768836
  • Loading branch information
microkatz authored and Copybara-Service committed Oct 23, 2023
1 parent a79d44e commit 4515a0c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 25 deletions.
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<PerformancePoint> 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
Expand All @@ -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<PerformancePoint> performancePointList, PerformancePoint targetPerformancePoint) {
for (int i = 0; i < performancePointList.size(); i++) {
if (performancePointList.get(i).covers(targetPerformancePoint)) {
return COVERAGE_RESULT_YES;
Expand Down

1 comment on commit 4515a0c

@JonWatson
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixed resolution on many other devices for us, thank you

Please sign in to comment.