[FFmpeg-cvslog] avcodec/flicvideo: add 1bit support

Paul B Mahol git at videolan.org
Sun Oct 1 19:47:18 EEST 2023


ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Sun Oct  1 18:27:26 2023 +0200| [873ce68f54505118de48675d5c3d4b65a264489e] | committer: Paul B Mahol

avcodec/flicvideo: add 1bit support

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=873ce68f54505118de48675d5c3d4b65a264489e
---

 libavcodec/flicvideo.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 197 insertions(+), 1 deletion(-)

diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c
index f0d32bab38..6ce033ba40 100644
--- a/libavcodec/flicvideo.c
+++ b/libavcodec/flicvideo.c
@@ -144,6 +144,7 @@ static av_cold int flic_decode_init(AVCodecContext *avctx)
     }
 
     switch (depth) {
+        case 1  : avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; break;
         case 8  : avctx->pix_fmt = AV_PIX_FMT_PAL8; break;
         case 15 : avctx->pix_fmt = AV_PIX_FMT_RGB555; break;
         case 16 : avctx->pix_fmt = AV_PIX_FMT_RGB565; break;
@@ -162,6 +163,198 @@ static av_cold int flic_decode_init(AVCodecContext *avctx)
     return 0;
 }
 
+static int flic_decode_frame_1BPP(AVCodecContext *avctx,
+                                  AVFrame *rframe, int *got_frame,
+                                  const uint8_t *buf, int buf_size)
+{
+    FlicDecodeContext *s = avctx->priv_data;
+
+    GetByteContext g2;
+    ptrdiff_t pixel_ptr;
+
+    unsigned int frame_size;
+    int num_chunks;
+
+    unsigned int chunk_size;
+    int chunk_type;
+
+    int i, j, ret, direction;
+
+    int lines;
+    int compressed_lines;
+    int starting_line;
+    int line_packets;
+    ptrdiff_t y_ptr;
+    int byte_run;
+    int pixel_skip;
+    int pixel_countdown;
+    unsigned char *pixels;
+    ptrdiff_t pixel_limit;
+
+    bytestream2_init(&g2, buf, buf_size);
+
+    if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
+        return ret;
+
+    direction = s->frame->linesize[0] > 0;
+    pixels = s->frame->data[0];
+    pixel_limit = s->avctx->height * s->frame->linesize[0];
+    if (buf_size < 16 || buf_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
+        return AVERROR_INVALIDDATA;
+    frame_size = bytestream2_get_le32(&g2);
+    if (frame_size > buf_size)
+        frame_size = buf_size;
+    bytestream2_skip(&g2, 2); /* skip the magic number */
+    num_chunks = bytestream2_get_le16(&g2);
+    bytestream2_skip(&g2, 8);  /* skip padding */
+
+    if (frame_size < 16)
+        return AVERROR_INVALIDDATA;
+
+    frame_size -= 16;
+
+    /* iterate through the chunks */
+    while ((frame_size >= 6) && (num_chunks > 0) &&
+            bytestream2_get_bytes_left(&g2) >= 4) {
+        int stream_ptr_after_chunk;
+        chunk_size = bytestream2_get_le32(&g2);
+        if (chunk_size > frame_size) {
+            av_log(avctx, AV_LOG_WARNING,
+                   "Invalid chunk_size = %u > frame_size = %u\n", chunk_size, frame_size);
+            chunk_size = frame_size;
+        }
+        stream_ptr_after_chunk = bytestream2_tell(&g2) - 4 + chunk_size;
+
+        chunk_type = bytestream2_get_le16(&g2);
+
+        switch (chunk_type) {
+        case FLI_BRUN:
+            /* Byte run compression: This chunk type only occurs in the first
+             * FLI frame and it will update the entire frame. */
+            y_ptr = 0;
+            for (lines = 0; lines < s->avctx->height; lines++) {
+                pixel_ptr = y_ptr;
+                /* disregard the line packets; instead, iterate through all
+                 * pixels on a row */
+                bytestream2_skip(&g2, 1);
+                pixel_countdown = (s->avctx->width + 7) >> 3;
+                while (pixel_countdown > 0) {
+                    if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk)
+                        break;
+                    byte_run = sign_extend(bytestream2_get_byte(&g2), 8);
+                    if (!byte_run) {
+                        av_log(avctx, AV_LOG_ERROR, "Invalid byte run value.\n");
+                        return AVERROR_INVALIDDATA;
+                    }
+
+                    if (byte_run > 0) {
+                        int value = bytestream2_get_byte(&g2);
+                        CHECK_PIXEL_PTR(byte_run);
+                        for (j = 0; j < byte_run; j++) {
+                            pixels[pixel_ptr++] = value;
+                            pixel_countdown--;
+                            if (pixel_countdown < 0)
+                                av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n",
+                                       pixel_countdown, lines);
+                        }
+                    } else {  /* copy bytes if byte_run < 0 */
+                        byte_run = -byte_run;
+                        CHECK_PIXEL_PTR(byte_run);
+                        if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk)
+                            break;
+                        for (j = 0; j < byte_run; j++) {
+                            pixels[pixel_ptr++] = bytestream2_get_byte(&g2);
+                            pixel_countdown--;
+                            if (pixel_countdown < 0)
+                                av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n",
+                                       pixel_countdown, lines);
+                        }
+                    }
+                }
+
+                y_ptr += s->frame->linesize[0];
+            }
+            break;
+
+        case FLI_LC:
+            /* line compressed */
+            starting_line = bytestream2_get_le16(&g2);
+            if (starting_line >= s->avctx->height)
+                return AVERROR_INVALIDDATA;
+            y_ptr = 0;
+            y_ptr += starting_line * s->frame->linesize[0];
+
+            compressed_lines = bytestream2_get_le16(&g2);
+            while (compressed_lines > 0) {
+                pixel_ptr = y_ptr;
+                CHECK_PIXEL_PTR(0);
+                pixel_countdown = (s->avctx->width + 7) >> 3;
+                if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk)
+                    break;
+                line_packets = bytestream2_get_byte(&g2);
+                if (line_packets > 0) {
+                    for (i = 0; i < line_packets; i++) {
+                        /* account for the skip bytes */
+                        if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk)
+                            break;
+                        pixel_skip = bytestream2_get_byte(&g2);
+                        pixel_ptr += pixel_skip;
+                        pixel_countdown -= pixel_skip;
+                        byte_run = sign_extend(bytestream2_get_byte(&g2),8);
+                        if (byte_run > 0) {
+                            CHECK_PIXEL_PTR(byte_run);
+                            if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk)
+                                break;
+                            for (j = 0; j < byte_run; j++, pixel_countdown--) {
+                                pixels[pixel_ptr++] = bytestream2_get_byte(&g2);
+                            }
+                        } else if (byte_run < 0) {
+                            int value = bytestream2_get_byte(&g2);
+                            byte_run = -byte_run;
+                            CHECK_PIXEL_PTR(byte_run);
+                            for (j = 0; j < byte_run; j++, pixel_countdown--) {
+                                pixels[pixel_ptr++] = value;
+                            }
+                        }
+                    }
+                }
+
+                y_ptr += s->frame->linesize[0];
+                compressed_lines--;
+            }
+            break;
+
+        default:
+            av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n", chunk_type);
+            break;
+        }
+
+        if (stream_ptr_after_chunk - bytestream2_tell(&g2) >= 0) {
+            bytestream2_skip(&g2, stream_ptr_after_chunk - bytestream2_tell(&g2));
+        } else {
+            av_log(avctx, AV_LOG_ERROR, "Chunk overread\n");
+            break;
+        }
+
+        frame_size -= chunk_size;
+        num_chunks--;
+    }
+
+    /* by the end of the chunk, the stream ptr should equal the frame
+     * size (minus 1 or 2, possibly); if it doesn't, issue a warning */
+    if (bytestream2_get_bytes_left(&g2) > 2)
+        av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d " \
+               "and final chunk ptr = %d\n", buf_size,
+               buf_size - bytestream2_get_bytes_left(&g2));
+
+    if ((ret = av_frame_ref(rframe, s->frame)) < 0)
+        return ret;
+
+    *got_frame = 1;
+
+    return buf_size;
+}
+
 static int flic_decode_frame_8BPP(AVCodecContext *avctx,
                                   AVFrame *rframe, int *got_frame,
                                   const uint8_t *buf, int buf_size)
@@ -1092,7 +1285,10 @@ static int flic_decode_frame(AVCodecContext *avctx, AVFrame *frame,
 {
     const uint8_t *buf = avpkt->data;
     int buf_size = avpkt->size;
-    if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
+    if (avctx->pix_fmt == AV_PIX_FMT_MONOBLACK) {
+        return flic_decode_frame_1BPP(avctx, frame, got_frame,
+                                      buf, buf_size);
+    } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
         return flic_decode_frame_8BPP(avctx, frame, got_frame,
                                       buf, buf_size);
     } else if ((avctx->pix_fmt == AV_PIX_FMT_RGB555) ||



More information about the ffmpeg-cvslog mailing list