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