[Swfdec-commits] 7 commits - configure.ac swfdec-gtk/swfdec_playback_oss.c swfdec/Makefile.am swfdec/swfdec_cached_video.c swfdec/swfdec_cached_video.h swfdec/swfdec_codec_video.c swfdec/swfdec_codec_video.h swfdec/swfdec_image.c swfdec/swfdec_movie.c swfdec/swfdec_net_stream.c swfdec/swfdec_net_stream.h swfdec/swfdec_renderer.c swfdec/swfdec_renderer_internal.h swfdec/swfdec_video.c swfdec/swfdec_video.h swfdec/swfdec_video_movie_as.c swfdec/swfdec_video_movie.c swfdec/swfdec_video_movie.h 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
Fri Apr 11 15:40:23 PDT 2008


 configure.ac                         |   10 -
 swfdec-gtk/swfdec_playback_oss.c     |  266 -----------------------------------
 swfdec/Makefile.am                   |    8 -
 swfdec/swfdec_cached_video.c         |  113 ++++++++++++++
 swfdec/swfdec_cached_video.h         |   73 +++++++++
 swfdec/swfdec_codec_video.c          |   28 +--
 swfdec/swfdec_codec_video.h          |    8 -
 swfdec/swfdec_image.c                |    6 
 swfdec/swfdec_movie.c                |    2 
 swfdec/swfdec_net_stream.c           |  190 ++++++++++++++++---------
 swfdec/swfdec_net_stream.h           |    2 
 swfdec/swfdec_renderer.c             |   14 +
 swfdec/swfdec_renderer_internal.h    |    1 
 swfdec/swfdec_video.c                |  171 ++--------------------
 swfdec/swfdec_video.h                |   10 +
 swfdec/swfdec_video_movie.c          |  134 +++++++----------
 swfdec/swfdec_video_movie.h          |   27 ---
 swfdec/swfdec_video_movie_as.c       |   10 -
 swfdec/swfdec_video_provider.c       |  110 ++++++++++++++
 swfdec/swfdec_video_provider.h       |   64 ++++++++
 swfdec/swfdec_video_video_provider.c |  189 ++++++++++++++++++++++++
 swfdec/swfdec_video_video_provider.h |   58 +++++++
 22 files changed, 877 insertions(+), 617 deletions(-)

New commits:
commit 65560d0fe29124f15debfa545bcfeac5e67e1db4
Merge: 7863060... 2396fe8...
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:40:16 2008 +0200

    Merge branch 'master' of ssh://company@git.freedesktop.org/git/swfdec/swfdec

commit 7863060a4d1d320b782ad63127a8c36f24f3122a
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:34:54 2008 +0200

    remove OSS audio backend
    
    Eric says it's dead and he wass the only one ever hacking on it.

diff --git a/configure.ac b/configure.ac
index c684739..ec29c0d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -137,7 +137,7 @@ dnl
 dnl audio backend
 dnl
 AC_ARG_WITH(audio,
-            [AC_HELP_STRING([--with-audio=@<:@auto/alsa/oss/none@:>@],
+            [AC_HELP_STRING([--with-audio=@<:@auto/alsa/none@:>@],
                             [audio backend to use])],,
 	    [with_audio=auto])
 
@@ -174,14 +174,6 @@ if test "$with_audio" = "auto" -o "$with_audio" = "pa"; then
   fi
 fi
 
-dnl Assume OSS is available if ALSA wasn't found and we're "auto".
-if test "$with_audio" = "auto" -o "$with_audio" = "oss"; then
-  with_audio="oss"
-  AUDIO_CFLAGS=
-  AUDIO_LIBS=
-  AUDIO_TYPE=oss
-fi
-
 dnl If all else fails, fall back to none.
 if test "$with_audio" = "auto" -o "$with_audio" = "none"; then
   AUDIO_CFLAGS=
diff --git a/swfdec-gtk/swfdec_playback_oss.c b/swfdec-gtk/swfdec_playback_oss.c
deleted file mode 100644
index 160f26d..0000000
--- a/swfdec-gtk/swfdec_playback_oss.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/* Swfdec
- * Copyright © 2006 Benjamin Otte <otte at gnome.org>
- * Copyright © 2007 Eric Anholt <eric at anholt.net>
- *
- * 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 <sys/ioctl.h>
-#include <sys/soundcard.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "swfdec_playback.h"
-
-/** @file Implements swfdec audio playback by opening /dev/dsp per stream
- * and playing out through that.
- *
- * Allowing multiple access to /dev/dsp is not required by the OSS API spec,
- * but FreeBSD's sound system lets you, which is what this file was written
- * for.
- */
-
-/*** DEFINITIONS ***/
-
-struct _SwfdecPlayback {
-  SwfdecPlayer *	player;
-  GList *		streams;	/* all Stream objects */
-  GMainContext *	context;	/* context we work in */
-};
-
-typedef struct {
-  SwfdecPlayback *     	sound;		/* reference to sound object */
-  SwfdecAudio *		audio;		/* the audio we play back */
-  int			dsp_fd;
-  int			fragsize;	/* Audio fragment size */
-  GSource *		source;		/* source for writing data */
-  guint			offset;		/* offset into sound */
-} Stream;
-
-/* Size of one of our audio samples, in bytes */
-#define SAMPLESIZE	2
-#define CHANNELS	2
-
-/*** STREAMS ***/
-
-static gboolean
-handle_stream (GIOChannel *source, GIOCondition cond, gpointer data)
-{
-  Stream *stream = data;
-  char *frag = malloc(stream->fragsize);
-
-  if (frag == NULL) {
-    g_printerr ("Failed to allocate fragment of size %d\n",
-		stream->fragsize);
-    return FALSE;
-  }
-
-  while (TRUE) {
-    int ret;
-    audio_buf_info spaceinfo;
-
-    ret = ioctl(stream->dsp_fd, SNDCTL_DSP_GETOSPACE, &spaceinfo);
-    if (ret == -1) {
-      g_printerr ("Failed to get output buffer availability\n");
-      free(frag);
-      return FALSE;
-    }
-
-    if (spaceinfo.fragments == 0)
-      break;
-
-    memset (frag, 0, stream->fragsize);
-    swfdec_audio_render (stream->audio, (gint16 *)frag, stream->offset,
-			 stream->fragsize / SAMPLESIZE / CHANNELS);
-
-    ret = write (stream->dsp_fd, frag, stream->fragsize);
-    if (ret != stream->fragsize) {
-      g_printerr ("Failed to write fragment\n");
-      free(frag);
-      return FALSE;
-    }
-
-    stream->offset += stream->fragsize / SAMPLESIZE / CHANNELS;
-  }
-
-  free(frag);
-
-  return TRUE;
-}
-
-static void
-swfdec_playback_stream_open (SwfdecPlayback *sound, SwfdecAudio *audio)
-{
-  GIOChannel *channel;
-  Stream *stream;
-  guint rate;
-  int dsp_fd, ret, format, channels, fragsize;
-
-  dsp_fd = open("/dev/dsp", O_WRONLY);
-  if (dsp_fd == -1) {
-    g_printerr ("Failed to open /dev/dsp\n");
-    return;
-  }
-
-  format = AFMT_S16_LE;
-  ret = ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format);
-  if (ret == -1) {
-    g_printerr ("Failed to set sound format\n");
-    close(dsp_fd);
-    return;
-  }
-
-  channels = 2;
-  ret = ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, &channels);
-  if (ret == -1) {
-    g_printerr ("Failed to set stereo\n");
-    close(dsp_fd);
-    return;
-  }
-
-  rate = 44100;
-  ret = ioctl(dsp_fd, SNDCTL_DSP_SPEED, &rate);
-  if (ret == -1) {
-    g_printerr ("Failed to set rate\n");
-    close(dsp_fd);
-    return;
-  }
-
-  ret = ioctl(dsp_fd, SNDCTL_DSP_GETBLKSIZE, &fragsize);
-  if (ret == -1) {
-    g_printerr ("Failed to get fragment size\n");
-    close(dsp_fd);
-    return;
-  }
-
-  stream = g_new0 (Stream, 1);
-  stream->sound = sound;
-  stream->audio = g_object_ref (audio);
-  stream->dsp_fd = dsp_fd;
-  stream->fragsize = fragsize;
-  sound->streams = g_list_prepend (sound->streams, stream);
-
-  channel = g_io_channel_unix_new (stream->dsp_fd);
-  stream->source = g_io_create_watch (channel, G_IO_OUT);
-  g_source_set_priority (stream->source, G_PRIORITY_HIGH);
-  g_source_set_callback (stream->source, (GSourceFunc) handle_stream, stream,
-			 NULL);
-  g_io_channel_unref (channel);
-  g_source_attach (stream->source, stream->sound->context);
-
-  return;
-}
-
-static void
-swfdec_playback_stream_close (Stream *stream)
-{
-  close (stream->dsp_fd);
-  g_source_destroy (stream->source);
-  g_source_unref (stream->source);
-  stream->sound->streams = g_list_remove (stream->sound->streams, stream);
-  g_object_unref (stream->audio);
-  g_free (stream);
-}
-
-/*** SOUND ***/
-
-static void
-advance_before (SwfdecPlayer *player, guint msecs, guint audio_samples, gpointer data)
-{
-  SwfdecPlayback *sound = data;
-  GList *walk;
-
-  for (walk = sound->streams; walk; walk = walk->next) {
-    Stream *stream = walk->data;
-    if (audio_samples >= stream->offset) {
-      stream->offset = 0;
-    } else {
-      stream->offset -= audio_samples;
-    }
-  }
-}
-
-static void
-audio_added (SwfdecPlayer *player, SwfdecAudio *audio, SwfdecPlayback *sound)
-{
-  swfdec_playback_stream_open (sound, audio);
-}
-
-static void
-audio_removed (SwfdecPlayer *player, SwfdecAudio *audio, SwfdecPlayback *sound)
-{
-  GList *walk;
-
-  for (walk = sound->streams; walk; walk = walk->next) {
-    Stream *stream = walk->data;
-    if (stream->audio == audio) {
-      swfdec_playback_stream_close (stream);
-      return;
-    }
-  }
-  g_assert_not_reached ();
-}
-
-SwfdecPlayback *
-swfdec_playback_open (SwfdecPlayer *player, GMainContext *context)
-{
-  SwfdecPlayback *sound;
-  const GList *walk;
-
-  g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
-  g_return_val_if_fail (context != NULL, NULL);
-
-  sound = g_new0 (SwfdecPlayback, 1);
-  sound->player = player;
-  g_signal_connect (player, "advance", G_CALLBACK (advance_before), sound);
-  g_signal_connect (player, "audio-added", G_CALLBACK (audio_added), sound);
-  g_signal_connect (player, "audio-removed", G_CALLBACK (audio_removed), sound);
-  for (walk = swfdec_player_get_audio (player); walk; walk = walk->next) {
-    swfdec_playback_stream_open (sound, walk->data);
-  }
-  g_main_context_ref (context);
-  sound->context = context;
-  return sound;
-}
-
-void
-swfdec_playback_close (SwfdecPlayback *sound)
-{
-#define REMOVE_HANDLER_FULL(obj,func,data,count) G_STMT_START {\
-  if (g_signal_handlers_disconnect_by_func ((obj), \
-	G_CALLBACK (func), (data)) != (count)) { \
-    g_assert_not_reached (); \
-  } \
-} G_STMT_END
-#define REMOVE_HANDLER(obj,func,data) REMOVE_HANDLER_FULL (obj, func, data, 1)
-
-  while (sound->streams)
-    swfdec_playback_stream_close (sound->streams->data);
-  REMOVE_HANDLER (sound->player, advance_before, sound);
-  REMOVE_HANDLER (sound->player, audio_added, sound);
-  REMOVE_HANDLER (sound->player, audio_removed, sound);
-  g_main_context_unref (sound->context);
-  g_free (sound);
-}
-
-
commit 74fa4203ce15133db40e54f8ffad89c262f32eac
Merge: c79f285... af9f920...
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:34:02 2008 +0200

    Merge branch '0.6'

commit af9f920058a3f546996b725574927e189bab12bb
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:33:13 2008 +0200

    remove assertion
    
    our only user claims it fixes playback for him, so we're nice and don't care
    why it was there.

diff --git a/swfdec-gtk/swfdec_playback_oss.c b/swfdec-gtk/swfdec_playback_oss.c
index 88ce8fd..160f26d 100644
--- a/swfdec-gtk/swfdec_playback_oss.c
+++ b/swfdec-gtk/swfdec_playback_oss.c
@@ -84,7 +84,6 @@ handle_stream (GIOChannel *source, GIOCondition cond, gpointer data)
       free(frag);
       return FALSE;
     }
-    g_assert(spaceinfo.fragsize == stream->fragsize);
 
     if (spaceinfo.fragments == 0)
       break;
commit d8e8d6ca169417de6551c32effa21abd8cd98b1e
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:32:18 2008 +0200

    a $ is not a valid character for identifiers
    
    gcc allows it for compatibility reasons, but other compilers don't.

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index 217b5c0..d05145e 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -367,7 +367,7 @@ swfdec_as_strings.h: swfdec_as_strings.c
 	  && echo "extern const char swfdec_as_strings[];" \
 	  && grep "  SWFDEC_AS_CONSTANT_STRING" swfdec_as_strings.c \
 	  | sed "s/.*(\"\(.*\)\").*/\1/" \
-	  | LC_ALL="C" $(AWK) '{ if ($$0 == "") name = "EMPTY"; else if ($$0 == ",") name = "COMMA"; else if ($$0 == "/") name = "SLASH"; else name = $$0; gsub("[^a-zA-Z0-9\\$$]","_",name); print "#define SWFDEC_AS_STR_" name " &swfdec_as_strings[" x + 1 "]"; x = x + length ($$0) + 2 }' \
+	  | LC_ALL="C" $(AWK) '{ if ($$0 == "") name = "EMPTY"; else if ($$0 == ",") name = "COMMA"; else if ($$0 == "/") name = "SLASH"; else name = $$0; gsub("[^a-zA-Z0-9]","_",name); print "#define SWFDEC_AS_STR_" name " &swfdec_as_strings[" x + 1 "]"; x = x + length ($$0) + 2 }' \
 	  && echo "#endif" ) > xgen-sas \
 	&& (cmp -s xgen-sas swfdec_as_strings.h || cp xgen-sas swfdec_as_strings.h ) \
 	&& rm -f xgen-sas
diff --git a/swfdec/swfdec_movie.c b/swfdec/swfdec_movie.c
index c4d0b55..34e5eef 100644
--- a/swfdec/swfdec_movie.c
+++ b/swfdec/swfdec_movie.c
@@ -1526,7 +1526,7 @@ swfdec_movie_set_version (SwfdecMovie *movie)
   o = SWFDEC_AS_OBJECT (movie);
   cx = o->context;
   SWFDEC_AS_VALUE_SET_STRING (&val, swfdec_as_context_get_string (cx, SWFDEC_PLAYER (cx)->priv->system->version));
-  swfdec_as_object_set_variable (o, SWFDEC_AS_STR_$version, &val);
+  swfdec_as_object_set_variable (o, SWFDEC_AS_STR__version, &val);
 }
 
 /**
commit c79f285f8a984d09413cdbf906d54e259e2ca38d
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:27:49 2008 +0200

    compute the video frames required to decompress better
    
    The code is a mess, someone rewrite SwfdecFlvDecoder, please.
    Maybe we can bribe Benjamin to do it...

diff --git a/swfdec/swfdec_net_stream.c b/swfdec/swfdec_net_stream.c
index f809866..1dc2ddb 100644
--- a/swfdec/swfdec_net_stream.c
+++ b/swfdec/swfdec_net_stream.c
@@ -359,12 +359,20 @@ swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
     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, &stream->decoder_time,
-	  &next);
+	  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, TRUE, &format, &stream->decoder_time,
+	  next, FALSE, &format, &stream->decoder_time,
 	  &next);
     }
   }
commit ba07a419a7a7074bcdf28d397245c15a687ef623
Author: Benjamin Otte <otte at gnome.org>
Date:   Sat Apr 12 00:10:02 2008 +0200

    rewrite video input handling
    
    the video input is now only computed when rendering, and is directly attached
    to the renderer. So caching now works.
    Also, the video input is an interface now, whcih made it less code.

diff --git a/swfdec/Makefile.am b/swfdec/Makefile.am
index 9e31986..8370b8d 100644
--- a/swfdec/Makefile.am
+++ b/swfdec/Makefile.am
@@ -55,6 +55,7 @@ libswfdec_source_files = \
 	swfdec_cache.c \
 	swfdec_cached.c \
 	swfdec_cached_image.c \
+	swfdec_cached_video.c \
 	swfdec_camera.c \
 	swfdec_character.c \
 	swfdec_codec_adpcm.c \
@@ -158,6 +159,8 @@ libswfdec_source_files = \
 	swfdec_video.c \
 	swfdec_video_movie.c \
 	swfdec_video_movie_as.c \
+	swfdec_video_provider.c \
+	swfdec_video_video_provider.c \
 	swfdec_xml_node.c \
 	swfdec_xml.c \
 	swfdec_xml_socket.c
@@ -236,6 +239,7 @@ noinst_HEADERS = \
 	swfdec_cache.h \
 	swfdec_cached.h \
 	swfdec_cached_image.h \
+	swfdec_cached_video.h \
 	swfdec_character.h \
 	swfdec_codec_audio.h \
 	swfdec_codec_video.h \
@@ -294,6 +298,8 @@ noinst_HEADERS = \
 	swfdec_utils.h \
 	swfdec_video.h \
 	swfdec_video_movie.h \
+	swfdec_video_provider.h \
+	swfdec_video_video_provider.h \
 	swfdec_xml_node.h \
 	swfdec_xml.h \
 	swfdec_xml_socket.h
diff --git a/swfdec/swfdec_cached_video.c b/swfdec/swfdec_cached_video.c
new file mode 100644
index 0000000..ac68171
--- /dev/null
+++ b/swfdec/swfdec_cached_video.c
@@ -0,0 +1,113 @@
+/* Swfdec
+ * Copyright (C) 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_cached_video.h"
+#include "swfdec_debug.h"
+
+G_DEFINE_TYPE (SwfdecCachedVideo, swfdec_cached_video, SWFDEC_TYPE_CACHED)
+
+static void
+swfdec_cached_video_dispose (GObject *object)
+{
+  SwfdecCachedVideo *video = SWFDEC_CACHED_VIDEO (object);
+
+  if (video->surface) {
+    cairo_surface_destroy (video->surface);
+    video->surface = NULL;
+  }
+
+  G_OBJECT_CLASS (swfdec_cached_video_parent_class)->dispose (object);
+}
+
+static void
+swfdec_cached_video_class_init (SwfdecCachedVideoClass * g_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
+
+  object_class->dispose = swfdec_cached_video_dispose;
+}
+
+static void
+swfdec_cached_video_init (SwfdecCachedVideo *cached)
+{
+}
+
+SwfdecCachedVideo *
+swfdec_cached_video_new (cairo_surface_t *surface, gsize size)
+{
+  SwfdecCachedVideo *video;
+
+  g_return_val_if_fail (surface != NULL, NULL);
+  g_return_val_if_fail (size > 0, NULL);
+
+  size += sizeof (SwfdecCachedVideo);
+  video = g_object_new (SWFDEC_TYPE_CACHED_VIDEO, "size", size, NULL);
+  video->surface = cairo_surface_reference (surface);
+
+  return video;
+}
+
+cairo_surface_t *
+swfdec_cached_video_get_surface (SwfdecCachedVideo *video)
+{
+  g_return_val_if_fail (SWFDEC_IS_CACHED_VIDEO (video), NULL);
+
+  return cairo_surface_reference (video->surface);
+}
+
+guint
+swfdec_cached_video_get_frame (SwfdecCachedVideo *video)
+{
+  g_return_val_if_fail (SWFDEC_IS_CACHED_VIDEO (video), 0);
+
+  return video->frame;
+}
+
+void
+swfdec_cached_video_set_frame (SwfdecCachedVideo *video, guint frame)
+{
+  g_return_if_fail (SWFDEC_IS_CACHED_VIDEO (video));
+
+  video->frame = frame;
+}
+
+void
+swfdec_cached_video_get_size (SwfdecCachedVideo *video, guint *width, guint *height)
+{
+  g_return_if_fail (SWFDEC_IS_CACHED_VIDEO (video));
+
+  if (width)
+    *width = video->width;
+  if (height)
+    *height = video->height;
+}
+
+void
+swfdec_cached_video_set_size (SwfdecCachedVideo *video, guint width, guint height)
+{
+  g_return_if_fail (SWFDEC_IS_CACHED_VIDEO (video));
+
+  video->width = width;
+  video->height = height;
+}
+
diff --git a/swfdec/swfdec_cached_video.h b/swfdec/swfdec_cached_video.h
new file mode 100644
index 0000000..2c94e80
--- /dev/null
+++ b/swfdec/swfdec_cached_video.h
@@ -0,0 +1,73 @@
+/* Swfdec
+ * Copyright (c) 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_CACHED_VIDEO_H_
+#define _SWFDEC_CACHED_VIDEO_H_
+
+#include <cairo.h>
+#include <swfdec/swfdec_cached.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SwfdecCachedVideo SwfdecCachedVideo;
+typedef struct _SwfdecCachedVideoClass SwfdecCachedVideoClass;
+
+#define SWFDEC_TYPE_CACHED_VIDEO                    (swfdec_cached_video_get_type())
+#define SWFDEC_IS_CACHED_VIDEO(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_CACHED_VIDEO))
+#define SWFDEC_IS_CACHED_VIDEO_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_CACHED_VIDEO))
+#define SWFDEC_CACHED_VIDEO(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_CACHED_VIDEO, SwfdecCachedVideo))
+#define SWFDEC_CACHED_VIDEO_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_CACHED_VIDEO, SwfdecCachedVideoClass))
+#define SWFDEC_CACHED_VIDEO_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), SWFDEC_TYPE_CACHED_VIDEO, SwfdecCachedVideoClass))
+
+
+struct _SwfdecCachedVideo {
+  SwfdecCached		cached;
+
+  guint			frame;		/* number of the frame */
+  guint			width;		/* relevant width of surface */
+  guint			height;		/* relevant height of surface */
+
+  cairo_surface_t *	surface;
+};
+
+struct _SwfdecCachedVideoClass
+{
+  SwfdecCachedClass	cached_class;
+};
+
+GType			swfdec_cached_video_get_type	(void);
+
+SwfdecCachedVideo *	swfdec_cached_video_new		(cairo_surface_t *	surface,
+							 gsize			size);
+
+
+cairo_surface_t *	swfdec_cached_video_get_surface	(SwfdecCachedVideo *	video);
+guint			swfdec_cached_video_get_frame	(SwfdecCachedVideo *	video);
+void			swfdec_cached_video_set_frame	(SwfdecCachedVideo *	video,
+							 guint			frame);
+void			swfdec_cached_video_get_size	(SwfdecCachedVideo *	video,
+							 guint *		width,
+							 guint *		height);
+void			swfdec_cached_video_set_size	(SwfdecCachedVideo *	video,
+							 guint			width,
+							 guint			height);
+
+
+G_END_DECLS
+#endif
diff --git a/swfdec/swfdec_codec_video.c b/swfdec/swfdec_codec_video.c
index 0b04285..bfa5593 100644
--- a/swfdec/swfdec_codec_video.c
+++ b/swfdec/swfdec_codec_video.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * 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
@@ -26,6 +26,7 @@
 #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)
@@ -250,23 +251,30 @@ swfdec_video_codec_apply_mask (guint8 *data, guint rowstride, const guint8 *mask
 /**
  * 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 an image surface.
+ * 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, SwfdecBuffer *buffer)
+swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecRenderer *renderer,
+    SwfdecBuffer *buffer, guint *width, guint *height)
 {
   SwfdecVideoImage image;
-  static const cairo_user_data_key_t key;
   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");
@@ -289,17 +297,11 @@ swfdec_video_decoder_decode (SwfdecVideoDecoder *decoder, SwfdecBuffer *buffer)
     swfdec_video_codec_apply_mask (data, image.width * 4, image.mask, 
 	image.mask_rowstride, image.width, image.height);
   }
-  surface = cairo_image_surface_create_for_data (data, 
+  surface = swfdec_renderer_create_for_data (renderer, data,
       image.mask ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
       image.width, image.height, rowstride);
-  if (cairo_surface_status (surface)) {
-    SWFDEC_ERROR ("failed to create surface: %s", 
-	cairo_status_to_string (cairo_surface_status (surface)));
-    cairo_surface_destroy (surface);
-    return NULL;
-  }
-  cairo_surface_set_user_data (surface, &key, data, 
-      (cairo_destroy_func_t) g_free);
+  *width = image.width;
+  *height = image.height;
   return surface;
 }
 
diff --git a/swfdec/swfdec_codec_video.h b/swfdec/swfdec_codec_video.h
index eb6e3ab..5ad961f 100644
--- a/swfdec/swfdec_codec_video.h
+++ b/swfdec/swfdec_codec_video.h
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * 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
@@ -23,6 +23,7 @@
 #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
@@ -69,7 +70,10 @@ SwfdecVideoDecoder *	swfdec_video_decoder_new      	(guint			codec);
 void			swfdec_video_decoder_free	(SwfdecVideoDecoder *   decoder);
 
 cairo_surface_t *     	swfdec_video_decoder_decode	(SwfdecVideoDecoder *	decoder,
-							 SwfdecBuffer *		buffer);
+							 SwfdecRenderer *	renderer,
+							 SwfdecBuffer *		buffer,
+							 guint *      		width,
+							 guint *		height);
 
 
 G_END_DECLS
diff --git a/swfdec/swfdec_image.c b/swfdec/swfdec_image.c
index 32a7fa7..2885969 100644
--- a/swfdec/swfdec_image.c
+++ b/swfdec/swfdec_image.c
@@ -611,7 +611,7 @@ swfdec_image_create_surface (SwfdecImage *image, SwfdecRenderer *renderer)
   if (renderer) {
     /* FIXME: The size is just an educated guess */
     cached = swfdec_cached_image_new (surface, image->width * image->height * 4);
-    swfdec_renderer_add_cache (renderer, image, SWFDEC_CACHED (cached));
+    swfdec_renderer_add_cache (renderer, FALSE, image, SWFDEC_CACHED (cached));
     g_object_unref (cached);
   }
 
@@ -652,7 +652,7 @@ swfdec_image_create_surface_transformed (SwfdecImage *image, SwfdecRenderer *ren
     if (renderer) {
       cached = swfdec_cached_image_new (source, image->width * image->height * 4);
       swfdec_cached_image_set_color_transform (cached, &mask);
-      swfdec_renderer_add_cache (renderer, image, SWFDEC_CACHED (cached));
+      swfdec_renderer_add_cache (renderer, FALSE, image, SWFDEC_CACHED (cached));
       g_object_unref (cached);
     }
   }
@@ -680,7 +680,7 @@ swfdec_image_create_surface_transformed (SwfdecImage *image, SwfdecRenderer *ren
     /* FIXME: The size is just an educated guess */
     cached = swfdec_cached_image_new (surface, image->width * image->height * 4);
     swfdec_cached_image_set_color_transform (cached, trans);
-    swfdec_renderer_add_cache (renderer, image, SWFDEC_CACHED (cached));
+    swfdec_renderer_add_cache (renderer, FALSE, image, SWFDEC_CACHED (cached));
     g_object_unref (cached);
   }
   return surface;
diff --git a/swfdec/swfdec_net_stream.c b/swfdec/swfdec_net_stream.c
index 25d3c1e..f809866 100644
--- a/swfdec/swfdec_net_stream.c
+++ b/swfdec/swfdec_net_stream.c
@@ -27,12 +27,15 @@
 #include "swfdec_as_frame_internal.h"
 #include "swfdec_as_strings.h"
 #include "swfdec_audio_flv.h"
+#include "swfdec_cached_video.h"
 #include "swfdec_debug.h"
 #include "swfdec_loader_internal.h"
 #include "swfdec_player_internal.h"
+#include "swfdec_renderer_internal.h"
 #include "swfdec_resource.h"
 #include "swfdec_sandbox.h"
 #include "swfdec_stream_target.h"
+#include "swfdec_video_provider.h"
 
 /* NB: code and level must be rooted gc-strings */
 static void
@@ -58,38 +61,6 @@ swfdec_net_stream_onstatus (SwfdecNetStream *stream, const char *code, const cha
   swfdec_sandbox_unuse (stream->sandbox);
 }
 
-static cairo_surface_t *
-swfdec_net_stream_decode_video (SwfdecNetStream *stream, SwfdecBuffer *buffer)
-{
-  SwfdecVideoDecoder *decoder = stream->decoder;
-  cairo_surface_t *surface;
-
-  if (decoder == NULL)
-    return NULL;
-
-  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, tmp);
-    swfdec_buffer_unref (tmp);
-    if (hsub || wsub) {
-      SWFDEC_FIXME ("need to subtract %ux%u pixels", wsub, hsub);
-    }
-  } else {
-    surface = swfdec_video_decoder_decode (decoder, buffer);
-  }
-  return surface;
-}
-
 static void swfdec_net_stream_update_playing (SwfdecNetStream *stream);
 static void
 swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
@@ -121,15 +92,9 @@ swfdec_net_stream_video_goto (SwfdecNetStream *stream, guint timestamp)
       if (stream->decoder)
 	swfdec_video_decoder_free (stream->decoder);
       stream->format = format;
-      stream->decoder = swfdec_video_decoder_new (format);
-    }
-    stream->surface = swfdec_net_stream_decode_video (stream, buffer);
-    if (stream->surface) {
-      GList *walk;
-      for (walk = stream->movies; walk; walk = walk->next) {
-	swfdec_video_movie_new_image (walk->data);
-      }
+      stream->decoder = NULL;
     }
+    swfdec_video_provider_new_image (SWFDEC_VIDEO_PROVIDER (stream));
   }
   if (stream->next_time <= stream->current_time) {
     if (swfdec_flv_decoder_is_eof (stream->flvdecoder)) {
@@ -323,38 +288,133 @@ swfdec_net_stream_stream_target_init (SwfdecStreamTargetInterface *iface)
   iface->error = swfdec_net_stream_stream_target_error;
 }
 
-/*** SWFDEC VIDEO MOVIE INPUT ***/
+/*** SWFDEC VIDEO PROVIDER ***/
 
-static void
-swfdec_net_stream_input_connect (SwfdecVideoMovieInput *input, SwfdecVideoMovie *movie)
+static cairo_surface_t *
+swfdec_net_stream_decode_video (SwfdecVideoDecoder *decoder, SwfdecRenderer *renderer,
+    SwfdecBuffer *buffer, guint *width, guint *height)
 {
-  SwfdecNetStream *stream = SWFDEC_NET_STREAM ((void *)((guchar *) input - G_STRUCT_OFFSET (SwfdecNetStream, input)));
+  cairo_surface_t *surface;
 
-  stream->movies = g_list_prepend (stream->movies, movie);
-  g_object_ref (stream);
+  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 void
-swfdec_net_stream_input_disconnect (SwfdecVideoMovieInput *input, SwfdecVideoMovie *movie)
+static cairo_surface_t *
+swfdec_net_stream_video_provider_get_image (SwfdecVideoProvider *provider,
+    SwfdecRenderer *renderer, guint *width, guint *height)
 {
-  SwfdecNetStream *stream = SWFDEC_NET_STREAM ((void *)((guchar *) input - G_STRUCT_OFFSET (SwfdecNetStream, input)));
+  SwfdecNetStream *stream = SWFDEC_NET_STREAM (provider);
+  SwfdecCachedVideo *cached;
+  cairo_surface_t *surface;
+  SwfdecBuffer *buffer;
+  guint next, format;
 
-  stream->movies = g_list_remove (stream->movies, movie);
-  g_object_unref (stream);
+  cached = SWFDEC_CACHED_VIDEO (swfdec_renderer_get_cache (renderer, stream, NULL, NULL));
+  if (cached != NULL && swfdec_cached_video_get_frame (cached) == stream->current_time) {
+    swfdec_cached_use (SWFDEC_CACHED (cached));
+    swfdec_cached_video_get_size (cached, width, height);
+    return swfdec_cached_video_get_surface (cached);
+  }
+
+  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, &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) {
+      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	  stream->current_time, TRUE, &format, &stream->decoder_time,
+	  &next);
+    } else {
+      buffer = swfdec_flv_decoder_get_video (stream->flvdecoder, 
+	  next, TRUE, &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);
+  }
+
+  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);
+  swfdec_renderer_add_cache (renderer, TRUE, stream, SWFDEC_CACHED (cached));
+  g_object_unref (cached);
+
+  return surface;
 }
 
-static cairo_surface_t *
-swfdec_net_stream_input_get_image (SwfdecVideoMovieInput *input)
+static void
+swfdec_net_stream_video_provider_init (SwfdecVideoProviderInterface *iface)
 {
-  SwfdecNetStream *stream = SWFDEC_NET_STREAM ((void *)((guchar *) input - G_STRUCT_OFFSET (SwfdecNetStream, input)));
-
-  return stream->surface;
+  iface->get_image = swfdec_net_stream_video_provider_get_image;
 }
 
 /*** SWFDEC_NET_STREAM ***/
 
 G_DEFINE_TYPE_WITH_CODE (SwfdecNetStream, swfdec_net_stream, SWFDEC_TYPE_AS_OBJECT,
-    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_STREAM_TARGET, swfdec_net_stream_stream_target_init))
+    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_STREAM_TARGET, swfdec_net_stream_stream_target_init)
+    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_VIDEO_PROVIDER, swfdec_net_stream_video_provider_init))
 
 static void
 swfdec_net_stream_dispose (GObject *object)
@@ -454,10 +514,6 @@ swfdec_net_stream_class_init (SwfdecNetStreamClass *klass)
 static void
 swfdec_net_stream_init (SwfdecNetStream *stream)
 {
-  stream->input.connect = swfdec_net_stream_input_connect;
-  stream->input.disconnect = swfdec_net_stream_input_disconnect;
-  stream->input.get_image = swfdec_net_stream_input_get_image;
-
   stream->buffer_time = 100; /* msecs */
 }
 
@@ -642,6 +698,10 @@ swfdec_net_stream_seek (SwfdecNetStream *stream, double secs)
     SWFDEC_ERROR ("FIXME: implement seeking in audio only NetStream");
     return;
   }
+  if (stream->decoder) {
+    g_object_unref (stream->decoder);
+    stream->decoder = NULL;
+  }
   msecs = secs * 1000;
   msecs += first;
   if (msecs > last)
diff --git a/swfdec/swfdec_net_stream.h b/swfdec/swfdec_net_stream.h
index 5520d05..e8414de 100644
--- a/swfdec/swfdec_net_stream.h
+++ b/swfdec/swfdec_net_stream.h
@@ -62,9 +62,9 @@ struct _SwfdecNetStream
   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 */
   SwfdecTimeout		timeout;	/* timeout to advance to */
-  SwfdecVideoMovieInput	input;		/* used when attaching to a video movie */
   GList *		movies;		/* movies we're connected to */
 
   /* audio */
diff --git a/swfdec/swfdec_renderer.c b/swfdec/swfdec_renderer.c
index dc88721..2bc7d23 100644
--- a/swfdec/swfdec_renderer.c
+++ b/swfdec/swfdec_renderer.c
@@ -203,7 +203,8 @@ swfdec_renderer_init (SwfdecRenderer *renderer)
 /*** INTERNAL API ***/
 
 void
-swfdec_renderer_add_cache (SwfdecRenderer *renderer, gpointer key, SwfdecCached *cached)
+swfdec_renderer_add_cache (SwfdecRenderer *renderer, gboolean replace,
+    gpointer key, SwfdecCached *cached)
 {
   SwfdecRendererPrivate *priv;
   GList *list;
@@ -214,6 +215,17 @@ swfdec_renderer_add_cache (SwfdecRenderer *renderer, gpointer key, SwfdecCached
 
   priv = renderer->priv;
   list = g_hash_table_lookup (priv->cache_lookup, key);
+  if (replace) {
+    GList *walk;
+    for (walk = list; walk; walk = walk->next) {
+      if (walk->data) {
+	g_object_remove_weak_pointer (walk->data, &walk->data);
+	swfdec_cached_unuse (walk->data);
+      }
+    }
+    g_list_free (list);
+    list = NULL;
+  }
   list = g_list_prepend (list, cached);
   /* NB: empty list entries mean object was deleted */
   g_object_add_weak_pointer (G_OBJECT (cached), &list->data);
diff --git a/swfdec/swfdec_renderer_internal.h b/swfdec/swfdec_renderer_internal.h
index b8a3dd9..f757ccd 100644
--- a/swfdec/swfdec_renderer_internal.h
+++ b/swfdec/swfdec_renderer_internal.h
@@ -34,6 +34,7 @@ void			swfdec_renderer_attach		(SwfdecRenderer *	renderer,
 							 cairo_t *		cr);
 SwfdecRenderer *	swfdec_renderer_get		(cairo_t *		cr);
 void			swfdec_renderer_add_cache	(SwfdecRenderer *	renderer,
+							 gboolean		replace,
 							 gpointer		key,
 							 SwfdecCached *		value);
 SwfdecCached *		swfdec_renderer_get_cache	(SwfdecRenderer *	renderer,
diff --git a/swfdec/swfdec_video.c b/swfdec/swfdec_video.c
index b75649c..322a376 100644
--- a/swfdec/swfdec_video.c
+++ b/swfdec/swfdec_video.c
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * 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
@@ -21,150 +21,12 @@
 #include "config.h"
 #endif
 
-#include <stdlib.h>
-
 #include "swfdec_video.h"
 #include "swfdec_debug.h"
-#include "swfdec_font.h"
 #include "swfdec_player_internal.h"
 #include "swfdec_swf_decoder.h"
 #include "swfdec_video_movie.h"
-
-typedef struct {
-  guint			frame;
-  SwfdecBuffer *	buffer;
-} SwfdecVideoTag;
-
-static int
-swfdec_video_compare_frame (gconstpointer a, gconstpointer b)
-{
-  return (int) *((const guint *) a) - *((const guint *) b);
-}
-
-static SwfdecBuffer *
-swfdec_video_find_frame (SwfdecVideo *video, guint frame)
-{
-  SwfdecVideoTag tmp = { frame, NULL };
-  SwfdecVideoTag *tag;
-
-  if (video->images->len == 0)
-    return NULL;
-
-  tag = bsearch (&tmp, video->images->data, video->images->len,
-      sizeof (SwfdecVideoTag), swfdec_video_compare_frame);
-
-  return tag ? tag->buffer : NULL;
-}
-
-/*** INPUT FOR MOVIE ***/
-
-typedef struct {
-  SwfdecVideoMovieInput	input;
-  SwfdecVideoMovie *	movie;
-  SwfdecVideo *		video;
-  gpointer		decoder;
-  guint			current_frame;
-  cairo_surface_t *	surface;
-} SwfdecVideoInput;
-
-static void
-swfdec_video_input_set_ratio (SwfdecVideoMovieInput *input_, SwfdecVideoMovie *movie)
-{
-  SwfdecVideoInput *input = (SwfdecVideoInput *) input_;
-  guint ratio = SWFDEC_MOVIE (movie)->original_ratio;
-  SwfdecBuffer *buffer;
-
-  if (input->decoder == NULL)
-    return;
-  ratio++;
-  if (ratio > input->video->n_frames) {
-    SWFDEC_ERROR ("ratio %u too big: only %u frames", ratio, input->video->n_frames);
-    ratio = input->video->n_frames - 1;
-  }
-  if (ratio < input->current_frame) {
-    /* FIXME: need to reset decoder here? */
-    input->current_frame = 0;
-  }
-
-  while (input->current_frame < ratio) {
-    buffer = swfdec_video_find_frame (input->video, input->current_frame);
-    if (buffer == NULL) {
-      SWFDEC_INFO ("no buffer for frame %u", input->current_frame);
-    } else {
-      if (input->surface)
-	cairo_surface_destroy (input->surface);
-      input->surface = swfdec_video_decoder_decode (input->decoder, buffer);
-      if (input->surface == NULL)
-	return;
-      if (input->video->width < (guint) cairo_image_surface_get_width (input->surface) ||
-	  input->video->height < (guint) cairo_image_surface_get_height (input->surface)) {
-	static cairo_user_data_key_t key;
-	cairo_surface_t *old = input->surface;
-	input->surface = cairo_image_surface_create_for_data (
-	    cairo_image_surface_get_data (old), cairo_image_surface_get_format (old),
-	    input->video->width, input->video->height,
-	    cairo_image_surface_get_stride (old));
-	cairo_surface_set_user_data (input->surface, &key, old, 
-	    (cairo_destroy_func_t) cairo_surface_destroy);
-      }
-    }
-    input->current_frame++;
-  }
-}
-
-static void
-swfdec_video_input_connect (SwfdecVideoMovieInput *input_, SwfdecVideoMovie *movie)
-{
-  SwfdecVideoInput *input = (SwfdecVideoInput *) input_;
-
-  g_assert (input->movie == NULL);
-  input->movie = movie;
-}
-
-static void
-swfdec_video_input_disconnect (SwfdecVideoMovieInput *input_, SwfdecVideoMovie *movie)
-{
-  SwfdecVideoInput *input = (SwfdecVideoInput *) input_;
-
-  g_assert (input->movie == movie);
-  if (input->decoder)
-    swfdec_video_decoder_free (input->decoder);
-  if (input->surface)
-    cairo_surface_destroy (input->surface);
-  g_object_unref (input->video);
-  g_slice_free (SwfdecVideoInput, input);
-}
-
-static cairo_surface_t *
-swfdec_video_input_get_image (SwfdecVideoMovieInput *input_)
-{
-  SwfdecVideoInput *input = (SwfdecVideoInput *) input_;
-
-  return input->surface;
-}
-
-static SwfdecVideoMovieInput *
-swfdec_video_input_new (SwfdecVideo *video)
-{
-  SwfdecVideoInput *input;
-  
-  if (video->n_frames == 0)
-    return NULL;
-  input = g_slice_new0 (SwfdecVideoInput);
-  input->decoder = swfdec_video_decoder_new (video->format);
-  if (input->decoder == NULL) {
-    g_slice_free (SwfdecVideoInput, input);
-    return NULL;
-  }
-  input->input.connect = swfdec_video_input_connect;
-  input->input.disconnect = swfdec_video_input_disconnect;
-  input->input.set_ratio = swfdec_video_input_set_ratio;
-  input->input.get_image = swfdec_video_input_get_image;
-  g_object_ref (video);
-  input->video = video;
-  input->current_frame = (guint) -1;
-  return &input->input;
-}
+#include "swfdec_video_video_provider.h"
 
 /*** SWFDEC_VIDEO ***/
 
@@ -175,12 +37,14 @@ swfdec_video_create_movie (SwfdecGraphic *graphic, gsize *size)
 {
   SwfdecVideo *video = SWFDEC_VIDEO (graphic);
   SwfdecVideoMovie *movie = g_object_new (SWFDEC_TYPE_VIDEO_MOVIE, NULL);
-  SwfdecVideoMovieInput *input = swfdec_video_input_new (video);
 
-  movie->video = SWFDEC_VIDEO (graphic);
-  g_object_ref (graphic);
-  if (input)
-    swfdec_video_movie_set_input (movie, input);
+  movie->video = video;
+  g_object_ref (video);
+  if (video->n_frames > 0) {
+    SwfdecVideoProvider *provider = swfdec_video_video_provider_new (video);
+    swfdec_video_movie_set_provider (movie, provider);
+    g_object_unref (provider);
+  }
   *size = sizeof (SwfdecVideoMovie);
   return SWFDEC_MOVIE (movie);
 }
@@ -192,7 +56,7 @@ swfdec_video_dispose (GObject *object)
   guint i;
 
   for (i = 0; i < video->images->len; i++) {
-    swfdec_buffer_unref (g_array_index (video->images, SwfdecVideoTag, i).buffer);
+    swfdec_buffer_unref (g_array_index (video->images, SwfdecVideoFrame, i).buffer);
   }
   g_array_free (video->images, TRUE);
   G_OBJECT_CLASS (swfdec_video_parent_class)->dispose (object);
@@ -212,7 +76,7 @@ swfdec_video_class_init (SwfdecVideoClass * g_class)
 static void
 swfdec_video_init (SwfdecVideo * video)
 {
-  video->images = g_array_new (FALSE, FALSE, sizeof (SwfdecVideoTag));
+  video->images = g_array_new (FALSE, FALSE, sizeof (SwfdecVideoFrame));
 }
 
 int
@@ -249,7 +113,7 @@ tag_func_video_frame (SwfdecSwfDecoder *s, guint unused_tag)
 {
   SwfdecVideo *video;
   guint id;
-  SwfdecVideoTag tag;
+  SwfdecVideoFrame tag;
 
   id = swfdec_bits_get_u16 (&s->b);
   video = (SwfdecVideo *) swfdec_swf_decoder_get_character (s, id);
@@ -279,19 +143,20 @@ tag_func_video_frame (SwfdecSwfDecoder *s, guint unused_tag)
   }
   if (video->images->len == 0) {
     g_array_append_val (video->images, tag);
-  } else if (g_array_index (video->images, SwfdecVideoTag, video->images->len - 1).frame < tag.frame) {
+  } else if (g_array_index (video->images, SwfdecVideoFrame, video->images->len - 1).frame < tag.frame) {
     g_array_append_val (video->images, tag);
   } else {
     guint i;
     SWFDEC_WARNING ("frame not in ascending order (last is %u, this is %u)",
-	g_array_index (video->images, SwfdecVideoTag, video->images->len - 1).frame, tag.frame);
+	g_array_index (video->images, SwfdecVideoFrame, video->images->len - 1).frame, tag.frame);
     for (i = 0; i < video->images->len; i++) {
-      SwfdecVideoTag *cur = &g_array_index (video->images, SwfdecVideoTag, i);
+      SwfdecVideoFrame *cur = &g_array_index (video->images, SwfdecVideoFrame, i);
       if (cur->frame < tag.frame)
 	continue;
       if (cur->frame == tag.frame) {
-	SWFDEC_ERROR ("duplicate frame id %u", cur->frame);
-	continue;
+	SWFDEC_ERROR ("duplicate frame id %u, discarding", cur->frame);
+	swfdec_buffer_unref (tag.buffer);
+	break;
       }
       g_array_insert_val (video->images, i, tag);
       break;
diff --git a/swfdec/swfdec_video.h b/swfdec/swfdec_video.h
index 94569f7..13135b3 100644
--- a/swfdec/swfdec_video.h
+++ b/swfdec/swfdec_video.h
@@ -1,5 +1,5 @@
 /* Swfdec
- * Copyright (C) 2007 Benjamin Otte <otte at gnome.org>
+ * 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
@@ -20,12 +20,13 @@
 #ifndef _SWFDEC_VIDEO_H_
 #define _SWFDEC_VIDEO_H_
 
+#include <swfdec/swfdec_buffer.h>
 #include <swfdec/swfdec_graphic.h>
-#include <swfdec/swfdec_codec_video.h>
 
 G_BEGIN_DECLS
 
 typedef struct _SwfdecVideo SwfdecVideo;
+typedef struct _SwfdecVideoFrame SwfdecVideoFrame;
 typedef struct _SwfdecVideoClass SwfdecVideoClass;
 
 #define SWFDEC_TYPE_VIDEO                    (swfdec_video_get_type())
@@ -34,6 +35,11 @@ typedef struct _SwfdecVideoClass SwfdecVideoClass;
 #define SWFDEC_VIDEO(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO, SwfdecVideo))
 #define SWFDEC_VIDEO_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO, SwfdecVideoClass))
 
+struct _SwfdecVideoFrame {
+  guint			frame;
+  SwfdecBuffer *	buffer;
+};
+
 struct _SwfdecVideo {
   SwfdecGraphic			graphic;
 
diff --git a/swfdec/swfdec_video_movie.c b/swfdec/swfdec_video_movie.c
index 1f32922..7c4f868 100644
--- a/swfdec/swfdec_video_movie.c
+++ b/swfdec/swfdec_video_movie.c
@@ -22,11 +22,13 @@
 #endif
 
 #include "swfdec_video_movie.h"
+#include "swfdec_as_strings.h"
+#include "swfdec_debug.h"
 #include "swfdec_player_internal.h"
 #include "swfdec_resource.h"
-#include "swfdec_as_strings.h"
+#include "swfdec_renderer_internal.h"
 #include "swfdec_utils.h"
-#include "swfdec_debug.h"
+#include "swfdec_video_provider.h"
 
 G_DEFINE_TYPE (SwfdecVideoMovie, swfdec_video_movie, SWFDEC_TYPE_MOVIE)
 
@@ -43,53 +45,28 @@ swfdec_video_movie_update_extents (SwfdecMovie *movie,
 }
 
 static void
-swfdec_video_movie_update_image (SwfdecVideoMovie *movie)
-{
-  if (!movie->needs_update)
-    return;
-
-  if (movie->input != NULL) {
-    if (movie->input->set_ratio)
-      movie->input->set_ratio (movie->input, movie);
-    
-    if (movie->image)
-      cairo_surface_destroy (movie->image);
-    movie->image = movie->input->get_image (movie->input);
-    if (movie->image)
-      cairo_surface_reference (movie->image);
-  }
-  movie->needs_update = FALSE;
-}
-
-static void
 swfdec_video_movie_render (SwfdecMovie *mov, cairo_t *cr, 
     const SwfdecColorTransform *trans, const SwfdecRect *inval)
 {
   SwfdecVideoMovie *movie = SWFDEC_VIDEO_MOVIE (mov);
+  cairo_surface_t *surface;
+  guint width, height;
 
-  swfdec_video_movie_update_image (movie);
-  if (movie->image == NULL)
+  if (movie->provider == NULL || movie->clear)
     return;
 
+  surface = swfdec_video_provider_get_image (movie->provider,
+      swfdec_renderer_get (cr), &width, &height);
+  if (surface == NULL)
+    return;
   cairo_scale (cr, 
       (mov->original_extents.x1 - mov->original_extents.x0)
-      / cairo_image_surface_get_width (movie->image),
+      / width,
       (mov->original_extents.y1 - mov->original_extents.y0)
-      / cairo_image_surface_get_height (movie->image));
-  cairo_set_source_surface (cr, movie->image, 0.0, 0.0);
+      / height);
+  cairo_set_source_surface (cr, surface, 0.0, 0.0);
   cairo_paint (cr);
-}
-
-static void
-swfdec_video_movie_unset_input (SwfdecVideoMovie *movie)
-{
-  if (movie->input == NULL)
-    return;
-
-  if (movie->input->disconnect)
-    movie->input->disconnect (movie->input, movie);
-  movie->input = NULL;
-  swfdec_video_movie_update_image (movie);
+  cairo_surface_destroy (surface);
 }
 
 static void
@@ -97,12 +74,8 @@ swfdec_video_movie_dispose (GObject *object)
 {
   SwfdecVideoMovie *movie = SWFDEC_VIDEO_MOVIE (object);
 
-  swfdec_video_movie_unset_input (movie);
+  swfdec_video_movie_set_provider (movie, NULL);
   g_object_unref (movie->video);
-  if (movie->image) {
-    cairo_surface_destroy (movie->image);
-    movie->image = NULL;
-  }
 
   G_OBJECT_CLASS (swfdec_video_movie_parent_class)->dispose (object);
 }
@@ -112,10 +85,8 @@ swfdec_video_movie_set_ratio (SwfdecMovie *movie)
 {
   SwfdecVideoMovie *video = SWFDEC_VIDEO_MOVIE (movie);
 
-  if (video->input && video->input->set_ratio) {
-    video->needs_update = TRUE;
-    swfdec_movie_invalidate_last (movie);
-  }
+  if (video->provider)
+    swfdec_video_provider_set_ratio (video->provider, movie->original_ratio);
 }
 
 static gboolean
@@ -124,18 +95,27 @@ 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) {
-    swfdec_video_movie_update_image (video);
-    SWFDEC_AS_VALUE_SET_NUMBER (val, (video->image != NULL ?
-	  cairo_image_surface_get_width (video->image) : 0));
+    if (video->provider) {
+      swfdec_video_provider_get_image (video->provider,
+	  SWFDEC_PLAYER (object->context)->priv->renderer, &w, &h);
+    } else {
+      w = 0;
+    }
+    SWFDEC_AS_VALUE_SET_NUMBER (val, w);
     return TRUE;
   } else if (swfdec_strcmp (version, variable, SWFDEC_AS_STR_height) == 0) {
-    swfdec_video_movie_update_image (video);
-    SWFDEC_AS_VALUE_SET_NUMBER (val, (video->image != NULL ?
-	  cairo_image_surface_get_height (video->image) : 0));
+    if (video->provider) {
+      swfdec_video_provider_get_image (video->provider,
+	  SWFDEC_PLAYER (object->context)->priv->renderer, &w, &h);
+    } else {
+      h = 0;
+    }
+    SWFDEC_AS_VALUE_SET_NUMBER (val, h);
     return TRUE;
   } else if (swfdec_strcmp (version, variable, SWFDEC_AS_STR_deblocking) == 0) {
     SWFDEC_STUB ("Video.deblocking (get)");
@@ -228,45 +208,45 @@ swfdec_video_movie_init (SwfdecVideoMovie * video_movie)
 {
 }
 
-void
-swfdec_video_movie_set_input (SwfdecVideoMovie *movie, SwfdecVideoMovieInput *input)
+static void
+swfdec_video_movie_new_image (SwfdecVideoProvider *provider, SwfdecVideoMovie *movie)
 {
-  g_return_if_fail (SWFDEC_IS_VIDEO_MOVIE (movie));
-
-  swfdec_video_movie_unset_input (movie);
-  movie->input = input;
-  if (input == NULL)
-    return;
-  if (movie->input->set_ratio) {
-    swfdec_movie_invalidate_last (SWFDEC_MOVIE (movie));
-  }
-  movie->needs_update = TRUE;
-  if (input->connect)
-    input->connect (input, movie);
+  movie->clear = FALSE;
+  swfdec_movie_invalidate_last (SWFDEC_MOVIE (movie));
 }
 
 void
-swfdec_video_movie_clear (SwfdecVideoMovie *movie)
+swfdec_video_movie_set_provider (SwfdecVideoMovie *movie, 
+    SwfdecVideoProvider *provider)
 {
   g_return_if_fail (SWFDEC_IS_VIDEO_MOVIE (movie));
+  g_return_if_fail (provider == NULL || SWFDEC_IS_VIDEO_PROVIDER (provider));
+
+  if (provider == movie->provider)
+    return;
+
+  if (provider) {
+    g_object_ref (provider);
+    g_signal_connect (provider, "new-image", 
+	G_CALLBACK (swfdec_video_movie_new_image), movie);
+  }
 
-  if (movie->image) {
-    cairo_surface_destroy (movie->image);
-    movie->image = NULL;
+  if (movie->provider) {
+    g_signal_handlers_disconnect_by_func (movie->provider,
+	swfdec_video_movie_new_image, movie);
+    g_object_unref (movie->provider);
   }
+
+  movie->provider = provider;
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (movie));
 }
 
 void
-swfdec_video_movie_new_image (SwfdecVideoMovie *movie)
+swfdec_video_movie_clear (SwfdecVideoMovie *movie)
 {
   g_return_if_fail (SWFDEC_IS_VIDEO_MOVIE (movie));
 
-  if (movie->image) {
-    cairo_surface_destroy (movie->image);
-    movie->image = NULL;
-  }
-  movie->needs_update = TRUE;
+  movie->clear = TRUE;
   swfdec_movie_invalidate_last (SWFDEC_MOVIE (movie));
 }
 
diff --git a/swfdec/swfdec_video_movie.h b/swfdec/swfdec_video_movie.h
index 9d5af15..09543e0 100644
--- a/swfdec/swfdec_video_movie.h
+++ b/swfdec/swfdec_video_movie.h
@@ -23,6 +23,7 @@
 #include <glib-object.h>
 #include <swfdec/swfdec_movie.h>
 #include <swfdec/swfdec_video.h>
+#include <swfdec/swfdec_video_provider.h>
 
 G_BEGIN_DECLS
 
@@ -37,28 +38,12 @@ typedef struct _SwfdecVideoMovieInput SwfdecVideoMovieInput;
 #define SWFDEC_VIDEO_MOVIE(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_MOVIE, SwfdecVideoMovie))
 #define SWFDEC_VIDEO_MOVIE_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_MOVIE, SwfdecVideoMovieClass))
 
-/* FIXME: make an interface? */
-struct _SwfdecVideoMovieInput {
-  /* connect to movie */
-  void			(* connect)	(SwfdecVideoMovieInput *input,
-					 SwfdecVideoMovie *	movie);
-  /* called when input is unset */
-  void			(* disconnect)	(SwfdecVideoMovieInput *input,
-					 SwfdecVideoMovie *	movie);
-  /* called when movie ratio changed */
-  void			(* set_ratio)	(SwfdecVideoMovieInput *input,
-					 SwfdecVideoMovie *	movie);
-  /* called to request the current image */
-  cairo_surface_t *	(* get_image)	(SwfdecVideoMovieInput *input);
-};
-
 struct _SwfdecVideoMovie {
   SwfdecMovie		movie;
 
   SwfdecVideo *		video;		/* video we play back */
-  SwfdecVideoMovieInput *input;		/* where we take the input from */
-  gboolean		needs_update;	/* TRUE if we should call set_ratio and get_image */
-  cairo_surface_t *	image;	 	/* currently displayed image */
+  SwfdecVideoProvider *	provider;	/* where we take the video from */
+  gboolean		clear;		/* do not display a video image */
 };
 
 struct _SwfdecVideoMovieClass {
@@ -67,11 +52,9 @@ struct _SwfdecVideoMovieClass {
 
 GType		swfdec_video_movie_get_type		(void);
 
-void		swfdec_video_movie_set_input		(SwfdecVideoMovie *	movie,
-							 SwfdecVideoMovieInput *input);
+void		swfdec_video_movie_set_provider		(SwfdecVideoMovie *	movie,
+							 SwfdecVideoProvider *	provider);
 void		swfdec_video_movie_clear	      	(SwfdecVideoMovie *	movie);
-/* API for SwfdecVideoMovieInput */
-void		swfdec_video_movie_new_image		(SwfdecVideoMovie *	movie);
 
 G_END_DECLS
 #endif
diff --git a/swfdec/swfdec_video_movie_as.c b/swfdec/swfdec_video_movie_as.c
index ecc7a28..4ac2b2f 100644
--- a/swfdec/swfdec_video_movie_as.c
+++ b/swfdec/swfdec_video_movie_as.c
@@ -36,17 +36,17 @@ swfdec_video_attach_video (SwfdecAsContext *cx, SwfdecAsObject *object,
     guint argc, SwfdecAsValue *argv, SwfdecAsValue *rval)
 {
   SwfdecVideoMovie *video;
-  SwfdecAsObject *stream;
+  SwfdecAsObject *o;
 
-  SWFDEC_AS_CHECK (SWFDEC_TYPE_VIDEO_MOVIE, &video, "O", &stream);
+  SWFDEC_AS_CHECK (SWFDEC_TYPE_VIDEO_MOVIE, &video, "O", &o);
 
-  if (stream == NULL || !SWFDEC_IS_NET_STREAM (stream)) {
+  if (!SWFDEC_IS_VIDEO_PROVIDER (o)) {
     SWFDEC_WARNING ("calling attachVideo without a NetStream object");
-    swfdec_video_movie_set_input (video, NULL);
+    swfdec_video_movie_set_provider (video, NULL);
     return;
   }
 
-  swfdec_video_movie_set_input (video, &SWFDEC_NET_STREAM (stream)->input);
+  swfdec_video_movie_set_provider (video, SWFDEC_VIDEO_PROVIDER (o));
 }
 
 SWFDEC_AS_NATIVE (667, 2, swfdec_video_clear)
diff --git a/swfdec/swfdec_video_provider.c b/swfdec/swfdec_video_provider.c
new file mode 100644
index 0000000..4006648
--- /dev/null
+++ b/swfdec/swfdec_video_provider.c
@@ -0,0 +1,110 @@
+/* Swfdec
+ * Copyright (C) 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_provider.h"
+#include "swfdec_debug.h"
+#include "swfdec_loader_internal.h"
+#include "swfdec_player_internal.h"
+
+enum {
+  NEW_IMAGE,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+static void
+swfdec_video_provider_base_init (gpointer klass)
+{
+  static gboolean initialized = FALSE;
+
+  if (G_UNLIKELY (!initialized)) {
+    initialized = TRUE;
+
+    signals[NEW_IMAGE] = g_signal_new ("new-image", G_TYPE_FROM_CLASS (klass),
+	G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
+	G_TYPE_NONE, 0);
+  }
+}
+
+GType
+swfdec_video_provider_get_type (void)
+{
+  static GType video_provider_type = 0;
+  
+  if (!video_provider_type) {
+    static const GTypeInfo video_provider_info = {
+      sizeof (SwfdecVideoProviderInterface),
+      swfdec_video_provider_base_init,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      0,
+      0,
+      NULL,
+    };
+    
+    video_provider_type = g_type_register_static (G_TYPE_INTERFACE,
+        "SwfdecVideoProvider", &video_provider_info, 0);
+    g_type_interface_add_prerequisite (video_provider_type, G_TYPE_OBJECT);
+  }
+  
+  return video_provider_type;
+}
+
+cairo_surface_t *
+swfdec_video_provider_get_image (SwfdecVideoProvider *provider, 
+    SwfdecRenderer *renderer, guint *width, guint *height)
+{
+  SwfdecVideoProviderInterface *iface;
+  
+  g_return_val_if_fail (SWFDEC_IS_VIDEO_PROVIDER (provider), NULL);
+  g_return_val_if_fail (SWFDEC_IS_RENDERER (renderer), NULL);
+  g_return_val_if_fail (width != NULL, NULL);
+  g_return_val_if_fail (height != NULL, NULL);
+
+  iface = SWFDEC_VIDEO_PROVIDER_GET_INTERFACE (provider);
+  g_assert (iface->get_image != NULL);
+  return iface->get_image (provider, renderer, width, height);
+}
+
+void
+swfdec_video_provider_set_ratio (SwfdecVideoProvider *provider, guint ratio)
+{
+  SwfdecVideoProviderInterface *iface;
+  
+  g_return_if_fail (SWFDEC_IS_VIDEO_PROVIDER (provider));
+
+  iface = SWFDEC_VIDEO_PROVIDER_GET_INTERFACE (provider);
+  if (iface->set_ratio != NULL)
+    iface->set_ratio (provider, ratio);
+}
+
+void
+swfdec_video_provider_new_image	(SwfdecVideoProvider *provider)
+{
+  g_return_if_fail (SWFDEC_IS_VIDEO_PROVIDER (provider));
+  g_signal_emit (provider, signals[NEW_IMAGE], 0);
+}
+
diff --git a/swfdec/swfdec_video_provider.h b/swfdec/swfdec_video_provider.h
new file mode 100644
index 0000000..b59f437
--- /dev/null
+++ b/swfdec/swfdec_video_provider.h
@@ -0,0 +1,64 @@
+/* Swfdec
+ * Copyright (C) 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_PROVIDER_H__
+#define __SWFDEC_VIDEO_PROVIDER_H__
+
+#include <swfdec/swfdec.h>
+
+G_BEGIN_DECLS
+
+
+#define SWFDEC_TYPE_VIDEO_PROVIDER                (swfdec_video_provider_get_type ())
+#define SWFDEC_VIDEO_PROVIDER(obj)                (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_PROVIDER, SwfdecVideoProvider))
+#define SWFDEC_IS_VIDEO_PROVIDER(obj)             (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_PROVIDER))
+#define SWFDEC_VIDEO_PROVIDER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), SWFDEC_TYPE_VIDEO_PROVIDER, SwfdecVideoProviderInterface))
+
+typedef struct _SwfdecVideoProvider SwfdecVideoProvider; /* dummy object */
+typedef struct _SwfdecVideoProviderInterface SwfdecVideoProviderInterface;
+
+struct _SwfdecVideoProviderInterface {
+  GTypeInterface	interface;
+
+  /* called when movie ratio changed */
+  void			(* set_ratio)				(SwfdecVideoProvider *	provider,
+								 guint			ratio);
+  /* called to request the current image */
+  cairo_surface_t *	(* get_image)				(SwfdecVideoProvider *	provider,
+								 SwfdecRenderer *	renderer,
+								 guint *		width,
+								 guint *		height);
+};
+
+GType			swfdec_video_provider_get_type		(void) G_GNUC_CONST;
+
+cairo_surface_t *     	swfdec_video_provider_get_image		(SwfdecVideoProvider *	provider,
+								 SwfdecRenderer *	renderer,
+								 guint *		width,
+								 guint *		height);
+void			swfdec_video_provider_set_ratio		(SwfdecVideoProvider *	provider,
+								 guint			ratio);
+
+/* for subclasses */
+void			swfdec_video_provider_new_image		(SwfdecVideoProvider *	provider);
+
+
+G_END_DECLS
+
+#endif /* __SWFDEC_VIDEO_PROVIDER_H__ */
diff --git a/swfdec/swfdec_video_video_provider.c b/swfdec/swfdec_video_video_provider.c
new file mode 100644
index 0000000..0b785c2
--- /dev/null
+++ b/swfdec/swfdec_video_video_provider.c
@@ -0,0 +1,189 @@
+/* Swfdec
+ * Copyright (C) 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 <stdlib.h>
+
+#include "swfdec_video_video_provider.h"
+#include "swfdec_cached_video.h"
+#include "swfdec_debug.h"
+#include "swfdec_font.h"
+#include "swfdec_player_internal.h"
+#include "swfdec_renderer_internal.h"
+#include "swfdec_swf_decoder.h"
+
+static SwfdecVideoFrame *
+swfdec_video_video_provider_find_frame (SwfdecVideo *video, guint frame)
+{
+  SwfdecVideoFrame *cur;
+  guint i;
+
+  for (i = 0; i < video->images->len; i++) {
+    cur = &g_array_index (video->images, SwfdecVideoFrame, i);
+    if (cur->frame > frame)
+      break;
+  }
+
+  return &g_array_index (video->images, SwfdecVideoFrame, MAX (1, i) - 1);
+}
+
+/*** VIDEO PROVIDER INTERFACE ***/
+
+static void
+swfdec_video_video_provider_set_ratio (SwfdecVideoProvider *prov, guint ratio)
+{
+  SwfdecVideoVideoProvider *provider = SWFDEC_VIDEO_VIDEO_PROVIDER (prov);
+  SwfdecVideoFrame *frame;
+
+  if (ratio >= provider->video->n_frames) {
+    SWFDEC_ERROR ("ratio %u too big: only %u frames", ratio, provider->video->n_frames);
+    ratio = provider->video->n_frames - 1;
+  }
+  
+  frame = swfdec_video_video_provider_find_frame (provider->video, ratio);
+  ratio = frame->frame;
+
+  if (ratio != provider->current_frame) {
+    provider->current_frame = ratio;
+    swfdec_video_provider_new_image (prov);
+  }
+}
+
+static gboolean
+swfdec_cached_video_compare (SwfdecCached *cached, gpointer data)
+{
+  return swfdec_cached_video_get_frame (SWFDEC_CACHED_VIDEO (cached)) == GPOINTER_TO_UINT (data);
+}
+
+static cairo_surface_t *
+swfdec_video_video_provider_get_image (SwfdecVideoProvider *prov,
+    SwfdecRenderer *renderer, guint *width, guint *height)
+{
+  SwfdecVideoVideoProvider *provider = SWFDEC_VIDEO_VIDEO_PROVIDER (prov);
+  SwfdecCachedVideo *cached;
+  SwfdecVideoFrame *frame;
+  cairo_surface_t *surface;
+  guint w, h;
+
+  if (provider->current_frame == 0)
+    return NULL;
+
+  cached = SWFDEC_CACHED_VIDEO (swfdec_renderer_get_cache (renderer, provider->video, 
+	swfdec_cached_video_compare, GUINT_TO_POINTER (provider->current_frame)));
+  if (cached != NULL) {
+    swfdec_cached_use (SWFDEC_CACHED (cached));
+    swfdec_cached_video_get_size (cached, width, height);
+    return swfdec_cached_video_get_surface (cached);
+  }
+
+  if (provider->decoder == NULL || provider->current_frame < provider->decoder_frame) {
+    if (provider->decoder != NULL) {
+      swfdec_video_decoder_free (provider->decoder);
+    }
+    provider->decoder = swfdec_video_decoder_new (provider->video->format);
+    if (provider->decoder == NULL)
+      return NULL;
+    frame = &g_array_index (provider->video->images, SwfdecVideoFrame, 0);
+    g_assert (frame->frame <= provider->current_frame);
+  } else {
+    frame = swfdec_video_video_provider_find_frame (provider->video, provider->decoder_frame);
+    g_assert (provider->decoder_frame == frame->frame);
+    frame++;
+  }
+
+  for (;;) {
+    g_assert (frame->buffer);
+    surface = swfdec_video_decoder_decode (provider->decoder, renderer, 
+	frame->buffer, &w, &h);
+    provider->decoder_frame = provider->current_frame;
+    if (surface == NULL)
+      return NULL;
+    if (frame->frame == provider->current_frame)
+      break;
+    frame++;
+    cairo_surface_destroy (surface);
+  };
+
+  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);
+  swfdec_renderer_add_cache (renderer, FALSE, provider->video, SWFDEC_CACHED (cached));
+  g_object_unref (cached);
+
+  *width = w;
+  *height = h;
+
+  return surface;
+}
+
+static void
+swfdec_video_video_provider_video_provider_init (SwfdecVideoProviderInterface *iface)
+{
+  iface->get_image = swfdec_video_video_provider_get_image;
+  iface->set_ratio = swfdec_video_video_provider_set_ratio;
+}
+
+/*** SWFDEC_VIDEO_VIDEO_PROVIDER ***/
+
+G_DEFINE_TYPE_WITH_CODE (SwfdecVideoVideoProvider, swfdec_video_video_provider, G_TYPE_OBJECT,
+    G_IMPLEMENT_INTERFACE (SWFDEC_TYPE_VIDEO_PROVIDER, swfdec_video_video_provider_video_provider_init))
+
+static void
+swfdec_video_video_provider_dispose (GObject *object)
+{
+  SwfdecVideoVideoProvider * provider = SWFDEC_VIDEO_VIDEO_PROVIDER (object);
+
+  if (provider->decoder != NULL) {
+    swfdec_video_decoder_free (provider->decoder);
+    provider->decoder = NULL;
+  }
+  g_object_unref (provider->video);
+
+  G_OBJECT_CLASS (swfdec_video_video_provider_parent_class)->dispose (object);
+}
+
+static void
+swfdec_video_video_provider_class_init (SwfdecVideoVideoProviderClass * g_class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (g_class);
+
+  object_class->dispose = swfdec_video_video_provider_dispose;
+}
+
+static void
+swfdec_video_video_provider_init (SwfdecVideoVideoProvider * video)
+{
+}
+
+SwfdecVideoProvider *
+swfdec_video_video_provider_new (SwfdecVideo *video)
+{
+  SwfdecVideoVideoProvider *ret;
+
+  g_return_val_if_fail (SWFDEC_IS_VIDEO (video), NULL);
+
+  ret = g_object_new (SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER, NULL);
+  ret->video = g_object_ref (video);
+
+  return SWFDEC_VIDEO_PROVIDER (ret);
+}
+
diff --git a/swfdec/swfdec_video_video_provider.h b/swfdec/swfdec_video_video_provider.h
new file mode 100644
index 0000000..d4804ba
--- /dev/null
+++ b/swfdec/swfdec_video_video_provider.h
@@ -0,0 +1,58 @@
+/* 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
+ */
+
+#ifndef _SWFDEC_VIDEO_VIDEO_PROVIDER_H_
+#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_provider.h>
+
+G_BEGIN_DECLS
+
+typedef struct _SwfdecVideoVideoProvider SwfdecVideoVideoProvider;
+typedef struct _SwfdecVideoVideoProviderClass SwfdecVideoVideoProviderClass;
+
+#define SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER                    (swfdec_video_video_provider_get_type())
+#define SWFDEC_IS_VIDEO_VIDEO_PROVIDER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER))
+#define SWFDEC_IS_VIDEO_VIDEO_PROVIDER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER))
+#define SWFDEC_VIDEO_VIDEO_PROVIDER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER, SwfdecVideoVideoProvider))
+#define SWFDEC_VIDEO_VIDEO_PROVIDER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), SWFDEC_TYPE_VIDEO_VIDEO_PROVIDER, SwfdecVideoVideoProviderClass))
+
+struct _SwfdecVideoVideoProvider {
+  GObject			object;
+
+  SwfdecVideo *			video;		/* video we play back */
+  guint				current_frame;	/* id of the frame we should have decoded */
+  guint				decoder_frame;	/* id of the last frame the decoder has decoded */
+  SwfdecVideoDecoder *		decoder;	/* the current decoder */
+};
+
+struct _SwfdecVideoVideoProviderClass {
+  GObjectClass			object_class;
+};
+
+GType			swfdec_video_video_provider_get_type	(void);
+
+SwfdecVideoProvider *	swfdec_video_video_provider_new		(SwfdecVideo *	video);
+
+
+G_END_DECLS
+#endif


More information about the Swfdec-commits mailing list