add Flash Screen Video 2 decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 16 Jul 2011 10:02:55 +0000 (12:02 +0200)
committerRonald S. Bultje <rsbultje@gmail.com>
Fri, 22 Jul 2011 16:12:30 +0000 (09:12 -0700)
Signed-off-by: Ronald S. Bultje <rsbultje@gmail.com>
Changelog
configure
doc/general.texi
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/flashsv.c
libavcodec/version.h

index 782d8b3..7b8be5b 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -4,6 +4,7 @@ releases are sorted from youngest to oldest.
 
 version <next>:
 - BWF muxer
+- Flash Screen Video 2 decoder
 
 
 version 0.7:
index 3953cfc..a83d339 100755 (executable)
--- a/configure
+++ b/configure
@@ -1270,6 +1270,7 @@ flac_decoder_select="golomb"
 flac_encoder_select="golomb lpc"
 flashsv_decoder_select="zlib"
 flashsv_encoder_select="zlib"
+flashsv2_decoder_select="zlib"
 flv_decoder_select="h263_decoder"
 flv_encoder_select="h263_encoder"
 fraps_decoder_select="huffman"
index fe31448..c991939 100644 (file)
@@ -401,6 +401,7 @@ following image formats are supported:
     @tab experimental lossless codec (fourcc: FFV1)
 @item Flash Screen Video v1  @tab  X  @tab  X
     @tab fourcc: FSV1
+@item Flash Screen Video v2  @tab     @tab  X
 @item Flash Video (FLV)      @tab  X  @tab  X
     @tab Sorenson H.263 used in Flash
 @item Fraps                  @tab     @tab  X
index f23ade2..99ecbbf 100644 (file)
@@ -146,6 +146,7 @@ OBJS-$(CONFIG_FLAC_DECODER)            += flacdec.o flacdata.o flac.o
 OBJS-$(CONFIG_FLAC_ENCODER)            += flacenc.o flacdata.o flac.o
 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_FOURXM_DECODER)          += 4xm.o
 OBJS-$(CONFIG_FRAPS_DECODER)           += fraps.o
index 71b6094..dcef0d6 100644 (file)
@@ -106,6 +106,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC  (FFV1, ffv1);
     REGISTER_ENCDEC  (FFVHUFF, ffvhuff);
     REGISTER_ENCDEC  (FLASHSV, flashsv);
+    REGISTER_DECODER (FLASHSV2, flashsv2);
     REGISTER_DECODER (FLIC, flic);
     REGISTER_ENCDEC  (FLV, flv);
     REGISTER_DECODER (FOURXM, fourxm);
index 51831eb..57d33c0 100644 (file)
@@ -25,6 +25,8 @@
  * Flash Screen Video decoder
  * @author Alex Beregszaszi
  * @author Benjamin Larsson
+ * @author Daniel Verkamp
+ * @author Konstantin Shishkov
  *
  * A description of the bitstream format for Flash Screen Video version 1/2
  * is part of the SWF File Format Specification (version 10), which can be
 #include <stdlib.h>
 #include <zlib.h>
 
+#include "libavutil/intreadwrite.h"
 #include "avcodec.h"
+#include "bytestream.h"
 #include "get_bits.h"
 
+typedef struct BlockInfo {
+    uint8_t *pos;
+    int      size;
+    int      unp_size;
+} BlockInfo;
+
 typedef struct FlashSVContext {
     AVCodecContext *avctx;
     AVFrame         frame;
@@ -46,9 +56,50 @@ typedef struct FlashSVContext {
     uint8_t        *tmpblock;
     int             block_size;
     z_stream        zstream;
+    int             ver;
+    const uint32_t *pal;
+    int             is_keyframe;
+    uint8_t        *keyframedata;
+    uint8_t        *keyframe;
+    BlockInfo      *blocks;
+    uint8_t        *deflate_block;
+    int             deflate_block_size;
+    int             color_depth;
+    int             zlibprime_curr, zlibprime_prev;
+    int             diff_start, diff_height;
 } FlashSVContext;
 
 
+static int decode_hybrid(const uint8_t *sptr, uint8_t *dptr, int dx, int dy,
+                         int h, int w, int stride, const uint32_t *pal)
+{
+    int x, y;
+    const uint8_t *orig_src = sptr;
+
+    for (y = dx+h; y > dx; y--) {
+        uint8_t *dst = dptr + (y * stride) + dy * 3;
+        for (x = 0; x < w; x++) {
+            if (*sptr & 0x80) {
+                /* 15-bit color */
+                unsigned c = AV_RB16(sptr) & ~0x8000;
+                unsigned b =  c        & 0x1F;
+                unsigned g = (c >>  5) & 0x1F;
+                unsigned r =  c >> 10;
+                /* 000aaabb -> aaabbaaa  */
+                *dst++ = (b << 3) | (b >> 2);
+                *dst++ = (g << 3) | (g >> 2);
+                *dst++ = (r << 3) | (r >> 2);
+                sptr += 2;
+            } else {
+                /* palette index */
+                uint32_t c = pal[*sptr++];
+                bytestream_put_le24(&dst, c);
+            }
+        }
+    }
+    return sptr - orig_src;
+}
+
 static av_cold int flashsv_decode_init(AVCodecContext *avctx)
 {
     FlashSVContext *s = avctx->priv_data;
@@ -70,9 +121,42 @@ static av_cold int flashsv_decode_init(AVCodecContext *avctx)
 }
 
 
+static void flashsv2_prime(FlashSVContext *s, uint8_t *src,
+                           int size, int unp_size)
+{
+    z_stream zs;
+
+    zs.zalloc = NULL;
+    zs.zfree  = NULL;
+    zs.opaque = NULL;
+
+    s->zstream.next_in   = src;
+    s->zstream.avail_in  = size;
+    s->zstream.next_out  = s->tmpblock;
+    s->zstream.avail_out = s->block_size * 3;
+    inflate(&s->zstream, Z_SYNC_FLUSH);
+
+    deflateInit(&zs, 0);
+    zs.next_in   = s->tmpblock;
+    zs.avail_in  = s->block_size * 3 - s->zstream.avail_out;
+    zs.next_out  = s->deflate_block;
+    zs.avail_out = s->deflate_block_size;
+    deflate(&zs, Z_SYNC_FLUSH);
+    deflateEnd(&zs);
+
+    inflateReset(&s->zstream);
+
+    s->zstream.next_in   = s->deflate_block;
+    s->zstream.avail_in  = s->deflate_block_size - zs.avail_out;
+    s->zstream.next_out  = s->tmpblock;
+    s->zstream.avail_out = s->block_size * 3;
+    inflate(&s->zstream, Z_SYNC_FLUSH);
+}
+
 static int flashsv_decode_block(AVCodecContext *avctx, AVPacket *avpkt,
                                 GetBitContext *gb, int block_size,
-                                int width, int height, int x_pos, int y_pos)
+                                int width, int height, int x_pos, int y_pos,
+                                int blk_idx)
 {
     struct FlashSVContext *s = avctx->priv_data;
     uint8_t *line = s->tmpblock;
@@ -81,6 +165,10 @@ static int flashsv_decode_block(AVCodecContext *avctx, AVPacket *avpkt,
     if (ret != Z_OK) {
         //return -1;
     }
+    if (s->zlibprime_curr || s->zlibprime_prev) {
+        flashsv2_prime(s, s->blocks[blk_idx].pos, s->blocks[blk_idx].size,
+                       s->blocks[blk_idx].unp_size);
+    }
     s->zstream.next_in   = avpkt->data + get_bits_count(gb) / 8;
     s->zstream.avail_in  = block_size;
     s->zstream.next_out  = s->tmpblock;
@@ -95,19 +183,48 @@ static int flashsv_decode_block(AVCodecContext *avctx, AVPacket *avpkt,
     if (ret != Z_OK && ret != Z_STREAM_END) {
         //return -1;
     }
-    /* Flash Screen Video stores the image upside down, so copy
-     * lines to destination in reverse order. */
-    for (k = 1; k <= height; k++) {
-        memcpy(s->frame.data[0] + x_pos * 3 +
-               (s->image_height - y_pos - k) * s->frame.linesize[0],
-               line, width * 3);
-        /* advance source pointer to next line */
-        line += width * 3;
+
+    if (s->is_keyframe) {
+        s->blocks[blk_idx].pos      = s->keyframedata + (get_bits_count(gb) / 8);
+        s->blocks[blk_idx].size     = block_size;
+        s->blocks[blk_idx].unp_size = s->block_size * 3 - s->zstream.avail_out;
+    }
+    if (!s->color_depth) {
+        /* Flash Screen Video stores the image upside down, so copy
+         * lines to destination in reverse order. */
+        for (k = 1; k <= s->diff_height; k++) {
+            memcpy(s->frame.data[0] + x_pos * 3 +
+                   (s->image_height - y_pos - s->diff_start - k) * s->frame.linesize[0],
+                   line, width * 3);
+            /* advance source pointer to next line */
+            line += width * 3;
+        }
+    } else {
+        /* hybrid 15-bit/palette mode */
+        decode_hybrid(s->tmpblock, s->frame.data[0],
+                      s->image_height - (y_pos + 1 + s->diff_start + s->diff_height),
+                      x_pos, s->diff_height, width,
+                      s->frame.linesize[0], s->pal);
     }
     skip_bits_long(gb, 8 * block_size); /* skip the consumed bits */
     return 0;
 }
 
+static int calc_deflate_block_size(int tmpblock_size)
+{
+    z_stream zstream;
+    int size;
+
+    zstream.zalloc = Z_NULL;
+    zstream.zfree  = Z_NULL;
+    zstream.opaque = Z_NULL;
+    if (deflateInit(&zstream, 0) != Z_OK)
+        return -1;
+    size = deflateBound(&zstream, tmpblock_size);
+    deflateEnd(&zstream);
+
+    return size;
+}
 
 static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
                                 int *data_size, AVPacket *avpkt)
@@ -131,6 +248,18 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
     s->block_height = 16 * (get_bits(&gb,  4) + 1);
     s->image_height =       get_bits(&gb, 12);
 
+    if (s->ver == 2) {
+        skip_bits(&gb, 6);
+        if (get_bits1(&gb)) {
+            av_log_missing_feature(avctx, "iframe", 1);
+            return AVERROR_PATCHWELCOME;
+        }
+        if (get_bits1(&gb)) {
+            av_log_missing_feature(avctx, "custom palette", 1);
+            return AVERROR_PATCHWELCOME;
+        }
+    }
+
     /* calculate number of blocks and size of border (partial) blocks */
     h_blocks = s->image_width  / s->block_width;
     h_part   = s->image_width  % s->block_width;
@@ -140,11 +269,25 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
     /* the block size could change between frames, make sure the buffer
      * is large enough, if not, get a larger one */
     if (s->block_size < s->block_width * s->block_height) {
-        av_free(s->tmpblock);
-        if ((s->tmpblock = av_malloc(3 * s->block_width * s->block_height)) == NULL) {
+        int tmpblock_size = 3 * s->block_width * s->block_height;
+
+        s->tmpblock = av_realloc(s->tmpblock, tmpblock_size);
+        if (!s->tmpblock) {
             av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
             return AVERROR(ENOMEM);
         }
+        if (s->ver == 2) {
+            s->deflate_block_size = calc_deflate_block_size(tmpblock_size);
+            if (s->deflate_block_size <= 0) {
+                av_log(avctx, AV_LOG_ERROR, "Can't determine deflate buffer size.\n");
+                return -1;
+            }
+            s->deflate_block = av_realloc(s->deflate_block, s->deflate_block_size);
+            if (!s->deflate_block) {
+                av_log(avctx, AV_LOG_ERROR, "Can't allocate deflate buffer.\n");
+                return AVERROR(ENOMEM);
+            }
+        }
     }
     s->block_size = s->block_width * s->block_height;
 
@@ -163,6 +306,16 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
         return AVERROR_INVALIDDATA;
     }
 
+    /* we care for keyframes only in Screen Video v2 */
+    s->is_keyframe = (avpkt->flags & AV_PKT_FLAG_KEY) && (s->ver == 2);
+    if (s->is_keyframe) {
+        s->keyframedata = av_realloc(s->keyframedata, avpkt->size);
+        memcpy(s->keyframedata, avpkt->data, avpkt->size);
+        s->blocks = av_realloc(s->blocks,
+                               (v_blocks + !!v_part) * (h_blocks + !!h_part)
+                               * sizeof(s->blocks[0]));
+    }
+
     av_dlog(avctx, "image: %dx%d block: %dx%d num: %dx%d part: %dx%d\n",
             s->image_width, s->image_height, s->block_width, s->block_height,
             h_blocks, v_blocks, h_part, v_part);
@@ -186,25 +339,90 @@ static int flashsv_decode_frame(AVCodecContext *avctx, void *data,
         for (i = 0; i < h_blocks + (h_part ? 1 : 0); i++) {
             int x_pos = i * s->block_width; // horizontal position in frame
             int cur_blk_width = (i < h_blocks) ? s->block_width : h_part;
+            int has_diff = 0;
 
             /* get the size of the compressed zlib chunk */
             int size = get_bits(&gb, 16);
+
+            s->color_depth    = 0;
+            s->zlibprime_curr = 0;
+            s->zlibprime_prev = 0;
+            s->diff_start     = 0;
+            s->diff_height    = cur_blk_height;
+
             if (8 * size > get_bits_left(&gb)) {
                 avctx->release_buffer(avctx, &s->frame);
                 s->frame.data[0] = NULL;
                 return AVERROR_INVALIDDATA;
             }
 
+            if (s->ver == 2 && size) {
+                skip_bits(&gb, 3);
+                s->color_depth    = get_bits(&gb, 2);
+                has_diff          = get_bits1(&gb);
+                s->zlibprime_curr = get_bits1(&gb);
+                s->zlibprime_prev = get_bits1(&gb);
+
+                if (s->color_depth != 0 && s->color_depth != 2) {
+                    av_log(avctx, AV_LOG_ERROR,
+                           "%dx%d invalid color depth %d\n", i, j, s->color_depth);
+                    return -1;
+                }
+
+                if (has_diff) {
+                    s->diff_start  = get_bits(&gb, 8);
+                    s->diff_height = get_bits(&gb, 8);
+                    av_log(avctx, AV_LOG_DEBUG,
+                           "%dx%d diff start %d height %d\n",
+                           i, j, s->diff_start, s->diff_height);
+                    size -= 2;
+                }
+
+                if (s->zlibprime_prev)
+                    av_log(avctx, AV_LOG_DEBUG, "%dx%d zlibprime_prev\n", i, j);
+
+                if (s->zlibprime_curr) {
+                    int col = get_bits(&gb, 8);
+                    int row = get_bits(&gb, 8);
+                    av_log(avctx, AV_LOG_DEBUG, "%dx%d zlibprime_curr %dx%d\n", i, j, col, row);
+                    size -= 2;
+                    av_log_missing_feature(avctx, "zlibprime_curr", 1);
+                    return AVERROR_PATCHWELCOME;
+                }
+                size--; // account for flags byte
+            }
+
+            if (has_diff) {
+                int k;
+                int off = (s->image_height - y_pos - 1) * s->frame.linesize[0];
+
+                for (k = 0; k < cur_blk_height; k++)
+                    memcpy(s->frame.data[0] + off - k*s->frame.linesize[0] + x_pos*3,
+                           s->keyframe + off - k*s->frame.linesize[0] + x_pos*3,
+                           cur_blk_width * 3);
+            }
+
             /* skip unchanged blocks, which have size 0 */
             if (size) {
                 if (flashsv_decode_block(avctx, avpkt, &gb, size,
                                          cur_blk_width, cur_blk_height,
-                                         x_pos, y_pos))
+                                         x_pos, y_pos,
+                                         i + j * (h_blocks + !!h_part)))
                     av_log(avctx, AV_LOG_ERROR,
                            "error in decompression of block %dx%d\n", i, j);
             }
         }
     }
+    if (s->is_keyframe && s->ver == 2) {
+        if (!s->keyframe) {
+            s->keyframe = av_malloc(s->frame.linesize[0] * avctx->height);
+            if (!s->keyframe) {
+                av_log(avctx, AV_LOG_ERROR, "Cannot allocate image data\n");
+                return AVERROR(ENOMEM);
+            }
+        }
+        memcpy(s->keyframe, s->frame.data[0], s->frame.linesize[0] * avctx->height);
+    }
 
     *data_size = sizeof(AVFrame);
     *(AVFrame*)data = s->frame;
@@ -233,6 +451,7 @@ static av_cold int flashsv_decode_end(AVCodecContext *avctx)
 }
 
 
+#if CONFIG_FLASHSV_DECODER
 AVCodec ff_flashsv_decoder = {
     .name           = "flashsv",
     .type           = AVMEDIA_TYPE_VIDEO,
@@ -245,3 +464,67 @@ AVCodec ff_flashsv_decoder = {
     .pix_fmts       = (const enum PixelFormat[]){PIX_FMT_BGR24, PIX_FMT_NONE},
     .long_name      = NULL_IF_CONFIG_SMALL("Flash Screen Video v1"),
 };
+#endif /* CONFIG_FLASHSV_DECODER */
+
+#if CONFIG_FLASHSV2_DECODER
+static const uint32_t ff_flashsv2_default_palette[128] = {
+    0x000000, 0x333333, 0x666666, 0x999999, 0xCCCCCC, 0xFFFFFF,
+    0x330000, 0x660000, 0x990000, 0xCC0000, 0xFF0000, 0x003300,
+    0x006600, 0x009900, 0x00CC00, 0x00FF00, 0x000033, 0x000066,
+    0x000099, 0x0000CC, 0x0000FF, 0x333300, 0x666600, 0x999900,
+    0xCCCC00, 0xFFFF00, 0x003333, 0x006666, 0x009999, 0x00CCCC,
+    0x00FFFF, 0x330033, 0x660066, 0x990099, 0xCC00CC, 0xFF00FF,
+    0xFFFF33, 0xFFFF66, 0xFFFF99, 0xFFFFCC, 0xFF33FF, 0xFF66FF,
+    0xFF99FF, 0xFFCCFF, 0x33FFFF, 0x66FFFF, 0x99FFFF, 0xCCFFFF,
+    0xCCCC33, 0xCCCC66, 0xCCCC99, 0xCCCCFF, 0xCC33CC, 0xCC66CC,
+    0xCC99CC, 0xCCFFCC, 0x33CCCC, 0x66CCCC, 0x99CCCC, 0xFFCCCC,
+    0x999933, 0x999966, 0x9999CC, 0x9999FF, 0x993399, 0x996699,
+    0x99CC99, 0x99FF99, 0x339999, 0x669999, 0xCC9999, 0xFF9999,
+    0x666633, 0x666699, 0x6666CC, 0x6666FF, 0x663366, 0x669966,
+    0x66CC66, 0x66FF66, 0x336666, 0x996666, 0xCC6666, 0xFF6666,
+    0x333366, 0x333399, 0x3333CC, 0x3333FF, 0x336633, 0x339933,
+    0x33CC33, 0x33FF33, 0x663333, 0x993333, 0xCC3333, 0xFF3333,
+    0x003366, 0x336600, 0x660033, 0x006633, 0x330066, 0x663300,
+    0x336699, 0x669933, 0x993366, 0x339966, 0x663399, 0x996633,
+    0x6699CC, 0x99CC66, 0xCC6699, 0x66CC99, 0x9966CC, 0xCC9966,
+    0x99CCFF, 0xCCFF99, 0xFF99CC, 0x99FFCC, 0xCC99FF, 0xFFCC99,
+    0x111111, 0x222222, 0x444444, 0x555555, 0xAAAAAA, 0xBBBBBB,
+    0xDDDDDD, 0xEEEEEE
+};
+
+static av_cold int flashsv2_decode_init(AVCodecContext *avctx)
+{
+    FlashSVContext *s = avctx->priv_data;
+    flashsv_decode_init(avctx);
+    s->pal = ff_flashsv2_default_palette;
+    s->ver = 2;
+
+    return 0;
+}
+
+static av_cold int flashsv2_decode_end(AVCodecContext *avctx)
+{
+    FlashSVContext *s = avctx->priv_data;
+
+    av_freep(&s->keyframedata);
+    av_freep(&s->blocks);
+    av_freep(&s->keyframe);
+    av_freep(&s->deflate_block);
+    flashsv_decode_end(avctx);
+
+    return 0;
+}
+
+AVCodec ff_flashsv2_decoder = {
+    .name           = "flashsv2",
+    .type           = AVMEDIA_TYPE_VIDEO,
+    .id             = CODEC_ID_FLASHSV2,
+    .priv_data_size = sizeof(FlashSVContext),
+    .init           = flashsv2_decode_init,
+    .close          = flashsv2_decode_end,
+    .decode         = flashsv_decode_frame,
+    .capabilities   = CODEC_CAP_DR1,
+    .pix_fmts       = (const enum PixelFormat[]){PIX_FMT_BGR24, PIX_FMT_NONE},
+    .long_name      = NULL_IF_CONFIG_SMALL("Flash Screen Video v2"),
+};
+#endif /* CONFIG_FLASHSV2_DECODER */
index a4e0402..8d40899 100644 (file)
@@ -21,7 +21,7 @@
 #define AVCODEC_VERSION_H
 
 #define LIBAVCODEC_VERSION_MAJOR 53
-#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, \