blob: 37f0106e8c692d1acd275aa071a1dd56a2bbce1a [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.mediapc.cts;
import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
import static android.mediapc.cts.CodecDecoderTestBase.WIDEVINE_UUID;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
import android.media.MediaDrm;
import android.media.MediaFormat;
import android.media.UnsupportedSchemeException;
import android.mediapc.cts.common.Utils;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.Network;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assume;
import org.junit.Before;
public class MultiCodecPerfTestBase {
private static final String LOG_TAG = MultiCodecPerfTestBase.class.getSimpleName();
static final boolean[] boolStates = {true, false};
static final int REQUIRED_MIN_CONCURRENT_INSTANCES = 6;
static final int REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9 = 2;
static final int REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES = 2;
static ArrayList<String> mMimeList = new ArrayList<>();
static Map<String, String> mTestFiles = new HashMap<>();
static Map<String, String> m720pTestFiles = new HashMap<>();
static Map<String, String> m1080pTestFiles = new HashMap<>();
static Map<String, String> m1080pWidevineTestFiles = new HashMap<>();
static {
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4");
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4");
// Test VP9 and AV1 as well for Build.VERSION_CODES.S and beyond
if (Utils.getPerfClass() >= Build.VERSION_CODES.S) {
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP9);
mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AV1);
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm");
m720pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4");
}
m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
m1080pWidevineTestFiles
.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc_cenc.mp4");
m1080pWidevineTestFiles
.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1920x1080_4mbps_30fps_hevc_cenc.mp4");
// TODO(b/230682028)
// m1080pWidevineTestFiles
// .put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9_cenc.webm");
m1080pWidevineTestFiles
.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1_cenc.mp4");
}
String mMime;
String mTestFile;
final boolean mIsAsync;
@Before
public void isPerformanceClassCandidate() {
Utils.assumeDeviceMeetsPerformanceClassPreconditions();
}
public MultiCodecPerfTestBase(String mime, String testFile, boolean isAsync) {
mMime = mime;
mTestFile = testFile;
mIsAsync = isAsync;
}
// Returns the list of hardware codecs for given mime
public static ArrayList<String> getHardwareCodecsForMime(String mime, boolean isEncoder) {
return getHardwareCodecsForMime(mime, isEncoder, false);
}
public static ArrayList<String> getHardwareCodecsForMime(String mime, boolean isEncoder,
boolean allCodecs) {
// All the multi-instance tests are limited to codecs that support at least 1280x720 @ 30fps
// This will exclude hevc constant quality encoders that are limited to max resolution of
// 512x512
MediaFormat fmt = MediaFormat.createVideoFormat(mime, 1280, 720);
fmt.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
ArrayList<MediaFormat> formatsList = new ArrayList<>();
formatsList.add(fmt);
return selectHardwareCodecs(mime, formatsList, null, isEncoder, allCodecs);
}
// Returns the max number of 30 fps instances that the given list of mimeCodecPairs
// supports. It also checks that the each codec supports a PerformancePoint that covers
// required number of 30 fps instances.
public int checkAndGetMaxSupportedInstancesForCodecCombinations(int height, int width,
ArrayList<Pair<String, String>> mimeCodecPairs, int requiredMinInstances)
throws IOException {
int[] maxInstances = new int[mimeCodecPairs.size()];
int[] maxFrameRates = new int[mimeCodecPairs.size()];
int[] maxMacroBlockRates = new int[mimeCodecPairs.size()];
int loopCount = 0;
for (Pair<String, String> mimeCodecPair : mimeCodecPairs) {
MediaCodec codec = MediaCodec.createByCodecName(mimeCodecPair.second);
MediaCodecInfo.CodecCapabilities cap = codec.getCodecInfo()
.getCapabilitiesForType(mimeCodecPair.first);
List<PerformancePoint> pps = cap.getVideoCapabilities().getSupportedPerformancePoints();
assertTrue(pps.size() > 0);
boolean hasVP9 = mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9);
int requiredFrameRate = requiredMinInstances * 30;
maxInstances[loopCount] = cap.getMaxSupportedInstances();
PerformancePoint PPRes = new PerformancePoint(width, height, requiredFrameRate);
maxMacroBlockRates[loopCount] = 0;
boolean supportsResolutionPerformance = false;
for (PerformancePoint pp : pps) {
if (pp.covers(PPRes)) {
supportsResolutionPerformance = true;
if (pp.getMaxMacroBlockRate() > maxMacroBlockRates[loopCount]) {
maxMacroBlockRates[loopCount] = (int) pp.getMaxMacroBlockRate();
maxFrameRates[loopCount] = pp.getMaxFrameRate();
}
}
}
codec.release();
if (!supportsResolutionPerformance) {
Log.e(LOG_TAG,
"Codec " + mimeCodecPair.second + " doesn't support " + height + "p/" +
requiredFrameRate + " performance point");
return 0;
}
loopCount++;
}
Arrays.sort(maxInstances);
Arrays.sort(maxFrameRates);
Arrays.sort(maxMacroBlockRates);
int minOfMaxInstances = maxInstances[0];
int minOfMaxFrameRates = maxFrameRates[0];
int minOfMaxMacroBlockRates = maxMacroBlockRates[0];
// Calculate how many 30fps max instances it can support from it's mMaxFrameRate
// amd maxMacroBlockRate. (assuming 16x16 macroblocks)
return Math.min(minOfMaxInstances, Math.min((int) (minOfMaxFrameRates / 30.0),
(int) (minOfMaxMacroBlockRates / ((width / 16) * (height / 16)) / 30.0)));
}
public int getRequiredMinConcurrentInstances720p(boolean hasVP9) throws IOException {
// Below T, VP9 requires 60 fps at 720p and minimum of 2 instances
if (!Utils.isTPerfClass() && hasVP9) {
return REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9;
}
return REQUIRED_MIN_CONCURRENT_INSTANCES;
}
boolean isSecureSupportedCodec(String codecName, String mime) throws IOException {
boolean isSecureSupported;
MediaCodec codec = MediaCodec.createByCodecName(codecName);
isSecureSupported = codec.getCodecInfo().getCapabilitiesForType(mime).isFeatureSupported(
FEATURE_SecurePlayback);
codec.release();
return isSecureSupported;
}
boolean isWidevineSupported() {
return MediaDrm.isCryptoSchemeSupported(WIDEVINE_UUID);
}
boolean isWidevineL1Supported() throws UnsupportedSchemeException {
boolean isL1Supported = false;
if (isWidevineSupported()) {
MediaDrm mediaDrm = new MediaDrm(WIDEVINE_UUID);
isL1Supported = mediaDrm.getPropertyString("securityLevel").equals("L1");
mediaDrm.close();
}
return isL1Supported;
}
boolean isInternetAvailable() {
Context context = androidx.test.core.app.ApplicationProvider.getApplicationContext();
ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
NetworkCapabilities cap = cm.getNetworkCapabilities(cm.getActiveNetwork());
if (cap == null) return false;
return cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
boolean meetsSecureDecodePreconditions() throws UnsupportedSchemeException {
Assume.assumeTrue("Skipping secure decoder performance tests as Widevine is not supported",
isWidevineSupported());
if (Utils.isTPerfClass()) {
assertTrue("If Widevine is supported, L1 support is required for media performance " +
"class T devices",
isWidevineL1Supported());
assertTrue("Test requires internet connection for validating secure decoder " +
"requirements for media performance class T devices",
isInternetAvailable());
return true;
}
return isWidevineL1Supported() && isInternetAvailable();
}
}