Commit 7fd38bc06a for ffmpeg
commit 7fd38bc06ac28fd58b8951af240f1fd20d6fdc0d
Author: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Thu Nov 12 15:57:48 2026 -0800
avcodec/proresenc: write only RDD 36 colour codes in the frame header
prores_ks wrote reserved codes as-is and prores_aw dropped equivalent ones;
map bt470bg matrix and bt601/bt2020 transfers to their codes, others to unspecified.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Baptiste Coudurier <baptiste.coudurier@gmail.com>
diff --git a/libavcodec/proresdata.c b/libavcodec/proresdata.c
index 00af97d4f1..d386008f9d 100644
--- a/libavcodec/proresdata.c
+++ b/libavcodec/proresdata.c
@@ -51,3 +51,54 @@ const uint8_t ff_prores_run_to_cb[16] = { 0x06, 0x06, 0x05, 0x05, 0x04, 0x29,
const uint8_t ff_prores_level_to_cb[10] = { 0x04, 0x0A, 0x05, 0x06, 0x04, 0x28,
0x28, 0x28, 0x28, 0x4C };
+
+int ff_prores_color_primaries(enum AVColorPrimaries color_primaries)
+{
+ switch (color_primaries) {
+ case AVCOL_PRI_RESERVED0:
+ case AVCOL_PRI_BT709:
+ case AVCOL_PRI_UNSPECIFIED:
+ case AVCOL_PRI_BT470BG:
+ case AVCOL_PRI_SMPTE170M:
+ case AVCOL_PRI_BT2020:
+ case AVCOL_PRI_SMPTE431:
+ case AVCOL_PRI_SMPTE432:
+ return color_primaries;
+ default:
+ return AVCOL_PRI_UNSPECIFIED;
+ }
+}
+
+int ff_prores_color_trc(enum AVColorTransferCharacteristic color_trc)
+{
+ switch (color_trc) {
+ case AVCOL_TRC_RESERVED0:
+ case AVCOL_TRC_BT709:
+ case AVCOL_TRC_UNSPECIFIED:
+ case AVCOL_TRC_SMPTE2084:
+ case AVCOL_TRC_ARIB_STD_B67:
+ return color_trc;
+ case AVCOL_TRC_SMPTE170M:
+ case AVCOL_TRC_BT2020_10:
+ case AVCOL_TRC_BT2020_12:
+ return AVCOL_TRC_BT709;
+ default:
+ return AVCOL_TRC_UNSPECIFIED;
+ }
+}
+
+int ff_prores_colorspace(enum AVColorSpace colorspace)
+{
+ switch (colorspace) {
+ case AVCOL_SPC_RGB:
+ case AVCOL_SPC_BT709:
+ case AVCOL_SPC_UNSPECIFIED:
+ case AVCOL_SPC_SMPTE170M:
+ case AVCOL_SPC_BT2020_NCL:
+ return colorspace;
+ case AVCOL_SPC_BT470BG:
+ return AVCOL_SPC_SMPTE170M;
+ default:
+ return AVCOL_SPC_UNSPECIFIED;
+ }
+}
diff --git a/libavcodec/proresdata.h b/libavcodec/proresdata.h
index d859e93e88..1b727d2626 100644
--- a/libavcodec/proresdata.h
+++ b/libavcodec/proresdata.h
@@ -25,6 +25,8 @@
#include <stdint.h>
+#include "libavutil/pixfmt.h"
+
#define FRAME_ID MKBETAG('i', 'c', 'p', 'f')
extern const uint8_t ff_prores_progressive_scan[64];
@@ -35,4 +37,12 @@ extern const uint8_t ff_prores_level_to_cb[10];
#define FIRST_DC_CB 0xB8 // rice_order = 5, exp_golomb_order = 6, switch_bits = 0
+/**
+ * Map color properties to the codes a frame header can carry (SMPTE RDD 36),
+ * substituting an equivalent code or unspecified for the others.
+ */
+int ff_prores_color_primaries(enum AVColorPrimaries color_primaries);
+int ff_prores_color_trc(enum AVColorTransferCharacteristic color_trc);
+int ff_prores_colorspace(enum AVColorSpace colorspace);
+
#endif /* AVCODEC_PRORESDATA_H */
diff --git a/libavcodec/proresenc_anatoliy.c b/libavcodec/proresenc_anatoliy.c
index d8c5a48300..2cbbfdceb0 100644
--- a/libavcodec/proresenc_anatoliy.c
+++ b/libavcodec/proresenc_anatoliy.c
@@ -56,13 +56,6 @@ static const int qp_start_table[] = { 8, 3, 2, 1, 1, 1};
static const int qp_end_table[] = { 13, 9, 6, 6, 5, 4};
static const int bitrate_table[] = { 1000, 2100, 3500, 5400, 7000, 10000};
-static const int valid_primaries[] = { AVCOL_PRI_RESERVED0, AVCOL_PRI_BT709, AVCOL_PRI_UNSPECIFIED, AVCOL_PRI_BT470BG,
- AVCOL_PRI_SMPTE170M, AVCOL_PRI_BT2020, AVCOL_PRI_SMPTE431, AVCOL_PRI_SMPTE432, INT_MAX };
-static const int valid_trc[] = { AVCOL_TRC_RESERVED0, AVCOL_TRC_BT709, AVCOL_TRC_UNSPECIFIED, AVCOL_TRC_SMPTE2084,
- AVCOL_TRC_ARIB_STD_B67, INT_MAX };
-static const int valid_colorspace[] = { AVCOL_SPC_BT709, AVCOL_SPC_UNSPECIFIED, AVCOL_SPC_SMPTE170M,
- AVCOL_SPC_BT2020_NCL, INT_MAX };
-
static const uint8_t QMAT_LUMA[6][64] = {
{
4, 7, 9, 11, 13, 14, 15, 63,
@@ -199,35 +192,6 @@ typedef struct {
char *vendor;
} ProresContext;
-/**
- * Check if a value is in the list. If not, return the default value
- *
- * @param ctx Context for the log msg
- * @param val_name Name of the checked value, for log msg
- * @param array_valid_values Array of valid int, ended with INT_MAX
- * @param default_value Value return if checked value is not in the array
- * @return Value or default_value.
- */
-static int int_from_list_or_default(void *ctx, const char *val_name, int val,
- const int *array_valid_values, int default_value)
-{
- int i = 0;
-
- while (1) {
- int ref_val = array_valid_values[i];
- if (ref_val == INT_MAX)
- break;
- if (val == ref_val)
- return val;
- i++;
- }
- /* val is not a valid value */
- av_log(ctx, AV_LOG_DEBUG,
- "%s %d are not supported. Set to default value : %d\n",
- val_name, val, default_value);
- return default_value;
-}
-
static void encode_vlc_codeword(PutBitContext *pb, unsigned codebook, int val)
{
unsigned int rice_order, exp_order, switch_bits, switch_val;
@@ -769,13 +733,9 @@ static int prores_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
}
*buf++ = frame_flags;
*buf++ = 0; /* reserved */
- /* only write color properties, if valid value. set to unspecified otherwise */
- *buf++ = int_from_list_or_default(avctx, "frame color primaries",
- pict->color_primaries, valid_primaries, 0);
- *buf++ = int_from_list_or_default(avctx, "frame color trc",
- pict->color_trc, valid_trc, 0);
- *buf++ = int_from_list_or_default(avctx, "frame colorspace",
- pict->colorspace, valid_colorspace, 0);
+ *buf++ = ff_prores_color_primaries(pict->color_primaries);
+ *buf++ = ff_prores_color_trc(pict->color_trc);
+ *buf++ = ff_prores_colorspace(pict->colorspace);
*buf++ = ctx->need_alpha ? 0x2 /* 16-bit alpha */ : 0;
*buf++ = 0; /* reserved */
*buf++ = 3; /* luma and chroma matrix present */
diff --git a/libavcodec/proresenc_kostya.c b/libavcodec/proresenc_kostya.c
index bf7c4bda43..944a6113ef 100644
--- a/libavcodec/proresenc_kostya.c
+++ b/libavcodec/proresenc_kostya.c
@@ -792,9 +792,9 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
bytestream_put_byte (&buf, frame_flags);
bytestream_put_byte (&buf, 0); // reserved
- bytestream_put_byte (&buf, pic->color_primaries);
- bytestream_put_byte (&buf, pic->color_trc);
- bytestream_put_byte (&buf, pic->colorspace);
+ bytestream_put_byte (&buf, ff_prores_color_primaries(pic->color_primaries));
+ bytestream_put_byte (&buf, ff_prores_color_trc(pic->color_trc));
+ bytestream_put_byte (&buf, ff_prores_colorspace(pic->colorspace));
bytestream_put_byte (&buf, ctx->alpha_bits >> 3);
bytestream_put_byte (&buf, 0); // reserved
if (ctx->quant_sel != QUANT_MAT_DEFAULT) {
diff --git a/libavcodec/proresenc_kostya_common.c b/libavcodec/proresenc_kostya_common.c
index 173268fff8..4ae5c23bbe 100644
--- a/libavcodec/proresenc_kostya_common.c
+++ b/libavcodec/proresenc_kostya_common.c
@@ -347,9 +347,9 @@ uint8_t *ff_prores_kostya_write_frame_header(AVCodecContext *avctx, ProresContex
bytestream_put_byte (&buf, frame_flags);
bytestream_put_byte (&buf, 0); // reserved
- bytestream_put_byte (&buf, color_primaries);
- bytestream_put_byte (&buf, color_trc);
- bytestream_put_byte (&buf, colorspace);
+ bytestream_put_byte (&buf, ff_prores_color_primaries(color_primaries));
+ bytestream_put_byte (&buf, ff_prores_color_trc(color_trc));
+ bytestream_put_byte (&buf, ff_prores_colorspace(colorspace));
bytestream_put_byte (&buf, ctx->alpha_bits >> 3);
bytestream_put_byte (&buf, 0); // reserved
if (ctx->quant_sel != QUANT_MAT_DEFAULT) {
diff --git a/libavcodec/proresenc_kostya_vulkan.c b/libavcodec/proresenc_kostya_vulkan.c
index 8fa0d7f128..7ae2494962 100644
--- a/libavcodec/proresenc_kostya_vulkan.c
+++ b/libavcodec/proresenc_kostya_vulkan.c
@@ -621,9 +621,9 @@ static uint8_t *write_frame_header(AVCodecContext *avctx, ProresContext *ctx,
bytestream_put_byte (&buf, frame_flags);
bytestream_put_byte (&buf, 0); // reserved
- bytestream_put_byte (&buf, color_primaries);
- bytestream_put_byte (&buf, color_trc);
- bytestream_put_byte (&buf, colorspace);
+ bytestream_put_byte (&buf, ff_prores_color_primaries(color_primaries));
+ bytestream_put_byte (&buf, ff_prores_color_trc(color_trc));
+ bytestream_put_byte (&buf, ff_prores_colorspace(colorspace));
bytestream_put_byte (&buf, ctx->alpha_bits >> 3);
bytestream_put_byte (&buf, 0); // reserved
if (ctx->quant_sel != QUANT_MAT_DEFAULT) {
diff --git a/tests/fate/prores.mak b/tests/fate/prores.mak
index 7bc7d7de87..3c98d9f175 100644
--- a/tests/fate/prores.mak
+++ b/tests/fate/prores.mak
@@ -40,5 +40,20 @@ fate-prores-color-tags-fallback: CMD = run ffprobe$(PROGSSUF)$(EXESUF) -v error
-show_entries frame=color_primaries,color_transfer,color_space -of default=nw=1 \
$(TARGET_SAMPLES)/prores/color-tags-fallback.mov
+# Frame tags without a ProRes code get an equivalent code or unspecified in the frame header.
+# The decoder is told the container tags are unspecified so the decoded tags come from the frame header.
+PRORES_COLOR_TAGS_ENC = -frames:v 1 -vf scale,format=yuv422p10,setparams=color_primaries=smpte240m:color_trc=bt2020-10:colorspace=bt470bg
+PRORES_COLOR_TAGS_PROBE = "-color_primaries unknown -color_trc unknown -colorspace unknown -show_entries frame=color_primaries,color_transfer,color_space -of default=nw=1"
+PRORES_COLOR_TAGS_DEPS = RAWVIDEO_DEMUXER RAWVIDEO_DECODER SETPARAMS_FILTER FORMAT_FILTER SCALE_FILTER
+FATE_PRORES_COLOR_TAGS-$(call TRANSCODE, PRORES_AW PRORES, MOV, $(PRORES_COLOR_TAGS_DEPS)) += fate-prores-aw-color-tags
+fate-prores-aw-color-tags: CMD = transcode "rawvideo -s 352x288 -pix_fmt yuv420p" tests/data/vsynth1.yuv mov \
+ "-c:v prores_aw $(PRORES_COLOR_TAGS_ENC)" "-c:v copy" $(PRORES_COLOR_TAGS_PROBE)
+FATE_PRORES_COLOR_TAGS-$(call TRANSCODE, PRORES_KS PRORES, MOV, $(PRORES_COLOR_TAGS_DEPS)) += fate-prores-ks-color-tags
+fate-prores-ks-color-tags: CMD = transcode "rawvideo -s 352x288 -pix_fmt yuv420p" tests/data/vsynth1.yuv mov \
+ "-c:v prores_ks $(PRORES_COLOR_TAGS_ENC)" "-c:v copy" $(PRORES_COLOR_TAGS_PROBE)
+$(FATE_PRORES_COLOR_TAGS-yes): tests/data/vsynth1.yuv
+FATE_FFMPEG_FFPROBE += $(FATE_PRORES_COLOR_TAGS-yes)
+fate-prores-color-tags: $(FATE_PRORES_COLOR_TAGS-yes)
+
FATE_SAMPLES_FFMPEG-$(call DEMMUX, MOV, MOV, PRORES_METADATA_BSF) += $(FATE_PRORES_METADATA_BSF)
FATE_SAMPLES_FFPROBE += $(FATE_PRORES_FFPROBE-yes)
diff --git a/tests/ref/fate/prores-aw-color-tags b/tests/ref/fate/prores-aw-color-tags
new file mode 100644
index 0000000000..1e1b8a9d4f
--- /dev/null
+++ b/tests/ref/fate/prores-aw-color-tags
@@ -0,0 +1,11 @@
+a8f6ac525b5969628b57ea7790b0a6dc *tests/data/fate/prores-aw-color-tags.mov
+101914 tests/data/fate/prores-aw-color-tags.mov
+#tb 0: 1/12800
+#media_type 0: video
+#codec_id 0: prores
+#dimensions 0: 352x288
+#sar 0: 0/1
+0, 0, 0, 512, 101203, 0x8c33d3ee
+color_space=smpte170m
+color_primaries=unknown
+color_transfer=bt709
diff --git a/tests/ref/fate/prores-ks-color-tags b/tests/ref/fate/prores-ks-color-tags
new file mode 100644
index 0000000000..10a1c0ab4a
--- /dev/null
+++ b/tests/ref/fate/prores-ks-color-tags
@@ -0,0 +1,11 @@
+30647bfc1da947fe42c90e9c169df5f3 *tests/data/fate/prores-ks-color-tags.mov
+77871 tests/data/fate/prores-ks-color-tags.mov
+#tb 0: 1/12800
+#media_type 0: video
+#codec_id 0: prores
+#dimensions 0: 352x288
+#sar 0: 0/1
+0, 0, 0, 512, 77160, 0x31bba40d
+color_space=smpte170m
+color_primaries=unknown
+color_transfer=bt709