5
5
import android .graphics .Bitmap ;
6
6
import android .graphics .Matrix ;
7
7
import android .media .MediaDataSource ;
8
+ import android .media .MediaExtractor ;
8
9
import android .media .MediaFormat ;
9
10
import android .media .MediaMetadataRetriever ;
10
11
import android .os .Build ;
@@ -125,7 +126,9 @@ public void update(
125
126
private static final List <String > PIXEL_T_BUILD_ID_PREFIXES_REQUIRING_HDR_180_ROTATION_FIX =
126
127
Collections .unmodifiableList (Arrays .asList ("TP1A" , "TD1A.220804.031" ));
127
128
128
- private final MediaMetadataRetrieverInitializer <T > initializer ;
129
+ private static final String WEBM_MIME_TYPE = "video/webm" ;
130
+
131
+ private final MediaInitializer <T > initializer ;
129
132
private final BitmapPool bitmapPool ;
130
133
private final MediaMetadataRetrieverFactory factory ;
131
134
@@ -142,14 +145,14 @@ public static ResourceDecoder<ByteBuffer, Bitmap> byteBuffer(BitmapPool bitmapPo
142
145
return new VideoDecoder <>(bitmapPool , new ByteBufferInitializer ());
143
146
}
144
147
145
- VideoDecoder (BitmapPool bitmapPool , MediaMetadataRetrieverInitializer <T > initializer ) {
148
+ VideoDecoder (BitmapPool bitmapPool , MediaInitializer <T > initializer ) {
146
149
this (bitmapPool , initializer , DEFAULT_FACTORY );
147
150
}
148
151
149
152
@ VisibleForTesting
150
153
VideoDecoder (
151
154
BitmapPool bitmapPool ,
152
- MediaMetadataRetrieverInitializer <T > initializer ,
155
+ MediaInitializer <T > initializer ,
153
156
MediaMetadataRetrieverFactory factory ) {
154
157
this .bitmapPool = bitmapPool ;
155
158
this .initializer = initializer ;
@@ -185,9 +188,10 @@ public Resource<Bitmap> decode(
185
188
final Bitmap result ;
186
189
MediaMetadataRetriever mediaMetadataRetriever = factory .build ();
187
190
try {
188
- initializer .initialize (mediaMetadataRetriever , resource );
191
+ initializer .initializeRetriever (mediaMetadataRetriever , resource );
189
192
result =
190
193
decodeFrame (
194
+ resource ,
191
195
mediaMetadataRetriever ,
192
196
frameTimeMicros ,
193
197
frameOption ,
@@ -206,13 +210,18 @@ public Resource<Bitmap> decode(
206
210
}
207
211
208
212
@ Nullable
209
- private static Bitmap decodeFrame (
213
+ private Bitmap decodeFrame (
214
+ @ NonNull T resource ,
210
215
MediaMetadataRetriever mediaMetadataRetriever ,
211
216
long frameTimeMicros ,
212
217
int frameOption ,
213
218
int outWidth ,
214
219
int outHeight ,
215
220
DownsampleStrategy strategy ) {
221
+ if (isUnsupportedFormat (resource , mediaMetadataRetriever )) {
222
+ throw new IllegalStateException ("Cannot decode VP8 video on CrOS." );
223
+ }
224
+
216
225
Bitmap result = null ;
217
226
// Arguably we should handle the case where just width or just height is set to
218
227
// Target.SIZE_ORIGINAL. Up to and including OMR1, MediaMetadataRetriever defaults to setting
@@ -402,6 +411,54 @@ private static Bitmap decodeOriginalFrame(
402
411
return mediaMetadataRetriever .getFrameAtTime (frameTimeMicros , frameOption );
403
412
}
404
413
414
+ /** Returns true if the format type is unsupported on the device. */
415
+ private boolean isUnsupportedFormat (
416
+ @ NonNull T resource , MediaMetadataRetriever mediaMetadataRetriever ) {
417
+ // MediaFormat.KEY_MIME check below requires at least JELLY_BEAN
418
+ if (Build .VERSION .SDK_INT < VERSION_CODES .JELLY_BEAN ) {
419
+ return false ;
420
+ }
421
+
422
+ // The primary known problem is vp8 video on ChromeOS (ARC) devices.
423
+ boolean isArc = Build .DEVICE != null && Build .DEVICE .matches (".+_cheets|cheets_.+" );
424
+ if (!isArc ) {
425
+ return false ;
426
+ }
427
+
428
+ MediaExtractor mediaExtractor = null ;
429
+ try {
430
+ // Include the MediaMetadataRetriever extract in the try block out of an abundance of caution.
431
+ String mimeType =
432
+ mediaMetadataRetriever .extractMetadata (MediaMetadataRetriever .METADATA_KEY_MIMETYPE );
433
+ if (!WEBM_MIME_TYPE .equals (mimeType )) {
434
+ return false ;
435
+ }
436
+
437
+ // Only construct a MediaExtractor for webm files, since the constructor makes a JNI call
438
+ mediaExtractor = new MediaExtractor ();
439
+ initializer .initializeExtractor (mediaExtractor , resource );
440
+ int numTracks = mediaExtractor .getTrackCount ();
441
+ for (int i = 0 ; i < numTracks ; ++i ) {
442
+ MediaFormat mediaformat = mediaExtractor .getTrackFormat (i );
443
+ String trackMimeType = mediaformat .getString (MediaFormat .KEY_MIME );
444
+ if (MediaFormat .MIMETYPE_VIDEO_VP8 .equals (trackMimeType )) {
445
+ return true ;
446
+ }
447
+ }
448
+ } catch (Throwable t ) {
449
+ // Catching everything here out of an abundance of caution
450
+ if (Log .isLoggable (TAG , Log .DEBUG )) {
451
+ Log .d (TAG , "Exception trying to extract track info for a webm video on CrOS." , t );
452
+ }
453
+ } finally {
454
+ if (mediaExtractor != null ) {
455
+ mediaExtractor .release ();
456
+ }
457
+ }
458
+
459
+ return false ;
460
+ }
461
+
405
462
@ VisibleForTesting
406
463
static class MediaMetadataRetrieverFactory {
407
464
public MediaMetadataRetriever build () {
@@ -410,56 +467,78 @@ public MediaMetadataRetriever build() {
410
467
}
411
468
412
469
@ VisibleForTesting
413
- interface MediaMetadataRetrieverInitializer <T > {
414
- void initialize (MediaMetadataRetriever retriever , T data );
470
+ interface MediaInitializer <T > {
471
+ void initializeRetriever (MediaMetadataRetriever retriever , T data );
472
+
473
+ void initializeExtractor (MediaExtractor extractor , T data ) throws IOException ;
415
474
}
416
475
417
476
private static final class AssetFileDescriptorInitializer
418
- implements MediaMetadataRetrieverInitializer <AssetFileDescriptor > {
477
+ implements MediaInitializer <AssetFileDescriptor > {
419
478
420
479
@ Override
421
- public void initialize (MediaMetadataRetriever retriever , AssetFileDescriptor data ) {
480
+ public void initializeRetriever (MediaMetadataRetriever retriever , AssetFileDescriptor data ) {
422
481
retriever .setDataSource (data .getFileDescriptor (), data .getStartOffset (), data .getLength ());
423
482
}
483
+
484
+ @ Override
485
+ public void initializeExtractor (MediaExtractor extractor , AssetFileDescriptor data )
486
+ throws IOException {
487
+ extractor .setDataSource (data .getFileDescriptor (), data .getStartOffset (), data .getLength ());
488
+ }
424
489
}
425
490
426
491
// Visible for VideoBitmapDecoder.
427
492
static final class ParcelFileDescriptorInitializer
428
- implements MediaMetadataRetrieverInitializer <ParcelFileDescriptor > {
493
+ implements MediaInitializer <ParcelFileDescriptor > {
429
494
430
495
@ Override
431
- public void initialize (MediaMetadataRetriever retriever , ParcelFileDescriptor data ) {
496
+ public void initializeRetriever (MediaMetadataRetriever retriever , ParcelFileDescriptor data ) {
432
497
retriever .setDataSource (data .getFileDescriptor ());
433
498
}
499
+
500
+ @ Override
501
+ public void initializeExtractor (MediaExtractor extractor , ParcelFileDescriptor data )
502
+ throws IOException {
503
+ extractor .setDataSource (data .getFileDescriptor ());
504
+ }
434
505
}
435
506
436
507
@ RequiresApi (Build .VERSION_CODES .M )
437
- static final class ByteBufferInitializer
438
- implements MediaMetadataRetrieverInitializer <ByteBuffer > {
508
+ static final class ByteBufferInitializer implements MediaInitializer <ByteBuffer > {
439
509
440
510
@ Override
441
- public void initialize (MediaMetadataRetriever retriever , final ByteBuffer data ) {
442
- retriever .setDataSource (
443
- new MediaDataSource () {
444
- @ Override
445
- public int readAt (long position , byte [] buffer , int offset , int size ) {
446
- if (position >= data .limit ()) {
447
- return -1 ;
448
- }
449
- data .position ((int ) position );
450
- int numBytesRead = Math .min (size , data .remaining ());
451
- data .get (buffer , offset , numBytesRead );
452
- return numBytesRead ;
453
- }
511
+ public void initializeRetriever (MediaMetadataRetriever retriever , final ByteBuffer data ) {
512
+ retriever .setDataSource (getMediaDataSource (data ));
513
+ }
454
514
455
- @ Override
456
- public long getSize () {
457
- return data .limit ();
458
- }
515
+ @ Override
516
+ public void initializeExtractor (MediaExtractor extractor , final ByteBuffer data )
517
+ throws IOException {
518
+ extractor .setDataSource (getMediaDataSource (data ));
519
+ }
459
520
460
- @ Override
461
- public void close () {}
462
- });
521
+ private MediaDataSource getMediaDataSource (final ByteBuffer data ) {
522
+ return new MediaDataSource () {
523
+ @ Override
524
+ public int readAt (long position , byte [] buffer , int offset , int size ) {
525
+ if (position >= data .limit ()) {
526
+ return -1 ;
527
+ }
528
+ data .position ((int ) position );
529
+ int numBytesRead = Math .min (size , data .remaining ());
530
+ data .get (buffer , offset , numBytesRead );
531
+ return numBytesRead ;
532
+ }
533
+
534
+ @ Override
535
+ public long getSize () {
536
+ return data .limit ();
537
+ }
538
+
539
+ @ Override
540
+ public void close () {}
541
+ };
463
542
}
464
543
}
465
544
0 commit comments