PR #22917 opened by Jack Dunford (dun4d)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22917
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22917.patch

AJPG is a format supported by some early webcams. Bayer sensor data stored in 
JPEG.

Signed-off-by: Jack Dunford <[email protected]>


>From 6de9be1b632b079100a7f35c163894ce6e6d88d4 Mon Sep 17 00:00:00 2001
From: dun4d <[email protected]>
Date: Fri, 24 Apr 2026 22:35:45 +0100
Subject: [PATCH] libavcodec/ajpgdec: AJPG decoder

AJPG is a format supported by some early webcams. Bayer sensor data stored in 
JPEG.

Signed-off-by: Jack Dunford <[email protected]>
---
 libavcodec/ajpgdec.c | 512 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 512 insertions(+)
 create mode 100644 libavcodec/ajpgdec.c

diff --git a/libavcodec/ajpgdec.c b/libavcodec/ajpgdec.c
new file mode 100644
index 0000000000..efe978dfe0
--- /dev/null
+++ b/libavcodec/ajpgdec.c
@@ -0,0 +1,512 @@
+/*
+ * AJPG (AIPTEK, Kensington) JPEG Decoder.
+ * Copyright (c) 2026 Jack Dunford
+ *
+ * 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
+ */
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/common.h"
+#include "libavutil/opt.h"
+#include <stddef.h>
+
+/* JPEG Annex K Huffman tables as a prebuilt DHT blob. */
+static const uint8_t annex_k_dht[] = {
+    /* Luma DC  (Tc=0, Th=0) */
+    0xFF,0xC4, 0x00,0x1F, 0x00,
+    0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,
+    0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,
+    /* Luma AC  (Tc=1, Th=0) */
+    0xFF,0xC4, 0x00,0xB5, 0x10,
+    0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,
+    0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D,
+    0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,
+    0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,
+    0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0,0x24,0x33,0x62,0x72,
+    0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28,
+    0x29,0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,
+    0x46,0x47,0x48,0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
+    0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x73,0x74,0x75,
+    0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
+    0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,
+    0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,
+    0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,
+    0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2,
+    0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,
+    0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,
+    /* Chroma DC  (Tc=0, Th=1) */
+    0xFF,0xC4, 0x00,0x1F, 0x01,
+    0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,
+    0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
+    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,
+    /* Chroma AC  (Tc=1, Th=1) */
+    0xFF,0xC4, 0x00,0xB5, 0x11,
+    0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,
+    0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77,
+    0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,
+    0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
+    0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0,0x15,0x62,0x72,0xD1,
+    0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26,
+    0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,
+    0x45,0x46,0x47,0x48,0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,
+    0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x73,0x74,
+    0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87,
+    0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,
+    0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,
+    0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
+    0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,
+    0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,
+    0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,
+};
+
+/* Dodgy header from sample files */
+static const uint8_t old_sof0[] = {
+    0xFF, 0xC0, 0x00, 0x14, 0x08, 0x01, 0xE0, 0x02, 0x80, 0x04,
+    0x01, 0x22, 0x00, 0x02, 0x22, 0x00, 0x03, 0x22, 0x00, 0x04, 0x22, 0x00
+};
+
+/* State context for the decoder */
+typedef struct AJPGContext {
+    const AVClass  *class;         
+    AVCodecContext *mjpeg_avctx;   
+    AVPacket       *pkt;           
+    AVFrame        *tmp;           
+    int            bayer_pattern;  
+    /* AJPG or MJPEG */
+    int            format_probed;
+    int            is_aiptek_bayer;
+} AJPGContext;
+
+/* Command line options to change bayer table -bayer */
+#define OFFSET(x) offsetof(AJPGContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+
+static const AVOption ajpg_options[] = {
+    { "bayer", "Bayer color pattern", OFFSET(bayer_pattern), AV_OPT_TYPE_INT, 
{.i64 = -1}, -2, INT_MAX, VD, .unit = "bayer" },
+    { "none", "Disable weaving (output raw CMYK frame)", 0, AV_OPT_TYPE_CONST, 
{.i64 = -2}, 0, 0, VD, .unit = "bayer" },
+    { "auto", "Auto-detect from metadata", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 
0, 0, VD, .unit = "bayer" },
+    { "rggb", "RGGB", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PIX_FMT_BAYER_RGGB8}, 
0, 0, VD, .unit = "bayer" },
+    { "bggr", "BGGR", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PIX_FMT_BAYER_BGGR8}, 
0, 0, VD, .unit = "bayer" },
+    { "gbrg", "GBRG", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PIX_FMT_BAYER_GBRG8}, 
0, 0, VD, .unit = "bayer" },
+    { "grbg", "GRBG", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PIX_FMT_BAYER_GRBG8}, 
0, 0, VD, .unit = "bayer" },
+    { NULL }
+};
+
+static const AVClass ajpg_class = {
+    .class_name = "ajpg",
+    .item_name  = av_default_item_name,
+    .option     = ajpg_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+/* JPG Markers */
+static int ajpg_patch_sof0(uint8_t *buf, int size)
+{
+    int i, off, h, w, comp, c;
+
+    for (i = 0; i < size - 8; i++) {
+        if (buf[i] != 0xFF || buf[i + 1] != 0xC0)
+            continue;
+
+        off = i + 4;
+        if (off + 6 > size)
+            return AVERROR_INVALIDDATA;
+
+        h = (buf[off + 1] << 8) | buf[off + 2];
+        w = (buf[off + 3] << 8) | buf[off + 4];
+        h /= 2; 
+        w /= 2;
+        
+        buf[off + 1] = (h >> 8) & 0xFF;
+        buf[off + 2] =  h       & 0xFF;
+        buf[off + 3] = (w >> 8) & 0xFF;
+        buf[off + 4] =  w       & 0xFF;
+
+        buf[off + 5] = 4;
+
+        comp = off + 6;
+        for (c = 0; c < 4; c++) {
+            if (comp + 3 > size)
+                return AVERROR_INVALIDDATA;
+            buf[comp + 1] = 0x11;
+            comp += 3;
+        }
+        return 0;
+    }
+    return AVERROR_INVALIDDATA;
+}
+
+static const uint8_t *find_soi(const uint8_t *buf, int size)
+{
+    for (int i = 0; i < size - 1; i++)
+        if (buf[i] == 0xFF && buf[i+1] == 0xD8)
+            return buf + i;
+    return NULL;
+}
+
+static const uint8_t *find_marker(const uint8_t *buf, int size, uint8_t marker)
+{
+    int i = 2; 
+    uint8_t m;
+    int len;
+
+    while (i + 1 < size) {
+        if (buf[i] != 0xFF) { 
+            i++; 
+            continue; 
+        }
+        m = buf[i+1];
+        if (m == marker)
+            return buf + i;
+            
+        if (m == 0xD8 || m == 0xD9 || (m >= 0xD0 && m <= 0xD7)) {
+            i += 2; 
+            continue;
+        }
+        
+        if (i + 3 >= size)
+            break;
+        len = (buf[i+2] << 8) | buf[i+3];
+        if (len < 2)
+            break;
+        i += 2 + len;
+    }
+    return NULL;
+}
+
+/* BAYER Auto Detect */
+static int detect_bayer_pattern(const uint8_t *buf, int size, int user_choice)
+{
+    const uint8_t *app1;
+    uint8_t pattern_id;
+
+    if (user_choice != -1)
+        return user_choice;
+
+    app1 = find_marker(buf, size, 0xE1);
+    if (app1 && size - (app1 - buf) >= 16) {
+        if (memcmp(app1 + 4, "EPJPGBYR", 8) == 0) {
+            pattern_id = app1[13]; 
+            switch (pattern_id) {
+                case 0x00: return AV_PIX_FMT_BAYER_RGGB8;
+                case 0x01: return AV_PIX_FMT_BAYER_GBRG8;
+                case 0x02: return AV_PIX_FMT_BAYER_BGGR8;
+                case 0x03: return AV_PIX_FMT_BAYER_GRBG8;
+            }
+        }
+    }
+    return AV_PIX_FMT_BAYER_RGGB8;
+}
+
+/* AJPG Init */
+static av_cold int ajpg_decode_init(AVCodecContext *avctx)
+{
+    AJPGContext *s = avctx->priv_data;
+    const AVCodec *mjpeg = avcodec_find_decoder(AV_CODEC_ID_MJPEG);
+    int ret;
+
+    if (!mjpeg) {
+        av_log(avctx, AV_LOG_ERROR, "Internal MJPEG decoder not available\n");
+        return AVERROR_DECODER_NOT_FOUND;
+    }
+
+       s->mjpeg_avctx = avcodec_alloc_context3(mjpeg);
+           if (!s->mjpeg_avctx)
+               return AVERROR(ENOMEM);
+
+    ret = avcodec_open2(s->mjpeg_avctx, mjpeg, NULL);
+    if (ret < 0) {
+        avcodec_free_context(&s->mjpeg_avctx);
+        return ret;
+    }
+
+    s->pkt = av_packet_alloc();
+    s->tmp = av_frame_alloc();
+    if (!s->pkt || !s->tmp) {
+        avcodec_free_context(&s->mjpeg_avctx);
+        av_packet_free(&s->pkt);
+        av_frame_free(&s->tmp);
+        return AVERROR(ENOMEM);
+    }
+    
+    s->format_probed = 0;
+    return 0;
+}
+
+/* Cold open */
+static av_cold int ajpg_decode_end(AVCodecContext *avctx)
+{
+    AJPGContext *s = avctx->priv_data;
+    avcodec_free_context(&s->mjpeg_avctx);
+    av_packet_free(&s->pkt);
+    av_frame_free(&s->tmp);
+    return 0;
+}
+
+/* Frame decode */
+static int ajpg_decode_frame(AVCodecContext *avctx, AVFrame *frame,
+                             int *got_frame, AVPacket *avpkt)
+{
+    AJPGContext *s   = avctx->priv_data;
+    const uint8_t *src      = avpkt->data;
+    int            src_size = avpkt->size;
+    uint8_t       *patched  = NULL;
+    int            patched_size = 0;
+    int            owns_patched = 0;
+    
+    /* ALL variables declared at the top to satisfy C89 */
+    int ret, skipped, has_dht, needs_sof_patch;
+    int pre, post, dht_size;
+    const uint8_t *soi, *sof, *sos, *app1;
+
+    if (src_size == 0) {
+        *got_frame = 0;
+        return 0;
+    }
+
+    av_packet_unref(s->pkt);
+    av_frame_unref(s->tmp);
+
+    soi = find_soi(src, src_size);
+    if (!soi) {
+        av_log(avctx, AV_LOG_ERROR, "No SOI marker\n");
+        return AVERROR_INVALIDDATA;
+    }
+    skipped = (int)(soi - src);
+    src_size -= skipped;
+    src = soi;
+
+    /* First-Frame Fingerprint Probing --- */
+    if (!s->format_probed) {
+        /* Assume MJPEG until not */
+        s->is_aiptek_bayer = 0;
+        
+        if (find_marker(src, src_size, 0xC4) == NULL) {
+            s->is_aiptek_bayer = 1;
+        }
+        
+        app1 = find_marker(src, src_size, 0xE1);
+        if (app1 && src_size - (app1 - src) >= 16) {
+            if (memcmp(app1 + 4, "EPJPGBYR", 8) == 0) {
+                s->is_aiptek_bayer = 1;
+            }
+        }
+        
+        if (s->is_aiptek_bayer) {
+            av_log(avctx, AV_LOG_INFO, "Detected AIPTEK Bayer stream. Enabling 
AJPG pipeline.\n");
+        } else {
+            av_log(avctx, AV_LOG_INFO, "Detected standard MJPEG stream. 
Bypassing AJPG pipeline.\n");
+        }
+        s->format_probed = 1;
+    }
+
+    /* --- MJPEG Passthrough --- */
+    if (!s->is_aiptek_bayer) {
+        /* Pass the actual avpkt, NOT the empty s->pkt! */
+        ret = avcodec_send_packet(s->mjpeg_avctx, avpkt);
+        if (ret < 0)
+            return ret;
+        
+        ret = avcodec_receive_frame(s->mjpeg_avctx, frame);
+        if (ret < 0)
+            return ret;
+        
+        avctx->width   = s->mjpeg_avctx->width;
+        avctx->height  = s->mjpeg_avctx->height;
+        avctx->pix_fmt = s->mjpeg_avctx->pix_fmt;
+        
+        *got_frame = 1;
+        return avpkt->size;
+    }
+
+    /* --- 1: Stream Analysis and Patching --- */
+    has_dht = (find_marker(src, src_size, 0xC4) != NULL);
+    sof = find_marker(src, src_size, 0xC0);
+    needs_sof_patch = 0;
+
+    if (sof && (src_size - (sof - src) >= sizeof(old_sof0))) {
+        if (memcmp(sof, old_sof0, sizeof(old_sof0)) == 0)
+            needs_sof_patch = 1;
+    }
+
+    if (!needs_sof_patch && sof && (src_size - (sof - src) >= 20)) {
+        if (sof[11] == 0x22)
+            needs_sof_patch = 1;
+    }
+
+    if (has_dht && !needs_sof_patch) {
+        patched      = (uint8_t *)src;
+        patched_size = src_size;
+    } else {
+        sos = find_marker(src, src_size, 0xDA);
+        if (!sos)
+            return AVERROR_INVALIDDATA;
+
+        pre      = (int)(sos - src);
+        post     = src_size - pre;
+        dht_size = has_dht ? 0 : sizeof(annex_k_dht);
+
+        patched_size = pre + dht_size + post;
+        patched = av_malloc(patched_size + AV_INPUT_BUFFER_PADDING_SIZE + 2);
+        if (!patched)
+            return AVERROR(ENOMEM);
+        owns_patched = 1;
+
+        if (has_dht) {
+            memcpy(patched, src, src_size);
+        } else {
+            memcpy(patched, src, pre);
+            memcpy(patched + pre, annex_k_dht, sizeof(annex_k_dht));
+            memcpy(patched + pre + sizeof(annex_k_dht), sos, post);
+        }
+
+        if (needs_sof_patch)
+            ajpg_patch_sof0(patched, patched_size);
+
+        patched[patched_size]     = 0xFF;
+        patched[patched_size + 1] = 0xD9;
+        patched_size += 2;
+        memset(patched + patched_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+    }
+
+    /* --- 2: Feed to internal MJPEG decoder --- */
+    s->pkt->data = patched;
+    s->pkt->size = patched_size;
+
+    ret = avcodec_send_packet(s->mjpeg_avctx, s->pkt);
+    if (ret < 0)
+        goto done;
+
+    ret = avcodec_receive_frame(s->mjpeg_avctx, s->tmp);
+    if (ret < 0)
+        goto done;
+
+    /* --- THE FIX: Anti-Segfault Check --- */
+    /* Ensure all 4 color planes exist before proceeding. If the frame is 
corrupted, 
+     data[3] will be NULL and attempting to read it will cause a segfault. */
+    if (!s->tmp->data[0] || !s->tmp->data[1] || !s->tmp->data[2] || 
!s->tmp->data[3]) {
+        av_log(avctx, AV_LOG_WARNING, "Corrupted frame dropped (missing color 
planes).\n");
+        ret = avpkt->size;
+        *got_frame = 0;
+        goto done;
+    }
+
+    /* --- 3a: CMYK Debug View (-bayer none) --- */
+    if (s->bayer_pattern == -2) {
+        int x, y;
+        avctx->width   = s->tmp->width;
+        avctx->height  = s->tmp->height;
+        avctx->pix_fmt = AV_PIX_FMT_RGB24;
+
+        ret = ff_get_buffer(avctx, frame, 0);
+        if (ret < 0)
+            goto done;
+
+        for (y = 0; y < s->tmp->height; y++) {
+            uint8_t *c_row = s->tmp->data[0] + y * s->tmp->linesize[0];
+            uint8_t *m_row = s->tmp->data[1] + y * s->tmp->linesize[1];
+            uint8_t *y_row = s->tmp->data[2] + y * s->tmp->linesize[2];
+            uint8_t *k_row = s->tmp->data[3] + y * s->tmp->linesize[3];
+            
+            uint8_t *rgb_row = frame->data[0] + y * frame->linesize[0];
+            
+            for (x = 0; x < s->tmp->width; x++) {
+                int c = 255 - c_row[x];
+                int m = 255 - m_row[x];
+                int y_ink = 255 - y_row[x];
+                int k = 255 - k_row[x];
+
+                int r = (255 - c) * (255 - k) / 255;
+                int g = (255 - m) * (255 - k) / 255;
+                int b = (255 - y_ink) * (255 - k) / 255;
+
+                rgb_row[x * 3 + 0] = av_clip_uint8(r);
+                rgb_row[x * 3 + 1] = av_clip_uint8(g);
+                rgb_row[x * 3 + 2] = av_clip_uint8(b);
+            }
+        }
+        
+        frame->pts = s->tmp->pts;
+        frame->pkt_dts = s->tmp->pkt_dts;
+        frame->pict_type = AV_PICTURE_TYPE_I;
+        
+        *got_frame = 1;
+        ret = avpkt->size;
+        goto done;
+    }
+
+    /* --- 3b: Bayer Weaving --- */
+    avctx->width   = s->tmp->width * 2;
+    avctx->height  = s->tmp->height * 2;
+    avctx->pix_fmt = detect_bayer_pattern(src, src_size, s->bayer_pattern);
+
+    ret = ff_get_buffer(avctx, frame, 0);
+    if (ret < 0)
+        goto done;
+
+    {
+        /* Declare loop variables at top of scope block */
+        int x, y;
+        for (y = 0; y < s->tmp->height; y++) {
+            uint8_t *dst_row0 = frame->data[0] + (y * 2) * frame->linesize[0];
+            uint8_t *dst_row1 = frame->data[0] + (y * 2 + 1) * 
frame->linesize[0];
+            
+            uint8_t *src_r  = s->tmp->data[0] + y * s->tmp->linesize[0];
+            uint8_t *src_g1 = s->tmp->data[1] + y * s->tmp->linesize[1];
+            uint8_t *src_g2 = s->tmp->data[2] + y * s->tmp->linesize[2];
+            uint8_t *src_b  = s->tmp->data[3] + y * s->tmp->linesize[3];
+            
+            for (x = 0; x < s->tmp->width; x++) {
+                dst_row0[x * 2]     = src_r[x];      
+                dst_row0[x * 2 + 1] = src_g1[x];     
+                dst_row1[x * 2]     = src_g2[x];     
+                dst_row1[x * 2 + 1] = src_b[x];      
+            }
+        }
+    }
+
+    frame->pts = s->tmp->pts;
+    frame->pkt_dts = s->tmp->pkt_dts;
+    frame->pict_type = AV_PICTURE_TYPE_I;
+    
+    *got_frame = 1;
+    ret = avpkt->size;
+    
+done:
+    if (owns_patched)
+        av_free(patched);
+    return ret;
+}
+
+/* Register */
+const FFCodec ff_ajpg_decoder = {
+    .p.name         = "ajpg",
+    CODEC_LONG_NAME("AJPG"),
+    .p.type         = AVMEDIA_TYPE_VIDEO,
+    .p.id           = AV_CODEC_ID_AJPG,
+    .priv_data_size = sizeof(AJPGContext),
+    .p.priv_class   = &ajpg_class,
+    .init           = ajpg_decode_init,
+    FF_CODEC_DECODE_CB(ajpg_decode_frame),
+    .close          = ajpg_decode_end,
+       .p.capabilities = AV_CODEC_CAP_DR1,
+};
\ No newline at end of file
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to