blob: 5568f59580b3120806c0c86e50053376e38b8a60 [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.mediapc.cts.CodecTestBase.selectCodecs;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import android.media.MediaFormat;
import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.view.Surface;
import androidx.test.rule.ActivityTestRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FrameDropTestBase {
private static final String LOG_TAG = FrameDropTestBase.class.getSimpleName();
static final boolean[] boolStates = {false, true};
static final String AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
static final String HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
static final String VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
static final String VP9 = MediaFormat.MIMETYPE_VIDEO_VP9;
static final String AV1 = MediaFormat.MIMETYPE_VIDEO_AV1;
static final String AAC = MediaFormat.MIMETYPE_AUDIO_AAC;
static final String AAC_LOAD_FILE_NAME = "bbb_1c_128kbps_aac_audio.mp4";
static final String AVC_LOAD_FILE_NAME = "bbb_1280x720_3mbps_30fps_avc.mp4";
static final long DECODE_31S = 31000; // In ms
static final int MAX_FRAME_DROP_FOR_30S;
// For perf class R, one frame drop per 10 seconds at 30 fps i.e. 3 drops per 30 seconds
static final int MAX_FRAME_DROP_FOR_30S_30FPS_PC_R = 3;
// For perf class S, two frame drops per 10 seconds at 60 fps i.e. 6 drops per 30 seconds
static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_S = 6;
// For perf class T, one frame drop per 10 seconds at 60 fps i.e. 3 drops per 30 seconds
static final int MAX_FRAME_DROP_FOR_30S_60FPS_PC_T = 3;
final String mMime;
final String mDecoderName;
final boolean mIsAsync;
Surface mSurface;
private LoadStatus mLoadStatus = null;
private Thread mTranscodeLoadThread = null;
private Thread mAudioPlaybackLoadThread = null;
private Exception mTranscodeLoadException = null;
private Exception mAudioPlaybackLoadException = null;
static String AVC_DECODER_NAME;
static String AVC_ENCODER_NAME;
static String AAC_DECODER_NAME;
static Map<String, String> m540p30FpsTestFiles = new HashMap<>();
static Map<String, String> m1080p30FpsTestFiles = new HashMap<>();
static Map<String, String> m540p60FpsTestFiles = new HashMap<>();
static Map<String, String> m1080p60FpsTestFiles = new HashMap<>();
static {
m540p60FpsTestFiles.put(AVC, "bbb_960x540_3mbps_60fps_avc.mp4");
m540p60FpsTestFiles.put(HEVC, "bbb_960x540_3mbps_60fps_hevc.mp4");
m540p60FpsTestFiles.put(VP8, "bbb_960x540_3mbps_60fps_vp8.webm");
m540p60FpsTestFiles.put(VP9, "bbb_960x540_3mbps_60fps_vp9.webm");
m540p60FpsTestFiles.put(AV1, "bbb_960x540_3mbps_60fps_av1.mp4");
m1080p60FpsTestFiles.put(AVC, "bbb_1920x1080_8mbps_60fps_avc.mp4");
m1080p60FpsTestFiles.put(HEVC, "bbb_1920x1080_6mbps_60fps_hevc.mp4");
m1080p60FpsTestFiles.put(VP8, "bbb_1920x1080_8mbps_60fps_vp8.webm");
m1080p60FpsTestFiles.put(VP9, "bbb_1920x1080_6mbps_60fps_vp9.webm");
m1080p60FpsTestFiles.put(AV1, "bbb_1920x1080_6mbps_60fps_av1.mp4");
m540p30FpsTestFiles.put(AVC, "bbb_960x540_2mbps_30fps_avc.mp4");
m540p30FpsTestFiles.put(HEVC, "bbb_960x540_2mbps_30fps_hevc.mp4");
m540p30FpsTestFiles.put(VP8, "bbb_960x540_2mbps_30fps_vp8.webm");
m540p30FpsTestFiles.put(VP9, "bbb_960x540_2mbps_30fps_vp9.webm");
m540p30FpsTestFiles.put(AV1, "bbb_960x540_2mbps_30fps_av1.mp4");
m1080p30FpsTestFiles.put(AVC, "bbb_1920x1080_6mbps_30fps_avc.mp4");
m1080p30FpsTestFiles.put(HEVC, "bbb_1920x1080_4mbps_30fps_hevc.mp4");
m1080p30FpsTestFiles.put(VP8, "bbb_1920x1080_6mbps_30fps_vp8.webm");
m1080p30FpsTestFiles.put(VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm");
m1080p30FpsTestFiles.put(AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4");
switch (Utils.getPerfClass()) {
case Build.VERSION_CODES.TIRAMISU:
MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_T;
break;
case Build.VERSION_CODES.S:
MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_60FPS_PC_S;
break;
case Build.VERSION_CODES.R:
default:
MAX_FRAME_DROP_FOR_30S = MAX_FRAME_DROP_FOR_30S_30FPS_PC_R;
break;
}
}
@Before
public void setUp() throws Exception {
Utils.assumeDeviceMeetsPerformanceClassPreconditions();
ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false);
assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty());
AVC_DECODER_NAME = listOfAvcHwDecoders.get(0);
ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true);
assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty());
AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0);
ArrayList<String> listOfAacDecoders = selectCodecs(AAC, null, null, false);
assertFalse("Test requires aac decoder", listOfAacDecoders.isEmpty());
AAC_DECODER_NAME = listOfAacDecoders.get(0);
createSurface();
startLoad();
}
@After
public void tearDown() throws Exception {
stopLoad();
releaseSurface();
}
@Rule
public ActivityTestRule<TestActivity> mActivityRule =
new ActivityTestRule<>(TestActivity.class);
public FrameDropTestBase(String mimeType, String decoderName, boolean isAsync) {
mMime = mimeType;
mDecoderName = decoderName;
mIsAsync = isAsync;
}
// Returns the list of objects with mimeTypes and their hardware decoders supporting the
// given features combining with sync and async modes.
static List<Object[]> prepareArgumentsList(String[] features) {
final List<Object[]> argsList = new ArrayList<>();
final String[] mimesList = new String[] {AVC, HEVC, VP8, VP9, AV1};
for (String mime : mimesList) {
MediaFormat format = MediaFormat.createVideoFormat(mime, 1920, 1080);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
ArrayList<MediaFormat> formats = new ArrayList<>();
formats.add(format);
ArrayList<String> listOfDecoders =
selectHardwareCodecs(mime, formats, features, false);
for (String decoder : listOfDecoders) {
for (boolean isAsync : boolStates) {
argsList.add(new Object[]{mime, decoder, isAsync});
}
}
}
return argsList;
}
protected int getAchievedPerfClass(int frameRate, int frameDropCount) {
int pc = 0;
if (frameRate == 30) {
pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_30FPS_PC_R ? Build.VERSION_CODES.R : 0;
} else {
pc = frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_T ? Build.VERSION_CODES.TIRAMISU
: frameDropCount <= MAX_FRAME_DROP_FOR_30S_60FPS_PC_S ? Build.VERSION_CODES.S
: 0;
}
return pc;
}
private void createSurface() throws InterruptedException {
mActivityRule.getActivity().waitTillSurfaceIsCreated();
mSurface = mActivityRule.getActivity().getSurface();
assertTrue("Surface created is null.", mSurface != null);
assertTrue("Surface created is invalid.", mSurface.isValid());
// As we display 1920x1080 and 960x540 only which are of same aspect ratio, we will
// be setting screen params to 1920x1080
mActivityRule.getActivity().setScreenParams(1920, 1080, true);
}
private void releaseSurface() {
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
}
private Thread createTranscodeLoad() {
Thread transcodeLoadThread = new Thread(() -> {
try {
TranscodeLoad transcodeLoad = new TranscodeLoad(AVC, AVC_LOAD_FILE_NAME,
AVC_DECODER_NAME, AVC_ENCODER_NAME, mLoadStatus);
transcodeLoad.doTranscode();
} catch (Exception e) {
mTranscodeLoadException = e;
}
});
return transcodeLoadThread;
}
private Thread createAudioPlaybackLoad() {
Thread audioPlaybackLoadThread = new Thread(() -> {
try {
AudioPlaybackLoad audioPlaybackLoad = new AudioPlaybackLoad(AAC, AAC_LOAD_FILE_NAME,
AAC_DECODER_NAME, mLoadStatus);
audioPlaybackLoad.doDecodeAndPlayback();
} catch (Exception e) {
mAudioPlaybackLoadException = e;
}
});
return audioPlaybackLoadThread;
}
private void startLoad() {
// TODO: b/183671436
// Start Transcode load (Decoder(720p) + Encoder(720p))
mLoadStatus = new LoadStatus();
mTranscodeLoadThread = createTranscodeLoad();
mTranscodeLoadThread.start();
// Start 128kbps AAC audio playback
mAudioPlaybackLoadThread = createAudioPlaybackLoad();
mAudioPlaybackLoadThread.start();
}
private void stopLoad() throws Exception {
if (mLoadStatus != null) {
mLoadStatus.setLoadFinished();
}
if (mTranscodeLoadThread != null) {
mTranscodeLoadThread.join();
mTranscodeLoadThread = null;
}
if (mAudioPlaybackLoadThread != null) {
mAudioPlaybackLoadThread.join();
mAudioPlaybackLoadThread = null;
}
if (mTranscodeLoadException != null) throw mTranscodeLoadException;
if (mAudioPlaybackLoadException != null) throw mAudioPlaybackLoadException;
mLoadStatus = null;
}
}