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.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 import static org.junit.Assume.assumeFalse; 23 24 import android.content.res.AssetFileDescriptor; 25 import android.hardware.display.DisplayManager; 26 import android.media.MediaCodec; 27 import android.media.MediaCodec.BufferInfo; 28 import android.media.MediaCodecInfo; 29 import android.media.MediaExtractor; 30 import android.media.MediaFormat; 31 import android.media.cts.MediaHeavyPresubmitTest; 32 import android.media.cts.MediaTestBase; 33 import android.media.cts.TestUtils; 34 import android.os.Bundle; 35 import android.platform.test.annotations.AppModeFull; 36 import android.util.Log; 37 import android.view.Display; 38 import android.view.Surface; 39 40 import com.android.compatibility.common.util.ApiTest; 41 import com.android.compatibility.common.util.CddTest; 42 import com.android.compatibility.common.util.MediaUtils; 43 import com.android.compatibility.common.util.Preconditions; 44 45 import org.junit.After; 46 import org.junit.Before; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.junit.runners.Parameterized; 50 51 import java.nio.ByteBuffer; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Collection; 55 import java.util.List; 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.TimeUnit; 58 import java.util.regex.Matcher; 59 import java.util.regex.Pattern; 60 61 @MediaHeavyPresubmitTest 62 @AppModeFull(reason = "There should be no instant apps specific behavior related to decoders") 63 @RunWith(Parameterized.class) 64 public class HDRDecoderTest extends MediaTestBase { 65 private static final String TAG = "HDRDecoderTest"; 66 private static final String MEDIA_DIR = WorkDir.getMediaDirString(); 67 private static final String VP9_HDR_RES = "video_1280x720_vp9_hdr_static_3mbps.mkv"; 68 private static final String VP9_HDR_STATIC_INFO = 69 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 70 "40 e8 03 64 00 e8 03 2c 01 " ; 71 72 private static final String AV1_HDR_RES = "video_1280x720_av1_hdr_static_3mbps.webm"; 73 private static final String AV1_HDR_STATIC_INFO = 74 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 75 "40 e8 03 64 00 e8 03 2c 01 " ; 76 77 // Expected value of MediaFormat.KEY_HDR_STATIC_INFO key. 78 // The associated value is a ByteBuffer. This buffer contains the raw contents of the 79 // Static Metadata Descriptor (including the descriptor ID) of an HDMI Dynamic Range and 80 // Mastering InfoFrame as defined by CTA-861.3. 81 // Media frameworks puts the display primaries in RGB order, here we verify the three 82 // primaries are indeed in this order and fail otherwise. 83 private static final String H265_HDR10_RES = "video_1280x720_hevc_hdr10_static_3mbps.mp4"; 84 private static final String H265_HDR10_STATIC_INFO = 85 "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42" + 86 "40 e8 03 00 00 e8 03 90 01 " ; 87 88 private static final String VP9_HDR10PLUS_RES = "video_bikes_hdr10plus.webm"; 89 private static final String VP9_HDR10PLUS_STATIC_INFO = 90 "00 4c 1d b8 0b d0 84 80 3e c0 33 c4 86 12 3d 42" + 91 "40 e8 03 32 00 e8 03 c8 00 " ; 92 // TODO: Use some manually extracted metadata for now. 93 // MediaExtractor currently doesn't have an API for extracting 94 // the dynamic metadata. Get the metadata from extractor when 95 // it's supported. 96 private static final String[] VP9_HDR10PLUS_DYNAMIC_INFO = new String[] { 97 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 98 "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + 99 "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + 100 "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00" , 101 102 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 103 "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + 104 "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + 105 "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00" , 106 107 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 108 "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + 109 "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + 110 "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00" , 111 112 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 113 "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + 114 "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + 115 "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00" , 116 }; 117 118 private static final String H265_HDR10PLUS_RES = "video_h265_hdr10plus.mp4"; 119 private static final String H265_HDR10PLUS_STATIC_INFO = 120 "00 4c 1d b8 0b d0 84 80 3e c2 33 c4 86 13 3d 42" + 121 "40 e8 03 32 00 e8 03 c8 00 " ; 122 private static final String[] H265_HDR10PLUS_DYNAMIC_INFO = new String[] { 123 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 124 "0f 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 a1" + 125 "90 03 9a 58 0b 6a d0 23 2a f8 40 8b 18 9c 18 00" + 126 "40 78 13 64 cf 78 ed cc bf 5a de f9 8e c7 c3 00" , 127 128 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 129 "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 a1" + 130 "90 03 9a 58 0b 6a d0 23 2a f8 40 8b 18 9c 18 00" + 131 "40 78 13 64 cf 78 ed cc bf 5a de f9 8e c7 c3 00" , 132 133 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 134 "0f 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 a1" + 135 "90 03 9a 58 0b 6a d0 23 2a f8 40 8b 18 9c 18 00" + 136 "40 78 13 64 cf 78 ed cc bf 5a de f9 8e c7 c3 00" , 137 138 "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + 139 "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 a1" + 140 "90 03 9a 58 0b 6a d0 23 2a f8 40 8b 18 9c 18 00" + 141 "40 78 13 64 cf 78 ed cc bf 5a de f9 8e c7 c3 00" 142 }; 143 144 private DisplayManager mDisplayManager; 145 private MediaExtractor mExtractor = null; 146 private MediaCodec mDecoder = null; 147 @Parameterized.Parameter(0) 148 public String mCodecName; 149 150 @Parameterized.Parameter(1) 151 public String mTestId; 152 153 @Parameterized.Parameter(2) 154 public String mMediaType; 155 156 @Parameterized.Parameter(3) 157 public String mInputFile; 158 159 @Parameterized.Parameter(4) 160 public String mHdrStaticInfo; 161 162 @Parameterized.Parameter(5) 163 public String[] mHdrDynamicInfo; 164 165 @Parameterized.Parameter(6) 166 public boolean mMetaDataInContainer; getHdrProfile(String mediaType, boolean dynamic)167 static int getHdrProfile(String mediaType, boolean dynamic) { 168 int profile = 0; 169 if (MediaFormat.MIMETYPE_VIDEO_HEVC.equals(mediaType)) { 170 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10Plus 171 : MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10HDR10; 172 } else if (MediaFormat.MIMETYPE_VIDEO_VP9.equals(mediaType)) { 173 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR10Plus 174 : MediaCodecInfo.CodecProfileLevel.VP9Profile2HDR; 175 } else if (MediaFormat.MIMETYPE_VIDEO_AV1.equals(mediaType)) { 176 profile = dynamic ? MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10Plus 177 : MediaCodecInfo.CodecProfileLevel.AV1ProfileMain10HDR10; 178 } else { 179 Log.e(TAG, "Unsupported mediaType " + mediaType); 180 } 181 return profile; 182 } prepareParamList(List<Object[]> exhaustiveArgsList)183 static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) { 184 final List<Object[]> argsList = new ArrayList<>(); 185 int argLength = exhaustiveArgsList.get(0).length; 186 for (Object[] arg : exhaustiveArgsList) { 187 String mediaType = (String)arg[0]; 188 boolean dynamic = (String[])arg[3] != null; 189 190 MediaFormat format = new MediaFormat(); 191 format.setString(MediaFormat.KEY_MIME, mediaType); 192 format.setInteger(MediaFormat.KEY_PROFILE, getHdrProfile(mediaType, dynamic)); 193 194 String[] decoderNames = MediaUtils.getDecoderNames(format); 195 196 for (String decoder : decoderNames) { 197 if (TestUtils.isMainlineCodec(decoder)) { 198 if (!TestUtils.isTestingModules()) { 199 Log.i(TAG, "not testing modules, skip module codec " + decoder); 200 continue; 201 } 202 } else { 203 if (TestUtils.isTestingModules()) { 204 Log.i(TAG, "testing modules, skip non-module codec " + decoder); 205 continue; 206 } 207 } 208 Object[] testArgs = new Object[argLength + 2]; 209 testArgs[0] = decoder; 210 testArgs[1] = dynamic ? "dynamic" : "static"; 211 System.arraycopy(arg, 0, testArgs, 2, argLength); 212 argsList.add(testArgs); 213 } 214 } 215 return argsList; 216 } 217 218 @Parameterized.Parameters(name = "{index}_{0}_{1}_{2}") input()219 public static Collection<Object[]> input() { 220 final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ 221 {MediaFormat.MIMETYPE_VIDEO_AV1, AV1_HDR_RES, AV1_HDR_STATIC_INFO, null, false}, 222 {MediaFormat.MIMETYPE_VIDEO_HEVC, H265_HDR10_RES, H265_HDR10_STATIC_INFO, null, 223 false}, 224 {MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR_RES, VP9_HDR_STATIC_INFO, null, true}, 225 {MediaFormat.MIMETYPE_VIDEO_HEVC, H265_HDR10PLUS_RES, H265_HDR10PLUS_STATIC_INFO, 226 H265_HDR10PLUS_DYNAMIC_INFO, false}, 227 {MediaFormat.MIMETYPE_VIDEO_VP9, VP9_HDR10PLUS_RES, VP9_HDR10PLUS_STATIC_INFO, 228 VP9_HDR10PLUS_DYNAMIC_INFO, true}, 229 }); 230 231 return prepareParamList(exhaustiveArgsList); 232 } 233 234 @Before 235 @Override setUp()236 public void setUp() throws Throwable { 237 super.setUp(); 238 mDisplayManager = mContext.getSystemService(DisplayManager.class); 239 int numberOfSupportedHdrTypes = 240 mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getHdrCapabilities() 241 .getSupportedHdrTypes().length; 242 assumeFalse("Device doesn't support HDR display", numberOfSupportedHdrTypes == 0); 243 244 mExtractor = new MediaExtractor(); 245 } 246 247 @After 248 @Override tearDown()249 public void tearDown() { 250 if (mDecoder != null) { 251 mDecoder.release(); 252 } 253 if (mExtractor != null) { 254 mExtractor.release(); 255 } 256 super.tearDown(); 257 } 258 259 @CddTest(requirements = {"5.3.5/C-3-1", "5.3.7/C-4-1", "5.3.9"}) 260 @Test testHdrMetadata()261 public void testHdrMetadata() throws Exception { 262 AssetFileDescriptor infd = null; 263 final boolean dynamic = mHdrDynamicInfo != null; 264 265 Preconditions.assertTestFileExists(MEDIA_DIR + mInputFile); 266 267 mExtractor.setDataSource(MEDIA_DIR + mInputFile); 268 269 MediaFormat format = null; 270 int trackIndex = -1; 271 for (int i = 0; i < mExtractor.getTrackCount(); i++) { 272 format = mExtractor.getTrackFormat(i); 273 if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) { 274 trackIndex = i; 275 break; 276 } 277 } 278 279 assertTrue("Extractor failed to extract video track", 280 format != null && trackIndex >= 0); 281 if (mMetaDataInContainer) { 282 verifyHdrStaticInfo("Extractor failed to extract static info", format, 283 mHdrStaticInfo); 284 } 285 286 mExtractor.selectTrack(trackIndex); 287 Log.v(TAG, "format " + format); 288 289 String mime = format.getString(MediaFormat.KEY_MIME); 290 format.setInteger(MediaFormat.KEY_PROFILE, getHdrProfile(mime, dynamic)); 291 292 final Surface surface = getActivity().getSurfaceHolder().getSurface(); 293 294 Log.d(TAG, "Testing candicate decoder " + mCodecName); 295 CountDownLatch latch = new CountDownLatch(1); 296 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 297 298 mDecoder = MediaCodec.createByCodecName(mCodecName); 299 mDecoder.setCallback(new MediaCodec.Callback() { 300 boolean mInputEOS; 301 boolean mOutputReceived; 302 int mInputCount; 303 int mOutputCount; 304 305 @Override 306 public void onOutputBufferAvailable( 307 MediaCodec codec, int index, BufferInfo info) { 308 if (mOutputReceived) { 309 return; 310 } 311 312 MediaFormat bufferFormat = codec.getOutputFormat(index); 313 Log.i(TAG, "got output buffer: format " + bufferFormat); 314 315 verifyHdrStaticInfo("Output buffer has wrong static info", 316 bufferFormat, mHdrStaticInfo); 317 318 if (!dynamic) { 319 codec.releaseOutputBuffer(index, true); 320 321 mOutputReceived = true; 322 latch.countDown(); 323 } else { 324 ByteBuffer hdr10plus = 325 bufferFormat.containsKey(MediaFormat.KEY_HDR10_PLUS_INFO) 326 ? bufferFormat.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO) 327 : null; 328 329 verifyHdrDynamicInfo("Output buffer has wrong hdr10+ info", 330 bufferFormat, mHdrDynamicInfo[mOutputCount]); 331 332 codec.releaseOutputBuffer(index, true); 333 334 mOutputCount++; 335 if (mOutputCount >= mHdrDynamicInfo.length) { 336 mOutputReceived = true; 337 latch.countDown(); 338 } 339 } 340 } 341 342 @Override 343 public void onInputBufferAvailable(MediaCodec codec, int index) { 344 // keep queuing until input EOS, or first output buffer received. 345 if (mInputEOS || mOutputReceived) { 346 return; 347 } 348 349 ByteBuffer inputBuffer = codec.getInputBuffer(index); 350 351 if (mExtractor.getSampleTrackIndex() == -1) { 352 codec.queueInputBuffer( 353 index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 354 mInputEOS = true; 355 } else { 356 int size = mExtractor.readSampleData(inputBuffer, 0); 357 long timestamp = mExtractor.getSampleTime(); 358 mExtractor.advance(); 359 360 if (dynamic && mMetaDataInContainer) { 361 final Bundle params = new Bundle(); 362 // TODO: extractor currently doesn't extract the dynamic metadata. 363 // Send in the test pattern for now to test the metadata propagation. 364 byte[] info = loadByteArrayFromString(mHdrDynamicInfo[mInputCount]); 365 params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); 366 codec.setParameters(params); 367 mInputCount++; 368 if (mInputCount >= mHdrDynamicInfo.length) { 369 mInputEOS = true; 370 } 371 } 372 codec.queueInputBuffer(index, 0, size, timestamp, 0); 373 } 374 } 375 376 @Override 377 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 378 Log.e(TAG, "got codec exception", e); 379 } 380 381 @Override 382 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 383 Log.i(TAG, "got output format: " + format); 384 verifyHdrStaticInfo("Output format has wrong static info", 385 format, mHdrStaticInfo); 386 } 387 }); 388 mDecoder.configure(format, surface, null/*crypto*/, 0/*flags*/); 389 mDecoder.start(); 390 try { 391 assertTrue(latch.await(2000, TimeUnit.MILLISECONDS)); 392 } catch (InterruptedException e) { 393 fail("playback interrupted"); 394 } 395 mDecoder.stop(); 396 } 397 verifyHdrStaticInfo(String reason, MediaFormat format, String pattern)398 private void verifyHdrStaticInfo(String reason, MediaFormat format, String pattern) { 399 ByteBuffer staticMetadataBuffer = format.containsKey("hdr-static-info") ? 400 format.getByteBuffer("hdr-static-info") : null; 401 assertTrue(reason + ": empty", 402 staticMetadataBuffer != null && staticMetadataBuffer.remaining() > 0); 403 assertTrue(reason + ": mismatch", 404 Arrays.equals(loadByteArrayFromString(pattern), staticMetadataBuffer.array())); 405 } 406 verifyHdrDynamicInfo(String reason, MediaFormat format, String pattern)407 private void verifyHdrDynamicInfo(String reason, MediaFormat format, String pattern) { 408 ByteBuffer hdr10PlusInfoBuffer = format.containsKey(MediaFormat.KEY_HDR10_PLUS_INFO) ? 409 format.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO) : null; 410 assertTrue(reason + ":empty", 411 hdr10PlusInfoBuffer != null && hdr10PlusInfoBuffer.remaining() > 0); 412 assertTrue(reason + ": mismatch", 413 Arrays.equals(loadByteArrayFromString(pattern), hdr10PlusInfoBuffer.array())); 414 } 415 416 // helper to load byte[] from a String loadByteArrayFromString(final String str)417 private byte[] loadByteArrayFromString(final String str) { 418 Pattern pattern = Pattern.compile("[0-9a-fA-F]{2}"); 419 Matcher matcher = pattern.matcher(str); 420 // allocate a large enough byte array first 421 byte[] tempArray = new byte[str.length() / 2]; 422 int i = 0; 423 while (matcher.find()) { 424 tempArray[i++] = (byte)Integer.parseInt(matcher.group(), 16); 425 } 426 return Arrays.copyOfRange(tempArray, 0, i); 427 } 428 429 private static boolean DEBUG_HDR_TO_SDR_PLAY_VIDEO = false; 430 private static final String INVALID_HDR_STATIC_INFO = 431 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" + 432 "00 00 00 00 00 00 00 00 00 " ; 433 434 @Test 435 @ApiTest(apis = {"android.media.MediaFormat#KEY_COLOR_TRANSFER_REQUEST"}) testHdrToSdr()436 public void testHdrToSdr() throws Exception { 437 AssetFileDescriptor infd = null; 438 final boolean dynamic = mHdrDynamicInfo != null; 439 440 Preconditions.assertTestFileExists(MEDIA_DIR + mInputFile); 441 mExtractor = new MediaExtractor(); 442 mExtractor.setDataSource(MEDIA_DIR + mInputFile); 443 444 MediaFormat format = null; 445 int trackIndex = -1; 446 for (int i = 0; i < mExtractor.getTrackCount(); i++) { 447 format = mExtractor.getTrackFormat(i); 448 if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) { 449 trackIndex = i; 450 break; 451 } 452 } 453 454 mExtractor.selectTrack(trackIndex); 455 Log.v(TAG, "format " + format); 456 457 String mime = format.getString(MediaFormat.KEY_MIME); 458 format.setInteger(MediaFormat.KEY_PROFILE, getHdrProfile(mime, dynamic)); 459 format.setInteger( 460 MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO); 461 462 final Surface surface = getActivity().getSurfaceHolder().getSurface(); 463 464 Log.d(TAG, "Testing candicate decoder " + mCodecName); 465 CountDownLatch latch = new CountDownLatch(1); 466 mExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC); 467 468 mDecoder = MediaCodec.createByCodecName(mCodecName); 469 mDecoder.setCallback(new MediaCodec.Callback() { 470 boolean mInputEOS; 471 boolean mOutputReceived; 472 int mInputCount; 473 int mOutputCount; 474 475 @Override 476 public void onOutputBufferAvailable( 477 MediaCodec codec, int index, BufferInfo info) { 478 if (mOutputReceived && !DEBUG_HDR_TO_SDR_PLAY_VIDEO) { 479 return; 480 } 481 482 MediaFormat bufferFormat = codec.getOutputFormat(index); 483 Log.i(TAG, "got output buffer: format " + bufferFormat); 484 485 assertEquals("unexpected color transfer for the buffer", 486 MediaFormat.COLOR_TRANSFER_SDR_VIDEO, 487 bufferFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER, 0)); 488 ByteBuffer staticInfo = bufferFormat.getByteBuffer( 489 MediaFormat.KEY_HDR_STATIC_INFO, null); 490 if (staticInfo != null) { 491 assertTrue( 492 "Buffer should not have a valid static HDR metadata present", 493 Arrays.equals(loadByteArrayFromString(INVALID_HDR_STATIC_INFO), 494 staticInfo.array())); 495 } 496 ByteBuffer hdr10PlusInfo = bufferFormat.getByteBuffer( 497 MediaFormat.KEY_HDR10_PLUS_INFO, null); 498 if (hdr10PlusInfo != null) { 499 assertEquals( 500 "Buffer should not have a valid dynamic HDR metadata present", 501 0, hdr10PlusInfo.remaining()); 502 } 503 504 if (!dynamic) { 505 codec.releaseOutputBuffer(index, true); 506 mOutputReceived = true; 507 latch.countDown(); 508 } else { 509 codec.releaseOutputBuffer(index, true); 510 mOutputCount++; 511 if (mOutputCount >= mHdrDynamicInfo.length) { 512 mOutputReceived = true; 513 latch.countDown(); 514 } 515 } 516 } 517 518 @Override 519 public void onInputBufferAvailable(MediaCodec codec, int index) { 520 // keep queuing until input EOS, or first output buffer received. 521 if (mInputEOS || (mOutputReceived && !DEBUG_HDR_TO_SDR_PLAY_VIDEO)) { 522 return; 523 } 524 525 ByteBuffer inputBuffer = codec.getInputBuffer(index); 526 527 if (mExtractor.getSampleTrackIndex() == -1) { 528 codec.queueInputBuffer( 529 index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); 530 mInputEOS = true; 531 } else { 532 int size = mExtractor.readSampleData(inputBuffer, 0); 533 long timestamp = mExtractor.getSampleTime(); 534 mExtractor.advance(); 535 536 if (dynamic && mMetaDataInContainer) { 537 final Bundle params = new Bundle(); 538 // TODO: extractor currently doesn't extract the dynamic metadata. 539 // Send in the test pattern for now to test the metadata propagation. 540 byte[] info = loadByteArrayFromString(mHdrDynamicInfo[mInputCount]); 541 params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); 542 codec.setParameters(params); 543 mInputCount++; 544 if (mInputCount >= mHdrDynamicInfo.length) { 545 mInputEOS = true; 546 } 547 } 548 codec.queueInputBuffer(index, 0, size, timestamp, 0); 549 } 550 } 551 552 @Override 553 public void onError(MediaCodec codec, MediaCodec.CodecException e) { 554 Log.e(TAG, "got codec exception", e); 555 } 556 557 @Override 558 public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) { 559 Log.i(TAG, "got output format: " + format); 560 ByteBuffer staticInfo = format.getByteBuffer( 561 MediaFormat.KEY_HDR_STATIC_INFO, null); 562 if (staticInfo != null) { 563 assertTrue( 564 "output format should not have a valid " + 565 "static HDR metadata present", 566 Arrays.equals(loadByteArrayFromString(INVALID_HDR_STATIC_INFO), 567 staticInfo.array())); 568 } 569 } 570 }); 571 mDecoder.configure(format, surface, null/*crypto*/, 0/*flags*/); 572 int transferRequest = mDecoder.getInputFormat().getInteger( 573 MediaFormat.KEY_COLOR_TRANSFER_REQUEST, 0); 574 assumeFalse(mCodecName + " does not support HDR to SDR tone mapping", 575 transferRequest == 0); 576 assertEquals("unexpected color transfer request value from input format", 577 MediaFormat.COLOR_TRANSFER_SDR_VIDEO, transferRequest); 578 mDecoder.start(); 579 try { 580 assertTrue(latch.await(2000, TimeUnit.MILLISECONDS)); 581 } catch (InterruptedException e) { 582 fail("playback interrupted"); 583 } 584 if (DEBUG_HDR_TO_SDR_PLAY_VIDEO) { 585 Thread.sleep(5000); 586 } 587 mDecoder.stop(); 588 } 589 } 590