Commit 7905079bef for ffmpeg

commit 7905079bef56b67198be178a369a719826e67cd5
Author: Lynne <dev@lynne.ee>
Date:   Thu Oct 8 13:27:47 2026 +0900

    lavc: add a Panasonic RW2 parser

    Reads IFD0 from the first chunk to set the dimensions and pixel format,
    so that probing fills in the stream parameters without decoding a frame.

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 93d526ee56..3d2200af8c 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1309,6 +1309,7 @@ OBJS-$(CONFIG_PRORES_RAW_PARSER)       += prores_raw_parser.o
 OBJS-$(CONFIG_QOI_PARSER)              += qoi_parser.o
 OBJS-$(CONFIG_PRORES_PARSER)           += prores_parser.o
 OBJS-$(CONFIG_RV34_PARSER)             += rv34_parser.o
+OBJS-$(CONFIG_RW2_PARSER)              += rw2_parser.o
 OBJS-$(CONFIG_SBC_PARSER)              += sbc_parser.o
 OBJS-$(CONFIG_SIPR_PARSER)             += sipr_parser.o
 OBJS-$(CONFIG_TAK_PARSER)              += tak_parser.o tak.o
diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c
index b54442a0ea..263b606407 100644
--- a/libavcodec/parsers.c
+++ b/libavcodec/parsers.c
@@ -76,6 +76,7 @@ extern const FFCodecParser ff_pnm_parser;
 extern const FFCodecParser ff_prores_raw_parser;
 extern const FFCodecParser ff_qoi_parser;
 extern const FFCodecParser ff_rv34_parser;
+extern const FFCodecParser ff_rw2_parser;
 extern const FFCodecParser ff_sbc_parser;
 extern const FFCodecParser ff_sipr_parser;
 extern const FFCodecParser ff_tak_parser;
diff --git a/libavcodec/rw2_parser.c b/libavcodec/rw2_parser.c
new file mode 100644
index 0000000000..00c0ce81d0
--- /dev/null
+++ b/libavcodec/rw2_parser.c
@@ -0,0 +1,126 @@
+/*
+ * Panasonic RW2 parser
+ * Copyright (c) 2026 Lynne <dev@lynne.ee>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Delimits frames on the "IIU\0" magic and reads IFD0 to set the dimensions
+ * and pixel format without decoding.
+ */
+
+#include "libavutil/common.h"
+#include "libavutil/intreadwrite.h"
+
+#include "parser.h"
+#include "parser_internal.h"
+
+typedef struct RW2ParseContext {
+    ParseContext pc;
+} RW2ParseContext;
+
+static void rw2_set_params(AVCodecParserContext *s, AVCodecContext *avctx,
+                           const uint8_t *buf, int buf_size)
+{
+    static const enum AVPixelFormat cfa_fmts[] = {
+        AV_PIX_FMT_BAYER_RGGB16, AV_PIX_FMT_BAYER_GRBG16,
+        AV_PIX_FMT_BAYER_GBRG16, AV_PIX_FMT_BAYER_BGGR16,
+    };
+
+    if (buf_size < 8 || AV_RL32(buf) != MKTAG('I', 'I', 'U', 0))
+        return;
+
+    int64_t ifd = AV_RL32(buf + 4);
+    if (ifd < 8 || ifd + 2 > buf_size)
+        return;
+
+    int width = 0, height = 0, cfa = 0, bpp = 0;
+    int n = FFMIN(AV_RL16(buf + ifd), (buf_size - ifd - 2) / 12);
+    for (int i = 0; i < n; i++) {
+        const uint8_t *e = buf + ifd + 2 + 12*i;
+        unsigned tag = AV_RL16(e), type = AV_RL16(e + 2), val = AV_RL32(e + 8);
+        unsigned v = type == 3 ? val & 0xFFFF : val;
+
+        switch (tag) {
+        case 0x02: width  = v; break;
+        case 0x03: height = v; break;
+        case 0x09: cfa    = v; break;
+        case 0x0A: bpp    = v; break;
+        }
+    }
+
+    if (width > 0 && height > 0 && cfa >= 1 && cfa <= 4) {
+        s->width         = width;
+        s->height        = height;
+        s->format        = cfa_fmts[cfa - 1];
+        avctx->color_trc = AVCOL_TRC_LINEAR;
+        if (bpp >= 8 && bpp <= 16)
+            avctx->bits_per_raw_sample = bpp;
+    }
+}
+
+static int rw2_parse(AVCodecParserContext *s, AVCodecContext *avctx,
+                     const uint8_t **poutbuf, int *poutbuf_size,
+                     const uint8_t *buf, int buf_size)
+{
+    RW2ParseContext *ipc = s->priv_data;
+    uint32_t state = ipc->pc.state;
+    int next = END_NOT_FOUND;
+
+    s->key_frame = 1;
+    s->pict_type = AV_PICTURE_TYPE_I;
+
+    /* The header lands in the first chunk, so the parameters are known without
+     * waiting for, or decoding, the whole frame */
+    if (buf_size >= 8 && AV_RL32(buf) == MKTAG('I', 'I', 'U', 0))
+        rw2_set_params(s, avctx, buf, buf_size);
+
+    if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) {
+        next = buf_size;
+    } else {
+        for (int i = 0; i < buf_size; i++) {
+            state = (state << 8) | buf[i];
+            if (state == MKBETAG('I', 'I', 'U', 0)) {
+                /* The next frame's magic ends this one */
+                if (ipc->pc.frame_start_found) {
+                    next = i - 3;
+                    ipc->pc.frame_start_found = 0;
+                    break;
+                }
+                ipc->pc.frame_start_found = 1;
+            }
+        }
+        ipc->pc.state = state;
+        if (ff_combine_frame(&ipc->pc, next, &buf, &buf_size) < 0) {
+            *poutbuf      = NULL;
+            *poutbuf_size = 0;
+            return buf_size;
+        }
+    }
+
+    *poutbuf      = buf;
+    *poutbuf_size = buf_size;
+    return next;
+}
+
+const FFCodecParser ff_rw2_parser = {
+    PARSER_CODEC_LIST(AV_CODEC_ID_RW2),
+    .priv_data_size = sizeof(RW2ParseContext),
+    .parse          = rw2_parse,
+    .close          = ff_parse_close,
+};