[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