From 5c6962082a095d77b8eb797f36353d4e1b4d9608 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 19 Feb 2026 10:42:17 -0500 Subject: [PATCH] Only set HDR transcoder flags for HDR content. --- .../VideoThumbnailsExtractor.java | 28 ++++++++++++------- .../videoconverter/VideoTrackConverter.java | 10 ++++--- .../videoconverter/utils/MediaCodecCompat.kt | 16 +++++++++++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsExtractor.java b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsExtractor.java index 207f841c40..f5f4477ed0 100644 --- a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsExtractor.java +++ b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoThumbnailsExtractor.java @@ -13,6 +13,7 @@ import androidx.annotation.RequiresApi; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.video.interfaces.MediaInput; +import org.thoughtcrime.securesms.video.videoconverter.utils.MediaCodecCompat; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -88,19 +89,25 @@ final class VideoThumbnailsExtractor { outputSurface = new OutputSurface(outputWidthRotated, outputHeightRotated, true); decoder = MediaCodec.createDecoderByType(mime); - if (Build.VERSION.SDK_INT >= 31) { - final String VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY = "vendor.dolby.codec.transfer.value"; - MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY); - if (descriptor != null) { - Bundle transferBundle = new Bundle(); - transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal"); - decoder.setParameters(transferBundle); - } else { - mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO); - } + final boolean isHdr = MediaCodecCompat.isHdrVideo(mediaFormat); + if (Build.VERSION.SDK_INT >= 31 && isHdr) { + mediaFormat.setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO); } decoder.configure(mediaFormat, outputSurface.getSurface(), null, 0); decoder.start(); + if (Build.VERSION.SDK_INT >= 31 && isHdr) { + try { + final String VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY = "vendor.dolby.codec.transfer.value"; + MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY); + if (descriptor != null) { + Bundle transferBundle = new Bundle(); + transferBundle.putString(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY, "transfer.sdr.normal"); + decoder.setParameters(transferBundle); + } + } catch (IllegalStateException e) { + Log.w(TAG, "Failed to set Dolby Vision transfer parameter", e); + } + } long duration = 0; @@ -217,4 +224,5 @@ final class VideoThumbnailsExtractor { } Log.i(TAG, "doExtract finished"); } + } \ No newline at end of file diff --git a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoTrackConverter.java b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoTrackConverter.java index 920a34d707..105965e33f 100644 --- a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoTrackConverter.java +++ b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/VideoTrackConverter.java @@ -481,14 +481,16 @@ final class VideoTrackConverter { final Pair decoderPair = MediaCodecCompat.findDecoder(inputFormat); final MediaCodec decoder = decoderPair.getFirst(); - // Try to use the Dolby Vision decoder, but if it doesn't support the transfer parameter, the decoded video buffer - // is HLG and in-app tone mapping has to be used instead - if (Build.VERSION.SDK_INT >= 31) { + // For HDR video, request SDR tone-mapping from the decoder. Only do this for HDR content + // (PQ or HLG transfer), as some hardware decoders (e.g. Qualcomm HEVC) crash when this is + // set on non-HDR video. + final boolean isHdr = MediaCodecCompat.isHdrVideo(decoderPair.getSecond()); + if (Build.VERSION.SDK_INT >= 31 && isHdr) { decoderPair.getSecond().setInteger(MediaFormat.KEY_COLOR_TRANSFER_REQUEST, MediaFormat.COLOR_TRANSFER_SDR_VIDEO); } decoder.configure(decoderPair.getSecond(), surface, null, 0); decoder.start(); - if (Build.VERSION.SDK_INT >= 31) { + if (Build.VERSION.SDK_INT >= 31 && isHdr) { try { MediaCodec.ParameterDescriptor descriptor = decoder.getParameterDescriptor(VENDOR_DOLBY_CODEC_TRANSFER_PARAMKEY); if (descriptor != null) { diff --git a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/utils/MediaCodecCompat.kt b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/utils/MediaCodecCompat.kt index ca38600c23..334dcc7281 100644 --- a/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/utils/MediaCodecCompat.kt +++ b/lib/video/src/main/java/org/thoughtcrime/securesms/video/videoconverter/utils/MediaCodecCompat.kt @@ -149,4 +149,20 @@ object MediaCodecCompat { } else { null } + + /** + * Returns true if the given [MediaFormat] describes an HDR video (PQ or HLG color transfer). + * Some hardware decoders crash when tone-mapping parameters are set on non-HDR video. + */ + @JvmStatic + fun isHdrVideo(format: MediaFormat): Boolean { + return try { + val colorTransfer = format.getInteger(MediaFormat.KEY_COLOR_TRANSFER) + colorTransfer == MediaFormat.COLOR_TRANSFER_ST2084 || colorTransfer == MediaFormat.COLOR_TRANSFER_HLG + } catch (e: NullPointerException) { + false + } catch (e: ClassCastException) { + false + } + } }