Commit 7461dbcb55 for ffmpeg

commit 7461dbcb55932729963efd43530fea492984b5c5
Author: James Almer <jamrial@gmail.com>
Date:   Sat Oct 17 00:59:06 2026 -0300

    avformat/movenc: use bitstream information to write AVIF parameters if needed

    If the pixel format is a hardware one, components will be unset, and we will
    end up writing an invalid pixi box.

    Signed-off-by: James Almer <jamrial@gmail.com>

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 2c349ef663..52a73ba49f 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -3831,16 +3831,29 @@ static int mov_write_ispe_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
 static int mov_write_pixi_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s,
                               int stream_index)
 {
+    MOVTrack *trk = &mov->tracks[stream_index];
     int64_t pos = avio_tell(pb);
     const AVPixFmtDescriptor *pixdesc =
         av_pix_fmt_desc_get(s->streams[stream_index]->codecpar->format);
+    int depth, nb_components;
+    if (pixdesc->nb_components > 0) {
+        depth = pixdesc->comp[0].depth;
+        nb_components = pixdesc->nb_components;
+    } else {
+        AV1SequenceParameters seq;
+        int ret = ff_av1_parse_seq_header(&seq, trk->extradata[trk->last_stsd_index],
+                                                trk->extradata_size[trk->last_stsd_index]);
+        if (ret < 0)
+            return ret;
+        depth = seq.bitdepth;
+        nb_components = seq.monochrome ? 1 : 3;
+    }
     avio_wb32(pb, 0); /* size */
     ffio_wfourcc(pb, "pixi");
     avio_wb32(pb, 0); /* Version & flags */
-    avio_w8(pb, pixdesc->nb_components); /* num_channels */
-    for (int i = 0; i < pixdesc->nb_components; ++i) {
-      avio_w8(pb, pixdesc->comp[i].depth); /* bits_per_channel */
-    }
+    avio_w8(pb, nb_components); /* num_channels */
+    for (int i = 0; i < nb_components; ++i)
+        avio_w8(pb, depth); /* bits_per_channel */
     return update_size(pb, pos);
 }

@@ -6373,9 +6386,25 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
     if (mov->mode == MODE_ISM) {
         ffio_wfourcc(pb, "piff");
     } else if (mov->mode == MODE_AVIF) {
+        const MOVTrack *trk = &mov->tracks[0];
         const AVPixFmtDescriptor *pix_fmt_desc =
             av_pix_fmt_desc_get(s->streams[0]->codecpar->format);
-        const int depth = pix_fmt_desc->comp[0].depth;
+        int depth = 0, log2_chroma_w, log2_chroma_h;
+
+        if (pix_fmt_desc->nb_components > 0) {
+            depth = pix_fmt_desc->comp[0].depth;
+            log2_chroma_w = pix_fmt_desc->log2_chroma_w;
+            log2_chroma_h = pix_fmt_desc->log2_chroma_h;
+        } else {
+            AV1SequenceParameters seq;
+            int ret = ff_av1_parse_seq_header(&seq, trk->extradata[trk->last_stsd_index],
+                                                    trk->extradata_size[trk->last_stsd_index]);
+            if (!ret) {
+                depth = seq.bitdepth;
+                log2_chroma_w = seq.chroma_subsampling_x;
+                log2_chroma_h = seq.chroma_subsampling_y;
+            }
+        }
         if (mov->is_animated_avif) {
             // For animated AVIF, major brand is "avis". Add "avif" as a
             // compatible brand.
@@ -6389,7 +6418,7 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
             // MA1B and MA1A brands are based on AV1 profile. Short hand for
             // computing that is based on chroma subsampling type. 420 chroma
             // subsampling is MA1B.  444 chroma subsampling is MA1A.
-            if (!pix_fmt_desc->log2_chroma_w && !pix_fmt_desc->log2_chroma_h) {
+            if (!log2_chroma_w && !log2_chroma_h) {
                 // 444 chroma subsampling.
                 ffio_wfourcc(pb, "MA1A");
             } else {