1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.video.cts; 18 19 import android.media.MediaCodec; 20 import android.media.MediaCodecInfo; 21 import android.media.MediaCodecList; 22 import android.media.MediaFormat; 23 24 import java.io.IOException; 25 import java.util.ArrayList; 26 import java.util.HashMap; 27 import java.util.Map; 28 29 import static org.junit.Assert.assertNotNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.junit.Assume.assumeTrue; 32 33 class CodecEncoderPerformanceTestBase extends CodecPerformanceTestBase { 34 private static final String LOG_TAG = CodecEncoderPerformanceTest.class.getSimpleName(); 35 private static final Map<String, Float> transcodeAVCToTargetBitrateMap = new HashMap<>(); 36 37 final String mEncoderMime; 38 final String mEncoderName; 39 final int mBitrate; 40 double mAchievedFps; 41 42 private boolean mSawEncInputEOS = false; 43 private boolean mSawEncOutputEOS = false; 44 private int mEncOutputNum = 0; 45 private MediaCodec mEncoder; 46 private MediaFormat mEncoderFormat; 47 48 // Suggested bitrate scaling factors for transcoding avc to target format. 49 static { transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP8, 1.25f)50 transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP8, 1.25f); transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, 1.0f)51 transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, 1.0f); transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, 0.7f)52 transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, 0.7f); transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, 0.6f)53 transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, 0.6f); transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, 0.4f)54 transcodeAVCToTargetBitrateMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, 0.4f); 55 } 56 getBitrateScalingFactor(String mime)57 public static float getBitrateScalingFactor(String mime) { 58 return transcodeAVCToTargetBitrateMap.getOrDefault(mime, 1.5f); 59 } 60 CodecEncoderPerformanceTestBase(String decoderName, String testFile, String encoderMime, String encoderName, int bitrate, int keyPriority, float scalingFactor)61 public CodecEncoderPerformanceTestBase(String decoderName, String testFile, String encoderMime, 62 String encoderName, int bitrate, int keyPriority, float scalingFactor) { 63 super(decoderName, testFile, keyPriority, scalingFactor); 64 mEncoderMime = encoderMime; 65 mEncoderName = encoderName; 66 mBitrate = bitrate; 67 } 68 getMimesOfAvailableHardwareVideoEncoders()69 static ArrayList<String> getMimesOfAvailableHardwareVideoEncoders() { 70 MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 71 MediaCodecInfo[] codecInfos = codecList.getCodecInfos(); 72 ArrayList<String> listOfMimes = new ArrayList<>(); 73 for (MediaCodecInfo codecInfo : codecInfos) { 74 if (!codecInfo.isEncoder() || !codecInfo.isHardwareAccelerated()) continue; 75 String[] types = codecInfo.getSupportedTypes(); 76 for (String type : types) { 77 if (type.startsWith("video/") && !listOfMimes.contains(type)) { 78 listOfMimes.add(type); 79 } 80 } 81 } 82 return listOfMimes; 83 } 84 setUpEncoderFormat(MediaFormat format, String mime, int bitrate)85 public static MediaFormat setUpEncoderFormat(MediaFormat format, String mime, int bitrate) { 86 MediaFormat fmt = new MediaFormat(); 87 fmt.setString(MediaFormat.KEY_MIME, mime); 88 fmt.setInteger(MediaFormat.KEY_WIDTH, format.getInteger(MediaFormat.KEY_WIDTH)); 89 fmt.setInteger(MediaFormat.KEY_HEIGHT, format.getInteger(MediaFormat.KEY_HEIGHT)); 90 fmt.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); 91 fmt.setInteger(MediaFormat.KEY_FRAME_RATE, 92 format.getInteger(MediaFormat.KEY_FRAME_RATE, 30)); 93 fmt.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f); 94 fmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, 95 MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); 96 return fmt; 97 } 98 dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info)99 private void dequeueEncoderOutput(int bufferIndex, MediaCodec.BufferInfo info) { 100 if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { 101 mEncOutputNum++; 102 } 103 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 104 mSawEncOutputEOS = true; 105 } 106 mEncoder.releaseOutputBuffer(bufferIndex, false); 107 } 108 setUpFormats(MediaFormat format)109 private void setUpFormats(MediaFormat format) throws IOException { 110 mDecoderFormat = new MediaFormat(format); 111 mDecoderFormat.setInteger(MediaFormat.KEY_PRIORITY, mKeyPriority); 112 mEncoderFormat = setUpEncoderFormat(mDecoderFormat, mEncoderMime, mBitrate); 113 mEncoderFormat.setInteger(MediaFormat.KEY_PRIORITY, mKeyPriority); 114 double maxOperatingRateDecoder = getMaxOperatingRate(mDecoderName, mDecoderMime); 115 double maxOperatingRateEncoder = getMaxOperatingRate(mEncoderName, mEncoderMime); 116 mOperatingRateExpected = Math.min(maxOperatingRateDecoder, maxOperatingRateEncoder); 117 // As both decoder and encoder are running in concurrently, expected rate is halved 118 mOperatingRateExpected /= 2.0; 119 if (mMaxOpRateScalingFactor > 0.0f) { 120 int operatingRateToSet = (int) (mOperatingRateExpected * mMaxOpRateScalingFactor); 121 if (mMaxOpRateScalingFactor < 1.0f) { 122 mOperatingRateExpected = operatingRateToSet; 123 } 124 mDecoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRateToSet); 125 mEncoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRateToSet); 126 } else if (mMaxOpRateScalingFactor < 0.0f) { 127 mDecoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, -1); 128 mEncoderFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, -1); 129 } 130 mEncoderFormat.setInteger(MediaFormat.KEY_COMPLEXITY, 131 getEncoderMinComplexity(mEncoderName, mEncoderMime)); 132 } 133 doWork()134 private void doWork() { 135 while (!mSawEncOutputEOS) { 136 if (!mSawDecInputEOS) { 137 int inputBufIndex = mDecoder.dequeueInputBuffer(Q_DEQ_TIMEOUT_US); 138 if (inputBufIndex >= 0) { 139 enqueueDecoderInput(inputBufIndex); 140 } 141 } 142 if (!mSawDecOutputEOS) { 143 MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 144 int outputBufIndex = mDecoder.dequeueOutputBuffer(info, Q_DEQ_TIMEOUT_US); 145 if (outputBufIndex >= 0) { 146 dequeueDecoderOutput(outputBufIndex, info, true); 147 } 148 } 149 if (mSawDecOutputEOS && !mSawEncInputEOS) { 150 mEncoder.signalEndOfInputStream(); 151 mSawEncInputEOS = true; 152 } 153 MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo(); 154 int outputBufferId = mEncoder.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US); 155 if (outputBufferId >= 0) { 156 dequeueEncoderOutput(outputBufferId, outInfo); 157 } 158 } 159 } 160 encode()161 public void encode() throws IOException { 162 MediaFormat format = setUpDecoderInput(); 163 assertNotNull("Video track not present in " + mTestFile, format); 164 165 if (EXCLUDE_ENCODER_MAX_RESOLUTION) { 166 int maxFrameSize = getMaxFrameSize(mEncoderName, mEncoderMime); 167 assumeTrue(mWidth + "x" + mHeight + " is skipped as it not less than half of " + 168 "maximum frame size: " + maxFrameSize + " supported by the encoder.", 169 mWidth * mHeight < maxFrameSize / 2); 170 } 171 172 setUpFormats(format); 173 mDecoder = MediaCodec.createByCodecName(mDecoderName); 174 mEncoder = MediaCodec.createByCodecName(mEncoderName); 175 mEncoder.configure(mEncoderFormat, null, MediaCodec.CONFIGURE_FLAG_ENCODE, null); 176 mSurface = mEncoder.createInputSurface(); 177 assertTrue("Surface created is null.", mSurface != null); 178 assertTrue("Surface is not valid", mSurface.isValid()); 179 mDecoder.configure(mDecoderFormat, mSurface, null, 0); 180 mDecoder.start(); 181 mEncoder.start(); 182 long start = System.currentTimeMillis(); 183 doWork(); 184 long finish = System.currentTimeMillis(); 185 mEncoder.stop(); 186 mSurface.release(); 187 mEncoder.release(); 188 mDecoder.stop(); 189 mDecoder.release(); 190 mEncoder = null; 191 mDecoder = null; 192 assertTrue("Encoder output count is zero", mEncOutputNum > 0); 193 mAchievedFps = mEncOutputNum / ((finish - start) / 1000.0); 194 } 195 196 @Override finalize()197 protected void finalize() throws Throwable { 198 if (mDecoder != null) { 199 mDecoder.release(); 200 mDecoder = null; 201 } 202 if (mEncoder != null) { 203 mEncoder.release(); 204 mEncoder = null; 205 } 206 } 207 } 208