Commit f94d6640 for libheif
commit f94d6640116ba9600aab507caa7f5ea065f0b839
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun Aug 30 13:44:39 2026 +0200
FFmpeg decoder: only attach nclx from codecs that carry CICP signalling
The plugin built an nclx profile from the AVCodecContext colour fields for
every codec. For JPEG and JPEG 2000 these are FFmpeg's synthesized defaults
(unspecified CICP and limited range for JPEG 2000), not signalling read from
the codestream. libheif treats the plugin's profile as the bitstream's and,
for items without a 'colr' box, uses it as the source profile, so 3-component
JPEG 2000 images were run through a limited->full range YCbCr conversion and
came out with wrong sample values (16-bit ones failed with "Unsupported color
conversion"). Only attach the profile for AVC/HEVC/VVC (VUI) and AV1
(sequence header).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QAo5hPipqTKvaQDDpSEtxT
diff --git a/libheif/plugins/decoder_ffmpeg.cc b/libheif/plugins/decoder_ffmpeg.cc
index 3f5313be..53d521f4 100644
--- a/libheif/plugins/decoder_ffmpeg.cc
+++ b/libheif/plugins/decoder_ffmpeg.cc
@@ -74,6 +74,12 @@ static bool supportsNal(AVCodecID id) {
return id == AV_CODEC_ID_H264 || id == AV_CODEC_ID_H265 || id == AV_CODEC_ID_H266;
}
+// Codecs whose bitstream carries CICP colour signalling (VUI / sequence header).
+static bool codec_has_cicp_signalling(AVCodecID id)
+{
+ return supportsNal(id) || id == AV_CODEC_ID_AV1;
+}
+
static const int FFMPEG_DECODER_PLUGIN_PRIORITY = 90;
#define MAX_PLUGIN_NAME_LENGTH 80
@@ -687,6 +693,15 @@ static heif_error ffmpeg_decode_next_image2(void* decoder_raw,
return { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, "avcodec_parameters_from_context returned error" };
}
+ // Only AVC/HEVC/VVC (VUI) and AV1 (sequence header) carry CICP colour signalling in the
+ // bitstream. For JPEG and JPEG 2000, FFmpeg's colour fields are synthesized defaults
+ // (e.g. "unspecified" and limited range for JPEG 2000), not something read from the
+ // codestream. Attaching them as a bitstream profile would make libheif convert the
+ // decoded planes when the file has no 'colr' box. Leave the profile unset for those.
+ if (!codec_has_cicp_signalling(decoder->av_codec->id)) {
+ return heif_error_ok;
+ }
+
uint8_t video_full_range_flag = 0;
uint8_t color_primaries = 0;
uint8_t transfer_characteristics = 0;