blob: f730c7e3c80cc09db01edccc9e392eead6a4b655 [file] [log] [blame]
/*
* Copyright (C) 2022 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.media.codec.cts;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.cts.MediaCodecWrapper;
import android.media.cts.MediaHeavyPresubmitTest;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.MediaUtils;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* Verification test for video encoding statistics.
*
* Check whether a higher bitrate gives a lower average QP reported from encoder
*
*/
@MediaHeavyPresubmitTest
@AppModeFull(reason = "TODO: evaluate and port to instant")
@RunWith(Parameterized.class)
public class VideoEncodingStatisticsTest extends VideoCodecTestBase {
private static final String ENCODED_IVF_BASE = "football";
private static final String INPUT_YUV = null;
private static final String OUTPUT_YUV = SDCARD_DIR + File.separator +
ENCODED_IVF_BASE + "_out.yuv";
// YUV stream properties.
private static final int WIDTH = 320;
private static final int HEIGHT = 240;
private static final int FPS = 30;
// Default encoding bitrate.
private static final int BITRATE = 400000;
// List of bitrates used in quality and basic bitrate tests.
private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
private static final String CODEC_PREFIX_KEY = "codec-prefix";
private static final String mCodecPrefix;
@Parameterized.Parameter(0)
public String mCodecName;
@Parameterized.Parameter(1)
public String mCodecMimeType;
@Parameterized.Parameter(2)
public int mBitRateMode;
static {
android.os.Bundle args = InstrumentationRegistry.getArguments();
mCodecPrefix = args.getString(CODEC_PREFIX_KEY);
}
static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
final List<Object[]> argsList = new ArrayList<>();
int argLength = exhaustiveArgsList.get(0).length;
for (Object[] arg : exhaustiveArgsList) {
String[] encodersForMime = MediaUtils.getEncoderNamesForMime((String) arg[0]);
for (String encoder : encodersForMime) {
if (mCodecPrefix != null && !encoder.startsWith(mCodecPrefix)) {
continue;
}
Object[] testArgs = new Object[argLength + 1];
testArgs[0] = encoder;
System.arraycopy(arg, 0, testArgs, 1, argLength);
argsList.add(testArgs);
}
}
return argsList;
}
@Parameterized.Parameters(name = "{index}({0}:{1}:{2})")
public static Collection<Object[]> input() {
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
{AVC_MIME, VIDEO_ControlRateConstant},
{AVC_MIME, VIDEO_ControlRateVariable},
{HEVC_MIME, VIDEO_ControlRateConstant},
{HEVC_MIME, VIDEO_ControlRateVariable},
});
return prepareParamList(exhaustiveArgsList);
}
private static CodecCapabilities getCodecCapabilities(
String encoderName, String mime, boolean isEncoder) {
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) {
if (isEncoder != codecInfo.isEncoder()) {
continue;
}
if (encoderName.equals(codecInfo.getName())) {
return codecInfo.getCapabilitiesForType(mime);
}
}
return null;
}
/**
* Check whethera a higher bitrate gives a lower average QP
*
* Video streams with higher bitrate should have lower average qp.
*/
private void testEncStatRateAvgQp(String codecName, String codecMimeType, int bitRateMode)
throws Exception {
int encodeSeconds = 9; // Encoding sequence duration in seconds for each bitrate.
float[] avgSeqQp = new float[TEST_BITRATES_SET.length];
boolean[] completed = new boolean[TEST_BITRATES_SET.length];
boolean skipped = true;
ArrayList<MediaCodec.BufferInfo> bufInfos;
CodecCapabilities caps = getCodecCapabilities(codecName, codecMimeType, true);
Assume.assumeTrue(codecName + " does not support FEATURE_EncodingStatistics",
caps.isFeatureSupported(CodecCapabilities.FEATURE_EncodingStatistics));
for (int i = 0; i < TEST_BITRATES_SET.length; i++) {
EncoderOutputStreamParameters params = getDefaultEncodingParameters(
INPUT_YUV,
ENCODED_IVF_BASE,
codecName,
codecMimeType,
encodeSeconds,
WIDTH,
HEIGHT,
FPS,
bitRateMode,
TEST_BITRATES_SET[i],
true);
// Enable encoding statistics at VIDEO_ENCODING_STATISTICS_LEVEL_1
params.encodingStatisticsLevel = MediaFormat.VIDEO_ENCODING_STATISTICS_LEVEL_1;
ArrayList<ByteBuffer> codecConfigs = new ArrayList<>();
VideoEncodeOutput videoEncodeOutput = encodeAsync(params, codecConfigs);
bufInfos = videoEncodeOutput.bufferInfo;
if (bufInfos == null) {
// parameters not supported, try other bitrates
completed[i] = false;
continue;
}
completed[i] = true;
skipped = false;
if (videoEncodeOutput.encStat.encodedFrames > 0) {
avgSeqQp[i] = (float) videoEncodeOutput.encStat.averageSeqQp
/ videoEncodeOutput.encStat.encodedFrames;
}
}
if (skipped) {
Log.i(TAG, "SKIPPING testEncodingStatisticsAvgQp(): no bitrates supported");
return;
}
// First do a validity check - higher bitrates should results in lower QP.
for (int i = 1; i < TEST_BITRATES_SET.length; i++) {
if (!completed[i]) {
continue;
}
for (int j = 0; j < i; j++) {
if (!completed[j]) {
continue;
}
double differenceBitrate = TEST_BITRATES_SET[i] - TEST_BITRATES_SET[j];
double differenceAvgQp = avgSeqQp[i] - avgSeqQp[j];
if (differenceBitrate * differenceAvgQp > 0) {
throw new RuntimeException("Target bitrates: " +
TEST_BITRATES_SET[j] + ", " + TEST_BITRATES_SET[i] +
". Average QP: "
+ avgSeqQp[j] + ", " + avgSeqQp[i]);
}
}
}
}
@Test
public void testEncodingStatisticsAvgQp() throws Exception {
testEncStatRateAvgQp(mCodecName, mCodecMimeType, mBitRateMode);
}
}