Commit 5955acac authored by Patrick Rohr's avatar Patrick Rohr Committed by Commit Bot

Fix Display Size Detection for Cast Shell on TV Devices

Adding support for casting to 4k Android TV devices.

	use_goma=true target_os="android" target_cpu="x86"' \
	&& ninja -j500 -C out/Debug media_base_junit_tests \
	&& out/Debug/bin/run_media_base_junit_tests

Test: gn gen out/Debug --args='is_chromecast=true is_debug=true \
Bug: 1064263
Change-Id: Ibd1fed93af30fd38a2ede189641aa5da0e5225ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2117733
Commit-Queue: Patrick Rohr <prohr@google.com>
Auto-Submit: Patrick Rohr <prohr@google.com>
Reviewed-by: default avatarSimeon Anfinrud <sanfin@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Cr-Commit-Position: refs/heads/master@{#759201}
parent c53a255b
......@@ -160,6 +160,7 @@ if (is_android) {
android_library("media_java") {
deps = [
":display_java",
":media_java_resources",
"//base:base_java",
"//base:jni_java",
......@@ -177,6 +178,7 @@ if (is_android) {
"java/src/org/chromium/media/BitrateAdjuster.java",
"java/src/org/chromium/media/CodecProfileLevelList.java",
"java/src/org/chromium/media/HdrMetadata.java",
"java/src/org/chromium/media/MaxAnticipatedResolutionEstimator.java",
"java/src/org/chromium/media/MediaCodecBridge.java",
"java/src/org/chromium/media/MediaCodecBridgeBuilder.java",
"java/src/org/chromium/media/MediaCodecEncoder.java",
......@@ -188,9 +190,17 @@ if (is_android) {
"java/src/org/chromium/media/MediaPlayerBridge.java",
"java/src/org/chromium/media/MediaPlayerListener.java",
"java/src/org/chromium/media/MediaServerCrashListener.java",
"java/src/org/chromium/media/ScreenResolutionUtil.java",
]
}
# TODO (b/146418831): Replace with androidx version
android_library("display_java") {
sources = [ "java/src/org/chromium/media/DisplayCompat.java" ]
deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
}
junit_binary("media_base_junit_tests") {
sources = [
"java/src/test/org/chromium/media/AudioTrackOutputStreamTest.java",
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.media;
import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Size;
import android.view.Display;
import androidx.annotation.Nullable;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.media.MediaCodecUtil.MimeTypes;
/**
* A utility class to make an estimate for the hints provided to MediaFormat as
* to the expected maximum resolution to prepare for.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MaxAnticipatedResolutionEstimator {
private Context mContext;
private DisplayManager mDisplayManager;
private static final String TAG = "EstimateResolution";
private static final int SCREEN_WIDTH_4K = 3840;
private static final int SCREEN_HEIGHT_4K = 2160;
/**
* Class to represent display resolution.
*/
public static class Resolution {
int mWidth;
int mHeight;
public Resolution(int width, int height) {
mWidth = width;
mHeight = height;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
}
private MaxAnticipatedResolutionEstimator() {}
public static Resolution getScreenResolution(MediaFormat format) {
Resolution resolution = getNativeResolution();
if (resolution == null) {
resolution.mWidth = format.getInteger(MediaFormat.KEY_WIDTH);
resolution.mHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
}
// Cap screen size at 1080p for non-4K codecs
if (!format.getString(MediaFormat.KEY_MIME).equals(MimeTypes.VIDEO_HEVC)
&& !format.getString(MediaFormat.KEY_MIME).equals(MimeTypes.VIDEO_VP9)) {
resolution.mWidth = Math.min(resolution.mWidth, 1920);
resolution.mHeight = Math.min(resolution.mHeight, 1080);
}
return resolution;
}
@Nullable
public static Resolution getNativeResolution() {
// Starting with P, DisplayCompat relies on having read access to
// vendor.display-size (except for devices that correctly implement
// DisplayMode#getPhysicalHeight / getPhysicalWidth
// (e.g. Nvidia Shield).
// Unfortunately, before Q, SoC vendors did not grant such access to
// priv_app in their SELinux policy files. This means that for P devices
// (except Nvidia Shield), we should continue to guess display size by
// looking at the installed codecs.
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P && !isNvidiaShield()
&& is4kVpxSupported()) {
Log.d(TAG, "Assuming 4K display capabilities because we can decode VP9 4K video.");
return new Resolution(SCREEN_WIDTH_4K, SCREEN_HEIGHT_4K);
}
// If we can't establish 4k support from the codecs, it's best to
// fall back on DisplayCompat.
Context context = ContextUtils.getApplicationContext();
DisplayManager displayManager =
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
DisplayCompat.ModeCompat[] supportedModes = DisplayCompat.getSupportedModes(
context, displayManager.getDisplay(Display.DEFAULT_DISPLAY));
// supportedModes always contain at least one native mode.
// All native modes are equal in resolution (but differ in refresh rates).
for (DisplayCompat.ModeCompat mode : supportedModes) {
if (mode.isNative()) {
return new Resolution(mode.getPhysicalWidth(), mode.getPhysicalHeight());
}
}
// Should never happen.
return null;
}
private static boolean isNvidiaShield() {
return "NVIDIA".equals(Build.MANUFACTURER) && Build.MODEL.startsWith("SHIELD");
}
private static boolean is4kVpxSupported() {
return ScreenResolutionUtil.isResolutionSupportedForType(
"video/x-vnd.on2.vp9", new Size(SCREEN_WIDTH_4K, SCREEN_HEIGHT_4K));
}
}
......@@ -7,6 +7,7 @@ package org.chromium.media;
import android.media.MediaFormat;
import android.os.Build;
import org.chromium.base.ContextUtils;
import org.chromium.media.MediaCodecUtil.MimeTypes;
import java.nio.ByteBuffer;
......@@ -63,14 +64,21 @@ class MediaFormatBuilder {
private static void addInputSizeInfoToFormat(
MediaFormat format, boolean allowAdaptivePlayback) {
if (allowAdaptivePlayback) {
// The max size is a hint to the codec, and causes it to allocate more memory up front.
// It still supports higher resolutions if they arrive. So, we try to ask only for the
// initial size.
// TODO(sanfin): The above statement that it supports higher resolutions if they arrive
// is false on some platforms. Provide a better hint.
format.setInteger(MediaFormat.KEY_MAX_WIDTH, format.getInteger(MediaFormat.KEY_WIDTH));
format.setInteger(
MediaFormat.KEY_MAX_HEIGHT, format.getInteger(MediaFormat.KEY_HEIGHT));
if (DisplayCompat.isTv(ContextUtils.getApplicationContext())) {
// For now, only set max width and height to native resolution on TVs.
// Some decoders on TVs interpret max width / height quite literally,
// and a crash can occur if these are exceeded.
MaxAnticipatedResolutionEstimator.Resolution resolution =
MaxAnticipatedResolutionEstimator.getScreenResolution(format);
format.setInteger(MediaFormat.KEY_MAX_WIDTH, resolution.getWidth());
format.setInteger(MediaFormat.KEY_MAX_HEIGHT, resolution.getHeight());
} else {
format.setInteger(
MediaFormat.KEY_MAX_WIDTH, format.getInteger(MediaFormat.KEY_WIDTH));
format.setInteger(
MediaFormat.KEY_MAX_HEIGHT, format.getInteger(MediaFormat.KEY_HEIGHT));
}
}
if (format.containsKey(android.media.MediaFormat.KEY_MAX_INPUT_SIZE)) {
// Already set. The source of the format may know better, so do nothing.
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.media;
import android.annotation.TargetApi;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.os.Build;
import android.util.Size;
/**
* This class is used as a means to guess the actual screen resolution that the
* device is capable of playing.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ScreenResolutionUtil {
public static boolean isResolutionSupportedForType(String mimeType, Size targetResolution) {
MediaCodecInfo[] codecInfos = new MediaCodecList(MediaCodecList.ALL_CODECS).getCodecInfos();
for (MediaCodecInfo codecInfo : codecInfos) {
try {
MediaCodecInfo.CodecCapabilities codecCapabilities =
codecInfo.getCapabilitiesForType(mimeType);
if (codecCapabilities == null) {
continue;
}
MediaCodecInfo.VideoCapabilities videoCapabilities =
codecCapabilities.getVideoCapabilities();
if (videoCapabilities == null) {
continue;
}
if (videoCapabilities.isSizeSupported(
targetResolution.getWidth(), targetResolution.getHeight())) {
return true;
}
} catch (IllegalArgumentException e) {
continue;
}
}
return false;
}
}
......@@ -11,10 +11,13 @@ import static org.junit.Assert.assertTrue;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.chromium.base.ContextUtils;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import java.nio.ByteBuffer;
......@@ -56,6 +59,11 @@ public class MediaFormatBuilderTest {
}
}
@Before
public void setUp() {
ContextUtils.initApplicationContextForTests(RuntimeEnvironment.application);
}
@Test
public void testCreateVideoDecoderWithNoCodecSpecificData() {
byte[][] csds = {};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment