[FFmpeg-cvslog] Add FM Screen Capture Codec decoder

Paul B Mahol git at videolan.org
Sat Oct 28 03:27:13 EEST 2017


ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Mon Feb  6 09:17:29 2017 +0100| [95a8a03a191204c3ca5c8cf6b69ab166765d5ff3] | committer: Diego Biurrun

Add FM Screen Capture Codec decoder

Signed-off-by: Paul B Mahol <onemda at gmail.com>
Signed-off-by: Diego Biurrun <diego at biurrun.de>

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

 Changelog               |   1 +
 doc/general.texi        |   1 +
 libavcodec/Makefile     |   1 +
 libavcodec/allcodecs.c  |   1 +
 libavcodec/avcodec.h    |   1 +
 libavcodec/codec_desc.c |   7 +
 libavcodec/fmvc.c       | 636 ++++++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h    |   2 +-
 libavformat/riff.c      |   1 +
 9 files changed, 650 insertions(+), 1 deletion(-)

diff --git a/Changelog b/Changelog
index b3aed3cd7e..dccf173741 100644
--- a/Changelog
+++ b/Changelog
@@ -13,6 +13,7 @@ version <next>:
   --x86asmexe=yasm to configure to restore the old behavior.
 - Cineform HD decoder
 - VP9 superframe split/merge bitstream filters
+- FM Screen Capture Codec decoder
 
 
 version 12:
diff --git a/doc/general.texi b/doc/general.texi
index b21195bde2..b1a42c50f8 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -667,6 +667,7 @@ following image formats are supported:
 @item Flash Screen Video v2  @tab     @tab  X
 @item Flash Video (FLV)      @tab  X  @tab  X
     @tab Sorenson H.263 used in Flash
+ at item FM Screen Capture Codec  @tab     @tab  X
 @item Forward Uncompressed   @tab     @tab  X
 @item Fraps                  @tab     @tab  X
 @item Go2Meeting             @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a82210d89f..e835133842 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -235,6 +235,7 @@ OBJS-$(CONFIG_FLASHSV_DECODER)         += flashsv.o
 OBJS-$(CONFIG_FLASHSV_ENCODER)         += flashsvenc.o
 OBJS-$(CONFIG_FLASHSV2_DECODER)        += flashsv.o
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
+OBJS-$(CONFIG_FMVC_DECODER)            += fmvc.o
 OBJS-$(CONFIG_FOURXM_DECODER)          += 4xm.o
 OBJS-$(CONFIG_FRAPS_DECODER)           += fraps.o
 OBJS-$(CONFIG_FRWU_DECODER)            += frwu.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 36f839c852..20b3b35b90 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -161,6 +161,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(FLASHSV2,          flashsv2);
     REGISTER_DECODER(FLIC,              flic);
     REGISTER_ENCDEC (FLV,               flv);
+    REGISTER_DECODER(FMVC,              fmvc);
     REGISTER_DECODER(FOURXM,            fourxm);
     REGISTER_DECODER(FRAPS,             fraps);
     REGISTER_DECODER(FRWU,              frwu);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 1c58fe2d68..4cf446faf9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -408,6 +408,7 @@ enum AVCodecID {
     AV_CODEC_ID_AV1,
     AV_CODEC_ID_PIXLET,
     AV_CODEC_ID_CFHD,
+    AV_CODEC_ID_FMVC,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 7103b5890a..ed97420d42 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1210,6 +1210,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Apple Pixlet"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_FMVC,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "fmvc",
+        .long_name = NULL_IF_CONFIG_SMALL("FM Screen Capture Codec"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* image codecs */
     {
diff --git a/libavcodec/fmvc.c b/libavcodec/fmvc.c
new file mode 100644
index 0000000000..64136e392d
--- /dev/null
+++ b/libavcodec/fmvc.c
@@ -0,0 +1,636 @@
+/*
+ * FM Screen Capture Codec decoder
+ *
+ * Copyright (c) 2017 Paul B Mahol
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+#define BLOCK_HEIGHT 112u
+#define BLOCK_WIDTH  84u
+
+typedef struct InterBlock {
+    int      w, h;
+    int      size;
+    int      xor;
+} InterBlock;
+
+typedef struct FMVCContext {
+    GetByteContext  gb;
+    PutByteContext  pb;
+    uint8_t        *buffer;
+    size_t          buffer_size;
+    uint8_t        *pbuffer;
+    size_t          pbuffer_size;
+    ptrdiff_t       stride;
+    int             bpp;
+    int             yb, xb;
+    InterBlock     *blocks;
+    unsigned        nb_blocks;
+} FMVCContext;
+
+static int decode_type2(GetByteContext *gb, PutByteContext *pb)
+{
+    unsigned repeat = 0, first = 1, opcode;
+    int i, len, pos;
+
+    while (bytestream2_get_bytes_left(gb) > 0) {
+        GetByteContext gbc;
+
+        while (bytestream2_get_bytes_left(gb) > 0) {
+            if (first) {
+                first = 0;
+                if (bytestream2_peek_byte(gb) > 17) {
+                    len = bytestream2_get_byte(gb) - 17;
+                    if (len < 4) {
+                        do {
+                            bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                            --len;
+                        } while (len);
+                        opcode = bytestream2_peek_byte(gb);
+                        continue;
+                    } else {
+                        do {
+                            bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                            --len;
+                        } while (len);
+                        opcode = bytestream2_peek_byte(gb);
+                        if (opcode < 0x10) {
+                            bytestream2_skip(gb, 1);
+                            pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049;
+
+                            bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+                            bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+
+                            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                            len = opcode & 3;
+                            if (!len) {
+                                repeat = 1;
+                            } else {
+                                do {
+                                    bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                                    --len;
+                                } while (len);
+                                opcode = bytestream2_peek_byte(gb);
+                            }
+                            continue;
+                        }
+                    }
+                    repeat = 0;
+                }
+                repeat = 1;
+            }
+            if (repeat) {
+                repeat = 0;
+                opcode = bytestream2_peek_byte(gb);
+                if (opcode < 0x10) {
+                    bytestream2_skip(gb, 1);
+                    if (!opcode) {
+                        if (!bytestream2_peek_byte(gb)) {
+                            do {
+                                bytestream2_skip(gb, 1);
+                                opcode += 255;
+                            } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
+                        }
+                        opcode += bytestream2_get_byte(gb) + 15;
+                    }
+                    bytestream2_put_le32(pb, bytestream2_get_le32(gb));
+                    for (i = opcode - 1; i > 0; --i)
+                        bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                    opcode = bytestream2_peek_byte(gb);
+                    if (opcode < 0x10) {
+                        bytestream2_skip(gb, 1);
+                        pos = - (opcode >> 2) - 4 * bytestream2_get_byte(gb) - 2049;
+
+                        bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+                        bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+
+                        bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                        bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                        bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                        len = opcode & 3;
+                        if (!len) {
+                            repeat = 1;
+                        } else {
+                            do {
+                                bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                                --len;
+                            } while (len);
+                            opcode = bytestream2_peek_byte(gb);
+                        }
+                        continue;
+                    }
+                }
+            }
+
+            if (opcode >= 0x40) {
+                bytestream2_skip(gb, 1);
+                pos = - ((opcode >> 2) & 7) - 1 - 8 * bytestream2_get_byte(gb);
+                len =    (opcode >> 5)      - 1;
+
+                bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+                bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                do {
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                    --len;
+                } while (len);
+
+                len = opcode & 3;
+
+                if (!len) {
+                    repeat = 1;
+                } else {
+                    do {
+                        bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                        --len;
+                    } while (len);
+                    opcode = bytestream2_peek_byte(gb);
+                }
+                continue;
+            } else if (opcode < 0x20) {
+                break;
+            }
+            len = opcode & 0x1F;
+            bytestream2_skip(gb, 1);
+            if (!len) {
+                if (!bytestream2_peek_byte(gb)) {
+                    do {
+                        bytestream2_skip(gb, 1);
+                        len += 255;
+                    } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
+                }
+                len += bytestream2_get_byte(gb) + 31;
+            }
+            i = bytestream2_get_le16(gb);
+            pos = - (i >> 2) - 1;
+
+            bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+            bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+
+            if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                do {
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                    --len;
+                } while (len);
+            } else {
+                bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
+                for (len = len - 2; len; --len)
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            }
+            len = i & 3;
+            if (!len) {
+                repeat = 1;
+            } else {
+                do {
+                    bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                    --len;
+                } while (len);
+                opcode = bytestream2_peek_byte(gb);
+            }
+        }
+        bytestream2_skip(gb, 1);
+        if (opcode < 0x10) {
+            pos = -(opcode >> 2) - 1 - 4 * bytestream2_get_byte(gb);
+
+            bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+            bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            len = opcode & 3;
+            if (!len) {
+                repeat = 1;
+            } else {
+                do {
+                    bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                    --len;
+                } while (len);
+                opcode = bytestream2_peek_byte(gb);
+            }
+            continue;
+        }
+        len = opcode & 7;
+        if (!len) {
+            if (!bytestream2_peek_byte(gb)) {
+                do {
+                    bytestream2_skip(gb, 1);
+                    len += 255;
+                } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
+            }
+            len += bytestream2_get_byte(gb) + 7;
+        }
+        i = bytestream2_get_le16(gb);
+        pos = bytestream2_tell_p(pb) - 2048 * (opcode & 8);
+        pos = pos - (i >> 2);
+        if (pos == bytestream2_tell_p(pb))
+            break;
+
+        pos = pos - 0x4000;
+        bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+        bytestream2_seek(&gbc, pos, SEEK_SET);
+
+        if (len < 6 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            do {
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                --len;
+            } while (len);
+        } else {
+            bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
+            for (len = len - 2; len; --len)
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+        }
+
+        len = i & 3;
+        if (!len) {
+            repeat = 1;
+        } else {
+            do {
+                bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                --len;
+            } while (len);
+            opcode = bytestream2_peek_byte(gb);
+        }
+    }
+
+    return 0;
+}
+
+static int decode_type1(GetByteContext *gb, PutByteContext *pb)
+{
+    unsigned opcode, len;
+    int high = 0;
+    int i, pos;
+
+    while (bytestream2_get_bytes_left(gb) > 0) {
+        GetByteContext gbc;
+
+        while (bytestream2_get_bytes_left(gb) > 0) {
+            while (bytestream2_get_bytes_left(gb) > 0) {
+                opcode = bytestream2_get_byte(gb);
+                high = opcode >= 0x20;
+                if (high)
+                    break;
+                if (opcode)
+                    break;
+                opcode = bytestream2_get_byte(gb);
+                if (opcode < 0xF8) {
+                    opcode += 32;
+                    break;
+                }
+                i = opcode - 0xF8;
+                if (i) {
+                    len = 256;
+                    do {
+                        len *= 2;
+                        --i;
+                    } while (i);
+                } else {
+                    len = 280;
+                }
+                do {
+                    bytestream2_put_le32(pb, bytestream2_get_le32(gb));
+                    bytestream2_put_le32(pb, bytestream2_get_le32(gb));
+                    len -= 8;
+                } while (len && bytestream2_get_bytes_left(gb) > 0);
+            }
+
+            if (!high) {
+                do {
+                    bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                    --opcode;
+                } while (opcode && bytestream2_get_bytes_left(gb) > 0);
+
+                while (bytestream2_get_bytes_left(gb) > 0) {
+                    GetByteContext gbc;
+
+                    opcode = bytestream2_get_byte(gb);
+                    if (opcode >= 0x20)
+                        break;
+                    bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+
+                    pos = -(opcode | 32 * bytestream2_get_byte(gb)) - 1;
+                    bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                    bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                    bytestream2_put_byte(pb, bytestream2_get_byte(gb));
+                }
+            }
+            high = 0;
+            if (opcode < 0x40)
+                break;
+            bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+            pos = (-((opcode & 0x1F) | 32 * bytestream2_get_byte(gb)) - 1);
+            bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos, SEEK_SET);
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            len = (opcode >> 5) - 1;
+            do {
+                bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+                --len;
+            } while (len && bytestream2_get_bytes_left(&gbc) > 0);
+        }
+        len = opcode & 0x1F;
+        if (!len) {
+            if (!bytestream2_peek_byte(gb)) {
+                do {
+                    bytestream2_skip(gb, 1);
+                    len += 255;
+                } while (!bytestream2_peek_byte(gb) && bytestream2_get_bytes_left(gb) > 0);
+            }
+            len += bytestream2_get_byte(gb) + 31;
+        }
+        pos = -bytestream2_get_byte(gb);
+        bytestream2_init(&gbc, pb->buffer_start, pb->buffer_end - pb->buffer_start);
+        bytestream2_seek(&gbc, bytestream2_tell_p(pb) + pos - (bytestream2_get_byte(gb) << 8), SEEK_SET);
+        if (bytestream2_tell_p(pb) == bytestream2_tell(&gbc))
+            break;
+        if (len < 5 || bytestream2_tell_p(pb) - bytestream2_tell(&gbc) < 4) {
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+        } else {
+            bytestream2_put_le32(pb, bytestream2_get_le32(&gbc));
+            len--;
+        }
+        do {
+            bytestream2_put_byte(pb, bytestream2_get_byte(&gbc));
+            len--;
+        } while (len && bytestream2_get_bytes_left(&gbc) > 0);
+    }
+
+    return 0;
+}
+
+static int decode_frame(AVCodecContext *avctx, void *data,
+                        int *got_frame, AVPacket *avpkt)
+{
+    FMVCContext *s = avctx->priv_data;
+    GetByteContext *gb = &s->gb;
+    PutByteContext *pb = &s->pb;
+    AVFrame *frame = data;
+    int ret, y, x;
+
+    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
+        return ret;
+
+    bytestream2_init(gb, avpkt->data, avpkt->size);
+    bytestream2_skip(gb, 2);
+
+    frame->key_frame = !!bytestream2_get_le16(gb);
+    frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+
+    if (frame->key_frame) {
+        const uint8_t *src;
+        unsigned type, size;
+        uint8_t *dst;
+
+        type = bytestream2_get_le16(gb);
+        size = bytestream2_get_le16(gb);
+        if (size > bytestream2_get_bytes_left(gb))
+            return AVERROR_INVALIDDATA;
+
+        bytestream2_init_writer(pb, s->buffer, s->buffer_size);
+        if (type == 1) {
+            decode_type1(gb, pb);
+        } else if (type == 2){
+            decode_type2(gb, pb);
+        } else {
+            avpriv_report_missing_feature(avctx, "Compression type %d", type);
+            return AVERROR_PATCHWELCOME;
+        }
+
+        src = s->buffer;
+        dst = frame->data[0] + (avctx->height - 1) * frame->linesize[0];
+        for (y = 0; y < avctx->height; y++) {
+            memcpy(dst, src, avctx->width * s->bpp);
+            dst -= frame->linesize[0];
+            src += avctx->width * s->bpp;
+        }
+    } else {
+        unsigned block, nb_blocks;
+        int type, k, l;
+        uint8_t *ssrc, *ddst;
+        const uint32_t *src;
+        uint32_t *dst;
+
+        for (block = 0; block < s->nb_blocks; block++)
+            s->blocks[block].xor = 0;
+
+        nb_blocks = bytestream2_get_le16(gb);
+        if (nb_blocks > s->nb_blocks)
+            return AVERROR_INVALIDDATA;
+
+        bytestream2_init_writer(pb, s->pbuffer, s->pbuffer_size);
+
+        type = bytestream2_get_le16(gb);
+        for (block = 0; block < nb_blocks; block++) {
+            unsigned size, offset;
+            int start = 0;
+
+            offset = bytestream2_get_le16(gb);
+            if (offset > s->nb_blocks)
+                return AVERROR_INVALIDDATA;
+
+            size = bytestream2_get_le16(gb);
+            if (size > bytestream2_get_bytes_left(gb))
+                return AVERROR_INVALIDDATA;
+
+            start = bytestream2_tell_p(pb);
+            if (type == 1) {
+                decode_type1(gb, pb);
+            } else if (type == 2){
+                decode_type2(gb, pb);
+            } else {
+                avpriv_report_missing_feature(avctx, "Compression type %d", type);
+                return AVERROR_PATCHWELCOME;
+            }
+
+            if (s->blocks[offset].size * 4 != bytestream2_tell_p(pb) - start)
+                return AVERROR_INVALIDDATA;
+
+            s->blocks[offset].xor = 1;
+        }
+
+        src = (const uint32_t *)s->pbuffer;
+        dst = (uint32_t *)s->buffer;
+
+        for (block = 0, y = 0; y < s->yb; y++) {
+            int block_h = s->blocks[block].h;
+            uint32_t *rect = dst;
+
+            for (x = 0; x < s->xb; x++) {
+                int block_w = s->blocks[block].w;
+                uint32_t *row = dst;
+
+                block_h = s->blocks[block].h;
+                if (s->blocks[block].xor) {
+                    for (k = 0; k < block_h; k++) {
+                        uint32_t *column = dst;
+                        for (l = 0; l < block_w; l++)
+                            *dst++ ^= *src++;
+                        dst = &column[s->stride];
+                    }
+                }
+                dst = &row[block_w];
+                ++block;
+            }
+            dst = &rect[block_h * s->stride];
+        }
+
+        ssrc = s->buffer;
+        ddst = frame->data[0] + (avctx->height - 1) * frame->linesize[0];
+        for (y = 0; y < avctx->height; y++) {
+            memcpy(ddst, ssrc, avctx->width * s->bpp);
+            ddst -= frame->linesize[0];
+            ssrc += avctx->width * s->bpp;
+        }
+    }
+
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+static av_cold int decode_init(AVCodecContext *avctx)
+{
+    FMVCContext *s = avctx->priv_data;
+    int i, j, m, block = 0, h = BLOCK_HEIGHT, w = BLOCK_WIDTH;
+
+    switch (avctx->bits_per_coded_sample) {
+    case 16:
+        avctx->pix_fmt = AV_PIX_FMT_RGB555;
+        break;
+    case 24:
+        avctx->pix_fmt = AV_PIX_FMT_BGR24;
+        break;
+    case 32:
+        avctx->pix_fmt = AV_PIX_FMT_BGRA;
+        break;
+    default:
+        av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n",
+               avctx->bits_per_coded_sample);
+        return AVERROR_INVALIDDATA;
+    }
+
+    s->stride = (avctx->width * avctx->bits_per_coded_sample + 31) / 32;
+    s->xb     = s->stride / BLOCK_WIDTH;
+    m         = s->stride % BLOCK_WIDTH;
+    if (m) {
+        if (m < 37) {
+            w = m + BLOCK_WIDTH;
+        } else {
+            w = m;
+            s->xb++;
+        }
+    }
+
+    s->yb = avctx->height / BLOCK_HEIGHT;
+    m     = avctx->height % BLOCK_HEIGHT;
+    if (m) {
+        if (m < 49) {
+            h = m + BLOCK_HEIGHT;
+        } else {
+            h = m;
+            s->yb++;
+        }
+    }
+
+    s->nb_blocks = s->xb * s->yb;
+    if (!s->nb_blocks)
+        return AVERROR_INVALIDDATA;
+    s->blocks    = av_mallocz(s->nb_blocks * sizeof(*s->blocks));
+    if (!s->blocks)
+        return AVERROR(ENOMEM);
+
+    for (i = 0; i < s->yb; i++) {
+        for (j = 0; j < s->xb; j++) {
+            if (i != (s->yb - 1) || j != (s->xb - 1)) {
+                if (i == s->yb - 1) {
+                    s->blocks[block].w    = BLOCK_WIDTH;
+                    s->blocks[block].h    = h;
+                    s->blocks[block].size = BLOCK_WIDTH * h;
+                } else if (j == s->xb - 1) {
+                    s->blocks[block].w    = w;
+                    s->blocks[block].h    = BLOCK_HEIGHT;
+                    s->blocks[block].size = BLOCK_HEIGHT * w;
+                } else {
+                    s->blocks[block].w    = BLOCK_WIDTH;
+                    s->blocks[block].h    = BLOCK_HEIGHT;
+                    s->blocks[block].size = BLOCK_WIDTH * BLOCK_HEIGHT;
+                }
+            } else {
+                s->blocks[block].w    = w;
+                s->blocks[block].h    = h;
+                s->blocks[block].size = w * h;
+            }
+            block++;
+        }
+    }
+
+    s->bpp          = avctx->bits_per_coded_sample >> 3;
+    s->buffer_size  = avctx->width * avctx->height * 4;
+    s->pbuffer_size = avctx->width * avctx->height * 4;
+    s->buffer       = av_malloc(s->buffer_size);
+    s->pbuffer      = av_malloc(s->pbuffer_size);
+    if (!s->buffer || !s->pbuffer)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static av_cold int decode_close(AVCodecContext *avctx)
+{
+    FMVCContext *s = avctx->priv_data;
+
+    av_freep(&s->buffer);
+    av_freep(&s->pbuffer);
+    av_freep(&s->blocks);
+
+    return 0;
+}
+
+AVCodec ff_fmvc_decoder = {
+    .name             = "fmvc",
+    .long_name        = NULL_IF_CONFIG_SMALL("FM Screen Capture Codec"),
+    .type             = AVMEDIA_TYPE_VIDEO,
+    .id               = AV_CODEC_ID_FMVC,
+    .priv_data_size   = sizeof(FMVCContext),
+    .init             = decode_init,
+    .close            = decode_close,
+    .decode           = decode_frame,
+    .capabilities     = AV_CODEC_CAP_DR1,
+    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
+                        FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 8943ef3d0f..63c9a1f634 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 58
-#define LIBAVCODEC_VERSION_MINOR  1
+#define LIBAVCODEC_VERSION_MINOR  2
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 6de4007202..49b8c762d6 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -369,6 +369,7 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_MAGICYUV,     MKTAG('M', 'A', 'G', 'Y') },
     { AV_CODEC_ID_AV1,          MKTAG('A', 'V', '0', '1') },
     { AV_CODEC_ID_CFHD,         MKTAG('C', 'F', 'H', 'D') },
+    { AV_CODEC_ID_FMVC,         MKTAG('F', 'M', 'V', 'C') },
     { AV_CODEC_ID_NONE,         0 }
 };
 



More information about the ffmpeg-cvslog mailing list