1 /* 2 * Copyright (C) 2022 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.videocodec.cts; 18 19 import static android.media.MediaFormat.PICTURE_TYPE_I; 20 import static android.media.MediaFormat.PICTURE_TYPE_UNKNOWN; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.fail; 25 26 import android.media.Image; 27 import android.media.MediaCodec; 28 import android.media.MediaFormat; 29 import android.mediav2.common.cts.BitStreamUtils; 30 import android.mediav2.common.cts.CodecEncoderTestBase; 31 import android.mediav2.common.cts.DecodeStreamToYuv; 32 import android.mediav2.common.cts.EncoderConfigParams; 33 import android.mediav2.common.cts.RawResource; 34 import android.util.Log; 35 36 import androidx.annotation.NonNull; 37 38 import com.android.compatibility.common.util.Preconditions; 39 40 import java.io.File; 41 import java.io.IOException; 42 import java.io.RandomAccessFile; 43 import java.nio.ByteBuffer; 44 import java.util.ArrayList; 45 import java.util.HashMap; 46 import java.util.Objects; 47 import java.util.TreeMap; 48 49 /** 50 * Wrapper class for handling and testing video encoder components. 51 */ 52 public class VideoEncoderValidationTestBase extends CodecEncoderTestBase { 53 private static final String LOG_TAG = VideoEncoderValidationTestBase.class.getSimpleName(); 54 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 55 56 protected static final boolean ENABLE_LOGS = false; 57 protected static final StringBuilder DIAGNOSTICS = new StringBuilder(); 58 59 protected final CompressedResource mCRes; 60 protected BitStreamUtils.ParserBase mParser; 61 62 final TreeMap<Long, Integer> mPtsPicTypeMap = new TreeMap<>(); 63 64 RandomAccessFile mFileInp; 65 long mFileReadOffset; 66 long mFileLength; 67 68 public static class CompressedResource { 69 final String mMediaType; 70 final String mResFile; 71 CompressedResource(String mediaType, String resFile)72 CompressedResource(String mediaType, String resFile) { 73 mMediaType = mediaType; 74 mResFile = resFile; 75 } 76 77 @NonNull 78 @Override toString()79 public String toString() { 80 return "CompressedResource{" + "res file ='" + mResFile + '\'' + '}'; 81 } 82 uniqueLabel()83 public String uniqueLabel() { 84 return mMediaType + mResFile; 85 } 86 } 87 88 public static final CompressedResource BIRTHDAY_FULLHD_LANDSCAPE = 89 new CompressedResource(MediaFormat.MIMETYPE_VIDEO_AVC, MEDIA_DIR 90 + "AVICON-MOBILE-BirthdayHalfway-SI17-CRUW03-L-420-8bit-SDR-1080p-30fps.mp4"); 91 public static final CompressedResource SELFIEGROUP_FULLHD_PORTRAIT = 92 new CompressedResource(MediaFormat.MIMETYPE_VIDEO_AVC, MEDIA_DIR 93 + "AVICON-MOBILE-SelfieGroupGarden-SF15-CF01-P-420-8bit-SDR-1080p-30fps.mp4"); 94 logAllFilesInCacheDir(boolean isStartOfTest)95 static void logAllFilesInCacheDir(boolean isStartOfTest) { 96 if (isStartOfTest) DIAGNOSTICS.setLength(0); 97 String cacheDir = CONTEXT.getCacheDir().toString(); 98 DIAGNOSTICS.append(String.format("\nThe state of cache dir : %s, %s is :", cacheDir, 99 isStartOfTest ? "at start" : "now")); 100 File dir = new File(cacheDir); 101 if (dir.exists()) { 102 for (File f : Objects.requireNonNull(dir.listFiles())) { 103 DIAGNOSTICS.append(f.getName()).append("\n"); 104 } 105 } else { 106 DIAGNOSTICS.append(" directory not present"); 107 } 108 } 109 decodeStreamsToYuv(ArrayList<CompressedResource> resources, HashMap<String, RawResource> streamYuvMap, String prefix)110 static void decodeStreamsToYuv(ArrayList<CompressedResource> resources, 111 HashMap<String, RawResource> streamYuvMap, String prefix) { 112 decodeStreamsToYuv(resources, streamYuvMap, Integer.MAX_VALUE, prefix); 113 } 114 decodeStreamsToYuv(ArrayList<CompressedResource> resources, HashMap<String, RawResource> streamYuvMap, int frameLimit, String prefix)115 static void decodeStreamsToYuv(ArrayList<CompressedResource> resources, 116 HashMap<String, RawResource> streamYuvMap, int frameLimit, String prefix) { 117 logAllFilesInCacheDir(true); 118 for (CompressedResource res : resources) { 119 if (!(streamYuvMap.containsKey(res.uniqueLabel()))) { 120 try { 121 DecodeStreamToYuv yuv = new DecodeStreamToYuv(res.mMediaType, res.mResFile, 122 frameLimit, prefix); 123 streamYuvMap.put(res.uniqueLabel(), yuv.getDecodedYuv()); 124 } catch (Exception e) { 125 streamYuvMap.put(res.uniqueLabel(), null); 126 DIAGNOSTICS.append(String.format("\nWhile decoding the resource : %s," 127 + " encountered exception : %s was thrown", res, e)); 128 logAllFilesInCacheDir(false); 129 } 130 } 131 } 132 } 133 VideoEncoderValidationTestBase(String encoder, String mediaType, EncoderConfigParams encCfgParams, CompressedResource res, String allTestParams)134 VideoEncoderValidationTestBase(String encoder, String mediaType, 135 EncoderConfigParams encCfgParams, CompressedResource res, String allTestParams) { 136 super(encoder, mediaType, new EncoderConfigParams[]{encCfgParams}, allTestParams); 137 mCRes = res; 138 } 139 setUpSource(String inpPath)140 protected void setUpSource(String inpPath) throws IOException { 141 Preconditions.assertTestFileExists(inpPath); 142 mFileInp = new RandomAccessFile(inpPath, "r"); 143 mInputData = null; 144 mFileReadOffset = 0L; 145 mFileLength = mFileInp.length(); 146 } 147 resetContext(boolean isAsync, boolean signalEOSWithLastFrame)148 protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { 149 super.resetContext(isAsync, signalEOSWithLastFrame); 150 mPtsPicTypeMap.clear(); 151 } 152 enqueueInput(int bufferIndex)153 protected void enqueueInput(int bufferIndex) { 154 int frmSize = 3 * mActiveRawRes.mBytesPerSample * mActiveRawRes.mWidth 155 * mActiveRawRes.mHeight / 2; 156 if (mInputData == null || mInputData.length != frmSize) { 157 mInputData = new byte[frmSize]; 158 } 159 int bytesRead = 0; 160 try { 161 bytesRead = mFileInp.read(mInputData); 162 if (mIsLoopBack && mInputCount < mLoopBackFrameLimit && bytesRead == -1) { 163 mFileInp.seek(0); 164 bytesRead = mFileInp.read(mInputData); 165 } 166 } catch (IOException e) { 167 fail("encountered exception during file read." + e + "\n" + mTestConfig + mTestEnv); 168 } 169 if (bytesRead != -1 && bytesRead != frmSize) { 170 fail("received partial frame to encode \n" + mTestConfig + mTestEnv); 171 } 172 if (bytesRead == -1) { 173 assertEquals("mFileReadOffset, mFileLength and EOS state are not in sync \n" 174 + mTestConfig + mTestEnv, mFileReadOffset, mFileLength); 175 enqueueEOS(bufferIndex); 176 } else { 177 int size = mActiveRawRes.mBytesPerSample * mActiveEncCfg.mWidth 178 * mActiveEncCfg.mHeight * 3 / 2; 179 int flags = 0; 180 long pts = mInputOffsetPts + mInputCount * 1000000L / mActiveEncCfg.mFrameRate; 181 182 Image img = mCodec.getInputImage(bufferIndex); 183 assertNotNull("CPU-read via ImageReader API is not available \n" + mTestConfig 184 + mTestEnv, img); 185 fillImage(img); 186 if (mSignalEOSWithLastFrame) { 187 if (mIsLoopBack ? (mInputCount + 1 >= mLoopBackFrameLimit) : 188 (mFileReadOffset + frmSize >= mFileLength)) { 189 flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM; 190 mSawInputEOS = true; 191 } 192 } 193 mNumBytesSubmitted += size; 194 mFileReadOffset += frmSize; 195 mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags); 196 if (ENABLE_LOGS) { 197 Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts 198 + " flags: " + flags); 199 } 200 mOutputBuff.saveInPTS(pts); 201 mInputCount++; 202 } 203 } 204 dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)205 protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { 206 if (info.size > 0 && ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0)) { 207 int picType = PICTURE_TYPE_UNKNOWN; 208 209 if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { 210 picType = PICTURE_TYPE_I; 211 } 212 if (picType == PICTURE_TYPE_UNKNOWN) { 213 MediaFormat format = mCodec.getOutputFormat(bufferIndex); 214 picType = format.getInteger(MediaFormat.KEY_PICTURE_TYPE, PICTURE_TYPE_UNKNOWN); 215 } 216 if (picType == PICTURE_TYPE_UNKNOWN && mParser != null) { 217 ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); 218 picType = BitStreamUtils.getFrameTypeFromBitStream(buf, info, mParser); 219 } 220 mPtsPicTypeMap.put(info.presentationTimeUs, picType); 221 } 222 super.dequeueOutput(bufferIndex, info); 223 } 224 } 225