[FFmpeg-devel] Flash Screen Video 2 samples?

Daniel Verkamp daniel
Wed Mar 25 08:49:19 CET 2009


On Wed, Mar 25, 2009 at 12:07 AM, Mike Melanson <mike at multimedia.cx> wrote:
> compn wrote:
>>
>> On Tue, 24 Mar 2009 20:49:24 -0700, Mike Melanson wrote:
>>>
>>> Daniel Verkamp wrote:
>>>>
>>>> Hi,
>>>>
>>>> Does anyone have any samples of Flash video files using the flashsv2
>>>> codec? ?The only flashsv sample I could find in the mplayer samples
>>>> archive is http://samples.mplayerhq.hu/FLV/flash_screen/screen.flv ,
>>>> which uses version 1 of the screen codec.
>>>
>>> None known to exist. Apparently, the only way that the flashsv2 codec
>>> exists "in the wild" is when it is streamed by certain Adobe media servers.
>>>
>>> I realize that this is a problem and I have been working on ways to
>>> capture/contrive some samples for the open source community.
>>
>> tried rtmpdump? got urls?
>
> This is moving up on my priority list since there is renewed interest due to
> GSoC.
>
>> :grumbles about supporting a codec with no samples while codecs with
>> lots of samples get ignored:
>
> As always, "patch welcome". :)
>
> --
> ? ?-Mike Melanson

Attached:

1.  Add new codec id for flashsv2 and hook it up in the FLV (de)muxer
- this should be ok for inclusion as-is.

2.  Temporary flashsv2 encoder - this is just a hacked-up version of
the flashsv1 encoder that does not use any new v2 features; however,
it *does not work*.  I've tested a file produced by this encoder in JW
Player with Flash Player 10.0.22.87 (Windows) and it does not play any
video (audio plays fine, as do files with flashsv1).  I am fairly
certain it does follow the SWF file format spec for v2, so maybe
something is missing/incorrect in the spec.  Also note that unchanged
block support is disabled (with the || 1) just to be sure that case is
not the problem; I was not sure from reading the spec whether the
Format field in IMAGEBLOCKV2 is to be coded when DataSize == 0 (it
appears that it should be).

Thanks,
-- Daniel Verkamp
-------------- next part --------------
>From 13f0e291fb3d0eab959b57f5b8d2312253e9879d Mon Sep 17 00:00:00 2001
From: Daniel Verkamp <daniel at drv.nu>
Date: Wed, 25 Mar 2009 02:40:53 -0500
Subject: [PATCH 1/2] Add CODEC_ID_FLASHSV2 and hook it up in the FLV (de)muxer

---
 libavcodec/Makefile  |    1 +
 libavcodec/avcodec.h |    1 +
 libavformat/flvdec.c |    1 +
 libavformat/flvenc.c |    1 +
 4 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 4bb6179..8881fae 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -87,6 +87,7 @@ OBJS-$(CONFIG_FLAC_DECODER)            += flacdec.o flacdata.o flac.o
 OBJS-$(CONFIG_FLAC_ENCODER)            += flacenc.o flacdata.o flac.o lpc.o
 OBJS-$(CONFIG_FLASHSV_DECODER)         += flashsv.o
 OBJS-$(CONFIG_FLASHSV_ENCODER)         += flashsvenc.o
+OBJS-$(CONFIG_FLASHSV2_ENCODER)        += flashsv2enc.o
 OBJS-$(CONFIG_FLIC_DECODER)            += flicvideo.o
 OBJS-$(CONFIG_FLV_DECODER)             += h263dec.o h263.o mpeg12data.o mpegvideo.o error_resilience.o
 OBJS-$(CONFIG_FLV_ENCODER)             += mpegvideo_enc.o motion_est.o ratecontrol.o h263.o mpeg12data.o mpegvideo.o error_resilience.o
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 2b9adf2..8cc4870 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -193,6 +193,7 @@ enum CodecID {
     CODEC_ID_TQI,
     CODEC_ID_AURA,
     CODEC_ID_AURA2,
+    CODEC_ID_FLASHSV2,
 
     /* various PCM "codecs" */
     CODEC_ID_PCM_S16LE= 0x10000,
diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c
index 89ece0f..199335a 100644
--- a/libavformat/flvdec.c
+++ b/libavformat/flvdec.c
@@ -93,6 +93,7 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, int flv_co
         case FLV_CODECID_H264:
             vcodec->codec_id = CODEC_ID_H264;
             return 3; // not 4, reading packet type will consume one byte
+        case FLV_CODECID_SCREEN2: vcodec->codec_id = CODEC_ID_FLASHSV2; break;
         default:
             av_log(s, AV_LOG_INFO, "Unsupported video codec (%x)\n", flv_codecid);
             vcodec->codec_tag = flv_codecid;
diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c
index 63accbb..c76fa12 100644
--- a/libavformat/flvenc.c
+++ b/libavformat/flvenc.c
@@ -32,6 +32,7 @@ static const AVCodecTag flv_video_codec_ids[] = {
     {CODEC_ID_VP6F,    FLV_CODECID_VP6   },
     {CODEC_ID_VP6,     FLV_CODECID_VP6   },
     {CODEC_ID_H264,    FLV_CODECID_H264  },
+    {CODEC_ID_FLASHSV2,FLV_CODECID_SCREEN2},
     {CODEC_ID_NONE,    0}
 };
 
-- 
1.6.2
-------------- next part --------------
>From 7a2dfdb201abb743b3e0e32a06291a7596d5e283 Mon Sep 17 00:00:00 2001
From: Daniel Verkamp <daniel at drv.nu>
Date: Wed, 25 Mar 2009 02:41:58 -0500
Subject: [PATCH 2/2] Temporary non-optimal flashsv2 encoder

---
 libavcodec/allcodecs.c   |    1 +
 libavcodec/flashsv2enc.c |  306 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 307 insertions(+), 0 deletions(-)
 create mode 100644 libavcodec/flashsv2enc.c

diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 17adff0..25d54de 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -85,6 +85,7 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC  (FFV1, ffv1);
     REGISTER_ENCDEC  (FFVHUFF, ffvhuff);
     REGISTER_ENCDEC  (FLASHSV, flashsv);
+    REGISTER_ENCODER (FLASHSV2, flashsv2);
     REGISTER_DECODER (FLIC, flic);
     REGISTER_ENCDEC  (FLV, flv);
     REGISTER_DECODER (FOURXM, fourxm);
diff --git a/libavcodec/flashsv2enc.c b/libavcodec/flashsv2enc.c
new file mode 100644
index 0000000..b8f120a
--- /dev/null
+++ b/libavcodec/flashsv2enc.c
@@ -0,0 +1,306 @@
+/*
+ * Flash Screen Video encoder
+ * Copyright (C) 2004 Alex Beregszaszi
+ * Copyright (C) 2006 Benjamin Larsson
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Encoding development sponsored by http://fh-campuswien.ac.at */
+
+/**
+ * @file libavcodec/flashsvenc.c
+ * Flash Screen Video encoder
+ * @author Alex Beregszaszi
+ * @author Benjamin Larsson
+ */
+
+/* Bitstream description
+ * The picture is divided into blocks that are zlib-compressed.
+ *
+ * The decoder is fed complete frames, the frameheader contains:
+ * 4bits of block width
+ * 12bits of frame width
+ * 4bits of block height
+ * 12bits of frame height
+ *
+ * Directly after the header are the compressed blocks. The blocks
+ * have their compressed size represented with 16bits in the beginig.
+ * If the size = 0 then the block is unchanged from the previous frame.
+ * All blocks are decompressed until the buffer is consumed.
+ *
+ * Encoding ideas, a basic encoder would just use a fixed block size.
+ * Block sizes can be multipels of 16, from 16 to 256. The blocks don't
+ * have to be quadratic. A brute force search with a set of different
+ * block sizes should give a better result than to just use a fixed size.
+ */
+
+/* TODO:
+ * Don't reencode the frame in brute force mode if the frame is a dupe. Speed up.
+ * Make the difference check faster.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "avcodec.h"
+#include "bitstream.h"
+#include "bytestream.h"
+
+
+typedef struct FlashSVContext {
+    AVCodecContext *avctx;
+    uint8_t *previous_frame;
+    AVFrame frame;
+    int image_width, image_height;
+    int block_width, block_height;
+    uint8_t* tmpblock;
+    uint8_t* encbuffer;
+    int block_size;
+    z_stream zstream;
+    int last_key_frame;
+} FlashSVContext;
+
+static int copy_region_enc(uint8_t *sptr, uint8_t *dptr,
+        int dx, int dy, int h, int w, int stride, uint8_t *pfptr) {
+    int i,j;
+    uint8_t *nsptr;
+    uint8_t *npfptr;
+    int diff = 0;
+
+    for (i = dx+h; i > dx; i--) {
+        nsptr = sptr+(i*stride)+dy*3;
+        npfptr = pfptr+(i*stride)+dy*3;
+        for (j=0 ; j<w*3 ; j++) {
+            diff |=npfptr[j]^nsptr[j];
+            dptr[j] = nsptr[j];
+        }
+        dptr += w*3;
+    }
+    if (diff)
+        return 1;
+    return 0;
+}
+
+static av_cold int flashsv_encode_init(AVCodecContext *avctx)
+{
+    FlashSVContext *s = avctx->priv_data;
+
+    s->avctx = avctx;
+
+    if ((avctx->width > 4095) || (avctx->height > 4095)) {
+        av_log(avctx, AV_LOG_ERROR, "Input dimensions too large, input must be max 4096x4096 !\n");
+        return -1;
+    }
+
+    if (avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) {
+        return -1;
+    }
+
+    // Needed if zlib unused or init aborted before deflateInit
+    memset(&(s->zstream), 0, sizeof(z_stream));
+
+    s->last_key_frame=0;
+
+    s->image_width = avctx->width;
+    s->image_height = avctx->height;
+
+    s->tmpblock = av_mallocz(3*256*256);
+    s->encbuffer = av_mallocz(s->image_width*s->image_height*3);
+
+    if (!s->tmpblock || !s->encbuffer) {
+        av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int encode_bitstream(FlashSVContext *s, AVFrame *p, uint8_t *buf, int buf_size,
+     int block_width, int block_height, uint8_t *previous_frame, int* I_frame) {
+
+    PutBitContext pb;
+    int h_blocks, v_blocks, h_part, v_part, i, j;
+    int buf_pos, res;
+    int pred_blocks = 0;
+
+    init_put_bits(&pb, buf, buf_size*8);
+
+    put_bits(&pb, 4, (block_width/16)-1);
+    put_bits(&pb, 12, s->image_width);
+    put_bits(&pb, 4, (block_height/16)-1);
+    put_bits(&pb, 12, s->image_height);
+
+    put_bits(&pb, 6, 0); // reserved
+    put_bits(&pb, 1, 0); // HasIFrameInfo
+    put_bits(&pb, 1, 0); // HasPaletteInfo
+
+	// TODO: PaletteInfo goes here
+    flush_put_bits(&pb);
+    buf_pos=5; // TODO: + size of PaletteInfo
+
+    h_blocks = s->image_width / block_width;
+    h_part = s->image_width % block_width;
+    v_blocks = s->image_height / block_height;
+    v_part = s->image_height % block_height;
+
+    /* loop over all block columns */
+    for (j = 0; j < v_blocks + (v_part?1:0); j++)
+    {
+
+        int hp = j*block_height; // horiz position in frame
+        int hs = (j<v_blocks)?block_height:v_part; // size of block
+
+        /* loop over all block rows */
+        for (i = 0; i < h_blocks + (h_part?1:0); i++)
+        {
+            int wp = i*block_width; // vert position in frame
+            int ws = (i<h_blocks)?block_width:h_part; // size of block
+            int ret=Z_OK;
+            uint8_t *ptr;
+
+            ptr = buf+buf_pos;
+
+            //copy the block to the temp buffer before compression (if it differs from the previous frame's block)
+            res = copy_region_enc(p->data[0], s->tmpblock, s->image_height-(hp+hs+1), wp, hs, ws, p->linesize[0], previous_frame);
+
+            if (res || *I_frame || 1) {
+                unsigned long zsize;
+                zsize = 3*block_width*block_height;
+                ret = compress2(ptr+3, &zsize, s->tmpblock, 3*ws*hs, 9);
+
+
+                //ret = deflateReset(&(s->zstream));
+                if (ret != Z_OK)
+                    av_log(s->avctx, AV_LOG_ERROR, "error while compressing block %dx%d\n", i, j);
+
+                bytestream_put_be16(&ptr,(unsigned int)zsize);
+                bytestream_put_byte(&ptr, 0); // TODO: IMAGEFORMAT
+                buf_pos += zsize+3;
+                //av_log(avctx, AV_LOG_ERROR, "buf_pos = %d\n", buf_pos);
+            } else {
+                pred_blocks++;
+                bytestream_put_be16(&ptr,0);
+                bytestream_put_byte(&ptr, 0); // TODO: IMAGEFORMAT
+                buf_pos += 3;
+            }
+        }
+    }
+
+    if (pred_blocks)
+        *I_frame = 0;
+    else
+        *I_frame = 1;
+
+    return buf_pos;
+}
+
+
+static int flashsv_encode_frame(AVCodecContext *avctx, uint8_t *buf, int buf_size, void *data)
+{
+    FlashSVContext * const s = avctx->priv_data;
+    AVFrame *pict = data;
+    AVFrame * const p = &s->frame;
+    uint8_t *pfptr;
+    int res;
+    int I_frame = 0;
+    int opt_w, opt_h;
+
+    *p = *pict;
+
+    /* First frame needs to be a keyframe */
+    if (avctx->frame_number == 0) {
+        s->previous_frame = av_mallocz(FFABS(p->linesize[0])*s->image_height);
+        if (!s->previous_frame) {
+            av_log(avctx, AV_LOG_ERROR, "Memory allocation failed.\n");
+            return -1;
+        }
+        I_frame = 1;
+    }
+
+    if (p->linesize[0] < 0)
+        pfptr = s->previous_frame - ((s->image_height-1) * p->linesize[0]);
+    else
+        pfptr = s->previous_frame;
+
+    /* Check the placement of keyframes */
+    if (avctx->gop_size > 0) {
+        if (avctx->frame_number >= s->last_key_frame + avctx->gop_size) {
+            I_frame = 1;
+        }
+    }
+
+    opt_w=4;
+    opt_h=4;
+
+    if (buf_size < s->image_width*s->image_height*3) {
+        //Conservative upper bound check for compressed data
+        av_log(avctx, AV_LOG_ERROR, "buf_size %d <  %d\n", buf_size, s->image_width*s->image_height*3);
+        return -1;
+    }
+
+    res = encode_bitstream(s, p, buf, buf_size, opt_w*16, opt_h*16, pfptr, &I_frame);
+
+    //save the current frame
+    if(p->linesize[0] > 0)
+        memcpy(s->previous_frame, p->data[0], s->image_height*p->linesize[0]);
+    else
+        memcpy(s->previous_frame, p->data[0] + p->linesize[0] * (s->image_height-1), s->image_height*FFABS(p->linesize[0]));
+
+    //mark the frame type so the muxer can mux it correctly
+    if (I_frame) {
+        p->pict_type = FF_I_TYPE;
+        p->key_frame = 1;
+        s->last_key_frame = avctx->frame_number;
+        av_log(avctx, AV_LOG_DEBUG, "Inserting key frame at frame %d\n",avctx->frame_number);
+    } else {
+        p->pict_type = FF_P_TYPE;
+        p->key_frame = 0;
+    }
+
+    avctx->coded_frame = p;
+
+    return res;
+}
+
+static av_cold int flashsv_encode_end(AVCodecContext *avctx)
+{
+    FlashSVContext *s = avctx->priv_data;
+
+    deflateEnd(&(s->zstream));
+
+    av_free(s->encbuffer);
+    av_free(s->previous_frame);
+    av_free(s->tmpblock);
+
+    return 0;
+}
+
+AVCodec flashsv2_encoder = {
+    "flashsv2",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_FLASHSV2,
+    sizeof(FlashSVContext),
+    flashsv_encode_init,
+    flashsv_encode_frame,
+    flashsv_encode_end,
+    .pix_fmts = (enum PixelFormat[]){PIX_FMT_BGR24, PIX_FMT_NONE},
+    .long_name = NULL_IF_CONFIG_SMALL("Flash Screen Video v2"),
+};
+
-- 
1.6.2



More information about the ffmpeg-devel mailing list