| /* |
| * 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); |
| } |
| } |
| |