innoHeim/Rsupport Screen Capture Codec decoder
authorVittorio Giovara <vittorio.giovara@gmail.com>
Mon, 19 Oct 2015 12:23:43 +0000 (14:23 +0200)
committerVittorio Giovara <vittorio.giovara@gmail.com>
Fri, 23 Oct 2015 12:56:43 +0000 (14:56 +0200)
Signed-off-by: Vittorio Giovara <vittorio.giovara@gmail.com>
Changelog
configure
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/codec_desc.c
libavcodec/rscc.c [new file with mode: 0644]
libavcodec/version.h
libavformat/riff.c
tests/fate/screen.mak
tests/ref/fate/rscc [new file with mode: 0644]

index 5627826..62f24ed 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -46,6 +46,7 @@ version <next>:
 - Screenpresso SPV1 decoding
 - zero-copy Intel QSV transcoding in avconv
 - libkvazaar HEVC encoder
+- innoHeim/Rsupport Screen Capture Codec decoder
 
 
 version 11:
index f2f852e..8d44768 100755 (executable)
--- a/configure
+++ b/configure
@@ -1989,6 +1989,7 @@ qcelp_decoder_select="lsp"
 qdm2_decoder_select="mdct rdft mpegaudiodsp"
 ra_144_encoder_select="audio_frame_queue lpc"
 ralf_decoder_select="golomb"
+rscc_decoder_deps="zlib"
 rv10_decoder_select="error_resilience h263_decoder h263dsp mpeg_er"
 rv10_encoder_select="h263_encoder"
 rv20_decoder_select="error_resilience h263_decoder h263dsp mpeg_er"
index 7542c07..9dc8009 100644 (file)
@@ -378,6 +378,7 @@ OBJS-$(CONFIG_ROQ_ENCODER)             += roqvideoenc.o roqvideo.o elbg.o
 OBJS-$(CONFIG_ROQ_DPCM_DECODER)        += dpcm.o
 OBJS-$(CONFIG_ROQ_DPCM_ENCODER)        += roqaudioenc.o
 OBJS-$(CONFIG_RPZA_DECODER)            += rpza.o
+OBJS-$(CONFIG_RSCC_DECODER)            += rscc.o
 OBJS-$(CONFIG_RV10_DECODER)            += rv10.o
 OBJS-$(CONFIG_RV10_ENCODER)            += rv10enc.o
 OBJS-$(CONFIG_RV20_DECODER)            += rv10.o
index c0fca74..731b757 100644 (file)
@@ -243,6 +243,7 @@ void avcodec_register_all(void)
     REGISTER_DECODER(RL2,               rl2);
     REGISTER_ENCDEC (ROQ,               roq);
     REGISTER_DECODER(RPZA,              rpza);
+    REGISTER_DECODER(RSCC,              rscc);
     REGISTER_ENCDEC (RV10,              rv10);
     REGISTER_ENCDEC (RV20,              rv20);
     REGISTER_DECODER(RV30,              rv30);
index 0876343..cf8cc5a 100644 (file)
@@ -300,6 +300,7 @@ enum AVCodecID {
     AV_CODEC_ID_DDS,
     AV_CODEC_ID_DXV,
     AV_CODEC_ID_SCREENPRESSO,
+    AV_CODEC_ID_RSCC,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
index 91c77c8..0d3dac2 100644 (file)
@@ -1176,6 +1176,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("AVFrame to AVPacket passthrough"),
         .props     = AV_CODEC_PROP_LOSSLESS,
     },
+    {
+        .id        = AV_CODEC_ID_RSCC,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "rscc",
+        .long_name = NULL_IF_CONFIG_SMALL("innoHeim/Rsupport Screen Capture Codec"),
+        .props     = AV_CODEC_PROP_LOSSLESS,
+    },
 
     /* image codecs */
     {
diff --git a/libavcodec/rscc.c b/libavcodec/rscc.c
new file mode 100644 (file)
index 0000000..c54482f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * innoHeim/Rsupport Screen Capture Codec
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * innoHeim/Rsupport Screen Capture Codec decoder
+ *
+ * Fourcc: ISCC, RSCC
+ *
+ * Lossless codec, data stored in tiles, with optional deflate compression.
+ *
+ * Header contains the number of tiles in a frame with the tile coordinates,
+ * and it can be deflated or not. Similarly, pixel data comes after the header
+ * and a variable size value, and it can be deflated or just raw.
+ *
+ * Supports: BGRA
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "libavutil/imgutils.h"
+#include "libavutil/internal.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+
+#define TILE_SIZE 8
+
+typedef struct Tile {
+    int x, y;
+    int w, h;
+} Tile;
+
+typedef struct RsccContext {
+    GetByteContext gbc;
+    AVFrame *reference;
+    Tile *tiles;
+    unsigned int tiles_size;
+
+    /* zlib interaction */
+    uint8_t *inflated_buf;
+    uLongf inflated_size;
+} RsccContext;
+
+static av_cold int rscc_init(AVCodecContext *avctx)
+{
+    RsccContext *ctx = avctx->priv_data;
+
+    /* These needs to be set to estimate uncompressed buffer */
+    int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+    if (ret < 0) {
+        av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
+               avctx->width, avctx->height);
+        return ret;
+    }
+
+    /* Allocate reference frame */
+    ctx->reference = av_frame_alloc();
+    if (!ctx->reference)
+        return AVERROR(ENOMEM);
+
+    avctx->pix_fmt = AV_PIX_FMT_BGRA;
+
+    /* Store the value to check for keyframes */
+    ctx->inflated_size = avctx->width * avctx->height * 4;
+
+    /* Allocate maximum size possible, a full frame */
+    ctx->inflated_buf = av_malloc(ctx->inflated_size);
+    if (!ctx->inflated_buf)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static av_cold int rscc_close(AVCodecContext *avctx)
+{
+    RsccContext *ctx = avctx->priv_data;
+
+    av_freep(&ctx->tiles);
+    av_freep(&ctx->inflated_buf);
+    av_frame_free(&ctx->reference);
+
+    return 0;
+}
+
+static int rscc_decode_frame(AVCodecContext *avctx, void *data,
+                                     int *got_frame, AVPacket *avpkt)
+{
+    RsccContext *ctx = avctx->priv_data;
+    GetByteContext *gbc = &ctx->gbc;
+    GetByteContext tiles_gbc;
+    AVFrame *frame = data;
+    const uint8_t *pixels, *raw;
+    uint8_t *inflated_tiles = NULL;
+    int tiles_nb, packed_size, pixel_size = 0;
+    int i, ret = 0;
+
+    bytestream2_init(gbc, avpkt->data, avpkt->size);
+
+    /* Size check */
+    if (bytestream2_get_bytes_left(gbc) < 12) {
+        av_log(avctx, AV_LOG_ERROR, "Packet too small (%d)\n", avpkt->size);
+        return AVERROR_INVALIDDATA;
+    }
+
+    /* Read number of tiles, and allocate the array */
+    tiles_nb = bytestream2_get_le16(gbc);
+    av_fast_malloc(&ctx->tiles, &ctx->tiles_size,
+                   tiles_nb * sizeof(*ctx->tiles));
+    if (!ctx->tiles) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
+
+    av_log(avctx, AV_LOG_DEBUG, "Frame with %d tiles.\n", tiles_nb);
+
+    /* When there are more than 5 tiles, they are packed together with
+     * a size header. When that size does not match the number of tiles
+     * times the tile size, it means it needs to be inflated as well */
+    if (tiles_nb > 5) {
+        uLongf packed_tiles_size;
+
+        if (tiles_nb < 32)
+            packed_tiles_size = bytestream2_get_byte(gbc);
+        else
+            packed_tiles_size = bytestream2_get_le16(gbc);
+
+        ff_dlog(avctx, "packed tiles of size %lu.\n", packed_tiles_size);
+
+        /* If necessary, uncompress tiles, and hijack the bytestream reader */
+        if (packed_tiles_size != tiles_nb * TILE_SIZE) {
+            uLongf length = tiles_nb * TILE_SIZE;
+            inflated_tiles = av_malloc(length);
+            if (!inflated_tiles) {
+                ret = AVERROR(ENOMEM);
+                goto end;
+            }
+
+            ret = uncompress(inflated_tiles, &length,
+                             gbc->buffer, packed_tiles_size);
+            if (ret) {
+                av_log(avctx, AV_LOG_ERROR, "Tile deflate error %d.\n", ret);
+                ret = AVERROR_UNKNOWN;
+                goto end;
+            }
+
+            /* Skip the compressed tile section in the main byte reader,
+             * and point it to read the newly uncompressed data */
+            bytestream2_skip(gbc, packed_tiles_size);
+            bytestream2_init(&tiles_gbc, inflated_tiles, length);
+            gbc = &tiles_gbc;
+        }
+    }
+
+    /* Fill in array of tiles, keeping track of how many pixels are updated */
+    for (i = 0; i < tiles_nb; i++) {
+        ctx->tiles[i].x = bytestream2_get_le16(gbc);
+        ctx->tiles[i].w = bytestream2_get_le16(gbc);
+        ctx->tiles[i].y = bytestream2_get_le16(gbc);
+        ctx->tiles[i].h = bytestream2_get_le16(gbc);
+
+        pixel_size += ctx->tiles[i].w * ctx->tiles[i].h * 4;
+
+        ff_dlog(avctx, "tile %d orig(%d,%d) %dx%d.\n", i,
+                ctx->tiles[i].x, ctx->tiles[i].y,
+                ctx->tiles[i].w, ctx->tiles[i].h);
+
+        if (ctx->tiles[i].w == 0 || ctx->tiles[i].h == 0) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "invalid tile %d at (%d.%d) with size %dx%d.\n", i,
+                   ctx->tiles[i].x, ctx->tiles[i].y,
+                   ctx->tiles[i].w, ctx->tiles[i].h);
+            ret = AVERROR_INVALIDDATA;
+            goto end;
+        } else if (ctx->tiles[i].x + ctx->tiles[i].w > avctx->width ||
+                   ctx->tiles[i].y + ctx->tiles[i].h > avctx->height) {
+            av_log(avctx, AV_LOG_ERROR,
+                   "out of bounds tile %d at (%d.%d) with size %dx%d.\n", i,
+                   ctx->tiles[i].x, ctx->tiles[i].y,
+                   ctx->tiles[i].w, ctx->tiles[i].h);
+            ret = AVERROR_INVALIDDATA;
+            goto end;
+        }
+    }
+
+    /* Reset the reader in case it had been modified before */
+    gbc = &ctx->gbc;
+
+    /* Extract how much pixel data the tiles contain */
+    if (pixel_size < 0x100)
+        packed_size = bytestream2_get_byte(gbc);
+    else if (pixel_size < 0x10000)
+        packed_size = bytestream2_get_le16(gbc);
+    else if (pixel_size < 0x1000000)
+        packed_size = bytestream2_get_le24(gbc);
+    else
+        packed_size = bytestream2_get_le32(gbc);
+
+    ff_dlog(avctx, "pixel_size %d packed_size %d.\n", pixel_size, packed_size);
+
+    /* Get pixels buffer, it may be deflated or just raw */
+    if (pixel_size == packed_size) {
+        pixels = gbc->buffer;
+    } else {
+        uLongf len = ctx->inflated_size;
+        ret = uncompress(ctx->inflated_buf, &len, gbc->buffer, packed_size);
+        if (ret) {
+            av_log(avctx, AV_LOG_ERROR, "Pixel deflate error %d.\n", ret);
+            ret = AVERROR_UNKNOWN;
+            goto end;
+        }
+        pixels = ctx->inflated_buf;
+    }
+
+    /* Allocate when needed */
+    ret = ff_reget_buffer(avctx, ctx->reference);
+    if (ret < 0)
+        goto end;
+
+    /* Pointer to actual pixels, will be updated when data is consumed */
+    raw = pixels;
+    for (i = 0; i < tiles_nb; i++) {
+        uint8_t *dst = ctx->reference->data[0] + ctx->reference->linesize[0] *
+                       (avctx->height - ctx->tiles[i].y - 1) +
+                       ctx->tiles[i].x * 4;
+        av_image_copy_plane(dst, -1 * ctx->reference->linesize[0],
+                            raw, ctx->tiles[i].w * 4,
+                            ctx->tiles[i].w * 4, ctx->tiles[i].h);
+        raw += ctx->tiles[i].w * 4 * ctx->tiles[i].h;
+    }
+
+    /* Frame is ready to be output */
+    ret = av_frame_ref(frame, ctx->reference);
+    if (ret < 0)
+        goto end;
+
+    /* Keyframe when the number of pixels updated matches the whole surface */
+    if (pixel_size == ctx->inflated_size) {
+        frame->pict_type = AV_PICTURE_TYPE_I;
+        frame->key_frame = 1;
+    } else {
+        frame->pict_type = AV_PICTURE_TYPE_P;
+    }
+    *got_frame = 1;
+
+end:
+    av_free(inflated_tiles);
+    return ret;
+}
+
+AVCodec ff_rscc_decoder = {
+    .name           = "rscc",
+    .long_name      = NULL_IF_CONFIG_SMALL("innoHeim/Rsupport Screen Capture Codec"),
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = AV_CODEC_ID_RSCC,
+    .init           = rscc_init,
+    .decode         = rscc_decode_frame,
+    .close          = rscc_close,
+    .priv_data_size = sizeof(RsccContext),
+    .capabilities   = AV_CODEC_CAP_DR1,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
+                      FF_CODEC_CAP_INIT_CLEANUP,
+};
index ddf549a..22b0f3d 100644 (file)
@@ -29,7 +29,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR 57
-#define LIBAVCODEC_VERSION_MINOR  6
+#define LIBAVCODEC_VERSION_MINOR  7
 #define LIBAVCODEC_VERSION_MICRO  0
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
index f0e99df..eb90d98 100644 (file)
@@ -361,6 +361,8 @@ const AVCodecTag ff_codec_bmp_tags[] = {
     { AV_CODEC_ID_HQ_HQA,       MKTAG('C', 'U', 'V', 'C') },
     { AV_CODEC_ID_RV40,         MKTAG('R', 'V', '4', '0') },
     { AV_CODEC_ID_SCREENPRESSO, MKTAG('S', 'P', 'V', '1') },
+    { AV_CODEC_ID_RSCC,         MKTAG('R', 'S', 'C', 'C') },
+    { AV_CODEC_ID_RSCC,         MKTAG('I', 'S', 'C', 'C') },
     { AV_CODEC_ID_NONE,         0 }
 };
 
index 0536909..82d4861 100644 (file)
@@ -41,6 +41,9 @@ fate-g2m4: CMD = framecrc -idct simple -i $(TARGET_SAMPLES)/g2m/g2m4.asf
 FATE_SAMPLES_AVCONV-$(call DEMDEC, ASF, G2M) += $(FATE_G2M)
 fate-g2m: $(FATE_G2M)
 
+FATE_SAMPLES_AVCONV-$(call DEMDEC, AVI, RSCC) += fate-rscc
+fate-rscc: CMD = framecrc -i $(TARGET_SAMPLES)/rscc/pip.avi -an
+
 FATE_SAMPLES_AVCONV-$(call DEMDEC, AVI, SCREENPRESSO) += fate-screenpresso
 fate-screenpresso: CMD = framecrc -i $(TARGET_SAMPLES)/spv1/bunny.avi
 
diff --git a/tests/ref/fate/rscc b/tests/ref/fate/rscc
new file mode 100644 (file)
index 0000000..69dbfe1
--- /dev/null
@@ -0,0 +1,6 @@
+#tb 0: 1/10
+0,          0,          0,        1,  6814720, 0x1365f8ef
+0,          1,          1,        1,  6814720, 0x90838983
+0,          2,          2,        1,  6814720, 0xf0cc3131
+0,          3,          3,        1,  6814720, 0xc07e404d
+0,          4,          4,        1,  6814720, 0x945962dd