[FFmpeg-devel] [PATCH] encoder for adobe's flash ScreenVideo2 codec

Daniel Verkamp daniel
Fri Nov 20 16:01:46 CET 2009


On Thu, Jul 23, 2009 at 8:42 AM, Joshua Warner <joshuawarner32 at gmail.com> wrote:
[...]
>>>>> I've developed an encoder for Adobe's Flash ScreenVideo2 format
[...]

I started on a decoder for this format; attached is my progress so far.

It correctly decodes the first frame of samples created by the encoder
from this thread, but it does not implement any of the fancy features
yet (interframes, diff blocks, custom palettes, zlib priming of either
variety).

The official documentation seems to be missing a few points or just
plain wrong based on comparison with the encoder patch; specifically,
ZlibPrimeCompressPrevious (by my reading of this encoder) does not
mean that the previous block used zlib priming.  It rather means to
prime zlib with data from the equivalent block of the previous frame
before decompressing the current block's data.  This seems like a
reinvention of zlib's deflate/inflateSetDictionary functionality...

I'm not sure how to prime it on the decompression side (using
inflateSetDictionary doesn't work, as far as I can tell, because the
stream was not actually compressed with a custom dictionary - the
encoder actually runs the previous block through the compressor,
discards the result, and then compresses the current block without
reinitializing the decoder).  It seems like I would need to store the
zlib state for each block of the previous frame to make this possible,
but surely that is not how it is intended.

Anyway, if any intrepid hackers want something to do, have at it - I
probably will not do any serious work on it in the near future.

Thanks,
-- Daniel Verkamp
-------------- next part --------------
>From 6f66c0e1779528fd6f3c1ebe599dd1bff9adc8b9 Mon Sep 17 00:00:00 2001
From: Daniel Verkamp <daniel at drv.nu>
Date: Fri, 20 Nov 2009 07:03:28 -0500
Subject: [PATCH] WIP flashsv2 decoder

---
 libavcodec/Makefile    |    1 +
 libavcodec/allcodecs.c |    1 +
 libavcodec/avcodec.h   |    1 +
 libavcodec/flashsv.c   |  145 +++++++++++++++++++++++++++++++++++++++++++++++-
 libavformat/flvdec.c   |    1 +
 5 files changed, 148 insertions(+), 1 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index dc572e8..fdf547d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -111,6 +111,7 @@ OBJS-$(CONFIG_FFVHUFF_ENCODER)         += huffyuv.o
 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_FLASHSV2_DECODER)        += flashsv.o
 OBJS-$(CONFIG_FLASHSV_ENCODER)         += flashsvenc.o
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
 OBJS-$(CONFIG_FLV_DECODER)             += h263dec.o h263.o \
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 6a2eec2..a0f8ec1 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -92,6 +92,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);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 7ffbd95..ac3b803 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -199,6 +199,7 @@ enum CodecID {
     CODEC_ID_DPX,
     CODEC_ID_MAD,
     CODEC_ID_FRWU,
+    CODEC_ID_FLASHSV2,
 
     /* various PCM "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
diff --git a/libavcodec/flashsv.c b/libavcodec/flashsv.c
index 8870fe6..0407fd9 100644
--- a/libavcodec/flashsv.c
+++ b/libavcodec/flashsv.c
@@ -55,6 +55,33 @@
 
 #include <zlib.h>
 
+#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
+};
+#endif /* CONFIG_FLASHSV2_DECODER */
+
 typedef struct FlashSVContext {
     AVCodecContext *avctx;
     AVFrame frame;
@@ -63,6 +90,8 @@ typedef struct FlashSVContext {
     uint8_t* tmpblock;
     int block_size;
     z_stream zstream;
+    int ver;
+    const uint32_t *pal;
 } FlashSVContext;
 
 
@@ -79,6 +108,38 @@ static void copy_region(uint8_t *sptr, uint8_t *dptr,
 }
 
 
+static void decode_hybird(const uint8_t *sptr, uint8_t *dptr,
+        int dx, int dy, int h, int w, int stride, const uint32_t *pal)
+{
+    int x, y;
+
+    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 = ((sptr[0] & ~0x80) << 8) | sptr[1];
+                unsigned b =  c        & 0x1F;
+                unsigned g = (c >>  5) & 0x1F;
+                unsigned r =  c >> 10;
+                /* 000aabbb -> aa bbb aa  */
+                *dst++ = (b << 3) | (r >> 3);
+                *dst++ = (g << 3) | (g >> 3);
+                *dst++ = (r << 3) | (b >> 3);
+                sptr += 2;
+            } else {
+                /* palette index */
+                uint32_t c = pal[*sptr++];
+                *dst++ =  c        & 0xFF;
+                *dst++ = (c >>  8) & 0xFF;
+                *dst++ =  c >> 16;
+            }
+        }
+    }
+}
+
+
 static av_cold int flashsv_decode_init(AVCodecContext *avctx)
 {
     FlashSVContext *s = avctx->priv_data;
@@ -95,6 +156,18 @@ static av_cold int flashsv_decode_init(AVCodecContext *avctx)
     }
     avctx->pix_fmt = PIX_FMT_BGR24;
     s->frame.data[0] = NULL;
+    s->ver = 1;
+
+    return 0;
+}
+
+
+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;
 }
@@ -122,6 +195,18 @@ static int flashsv_decode_frame(AVCodecContext *avctx,
     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(avctx, AV_LOG_ERROR, "FIXME iframe not implemented yet\n");
+            return -1;
+        }
+        if (get_bits1(&gb)) {
+            av_log(avctx, AV_LOG_ERROR, "FIXME custom palette not implemented yet\n");
+            return -1;
+        }
+    }
+
     /* calculate amount of blocks and the size of the border blocks */
     h_blocks = s->image_width / s->block_width;
     h_part = s->image_width % s->block_width;
@@ -133,7 +218,7 @@ static int flashsv_decode_frame(AVCodecContext *avctx,
     if(s->block_size < s->block_width*s->block_height) {
         if (s->tmpblock != NULL)
             av_free(s->tmpblock);
-        if ((s->tmpblock = av_malloc(3*s->block_width*s->block_height)) == NULL) {
+        if ((s->tmpblock = av_malloc(2*3*s->block_width*s->block_height)) == NULL) {
             av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
             return -1;
         }
@@ -182,6 +267,39 @@ static int flashsv_decode_frame(AVCodecContext *avctx,
             /* get the size of the compressed zlib chunk */
             int size = get_bits(&gb, 16);
 
+            int color_depth = 0, zlibprime_curr = 0, zlibprime_prev = 0, has_diff = 0;
+
+            if (s->ver == 2 && size) {
+                skip_bits(&gb, 3);
+                color_depth = get_bits(&gb, 2);
+                has_diff = get_bits1(&gb);
+                zlibprime_curr = get_bits1(&gb);
+                zlibprime_prev = get_bits1(&gb);
+
+                if (color_depth != 0 && color_depth != 2) {
+                    av_log(avctx, AV_LOG_ERROR, "%dx%d invalid color depth %d\n", i, j, color_depth);
+                    return -1;
+                }
+
+                if (has_diff) {
+                    int start = get_bits(&gb, 8);
+                    int diff_h = get_bits(&gb, 8);
+                    av_log(avctx, AV_LOG_DEBUG, "%dx%d diff start %d height %d\n", i, j, start, diff_h);
+                    size -= 2;
+                }
+
+                if (zlibprime_prev)
+                    av_log(avctx, AV_LOG_DEBUG, "%dx%d zlibprime_prev\n", i, j);
+
+                if (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;
+                }
+                size--; // account for flags byte
+            }
+
             if (size == 0) {
                 /* no change, don't do anything */
             } else {
@@ -209,6 +327,13 @@ static int flashsv_decode_frame(AVCodecContext *avctx,
                     av_log(avctx, AV_LOG_ERROR, "error in decompression of block %dx%d: %d\n", i, j, ret);
                     /* return -1; */
                 }
+
+                if (color_depth) {
+                    /* hybird 15-bit/palette mode */
+                    decode_hybird(s->tmpblock, s->frame.data[0],
+                                  s->image_height-(hp+hs+1), wp, hs, ws,
+                                  s->frame.linesize[0], s->pal);
+                } else
                 copy_region(s->tmpblock, s->frame.data[0], s->image_height-(hp+hs+1), wp, hs, ws, s->frame.linesize[0]);
                 skip_bits_long(&gb, 8*size);   /* skip the consumed bits */
             }
@@ -243,6 +368,7 @@ static av_cold int flashsv_decode_end(AVCodecContext *avctx)
 }
 
 
+#if CONFIG_FLASHSV_DECODER
 AVCodec flashsv_decoder = {
     "flashsv",
     CODEC_TYPE_VIDEO,
@@ -256,3 +382,20 @@ AVCodec 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
+AVCodec flashsv2_decoder = {
+    "flashsv2",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_FLASHSV2,
+    sizeof(FlashSVContext),
+    flashsv2_decode_init,
+    NULL,
+    flashsv_decode_end,
+    flashsv_decode_frame,
+    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 */
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index 27062dc..2be5e2d 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -82,6 +82,7 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_co
     switch(flv_codecid) {
         case FLV_CODECID_H263  : vcodec->codec_id = CODEC_ID_FLV1   ; break;
         case FLV_CODECID_SCREEN: vcodec->codec_id = CODEC_ID_FLASHSV; break;
+        case FLV_CODECID_SCREEN2: vcodec->codec_id = CODEC_ID_FLASHSV2; break;
         case FLV_CODECID_VP6   : vcodec->codec_id = CODEC_ID_VP6F   ;
         case FLV_CODECID_VP6A  :
             if(flv_codecid == FLV_CODECID_VP6A)
-- 
1.6.5.2



More information about the ffmpeg-devel mailing list