• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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