1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.media.decoder.cts; 18 19 import static org.junit.Assert.assertTrue; 20 import static org.junit.Assert.fail; 21 22 import android.content.res.AssetFileDescriptor; 23 import android.media.MediaCodec; 24 import android.media.MediaCodec.BufferInfo; 25 import android.media.MediaCodecInfo; 26 import android.media.MediaExtractor; 27 import android.media.MediaFormat; 28 import android.media.cts.MediaHeavyPresubmitTest; 29 import android.media.cts.TestUtils; 30 import android.os.Bundle; 31 import android.platform.test.annotations.AppModeFull; 32 import android.util.Log; 33 import android.view.Surface; 34 35 import com.android.compatibility.common.util.ApiTest; 36 import com.android.compatibility.common.util.CddTest; 37 import com.android.compatibility.common.util.MediaUtils; 38 import com.android.compatibility.common.util.Preconditions; 39 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.Parameterized; 43 44 import java.nio.ByteBuffer; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collection; 48 import java.util.List; 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.TimeUnit; 51 52 @MediaHeavyPresubmitTest 53 @AppModeFull(reason = "There should be no instant apps specific behavior related to decoders") 54 @RunWith(Parameterized.class) 55 public class HDRDecoderTest extends HDRDecoderTestBase { 56 private static final String TAG = "HDRDecoderTest"; 57 58 @Parameterized.Parameter(0) 59 public String mCodecName; 60 61 @Parameterized.Parameter(1) 62 public String mTestId; 63 64 @Parameterized.Parameter(2) 65 public String mMediaType; 66 67 @Parameterized.Parameter(3) 68 public String mInputFile; 69 70 @Parameterized.Parameter(4) 71 public String mHdrStaticInfo; 72 73 @Parameterized.Parameter(5) 74 public String[] mHdrDynamicInfo; 75 76 @Parameterized.Parameter(6) 77 public boolean mMetaDataInContainer; 78 getHdrProfile(String mediaType, boolean dynamic)79 private static int getHdrProfile(String mediaType, boolean dynamic) { 80 int profile = 0; 81 if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mediaType)) { 82 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus 83 : MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10; 84 } else if (MediaFormat.MIMETYPE_VIDEO_VP9.equals(mediaType)) { 85 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR10Plus 86 : MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR; 87 } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mediaType)) { 88 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10Plus 89 : MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10; 90 } else { 91 Log.e(TAG, "Unsupported mediaType " + mediaType); 92 } 93 return profile; 94 } 95 prepareParamList(List<Object[]> exhaustiveArgsList)96 private static List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) { 97 final List<Object[]> argsList = new ArrayList<>(); 98 int argLength = exhaustiveArgsList.get(0).length; 99 for (Object[] arg : exhaustiveArgsList) { 100 String mediaType = (String) arg[0]; 101 boolean dynamic = (String[]) arg[3] != null; 102 103 MediaFormat format = new MediaFormat(); 104 format.setString(MediaFormat.KEY_MIME, mediaType); 105 format.setInteger(MediaFormat.KEY_PROFILE, getHdrProfile(mediaType, dynamic)); 106 107 String[] decoderNames = MediaUtils.getDecoderNames(format); 108 109 for (String decoder : decoderNames) { 110 if (TestUtils.isMainlineCodec(decoder)) { 111 if (!TestUtils.isTestingModules()) { 112 Log.i(TAG, "not testing modules, skip module codec " + decoder); 113 continue; 114 } 115 } else { 116 if (TestUtils.isTestingModules()) { 117 Log.i(TAG, "testing modules, skip non-module codec " + decoder); 118 continue; 119 } 120 } 121 Object[] testArgs = new Object[argLength + 2]; 122 testArgs[0] = decoder; 123 testArgs[1] = dynamic ? "dynamic" : "static"; 124 System.arraycopy(arg, 0, testArgs, 2, argLength); 125 argsList.add(testArgs); 126 } 127 } 128 return argsList; 129 } 130 131 @Parameterized.Parameters(name = "{index}_{0}_{1}_{2}") input()132 public static Collection<Object[]> input() { 133 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 134 {MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_RES, AV1_HDR_STATIC_INFO, null, false}, 135 {MediaFormat.MIMETYPE_VIDEO_HEVC, H265_HDR10_RES, H265_HDR10_STATIC_INFO, null, 136 false}, 137 {MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_RES, VP9_HDR_STATIC_INFO, null, true}, 138 {MediaFormat.MIMETYPE_VIDEO_HEVC, H265_HDR10PLUS_RES, H265_HDR10PLUS_STATIC_INFO, 139 H265_HDR10PLUS_DYNAMIC_INFO, false}, 140 {MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10PLUS_RES, VP9_HDR10PLUS_STATIC_INFO, 141 VP9_HDR10PLUS_DYNAMIC_INFO, true}, 142 }); 143 144 return prepareParamList(exhaustiveArgsList); 145 } 146 verifyHdrStaticInfo(String reason, MediaFormat format, String pattern)147 private void verifyHdrStaticInfo(String reason, MediaFormat format, String pattern) { 148 ByteBuffer staticMetadataBuffer = format.containsKey("hdr-static-info") ? 149 format.getByteBuffer("hdr-static-info") : null; 150 assertTrue(reason + ": empty", 151 staticMetadataBuffer != null && staticMetadataBuffer.remaining() > 0); 152 assertTrue(reason + ": mismatch", 153 Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array())); 154 } 155 verifyHdrDynamicInfo(String reason, MediaFormat format, String pattern)156 private void verifyHdrDynamicInfo(String reason, MediaFormat format, String pattern) { 157 ByteBuffer hdr10PlusInfoBuffer = format.containsKey(MediaFormat.KEY_HDR10_PLUS_INFO) ? 158 format.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO) : null; 159 assertTrue(reason + ":empty", 160 hdr10PlusInfoBuffer != null && hdr10PlusInfoBuffer.remaining() > 0); 161 assertTrue(reason + ": mismatch", 162 Arrays.equals(loadByteArrayFromString(pattern), hdr10PlusInfoBuffer.array())); 163 } 164 165 @CddTest(requirements = {"5.3.5/C-3-1", "5.3.7/C-4-1", "5.3.9/C-3-1"}) 166 @ApiTest(apis = {"android.media.MediaFormat#KEY_HDR_STATIC_INFO", 167 "android.media.MediaFormat#KEY_HDR10_PLUS_INFO"}) 168 @Test testHdrMetadata()169 public void testHdrMetadata() throws Exception { 170 AssetFileDescriptor infd = null; 171 final boolean dynamic = mHdrDynamicInfo != null; 172 173 Preconditions.assertTestFileExists(MEDIA_DIR + mInputFile); 174 175 mExtractor.setDataSource(MEDIA_DIR + mInputFile); 176 177 MediaFormat format = null; 178 int trackIndex = -1; 179 for (int i = 0; i < mExtractor.getTrackCount(); i++) { 180 format = mExtractor.getTrackFormat(i); 181 if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) { 182 trackIndex = i; 183 break; 184 } 185 } 186 187 assertTrue("Extractor failed to extract video track", 188 format != null && trackIndex >= 0); 189 if (mMetaDataInContainer) { 190 verifyHdrStaticInfo("Extractor failed to extract static info", format, 191 mHdrStaticInfo); 192 } 193 194 mExtractor.selectTrack(trackIndex); 195 Log.v(TAG, "format " + format); 196 197 String mime = format.getString(MediaFormat.KEY_MIME); 198 format.setInteger(MediaFormat.KEY_PROFILE, getHdrProfile(mime, dynamic)); 199 200 final Surface surface = getActivity().getSurfaceHolder().getSurface(); 201 202 Log.d(TAG, "Testing candicate decoder " + mCodecName); 203 CountDownLatch latch = new CountDownLatch(1); 204 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 205 206 mDecoder = MediaCodec.createByCodecName(mCodecName); 207 mDecoder.setCallback(new MediaCodec.Callback() { 208 boolean mInputEOS; 209 boolean mOutputReceived; 210 int mInputCount; 211 int mOutputCount; 212 213 @Override 214 public void onOutputBufferAvailable( 215 MediaCodec codec, int index, BufferInfo info) { 216 if (mOutputReceived) { 217 return; 218 } 219 220 MediaFormat bufferFormat = codec.getOutputFormat(index); 221 Log.i(TAG, "got output buffer: format " + bufferFormat); 222 223 verifyHdrStaticInfo("Output buffer has wrong static info", 224 bufferFormat, mHdrStaticInfo); 225 226 if (!dynamic) { 227 codec.releaseOutputBuffer(index, true); 228 229 mOutputReceived = true; 230 latch.countDown(); 231 } else { 232 ByteBuffer hdr10plus = 233 bufferFormat.containsKey(MediaFormat.KEY_HDR10_PLUS_INFO) 234 ? bufferFormat.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO) 235 : null; 236 237 verifyHdrDynamicInfo("Output buffer has wrong hdr10+ info", 238 bufferFormat, mHdrDynamicInfo[mOutputCount]); 239 240 codec.releaseOutputBuffer(index, true); 241 242 mOutputCount++; 243 if (mOutputCount >= mHdrDynamicInfo.length) { 244 mOutputReceived = true; 245 latch.countDown(); 246 } 247 } 248 } 249 250 @Override 251 public void onInputBufferAvailable(MediaCodec codec, int index) { 252 // keep queuing until input EOS, or first output buffer received. 253 if (mInputEOS || mOutputReceived) { 254 return; 255 } 256 257 ByteBuffer inputBuffer = codec.getInputBuffer(index); 258 259 if (mExtractor.getSampleTrackIndex() == -1) { 260 codec.queueInputBuffer( 261 index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 262 mInputEOS = true; 263 } else { 264 int size = mExtractor.readSampleData(inputBuffer, 0); 265 long timestamp = mExtractor.getSampleTime(); 266 mExtractor.advance(); 267 268 if (dynamic && mMetaDataInContainer) { 269 final Bundle params = new Bundle(); 270 // TODO: extractor currently doesn't extract the dynamic metadata. 271 // Send in the test pattern for now to test the metadata propagation. 272 byte[] info = loadByteArrayFromString(mHdrDynamicInfo[mInputCount]); 273 params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); 274 codec.setParameters(params); 275 mInputCount++; 276 if (mInputCount >= mHdrDynamicInfo.length) { 277 mInputEOS = true; 278 } 279 } 280 codec.queueInputBuffer(index, 0, size, timestamp, 0); 281 } 282 } 283 284 @Override 285 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 286 Log.e(TAG, "got codec exception", e); 287 } 288 289 @Override 290 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 291 Log.i(TAG, "got output format: " + format); 292 verifyHdrStaticInfo("Output format has wrong static info", 293 format, mHdrStaticInfo); 294 } 295 }); 296 mDecoder.configure(format, surface, null/*crypto*/, 0/*flags*/); 297 mDecoder.start(); 298 try { 299 assertTrue(latch.await(2000, TimeUnit.MILLISECONDS)); 300 } catch (InterruptedException e) { 301 fail("playback interrupted"); 302 } 303 mDecoder.stop(); 304 } 305 } 306