From 7a632a43ba027bec73de9e5104e4853c72c8cd6a Mon Sep 17 00:00:00 2001 From: tofunmi Date: Tue, 20 Feb 2024 06:53:45 -0800 Subject: [PATCH] Move bitmap decoding into datasource util PiperOrigin-RevId: 608588505 --- .../datasource/DataSourceBitmapLoader.java | 37 +------------- .../media3/datasource/DataSourceUtil.java | 42 ++++++++++++++++ .../image/BitmapFactoryImageDecoder.java | 48 ++++++------------- 3 files changed, 59 insertions(+), 68 deletions(-) diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceBitmapLoader.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceBitmapLoader.java index f8a927bbf6..fdd8d88467 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceBitmapLoader.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceBitmapLoader.java @@ -15,17 +15,14 @@ */ package androidx.media3.datasource; -import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Util.isBitmapFactorySupportedMimeType; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Matrix; import android.net.Uri; import androidx.annotation.Nullable; -import androidx.exifinterface.media.ExifInterface; import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.UnstableApi; import com.google.common.base.Supplier; @@ -33,9 +30,7 @@ import com.google.common.base.Suppliers; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.util.concurrent.Executors; /** @@ -101,7 +96,7 @@ public final class DataSourceBitmapLoader implements BitmapLoader { @Override public ListenableFuture decodeBitmap(byte[] data) { - return listeningExecutorService.submit(() -> decode(data, options)); + return listeningExecutorService.submit(() -> DataSourceUtil.decode(data, data.length, options)); } @Override @@ -110,41 +105,13 @@ public final class DataSourceBitmapLoader implements BitmapLoader { () -> load(dataSourceFactory.createDataSource(), uri, options)); } - // BitmapFactory's options parameter is null-ok. - @SuppressWarnings("nullness:argument.type.incompatible") - private static Bitmap decode(byte[] data, @Nullable BitmapFactory.Options options) - throws IOException { - @Nullable - Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, data.length, options); - checkArgument(bitmap != null, "Could not decode image data"); - ExifInterface exifInterface; - try (InputStream inputStream = new ByteArrayInputStream(data)) { - exifInterface = new ExifInterface(inputStream); - } - int rotationDegrees = exifInterface.getRotationDegrees(); - if (rotationDegrees != 0) { - Matrix matrix = new Matrix(); - matrix.postRotate(rotationDegrees); - bitmap = - Bitmap.createBitmap( - bitmap, - /* x= */ 0, - /* y= */ 0, - bitmap.getWidth(), - bitmap.getHeight(), - matrix, - /* filter= */ false); - } - return bitmap; - } - private static Bitmap load( DataSource dataSource, Uri uri, @Nullable BitmapFactory.Options options) throws IOException { try { DataSpec dataSpec = new DataSpec(uri); dataSource.open(dataSpec); byte[] readData = DataSourceUtil.readToEnd(dataSource); - return decode(readData, options); + return DataSourceUtil.decode(readData, readData.length, options); } finally { dataSource.close(); } diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceUtil.java b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceUtil.java index aa14615c8e..b8ea89a934 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceUtil.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/DataSourceUtil.java @@ -15,15 +15,24 @@ */ package androidx.media3.datasource; +import static androidx.media3.common.util.Assertions.checkArgument; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; import androidx.annotation.Nullable; +import androidx.exifinterface.media.ExifInterface; import androidx.media3.common.C; import androidx.media3.common.util.UnstableApi; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.Arrays; /** Utility methods for {@link DataSource}. */ @UnstableApi public final class DataSourceUtil { + public static final String BITMAP_DECODING_EXCEPTION_MESSAGE = "Could not decode image data"; private DataSourceUtil() {} @@ -90,4 +99,37 @@ public final class DataSourceUtil { // Ignore. } } + + /** + * Decodes a {@link Bitmap} from a byte array using {@link BitmapFactory} and the {@link + * ExifInterface}. + */ + // BitmapFactory's options parameter is null-ok. + @SuppressWarnings("nullness:argument.type.incompatible") + public static Bitmap decode(byte[] data, int length, @Nullable BitmapFactory.Options options) + throws IOException { + @Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, length, options); + checkArgument(bitmap != null, BITMAP_DECODING_EXCEPTION_MESSAGE); + // BitmapFactory doesn't read the exif header, so we use the ExifInterface to this do ensure the + // bitmap is correctly orientated. + ExifInterface exifInterface; + try (InputStream inputStream = new ByteArrayInputStream(data)) { + exifInterface = new ExifInterface(inputStream); + } + int rotationDegrees = exifInterface.getRotationDegrees(); + if (rotationDegrees != 0) { + Matrix matrix = new Matrix(); + matrix.postRotate(rotationDegrees); + bitmap = + Bitmap.createBitmap( + bitmap, + /* x= */ 0, + /* y= */ 0, + bitmap.getWidth(), + bitmap.getHeight(), + matrix, + /* filter= */ false); + } + return bitmap; + } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/BitmapFactoryImageDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/BitmapFactoryImageDecoder.java index b1a75f436e..89b7d2c936 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/BitmapFactoryImageDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/image/BitmapFactoryImageDecoder.java @@ -24,21 +24,19 @@ import static androidx.media3.decoder.DecoderInputBuffer.BUFFER_REPLACEMENT_MODE import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Matrix; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.exifinterface.media.ExifInterface; import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; +import androidx.media3.datasource.DataSourceUtil; import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.decoder.SimpleDecoder; import androidx.media3.exoplayer.RendererCapabilities; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; +import java.util.Objects; /** * An image decoder that uses {@link BitmapFactory} to decode images. @@ -160,37 +158,21 @@ public final class BitmapFactoryImageDecoder * @throws ImageDecoderException If a decoding error occurs. */ private static Bitmap decode(byte[] data, int length) throws ImageDecoderException { - @Nullable Bitmap bitmap = BitmapFactory.decodeByteArray(data, /* offset= */ 0, length); - if (bitmap == null) { - throw new ImageDecoderException( - "Could not decode image data with BitmapFactory. (data.length = " - + data.length - + ", input length = " - + length - + ")"); - } - // BitmapFactory doesn't read the exif header, so we use the ExifInterface to this do ensure the - // bitmap is correctly orientated. - ExifInterface exifInterface; - try (InputStream inputStream = new ByteArrayInputStream(data, /* offset= */ 0, length)) { - exifInterface = new ExifInterface(inputStream); + try { + return DataSourceUtil.decode(data, length, /* options= */ null); } catch (IOException e) { throw new ImageDecoderException(e); + } catch (IllegalArgumentException e) { + if (Objects.equals(e.getMessage(), DataSourceUtil.BITMAP_DECODING_EXCEPTION_MESSAGE)) { + throw new ImageDecoderException( + "Could not decode image data with BitmapFactory. (data.length = " + + data.length + + ", input length = " + + length + + ")"); + } else { + throw e; + } } - int rotationDegrees = exifInterface.getRotationDegrees(); - if (rotationDegrees != 0) { - Matrix matrix = new Matrix(); - matrix.postRotate(rotationDegrees); - bitmap = - Bitmap.createBitmap( - bitmap, - /* x= */ 0, - /* y= */ 0, - bitmap.getWidth(), - bitmap.getHeight(), - matrix, - /* filter= */ false); - } - return bitmap; } }