• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.mediav2.common.cts;
18 
19 import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing;
20 import static android.media.MediaCodecInfo.CodecProfileLevel.*;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 
28 import android.content.Context;
29 import android.hardware.camera2.CameraAccessException;
30 import android.hardware.camera2.CameraCharacteristics;
31 import android.hardware.camera2.CameraManager;
32 import android.hardware.camera2.CameraMetadata;
33 import android.hardware.camera2.params.DynamicRangeProfiles;
34 import android.hardware.display.DisplayManager;
35 import android.media.MediaCodec;
36 import android.media.MediaCodecInfo;
37 import android.media.MediaCodecInfo.CodecCapabilities;
38 import android.media.MediaCodecInfo.CodecProfileLevel;
39 import android.media.MediaCodecList;
40 import android.media.MediaFormat;
41 import android.os.Build;
42 import android.os.Bundle;
43 import android.os.PersistableBundle;
44 import android.os.SystemProperties;
45 import android.util.Log;
46 import android.util.Pair;
47 import android.view.Display;
48 import android.view.Surface;
49 
50 import androidx.test.platform.app.InstrumentationRegistry;
51 
52 import com.android.compatibility.common.util.ApiLevelUtil;
53 import com.android.compatibility.common.util.MediaUtils;
54 
55 import org.junit.After;
56 import org.junit.Assume;
57 import org.junit.Before;
58 import org.junit.Rule;
59 import org.junit.rules.TestName;
60 
61 import java.io.IOException;
62 import java.nio.ByteBuffer;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Comparator;
66 import java.util.HashMap;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71 import java.util.regex.Matcher;
72 import java.util.regex.Pattern;
73 import java.util.stream.IntStream;
74 import java.util.stream.Stream;
75 
76 /**
77  * This class comprises of routines that are generic to media codec component trying and testing.
78  * <p>
79  * A media codec component essentially processes input data to generate output data. The
80  * component uses a set of input and output buffers. At a simplistic level, the client requests
81  * (or receive) an empty input buffer, fills it up with data and sends it to the codec for
82  * processing. The codec uses up the data and transforms it into one of its empty output
83  * buffers. Finally, the client asks (or receive) a filled output buffer, consume its contents and
84  * release it back to the codec.
85  * <p>
86  * The type of data that a component receives and sends is dependent on the component. For
87  * instance video encoders expect raw yuv/rgb data and send compressed data. A video decoder
88  * receives compressed access-unit and sends reconstructed yuv. But the processes surrounding
89  * this enqueue and dequeue remain common to all components. Operations like configure, requesting
90  * the component for an empty input buffer, feeding the component a filled input buffer,
91  * requesting the component for an output buffer, releasing the processed output buffer, Sending
92  * state transition commands, waiting on component to send all outputs, closing the component and
93  * releasing the resources, ... remain more or less identical to all components. The routines
94  * that manage these generic processes are maintained by this class. Not only the methods that
95  * are responsible for component trying but also methods that test its functionality are covered
96  * here. A video component is expected to give same number of outputs as inputs. The output
97  * buffer timestamps of an audio component or a decoder component is expected to be strictly
98  * increasing. The routines that enforce these generic rules of all components at all times are
99  * part of this class. Besides these, mediaType specific constants, helper utilities to test
100  * specific components or mediaTypes are covered here.
101  * <p>
102  * enqueueInput and dequeueOutput are routines responsible for filling the input buffer and
103  * handing the received output buffer respectively. These shall be component specific, hence they
104  * are abstract methods. Any client intending to use this class shall implement these methods
105  * basing on the component under test.
106  * <p>
107  * In simple terms, the CodecTestBase is a wrapper class comprising generic routines for
108  * component trying and testing.
109  */
110 public abstract class CodecTestBase {
111     public static final boolean IS_Q = ApiLevelUtil.getApiLevel() == Build.VERSION_CODES.Q;
112     public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
113     public static final boolean IS_AT_LEAST_T =
114             ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU);
115     //TODO(b/248315681) Remove codenameEquals() check once devices return correct version for U
116     public static final boolean IS_AT_LEAST_U = ApiLevelUtil.isAfter(Build.VERSION_CODES.TIRAMISU)
117             || ApiLevelUtil.codenameEquals("UpsideDownCake");
118     public static final boolean IS_BEFORE_U = !IS_AT_LEAST_U;
119     public static final boolean FIRST_SDK_IS_AT_LEAST_T =
120             ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU);
121     public static final boolean VNDK_IS_AT_LEAST_T =
122             SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT)
123                     >= Build.VERSION_CODES.TIRAMISU;
124     public static final boolean VNDK_IS_BEFORE_U =
125             SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT)
126                     < Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
127     public static final boolean VNDK_IS_AT_MOST_U =
128             SystemProperties.getInt("ro.vndk.version", Build.VERSION_CODES.CUR_DEVELOPMENT)
129                     <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
130     public static final boolean BOARD_SDK_IS_AT_LEAST_T =
131             SystemProperties.getInt("ro.board.api_level", Build.VERSION_CODES.CUR_DEVELOPMENT)
132                     >= Build.VERSION_CODES.TIRAMISU;
133     public static final boolean IS_HDR_EDITING_SUPPORTED;
134     public static final boolean IS_HDR_CAPTURE_SUPPORTED;
135     private static final String LOG_TAG = CodecTestBase.class.getSimpleName();
136 
137     public static final ArrayList<String> HDR_INFO_IN_BITSTREAM_CODECS = new ArrayList<>();
138     public static final String HDR_STATIC_INFO =
139             "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d";
140     public static final String HDR_STATIC_INCORRECT_INFO =
141             "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d";
142     public static final String CODEC_PREFIX_KEY = "codec-prefix";
143     public static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix";
144     public static final String MEDIA_TYPE_SEL_KEY = "media-type-sel";
145     public static final Map<String, String> CODEC_SEL_KEY_MEDIA_TYPE_MAP = new HashMap<>();
146     public static final Map<String, String> DEFAULT_ENCODERS = new HashMap<>();
147     public static final Map<String, String> DEFAULT_DECODERS = new HashMap<>();
148     public static final HashMap<String, int[]> PROFILE_MAP = new HashMap<>();
149     public static final HashMap<String, int[]> PROFILE_SDR_MAP = new HashMap<>();
150     public static final HashMap<String, int[]> PROFILE_HLG_MAP = new HashMap<>();
151     public static final HashMap<String, int[]> PROFILE_HDR10_MAP = new HashMap<>();
152     public static final HashMap<String, int[]> PROFILE_HDR10_PLUS_MAP = new HashMap<>();
153     public static final HashMap<String, int[]> PROFILE_HDR_MAP = new HashMap<>();
154     public static final boolean ENABLE_LOGS = false;
155     public static final int PER_TEST_TIMEOUT_LARGE_TEST_MS = 300000;
156     public static final int PER_TEST_TIMEOUT_SMALL_TEST_MS = 60000;
157     public static final int UNSPECIFIED = 0;
158     // Maintain Timeouts in sync with their counterpart in NativeMediaCommon.h
159     // block at most 5ms while looking for io buffers
160     public static final long Q_DEQ_TIMEOUT_US = 5000;
161     // max poll counter before test aborts and returns error
162     public static final int RETRY_LIMIT = 100;
163     public static final String INVALID_CODEC = "unknown.codec_";
164     static final int[] MPEG2_PROFILES = new int[]{MPEG2ProfileSimple, MPEG2ProfileMain,
165             MPEG2Profile422, MPEG2ProfileSNR, MPEG2ProfileSpatial, MPEG2ProfileHigh};
166     static final int[] MPEG4_PROFILES = new int[]{MPEG4ProfileSimple, MPEG4ProfileSimpleScalable,
167             MPEG4ProfileCore, MPEG4ProfileMain, MPEG4ProfileNbit, MPEG4ProfileScalableTexture,
168             MPEG4ProfileSimpleFace, MPEG4ProfileSimpleFBA, MPEG4ProfileBasicAnimated,
169             MPEG4ProfileHybrid, MPEG4ProfileAdvancedRealTime, MPEG4ProfileCoreScalable,
170             MPEG4ProfileAdvancedCoding, MPEG4ProfileAdvancedCore, MPEG4ProfileAdvancedScalable,
171             MPEG4ProfileAdvancedSimple};
172     static final int[] H263_PROFILES = new int[]{H263ProfileBaseline, H263ProfileH320Coding,
173             H263ProfileBackwardCompatible, H263ProfileISWV2, H263ProfileISWV3,
174             H263ProfileHighCompression, H263ProfileInternet, H263ProfileInterlace,
175             H263ProfileHighLatency};
176     static final int[] VP8_PROFILES = new int[]{VP8ProfileMain};
177     static final int[] AVC_SDR_PROFILES = new int[]{AVCProfileBaseline, AVCProfileMain,
178             AVCProfileExtended, AVCProfileHigh, AVCProfileConstrainedBaseline,
179             AVCProfileConstrainedHigh};
180     static final int[] AVC_HLG_PROFILES = new int[]{AVCProfileHigh10};
181     static final int[] AVC_HDR_PROFILES = AVC_HLG_PROFILES;
182     static final int[] AVC_PROFILES = combine(AVC_SDR_PROFILES, AVC_HDR_PROFILES);
183     static final int[] VP9_SDR_PROFILES = new int[]{VP9Profile0};
184     static final int[] VP9_HLG_PROFILES = new int[]{VP9Profile2};
185     static final int[] VP9_HDR10_PROFILES = new int[]{VP9Profile2HDR};
186     static final int[] VP9_HDR10_PLUS_PROFILES = new int[]{VP9Profile2HDR10Plus};
187     static final int[] VP9_HDR_PROFILES =
188             combine(VP9_HLG_PROFILES, combine(VP9_HDR10_PROFILES, VP9_HDR10_PLUS_PROFILES));
189     static final int[] VP9_PROFILES = combine(VP9_SDR_PROFILES, VP9_HDR_PROFILES);
190     static final int[] HEVC_SDR_PROFILES = new int[]{HEVCProfileMain};
191     static final int[] HEVC_HLG_PROFILES = new int[]{HEVCProfileMain10};
192     static final int[] HEVC_HDR10_PROFILES = new int[]{HEVCProfileMain10HDR10};
193     static final int[] HEVC_HDR10_PLUS_PROFILES = new int[]{HEVCProfileMain10HDR10Plus};
194     static final int[] HEVC_HDR_PROFILES =
195             combine(HEVC_HLG_PROFILES, combine(HEVC_HDR10_PROFILES, HEVC_HDR10_PLUS_PROFILES));
196     static final int[] HEVC_PROFILES = combine(HEVC_SDR_PROFILES, HEVC_HDR_PROFILES);
197     static final int[] AV1_SDR_PROFILES = new int[]{AV1ProfileMain8};
198     static final int[] AV1_HLG_PROFILES = new int[]{AV1ProfileMain10};
199     static final int[] AV1_HDR10_PROFILES = new int[]{AV1ProfileMain10HDR10};
200     static final int[] AV1_HDR10_PLUS_PROFILES = new int[]{AV1ProfileMain10HDR10Plus};
201     static final int[] AV1_HDR_PROFILES =
202             combine(AV1_HLG_PROFILES, combine(AV1_HDR10_PROFILES, AV1_HDR10_PLUS_PROFILES));
203     static final int[] AV1_PROFILES = combine(AV1_SDR_PROFILES, AV1_HDR_PROFILES);
204     static final int[] AAC_PROFILES = new int[]{AACObjectMain, AACObjectLC, AACObjectSSR,
205             AACObjectLTP, AACObjectHE, AACObjectScalable, AACObjectERLC, AACObjectERScalable,
206             AACObjectLD, AACObjectELD, AACObjectXHE};
207     public static final Context CONTEXT =
208             InstrumentationRegistry.getInstrumentation().getTargetContext();
209 
210     public static final int MAX_DISPLAY_HEIGHT_CURRENT =
211             Arrays.stream(CONTEXT.getSystemService(DisplayManager.class).getDisplays())
212                     .map(Display::getSupportedModes)
213                     .flatMap(Stream::of)
214                     .max(Comparator.comparing(Display.Mode::getPhysicalHeight))
215                     .orElseThrow(() -> new RuntimeException("Failed to determine max height"))
216                     .getPhysicalHeight();
217     public static final int MAX_DISPLAY_WIDTH_CURRENT =
218             Arrays.stream(CONTEXT.getSystemService(DisplayManager.class).getDisplays())
219                     .map(Display::getSupportedModes)
220                     .flatMap(Stream::of)
221                     .max(Comparator.comparing(Display.Mode::getPhysicalHeight))
222                     .orElseThrow(() -> new RuntimeException("Failed to determine max height"))
223                     .getPhysicalWidth();
224     public static final int MAX_DISPLAY_WIDTH_LAND =
225             Math.max(MAX_DISPLAY_WIDTH_CURRENT, MAX_DISPLAY_HEIGHT_CURRENT);
226     public static final int MAX_DISPLAY_HEIGHT_LAND =
227             Math.min(MAX_DISPLAY_WIDTH_CURRENT, MAX_DISPLAY_HEIGHT_CURRENT);
228 
229     public static final String HDR10_INFO_SCENE_A =
230             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
231                     + "0a 00 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
232                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
233                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 00";
234     public static final String HDR10_INFO_SCENE_B =
235             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
236                     + "0a 00 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
237                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
238                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 00";
239     public static final String HDR10_INFO_SCENE_C =
240             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
241                     + "0e 80 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
242                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
243                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 00";
244     public static final String HDR10_INFO_SCENE_D =
245             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
246                     + "0e 80 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
247                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
248                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 00";
249 
250     public static final String HDR10_INCORRECT_INFO_SCENE_A =
251             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
252                     + "0a 00 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
253                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
254                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 00";
255     public static final String HDR10_INCORRECT_INFO_SCENE_B =
256             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
257                     + "0a 00 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
258                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
259                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 01";
260     public static final String HDR10_INCORRECT_INFO_SCENE_C =
261             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
262                     + "0e 80 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
263                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
264                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 02";
265     public static final String HDR10_INCORRECT_INFO_SCENE_D =
266             "b5 00 3c 00 01 04 00 40  00 0c 80 4e 20 27 10 00"
267                     + "0e 80 00 24 08 00 00 28  00 00 50 00 28 c8 00 c9"
268                     + "90 02 aa 58 05 ca d0 0c  0a f8 16 83 18 9c 18 00"
269                     + "40 78 13 64 d5 7c 2e 2c  c3 59 de 79 6e c3 c2 03";
270 
271     public static String mediaTypeSelKeys;
272     public static String codecPrefix;
273     public static String mediaTypePrefix;
274 
275     public enum SupportClass {
276         CODEC_ALL, // All codecs must support
277         CODEC_ANY, // At least one codec must support
278         CODEC_DEFAULT, // Default codec must support
279         CODEC_HW, // If the component is hardware, then it must support
280         CODEC_SHOULD, // Codec support is optional, but recommended
281         CODEC_HW_RECOMMENDED, // Codec support is optional, but strongly recommended if component
282         // is hardware accelerated
283         CODEC_OPTIONAL; // Codec support is optional
284 
toString(SupportClass supportRequirements)285         public static String toString(SupportClass supportRequirements) {
286             switch (supportRequirements) {
287                 case CODEC_ALL:
288                     return "CODEC_ALL";
289                 case CODEC_ANY:
290                     return "CODEC_ANY";
291                 case CODEC_DEFAULT:
292                     return "CODEC_DEFAULT";
293                 case CODEC_HW:
294                     return "CODEC_HW";
295                 case CODEC_SHOULD:
296                     return "CODEC_SHOULD";
297                 case CODEC_HW_RECOMMENDED:
298                     return "CODEC_HW_RECOMMENDED";
299                 case CODEC_OPTIONAL:
300                     return "CODEC_OPTIONAL";
301                 default:
302                     return "Unknown support class";
303             }
304         }
305     }
306 
307     public enum ComponentClass {
308         ALL,
309         SOFTWARE,
310         HARDWARE;
311 
toString(ComponentClass selectSwitch)312         public static String toString(ComponentClass selectSwitch) {
313             switch (selectSwitch) {
314                 case ALL:
315                     return "all";
316                 case SOFTWARE:
317                     return "software only";
318                 case HARDWARE:
319                     return "hardware accelerated";
320                 default:
321                     return "Unknown select switch";
322             }
323         }
324     }
325 
CodecTestBase(String encoder, String mediaType, String allTestParams)326     public CodecTestBase(String encoder, String mediaType, String allTestParams) {
327         mCodecName = encoder;
328         mMediaType = mediaType;
329         mAllTestParams = allTestParams;
330         mAsyncHandle = new CodecAsyncHandler();
331         mIsAudio = mMediaType.startsWith("audio/");
332         mIsVideo = mMediaType.startsWith("video/");
333     }
334 
335     protected final String mCodecName;
336     protected final String mMediaType;
337     protected final String mAllTestParams;  // logging
338 
339     protected final boolean mIsAudio;
340     protected final boolean mIsVideo;
341     protected final CodecAsyncHandler mAsyncHandle;
342     protected boolean mIsCodecInAsyncMode;
343     protected boolean mSawInputEOS;
344     protected boolean mSawOutputEOS;
345     protected boolean mSignalEOSWithLastFrame;
346     protected int mInputCount;
347     protected int mOutputCount;
348     protected long mPrevOutputPts;
349     protected boolean mSignalledOutFormatChanged;
350     protected MediaFormat mOutFormat;
351 
352     protected StringBuilder mTestConfig = new StringBuilder();
353     protected StringBuilder mTestEnv = new StringBuilder();
354 
355     protected boolean mSaveToMem;
356     protected OutputManager mOutputBuff;
357 
358     protected MediaCodec mCodec;
359     protected Surface mSurface;
360     // NOTE: mSurface is a place holder of current Surface used by the CodecTestBase.
361     // This doesn't own the handle. The ownership with instances that manage
362     // SurfaceView or TextureView, ... They hold the responsibility of calling release().
363     protected CodecTestActivity mActivity;
364 
365     public static final MediaCodecList MEDIA_CODEC_LIST_ALL;
366     public static final MediaCodecList MEDIA_CODEC_LIST_REGULAR;
367     static {
368         MEDIA_CODEC_LIST_ALL = new MediaCodecList(MediaCodecList.ALL_CODECS);
369         MEDIA_CODEC_LIST_REGULAR = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
370         IS_HDR_CAPTURE_SUPPORTED = isHDRCaptureSupported();
371         IS_HDR_EDITING_SUPPORTED = isHDREditingSupported();
372         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vp8", MediaFormat.MIMETYPE_VIDEO_VP8);
373         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vp9", MediaFormat.MIMETYPE_VIDEO_VP9);
374         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("av1", MediaFormat.MIMETYPE_VIDEO_AV1);
375         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("avc", MediaFormat.MIMETYPE_VIDEO_AVC);
376         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("hevc", MediaFormat.MIMETYPE_VIDEO_HEVC);
377         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mpeg4", MediaFormat.MIMETYPE_VIDEO_MPEG4);
378         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("h263", MediaFormat.MIMETYPE_VIDEO_H263);
379         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mpeg2", MediaFormat.MIMETYPE_VIDEO_MPEG2);
380         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vraw", MediaFormat.MIMETYPE_VIDEO_RAW);
381         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("amrnb", MediaFormat.MIMETYPE_AUDIO_AMR_NB);
382         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("amrwb", MediaFormat.MIMETYPE_AUDIO_AMR_WB);
383         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("mp3", MediaFormat.MIMETYPE_AUDIO_MPEG);
384         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("aac", MediaFormat.MIMETYPE_AUDIO_AAC);
385         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("vorbis", MediaFormat.MIMETYPE_AUDIO_VORBIS);
386         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("opus", MediaFormat.MIMETYPE_AUDIO_OPUS);
387         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("g711alaw", MediaFormat.MIMETYPE_AUDIO_G711_ALAW);
388         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("g711mlaw", MediaFormat.MIMETYPE_AUDIO_G711_MLAW);
389         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("araw", MediaFormat.MIMETYPE_AUDIO_RAW);
390         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("flac", MediaFormat.MIMETYPE_AUDIO_FLAC);
391         CODEC_SEL_KEY_MEDIA_TYPE_MAP.put("gsm", MediaFormat.MIMETYPE_AUDIO_MSGSM);
392 
393         android.os.Bundle args = InstrumentationRegistry.getArguments();
394         mediaTypeSelKeys = args.getString(MEDIA_TYPE_SEL_KEY);
395         codecPrefix = args.getString(CODEC_PREFIX_KEY);
396         mediaTypePrefix = args.getString(MEDIA_TYPE_PREFIX_KEY);
397 
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_SDR_PROFILES)398         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_SDR_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_SDR_PROFILES)399         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_SDR_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES)400         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES)401         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES)402         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES)403         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_SDR_PROFILES)404         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_SDR_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_SDR_PROFILES)405         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_SDR_PROFILES);
PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES)406         PROFILE_SDR_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES);
407 
PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HLG_PROFILES)408         PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HLG_PROFILES);
PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HLG_PROFILES)409         PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HLG_PROFILES);
PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HLG_PROFILES)410         PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HLG_PROFILES);
PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HLG_PROFILES)411         PROFILE_HLG_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HLG_PROFILES);
412 
PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PROFILES)413         PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PROFILES);
PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PROFILES)414         PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PROFILES);
PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PROFILES)415         PROFILE_HDR10_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PROFILES);
416 
PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PLUS_PROFILES)417         PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR10_PLUS_PROFILES);
PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PLUS_PROFILES)418         PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10_PLUS_PROFILES);
PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PLUS_PROFILES)419         PROFILE_HDR10_PLUS_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR10_PLUS_PROFILES);
420 
PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HDR_PROFILES)421         PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_HDR_PROFILES);
PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR_PROFILES)422         PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_HDR_PROFILES);
PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_PROFILES)423         PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_PROFILES);
PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_PROFILES)424         PROFILE_HDR_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_PROFILES);
425 
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_PROFILES)426         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_PROFILES)427         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES)428         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_H263, H263_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES)429         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG2, MPEG2_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES)430         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_MPEG4, MPEG4_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES)431         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP8, VP8_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES)432         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES)433         PROFILE_MAP.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES);
PROFILE_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES)434         PROFILE_MAP.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES);
435 
436         HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AV1);
437         HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AVC);
438         HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
439     }
440 
combine(int[] first, int[] second)441     static int[] combine(int[] first, int[] second) {
442         int[] result = Arrays.copyOf(first, first.length + second.length);
443         System.arraycopy(second, 0, result, first.length, second.length);
444         return result;
445     }
446 
isMediaTypeLossless(String mediaType)447     public static boolean isMediaTypeLossless(String mediaType) {
448         if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) return true;
449         if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)) return true;
450         return false;
451     }
452 
453     // some media types decode a pre-roll amount before playback. This would mean that decoding
454     // after seeking may not return the exact same values as would be obtained by decoding the
455     // stream straight through
isMediaTypeOutputUnAffectedBySeek(String mediaType)456     public static boolean isMediaTypeOutputUnAffectedBySeek(String mediaType) {
457         if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_FLAC)) return true;
458         if (mediaType.equals(MediaFormat.MIMETYPE_AUDIO_RAW)) return true;
459         if (mediaType.startsWith("video/")) return true;
460         return false;
461     }
462 
hasDecoder(String mediaType)463     public static boolean hasDecoder(String mediaType) {
464         return CodecTestBase.selectCodecs(mediaType, null, null, false).size() != 0;
465     }
466 
hasEncoder(String mediaType)467     public static boolean hasEncoder(String mediaType) {
468         return CodecTestBase.selectCodecs(mediaType, null, null, true).size() != 0;
469     }
470 
checkFormatSupport(String codecName, String mediaType, boolean isEncoder, ArrayList<MediaFormat> formats, String[] features, SupportClass supportRequirements)471     public static void checkFormatSupport(String codecName, String mediaType, boolean isEncoder,
472             ArrayList<MediaFormat> formats, String[] features, SupportClass supportRequirements)
473             throws IOException {
474         if (!areFormatsSupported(codecName, mediaType, formats)) {
475             switch (supportRequirements) {
476                 case CODEC_ALL:
477                     fail("format(s) not supported by codec: " + codecName + " for mediaType : "
478                             + mediaType + " formats: " + formats);
479                     break;
480                 case CODEC_ANY:
481                     if (selectCodecs(mediaType, formats, features, isEncoder).isEmpty()) {
482                         fail("format(s) not supported by any component for mediaType : " + mediaType
483                                 + " formats: " + formats);
484                     }
485                     break;
486                 case CODEC_DEFAULT:
487                     if (isDefaultCodec(codecName, mediaType, isEncoder)) {
488                         fail("format(s) not supported by default codec : " + codecName
489                                 + "for mediaType : " + mediaType + " formats: " + formats);
490                     }
491                     break;
492                 case CODEC_HW:
493                     if (isHardwareAcceleratedCodec(codecName)) {
494                         fail("format(s) not supported by codec: " + codecName + " for mediaType : "
495                                 + mediaType + " formats: " + formats);
496                     }
497                     break;
498                 case CODEC_SHOULD:
499                     Assume.assumeTrue(String.format("format(s) not supported by codec: %s for"
500                             + " mediaType : %s. It is recommended to support it",
501                             codecName, mediaType), false);
502                     break;
503                 case CODEC_HW_RECOMMENDED:
504                     Assume.assumeTrue(String.format(
505                             "format(s) not supported by codec: %s for mediaType : %s. It is %s "
506                                     + "recommended to support it", codecName, mediaType,
507                             isHardwareAcceleratedCodec(codecName) ? "strongly" : ""), false);
508                     break;
509                 case CODEC_OPTIONAL:
510                 default:
511                     // the later assumeTrue() ensures we skip the test for unsupported codecs
512                     break;
513             }
514             Assume.assumeTrue("format(s) not supported by codec: " + codecName + " for mediaType : "
515                     + mediaType, false);
516         }
517     }
518 
isFeatureSupported(String name, String mediaType, String feature)519     public static boolean isFeatureSupported(String name, String mediaType, String feature)
520             throws IOException {
521         MediaCodec codec = MediaCodec.createByCodecName(name);
522         MediaCodecInfo.CodecCapabilities codecCapabilities =
523                 codec.getCodecInfo().getCapabilitiesForType(mediaType);
524         boolean isSupported = codecCapabilities.isFeatureSupported(feature);
525         codec.release();
526         return isSupported;
527     }
528 
isHDRCaptureSupported()529     public static boolean isHDRCaptureSupported() {
530         // If the device supports HDR, hlg support should always return true
531         if (!MediaUtils.hasCamera()) return false;
532         CameraManager cm = CONTEXT.getSystemService(CameraManager.class);
533         try {
534             String[] cameraIds = cm.getCameraIdList();
535             for (String id : cameraIds) {
536                 CameraCharacteristics ch = cm.getCameraCharacteristics(id);
537                 int[] caps = ch.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
538                 if (IntStream.of(caps).anyMatch(x -> x
539                         == CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT)) {
540                     Set<Long> profiles =
541                             ch.get(CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES)
542                                     .getSupportedProfiles();
543                     if (profiles.contains(DynamicRangeProfiles.HLG10)) return true;
544                 }
545             }
546         } catch (CameraAccessException e) {
547             Log.e(LOG_TAG, "encountered " + e.getMessage()
548                     + " marking hdr capture to be available to catch your attention");
549             return true;
550         }
551         return false;
552     }
553 
isHDREditingSupported()554     public static boolean isHDREditingSupported() {
555         for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_REGULAR.getCodecInfos()) {
556             if (!codecInfo.isEncoder()) {
557                 continue;
558             }
559             for (String mediaType : codecInfo.getSupportedTypes()) {
560                 CodecCapabilities caps = codecInfo.getCapabilitiesForType(mediaType);
561                 if (caps != null && caps.isFeatureSupported(FEATURE_HdrEditing)) {
562                     return true;
563                 }
564             }
565         }
566         return false;
567     }
568 
doesAnyFormatHaveHDRProfile(String mediaType, ArrayList<MediaFormat> formats)569     public static boolean doesAnyFormatHaveHDRProfile(String mediaType,
570             ArrayList<MediaFormat> formats) {
571         int[] profileArray = PROFILE_HDR_MAP.get(mediaType);
572         if (profileArray != null) {
573             for (MediaFormat format : formats) {
574                 assertEquals(mediaType, format.getString(MediaFormat.KEY_MIME));
575                 int profile = format.getInteger(MediaFormat.KEY_PROFILE, -1);
576                 if (IntStream.of(profileArray).anyMatch(x -> x == profile)) return true;
577             }
578         }
579         return false;
580     }
581 
doesCodecSupportHDRProfile(String codecName, String mediaType)582     public static boolean doesCodecSupportHDRProfile(String codecName, String mediaType) {
583         int[] hdrProfiles = PROFILE_HDR_MAP.get(mediaType);
584         if (hdrProfiles == null) {
585             return false;
586         }
587         for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_REGULAR.getCodecInfos()) {
588             if (!codecName.equals(codecInfo.getName())) {
589                 continue;
590             }
591             CodecCapabilities caps = codecInfo.getCapabilitiesForType(mediaType);
592             if (caps == null) {
593                 return false;
594             }
595             for (CodecProfileLevel pl : caps.profileLevels) {
596                 if (IntStream.of(hdrProfiles).anyMatch(x -> x == pl.profile)) {
597                     return true;
598                 }
599             }
600         }
601         return false;
602     }
603 
canDisplaySupportHDRContent()604     public static boolean canDisplaySupportHDRContent() {
605         DisplayManager displayManager = CONTEXT.getSystemService(DisplayManager.class);
606         return displayManager.getDisplay(Display.DEFAULT_DISPLAY).getHdrCapabilities()
607                 .getSupportedHdrTypes().length > 0;
608     }
609 
areFormatsSupported(String name, String mediaType, ArrayList<MediaFormat> formats)610     public static boolean areFormatsSupported(String name, String mediaType,
611             ArrayList<MediaFormat> formats) throws IOException {
612         MediaCodec codec = MediaCodec.createByCodecName(name);
613         MediaCodecInfo.CodecCapabilities codecCapabilities =
614                 codec.getCodecInfo().getCapabilitiesForType(mediaType);
615         boolean isSupported = true;
616         if (formats != null) {
617             for (int i = 0; i < formats.size() && isSupported; i++) {
618                 isSupported = codecCapabilities.isFormatSupported(formats.get(i));
619             }
620         }
621         codec.release();
622         return isSupported;
623     }
624 
hasSupportForColorFormat(String name, String mediaType, int colorFormat)625     public static boolean hasSupportForColorFormat(String name, String mediaType, int colorFormat)
626             throws IOException {
627         MediaCodec codec = MediaCodec.createByCodecName(name);
628         MediaCodecInfo.CodecCapabilities cap =
629                 codec.getCodecInfo().getCapabilitiesForType(mediaType);
630         boolean hasSupport = false;
631         for (int c : cap.colorFormats) {
632             if (c == colorFormat) {
633                 hasSupport = true;
634                 break;
635             }
636         }
637         codec.release();
638         return hasSupport;
639     }
640 
isDefaultCodec(String codecName, String mediaType, boolean isEncoder)641     public static boolean isDefaultCodec(String codecName, String mediaType, boolean isEncoder)
642             throws IOException {
643         Map<String, String> mDefaultCodecs = isEncoder ? DEFAULT_ENCODERS : DEFAULT_DECODERS;
644         if (mDefaultCodecs.containsKey(mediaType)) {
645             return mDefaultCodecs.get(mediaType).equalsIgnoreCase(codecName);
646         }
647         MediaCodec codec = isEncoder ? MediaCodec.createEncoderByType(mediaType)
648                 : MediaCodec.createDecoderByType(mediaType);
649         boolean isDefault = codec.getName().equalsIgnoreCase(codecName);
650         mDefaultCodecs.put(mediaType, codec.getName());
651         codec.release();
652         return isDefault;
653     }
654 
isVendorCodec(String codecName)655     public static boolean isVendorCodec(String codecName) {
656         for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) {
657             if (codecName.equals(codecInfo.getName())) {
658                 return codecInfo.isVendor();
659             }
660         }
661         return false;
662     }
663 
isSoftwareCodec(String codecName)664     public static boolean isSoftwareCodec(String codecName) {
665         for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) {
666             if (codecName.equals(codecInfo.getName())) {
667                 return codecInfo.isSoftwareOnly();
668             }
669         }
670         return false;
671     }
672 
isHardwareAcceleratedCodec(String codecName)673     public static boolean isHardwareAcceleratedCodec(String codecName) {
674         for (MediaCodecInfo codecInfo : MEDIA_CODEC_LIST_ALL.getCodecInfos()) {
675             if (codecName.equals(codecInfo.getName())) {
676                 return codecInfo.isHardwareAccelerated();
677             }
678         }
679         return false;
680     }
681 
paramToString(Object[] param)682     protected static String paramToString(Object[] param) {
683         StringBuilder paramStr = new StringBuilder("[  ");
684         for (int j = 0; j < param.length - 1; j++) {
685             Object o = param[j];
686             if (o == null) {
687                 paramStr.append("null, ");
688             } else if (o instanceof String[]) {
689                 int length = Math.min(((String[]) o).length, 3);
690                 paramStr.append("{");
691                 for (int i = 0; i < length; i++) {
692                     paramStr.append(((String[]) o)[i]).append(", ");
693                 }
694                 paramStr.delete(paramStr.length() - 2, paramStr.length())
695                         .append(length == ((String[]) o).length ? "}, " : ", ... }, ");
696             } else if (o instanceof int[]) {
697                 paramStr.append("{");
698                 for (int i = 0; i < ((int[]) o).length; i++) {
699                     paramStr.append(((int[]) o)[i]).append(", ");
700                 }
701                 paramStr.delete(paramStr.length() - 2, paramStr.length()).append("}, ");
702             } else if (o instanceof Map) {
703                 int length = 0;
704                 paramStr.append("{ ");
705                 Map map = (Map) o;
706                 for (Object key : map.keySet()) {
707                     paramStr.append(key).append(" = ").append(map.get(key)).append(", ");
708                     length++;
709                     if (length > 1) break;
710                 }
711                 paramStr.delete(paramStr.length() - 2, paramStr.length())
712                         .append(length == map.size() ? "}, " : ", ... }, ");
713             } else if (o instanceof EncoderConfigParams[]) {
714                 int length = Math.min(((EncoderConfigParams[]) o).length, 3);
715                 paramStr.append("{");
716                 for (int i = 0; i < ((EncoderConfigParams[]) o).length; i++) {
717                     paramStr.append(((EncoderConfigParams[]) o)[i]).append(", ");
718                 }
719                 paramStr.delete(paramStr.length() - 2, paramStr.length())
720                         .append(length == ((EncoderConfigParams[]) o).length ? "}, " : ", ... }, ");
721             } else paramStr.append(o).append(", ");
722         }
723         paramStr.delete(paramStr.length() - 2, paramStr.length()).append("  ]");
724         return paramStr.toString();
725     }
726 
compileRequiredMediaTypeList(boolean isEncoder, boolean needAudio, boolean needVideo)727     public static ArrayList<String> compileRequiredMediaTypeList(boolean isEncoder,
728             boolean needAudio, boolean needVideo) {
729         Set<String> list = new HashSet<>();
730         if (!isEncoder) {
731             if (MediaUtils.hasAudioOutput() && needAudio) {
732                 // sec 5.1.2
733                 list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
734                 list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
735                 list.add(MediaFormat.MIMETYPE_AUDIO_MPEG);
736                 list.add(MediaFormat.MIMETYPE_AUDIO_VORBIS);
737                 list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
738                 list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
739             }
740             if (MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv()
741                     || MediaUtils.isAutomotive()) {
742                 // sec 2.2.2, 2.3.2, 2.5.2
743                 if (needAudio) {
744                     list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
745                 }
746                 if (needVideo) {
747                     list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
748                     list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
749                     list.add(MediaFormat.MIMETYPE_VIDEO_H263);
750                     list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
751                     list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
752                 }
753             }
754             if (MediaUtils.isHandheld() || MediaUtils.isTablet()) {
755                 // sec 2.2.2
756                 if (needAudio) {
757                     list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
758                     list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
759                 }
760                 if (needVideo) {
761                     list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
762                     if (IS_AT_LEAST_U) {
763                         list.add(MediaFormat.MIMETYPE_VIDEO_AV1);
764                     }
765                 }
766             }
767             if (MediaUtils.isTv() && needVideo) {
768                 // sec 2.3.2
769                 list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
770                 list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
771                 if (IS_AT_LEAST_U) {
772                     list.add(MediaFormat.MIMETYPE_VIDEO_AV1);
773                 }
774             }
775         } else {
776             if (MediaUtils.hasMicrophone() && needAudio) {
777                 // sec 5.1.1
778                 // TODO(b/154423550)
779                 // list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
780                 list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
781                 list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
782             }
783             if (MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv()
784                     || MediaUtils.isAutomotive()) {
785                 // sec 2.2.2, 2.3.2, 2.5.2
786                 if (needAudio) {
787                     list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
788                 }
789                 if (needVideo) {
790                     if ((MediaUtils.isHandheld() || MediaUtils.isTablet() || MediaUtils.isTv())
791                             && IS_AT_LEAST_U) {
792                         list.add(MediaFormat.MIMETYPE_VIDEO_AV1);
793                     }
794                     list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
795                     list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
796                 }
797             }
798             if ((MediaUtils.isHandheld() || MediaUtils.isTablet()) && needAudio) {
799                 // sec 2.2.2
800                 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
801                 list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
802             }
803         }
804         return new ArrayList<>(list);
805     }
806 
compileCompleteTestMediaTypesList(boolean isEncoder, boolean needAudio, boolean needVideo)807     private static ArrayList<String> compileCompleteTestMediaTypesList(boolean isEncoder,
808             boolean needAudio, boolean needVideo) {
809         ArrayList<String> mediaTypes = new ArrayList<>();
810         if (mediaTypeSelKeys == null) {
811             ArrayList<String> cddRequiredMediaTypesList =
812                     compileRequiredMediaTypeList(isEncoder, needAudio, needVideo);
813             MediaCodecInfo[] codecInfos = MEDIA_CODEC_LIST_REGULAR.getCodecInfos();
814             for (MediaCodecInfo codecInfo : codecInfos) {
815                 if (codecInfo.isEncoder() != isEncoder) continue;
816                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
817                 String[] types = codecInfo.getSupportedTypes();
818                 for (String type : types) {
819                     if (mediaTypePrefix != null && !type.startsWith(mediaTypePrefix)) {
820                         continue;
821                     }
822                     if (!needAudio && type.startsWith("audio/")) continue;
823                     if (!needVideo && type.startsWith("video/")) continue;
824                     if (!mediaTypes.contains(type)) {
825                         mediaTypes.add(type);
826                     }
827                 }
828             }
829             if (mediaTypePrefix != null) {
830                 return mediaTypes;
831             }
832             // feature_video_output is not exposed to package manager. Testing for video output
833             // ports, such as VGA, HDMI, DisplayPort, or a wireless port for display is also not
834             // direct.
835             /* sec 5.2: device implementations include an embedded screen display with the
836             diagonal length of at least 2.5 inches or include a video output port or declare the
837             support of a camera */
838             if (isEncoder && needVideo
839                     && (MediaUtils.hasCamera() || MediaUtils.getScreenSizeInInches() >= 2.5)
840                     && !mediaTypes.contains(MediaFormat.MIMETYPE_VIDEO_AVC)
841                     && !mediaTypes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
842                 // Add required cdd mediaTypes here so that respective codec tests fail.
843                 mediaTypes.add(MediaFormat.MIMETYPE_VIDEO_AVC);
844                 mediaTypes.add(MediaFormat.MIMETYPE_VIDEO_VP8);
845                 Log.e(LOG_TAG, "device must support at least one of VP8 or AVC video encoders");
846             }
847             for (String mediaType : cddRequiredMediaTypesList) {
848                 if (!mediaTypes.contains(mediaType)) {
849                     // Add required cdd mediaTypes here so that respective codec tests fail.
850                     mediaTypes.add(mediaType);
851                     Log.e(LOG_TAG, "no codec found for mediaType " + mediaType
852                             + " as required by cdd");
853                 }
854             }
855         } else {
856             for (Map.Entry<String, String> entry : CODEC_SEL_KEY_MEDIA_TYPE_MAP.entrySet()) {
857                 String key = entry.getKey();
858                 String value = entry.getValue();
859                 if (mediaTypeSelKeys.contains(key) && !mediaTypes.contains(value)) {
860                     mediaTypes.add(value);
861                 }
862             }
863         }
864         return mediaTypes;
865     }
866 
prepareParamList(List<Object[]> exhaustiveArgsList, boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs)867     public static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList,
868             boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs) {
869         return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo,
870                 mustTestAllCodecs, ComponentClass.ALL);
871     }
872 
prepareParamList(List<Object[]> exhaustiveArgsList, boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs, ComponentClass selectSwitch)873     public static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList,
874             boolean isEncoder, boolean needAudio, boolean needVideo, boolean mustTestAllCodecs,
875             ComponentClass selectSwitch) {
876         ArrayList<String> mediaTypes = compileCompleteTestMediaTypesList(isEncoder,
877                 needAudio, needVideo);
878         ArrayList<String> cddRequiredMediaTypesList =
879                 compileRequiredMediaTypeList(isEncoder, needAudio, needVideo);
880         final List<Object[]> argsList = new ArrayList<>();
881         int argLength = exhaustiveArgsList.get(0).length;
882         for (String mediaType : mediaTypes) {
883             ArrayList<String> totalListOfCodecs =
884                     selectCodecs(mediaType, null, null, isEncoder, selectSwitch);
885             ArrayList<String> listOfCodecs = new ArrayList<>();
886             if (codecPrefix != null) {
887                 for (String codec : totalListOfCodecs) {
888                     if (codec.startsWith(codecPrefix)) {
889                         listOfCodecs.add(codec);
890                     }
891                 }
892             } else {
893                 listOfCodecs = totalListOfCodecs;
894             }
895             if (mustTestAllCodecs && listOfCodecs.size() == 0 && codecPrefix == null) {
896                 listOfCodecs.add(INVALID_CODEC + mediaType);
897             }
898             boolean miss = true;
899             for (Object[] arg : exhaustiveArgsList) {
900                 if (mediaType.equals(arg[0])) {
901                     for (String codec : listOfCodecs) {
902                         Object[] argUpdate = new Object[argLength + 2];
903                         argUpdate[0] = codec;
904                         System.arraycopy(arg, 0, argUpdate, 1, argLength);
905                         argUpdate[argLength + 1] = paramToString(argUpdate);
906                         argsList.add(argUpdate);
907                     }
908                     miss = false;
909                 }
910             }
911             if (miss && mustTestAllCodecs) {
912                 if (!cddRequiredMediaTypesList.contains(mediaType)) {
913                     Log.w(LOG_TAG, "no test vectors available for optional mediaType type "
914                             + mediaType);
915                     continue;
916                 }
917                 for (String codec : listOfCodecs) {
918                     Object[] argUpdate = new Object[argLength + 2];
919                     argUpdate[0] = codec;
920                     argUpdate[1] = mediaType;
921                     System.arraycopy(exhaustiveArgsList.get(0), 1, argUpdate, 2, argLength - 1);
922                     argUpdate[argLength + 1] = paramToString(argUpdate);
923                     argsList.add(argUpdate);
924                 }
925             }
926         }
927         return argsList;
928     }
929 
selectCodecs(String mediaType, ArrayList<MediaFormat> formats, String[] features, boolean isEncoder)930     public static ArrayList<String> selectCodecs(String mediaType, ArrayList<MediaFormat> formats,
931             String[] features, boolean isEncoder) {
932         return selectCodecs(mediaType, formats, features, isEncoder, ComponentClass.ALL);
933     }
934 
selectCodecs(String mediaType, ArrayList<MediaFormat> formats, String[] features, boolean isEncoder, ComponentClass selectSwitch)935     public static ArrayList<String> selectCodecs(String mediaType, ArrayList<MediaFormat> formats,
936             String[] features, boolean isEncoder, ComponentClass selectSwitch) {
937         MediaCodecInfo[] codecInfos = MEDIA_CODEC_LIST_REGULAR.getCodecInfos();
938         ArrayList<String> listOfCodecs = new ArrayList<>();
939         for (MediaCodecInfo codecInfo : codecInfos) {
940             if (codecInfo.isEncoder() != isEncoder) continue;
941             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
942             if (selectSwitch == ComponentClass.HARDWARE && !codecInfo.isHardwareAccelerated()) {
943                 continue;
944             } else if (selectSwitch == ComponentClass.SOFTWARE && !codecInfo.isSoftwareOnly()) {
945                 continue;
946             }
947             String[] types = codecInfo.getSupportedTypes();
948             for (String type : types) {
949                 if (type.equalsIgnoreCase(mediaType)) {
950                     boolean isOk = true;
951                     MediaCodecInfo.CodecCapabilities codecCapabilities =
952                             codecInfo.getCapabilitiesForType(type);
953                     if (formats != null) {
954                         for (MediaFormat format : formats) {
955                             if (!codecCapabilities.isFormatSupported(format)) {
956                                 isOk = false;
957                                 break;
958                             }
959                         }
960                     }
961                     if (features != null) {
962                         for (String feature : features) {
963                             if (!codecCapabilities.isFeatureSupported(feature)) {
964                                 isOk = false;
965                                 break;
966                             }
967                         }
968                     }
969                     if (isOk) listOfCodecs.add(codecInfo.getName());
970                 }
971             }
972         }
973         return listOfCodecs;
974     }
975 
getWidth(MediaFormat format)976     public static int getWidth(MediaFormat format) {
977         int width = format.getInteger(MediaFormat.KEY_WIDTH, -1);
978         if (format.containsKey("crop-left") && format.containsKey("crop-right")) {
979             width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left");
980         }
981         return width;
982     }
983 
getHeight(MediaFormat format)984     public static int getHeight(MediaFormat format) {
985         int height = format.getInteger(MediaFormat.KEY_HEIGHT, -1);
986         if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) {
987             height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top");
988         }
989         return height;
990     }
991 
loadByteArrayFromString(final String str)992     public static byte[] loadByteArrayFromString(final String str) {
993         if (str == null) {
994             return null;
995         }
996         Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}");
997         Matcher matcher = pattern.matcher(str);
998         // allocate a large enough byte array first
999         byte[] tempArray = new byte[str.length() / 2];
1000         int i = 0;
1001         while (matcher.find()) {
1002             tempArray[i++] = (byte) Integer.parseInt(matcher.group(), 16);
1003         }
1004         return Arrays.copyOfRange(tempArray, 0, i);
1005     }
1006 
byteArrayToHexString(byte[] bytes)1007     public static String byteArrayToHexString(byte[] bytes) {
1008         final char[] hexArray =
1009                 {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
1010         char[] hexChars = new char[bytes.length * 3];
1011         int v;
1012         for (int j = 0; j < bytes.length; j++) {
1013             v = bytes[j] & 0xFF;
1014             hexChars[j * 3] = hexArray[v >>> 4];
1015             hexChars[j * 3 + 1] = hexArray[v & 0x0F];
1016             hexChars[j * 3 + 2] = ' ';
1017         }
1018         return new String(hexChars);
1019     }
1020 
enqueueInput(int bufferIndex)1021     protected abstract void enqueueInput(int bufferIndex) throws IOException;
1022 
dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info)1023     protected abstract void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info);
1024 
1025     @Rule
1026     public final TestName mTestName = new TestName();
1027 
1028     @Before
setUpCodecTestBase()1029     public void setUpCodecTestBase() {
1030         mTestConfig.setLength(0);
1031         mTestConfig.append("\n##################       Test Details        ####################\n");
1032         mTestConfig.append("Test Name :- ").append(mTestName.getMethodName()).append("\n");
1033         mTestConfig.append("Test Parameters :- ").append(mAllTestParams).append("\n");
1034         if (mCodecName != null && mCodecName.startsWith(INVALID_CODEC)) {
1035             fail("no valid component available for current test \n" + mTestConfig);
1036         }
1037     }
1038 
1039     @After
tearDownCodecTestBase()1040     public void tearDownCodecTestBase() {
1041         mSurface = null;
1042         if (mActivity != null) {
1043             mActivity.finish();
1044             mActivity = null;
1045         }
1046         if (mCodec != null) {
1047             mCodec.release();
1048             mCodec = null;
1049         }
1050     }
1051 
configureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame, boolean isEncoder)1052     protected void configureCodec(MediaFormat format, boolean isAsync,
1053             boolean signalEOSWithLastFrame, boolean isEncoder) {
1054         resetContext(isAsync, signalEOSWithLastFrame);
1055         mAsyncHandle.setCallBack(mCodec, isAsync);
1056         // signalEOS flag has nothing to do with configure. We are using this flag to try all
1057         // available configure apis
1058         if (signalEOSWithLastFrame) {
1059             mCodec.configure(format, mSurface, null,
1060                     isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0);
1061         } else {
1062             mCodec.configure(format, mSurface, isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0,
1063                     null);
1064         }
1065         mTestEnv.setLength(0);
1066         mTestEnv.append("###################      Test Environment       #####################\n");
1067         mTestEnv.append(String.format("Component under test :- %s \n", mCodecName));
1068         mTestEnv.append("Format under test :- ").append(format).append("\n");
1069         mTestEnv.append(String.format("Component operating in :- %s mode \n",
1070                 (isAsync ? "asynchronous" : "synchronous")));
1071         mTestEnv.append(String.format("Component received input eos :- %s \n",
1072                 (signalEOSWithLastFrame ? "with full buffer" : "with empty buffer")));
1073         if (ENABLE_LOGS) {
1074             Log.v(LOG_TAG, "codec configured");
1075         }
1076     }
1077 
getOutputManager()1078     public OutputManager getOutputManager() {
1079         return mOutputBuff;
1080     }
1081 
getOutputFormat()1082     public MediaFormat getOutputFormat() {
1083         return mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : mOutFormat;
1084     }
1085 
getOutputCount()1086     public int getOutputCount() {
1087         return mOutputCount;
1088     }
1089 
flushCodec()1090     protected void flushCodec() {
1091         mCodec.flush();
1092         // TODO(b/147576107): is it ok to clearQueues right away or wait for some signal
1093         mAsyncHandle.clearQueues();
1094         mSawInputEOS = false;
1095         mSawOutputEOS = false;
1096         mInputCount = 0;
1097         mOutputCount = 0;
1098         mPrevOutputPts = Long.MIN_VALUE;
1099         if (ENABLE_LOGS) {
1100             Log.v(LOG_TAG, "codec flushed");
1101         }
1102     }
1103 
reConfigureCodec(MediaFormat format, boolean isAsync, boolean signalEOSWithLastFrame, boolean isEncoder)1104     protected void reConfigureCodec(MediaFormat format, boolean isAsync,
1105             boolean signalEOSWithLastFrame, boolean isEncoder) {
1106         /* TODO(b/147348711) */
1107         if (false) mCodec.stop();
1108         else mCodec.reset();
1109         configureCodec(format, isAsync, signalEOSWithLastFrame, isEncoder);
1110     }
1111 
resetContext(boolean isAsync, boolean signalEOSWithLastFrame)1112     protected void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
1113         mAsyncHandle.resetContext();
1114         mIsCodecInAsyncMode = isAsync;
1115         mSawInputEOS = false;
1116         mSawOutputEOS = false;
1117         mSignalEOSWithLastFrame = signalEOSWithLastFrame;
1118         mInputCount = 0;
1119         mOutputCount = 0;
1120         mPrevOutputPts = Long.MIN_VALUE;
1121         mSignalledOutFormatChanged = false;
1122     }
1123 
enqueueEOS(int bufferIndex)1124     protected void enqueueEOS(int bufferIndex) {
1125         if (!mSawInputEOS) {
1126             mCodec.queueInputBuffer(bufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
1127             mSawInputEOS = true;
1128             if (ENABLE_LOGS) {
1129                 Log.v(LOG_TAG, "Queued End of Stream");
1130             }
1131         }
1132     }
1133 
doWork(int frameLimit)1134     protected void doWork(int frameLimit) throws InterruptedException, IOException {
1135         int frameCount = 0;
1136         if (mIsCodecInAsyncMode) {
1137             // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
1138             while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < frameLimit) {
1139                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
1140                 if (element != null) {
1141                     int bufferID = element.first;
1142                     MediaCodec.BufferInfo info = element.second;
1143                     if (info != null) {
1144                         // <id, info> corresponds to output callback. Handle it accordingly
1145                         dequeueOutput(bufferID, info);
1146                     } else {
1147                         // <id, null> corresponds to input callback. Handle it accordingly
1148                         enqueueInput(bufferID);
1149                         frameCount++;
1150                     }
1151                 }
1152             }
1153         } else {
1154             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
1155             // dequeue output after inputEOS is expected to be done in waitForAllOutputs()
1156             while (!mSawInputEOS && frameCount < frameLimit) {
1157                 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
1158                 if (outputBufferId >= 0) {
1159                     dequeueOutput(outputBufferId, outInfo);
1160                 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
1161                     mOutFormat = mCodec.getOutputFormat();
1162                     mSignalledOutFormatChanged = true;
1163                 }
1164                 int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
1165                 if (inputBufferId != -1) {
1166                     enqueueInput(inputBufferId);
1167                     frameCount++;
1168                 }
1169             }
1170         }
1171     }
1172 
queueEOS()1173     protected void queueEOS() throws InterruptedException {
1174         if (mIsCodecInAsyncMode) {
1175             while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
1176                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
1177                 if (element != null) {
1178                     int bufferID = element.first;
1179                     MediaCodec.BufferInfo info = element.second;
1180                     if (info != null) {
1181                         dequeueOutput(bufferID, info);
1182                     } else {
1183                         enqueueEOS(element.first);
1184                     }
1185                 }
1186             }
1187         } else {
1188             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
1189             while (!mSawInputEOS) {
1190                 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
1191                 if (outputBufferId >= 0) {
1192                     dequeueOutput(outputBufferId, outInfo);
1193                 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
1194                     mOutFormat = mCodec.getOutputFormat();
1195                     mSignalledOutFormatChanged = true;
1196                 }
1197                 int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
1198                 if (inputBufferId != -1) {
1199                     enqueueEOS(inputBufferId);
1200                 }
1201             }
1202         }
1203     }
1204 
waitForAllOutputs()1205     protected void waitForAllOutputs() throws InterruptedException {
1206         if (mIsCodecInAsyncMode) {
1207             while (!mAsyncHandle.hasSeenError() && !mSawOutputEOS) {
1208                 Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getOutput();
1209                 if (element != null) {
1210                     dequeueOutput(element.first, element.second);
1211                 }
1212             }
1213         } else {
1214             MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
1215             while (!mSawOutputEOS) {
1216                 int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
1217                 if (outputBufferId >= 0) {
1218                     dequeueOutput(outputBufferId, outInfo);
1219                 } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
1220                     mOutFormat = mCodec.getOutputFormat();
1221                     mSignalledOutFormatChanged = true;
1222                 }
1223             }
1224         }
1225         validateTestState();
1226     }
1227 
validateTestState()1228     void validateTestState() {
1229         assertFalse("Encountered error in async mode. \n" + mTestConfig + mTestEnv
1230                 + mAsyncHandle.getErrMsg(), mAsyncHandle.hasSeenError());
1231         if (mInputCount > 0) {
1232             assertTrue(String.format("fed %d input frames, received no output frames \n",
1233                     mInputCount) + mTestConfig + mTestEnv, mOutputCount > 0);
1234         }
1235         /*if (mInputCount == 0 && mInputCount != mOutputCount) {
1236             String msg = String.format("The number of output frames received is not same as number "
1237                             + "of input frames queued. Output count is %d, Input count is %d \n",
1238                     mOutputCount, mInputCount);
1239             // check the pts lists to see what frames are dropped, the below call is needed to
1240             // get useful error messages
1241             boolean unused = mOutputBuff.isOutPtsListIdenticalToInpPtsList(true);
1242             fail(msg + mTestConfig + mTestEnv + mOutputBuff.getErrMsg());
1243         }*/
1244     }
1245 
insertHdrDynamicInfo(byte[] info)1246     protected void insertHdrDynamicInfo(byte[] info) {
1247         final Bundle params = new Bundle();
1248         params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info);
1249         mCodec.setParameters(params);
1250     }
1251 
isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat)1252     public boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
1253         if (inpFormat == null || outFormat == null) return false;
1254         String inpMediaType = inpFormat.getString(MediaFormat.KEY_MIME);
1255         String outMediaType = outFormat.getString(MediaFormat.KEY_MIME);
1256         // not comparing input and output mediaTypes because for a codec, mediaType is raw on one
1257         // side and encoded type on the other
1258         if (outMediaType.startsWith("audio/")) {
1259             return (inpFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -1)
1260                     == outFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT, -2))
1261                     && (inpFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE, -1)
1262                     == outFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE, -2))
1263                     && inpMediaType.startsWith("audio/");
1264         } else if (outMediaType.startsWith("video/")) {
1265             return getWidth(inpFormat) == getWidth(outFormat)
1266                     && getHeight(inpFormat) == getHeight(outFormat)
1267                     && inpMediaType.startsWith("video/");
1268         }
1269         return true;
1270     }
1271 
validateMetrics(String codec)1272     protected PersistableBundle validateMetrics(String codec) {
1273         PersistableBundle metrics = mCodec.getMetrics();
1274         assertNotNull("error! MediaCodec.getMetrics() returns null \n" + mTestConfig + mTestEnv,
1275                 metrics);
1276         assertEquals("error! metrics#MetricsConstants.CODEC is not as expected \n" + mTestConfig
1277                 + mTestEnv, metrics.getString(MediaCodec.MetricsConstants.CODEC), codec);
1278         assertEquals("error! metrics#MetricsConstants.MODE is not as expected \n" + mTestConfig
1279                         + mTestEnv, mIsAudio ? MediaCodec.MetricsConstants.MODE_AUDIO :
1280                         MediaCodec.MetricsConstants.MODE_VIDEO,
1281                 metrics.getString(MediaCodec.MetricsConstants.MODE));
1282         return metrics;
1283     }
1284 
validateMetrics(String codec, MediaFormat format)1285     protected PersistableBundle validateMetrics(String codec, MediaFormat format) {
1286         PersistableBundle metrics = validateMetrics(codec);
1287         if (mIsVideo) {
1288             assertEquals("error! metrics#MetricsConstants.WIDTH is not as expected\n" + mTestConfig
1289                             + mTestEnv, metrics.getInt(MediaCodec.MetricsConstants.WIDTH),
1290                     getWidth(format));
1291             assertEquals("error! metrics#MetricsConstants.HEIGHT is not as expected\n" + mTestConfig
1292                             + mTestEnv, metrics.getInt(MediaCodec.MetricsConstants.HEIGHT),
1293                     getHeight(format));
1294         }
1295         assertEquals("error! metrics#MetricsConstants.SECURE is not as expected\n" + mTestConfig
1296                 + mTestEnv, 0, metrics.getInt(MediaCodec.MetricsConstants.SECURE));
1297         return metrics;
1298     }
1299 
validateColorAspects(MediaFormat fmt, int range, int standard, int transfer)1300     public void validateColorAspects(MediaFormat fmt, int range, int standard, int transfer) {
1301         int colorRange = fmt.getInteger(MediaFormat.KEY_COLOR_RANGE, UNSPECIFIED);
1302         int colorStandard = fmt.getInteger(MediaFormat.KEY_COLOR_STANDARD, UNSPECIFIED);
1303         int colorTransfer = fmt.getInteger(MediaFormat.KEY_COLOR_TRANSFER, UNSPECIFIED);
1304         if (range > UNSPECIFIED) {
1305             assertEquals("error! color range mismatch \n" + mTestConfig + mTestEnv, range,
1306                     colorRange);
1307         }
1308         if (standard > UNSPECIFIED) {
1309             assertEquals("error! color standard mismatch \n" + mTestConfig + mTestEnv, standard,
1310                     colorStandard);
1311         }
1312         if (transfer > UNSPECIFIED) {
1313             assertEquals("error! color transfer mismatch \n" + mTestConfig + mTestEnv, transfer,
1314                     colorTransfer);
1315         }
1316     }
1317 
validateHDRInfo(String hdrInfoKey, ByteBuffer hdrInfoRef, ByteBuffer hdrInfoTest, Long framePts)1318     protected void validateHDRInfo(String hdrInfoKey, ByteBuffer hdrInfoRef, ByteBuffer hdrInfoTest,
1319             Long framePts) {
1320         if (!hdrInfoRef.equals(hdrInfoTest)) {
1321             StringBuilder msg = new StringBuilder(
1322                     "###################       Error Details         #####################\n");
1323             byte[] ref = new byte[hdrInfoRef.capacity()];
1324             hdrInfoRef.get(ref);
1325             hdrInfoRef.rewind();
1326             byte[] test = new byte[hdrInfoTest.capacity()];
1327             hdrInfoTest.get(test);
1328             hdrInfoTest.rewind();
1329             msg.append("ref info :- \n");
1330             for (byte b : ref) {
1331                 msg.append(String.format("%2x ", b));
1332             }
1333             msg.append("\ntest info :- \n");
1334             for (byte b : test) {
1335                 msg.append(String.format("%2x ", b));
1336             }
1337             fail("Frame pts " + framePts + ": error! mismatch seen between ref and test info of "
1338                     + hdrInfoKey + "\n" + mTestConfig + mTestEnv + msg);
1339         }
1340     }
1341 
validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef, Long framePts)1342     protected void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef,
1343             Long framePts) {
1344         ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null);
1345         assertNotNull("error! no " + hdrInfoKey + " present in format : " + fmt + "\n "
1346                 + mTestConfig + mTestEnv, hdrInfo);
1347         validateHDRInfo(hdrInfoKey, hdrInfoRef, hdrInfo, framePts);
1348     }
1349 
setUpSurface(CodecTestActivity activity)1350     protected void setUpSurface(CodecTestActivity activity) throws InterruptedException {
1351         activity.waitTillSurfaceIsCreated();
1352         mSurface = activity.getSurface();
1353         assertNotNull("Surface created is null \n" + mTestConfig + mTestEnv, mSurface);
1354         assertTrue("Surface created is invalid \n" + mTestConfig + mTestEnv, mSurface.isValid());
1355     }
1356 }
1357