From 8da4800d107aa83e520b17254a796a2edf2b7384 Mon Sep 17 00:00:00 2001 From: tadepall_adobe Date: Fri, 3 Jul 2026 09:34:35 -0700 Subject: [PATCH 1/2] Handle HDR images --- .../BitmapToHardwareBufferProcessor.java | 25 +++++++++--------- .../HardwareBufferFrameReader.java | 26 ++++++++++++++++++- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java index 9c150ad01e9..bcc714c044a 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java @@ -116,27 +116,25 @@ public HardwareBufferFrame process(HardwareBufferFrame inputFrame) { if (currentFrame == null) { HardwareBuffer buffer = null; try { - if (SDK_INT >= 31) { + if (nextBitmap.getConfig() == Config.RGBA_1010102) { + buffer = copyCpuBitmapToHardwareBuffer(nextBitmap, hardwareBufferJniWrapper); + } else if (SDK_INT >= 31) { if (nextBitmap.getConfig() == Config.HARDWARE) { - // Input is HARDWARE and API >= 31: Direct access to HardwareBuffer is possible. buffer = nextBitmap.getHardwareBuffer(); } else { - // Input is not HARDWARE and API >= 31: We can create a HARDWARE Bitmap copy - // and get its HardwareBuffer. - buffer = nextBitmap.copy(Config.HARDWARE, /* isMutable= */ false).getHardwareBuffer(); + Bitmap hwCopy = nextBitmap.copy(Config.HARDWARE, /* isMutable= */ false); + if (hwCopy != null) { + buffer = hwCopy.getHardwareBuffer(); + } } } // Fallback to the native helper when the HardwareBuffer retrieved from the Bitmap was // null, or SDK_INT < 31. if (buffer == null) { if (nextBitmap.getConfig() == Config.HARDWARE) { - // Input is HARDWARE but API < 31: HardwareBuffer is not directly accessible. - // We must first copy to a software Bitmap (e.g. ARGB_8888) - // and then copy that to a new HardwareBuffer via JNI. Bitmap softwareBitmap = nextBitmap.copy(Config.ARGB_8888, /* isMutable= */ false); buffer = copyCpuBitmapToHardwareBuffer(softwareBitmap, hardwareBufferJniWrapper); } else { - // Input is not HARDWARE: Copy the software Bitmap to a new HardwareBuffer via JNI. buffer = copyCpuBitmapToHardwareBuffer(nextBitmap, hardwareBufferJniWrapper); } } @@ -199,16 +197,19 @@ public void close() { *

The created buffer will have {@linkplain HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE GPU read}, * {@linkplain HardwareBuffer#USAGE_GPU_COLOR_OUTPUT GPU write}, {@linkplain * HardwareBuffer#USAGE_CPU_READ_OFTEN CPU read} and {@linkplain - * HardwareBuffer#USAGE_CPU_WRITE_OFTEN CPU write} usage flags set, and pixelFormat of {@link - * HardwareBuffer#RGBA_8888}. + * HardwareBuffer#USAGE_CPU_WRITE_OFTEN CPU write} usage flags set. */ private static HardwareBuffer copyCpuBitmapToHardwareBuffer( Bitmap bitmap, HardwareBufferJniWrapper hardwareBufferJniWrapper) { + int pixelFormat = + bitmap.getConfig() == Config.RGBA_1010102 + ? HardwareBuffer.RGBA_1010102 + : HardwareBuffer.RGBA_8888; HardwareBuffer buffer = HardwareBuffer.create( bitmap.getWidth(), bitmap.getHeight(), - /* pixelFormat= */ HardwareBuffer.RGBA_8888, + pixelFormat, /* layers= */ 1, /* usageFlags= */ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/HardwareBufferFrameReader.java b/libraries/transformer/src/main/java/androidx/media3/transformer/HardwareBufferFrameReader.java index 76fab66ea92..a7bd3c3f15c 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/HardwareBufferFrameReader.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/HardwareBufferFrameReader.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkState; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.hardware.HardwareBuffer; import android.os.Handler; import android.os.Looper; @@ -228,7 +229,7 @@ void outputBitmap( .setSampleMimeType(MimeTypes.IMAGE_RAW) .setWidth(bitmap.getWidth()) .setHeight(bitmap.getHeight()) - .setColorInfo(ColorInfo.SRGB_BT709_FULL) + .setColorInfo(resolveColorInfoFromBitmap(bitmap)) .setFrameRate(/* frameRate= */ DEFAULT_FRAME_RATE) .build(); synchronized (this) { @@ -505,6 +506,29 @@ private void maybeWakeupVideoRenderer() { } } + private static ColorInfo resolveColorInfoFromBitmap(Bitmap bitmap) { + if (SDK_INT >= 34) { + ColorSpace colorSpace = bitmap.getColorSpace(); + if (colorSpace != null) { + int id = colorSpace.getId(); + if (id == ColorSpace.get(ColorSpace.Named.BT2020_HLG).getId()) { + return new ColorInfo.Builder() + .setColorSpace(C.COLOR_SPACE_BT2020) + .setColorRange(C.COLOR_RANGE_FULL) + .setColorTransfer(C.COLOR_TRANSFER_HLG) + .build(); + } else if (id == ColorSpace.get(ColorSpace.Named.BT2020_PQ).getId()) { + return new ColorInfo.Builder() + .setColorSpace(C.COLOR_SPACE_BT2020) + .setColorRange(C.COLOR_RANGE_FULL) + .setColorTransfer(C.COLOR_TRANSFER_ST2084) + .build(); + } + } + } + return ColorInfo.SRGB_BT709_FULL; + } + /** * A {@link Executor} which executes commands on a {@link Handler} and propagates {@linkplain * RuntimeException errors} to the {@link Listener}. From e2da5686fdf52edc0b28eb1873c716359135de28 Mon Sep 17 00:00:00 2001 From: tadepall_adobe Date: Sun, 5 Jul 2026 18:21:12 -0700 Subject: [PATCH 2/2] Restore original comments and revert null-safety refactor in BitmapToHardwareBufferProcessor Keep only the minimal HDR (RGBA_1010102) fast-path change while preserving the original inline comments and Bitmap.copy() call style. Co-authored-by: Cursor --- .../effect/BitmapToHardwareBufferProcessor.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java b/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java index bcc714c044a..3a03f0a0e26 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/BitmapToHardwareBufferProcessor.java @@ -120,21 +120,25 @@ public HardwareBufferFrame process(HardwareBufferFrame inputFrame) { buffer = copyCpuBitmapToHardwareBuffer(nextBitmap, hardwareBufferJniWrapper); } else if (SDK_INT >= 31) { if (nextBitmap.getConfig() == Config.HARDWARE) { + // Input is HARDWARE and API >= 31: Direct access to HardwareBuffer is possible. buffer = nextBitmap.getHardwareBuffer(); } else { - Bitmap hwCopy = nextBitmap.copy(Config.HARDWARE, /* isMutable= */ false); - if (hwCopy != null) { - buffer = hwCopy.getHardwareBuffer(); - } + // Input is not HARDWARE and API >= 31: We can create a HARDWARE Bitmap copy + // and get its HardwareBuffer. + buffer = nextBitmap.copy(Config.HARDWARE, /* isMutable= */ false).getHardwareBuffer(); } } // Fallback to the native helper when the HardwareBuffer retrieved from the Bitmap was // null, or SDK_INT < 31. if (buffer == null) { if (nextBitmap.getConfig() == Config.HARDWARE) { + // Input is HARDWARE but API < 31: HardwareBuffer is not directly accessible. + // We must first copy to a software Bitmap (e.g. ARGB_8888) + // and then copy that to a new HardwareBuffer via JNI. Bitmap softwareBitmap = nextBitmap.copy(Config.ARGB_8888, /* isMutable= */ false); buffer = copyCpuBitmapToHardwareBuffer(softwareBitmap, hardwareBufferJniWrapper); } else { + // Input is not HARDWARE: Copy the software Bitmap to a new HardwareBuffer via JNI. buffer = copyCpuBitmapToHardwareBuffer(nextBitmap, hardwareBufferJniWrapper); } }