[Swfdec-commits] 2 commits - swfdec/Makefile.am swfdec/swfdec_audio_decoder_gst.c swfdec/swfdec_codec_gst.c swfdec/swfdec_codec_screen.c swfdec/swfdec_codec_video.c swfdec/swfdec_codec_video.h swfdec/swfdec_codec_vp6_alpha.c swfdec/swfdec_decoder.c swfdec/swfdec_flv_decoder.c swfdec/swfdec_flv_decoder.h swfdec/swfdec_init.c swfdec/swfdec_internal.h swfdec/swfdec_net_stream.c swfdec/swfdec_net_stream.h swfdec/swfdec_video.c swfdec/swfdec_video_decoder.c swfdec/swfdec_video_decoder_gst.c swfdec/swfdec_video_decoder_gst.h swfdec/swfdec_video_decoder.h swfdec/swfdec_video_decoder_screen.c swfdec/swfdec_video_decoder_screen.h swfdec/swfdec_video_decoder_vp6_alpha.c swfdec/swfdec_video_decoder_vp6_alpha.h swfdec/swfdec_video_movie.c swfdec/swfdec_video_provider.c swfdec/swfdec_video_provider.h swfdec/swfdec_video_video_provider.c swfdec/swfdec_video_video_provider.h

Benjamin Otte company at kemper.freedesktop.org
Wed Jun 25 02:53:38 PDT 2008


 swfdec/Makefile.am                      |   13 -
 swfdec/swfdec_audio_decoder_gst.c       |    1 
 swfdec/swfdec_codec_gst.c               |  188 ----------------
 swfdec/swfdec_codec_screen.c            |  134 -----------
 swfdec/swfdec_codec_video.c             |  330 ----------------------------
 swfdec/swfdec_codec_video.h             |   80 ------
 swfdec/swfdec_codec_vp6_alpha.c         |  111 ---------
 swfdec/swfdec_decoder.c                 |    5 
 swfdec/swfdec_flv_decoder.c             |    1 
 swfdec/swfdec_flv_decoder.h             |    1 
 swfdec/swfdec_init.c                    |    6 
 swfdec/swfdec_internal.h                |   13 -
 swfdec/swfdec_net_stream.c              |  218 +++++++++---------
 swfdec/swfdec_net_stream.h              |    3 
 swfdec/swfdec_video.c                   |    1 
 swfdec/swfdec_video_decoder.c           |  372 ++++++++++++++++++++++++++++++++
 swfdec/swfdec_video_decoder.h           |  113 +++++++++
 swfdec/swfdec_video_decoder_gst.c       |  211 ++++++++++++++++++
 swfdec/swfdec_video_decoder_gst.h       |   56 ++++
 swfdec/swfdec_video_decoder_screen.c    |  138 +++++++++++
 swfdec/swfdec_video_decoder_screen.h    |   52 ++++
 swfdec/swfdec_video_decoder_vp6_alpha.c |  138 +++++++++++
 swfdec/swfdec_video_decoder_vp6_alpha.h |   55 ++++
 swfdec/swfdec_video_movie.c             |   27 --
 swfdec/swfdec_video_provider.c          |   28 ++
 swfdec/swfdec_video_provider.h          |    6 
 swfdec/swfdec_video_video_provider.c    |   30 +-
 swfdec/swfdec_video_video_provider.h    |    2 
 28 files changed, 1335 insertions(+), 998 deletions(-)

New commits:
commit 649cee75b316f728fcaa2010d2a863978b1b9c91
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jun 25 11:52:23 2008 +0200

    make swfdec's video decoder a proper object
    
    This follows the changes to the audio decoder.
    Also, it fixes the last testsuite failure

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index 9478371..85dda11 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -3,7 +3,7 @@ SUBDIRS = jpeg
 CODEC_FILES =
 
 if HAVE_GST
-CODEC_FILES += swfdec_codec_gst.c swfdec_audio_decoder_gst.c
+CODEC_FILES += swfdec_codec_gst.c swfdec_audio_decoder_gst.c swfdec_video_decoder_gst.c
 endif
 
 lib_LTLIBRARIES = libswfdec- at SWFDEC_MAJORMINOR@.la
@@ -59,9 +59,6 @@ libswfdec_source_files = \
 	swfdec_cached_video.c \
 	swfdec_camera.c \
 	swfdec_character.c \
-	swfdec_codec_screen.c \
-	swfdec_codec_video.c \
-	swfdec_codec_vp6_alpha.c \
 	swfdec_color.c \
 	swfdec_color_as.c \
 	swfdec_color_matrix_filter.c \
@@ -162,6 +159,9 @@ libswfdec_source_files = \
 	swfdec_url.c \
 	swfdec_utils.c \
 	swfdec_video.c \
+	swfdec_video_decoder.c \
+	swfdec_video_decoder_screen.c \
+	swfdec_video_decoder_vp6_alpha.c \
 	swfdec_video_movie.c \
 	swfdec_video_movie_as.c \
 	swfdec_video_provider.c \
@@ -255,7 +255,6 @@ noinst_HEADERS = \
 	swfdec_cached_video.h \
 	swfdec_character.h \
 	swfdec_codec_gst.h \
-	swfdec_codec_video.h \
 	swfdec_color.h \
 	swfdec_constant_pool.h \
 	swfdec_debug.h \
@@ -316,6 +315,10 @@ noinst_HEADERS = \
 	swfdec_types.h \
 	swfdec_utils.h \
 	swfdec_video.h \
+	swfdec_video_decoder.h \
+	swfdec_video_decoder_gst.h \
+	swfdec_video_decoder_screen.h \
+	swfdec_video_decoder_vp6_alpha.h \
 	swfdec_video_movie.h \
 	swfdec_video_provider.h \
 	swfdec_video_video_provider.h \
diff --git a/swfdec/swfdec_codec_gst.c b/swfdec/swfdec_codec_gst.c
index 919c8e8..638e8a6 100644
--- a/swfdec/swfdec_codec_gst.c
+++ b/swfdec/swfdec_codec_gst.c
@@ -24,31 +24,9 @@
 #include <gst/pbutils/pbutils.h>
 
 #include "swfdec_codec_gst.h"
-#include "swfdec_codec_video.h"
 #include "swfdec_debug.h"
 #include "swfdec_internal.h"
 
-/*** CAPS MATCHING ***/
-
-static GstCaps *
-swfdec_video_decoder_get_caps (guint codec)
-{
-  GstCaps *caps;
-
-  switch (codec) {
-    case SWFDEC_VIDEO_CODEC_H263:
-      caps = gst_caps_from_string ("video/x-flash-video");
-      break;
-    case SWFDEC_VIDEO_CODEC_VP6:
-      caps = gst_caps_from_string ("video/x-vp6-flash");
-      break;
-    default:
-      return NULL;
-  }
-  g_assert (caps);
-  return caps;
-}
-
 /*** BUFFER ***/
 
 GstBuffer *
@@ -332,169 +310,3 @@ swfdec_gst_decoder_pull (SwfdecGstDecoder *dec)
   return g_queue_pop_head (dec->queue);
 }
 
-/*** VIDEO ***/
-
-/* NB: We don't put a colorspace tansform here, we just assume that the codecs
- * in GStreamer decode to the native format that we enforce. */
-typedef struct _SwfdecGstVideo SwfdecGstVideo;
-struct _SwfdecGstVideo {
-  SwfdecVideoDecoder	decoder;
-
-  gboolean		error;
-  SwfdecGstDecoder	dec;		/* the decoder element */
-  GstBuffer *		last;		/* last decoded buffer */
-};
-
-static void
-swfdec_video_decoder_gst_free (SwfdecVideoDecoder *dec)
-{
-  SwfdecGstVideo *player = (SwfdecGstVideo *) dec;
-
-  swfdec_gst_decoder_finish (&player->dec);
-  if (player->last)
-    gst_buffer_unref (player->last);
-
-  g_slice_free (SwfdecGstVideo, player);
-}
-
-static gboolean
-swfdec_video_decoder_gst_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer,
-    SwfdecVideoImage *image)
-{
-  SwfdecGstVideo *player = (SwfdecGstVideo *) dec;
-#define SWFDEC_ALIGN(x, n) (((x) + (n) - 1) & (~((n) - 1)))
-  GstBuffer *buf;
-  GstCaps *caps;
-  GstStructure *structure;
-
-  if (player->error)
-    return FALSE;
-  if (player->last) {
-    gst_buffer_unref (player->last);
-    player->last = NULL;
-  }
-
-  buf = swfdec_gst_buffer_new (swfdec_buffer_ref (buffer));
-  if (!swfdec_gst_decoder_push (&player->dec, buf)) {
-    SWFDEC_ERROR ("failed to push buffer");
-    player->error = TRUE;
-    return FALSE;
-  }
-
-  player->last = swfdec_gst_decoder_pull (&player->dec);
-  if (player->last == NULL) {
-    SWFDEC_ERROR ("failed to pull decoded buffer");
-    player->error = TRUE;
-    return FALSE;
-  }
-  while ((buf = swfdec_gst_decoder_pull (&player->dec))) {
-    SWFDEC_WARNING ("too many output buffers!");
-  }
-  caps = gst_buffer_get_caps (player->last);
-  if (caps == NULL) {
-    SWFDEC_ERROR ("no caps on decoded buffer");
-    player->error = TRUE;
-    return FALSE;
-  }
-  structure = gst_caps_get_structure (caps, 0);
-  if (!gst_structure_get_int (structure, "width", (int *) &image->width) ||
-      !gst_structure_get_int (structure, "height", (int *) &image->height)) {
-    SWFDEC_ERROR ("invalid caps on decoded buffer");
-    player->error = TRUE;
-    return FALSE;
-  }
-  image->mask = NULL;
-  buf = player->last;
-  switch (swfdec_video_codec_get_format (dec->codec)) {
-    case SWFDEC_VIDEO_FORMAT_RGBA:
-      image->plane[0] = buf->data;
-      image->rowstride[0] = image->width * 4;
-      break;
-    case SWFDEC_VIDEO_FORMAT_I420:
-      image->plane[0] = buf->data;
-      image->rowstride[0] = SWFDEC_ALIGN (image->width, 4);
-      image->plane[1] = image->plane[0] + image->rowstride[0] * SWFDEC_ALIGN (image->height, 2);
-      image->rowstride[1] = SWFDEC_ALIGN (image->width, 8) / 2;
-      image->plane[2] = image->plane[1] + image->rowstride[1] * SWFDEC_ALIGN (image->height, 2) / 2;
-      image->rowstride[2] = image->rowstride[1];
-      g_assert (image->plane[2] + image->rowstride[2] * SWFDEC_ALIGN (image->height, 2) / 2 == image->plane[0] + buf->size);
-      break;
-    default:
-      g_return_val_if_reached (FALSE);
-  }
-  return TRUE;
-#undef SWFDEC_ALIGN
-}
-
-static GstCaps *
-swfdec_video_decoder_get_sink_caps (guint codec)
-{
-  switch (swfdec_video_codec_get_format (codec)) {
-    case SWFDEC_VIDEO_FORMAT_RGBA:
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-      return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, "
-	  "red_mask=16711680, green_mask=65280, blue_mask=255");
-#else
-      return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, "
-	  "red_mask=65280, green_mask=16711680, blue_mask=-16777216");
-#endif
-    case SWFDEC_VIDEO_FORMAT_I420:
-      return gst_caps_from_string ("video/x-raw-yuv, format=(fourcc)I420");
-    default:
-      g_return_val_if_reached (NULL);
-  }
-}
-
-SwfdecVideoDecoder *
-swfdec_video_decoder_gst_new (guint codec)
-{
-  SwfdecGstVideo *player;
-  GstCaps *srccaps, *sinkcaps;
-
-  srccaps = swfdec_video_decoder_get_caps (codec);
-  if (srccaps == NULL)
-    return NULL;
-  sinkcaps = swfdec_video_decoder_get_sink_caps (codec);
-
-  player = g_slice_new0 (SwfdecGstVideo);
-  player->decoder.decode = swfdec_video_decoder_gst_decode;
-  player->decoder.free = swfdec_video_decoder_gst_free;
-
-  if (!swfdec_gst_decoder_init (&player->dec, srccaps, sinkcaps, NULL)) {
-    swfdec_video_decoder_gst_free (&player->decoder);
-    gst_caps_unref (srccaps);
-    gst_caps_unref (sinkcaps);
-    return NULL;
-  }
-
-  gst_caps_unref (srccaps);
-  gst_caps_unref (sinkcaps);
-  return &player->decoder;
-}
-
-/*** MISSING PLUGIN SUPPORT ***/
-  
-gboolean
-swfdec_video_decoder_gst_prepare (guint codec, char **detail)
-{
-  GstElementFactory *factory;
-  GstCaps *caps;
-
-  /* Check if we can handle the format at all. If not, no plugin will help us. */
-  caps = swfdec_video_decoder_get_caps (codec);
-  if (caps == NULL)
-    return FALSE;
-
-  /* If we can already handle it, woohoo! */
-  factory = swfdec_gst_get_element_factory (caps);
-  if (factory != NULL) {
-    gst_object_unref (factory);
-    return TRUE;
-  }
-
-  /* need to install plugins... */
-  *detail = gst_missing_decoder_installer_detail_new (caps);
-  gst_caps_unref (caps);
-  return FALSE;
-}
-
diff --git a/swfdec/swfdec_codec_screen.c b/swfdec/swfdec_codec_screen.c
deleted file mode 100644
index 5dbdfce..0000000
--- a/swfdec/swfdec_codec_screen.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
- *
- * This library 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.
- * 
- * This library 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 this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
- * Boston, MA  02110-1301  USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <zlib.h>
-#include <liboil/liboil.h>
-
-#include "swfdec_codec_video.h"
-#include "swfdec_bits.h"
-#include "swfdec_debug.h"
-#include "swfdec_internal.h"
-
-typedef struct _SwfdecCodecScreen SwfdecCodecScreen;
-
-struct _SwfdecCodecScreen {
-  SwfdecVideoDecoder	decoder;	/* the decoder */
-  gulong		width;		/* width of last image */
-  gulong		height;		/* height of last image */
-  guint8 *		data;		/* contains decoded image */
-};
-
-static gboolean
-swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer,
-    SwfdecVideoImage *image)
-{
-  SwfdecCodecScreen *screen = (SwfdecCodecScreen *) dec;
-  SwfdecBits bits;
-  gulong i, j, w, h, bw, bh, stride;
-
-  swfdec_bits_init (&bits, buffer);
-  bw = (swfdec_bits_getbits (&bits, 4) + 1) * 16;
-  w = swfdec_bits_getbits (&bits, 12);
-  bh = (swfdec_bits_getbits (&bits, 4) + 1) * 16;
-  h = swfdec_bits_getbits (&bits, 12);
-  if (screen->width == 0 || screen->height == 0) {
-    if (w == 0 || h == 0) {
-      SWFDEC_ERROR ("width or height is 0: %lux%lu", w, h);
-      return FALSE;
-    }
-    screen->data = g_try_malloc (w * h * 4);
-    if (screen->data == NULL) {
-      SWFDEC_ERROR ("could not allocate %lu bytes", w * h * 4);
-      return FALSE;
-    }
-    screen->width = w;
-    screen->height = h;
-  } else if (screen->width != w || screen->height != h) {
-    SWFDEC_ERROR ("width or height differ from original: was %lux%lu, is %lux%lu",
-	screen->width, screen->height, w, h);
-    /* FIXME: this is what ffmpeg does, should we be more forgiving? */
-    return FALSE;
-  }
-  stride = w * 4;
-  SWFDEC_LOG ("size: %lu x %lu - block size %lu x %lu\n", w, h, bw, bh);
-  for (j = 0; j < h; j += bh) {
-    for (i = 0; i < w; i += bw) {
-      guint x, y, size;
-      SwfdecBuffer *buf;
-      guint8 *in, *out;
-      size = swfdec_bits_get_bu16 (&bits);
-      if (size == 0)
-	continue;
-      buf = swfdec_bits_decompress (&bits, size, bw * bh * 4);
-      if (buf == NULL) {
-	SWFDEC_WARNING ("error decoding block");
-	continue;
-      }
-      /* convert format and write out data */
-      out = screen->data + stride * (h - j - 1) + i * 4;
-      in = buf->data;
-      for (y = 0; y < MIN (bh, h - j); y++) {
-	for (x = 0; x < MIN (bw, w - i); x++) {
-	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_BLUE] = *in++;
-	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_GREEN] = *in++;
-	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_RED] = *in++;
-	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_ALPHA] = 0xFF;
-	}
-      }
-      swfdec_buffer_unref (buf);
-    }
-  }
-  image->width = screen->width;
-  image->height = screen->height;
-  image->plane[0] = screen->data;
-  image->rowstride[0] = stride;
-  image->mask = NULL;
-  return TRUE;
-}
-
-static void
-swfdec_video_decoder_screen_free (SwfdecVideoDecoder *dec)
-{
-  SwfdecCodecScreen *screen = (SwfdecCodecScreen *) dec;
-
-  if (screen->data)
-    g_free (screen->data);
-  g_free (screen);
-}
-
-SwfdecVideoDecoder *
-swfdec_video_decoder_screen_new (guint type)
-{
-  SwfdecCodecScreen *screen;
-  
-  if (type != SWFDEC_VIDEO_CODEC_SCREEN)
-    return NULL;
-  
-  screen = g_new0 (SwfdecCodecScreen, 1);
-  screen->decoder.decode = swfdec_video_decoder_screen_decode;
-  screen->decoder.free = swfdec_video_decoder_screen_free;
-
-  return &screen->decoder;
-}
-
diff --git a/swfdec/swfdec_codec_video.c b/swfdec/swfdec_codec_video.c
deleted file mode 100644
index 8260945..0000000
--- a/swfdec/swfdec_codec_video.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Swfdec
- * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
- *
- * This library 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.
- * 
- * This library 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 this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
- * Boston, MA  02110-1301  USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <liboil/liboil.h>
-#include "swfdec_codec_video.h"
-#include "swfdec_color.h"
-#include "swfdec_debug.h"
-#include "swfdec_internal.h"
-#include "swfdec_renderer_internal.h"
-
-static gboolean
-swfdec_video_decoder_builtin_prepare (guint codec, char **detail)
-{
-  return codec == SWFDEC_VIDEO_CODEC_SCREEN;
-}
-
-static SwfdecVideoDecoder *
-swfdec_video_decoder_builtin_new (guint codec)
-{
-  SwfdecVideoDecoder *ret;
-
-  ret = swfdec_video_decoder_screen_new (codec);
-  if (ret == NULL)
-    ret = swfdec_video_decoder_vp6_alpha_new (codec);
-
-  return ret;
-}
-
-static const struct {
-  const char *		name;
-  SwfdecVideoDecoder *	(* func) (guint);
-  gboolean		(* prepare) (guint, char **);
-} video_codecs[] = {
-    { "builtin",	swfdec_video_decoder_builtin_new,	swfdec_video_decoder_builtin_prepare }
-#ifdef HAVE_GST
-  , { "gst",		swfdec_video_decoder_gst_new,		swfdec_video_decoder_gst_prepare }
-#endif
-};
-
-char *
-swfdec_video_decoder_prepare (guint codec)
-{
-  char *detail = NULL, *s = NULL;
-  guint i;
-  
-  /* the builtin codec is implemented as a wrapper around VP6 */
-  if (codec == SWFDEC_VIDEO_CODEC_VP6_ALPHA)
-    codec = SWFDEC_VIDEO_CODEC_VP6;
-
-  for (i = 0; i < G_N_ELEMENTS (video_codecs); i++) {
-    if (video_codecs[i].prepare (codec, &s)) {
-      g_free (detail);
-      g_free (s);
-      return NULL;
-    }
-    if (s) {
-      if (detail == NULL)
-	detail = s;
-      else
-	g_free (s);
-      s = NULL;
-    }
-  }
-  return detail;
-}
-
-/**
- * swfdec_video_decoder_new:
- * @codec: #SwfdecVideoCodec to create the #SwfdecVideoDecoder for
- *
- * Creates a new decoder to decode videos of type @codec. If no suitable
- * decoder could be created, %NULL is returned.
- *
- * Returns:
- **/
-SwfdecVideoDecoder *
-swfdec_video_decoder_new (guint codec)
-{
-  SwfdecVideoDecoder *ret = NULL;
-  guint i;
-
-  for (i = 0; i < G_N_ELEMENTS (video_codecs); i++) {
-    ret = video_codecs[i].func (codec);
-    if (ret)
-      break;
-  }
-
-  if (ret != NULL) {
-    ret->codec = codec;
-    g_return_val_if_fail (ret->decode, ret);
-    g_return_val_if_fail (ret->free, ret);
-  } else {
-    SWFDEC_WARNING ("no decoder found for codec %u", (guint) codec);
-  }
-  return ret;
-}
-
-/**
- * swfdec_video_decoder_free:
- * @decoder: a #SwfdecVideoDecoder
- *
- * Frees the given @decoder and all associated ressources.
- **/
-void
-swfdec_video_decoder_free (SwfdecVideoDecoder *decoder)
-{
-  g_return_if_fail (decoder);
-
-  decoder->free (decoder);
-}
-
-#define oil_argb(a,r,g,b) (((a) << 24) | ((r) << 16) | ((g) << 8) | b)
-static gint16 jfif_matrix[24] = {
-  0,      0,      -8192,   -8192,
-  16384,  0,      0,       0,
-  0,      16384,  16384,   16384,
-  0,      0,      -5638,   29032,
-  0,      22970,  -11700,  0,
-  0, 0, 0, 0
-};
-
-static void
-yuv_mux (guint32 *dest, const guint8 *src_y, const guint8 *src_u, const guint8 *src_v,
-    int n)
-{
-  int i;
-  for (i = 0; i < n; i++) {
-    dest[i] = oil_argb(255, src_y[i], src_u[i], src_v[i]);
-  }
-}
-
-static void
-upsample (guint8 *d, guint8 *s, int n)
-{
-  int i;
-
-  d[0] = s[0];
-
-  for (i = 0; i < n-3; i+=2) {
-    d[i + 1] = (3*s[i/2] + s[i/2+1] + 2)>>2;
-    d[i + 2] = (s[i/2] + 3*s[i/2+1] + 2)>>2;
-  }
-
-  if (n&1) {
-    i = n-3;
-    d[n-2] = s[n/2];
-    d[n-1] = s[n/2];
-  } else {
-    d[n-1] = s[n/2-1];
-  }
-}
-
-static guint8 *
-swfdec_video_i420_to_rgb (SwfdecVideoImage *image)
-{
-  guint32 *tmp;
-  guint8 *tmp_u;
-  guint8 *tmp_v;
-  guint8 *tmp1;
-  guint32 *argb_image;
-  const guint8 *yp, *up, *vp;
-  guint32 *argbp;
-  int j;
-  guint halfwidth;
-  int halfheight;
-
-  halfwidth = (image->width + 1)>>1;
-  tmp = g_malloc (4 * image->width * image->height);
-  tmp_u = g_malloc (image->width);
-  tmp_v = g_malloc (image->width);
-  tmp1 = g_malloc (halfwidth);
-  argb_image = g_malloc (4 * image->width * image->height);
-
-  yp = image->plane[0];
-  up = image->plane[1];
-  vp = image->plane[2];
-  argbp = argb_image;
-  halfheight = (image->height+1)>>1;
-  for(j=0;(guint)j<image->height;j++){
-    guint32 weight = 192 - 128*(j&1);
-
-    oil_merge_linear_u8(tmp1,
-        up + image->rowstride[1] * CLAMP((j-1)/2,0,halfheight-1),
-        up + image->rowstride[1] * CLAMP((j+1)/2,0,halfheight-1),
-        &weight, halfwidth);
-    upsample (tmp_u, tmp1, image->width);
-    oil_merge_linear_u8(tmp1,
-        vp + image->rowstride[2] * CLAMP((j-1)/2,0,halfheight-1),
-        vp + image->rowstride[2] * CLAMP((j+1)/2,0,halfheight-1),
-        &weight, halfwidth);
-    upsample (tmp_v, tmp1, image->width);
-
-    yuv_mux (tmp, yp, tmp_u, tmp_v, image->width);
-    oil_colorspace_argb(argbp, tmp, jfif_matrix, image->width);
-    yp += image->rowstride[0];
-    argbp += image->width;
-  }
-  g_free(tmp);
-  g_free(tmp_u);
-  g_free(tmp_v);
-  g_free(tmp1);
-  return (unsigned char *)argb_image;
-}
-
-/* FIXME: use liboil for this */
-static void
-swfdec_video_codec_apply_mask (guint8 *data, guint rowstride, const guint8 *mask,
-    guint mask_rowstride, guint width, guint height)
-{
-  const guint8 *in;
-  guint8 *out;
-  guint x, y;
-
-  data += SWFDEC_COLOR_INDEX_ALPHA;
-  for (y = 0; y < height; y++) {
-    in = mask;
-    out = data;
-    for (x = 0; x < width; x++) {
-      *out = *in;
-      out += 4;
-      in++;
-    }
-    mask += mask_rowstride;
-    data += rowstride;
-  }
-}
-
-/**
- * swfdec_video_decoder_decode:
- * @decoder: a #SwfdecVideoDecoder
- * @renderer: renderer the resulting surface should belong to
- * @buffer: buffer to decode
- * @width: relevant width of resulting image
- * @height: relevant height of resulting image
- *
- * Decodes the given buffer into a surface. On success, the relevant area of
- * the surface will be indicated by @width and @height.
- *
- * Returns: a new cairo image surface or %NULL on error.
- **/
-cairo_surface_t *
-swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecRenderer *renderer,
-    SwfdecBuffer *buffer, guint *width, guint *height)
-{
-  SwfdecVideoImage image;
-  cairo_surface_t *surface;
-  guint8 *data;
-  guint rowstride;
-
-  g_return_val_if_fail (decoder != NULL, NULL);
-  g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
-  g_return_val_if_fail (buffer != NULL, NULL);
-  g_return_val_if_fail (width != NULL, NULL);
-  g_return_val_if_fail (height != NULL, NULL);
-
-  if (!decoder->decode (decoder, buffer, &image)) {
-    SWFDEC_ERROR ("failed to decode video");
-    return NULL;
-  }
-  g_assert (image.width != 0 && image.height != 0);
-  /* FIXME: use cairo for all of this when cairo accelerates it */
-  if (swfdec_video_codec_get_format (decoder->codec) == SWFDEC_VIDEO_FORMAT_I420) {
-    data = swfdec_video_i420_to_rgb (&image);
-    if (data == NULL) {
-      SWFDEC_ERROR ("I420 => RGB conversion failed");
-      return NULL;
-    }
-    rowstride = image.width * 4;
-  } else {
-    rowstride = image.rowstride[0];
-    data = g_memdup (image.plane[0], rowstride * image.height);
-  }
-  if (image.mask) {
-    swfdec_video_codec_apply_mask (data, image.width * 4, image.mask, 
-	image.mask_rowstride, image.width, image.height);
-  }
-  surface = swfdec_renderer_create_for_data (renderer, data,
-      image.mask ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
-      image.width, image.height, rowstride);
-  *width = image.width;
-  *height = image.height;
-  return surface;
-}
-
-/**
- * swfdec_video_codec_get_format:
- * @codec: codec to check
- *
- * Returns the output format used for this codec. Video codecs must use these
- * codecs when decoding video.
- *
- * Returns: the output format to use for this format
- **/
-SwfdecVideoFormat
-swfdec_video_codec_get_format (guint codec)
-{
-  switch (codec) {
-    case SWFDEC_VIDEO_CODEC_H263:
-    case SWFDEC_VIDEO_CODEC_VP6:
-    case SWFDEC_VIDEO_CODEC_VP6_ALPHA:
-      return SWFDEC_VIDEO_FORMAT_I420;
-    case SWFDEC_VIDEO_CODEC_UNDEFINED:
-    case SWFDEC_VIDEO_CODEC_SCREEN:
-    case SWFDEC_VIDEO_CODEC_SCREEN2:
-      return SWFDEC_VIDEO_FORMAT_RGBA;
-    default:
-      g_return_val_if_reached (SWFDEC_VIDEO_FORMAT_RGBA);
-  }
-}
-
diff --git a/swfdec/swfdec_codec_video.h b/swfdec/swfdec_codec_video.h
deleted file mode 100644
index 5ad961f..0000000
--- a/swfdec/swfdec_codec_video.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Swfdec
- * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
- *
- * This library 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.
- * 
- * This library 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 this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
- * Boston, MA  02110-1301  USA
- */
-
-#ifndef _SWFDEC_CODEC_VIDEO_H_
-#define _SWFDEC_CODEC_VIDEO_H_
-
-#include <glib.h>
-#include <cairo.h>
-#include <swfdec/swfdec_buffer.h>
-#include <swfdec/swfdec_renderer.h>
-
-#define SWFDEC_VIDEO_CODEC_UNDEFINED 0
-#define SWFDEC_VIDEO_CODEC_H263 2
-#define SWFDEC_VIDEO_CODEC_SCREEN 3
-#define SWFDEC_VIDEO_CODEC_VP6 4
-#define SWFDEC_VIDEO_CODEC_VP6_ALPHA 5
-#define SWFDEC_VIDEO_CODEC_SCREEN2 6
-
-typedef enum {
-  SWFDEC_VIDEO_FORMAT_RGBA,
-  SWFDEC_VIDEO_FORMAT_I420
-} SwfdecVideoFormat;
-
-typedef struct {
-  guint			width;	      	/* width of image in pixels */
-  guint			height;	    	/* height of image in pixels */
-  const guint8 *	plane[3];	/* planes of the image, not all might be used */
-  const guint8 *	mask;		/* A8 mask or NULL if none */
-  guint		  	rowstride[3];	/* rowstrides of the planes */
-  guint			mask_rowstride;	/* rowstride of mask plane */
-} SwfdecVideoImage;
-
-typedef struct _SwfdecVideoDecoder SwfdecVideoDecoder;
-typedef SwfdecVideoDecoder * (SwfdecVideoDecoderNewFunc) (guint format);
-
-/* notes about the decode function:
- * - the data must be in the format specified by swfdec_video_codec_get_format()
- * - the data returned in the image belongs to the decoder and must be valid 
- *   until the next function is called on the decoder.
- * - you need to explicitly set mask to %NULL.
- */
-struct _SwfdecVideoDecoder {
-  guint			codec;
-  gboolean		(* decode)	(SwfdecVideoDecoder *	decoder,
-					 SwfdecBuffer *		buffer,
-					 SwfdecVideoImage *	result);
-  void			(* free)	(SwfdecVideoDecoder *	decoder);
-};
-
-SwfdecVideoFormat     	swfdec_video_codec_get_format	(guint			codec);
-
-char *			swfdec_video_decoder_prepare	(guint			codec);
-SwfdecVideoDecoder *	swfdec_video_decoder_new      	(guint			codec);
-void			swfdec_video_decoder_free	(SwfdecVideoDecoder *   decoder);
-
-cairo_surface_t *     	swfdec_video_decoder_decode	(SwfdecVideoDecoder *	decoder,
-							 SwfdecRenderer *	renderer,
-							 SwfdecBuffer *		buffer,
-							 guint *      		width,
-							 guint *		height);
-
-
-G_END_DECLS
-#endif
diff --git a/swfdec/swfdec_codec_vp6_alpha.c b/swfdec/swfdec_codec_vp6_alpha.c
deleted file mode 100644
index 672d3d7..0000000
--- a/swfdec/swfdec_codec_vp6_alpha.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
- *
- * This library 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.
- * 
- * This library 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 this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
- * Boston, MA  02110-1301  USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <zlib.h>
-#include <liboil/liboil.h>
-
-#include "swfdec_codec_video.h"
-#include "swfdec_bits.h"
-#include "swfdec_debug.h"
-#include "swfdec_internal.h"
-
-typedef struct _SwfdecCodecVp6Alpha SwfdecCodecVp6Alpha;
-
-struct _SwfdecCodecVp6Alpha {
-  SwfdecVideoDecoder	decoder;	/* the decoder */
-  SwfdecVideoDecoder *	image;		/* the image decoder */
-  SwfdecVideoDecoder *	alpha;		/* the alpha decoder */
-};
-
-static gboolean
-swfdec_video_decoder_vp6_alpha_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer,
-    SwfdecVideoImage *image)
-{
-  SwfdecCodecVp6Alpha *vp6 = (SwfdecCodecVp6Alpha *) dec;
-  SwfdecBuffer *tmp;
-  SwfdecVideoImage alpha;
-  SwfdecBits bits;
-  guint size;
-
-  swfdec_bits_init (&bits, buffer);
-  size = swfdec_bits_get_bu24 (&bits);
-  tmp = swfdec_bits_get_buffer (&bits, size);
-  if (tmp == NULL)
-    return FALSE;
-  if (!vp6->image->decode (vp6->image, tmp, image)) {
-    swfdec_buffer_unref (tmp);
-    return FALSE;
-  }
-  swfdec_buffer_unref (tmp);
-  tmp = swfdec_bits_get_buffer (&bits, -1);
-  if (tmp == NULL)
-    return FALSE;
-  if (!vp6->alpha->decode (vp6->alpha, tmp, &alpha)) {
-    swfdec_buffer_unref (tmp);
-    return FALSE;
-  }
-  swfdec_buffer_unref (tmp);
-  if (alpha.width != image->width || alpha.height != image->height) {
-    SWFDEC_ERROR ("size of mask doesn't match image: %ux%u vs %ux%u", 
-	alpha.width, alpha.height, image->width, image->height);
-    return FALSE;
-  }
-  image->mask = alpha.plane[0];
-  image->mask_rowstride = alpha.rowstride[0];
-  return TRUE;
-}
-
-static void
-swfdec_video_decoder_vp6_alpha_free (SwfdecVideoDecoder *dec)
-{
-  SwfdecCodecVp6Alpha *vp6 = (SwfdecCodecVp6Alpha *) dec;
-
-  if (vp6->image)
-    swfdec_video_decoder_free (vp6->image);
-  if (vp6->alpha)
-    swfdec_video_decoder_free (vp6->alpha);
-  g_free (vp6);
-}
-
-SwfdecVideoDecoder *
-swfdec_video_decoder_vp6_alpha_new (guint type)
-{
-  SwfdecCodecVp6Alpha *vp6;
-  
-  if (type != SWFDEC_VIDEO_CODEC_VP6_ALPHA)
-    return NULL;
-  
-  vp6 = g_new0 (SwfdecCodecVp6Alpha, 1);
-  vp6->decoder.decode = swfdec_video_decoder_vp6_alpha_decode;
-  vp6->decoder.free = swfdec_video_decoder_vp6_alpha_free;
-  vp6->image = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6);
-  vp6->alpha = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6);
-  if (vp6->alpha == NULL || vp6->image == NULL) {
-    swfdec_video_decoder_vp6_alpha_free (&vp6->decoder);
-    return NULL;
-  }
-
-  return &vp6->decoder;
-}
-
diff --git a/swfdec/swfdec_decoder.c b/swfdec/swfdec_decoder.c
index 4991ea7..9ce55b8 100644
--- a/swfdec/swfdec_decoder.c
+++ b/swfdec/swfdec_decoder.c
@@ -25,12 +25,12 @@
 
 #include "swfdec_decoder.h"
 #include "swfdec_audio_decoder.h"
-#include "swfdec_codec_video.h"
 #include "swfdec_debug.h"
 #include "swfdec_decoder.h"
 #include "swfdec_image.h"
 #include "swfdec_image_decoder.h"
 #include "swfdec_swf_decoder.h"
+#include "swfdec_video_decoder.h"
 
 enum {
   MISSING_PLUGINS,
@@ -135,7 +135,8 @@ swfdec_decoder_use_video_codec (SwfdecDecoder *decoder, guint codec)
 
   g_return_if_fail (SWFDEC_IS_DECODER (decoder));
 
-  detail = swfdec_video_decoder_prepare (codec);
+  if (swfdec_video_decoder_prepare (codec, &detail))
+    return;
   if (detail == NULL)
     return;
 
diff --git a/swfdec/swfdec_flv_decoder.c b/swfdec/swfdec_flv_decoder.c
index a8d1aba..29c6817 100644
--- a/swfdec/swfdec_flv_decoder.c
+++ b/swfdec/swfdec_flv_decoder.c
@@ -27,6 +27,7 @@
 #include "swfdec_bits.h"
 #include "swfdec_debug.h"
 #include "swfdec_player_internal.h"
+#include "swfdec_video_decoder.h"
 
 enum {
   SWFDEC_STATE_HEADER,			/* need to parse header */
diff --git a/swfdec/swfdec_flv_decoder.h b/swfdec/swfdec_flv_decoder.h
index 3bf7b4b..ab1b1dd 100644
--- a/swfdec/swfdec_flv_decoder.h
+++ b/swfdec/swfdec_flv_decoder.h
@@ -21,7 +21,6 @@
 #define __SWFDEC_FLV_DECODER_H__
 
 #include <swfdec/swfdec_audio_internal.h>
-#include <swfdec/swfdec_codec_video.h>
 #include <swfdec/swfdec_decoder.h>
 
 G_BEGIN_DECLS
diff --git a/swfdec/swfdec_init.c b/swfdec/swfdec_init.c
index 12aef51..91e14e8 100644
--- a/swfdec/swfdec_init.c
+++ b/swfdec/swfdec_init.c
@@ -27,12 +27,15 @@
 #include <gst/gst.h>
 #include <gst/pbutils/pbutils.h>
 #include "swfdec_audio_decoder_gst.h"
+#include "swfdec_video_decoder_gst.h"
 #endif
 #include <liboil/liboil.h>
 
 #include "swfdec_audio_decoder_adpcm.h"
 #include "swfdec_audio_decoder_uncompressed.h"
 #include "swfdec_debug.h"
+#include "swfdec_video_decoder_screen.h"
+#include "swfdec_video_decoder_vp6_alpha.h"
 
 /**
  * swfdec_init:
@@ -76,8 +79,11 @@ swfdec_init (void)
    * NB: The order is important! */
   swfdec_audio_decoder_register (SWFDEC_TYPE_AUDIO_DECODER_UNCOMPRESSED);
   swfdec_audio_decoder_register (SWFDEC_TYPE_AUDIO_DECODER_ADPCM);
+  swfdec_video_decoder_register (SWFDEC_TYPE_VIDEO_DECODER_SCREEN);
+  swfdec_video_decoder_register (SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA);
 #ifdef HAVE_GST
   swfdec_audio_decoder_register (SWFDEC_TYPE_AUDIO_DECODER_GST);
+  swfdec_video_decoder_register (SWFDEC_TYPE_VIDEO_DECODER_GST);
 #endif
 }
 
diff --git a/swfdec/swfdec_internal.h b/swfdec/swfdec_internal.h
index 6af7e94..d42af09 100644
--- a/swfdec/swfdec_internal.h
+++ b/swfdec/swfdec_internal.h
@@ -22,22 +22,13 @@
 #ifndef _SWFDEC_INTERNAL_H_
 #define _SWFDEC_INTERNAL_H_
 
+#include <swfdec/swfdec_buffer.h>
+#include <swfdec/swfdec_player.h>
 #include <swfdec/swfdec_types.h>
-#include <swfdec/swfdec_codec_video.h>
 
 G_BEGIN_DECLS
 
 
-/* video codecs */
-
-SwfdecVideoDecoder *	swfdec_video_decoder_screen_new		(guint			format);
-SwfdecVideoDecoder *	swfdec_video_decoder_vp6_alpha_new    	(guint			format);
-#ifdef HAVE_GST
-SwfdecVideoDecoder *	swfdec_video_decoder_gst_new		(guint			format);
-gboolean		swfdec_video_decoder_gst_prepare	(guint			codec,
-								 char **		detail);
-#endif
-
 /* AS engine setup code */
 
 void			swfdec_player_preinit_global		(SwfdecAsContext *	context);
diff --git a/swfdec/swfdec_net_stream.c b/swfdec/swfdec_net_stream.c
index 350fc67..5df42f3 100644
--- a/swfdec/swfdec_net_stream.c
+++ b/swfdec/swfdec_net_stream.c
@@ -62,6 +62,39 @@ swfdec_net_stream_onstatus (SwfdecNetStream *stream, const char *code, const cha
   swfdec_sandbox_unuse (stream->sandbox);
 }
 
+static void
+swfdec_net_stream_decode_video (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer)
+{
+  if (decoder->codec == SWFDEC_VIDEO_CODEC_VP6 ||
+      decoder->codec == SWFDEC_VIDEO_CODEC_VP6_ALPHA) {
+    /* FIXME: This is somewhat nasty as we modify values in the decoder 
+     * directly. I know the current decoders don't mind, but if we expose 
+     * the decoder API... */
+    guint wsub, hsub;
+    SwfdecBuffer *tmp;
+    if (buffer->length == 0) {
+      swfdec_video_decoder_error (decoder, "0-byte VP6 video image buffer?");
+      return;
+    }
+    wsub = *buffer->data;
+    hsub = wsub & 0xF;
+    wsub >>= 4;
+    tmp = swfdec_buffer_new_subbuffer (buffer, 1, buffer->length - 1);
+    swfdec_video_decoder_decode (decoder, tmp);
+    swfdec_buffer_unref (tmp);
+    if (hsub || wsub) {
+      if (hsub >= decoder->height || wsub >= decoder->width) {
+	swfdec_video_decoder_error (decoder, "can't reduce size by more than available");
+	return;
+      }
+      decoder->width -= wsub;
+      decoder->height -= hsub;
+    }
+  } else {
+    swfdec_video_decoder_decode (decoder, buffer);
+  }
+}
+
 static void swfdec_net_stream_update_playing (SwfdecNetStream *stream);
 static void
 swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
@@ -91,12 +124,64 @@ swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
   if (buffer == NULL) {
     SWFDEC_ERROR ("got no buffer - no video available?");
   } else {
-    if (format != stream->format) {
-      if (stream->decoder)
-	swfdec_video_decoder_free (stream->decoder);
-      stream->format = format;
+    guint next;
+
+    if (stream->decoder && swfdec_video_decoder_get_codec (stream->decoder) != format) {
+      g_object_unref (stream->decoder);
       stream->decoder = NULL;
     }
+    if (stream->decoder != NULL &&
+	(stream->decoder_time >= stream->current_time)) {
+      g_object_unref (stream->decoder);
+      stream->decoder = NULL;
+    }
+
+    if (stream->decoder == NULL) {
+      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	  stream->current_time, TRUE, &format, &stream->decoder_time,
+	  &next);
+      stream->decoder = swfdec_video_decoder_new (format);
+    } else {
+      swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	  stream->decoder_time, FALSE, NULL, NULL, &next);
+      if (next != stream->current_time) {
+	guint key_time, key_next;
+	buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	    stream->current_time, TRUE, &format, &key_time, &key_next);
+	if (key_time > stream->decoder_time) {
+	  stream->decoder_time = key_time;
+	  next = key_next;
+	} else {
+	  buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	      next, FALSE, &format, &stream->decoder_time,
+	      &next);
+	}
+      } else {
+	buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	    next, FALSE, &format, &stream->decoder_time,
+	    &next);
+      }
+    }
+
+    /* the following things hold:
+     * buffer: next buffer to decode
+     * format: format of that buffer
+     * stream->decoder_time: timestamp of buffer to decode
+     * stream->decoder: non-null, using stream->format
+     */
+    for (;;) {
+      if (format != swfdec_video_decoder_get_codec (stream->decoder)) {
+	g_object_unref (stream->decoder);
+	stream->decoder = swfdec_video_decoder_new (format);
+      }
+      swfdec_net_stream_decode_video (stream->decoder, buffer);
+      if (stream->decoder_time >= stream->current_time)
+	break;
+
+      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder,
+	  next, FALSE, &format, &stream->decoder_time, &next);
+    }
+
     swfdec_video_provider_new_image (SWFDEC_VIDEO_PROVIDER (stream));
   }
   if (stream->next_time <= stream->current_time) {
@@ -296,49 +381,12 @@ swfdec_net_stream_stream_target_init (SwfdecStreamTargetInterface *iface)
 /*** SWFDEC VIDEO PROVIDER ***/
 
 static cairo_surface_t *
-swfdec_net_stream_decode_video (SwfdecVideoDecoder *decoder, SwfdecRenderer *renderer,
-    SwfdecBuffer *buffer, guint *width, guint *height)
-{
-  cairo_surface_t *surface;
-
-  if (decoder->codec == SWFDEC_VIDEO_CODEC_VP6 ||
-      decoder->codec == SWFDEC_VIDEO_CODEC_VP6_ALPHA) {
-    guint wsub, hsub;
-    SwfdecBuffer *tmp;
-    if (buffer->length == 0) {
-      SWFDEC_ERROR ("0-byte VP6 video image buffer?");
-      return NULL;
-    }
-    wsub = *buffer->data;
-    hsub = wsub & 0xF;
-    wsub >>= 4;
-    tmp = swfdec_buffer_new_subbuffer (buffer, 1, buffer->length - 1);
-    surface = swfdec_video_decoder_decode (decoder, renderer, tmp, width, height);
-    swfdec_buffer_unref (tmp);
-    if (hsub || wsub) {
-      if (hsub >= *height || wsub >= *width) {
-	SWFDEC_ERROR ("can't reduce size by more than available");
-	cairo_surface_destroy (surface);
-	return NULL;
-      }
-      *width -= wsub;
-      *height -= hsub;
-    }
-  } else {
-    surface = swfdec_video_decoder_decode (decoder, renderer, buffer, width, height);
-  }
-  return surface;
-}
-
-static cairo_surface_t *
 swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
     SwfdecRenderer *renderer, guint *width, guint *height)
 {
   SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);
   SwfdecCachedVideo *cached;
   cairo_surface_t *surface;
-  SwfdecBuffer *buffer;
-  guint next, format;
 
   cached = SWFDEC_CACHED_VIDEO (swfdec_renderer_get_cache (renderer, stream, NULL, NULL));
   if (cached != NULL && swfdec_cached_video_get_frame (cached) == stream->current_time) {
@@ -347,70 +395,14 @@ swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
     return swfdec_cached_video_get_surface (cached);
   }
 
-  if (stream->flvdecoder == NULL || stream->flvdecoder->video == NULL)
+  if (stream->decoder == NULL)
     return NULL;
 
-  if (stream->decoder != NULL &&
-      (stream->decoder_time >= stream->current_time)) {
-    swfdec_video_decoder_free (stream->decoder);
-    stream->decoder = NULL;
-  }
-  if (stream->decoder == NULL) {
-    buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
-	stream->current_time, TRUE, &stream->format, &stream->decoder_time,
-	&next);
-    stream->decoder = swfdec_video_decoder_new (stream->format);
-    if (stream->decoder == NULL)
-      return NULL;
-    format = stream->format;
-  } else {
-    swfdec_flv_decoder_get_video (stream->flvdecoder, 
-	stream->decoder_time, FALSE, NULL, NULL, &next);
-    if (next != stream->current_time) {
-      guint key_time, key_next;
-      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
-	  stream->current_time, TRUE, &format, &key_time, &key_next);
-      if (key_time > stream->decoder_time) {
-	stream->decoder_time = key_time;
-	next = key_next;
-      } else {
-	buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
-	    next, FALSE, &format, &stream->decoder_time,
-	    &next);
-      }
-    } else {
-      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
-	  next, FALSE, &format, &stream->decoder_time,
-	  &next);
-    }
-  }
-
-  /* the following things hold:
-   * buffer: next buffer to decode
-   * format: format of that buffer
-   * stream->decoder_time: timestamp of buffer to decode
-   * stream->decoder: non-null, using stream->format
-   */
-  for (;;) {
-    if (format != stream->format) {
-      swfdec_video_decoder_free (stream->decoder);
-      stream->format = format;
-      stream->decoder = swfdec_video_decoder_new (stream->format);
-      if (stream->decoder == NULL)
-	return NULL;
-    }
-    surface = swfdec_net_stream_decode_video (stream->decoder, renderer,
-	buffer, width, height);
-    if (surface == NULL)
-      return NULL;
-    if (stream->decoder_time >= stream->current_time)
-      break;
-
-    cairo_surface_destroy (surface);
-    buffer = swfdec_flv_decoder_get_video (stream->flvdecoder,
-	next, FALSE, &format, &stream->decoder_time, &next);
-  }
-
+  surface = swfdec_video_decoder_get_image (stream->decoder, renderer);
+  if (surface == NULL)
+    return NULL;
+  *width = swfdec_video_decoder_get_width (stream->decoder);
+  *height = swfdec_video_decoder_get_height (stream->decoder);
   cached = swfdec_cached_video_new (surface, *width * *height * 4);
   swfdec_cached_video_set_frame (cached, stream->decoder_time);
   swfdec_cached_video_set_size (cached, *width, *height);
@@ -421,9 +413,25 @@ swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
 }
 
 static void
+swfdec_net_stream_video_provider_get_size (SwfdecVideoProvider *provider,
+    guint *width, guint *height)
+{
+  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);
+
+  if (stream->decoder) {
+    *width = swfdec_video_decoder_get_width (stream->decoder);
+    *height = swfdec_video_decoder_get_height (stream->decoder);
+  } else {
+    *width = 0;
+    *height = 0;
+  }
+}
+
+static void
 swfdec_net_stream_video_provider_init (SwfdecVideoProviderInterface *iface)
 {
   iface->get_image = swfdec_net_stream_video_provider_get_image;
+  iface->get_size = swfdec_net_stream_video_provider_get_size;
 }
 
 /*** SWFDEC_NET_STREAM ***/
@@ -443,7 +451,7 @@ swfdec_net_stream_dispose (GObject *object)
     stream->surface = NULL;
   }
   if (stream->decoder) {
-    swfdec_video_decoder_free (stream->decoder);
+    g_object_unref (stream->decoder);
     stream->decoder = NULL;
   }
   swfdec_net_stream_set_loader (stream, NULL);
@@ -686,7 +694,7 @@ swfdec_net_stream_seek (SwfdecNetStream *stream, double secs)
     return;
   }
   if (stream->decoder) {
-    swfdec_video_decoder_free (stream->decoder);
+    g_object_unref (stream->decoder);
     stream->decoder = NULL;
   }
   msecs = secs * 1000;
diff --git a/swfdec/swfdec_net_stream.h b/swfdec/swfdec_net_stream.h
index 2f7783d..78356c6 100644
--- a/swfdec/swfdec_net_stream.h
+++ b/swfdec/swfdec_net_stream.h
@@ -22,11 +22,11 @@
 
 #include <swfdec/swfdec.h>
 #include <swfdec/swfdec_as_object.h>
-#include <swfdec/swfdec_codec_video.h>
 #include <swfdec/swfdec_net_connection.h>
 #include <swfdec/swfdec_flv_decoder.h>
 #include <swfdec/swfdec_player_internal.h>
 #include <swfdec/swfdec_sandbox.h>
+#include <swfdec/swfdec_video_decoder.h>
 #include <swfdec/swfdec_video_movie.h>
 
 G_BEGIN_DECLS
@@ -60,7 +60,6 @@ struct _SwfdecNetStream
   /* video decoding */
   guint			current_time;	/* current playback timestamp */
   guint			next_time;	/* next video image at this timestamp */
-  guint			format;		/* current format */
   SwfdecVideoDecoder *	decoder;	/* decoder used for decoding */
   guint			decoder_time;	/* last timestamp the decoder decoded */
   cairo_surface_t *	surface;	/* current image */
diff --git a/swfdec/swfdec_video.c b/swfdec/swfdec_video.c
index 322a376..a03c758 100644
--- a/swfdec/swfdec_video.c
+++ b/swfdec/swfdec_video.c
@@ -25,6 +25,7 @@
 #include "swfdec_debug.h"
 #include "swfdec_player_internal.h"
 #include "swfdec_swf_decoder.h"
+#include "swfdec_video_decoder.h"
 #include "swfdec_video_movie.h"
 #include "swfdec_video_video_provider.h"
 
diff --git a/swfdec/swfdec_video_decoder.c b/swfdec/swfdec_video_decoder.c
new file mode 100644
index 0000000..439f59a
--- /dev/null
+++ b/swfdec/swfdec_video_decoder.c
@@ -0,0 +1,372 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <liboil/liboil.h>
+#include "swfdec_video_decoder.h"
+#include "swfdec_color.h"
+#include "swfdec_debug.h"
+#include "swfdec_renderer_internal.h"
+
+G_DEFINE_TYPE (SwfdecVideoDecoder, swfdec_video_decoder, G_TYPE_OBJECT)
+
+static void
+swfdec_video_decoder_class_init (SwfdecVideoDecoderClass *klass)
+{
+}
+
+static void
+swfdec_video_decoder_init (SwfdecVideoDecoder *video_decoder)
+{
+}
+
+static GSList *video_codecs = NULL;
+
+void
+swfdec_video_decoder_register (GType type)
+{
+  g_return_if_fail (g_type_is_a (type, SWFDEC_TYPE_VIDEO_DECODER));
+
+  video_codecs = g_slist_append (video_codecs, GSIZE_TO_POINTER ((gsize) type));
+}
+
+/**
+ * swfdec_video_codec_get_format:
+ * @codec: codec to check
+ *
+ * Returns the output format used for this codec. Video codecs must use these
+ * codecs when decoding video.
+ *
+ * Returns: the output format to use for this format
+ **/
+SwfdecVideoFormat
+swfdec_video_codec_get_format (guint codec)
+{
+  switch (codec) {
+    case SWFDEC_VIDEO_CODEC_H263:
+    case SWFDEC_VIDEO_CODEC_VP6:
+    case SWFDEC_VIDEO_CODEC_VP6_ALPHA:
+      return SWFDEC_VIDEO_FORMAT_I420;
+    case SWFDEC_VIDEO_CODEC_UNDEFINED:
+    case SWFDEC_VIDEO_CODEC_SCREEN:
+    case SWFDEC_VIDEO_CODEC_SCREEN2:
+      return SWFDEC_VIDEO_FORMAT_RGBA;
+    default:
+      SWFDEC_ERROR ("unknown codec %u, assuming RGBA format", codec);
+      return SWFDEC_VIDEO_FORMAT_RGBA;
+  }
+}
+
+gboolean
+swfdec_video_decoder_prepare (guint codec, char **missing)
+{
+  char *detail = NULL, *s = NULL;
+  GSList *walk;
+  
+  for (walk = video_codecs; walk; walk = walk->next) {
+    SwfdecVideoDecoderClass *klass = g_type_class_ref (GPOINTER_TO_SIZE (walk->data));
+    if (klass->prepare (codec, &s)) {
+      g_free (detail);
+      g_free (s);
+      if (missing)
+	*missing = NULL;
+      g_type_class_unref (klass);
+      return TRUE;
+    }
+    if (s) {
+      if (detail == NULL)
+	detail = s;
+      else
+	g_free (s);
+      s = NULL;
+    }
+    g_type_class_unref (klass);
+  }
+  if (missing)
+    *missing = detail;
+  return FALSE;
+}
+
+/**
+ * swfdec_video_decoder_new:
+ * @codec: codec id
+ *
+ * Creates a decoder suitable for decoding @format. If no decoder is available
+ * for the given for mat, %NULL is returned.
+ *
+ * Returns: a new decoder or %NULL
+ **/
+SwfdecVideoDecoder *
+swfdec_video_decoder_new (guint codec)
+{
+  SwfdecVideoDecoder *ret = NULL;
+  GSList *walk;
+  
+  for (walk = video_codecs; walk; walk = walk->next) {
+    SwfdecVideoDecoderClass *klass = g_type_class_ref (GPOINTER_TO_SIZE (walk->data));
+    ret = klass->create (codec);
+    g_type_class_unref (klass);
+    if (ret)
+      break;
+  }
+
+  if (ret == NULL) {
+    ret = g_object_new (SWFDEC_TYPE_VIDEO_DECODER, NULL);
+    swfdec_video_decoder_error (ret, "no suitable decoder for video codec %u", codec);
+  }
+
+  ret->codec = codec;
+
+  return ret;
+}
+
+/**
+ * swfdec_video_decoder_decode:
+ * @decoder: a #SwfdecVideoDecoder
+ * @buffer: a #SwfdecBuffer to process
+ *
+ * Hands the decoder a new buffer for decoding. The buffer should be decoded 
+ * immediately. It is assumed that width, height and data areas are set to the 
+ * correct values upon return from this function.
+ **/
+void
+swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer)
+{
+  SwfdecVideoDecoderClass *klass;
+
+  g_return_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder));
+
+  if (decoder->error)
+    return;
+  klass = SWFDEC_VIDEO_DECODER_GET_CLASS (decoder);
+  klass->decode (decoder, buffer);
+}
+
+guint
+swfdec_video_decoder_get_codec (SwfdecVideoDecoder *decoder)
+{
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder), SWFDEC_VIDEO_CODEC_UNDEFINED);
+
+  return decoder->codec;
+}
+
+gboolean
+swfdec_video_decoder_get_error (SwfdecVideoDecoder *decoder)
+{
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder), TRUE);
+
+  return decoder->error;
+}
+
+guint
+swfdec_video_decoder_get_width (SwfdecVideoDecoder *decoder)
+{
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder), 0);
+
+  return decoder->width;
+}
+
+guint
+swfdec_video_decoder_get_height (SwfdecVideoDecoder *decoder)
+{
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder), 0);
+
+  return decoder->height;
+}
+
+#define oil_argb(a,r,g,b) (((a) << 24) | ((r) << 16) | ((g) << 8) | b)
+static gint16 jfif_matrix[24] = {
+  0,      0,      -8192,   -8192,
+  16384,  0,      0,       0,
+  0,      16384,  16384,   16384,
+  0,      0,      -5638,   29032,
+  0,      22970,  -11700,  0,
+  0, 0, 0, 0
+};
+
+static void
+yuv_mux (guint32 *dest, const guint8 *src_y, const guint8 *src_u, const guint8 *src_v,
+    int n)
+{
+  int i;
+  for (i = 0; i < n; i++) {
+    dest[i] = oil_argb(255, src_y[i], src_u[i], src_v[i]);
+  }
+}
+
+static void
+upsample (guint8 *d, guint8 *s, int n)
+{
+  int i;
+
+  d[0] = s[0];
+
+  for (i = 0; i < n-3; i+=2) {
+    d[i + 1] = (3*s[i/2] + s[i/2+1] + 2)>>2;
+    d[i + 2] = (s[i/2] + 3*s[i/2+1] + 2)>>2;
+  }
+
+  if (n&1) {
+    i = n-3;
+    d[n-2] = s[n/2];
+    d[n-1] = s[n/2];
+  } else {
+    d[n-1] = s[n/2-1];
+  }
+}
+
+static guint8 *
+swfdec_video_i420_to_rgb (SwfdecVideoDecoder *decoder)
+{
+  guint32 *tmp;
+  guint8 *tmp_u;
+  guint8 *tmp_v;
+  guint8 *tmp1;
+  guint32 *argb_image;
+  const guint8 *yp, *up, *vp;
+  guint32 *argbp;
+  int j;
+  guint halfwidth;
+  int halfheight;
+
+  halfwidth = (decoder->width + 1)>>1;
+  tmp = g_malloc (4 * decoder->width * decoder->height);
+  tmp_u = g_malloc (decoder->width);
+  tmp_v = g_malloc (decoder->width);
+  tmp1 = g_malloc (halfwidth);
+  argb_image = g_malloc (4 * decoder->width * decoder->height);
+
+  yp = decoder->plane[0];
+  up = decoder->plane[1];
+  vp = decoder->plane[2];
+  argbp = argb_image;
+  halfheight = (decoder->height+1)>>1;
+  for(j=0;(guint)j<decoder->height;j++){
+    guint32 weight = 192 - 128*(j&1);
+
+    oil_merge_linear_u8(tmp1,
+        up + decoder->rowstride[1] * CLAMP((j-1)/2,0,halfheight-1),
+        up + decoder->rowstride[1] * CLAMP((j+1)/2,0,halfheight-1),
+        &weight, halfwidth);
+    upsample (tmp_u, tmp1, decoder->width);
+    oil_merge_linear_u8(tmp1,
+        vp + decoder->rowstride[2] * CLAMP((j-1)/2,0,halfheight-1),
+        vp + decoder->rowstride[2] * CLAMP((j+1)/2,0,halfheight-1),
+        &weight, halfwidth);
+    upsample (tmp_v, tmp1, decoder->width);
+
+    yuv_mux (tmp, yp, tmp_u, tmp_v, decoder->width);
+    oil_colorspace_argb(argbp, tmp, jfif_matrix, decoder->width);
+    yp += decoder->rowstride[0];
+    argbp += decoder->width;
+  }
+  g_free(tmp);
+  g_free(tmp_u);
+  g_free(tmp_v);
+  g_free(tmp1);
+  return (unsigned char *)argb_image;
+}
+
+/* FIXME: use liboil (or better: cairo) for this */
+static void
+swfdec_video_codec_apply_mask (guint8 *data, guint rowstride, const guint8 *mask,
+    guint mask_rowstride, guint width, guint height)
+{
+  const guint8 *in;
+  guint8 *out;
+  guint x, y;
+
+  data += SWFDEC_COLOR_INDEX_ALPHA;
+  for (y = 0; y < height; y++) {
+    in = mask;
+    out = data;
+    for (x = 0; x < width; x++) {
+      *out = *in;
+      out += 4;
+      in++;
+    }
+    mask += mask_rowstride;
+    data += rowstride;
+  }
+}
+
+cairo_surface_t *
+swfdec_video_decoder_get_image (SwfdecVideoDecoder *decoder, SwfdecRenderer *renderer)
+{
+  cairo_surface_t *surface;
+  guint rowstride;
+  guint8 *data;
+
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder), NULL);
+  g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
+
+  if (decoder->error)
+    return NULL;
+
+  if (swfdec_video_codec_get_format (decoder->codec) == SWFDEC_VIDEO_FORMAT_I420) {
+    data = swfdec_video_i420_to_rgb (decoder);
+    if (data == NULL) {
+      SWFDEC_ERROR ("I420 => RGB conversion failed");
+      return NULL;
+    }
+    rowstride = decoder->width * 4;
+  } else {
+    rowstride = decoder->rowstride[0];
+    data = g_memdup (decoder->plane[0], rowstride * decoder->height);
+  }
+  if (decoder->mask) {
+    swfdec_video_codec_apply_mask (data, rowstride, decoder->mask, 
+	decoder->mask_rowstride, decoder->width, decoder->height);
+  }
+  surface = swfdec_renderer_create_for_data (renderer, data,
+      decoder->mask ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+      decoder->width, decoder->height, rowstride);
+  return surface;
+}
+
+void
+swfdec_video_decoder_error (SwfdecVideoDecoder *decoder, const char *error, ...)
+{
+  va_list args;
+
+  g_return_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder));
+  g_return_if_fail (error != NULL);
+
+  va_start (args, error);
+  swfdec_video_decoder_errorv (decoder, error, args);
+  va_end (args);
+}
+
+void
+swfdec_video_decoder_errorv (SwfdecVideoDecoder *decoder, const char *error, va_list args)
+{
+  char *real;
+
+  g_return_if_fail (SWFDEC_IS_VIDEO_DECODER (decoder));
+  g_return_if_fail (error != NULL);
+
+  real = g_strdup_vprintf (error, args);
+  SWFDEC_ERROR ("error decoding video: %s", real);
+  g_free (real);
+  decoder->error = TRUE;
+}
+
diff --git a/swfdec/swfdec_video_decoder.h b/swfdec/swfdec_video_decoder.h
new file mode 100644
index 0000000..2b1b663
--- /dev/null
+++ b/swfdec/swfdec_video_decoder.h
@@ -0,0 +1,113 @@
+/* Swfdec
+ * Copyright (C) 2006-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_VIDEO_DECODER_H_
+#define _SWFDEC_VIDEO_DECODER_H_
+
+#include <swfdec/swfdec_buffer.h>
+#include <swfdec/swfdec_renderer.h>
+
+G_BEGIN_DECLS
+
+
+#define SWFDEC_VIDEO_CODEC_UNDEFINED 0
+#define SWFDEC_VIDEO_CODEC_H263 2
+#define SWFDEC_VIDEO_CODEC_SCREEN 3
+#define SWFDEC_VIDEO_CODEC_VP6 4
+#define SWFDEC_VIDEO_CODEC_VP6_ALPHA 5
+#define SWFDEC_VIDEO_CODEC_SCREEN2 6
+
+typedef enum {
+  SWFDEC_VIDEO_FORMAT_RGBA,
+  SWFDEC_VIDEO_FORMAT_I420
+} SwfdecVideoFormat;
+
+
+typedef struct _SwfdecVideoDecoder SwfdecVideoDecoder;
+typedef struct _SwfdecVideoDecoderClass SwfdecVideoDecoderClass;
+
+#define SWFDEC_TYPE_VIDEO_DECODER                    (swfdec_video_decoder_get_type())
+#define SWFDEC_IS_VIDEO_DECODER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_DECODER))
+#define SWFDEC_IS_VIDEO_DECODER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_VIDEO_DECODER))
+#define SWFDEC_VIDEO_DECODER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_DECODER, SwfdecVideoDecoder))
+#define SWFDEC_VIDEO_DECODER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_DECODER, SwfdecVideoDecoderClass))
+#define SWFDEC_VIDEO_DECODER_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_VIDEO_DECODER, SwfdecVideoDecoderClass))
+
+struct _SwfdecVideoDecoder
+{
+  GObject		object;
+
+  /*< protected >*/
+  guint			width;		/* width of the current image */
+  guint			height;		/* height of the current image */
+  guint8 *		plane[3];	/* planes of the image, not all might be used */
+  guint		  	rowstride[3];	/* rowstrides of the planes */
+  guint8 *		mask;		/* A8 mask or NULL if none */
+  guint			mask_rowstride;	/* rowstride of mask plane */
+  /*< private >*/
+  guint			codec;		/* codec this decoder uses */
+  gboolean		error;		/* if this codec is in an error state */
+};
+
+struct _SwfdecVideoDecoderClass
+{
+  /*< private >*/
+  GObjectClass		object_class;
+
+  /*< public >*/
+  gboolean		(* prepare)	(guint                  codec,
+					 char **                missing);
+  SwfdecVideoDecoder *	(* create)	(guint                  codec);
+
+  void			(* decode)	(SwfdecVideoDecoder *	decoder,
+					 SwfdecBuffer *		buffer);
+};
+
+SwfdecVideoFormat     	swfdec_video_codec_get_format	(guint			codec);
+
+GType			swfdec_video_decoder_get_type	(void);
+
+void			swfdec_video_decoder_register	(GType			type);
+
+gboolean		swfdec_video_decoder_prepare	(guint			codec,
+							 char **		missing);
+
+SwfdecVideoDecoder *   	swfdec_video_decoder_new      	(guint			codec);
+
+void			swfdec_video_decoder_decode	(SwfdecVideoDecoder *	decoder,
+							 SwfdecBuffer *		buffer);
+guint			swfdec_video_decoder_get_codec	(SwfdecVideoDecoder *	decoder);
+guint			swfdec_video_decoder_get_width	(SwfdecVideoDecoder *	decoder);
+guint			swfdec_video_decoder_get_height	(SwfdecVideoDecoder *	decoder);
+cairo_surface_t *	swfdec_video_decoder_get_image	(SwfdecVideoDecoder *	decoder,
+							 SwfdecRenderer *	renderer);
+gboolean		swfdec_video_decoder_get_error	(SwfdecVideoDecoder *   decoder);
+
+/* for subclasses */
+void			swfdec_video_decoder_error	(SwfdecVideoDecoder *	decoder,
+							 const char *		error,
+							 ...) G_GNUC_PRINTF (2, 3);
+void			swfdec_video_decoder_errorv	(SwfdecVideoDecoder *	decoder,
+							 const char *		error,
+							 va_list		args);
+
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_video_decoder_gst.c b/swfdec/swfdec_video_decoder_gst.c
new file mode 100644
index 0000000..8122bc6
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_gst.c
@@ -0,0 +1,211 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <gst/pbutils/pbutils.h>
+
+#include "swfdec_video_decoder_gst.h"
+#include "swfdec_codec_gst.h"
+#include "swfdec_debug.h"
+
+static GstCaps *
+swfdec_video_decoder_get_caps (guint codec)
+{
+  GstCaps *caps;
+
+  switch (codec) {
+    case SWFDEC_VIDEO_CODEC_H263:
+      caps = gst_caps_from_string ("video/x-flash-video");
+      break;
+    case SWFDEC_VIDEO_CODEC_VP6:
+      caps = gst_caps_from_string ("video/x-vp6-flash");
+      break;
+    default:
+      return NULL;
+  }
+  g_assert (caps);
+  return caps;
+}
+
+static GstCaps *
+swfdec_video_decoder_get_sink_caps (guint codec)
+{
+  switch (swfdec_video_codec_get_format (codec)) {
+    case SWFDEC_VIDEO_FORMAT_RGBA:
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+      return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, "
+	  "red_mask=16711680, green_mask=65280, blue_mask=255");
+#else
+      return gst_caps_from_string ("video/x-raw-rgb, bpp=32, endianness=4321, depth=24, "
+	  "red_mask=65280, green_mask=16711680, blue_mask=-16777216");
+#endif
+    case SWFDEC_VIDEO_FORMAT_I420:
+      return gst_caps_from_string ("video/x-raw-yuv, format=(fourcc)I420");
+    default:
+      g_return_val_if_reached (NULL);
+  }
+}
+
+G_DEFINE_TYPE (SwfdecVideoDecoderGst, swfdec_video_decoder_gst, SWFDEC_TYPE_VIDEO_DECODER)
+
+static gboolean
+swfdec_video_decoder_gst_prepare (guint codec, char **missing)
+{
+  GstElementFactory *factory;
+  GstCaps *caps;
+
+  /* Check if we can handle the format at all. If not, no plugin will help us. */
+  caps = swfdec_video_decoder_get_caps (codec);
+  if (caps == NULL)
+    return FALSE;
+
+  /* If we can already handle it, woohoo! */
+  factory = swfdec_gst_get_element_factory (caps);
+  if (factory != NULL) {
+    gst_object_unref (factory);
+    gst_caps_unref (caps);
+    return TRUE;
+  }
+
+  /* need to install plugins... */
+  *missing = gst_missing_decoder_installer_detail_new (caps);
+  gst_caps_unref (caps);
+  return FALSE;
+}
+
+static SwfdecVideoDecoder *
+swfdec_video_decoder_gst_create (guint codec)
+{
+  SwfdecVideoDecoderGst *player;
+  GstCaps *srccaps, *sinkcaps;
+
+  srccaps = swfdec_video_decoder_get_caps (codec);
+  if (srccaps == NULL)
+    return NULL;
+  sinkcaps = swfdec_video_decoder_get_sink_caps (codec);
+
+  player = g_object_new (SWFDEC_TYPE_VIDEO_DECODER_GST, NULL);
+
+  if (!swfdec_gst_decoder_init (&player->dec, srccaps, sinkcaps, NULL)) {
+    g_object_unref (player);
+    gst_caps_unref (srccaps);
+    gst_caps_unref (sinkcaps);
+    return NULL;
+  }
+
+  gst_caps_unref (srccaps);
+  gst_caps_unref (sinkcaps);
+  return &player->decoder;
+}
+
+static void
+swfdec_video_decoder_gst_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer)
+{
+  SwfdecVideoDecoderGst *player = SWFDEC_VIDEO_DECODER_GST (dec);
+#define SWFDEC_ALIGN(x, n) (((x) + (n) - 1) & (~((n) - 1)))
+  GstBuffer *buf;
+  GstCaps *caps;
+  GstStructure *structure;
+
+  if (player->last) {
+    gst_buffer_unref (player->last);
+    player->last = NULL;
+  }
+
+  buf = swfdec_gst_buffer_new (swfdec_buffer_ref (buffer));
+  if (!swfdec_gst_decoder_push (&player->dec, buf)) {
+    swfdec_video_decoder_error (dec, "failed to push buffer");
+    return;
+  }
+
+  player->last = swfdec_gst_decoder_pull (&player->dec);
+  if (player->last == NULL) {
+    swfdec_video_decoder_error (dec, "failed to pull decoded buffer");
+    return;
+  }
+  while ((buf = swfdec_gst_decoder_pull (&player->dec))) {
+    SWFDEC_ERROR ("too many output buffers!");
+    gst_buffer_unref (buf);
+  }
+  caps = gst_buffer_get_caps (player->last);
+  if (caps == NULL) {
+    swfdec_video_decoder_error (dec, "no caps on decoded buffer");
+    return;
+  }
+  structure = gst_caps_get_structure (caps, 0);
+  if (!gst_structure_get_int (structure, "width", (int *) &dec->width) ||
+      !gst_structure_get_int (structure, "height", (int *) &dec->height)) {
+    swfdec_video_decoder_error (dec, "invalid caps on decoded buffer");
+    return;
+  }
+  buf = player->last;
+  switch (swfdec_video_codec_get_format (dec->codec)) {
+    case SWFDEC_VIDEO_FORMAT_RGBA:
+      dec->plane[0] = buf->data;
+      dec->rowstride[0] = dec->width * 4;
+      break;
+    case SWFDEC_VIDEO_FORMAT_I420:
+      dec->plane[0] = buf->data;
+      dec->rowstride[0] = SWFDEC_ALIGN (dec->width, 4);
+      dec->plane[1] = dec->plane[0] + dec->rowstride[0] * SWFDEC_ALIGN (dec->height, 2);
+      dec->rowstride[1] = SWFDEC_ALIGN (dec->width, 8) / 2;
+      dec->plane[2] = dec->plane[1] + dec->rowstride[1] * SWFDEC_ALIGN (dec->height, 2) / 2;
+      dec->rowstride[2] = dec->rowstride[1];
+      g_assert (dec->plane[2] + dec->rowstride[2] * SWFDEC_ALIGN (dec->height, 2) / 2 == dec->plane[0] + buf->size);
+      break;
+    default:
+      g_return_if_reached ();
+  }
+#undef SWFDEC_ALIGN
+}
+
+static void
+swfdec_video_decoder_gst_dispose (GObject *object)
+{
+  SwfdecVideoDecoderGst *player = SWFDEC_VIDEO_DECODER_GST (object);
+
+  swfdec_gst_decoder_finish (&player->dec);
+  if (player->last)
+    gst_buffer_unref (player->last);
+
+  G_OBJECT_CLASS (swfdec_video_decoder_gst_parent_class)->dispose (object);
+}
+
+static void
+swfdec_video_decoder_gst_class_init (SwfdecVideoDecoderGstClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  SwfdecVideoDecoderClass *decoder_class = SWFDEC_VIDEO_DECODER_CLASS (klass);
+
+  object_class->dispose = swfdec_video_decoder_gst_dispose;
+
+  decoder_class->prepare = swfdec_video_decoder_gst_prepare;
+  decoder_class->create = swfdec_video_decoder_gst_create;
+  decoder_class->decode = swfdec_video_decoder_gst_decode;
+}
+
+static void
+swfdec_video_decoder_gst_init (SwfdecVideoDecoderGst *dec)
+{
+}
+
diff --git a/swfdec/swfdec_video_decoder_gst.h b/swfdec/swfdec_video_decoder_gst.h
new file mode 100644
index 0000000..4fcc151
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_gst.h
@@ -0,0 +1,56 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_VIDEO_DECODER_GST_H_
+#define _SWFDEC_VIDEO_DECODER_GST_H_
+
+#include <swfdec/swfdec_codec_gst.h>
+#include <swfdec/swfdec_video_decoder.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _SwfdecVideoDecoderGst SwfdecVideoDecoderGst;
+typedef struct _SwfdecVideoDecoderGstClass SwfdecVideoDecoderGstClass;
+
+#define SWFDEC_TYPE_VIDEO_DECODER_GST                    (swfdec_video_decoder_gst_get_type())
+#define SWFDEC_IS_VIDEO_DECODER_GST(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_DECODER_GST))
+#define SWFDEC_IS_VIDEO_DECODER_GST_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_VIDEO_DECODER_GST))
+#define SWFDEC_VIDEO_DECODER_GST(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_DECODER_GST, SwfdecVideoDecoderGst))
+#define SWFDEC_VIDEO_DECODER_GST_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_DECODER_GST, SwfdecVideoDecoderGstClass))
+#define SWFDEC_VIDEO_DECODER_GST_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_VIDEO_DECODER_GST, SwfdecVideoDecoderGstClass))
+
+struct _SwfdecVideoDecoderGst
+{
+  SwfdecVideoDecoder		decoder;
+
+  SwfdecGstDecoder		dec;		/* the decoder element */
+  GstBuffer *			last;		/* last decoded buffer */
+};
+
+struct _SwfdecVideoDecoderGstClass
+{
+  SwfdecVideoDecoderClass	decoder_class;
+};
+
+GType			swfdec_video_decoder_gst_get_type	(void);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_video_decoder_screen.c b/swfdec/swfdec_video_decoder_screen.c
new file mode 100644
index 0000000..8a7b91d
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_screen.c
@@ -0,0 +1,138 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "swfdec_video_decoder_screen.h"
+#include "swfdec_bits.h"
+#include "swfdec_debug.h"
+
+G_DEFINE_TYPE (SwfdecVideoDecoderScreen, swfdec_video_decoder_screen, SWFDEC_TYPE_VIDEO_DECODER)
+
+static gboolean
+swfdec_video_decoder_screen_prepare (guint codec, char **missing)
+{
+  return codec == SWFDEC_VIDEO_CODEC_SCREEN;
+}
+
+static SwfdecVideoDecoder *
+swfdec_video_decoder_screen_create (guint codec)
+{
+  if (codec != SWFDEC_VIDEO_CODEC_SCREEN)
+    return NULL;
+
+  return g_object_new (SWFDEC_TYPE_VIDEO_DECODER_SCREEN, NULL);
+}
+
+static void
+swfdec_video_decoder_screen_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer)
+{
+  SwfdecBits bits;
+  guint i, j, w, h, bw, bh, stride;
+
+  swfdec_bits_init (&bits, buffer);
+  bw = (swfdec_bits_getbits (&bits, 4) + 1) * 16;
+  w = swfdec_bits_getbits (&bits, 12);
+  bh = (swfdec_bits_getbits (&bits, 4) + 1) * 16;
+  h = swfdec_bits_getbits (&bits, 12);
+  if (dec->width == 0 || dec->height == 0) {
+    if (w == 0 || h == 0) {
+      swfdec_video_decoder_error (dec, "width or height is 0: %ux%u", w, h);
+      return;
+    }
+    /* check for overflow */
+    if (w * 4 * h < w * 4) {
+      swfdec_video_decoder_error (dec, "overflowing video size: %ux%u", w, h);
+      return;
+    }
+    dec->plane[0] = g_try_malloc (w * h * 4);
+    if (dec->plane[0] == NULL) {
+      swfdec_video_decoder_error (dec, "could not allocate %u bytes", w * h * 4);
+      return;
+    }
+    dec->width = w;
+    dec->height = h;
+    dec->rowstride[0] = w * 4;
+  } else if (dec->width != w || dec->height != h) {
+    swfdec_video_decoder_error (dec, "width or height differ from original: was %ux%u, is %ux%u",
+	dec->width, dec->height, w, h);
+    /* FIXME: this is what ffmpeg does, should we be more forgiving? */
+    return;
+  }
+  stride = w * 4;
+  SWFDEC_LOG ("size: %u x %u - block size %u x %u\n", w, h, bw, bh);
+  for (j = 0; j < h; j += bh) {
+    for (i = 0; i < w; i += bw) {
+      guint x, y, size;
+      SwfdecBuffer *buf;
+      guint8 *in, *out;
+      size = swfdec_bits_get_bu16 (&bits);
+      if (size == 0)
+	continue;
+      buf = swfdec_bits_decompress (&bits, size, bw * bh * 4);
+      if (buf == NULL) {
+	SWFDEC_ERROR ("could not decode block at %ux%u", i, j);
+	continue;
+      }
+      /* convert format and write out data */
+      out = dec->plane[0] + stride * (h - j - 1) + i * 4;
+      in = buf->data;
+      for (y = 0; y < MIN (bh, h - j); y++) {
+	for (x = 0; x < MIN (bw, w - i); x++) {
+	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_BLUE] = *in++;
+	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_GREEN] = *in++;
+	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_RED] = *in++;
+	  out[x * 4 - y * stride + SWFDEC_COLOR_INDEX_ALPHA] = 0xFF;
+	}
+      }
+      swfdec_buffer_unref (buf);
+    }
+  }
+}
+
+static void
+swfdec_video_decoder_screen_dispose (GObject *object)
+{
+  SwfdecVideoDecoder *dec = SWFDEC_VIDEO_DECODER (object);
+
+  g_free (dec->plane[0]);
+
+  G_OBJECT_CLASS (swfdec_video_decoder_screen_parent_class)->dispose (object);
+}
+
+static void
+swfdec_video_decoder_screen_class_init (SwfdecVideoDecoderScreenClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  SwfdecVideoDecoderClass *decoder_class = SWFDEC_VIDEO_DECODER_CLASS (klass);
+
+  object_class->dispose = swfdec_video_decoder_screen_dispose;
+
+  decoder_class->prepare = swfdec_video_decoder_screen_prepare;
+  decoder_class->create = swfdec_video_decoder_screen_create;
+  decoder_class->decode = swfdec_video_decoder_screen_decode;
+}
+
+static void
+swfdec_video_decoder_screen_init (SwfdecVideoDecoderScreen *dec)
+{
+}
+
diff --git a/swfdec/swfdec_video_decoder_screen.h b/swfdec/swfdec_video_decoder_screen.h
new file mode 100644
index 0000000..2c84515
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_screen.h
@@ -0,0 +1,52 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_VIDEO_DECODER_SCREEN_H_
+#define _SWFDEC_VIDEO_DECODER_SCREEN_H_
+
+#include <swfdec/swfdec_video_decoder.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _SwfdecVideoDecoderScreen SwfdecVideoDecoderScreen;
+typedef struct _SwfdecVideoDecoderScreenClass SwfdecVideoDecoderScreenClass;
+
+#define SWFDEC_TYPE_VIDEO_DECODER_SCREEN                    (swfdec_video_decoder_screen_get_type())
+#define SWFDEC_IS_VIDEO_DECODER_SCREEN(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_DECODER_SCREEN))
+#define SWFDEC_IS_VIDEO_DECODER_SCREEN_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_VIDEO_DECODER_SCREEN))
+#define SWFDEC_VIDEO_DECODER_SCREEN(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_DECODER_SCREEN, SwfdecVideoDecoderScreen))
+#define SWFDEC_VIDEO_DECODER_SCREEN_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_DECODER_SCREEN, SwfdecVideoDecoderScreenClass))
+#define SWFDEC_VIDEO_DECODER_SCREEN_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_VIDEO_DECODER_SCREEN, SwfdecVideoDecoderScreenClass))
+
+struct _SwfdecVideoDecoderScreen
+{
+  SwfdecVideoDecoder		decoder;
+};
+
+struct _SwfdecVideoDecoderScreenClass
+{
+  SwfdecVideoDecoderClass	decoder_class;
+};
+
+GType			swfdec_video_decoder_screen_get_type	(void);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_video_decoder_vp6_alpha.c b/swfdec/swfdec_video_decoder_vp6_alpha.c
new file mode 100644
index 0000000..54b50bc
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_vp6_alpha.c
@@ -0,0 +1,138 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <liboil/liboil.h>
+#include "swfdec_video_decoder_vp6_alpha.h"
+#include "swfdec_bits.h"
+#include "swfdec_debug.h"
+
+G_DEFINE_TYPE (SwfdecVideoDecoderVp6Alpha, swfdec_video_decoder_vp6_alpha, SWFDEC_TYPE_VIDEO_DECODER)
+
+static gboolean
+swfdec_video_decoder_vp6_alpha_prepare (guint codec, char **missing)
+{
+  if (codec != SWFDEC_VIDEO_CODEC_VP6_ALPHA)
+    return FALSE;
+
+  return swfdec_video_decoder_prepare (SWFDEC_VIDEO_CODEC_VP6, missing);
+}
+
+static SwfdecVideoDecoder *
+swfdec_video_decoder_vp6_alpha_create (guint codec)
+{
+  if (codec != SWFDEC_VIDEO_CODEC_VP6_ALPHA)
+    return NULL;
+
+  return g_object_new (SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA, NULL);
+}
+
+static void
+swfdec_video_decoder_vp6_alpha_decode (SwfdecVideoDecoder *dec, SwfdecBuffer *buffer)
+{
+  SwfdecVideoDecoderVp6Alpha *vp6 = SWFDEC_VIDEO_DECODER_VP6_ALPHA (dec);
+  SwfdecBuffer *tmp;
+  SwfdecBits bits;
+  guint size;
+
+  swfdec_bits_init (&bits, buffer);
+  size = swfdec_bits_get_bu24 (&bits);
+  tmp = swfdec_bits_get_buffer (&bits, size);
+  if (tmp == NULL) {
+    swfdec_video_decoder_error (dec, "invalid buffer size for image buffer");
+    return;
+  }
+  swfdec_video_decoder_decode (vp6->image, tmp);
+  swfdec_buffer_unref (tmp);
+  tmp = swfdec_bits_get_buffer (&bits, -1);
+  if (tmp == NULL) {
+    swfdec_video_decoder_error (dec, "invalid buffer size for mask buffer");
+    return;
+  }
+  swfdec_video_decoder_decode (vp6->mask, tmp);
+  swfdec_buffer_unref (tmp);
+  if (swfdec_video_decoder_get_error (vp6->image) ||
+      swfdec_video_decoder_get_error (vp6->mask)) {
+    swfdec_video_decoder_error (dec, "decoding of VP6 data failed");
+    return;
+  }
+
+  if (swfdec_video_decoder_get_width (vp6->image) != swfdec_video_decoder_get_width (vp6->mask) ||
+      swfdec_video_decoder_get_height (vp6->image) != swfdec_video_decoder_get_height (vp6->mask)) {
+    SWFDEC_ERROR ("size of mask %ux%u doesn't match image size %ux%u",
+	swfdec_video_decoder_get_width (vp6->mask), swfdec_video_decoder_get_height (vp6->mask),
+	swfdec_video_decoder_get_width (vp6->image), swfdec_video_decoder_get_height (vp6->image));
+    return;
+  }
+  dec->plane[0] = vp6->image->plane[0];
+  dec->plane[1] = vp6->image->plane[1];
+  dec->plane[2] = vp6->image->plane[2];
+  dec->rowstride[0] = vp6->image->rowstride[0];
+  dec->rowstride[1] = vp6->image->rowstride[1];
+  dec->rowstride[2] = vp6->image->rowstride[2];
+  dec->mask = vp6->mask->plane[0];
+  dec->mask_rowstride = vp6->mask->rowstride[0];
+}
+
+static void
+swfdec_video_decoder_vp6_alpha_dispose (GObject *object)
+{
+  SwfdecVideoDecoderVp6Alpha *vp6 = SWFDEC_VIDEO_DECODER_VP6_ALPHA (object);
+
+  if (vp6->image) {
+    g_object_unref (vp6->image);
+    vp6->image = NULL;
+  }
+  if (vp6->mask) {
+    g_object_unref (vp6->mask);
+    vp6->mask = NULL;
+  }
+
+  G_OBJECT_CLASS (swfdec_video_decoder_vp6_alpha_parent_class)->dispose (object);
+}
+
+static void
+swfdec_video_decoder_vp6_alpha_class_init (SwfdecVideoDecoderVp6AlphaClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  SwfdecVideoDecoderClass *decoder_class = SWFDEC_VIDEO_DECODER_CLASS (klass);
+
+  object_class->dispose = swfdec_video_decoder_vp6_alpha_dispose;
+
+  decoder_class->prepare = swfdec_video_decoder_vp6_alpha_prepare;
+  decoder_class->create = swfdec_video_decoder_vp6_alpha_create;
+  decoder_class->decode = swfdec_video_decoder_vp6_alpha_decode;
+}
+
+static void
+swfdec_video_decoder_vp6_alpha_init (SwfdecVideoDecoderVp6Alpha *vp6)
+{
+  vp6->image = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6);
+  vp6->mask = swfdec_video_decoder_new (SWFDEC_VIDEO_CODEC_VP6);
+
+  if (swfdec_video_decoder_get_error (vp6->image) ||
+      swfdec_video_decoder_get_error (vp6->mask)) {
+    swfdec_video_decoder_error (SWFDEC_VIDEO_DECODER (vp6),
+	"error initializing children VP6 decoders");
+  }
+}
+
diff --git a/swfdec/swfdec_video_decoder_vp6_alpha.h b/swfdec/swfdec_video_decoder_vp6_alpha.h
new file mode 100644
index 0000000..2610c24
--- /dev/null
+++ b/swfdec/swfdec_video_decoder_vp6_alpha.h
@@ -0,0 +1,55 @@
+/* Swfdec
+ * Copyright (C) 2007-2008 Benjamin Otte <otte at gnome.org>
+ *
+ * This library 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.
+ * 
+ * This library 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 this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
+#ifndef _SWFDEC_VIDEO_DECODER_VP6_ALPHA_H_
+#define _SWFDEC_VIDEO_DECODER_VP6_ALPHA_H_
+
+#include <swfdec/swfdec_video_decoder.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _SwfdecVideoDecoderVp6Alpha SwfdecVideoDecoderVp6Alpha;
+typedef struct _SwfdecVideoDecoderVp6AlphaClass SwfdecVideoDecoderVp6AlphaClass;
+
+#define SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA                    (swfdec_video_decoder_vp6_alpha_get_type())
+#define SWFDEC_IS_VIDEO_DECODER_VP6_ALPHA(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA))
+#define SWFDEC_IS_VIDEO_DECODER_VP6_ALPHA_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA))
+#define SWFDEC_VIDEO_DECODER_VP6_ALPHA(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA, SwfdecVideoDecoderVp6Alpha))
+#define SWFDEC_VIDEO_DECODER_VP6_ALPHA_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA, SwfdecVideoDecoderVp6AlphaClass))
+#define SWFDEC_VIDEO_DECODER_VP6_ALPHA_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_VIDEO_DECODER_VP6_ALPHA, SwfdecVideoDecoderVp6AlphaClass))
+
+struct _SwfdecVideoDecoderVp6Alpha
+{
+  SwfdecVideoDecoder		decoder;
+
+  SwfdecVideoDecoder *		image;
+  SwfdecVideoDecoder *		mask;
+};
+
+struct _SwfdecVideoDecoderVp6AlphaClass
+{
+  SwfdecVideoDecoderClass	decoder_class;
+};
+
+GType			swfdec_video_decoder_vp6_alpha_get_type	(void);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_video_movie.c b/swfdec/swfdec_video_movie.c
index 2b81519..c846058 100644
--- a/swfdec/swfdec_video_movie.c
+++ b/swfdec/swfdec_video_movie.c
@@ -108,41 +108,26 @@ swfdec_video_movie_get_variable (SwfdecAsObject *object, SwfdecAsObject *orig,
 {
   guint version = object->context->version;
   SwfdecVideoMovie *video;
-  guint w, h;
 
   video = SWFDEC_VIDEO_MOVIE (object);
 
   if (swfdec_strcmp (version, variable, SWFDEC_AS_STR_width) == 0) {
-    /* FIXME: find better solution here */
-    cairo_surface_t *surface;
+    guint w;
     if (video->provider) {
-      surface = swfdec_video_provider_get_image (video->provider,
-	  SWFDEC_PLAYER (object->context)->priv->renderer, &w, &h);
-    } else {
-      surface = NULL;
-    }
-    if (surface) {
-      cairo_surface_destroy (surface);
+      w = swfdec_video_provider_get_width (video->provider);
     } else {
       w = 0;
     }
-    SWFDEC_AS_VALUE_SET_NUMBER (val, w);
+    SWFDEC_AS_VALUE_SET_INT (val, w);
     return TRUE;
   } else if (swfdec_strcmp (version, variable, SWFDEC_AS_STR_height) == 0) {
-    /* FIXME: find better solution here */
-    cairo_surface_t *surface;
+    guint h;
     if (video->provider) {
-      surface = swfdec_video_provider_get_image (video->provider,
-	  SWFDEC_PLAYER (object->context)->priv->renderer, &w, &h);
-    } else {
-      surface = NULL;
-    }
-    if (surface) {
-      cairo_surface_destroy (surface);
+      h = swfdec_video_provider_get_height (video->provider);
     } else {
       h = 0;
     }
-    SWFDEC_AS_VALUE_SET_NUMBER (val, h);
+    SWFDEC_AS_VALUE_SET_INT (val, h);
     return TRUE;
   } else if (swfdec_strcmp (version, variable, SWFDEC_AS_STR_deblocking) == 0) {
     SWFDEC_STUB ("Video.deblocking (get)");
diff --git a/swfdec/swfdec_video_provider.c b/swfdec/swfdec_video_provider.c
index 4006648..e5c7e0e 100644
--- a/swfdec/swfdec_video_provider.c
+++ b/swfdec/swfdec_video_provider.c
@@ -89,6 +89,34 @@ swfdec_video_provider_get_image (SwfdecVideoProvider *provider,
   return iface->get_image (provider, renderer, width, height);
 }
 
+guint
+swfdec_video_provider_get_width (SwfdecVideoProvider *provider)
+{
+  SwfdecVideoProviderInterface *iface;
+  guint w, h;
+  
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_PROVIDER (provider), 0);
+
+  iface = SWFDEC_VIDEO_PROVIDER_GET_INTERFACE (provider);
+  g_assert (iface->get_size != NULL);
+  iface->get_size (provider, &w, &h);
+  return w;
+}
+
+guint
+swfdec_video_provider_get_height (SwfdecVideoProvider *provider)
+{
+  SwfdecVideoProviderInterface *iface;
+  guint w, h;
+  
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_PROVIDER (provider), 0);
+
+  iface = SWFDEC_VIDEO_PROVIDER_GET_INTERFACE (provider);
+  g_assert (iface->get_size != NULL);
+  iface->get_size (provider, &w, &h);
+  return h;
+}
+
 void
 swfdec_video_provider_set_ratio (SwfdecVideoProvider *provider, guint ratio)
 {
diff --git a/swfdec/swfdec_video_provider.h b/swfdec/swfdec_video_provider.h
index b59f437..b939fee 100644
--- a/swfdec/swfdec_video_provider.h
+++ b/swfdec/swfdec_video_provider.h
@@ -44,6 +44,10 @@ struct _SwfdecVideoProviderInterface {
 								 SwfdecRenderer *	renderer,
 								 guint *		width,
 								 guint *		height);
+  /* get size of current image */
+  void			(* get_size)				(SwfdecVideoProvider *	provider,
+								 guint *		width,
+								 guint *		height);
 };
 
 GType			swfdec_video_provider_get_type		(void) G_GNUC_CONST;
@@ -54,6 +58,8 @@ cairo_surface_t *     	swfdec_video_provider_get_image		(SwfdecVideoProvider *	p
 								 guint *		height);
 void			swfdec_video_provider_set_ratio		(SwfdecVideoProvider *	provider,
 								 guint			ratio);
+guint			swfdec_video_provider_get_width		(SwfdecVideoProvider *	provider);
+guint			swfdec_video_provider_get_height	(SwfdecVideoProvider *	provider);
 
 /* for subclasses */
 void			swfdec_video_provider_new_image		(SwfdecVideoProvider *	provider);
diff --git a/swfdec/swfdec_video_video_provider.c b/swfdec/swfdec_video_video_provider.c
index 0b785c2..74d6bd3 100644
--- a/swfdec/swfdec_video_video_provider.c
+++ b/swfdec/swfdec_video_video_provider.c
@@ -97,7 +97,7 @@ swfdec_video_video_provider_get_image (SwfdecVideoProvider *prov,
 
   if (provider->decoder == NULL || provider->current_frame < provider->decoder_frame) {
     if (provider->decoder != NULL) {
-      swfdec_video_decoder_free (provider->decoder);
+      g_object_unref (provider->decoder);
     }
     provider->decoder = swfdec_video_decoder_new (provider->video->format);
     if (provider->decoder == NULL)
@@ -112,17 +112,18 @@ swfdec_video_video_provider_get_image (SwfdecVideoProvider *prov,
 
   for (;;) {
     g_assert (frame->buffer);
-    surface = swfdec_video_decoder_decode (provider->decoder, renderer, 
-	frame->buffer, &w, &h);
+    swfdec_video_decoder_decode (provider->decoder, frame->buffer);
     provider->decoder_frame = provider->current_frame;
-    if (surface == NULL)
-      return NULL;
     if (frame->frame == provider->current_frame)
       break;
     frame++;
-    cairo_surface_destroy (surface);
   };
 
+  surface = swfdec_video_decoder_get_image (provider->decoder, renderer);
+  if (surface == NULL)
+    return NULL;
+  w = swfdec_video_decoder_get_width (provider->decoder);
+  h = swfdec_video_decoder_get_height (provider->decoder);
   cached = swfdec_cached_video_new (surface, w * h * 4);
   swfdec_cached_video_set_frame (cached, provider->current_frame);
   swfdec_cached_video_set_size (cached, w, h);
@@ -136,9 +137,24 @@ swfdec_video_video_provider_get_image (SwfdecVideoProvider *prov,
 }
 
 static void
+swfdec_video_video_provider_get_size (SwfdecVideoProvider *prov, guint *width, guint *height)
+{
+  SwfdecVideoVideoProvider *provider = SWFDEC_VIDEO_VIDEO_PROVIDER (prov);
+
+  if (provider->decoder) {
+    *width = swfdec_video_decoder_get_width (provider->decoder);
+    *height = swfdec_video_decoder_get_height (provider->decoder);
+  } else {
+    *width = 0;
+    *height = 0;
+  }
+}
+
+static void
 swfdec_video_video_provider_video_provider_init (SwfdecVideoProviderInterface *iface)
 {
   iface->get_image = swfdec_video_video_provider_get_image;
+  iface->get_size = swfdec_video_video_provider_get_size;
   iface->set_ratio = swfdec_video_video_provider_set_ratio;
 }
 
@@ -153,7 +169,7 @@ swfdec_video_video_provider_dispose (GObject *object)
   SwfdecVideoVideoProvider * provider = SWFDEC_VIDEO_VIDEO_PROVIDER (object);
 
   if (provider->decoder != NULL) {
-    swfdec_video_decoder_free (provider->decoder);
+    g_object_unref (provider->decoder);
     provider->decoder = NULL;
   }
   g_object_unref (provider->video);
diff --git a/swfdec/swfdec_video_video_provider.h b/swfdec/swfdec_video_video_provider.h
index d4804ba..cd9b153 100644
--- a/swfdec/swfdec_video_video_provider.h
+++ b/swfdec/swfdec_video_video_provider.h
@@ -21,8 +21,8 @@
 #define _SWFDEC_VIDEO_VIDEO_PROVIDER_H_
 
 #include <swfdec/swfdec_graphic.h>
-#include <swfdec/swfdec_codec_video.h>
 #include <swfdec/swfdec_video.h>
+#include <swfdec/swfdec_video_decoder.h>
 #include <swfdec/swfdec_video_provider.h>
 
 G_BEGIN_DECLS
commit 49dc3363ac4329f6b4190185b940f828a504a8ae
Author: Benjamin Otte <otte at gnome.org>
Date:   Wed Jun 25 09:57:46 2008 +0200

    fix memleak

diff --git a/swfdec/swfdec_audio_decoder_gst.c b/swfdec/swfdec_audio_decoder_gst.c
index ea343fa..9b29957 100644
--- a/swfdec/swfdec_audio_decoder_gst.c
+++ b/swfdec/swfdec_audio_decoder_gst.c
@@ -74,6 +74,7 @@ swfdec_audio_decoder_gst_prepare (guint codec, SwfdecAudioFormat format, char **
   factory = swfdec_gst_get_element_factory (caps);
   if (factory != NULL) {
     gst_object_unref (factory);
+    gst_caps_unref (caps);
     return TRUE;
   }
 


More information about the Swfdec-commits mailing list