Commit 84b00425dd for ffmpeg

commit 84b00425dd81fff9b315d3a102dcab69a5b345e7
Author: Romain Beauxis <romain.beauxis@gmail.com>
Date:   Fri Oct 16 18:54:50 2026 +0200

    avcodec/proresdec, avformat/mov: report reserved colour tags as unspecified

    The ProRes frame header and the mov colr atom are copied into the codec
    context as they are. ProRes uses 0 for "unknown" in all three fields
    (RDD 36), and some writers store values that have no name, so decoded
    frames carry AVCOL_PRI_RESERVED0, AVCOL_TRC_RESERVED0, AVCOL_SPC_RGB or an
    out of range value. The swscale frame checks reject the reserved ones, so
    scaling such a file fails with ENOTSUP. The RGB matrix is accepted, and the
    YUV data is then converted with the wrong coefficients.

    A ProRes header field with no colour information (0 or 2, both unknown in
    RDD 36, reserved or unnamed) now takes the tag the codec context had when
    the decoder was opened, which is the container's, or unspecified. Each
    frame is resolved on its own, so the result does not depend on earlier
    frames or on where decoding started. Reserved and unnamed colr values are
    reported as unspecified, as h2645_vui.c does for H.264 and HEVC since
    c80002aa81 ("avcodec/h2645_vui: Ensure color primaries/trc/space isn't
    reserved value"). Valid values are untouched.

    Fixes: #25119

diff --git a/libavcodec/proresdec.c b/libavcodec/proresdec.c
index cfcc866885..13eea7b5dd 100644
--- a/libavcodec/proresdec.c
+++ b/libavcodec/proresdec.c
@@ -31,6 +31,7 @@
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 #include "libavutil/mem_internal.h"
+#include "libavutil/pixdesc.h"

 #include "avcodec.h"
 #include "codec_internal.h"
@@ -137,6 +138,10 @@ static av_cold int decode_init(AVCodecContext *avctx)

     avctx->bits_per_raw_sample = 10;

+    ctx->container_color_primaries = avctx->color_primaries;
+    ctx->container_color_trc       = avctx->color_trc;
+    ctx->container_colorspace      = avctx->colorspace;
+
     switch (avctx->codec_tag) {
     case MKTAG('a','p','c','o'):
         avctx->profile = AV_PROFILE_PRORES_PROXY;
@@ -183,6 +188,17 @@ static av_cold int decode_init(AVCodecContext *avctx)
     return 0;
 }

+/**
+ * Whether a frame header colour value describes a colour. 0 and 2 both mean
+ * unknown in all three fields (RDD 36), unlike 0 in H.273; 3 is reserved.
+ *
+ * @param name FFmpeg name of the value, NULL if it has none
+ */
+static int describes_colour(int value, const char *name)
+{
+    return value != 0 && value != 2 && value != 3 && name;
+}
+
 static int decode_frame_header(ProresContext *ctx, const uint8_t *buf,
                                const int data_size, AVCodecContext *avctx)
 {
@@ -280,9 +296,12 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf,
 #endif
     }

-    avctx->color_primaries = buf[14];
-    avctx->color_trc       = buf[15];
-    avctx->colorspace      = buf[16];
+    avctx->color_primaries = describes_colour(buf[14], av_color_primaries_name(buf[14])) ?
+                             buf[14] : ctx->container_color_primaries;
+    avctx->color_trc       = describes_colour(buf[15], av_color_transfer_name(buf[15])) ?
+                             buf[15] : ctx->container_color_trc;
+    avctx->colorspace      = describes_colour(buf[16], av_color_space_name(buf[16])) ?
+                             buf[16] : ctx->container_colorspace;
     avctx->color_range     = AVCOL_RANGE_MPEG;

     ptr   = buf + 20;
diff --git a/libavcodec/proresdec.h b/libavcodec/proresdec.h
index 965fc85b7a..0eaae15f14 100644
--- a/libavcodec/proresdec.h
+++ b/libavcodec/proresdec.h
@@ -61,6 +61,9 @@ typedef struct {
     int alpha_info;
     void (*unpack_alpha)(GetBitContext *gb, uint16_t *dst, int num_coeffs, const int num_bits);
     enum AVPixelFormat pix_fmt;
+    enum AVColorPrimaries container_color_primaries; ///< used when the frame header has none
+    enum AVColorTransferCharacteristic container_color_trc; ///< used when the frame header has none
+    enum AVColorSpace container_colorspace;          ///< used when the frame header has none
 } ProresContext;

 #endif /* AVCODEC_PRORESDEC_H */
diff --git a/libavformat/mov.c b/libavformat/mov.c
index edab98a037..16ef5ce1ae 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -2288,11 +2288,17 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom)
                 st->codecpar->color_range = AVCOL_RANGE_MPEG;
         }

-        if (!av_color_primaries_name(color_primaries))
+        // Reserved and unassigned values carry no colour information
+        if (color_primaries == AVCOL_PRI_RESERVED0 ||
+            color_primaries == AVCOL_PRI_RESERVED  ||
+            !av_color_primaries_name(color_primaries))
             color_primaries = AVCOL_PRI_UNSPECIFIED;
-        if (!av_color_transfer_name(color_trc))
+        if (color_trc == AVCOL_TRC_RESERVED0 ||
+            color_trc == AVCOL_TRC_RESERVED  ||
+            !av_color_transfer_name(color_trc))
             color_trc = AVCOL_TRC_UNSPECIFIED;
-        if (!av_color_space_name(color_matrix))
+        if (color_matrix == AVCOL_SPC_RESERVED ||
+            !av_color_space_name(color_matrix))
             color_matrix = AVCOL_SPC_UNSPECIFIED;

         st->codecpar->color_primaries = color_primaries;
diff --git a/tests/ref/fate/mov-colr-reserved-color-tags b/tests/ref/fate/mov-colr-reserved-color-tags
index 3d8ac8448a..ea12632aa9 100644
--- a/tests/ref/fate/mov-colr-reserved-color-tags
+++ b/tests/ref/fate/mov-colr-reserved-color-tags
@@ -6,6 +6,6 @@ c6b96e10140cb9554d1eb6a5dccc1c1b *tests/data/fate/mov-colr-reserved-color-tags.m
 #dimensions 0: 160x120
 #sar 0: 0/1
 0,          0,          0,     1920,     6838, 0x2d59b149
-color_space=reserved
-color_transfer=reserved
-color_primaries=reserved
+color_space=unknown
+color_transfer=unknown
+color_primaries=unknown
diff --git a/tests/ref/fate/prores-color-tags-fallback b/tests/ref/fate/prores-color-tags-fallback
index f1ef232182..906ca96175 100644
--- a/tests/ref/fate/prores-color-tags-fallback
+++ b/tests/ref/fate/prores-color-tags-fallback
@@ -1,6 +1,6 @@
 color_space=bt709
 color_primaries=bt709
 color_transfer=bt709
-color_space=gbr
-color_primaries=reserved
-color_transfer=reserved
+color_space=smpte170m
+color_primaries=smpte170m
+color_transfer=smpte170m
diff --git a/tests/ref/fate/prores-unknown-color-tags b/tests/ref/fate/prores-unknown-color-tags
index 97a01650da..584a0a8050 100644
--- a/tests/ref/fate/prores-unknown-color-tags
+++ b/tests/ref/fate/prores-unknown-color-tags
@@ -7,6 +7,6 @@ e8fedc135d405134af2dada81b173cc9 *tests/data/fate/prores-unknown-color-tags.mov
 #sar 0: 0/1
 0,          0,          0,      400,    57296, 0x4cc02182
 0,        400,        400,      400,    57296, 0x4cc02182
-color_space=gbr
-color_primaries=reserved
-color_transfer=reserved
+color_space=smpte170m
+color_primaries=smpte170m
+color_transfer=smpte170m