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