• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.mediaparser.cts;
18 
19 import android.media.MediaCodec;
20 import android.media.MediaFormat;
21 import android.media.MediaParser;
22 import android.util.Pair;
23 
24 import androidx.annotation.Nullable;
25 
26 import com.google.android.exoplayer2.C;
27 import com.google.android.exoplayer2.Format;
28 import com.google.android.exoplayer2.drm.DrmInitData;
29 import com.google.android.exoplayer2.extractor.SeekMap;
30 import com.google.android.exoplayer2.extractor.SeekPoint;
31 import com.google.android.exoplayer2.extractor.TrackOutput;
32 import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
33 import com.google.android.exoplayer2.upstream.DataReader;
34 import com.google.android.exoplayer2.video.ColorInfo;
35 
36 import java.io.IOException;
37 import java.util.ArrayList;
38 
39 public class MockMediaParserOutputConsumer implements MediaParser.OutputConsumer {
40 
41     private final boolean mUsingInBandCryptoInfo;
42     private final FakeExtractorOutput mFakeExtractorOutput;
43     private final ArrayList<TrackOutput> mTrackOutputs;
44 
45     @Nullable private MediaParser.SeekMap mSeekMap;
46     private int mCompletedSampleCount;
47     @Nullable private MediaCodec.CryptoInfo mLastOutputCryptoInfo;
48 
MockMediaParserOutputConsumer()49     public MockMediaParserOutputConsumer() {
50         this(/* usingInBandCryptoInfo= */ false);
51     }
52 
MockMediaParserOutputConsumer(boolean usingInBandCryptoInfo)53     public MockMediaParserOutputConsumer(boolean usingInBandCryptoInfo) {
54         mUsingInBandCryptoInfo = usingInBandCryptoInfo;
55         mFakeExtractorOutput = new FakeExtractorOutput();
56         mTrackOutputs = new ArrayList<>();
57     }
58 
getCompletedSampleCount()59     public int getCompletedSampleCount() {
60         return mCompletedSampleCount;
61     }
62 
63     @Nullable
getLastOutputCryptoInfo()64     public MediaCodec.CryptoInfo getLastOutputCryptoInfo() {
65         return mLastOutputCryptoInfo;
66     }
67 
clearTrackOutputs()68     public void clearTrackOutputs() {
69         mFakeExtractorOutput.clearTrackOutputs();
70     }
71 
72     // OutputConsumer implementation.
73 
74     @Override
onSeekMapFound(MediaParser.SeekMap seekMap)75     public void onSeekMapFound(MediaParser.SeekMap seekMap) {
76         mSeekMap = seekMap;
77         mFakeExtractorOutput.seekMap(
78                 new SeekMap() {
79                     @Override
80                     public boolean isSeekable() {
81                         return seekMap.isSeekable();
82                     }
83 
84                     @Override
85                     public long getDurationUs() {
86                         long durationUs = seekMap.getDurationMicros();
87                         return durationUs != MediaParser.SeekMap.UNKNOWN_DURATION
88                                 ? durationUs
89                                 : C.TIME_UNSET;
90                     }
91 
92                     @Override
93                     public SeekPoints getSeekPoints(long timeUs) {
94                         return toExoPlayerSeekPoints(seekMap.getSeekPoints(timeUs));
95                     }
96                 });
97     }
98 
99     @Override
onTrackCountFound(int numberOfTracks)100     public void onTrackCountFound(int numberOfTracks) {
101         // Do nothing.
102     }
103 
104     @Override
onTrackDataFound(int trackIndex, MediaParser.TrackData trackData)105     public void onTrackDataFound(int trackIndex, MediaParser.TrackData trackData) {
106         while (mTrackOutputs.size() <= trackIndex) {
107             mTrackOutputs.add(mFakeExtractorOutput.track(trackIndex, C.TRACK_TYPE_UNKNOWN));
108         }
109         mTrackOutputs.get(trackIndex).format(toExoPlayerFormat(trackData));
110     }
111 
112     @Override
onSampleDataFound(int trackIndex, MediaParser.InputReader inputReader)113     public void onSampleDataFound(int trackIndex, MediaParser.InputReader inputReader)
114             throws IOException {
115         mFakeExtractorOutput
116                 .track(trackIndex, C.TRACK_TYPE_UNKNOWN)
117                 .sampleData(
118                         new DataReaderAdapter(inputReader), (int) inputReader.getLength(), false);
119     }
120 
121     @Override
onSampleCompleted( int trackIndex, long timeUs, int flags, int size, int offset, @Nullable MediaCodec.CryptoInfo cryptoInfo)122     public void onSampleCompleted(
123             int trackIndex,
124             long timeUs,
125             int flags,
126             int size,
127             int offset,
128             @Nullable MediaCodec.CryptoInfo cryptoInfo) {
129         mCompletedSampleCount++;
130         if (!mUsingInBandCryptoInfo) {
131             mLastOutputCryptoInfo = cryptoInfo;
132         }
133     }
134 
135     // Internal methods.
136 
toExoPlayerSeekPoints( Pair<MediaParser.SeekPoint, MediaParser.SeekPoint> seekPoints)137     private static SeekMap.SeekPoints toExoPlayerSeekPoints(
138             Pair<MediaParser.SeekPoint, MediaParser.SeekPoint> seekPoints) {
139         return new SeekMap.SeekPoints(
140                 toExoPlayerSeekPoint(seekPoints.first), toExoPlayerSeekPoint(seekPoints.second));
141     }
142 
toExoPlayerSeekPoint(MediaParser.SeekPoint seekPoint)143     private static SeekPoint toExoPlayerSeekPoint(MediaParser.SeekPoint seekPoint) {
144         return new SeekPoint(seekPoint.timeMicros, seekPoint.position);
145     }
146 
toExoPlayerFormat(MediaParser.TrackData trackData)147     private static Format toExoPlayerFormat(MediaParser.TrackData trackData) {
148         MediaFormat mediaFormat = trackData.mediaFormat;
149         String sampleMimeType =
150                 mediaFormat.getString(MediaFormat.KEY_MIME, /* defaultValue= */ null);
151         String id =
152                 mediaFormat.containsKey(MediaFormat.KEY_TRACK_ID)
153                         ? String.valueOf(mediaFormat.getInteger(MediaFormat.KEY_TRACK_ID))
154                         : null;
155         String codecs =
156                 mediaFormat.getString(MediaFormat.KEY_CODECS_STRING, /* defaultValue= */ null);
157         int bitrate =
158                 mediaFormat.getInteger(
159                         MediaFormat.KEY_BIT_RATE, /* defaultValue= */ Format.NO_VALUE);
160         int maxInputSize =
161                 mediaFormat.getInteger(
162                         MediaFormat.KEY_MAX_INPUT_SIZE, /* defaultValue= */ Format.NO_VALUE);
163         int width =
164                 mediaFormat.getInteger(MediaFormat.KEY_WIDTH, /* defaultValue= */ Format.NO_VALUE);
165         int height =
166                 mediaFormat.getInteger(MediaFormat.KEY_HEIGHT, /* defaultValue= */ Format.NO_VALUE);
167         float frameRate =
168                 mediaFormat.getFloat(
169                         MediaFormat.KEY_FRAME_RATE, /* defaultValue= */ Format.NO_VALUE);
170         int rotationDegrees =
171                 mediaFormat.getInteger(
172                         MediaFormat.KEY_ROTATION, /* defaultValue= */ Format.NO_VALUE);
173         ArrayList<byte[]> initData = null;
174         if (mediaFormat.containsKey("csd-0")) {
175             initData = new ArrayList<>();
176             int index = 0;
177             while (mediaFormat.containsKey("csd-" + index)) {
178                 initData.add(mediaFormat.getByteBuffer("csd-" + index++).array());
179             }
180         }
181         float pixelAspectWidth =
182                 (float)
183                         mediaFormat.getInteger(
184                                 MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, /* defaultValue= */ 0);
185         float pixelAspectHeight =
186                 (float)
187                         mediaFormat.getInteger(
188                                 MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, /* defaultValue= */ 0);
189         float pixelAspectRatio =
190                 pixelAspectHeight == 0 || pixelAspectWidth == 0
191                         ? Format.NO_VALUE
192                         : pixelAspectWidth / pixelAspectHeight;
193         ColorInfo colorInfo = getExoPlayerColorInfo(mediaFormat);
194         DrmInitData drmInitData =
195                 getExoPlayerDrmInitData(
196                         mediaFormat.getString("crypto-mode-fourcc"), trackData.drmInitData);
197 
198         int selectionFlags =
199                 mediaFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT, /* defaultValue= */ 0) != 0
200                         ? C.SELECTION_FLAG_AUTOSELECT
201                         : 0;
202         selectionFlags |=
203                 mediaFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, /* defaultValue= */ 0)
204                                 != 0
205                         ? C.SELECTION_FLAG_FORCED
206                         : 0;
207         selectionFlags |=
208                 mediaFormat.getInteger(MediaFormat.KEY_IS_DEFAULT, /* defaultValue= */ 0) != 0
209                         ? C.SELECTION_FLAG_DEFAULT
210                         : 0;
211 
212         String language = mediaFormat.getString(MediaFormat.KEY_LANGUAGE, /* defaultValue= */ null);
213         int channels =
214                 mediaFormat.getInteger(
215                         MediaFormat.KEY_CHANNEL_COUNT, /* defaultValue= */ Format.NO_VALUE);
216         int sampleRate =
217                 mediaFormat.getInteger(
218                         MediaFormat.KEY_SAMPLE_RATE, /* defaultValue= */ Format.NO_VALUE);
219         int accessibilityChannel =
220                 mediaFormat.getInteger(
221                         MediaFormat.KEY_CAPTION_SERVICE_NUMBER,
222                         /* defaultValue= */ Format.NO_VALUE);
223 
224         return new Format.Builder()
225                 .setId(id)
226                 .setSampleMimeType(sampleMimeType)
227                 .setCodecs(codecs)
228                 .setPeakBitrate(bitrate)
229                 .setMaxInputSize(maxInputSize)
230                 .setWidth(width)
231                 .setHeight(height)
232                 .setFrameRate(frameRate)
233                 .setInitializationData(initData)
234                 .setRotationDegrees(rotationDegrees)
235                 .setPixelWidthHeightRatio(pixelAspectRatio)
236                 .setColorInfo(colorInfo)
237                 .setDrmInitData(drmInitData)
238                 .setChannelCount(channels)
239                 .setSampleRate(sampleRate)
240                 .setSelectionFlags(selectionFlags)
241                 .setLanguage(language)
242                 .setAccessibilityChannel(accessibilityChannel)
243                 .build();
244     }
245 
246     @Nullable
getExoPlayerDrmInitData( @ullable String encryptionScheme, @Nullable android.media.DrmInitData drmInitData)247     private static DrmInitData getExoPlayerDrmInitData(
248             @Nullable String encryptionScheme, @Nullable android.media.DrmInitData drmInitData) {
249         if (drmInitData == null) {
250             return null;
251         }
252         DrmInitData.SchemeData[] schemeDatas =
253                 new DrmInitData.SchemeData[drmInitData.getSchemeInitDataCount()];
254         for (int i = 0; i < schemeDatas.length; i++) {
255             android.media.DrmInitData.SchemeInitData schemeInitData =
256                     drmInitData.getSchemeInitDataAt(i);
257             schemeDatas[i] =
258                     new DrmInitData.SchemeData(
259                             schemeInitData.uuid, schemeInitData.mimeType, schemeInitData.data);
260         }
261         return new DrmInitData(encryptionScheme, schemeDatas);
262     }
263 
getExoPlayerColorInfo(MediaFormat mediaFormat)264     private static ColorInfo getExoPlayerColorInfo(MediaFormat mediaFormat) {
265         int colorSpace = Format.NO_VALUE;
266         if (mediaFormat.containsKey(MediaFormat.KEY_COLOR_FORMAT)) {
267             switch (mediaFormat.getInteger(MediaFormat.KEY_COLOR_FORMAT)) {
268                 case MediaFormat.COLOR_STANDARD_BT601_NTSC:
269                 case MediaFormat.COLOR_STANDARD_BT601_PAL:
270                     colorSpace = C.COLOR_SPACE_BT601;
271                     break;
272                 case MediaFormat.COLOR_STANDARD_BT709:
273                     colorSpace = C.COLOR_SPACE_BT709;
274                     break;
275                 case MediaFormat.COLOR_STANDARD_BT2020:
276                     colorSpace = C.COLOR_SPACE_BT2020;
277                     break;
278                 default:
279                     colorSpace = Format.NO_VALUE;
280             }
281         }
282 
283         int colorRange = Format.NO_VALUE;
284         if (mediaFormat.containsKey(MediaFormat.KEY_COLOR_RANGE)) {
285             switch (mediaFormat.getInteger(MediaFormat.KEY_COLOR_RANGE)) {
286                 case MediaFormat.COLOR_RANGE_FULL:
287                     colorRange = C.COLOR_RANGE_FULL;
288                     break;
289                 case MediaFormat.COLOR_RANGE_LIMITED:
290                     colorRange = C.COLOR_RANGE_LIMITED;
291                     break;
292                 default:
293                     colorRange = Format.NO_VALUE;
294             }
295         }
296 
297         int colorTransfer = Format.NO_VALUE;
298         if (mediaFormat.containsKey(MediaFormat.KEY_COLOR_TRANSFER)) {
299             switch (mediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER)) {
300                 case MediaFormat.COLOR_TRANSFER_HLG:
301                     colorTransfer = C.COLOR_TRANSFER_HLG;
302                     break;
303                 case MediaFormat.COLOR_TRANSFER_SDR_VIDEO:
304                     colorTransfer = C.COLOR_TRANSFER_SDR;
305                     break;
306                 case MediaFormat.COLOR_TRANSFER_ST2084:
307                     colorTransfer = C.COLOR_TRANSFER_ST2084;
308                     break;
309                 case MediaFormat.COLOR_TRANSFER_LINEAR:
310                     // Fall through, there's no mapping.
311                 default:
312                     colorTransfer = Format.NO_VALUE;
313             }
314         }
315         boolean hasHdrInfo = mediaFormat.containsKey(MediaFormat.KEY_HDR_STATIC_INFO);
316         if (colorSpace == Format.NO_VALUE
317                 && colorRange == Format.NO_VALUE
318                 && colorTransfer == Format.NO_VALUE
319                 && !hasHdrInfo) {
320             return null;
321         } else {
322             return new ColorInfo(
323                     colorSpace,
324                     colorRange,
325                     colorTransfer,
326                     hasHdrInfo
327                             ? mediaFormat.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO).array()
328                             : null);
329         }
330     }
331 
getSeekMap()332     public MediaParser.SeekMap getSeekMap() {
333         return mSeekMap;
334     }
335 
336     // Internal classes.
337 
338     private static class DataReaderAdapter implements DataReader {
339 
340         private final MediaParser.InputReader mInputReader;
341 
DataReaderAdapter(MediaParser.InputReader inputReader)342         private DataReaderAdapter(MediaParser.InputReader inputReader) {
343             mInputReader = inputReader;
344         }
345 
346         @Override
read(byte[] target, int offset, int length)347         public int read(byte[] target, int offset, int length) throws IOException {
348             return mInputReader.read(target, offset, length);
349         }
350     }
351 }
352