[byzanz] Create a separate encoder class for encoding



commit 6c653b4949eb5d3dc1020d57ce7272fe1570329f
Author: Benjamin Otte <otte gnome org>
Date:   Wed Aug 26 11:35:11 2009 +0200

    Create a separate encoder class for encoding

 src/Makefile.am        |    4 +
 src/byzanzapplet.c     |   16 +--
 src/byzanzencoder.c    |  317 +++++++++++++++++++++++++++++++++++++
 src/byzanzencoder.h    |   92 +++++++++++
 src/byzanzencodergif.c |  243 +++++++++++++++++++++++++++++
 src/byzanzencodergif.h |   58 +++++++
 src/byzanzsession.c    |  403 +++++++++++++-----------------------------------
 src/byzanzsession.h    |   27 ++--
 8 files changed, 837 insertions(+), 323 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index ba157ae..210cdd8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,8 @@ BUILT_SOURCES = \
 	byzanzmarshal.c
 
 noinst_HEADERS = \
+	byzanzencoder.h \
+	byzanzencodergif.h \
 	byzanzlayer.h \
 	byzanzlayercursor.h \
 	byzanzlayerwindow.h \
@@ -19,6 +21,8 @@ noinst_HEADERS = \
 	screenshot-utils.h
 
 libbyzanz_la_SOURCES = \
+	byzanzencoder.c \
+	byzanzencodergif.c \
 	byzanzlayer.c \
 	byzanzlayercursor.c \
 	byzanzlayerwindow.c \
diff --git a/src/byzanzapplet.c b/src/byzanzapplet.c
index 8c20cde..9bba3a5 100644
--- a/src/byzanzapplet.c
+++ b/src/byzanzapplet.c
@@ -47,7 +47,6 @@ typedef struct {
   GtkTooltips *		tooltips;	/* our tooltips */
   
   ByzanzSession *	rec;		/* the session (if recording) */
-  GFile *               destination;    /* file we are recording to */
 
   /* config */
   int			method;		/* recording method that was set */
@@ -160,7 +159,6 @@ panel_applet_start_response (GtkWidget *dialog, int response, AppletPrivate *pri
   GdkWindow *window;
   GdkRectangle area;
 
-  g_assert (priv->destination == NULL);
   file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
   if (file == NULL)
     goto out;
@@ -171,12 +169,10 @@ panel_applet_start_response (GtkWidget *dialog, int response, AppletPrivate *pri
       panel_applet_gconf_set_string (priv->applet, "save_filename", uri, NULL);
       g_free (uri);
       byzanz_applet_set_default_method (priv, i);
-      priv->destination = file;
-      file = NULL;
       break;
     }
   }
-  if (priv->destination == NULL)
+  if (i >= byzanz_select_get_method_count ())
     goto out;
 
   gtk_widget_destroy (dialog);
@@ -187,20 +183,20 @@ panel_applet_start_response (GtkWidget *dialog, int response, AppletPrivate *pri
   if (window == NULL)
     goto out2;
 
-  priv->rec = byzanz_session_new (priv->destination, window, &area, TRUE, TRUE);
+  priv->rec = byzanz_session_new (file, window, &area, TRUE, TRUE);
   g_signal_connect_swapped (priv->rec, "notify", G_CALLBACK (byzanz_applet_session_notify), priv);
   
   byzanz_session_start (priv->rec);
+  byzanz_applet_session_notify (priv);
+  g_object_unref (file);
   return;
 
 out:
   gtk_widget_destroy (dialog);
   priv->dialog = NULL;
 out2:
-  if (priv->destination) {
-    g_object_unref (priv->destination);
-    priv->destination = NULL;
-  }
+  if (file)
+    g_object_unref (file);
   if (priv->rec)
     byzanz_applet_session_notify (priv);
   else
diff --git a/src/byzanzencoder.c b/src/byzanzencoder.c
new file mode 100644
index 0000000..a25036d
--- /dev/null
+++ b/src/byzanzencoder.c
@@ -0,0 +1,317 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzencoder.h"
+
+/* all the encoders */
+#include "byzanzencodergif.h"
+
+typedef struct _ByzanzEncoderJob ByzanzEncoderJob;
+struct _ByzanzEncoderJob {
+  GTimeVal		tv;		/* time this job was enqueued */
+  cairo_surface_t *	surface;	/* image to process */
+  GdkRegion *		region;		/* relevant region of image */
+};
+
+static void
+byzanz_encoder_job_free (ByzanzEncoderJob *job)
+{
+  if (job->surface)
+    cairo_surface_destroy (job->surface);
+  if (job->region)
+    gdk_region_destroy (job->region);
+
+  g_slice_free (ByzanzEncoderJob, job);
+}
+
+static gboolean
+byzanz_encoder_finished (gpointer data)
+{
+  ByzanzEncoder *encoder = data;
+  ByzanzEncoderJob *job;
+
+  encoder->error = g_thread_join (encoder->thread);
+  encoder->thread = NULL;
+
+  while ((job = g_async_queue_try_pop (encoder->jobs)))
+    byzanz_encoder_job_free (job);
+
+  g_object_freeze_notify (G_OBJECT (encoder));
+  g_object_notify (G_OBJECT (encoder), "running");
+  if (encoder->error)
+    g_object_notify (G_OBJECT (encoder), "error");
+  g_object_thaw_notify (G_OBJECT (encoder));
+
+  return FALSE;
+}
+
+/*** INSIDE THREAD ***/
+
+static gpointer
+byzanz_encoder_run (gpointer enc)
+{
+  ByzanzEncoder *encoder = enc;
+  ByzanzEncoderClass *klass = BYZANZ_ENCODER_GET_CLASS (enc);
+  GError *error = NULL;
+
+  if (!klass->setup (encoder, encoder->output_stream, encoder->width, encoder->height,
+        encoder->cancellable, &error))
+    return error;
+
+  do {
+    ByzanzEncoderJob *job = g_async_queue_pop (encoder->jobs);
+
+    /* quit */
+    if (job->surface == NULL) {
+      if (klass->close (encoder, encoder->output_stream, &job->tv,
+              encoder->cancellable, &error))
+        g_output_stream_close (encoder->output_stream, encoder->cancellable, &error);
+      byzanz_encoder_job_free (job);
+      break;
+    }
+
+    /* decode */
+    klass->process (encoder, encoder->output_stream,
+          job->surface, job->region, &job->tv,
+          encoder->cancellable, &error);
+    byzanz_encoder_job_free (job);
+  } while (error == NULL);
+
+
+  g_idle_add_full (G_PRIORITY_DEFAULT, byzanz_encoder_finished, enc, NULL);
+  return error;
+}
+
+/*** OUTSIDE THREAD ***/
+
+enum {
+  PROP_0,
+  PROP_OUTPUT,
+  PROP_CANCELLABLE,
+  PROP_WIDTH,
+  PROP_HEIGHT,
+  PROP_ERROR,
+  PROP_RUNNING
+};
+
+G_DEFINE_ABSTRACT_TYPE (ByzanzEncoder, byzanz_encoder, G_TYPE_OBJECT)
+
+static void
+byzanz_encoder_get_property (GObject *object, guint param_id, GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzEncoder *encoder = BYZANZ_ENCODER (object);
+
+  switch (param_id) {
+    case PROP_OUTPUT:
+      g_value_set_object (value, encoder->output_stream);
+      break;
+    case PROP_CANCELLABLE:
+      g_value_set_object (value, encoder->cancellable);
+      break;
+    case PROP_ERROR:
+      g_value_set_pointer (value, encoder->error);
+      break;
+    case PROP_RUNNING:
+      g_value_set_boolean (value, encoder->thread != NULL);
+      break;
+    case PROP_WIDTH:
+      g_value_set_uint (value, encoder->width);
+      break;
+    case PROP_HEIGHT:
+      g_value_set_uint (value, encoder->height);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_encoder_set_property (GObject *object, guint param_id, const GValue *value, 
+    GParamSpec * pspec)
+{
+  ByzanzEncoder *encoder = BYZANZ_ENCODER (object);
+
+  switch (param_id) {
+    case PROP_OUTPUT:
+      encoder->output_stream = g_value_dup_object (value);
+      g_assert (encoder->output_stream != NULL);
+      break;
+    case PROP_CANCELLABLE:
+      encoder->cancellable = g_value_dup_object (value);
+      break;
+    case PROP_WIDTH:
+      encoder->width = g_value_get_uint (value);
+      break;
+    case PROP_HEIGHT:
+      encoder->height = g_value_get_uint (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+      break;
+  }
+}
+
+static void
+byzanz_encoder_finalize (GObject *object)
+{
+  ByzanzEncoder *encoder = BYZANZ_ENCODER (object);
+
+  /* FIXME: handle the case where the thread is still alive */
+  g_assert (encoder->thread == NULL);
+
+  g_object_unref (encoder->output_stream);
+  if (encoder->cancellable)
+    g_object_unref (encoder->cancellable);
+  if (encoder->error)
+    g_error_free (encoder->error);
+
+  g_async_queue_unref (encoder->jobs);
+
+  G_OBJECT_CLASS (byzanz_encoder_parent_class)->finalize (object);
+}
+
+static void
+byzanz_encoder_constructed (GObject *object)
+{
+  ByzanzEncoder *encoder = BYZANZ_ENCODER (object);
+
+  encoder->thread = g_thread_create (byzanz_encoder_run, encoder, 
+      TRUE, &encoder->error);
+
+  if (G_OBJECT_CLASS (byzanz_encoder_parent_class)->constructed)
+    G_OBJECT_CLASS (byzanz_encoder_parent_class)->constructed (object);
+}
+
+static void
+byzanz_encoder_class_init (ByzanzEncoderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = byzanz_encoder_get_property;
+  object_class->set_property = byzanz_encoder_set_property;
+  object_class->finalize = byzanz_encoder_finalize;
+  object_class->constructed = byzanz_encoder_constructed;
+
+  g_object_class_install_property (object_class, PROP_OUTPUT,
+      g_param_spec_object ("output", "output", "stream to write data to",
+	  G_TYPE_OUTPUT_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_CANCELLABLE,
+      g_param_spec_object ("cancellable", "cancellable", "cancellable for stopping the thread",
+	  G_TYPE_CANCELLABLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_ERROR,
+      g_param_spec_pointer ("error", "error", "error that happened on the thread",
+	  G_PARAM_READABLE));
+  g_object_class_install_property (object_class, PROP_RUNNING,
+      g_param_spec_boolean ("running", "running", "TRUE while the encoding thread is running",
+	  TRUE, G_PARAM_READABLE));
+  g_object_class_install_property (object_class, PROP_WIDTH,
+      g_param_spec_uint ("width", "width", "width of the stream that gets encoded",
+	  1, G_MAXUINT, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_HEIGHT,
+      g_param_spec_uint ("height", "height", "height of the stream that gets encoded",
+	  1, G_MAXUINT, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+byzanz_encoder_init (ByzanzEncoder *encoder)
+{
+  encoder->jobs = g_async_queue_new ();
+}
+
+ByzanzEncoder *
+byzanz_encoder_new (GOutputStream *stream, guint width, guint height, GCancellable *cancellable)
+{
+  ByzanzEncoder *encoder;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), NULL);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+  g_return_val_if_fail (width > 0, NULL);
+  g_return_val_if_fail (height > 0, NULL);
+
+  encoder = g_object_new (BYZANZ_TYPE_ENCODER_GIF, "output", stream,
+      "cancellable", cancellable, "width", width, "height", height, NULL);
+
+  return encoder;
+}
+
+void
+byzanz_encoder_process (ByzanzEncoder *	 encoder,
+		        cairo_surface_t *surface,
+			const GdkRegion *region,
+			const GTimeVal * total_elapsed)
+{
+  ByzanzEncoderJob *job;
+
+  g_return_if_fail (BYZANZ_IS_ENCODER (encoder));
+  g_return_if_fail (surface != NULL);
+  g_return_if_fail (region != NULL);
+  g_return_if_fail (total_elapsed != NULL);
+
+  if (encoder->error)
+    return;
+
+  job = g_slice_new (ByzanzEncoderJob);
+  job->surface = cairo_surface_reference (surface);
+  job->region = gdk_region_copy (region);
+  job->tv = *total_elapsed;
+
+  g_async_queue_push (encoder->jobs, job);
+}
+
+void
+byzanz_encoder_close (ByzanzEncoder *encoder,
+		      const GTimeVal * total_elapsed)
+{
+  ByzanzEncoderJob *job;
+
+  g_return_if_fail (BYZANZ_IS_ENCODER (encoder));
+  g_return_if_fail (total_elapsed != NULL);
+
+  if (encoder->error)
+    return;
+
+  job = g_slice_new (ByzanzEncoderJob);
+  job->surface = NULL;
+  job->region = NULL;
+  job->tv = *total_elapsed;
+
+  g_async_queue_push (encoder->jobs, job);
+}
+
+gboolean
+byzanz_encoder_is_running (ByzanzEncoder *encoder)
+{
+  g_return_val_if_fail (BYZANZ_IS_ENCODER (encoder), FALSE);
+
+  return encoder->thread != NULL;
+}
+
+const GError *
+byzanz_encoder_get_error (ByzanzEncoder *encoder)
+{
+  g_return_val_if_fail (BYZANZ_IS_ENCODER (encoder), FALSE);
+
+  return encoder->error;
+}
diff --git a/src/byzanzencoder.h b/src/byzanzencoder.h
new file mode 100644
index 0000000..2b323d2
--- /dev/null
+++ b/src/byzanzencoder.h
@@ -0,0 +1,92 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gdk/gdk.h>
+#include <cairo.h>
+
+#ifndef __HAVE_BYZANZ_ENCODER_H__
+#define __HAVE_BYZANZ_ENCODER_H__
+
+typedef struct _ByzanzEncoder ByzanzEncoder;
+typedef struct _ByzanzEncoderClass ByzanzEncoderClass;
+
+#define BYZANZ_TYPE_ENCODER                    (byzanz_encoder_get_type())
+#define BYZANZ_IS_ENCODER(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_ENCODER))
+#define BYZANZ_IS_ENCODER_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_ENCODER))
+#define BYZANZ_ENCODER(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_ENCODER, ByzanzEncoder))
+#define BYZANZ_ENCODER_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_ENCODER, ByzanzEncoderClass))
+#define BYZANZ_ENCODER_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_ENCODER, ByzanzEncoderClass))
+
+struct _ByzanzEncoder {
+  GObject		object;
+  
+  /*<private >*/
+  GOutputStream *       output_stream;          /* stream we write to (passed to the vfuncs) */
+  GCancellable *        cancellable;            /* cancellable to use in thread */
+  GError *              error;                  /* NULL or the encoding error */
+  guint                 width;                  /* width of image */
+  guint                 height;                 /* height of image */
+
+  GAsyncQueue *         jobs;                   /* the stuff we still need to encode */
+  GThread *             thread;                 /* the encoding thread */
+};
+
+struct _ByzanzEncoderClass {
+  GObjectClass		object_class;
+
+  gboolean		(* setup)		(ByzanzEncoder *	encoder,
+						 GOutputStream *	stream,
+                                                 guint                  width,
+                                                 guint                  height,
+                                                 GCancellable *         cancellable,
+						 GError **		error);
+  gboolean		(* process)		(ByzanzEncoder *	encoder,
+						 GOutputStream *	stream,
+						 cairo_surface_t *	surface,
+						 const GdkRegion *	region,
+						 const GTimeVal *	total_elapsed,
+                                                 GCancellable *         cancellable,
+						 GError **		error);
+  gboolean		(* close)		(ByzanzEncoder *	encoder,
+						 GOutputStream *	stream,
+						 const GTimeVal *	total_elapsed,
+                                                 GCancellable *         cancellable,
+						 GError **		error);
+};
+
+GType		byzanz_encoder_get_type		(void) G_GNUC_CONST;
+
+ByzanzEncoder *	byzanz_encoder_new		(GOutputStream *        stream,
+                                                 guint                  width,
+                                                 guint                  height,
+                                                 GCancellable *         cancellable);
+void		byzanz_encoder_process		(ByzanzEncoder *	encoder,
+						 cairo_surface_t *	surface,
+						 const GdkRegion *	region,
+						 const GTimeVal *	total_elapsed);
+void		byzanz_encoder_close		(ByzanzEncoder *	encoder,
+						 const GTimeVal *	total_elapsed);
+
+gboolean        byzanz_encoder_is_running       (ByzanzEncoder *        encoder);
+const GError *  byzanz_encoder_get_error        (ByzanzEncoder *        encoder);
+
+
+#endif /* __HAVE_BYZANZ_ENCODER_H__ */
diff --git a/src/byzanzencodergif.c b/src/byzanzencodergif.c
new file mode 100644
index 0000000..6c82cbf
--- /dev/null
+++ b/src/byzanzencodergif.c
@@ -0,0 +1,243 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "byzanzencodergif.h"
+
+#include <string.h>
+
+#include "gifenc.h"
+
+G_DEFINE_TYPE (ByzanzEncoderGif, byzanz_encoder_gif, BYZANZ_TYPE_ENCODER)
+
+static gboolean
+byzanz_encoder_write_data (gpointer       closure,
+                           const guchar * data,
+                           gsize          len,
+                           GError **      error)
+{
+  ByzanzEncoder *encoder = closure;
+
+  return g_output_stream_write_all (encoder->output_stream, data, len,
+      NULL, encoder->cancellable, error);
+}
+
+static gboolean
+byzanz_encoder_gif_setup (ByzanzEncoder * encoder,
+                          GOutputStream * stream,
+                          guint           width,
+                          guint           height,
+                          GCancellable *  cancellable,
+                          GError **	  error)
+{
+  ByzanzEncoderGif *gif = BYZANZ_ENCODER_GIF (encoder);
+
+  gif->gifenc = gifenc_new (width, height, byzanz_encoder_write_data, encoder, NULL);
+
+  gif->image_data = g_malloc (width * height);
+  gif->cached_data = g_malloc (width * height);
+  gif->cached_tmp = g_malloc (width * height);
+  return TRUE;
+}
+
+static void
+byzanz_encoder_gif_quantize (ByzanzEncoderGif * gif,
+                             cairo_surface_t *  surface)
+{
+  GifencPalette *palette;
+
+  g_assert (!gif->has_quantized);
+
+  palette = gifenc_quantize_image (cairo_image_surface_get_data (surface),
+      cairo_image_surface_get_width (surface), cairo_image_surface_get_height (surface),
+      cairo_image_surface_get_stride (surface), TRUE, 255);
+  
+  gifenc_initialize (gif->gifenc, palette, TRUE, NULL);
+  memset (gif->image_data,
+      gifenc_palette_get_alpha_index (palette),
+      gifenc_get_width (gif->gifenc) * gifenc_get_height (gif->gifenc));
+
+  gif->has_quantized = TRUE;
+}
+
+static void
+byzanz_encoder_write_image (ByzanzEncoderGif *gif, const GTimeVal *tv)
+{
+  glong msecs;
+  guint width;
+
+  g_assert (gif->cached_data != NULL);
+  g_assert (gif->cached_area.width > 0);
+  g_assert (gif->cached_area.height > 0);
+
+  width = gifenc_get_width (gif->gifenc);
+  msecs = (tv->tv_sec - gif->cached_time.tv_sec) * 1000 + 
+	  (tv->tv_usec - gif->cached_time.tv_usec) / 1000 + 5;
+  if (msecs < 10)
+    g_printerr ("<10 msecs for a frame, can this be?\n");
+  msecs = MAX (msecs, 10);
+
+  gifenc_add_image (gif->gifenc, gif->cached_area.x, gif->cached_area.y, 
+	gif->cached_area.width, gif->cached_area.height, msecs,
+	gif->cached_data + width * gif->cached_area.y + gif->cached_area.x,
+        width, NULL);
+
+  gif->cached_time = *tv;
+}
+
+static gboolean
+byzanz_encoder_gif_encode_image (ByzanzEncoderGif * gif,
+                                 cairo_surface_t *  surface,
+                                 const GdkRegion *  region,
+                                 GdkRectangle *     area_out)
+{
+  GdkRectangle extents, area, *rects;
+  guint8 transparent;
+  guint i, n_rects, stride, width;
+
+  gdk_region_get_clipbox (region, &extents);
+  transparent = gifenc_palette_get_alpha_index (gif->gifenc->palette);
+  stride = cairo_image_surface_get_stride (surface);
+  width = gifenc_get_width (gif->gifenc);
+
+  /* clear area */
+  /* FIXME: only do this in parts not captured by region */
+  for (i = extents.y; i < (guint) (extents.y + extents.height); i++) {
+    memset (gif->cached_tmp + width * i + extents.x, transparent, extents.width);
+  }
+
+  /* render changed parts */
+  gdk_region_get_rectangles (region, &rects, (int *) &n_rects);
+  memset (area_out, 0, sizeof (GdkRectangle));
+  for (i = 0; i < n_rects; i++) {
+    if (gifenc_dither_rgb_with_full_image (
+          gif->cached_tmp + width * rects[i].y + rects[i].x, width,
+	  gif->image_data + width * rects[i].y + rects[i].x, width, 
+	  gif->gifenc->palette, 
+          cairo_image_surface_get_data (surface) + (rects[i].x - extents.x) * 4
+              + (rects[i].y - extents.y) * stride,
+          rects[i].width, rects[i].height, stride, &area)) {
+      area.x += rects[i].x;
+      area.y += rects[i].y;
+      if (area_out->width > 0 && area_out->height > 0)
+        gdk_rectangle_union (area_out, &area, area_out);
+      else
+        *area_out = area;
+    }
+  }
+  g_free (rects);
+
+  return area_out->width > 0 && area_out->height > 0;
+}
+
+static void
+byzanz_encoder_swap_image (ByzanzEncoderGif * gif,
+                           GdkRectangle *     area)
+{
+  guint8 *swap;
+
+  swap = gif->cached_data;
+  gif->cached_data = gif->cached_tmp;
+  gif->cached_tmp = swap;
+  gif->cached_area = *area;
+}
+
+static gboolean
+byzanz_encoder_gif_process (ByzanzEncoder *   encoder,
+                            GOutputStream *   stream,
+                            cairo_surface_t * surface,
+                            const GdkRegion * region,
+                            const GTimeVal *  total_elapsed,
+                            GCancellable *    cancellable,
+                            GError **	      error)
+{
+  ByzanzEncoderGif *gif = BYZANZ_ENCODER_GIF (encoder);
+  GdkRectangle area;
+
+  if (!gif->has_quantized) {
+    byzanz_encoder_gif_quantize (gif, surface);
+    gif->cached_time = *total_elapsed;
+    if (!byzanz_encoder_gif_encode_image (gif, surface, region, &area)) {
+      g_assert_not_reached ();
+    }
+    byzanz_encoder_swap_image (gif, &area);
+  } else {
+    if (byzanz_encoder_gif_encode_image (gif, surface, region, &area)) {
+      byzanz_encoder_write_image (gif, total_elapsed);
+      byzanz_encoder_swap_image (gif, &area);
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+byzanz_encoder_gif_close (ByzanzEncoder *  encoder,
+                          GOutputStream *  stream,
+                          const GTimeVal * total_elapsed,
+                          GCancellable *   cancellable,
+                          GError **	   error)
+{
+  ByzanzEncoderGif *gif = BYZANZ_ENCODER_GIF (encoder);
+
+  if (!gif->has_quantized) {
+    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No image to encode.");
+    return FALSE;
+  }
+
+  byzanz_encoder_write_image (gif, total_elapsed);
+  gifenc_close (gif->gifenc, NULL);
+  return TRUE;
+}
+
+static void
+byzanz_encoder_gif_finalize (GObject *object)
+{
+  ByzanzEncoderGif *gif = BYZANZ_ENCODER_GIF (object);
+
+  g_free (gif->image_data);
+  g_free (gif->cached_data);
+  g_free (gif->cached_tmp);
+  if (gif->gifenc)
+    gifenc_free (gif->gifenc);
+
+  G_OBJECT_CLASS (byzanz_encoder_gif_parent_class)->finalize (object);
+}
+
+static void
+byzanz_encoder_gif_class_init (ByzanzEncoderGifClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ByzanzEncoderClass *encoder_class = BYZANZ_ENCODER_CLASS (klass);
+
+  object_class->finalize = byzanz_encoder_gif_finalize;
+
+  encoder_class->setup = byzanz_encoder_gif_setup;
+  encoder_class->process = byzanz_encoder_gif_process;
+  encoder_class->close = byzanz_encoder_gif_close;
+}
+
+static void
+byzanz_encoder_gif_init (ByzanzEncoderGif *encoder_gif)
+{
+}
+
diff --git a/src/byzanzencodergif.h b/src/byzanzencodergif.h
new file mode 100644
index 0000000..ac218c9
--- /dev/null
+++ b/src/byzanzencodergif.h
@@ -0,0 +1,58 @@
+/* desktop session recorder
+ * Copyright (C) 2009 Benjamin Otte <otte gnome org
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "byzanzencoder.h"
+#include "gifenc.h"
+
+#ifndef __HAVE_BYZANZ_ENCODER_GIF_H__
+#define __HAVE_BYZANZ_ENCODER_GIF_H__
+
+typedef struct _ByzanzEncoderGif ByzanzEncoderGif;
+typedef struct _ByzanzEncoderGifClass ByzanzEncoderGifClass;
+
+#define BYZANZ_TYPE_ENCODER_GIF                    (byzanz_encoder_gif_get_type())
+#define BYZANZ_IS_ENCODER_GIF(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_ENCODER_GIF))
+#define BYZANZ_IS_ENCODER_GIF_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_ENCODER_GIF))
+#define BYZANZ_ENCODER_GIF(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_ENCODER_GIF, ByzanzEncoderGif))
+#define BYZANZ_ENCODER_GIF_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_ENCODER_GIF, ByzanzEncoderGifClass))
+#define BYZANZ_ENCODER_GIF_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_ENCODER_GIF, ByzanzEncoderGifClass))
+
+struct _ByzanzEncoderGif {
+  ByzanzEncoder         encoder;
+
+  Gifenc *		gifenc;		/* encoder used to encode the image */
+
+  gboolean              has_quantized;  /* qantization has happened already */
+  guint8 *              image_data;     /* width * height of encoded image */
+
+  GdkRectangle          cached_area;    /* area that is saved in cached_data */
+  guint8 *              cached_data;    /* cached_area.{width x height} sized area of image */
+  GTimeVal              cached_time;    /* timestamp the cached image corresponds to */
+
+  guint8 *		cached_tmp;	/* temporary data to swap cached_data with */
+};
+
+struct _ByzanzEncoderGifClass {
+  ByzanzEncoderClass    encoder_class;
+};
+
+GType		byzanz_encoder_gif_get_type		(void) G_GNUC_CONST;
+
+
+#endif /* __HAVE_BYZANZ_ENCODER_GIF_H__ */
diff --git a/src/byzanzsession.c b/src/byzanzsession.c
index 2d4010e..28e1296 100644
--- a/src/byzanzsession.c
+++ b/src/byzanzsession.c
@@ -37,222 +37,8 @@
 #include <X11/extensions/Xdamage.h>
 #include <X11/extensions/Xfixes.h>
 
+#include "byzanzencoder.h"
 #include "byzanzrecorder.h"
-#include "gifenc.h"
-
-typedef enum {
-  SESSION_JOB_QUIT,
-  SESSION_JOB_ENCODE,
-} SessionJobType;
-
-typedef struct {
-  SessionJobType	type;		/* type of job */
-  GTimeVal		tv;		/* time this job was enqueued */
-  cairo_surface_t *	image;		/* image to process */
-  GdkRegion *		region;		/* relevant region of image */
-} SessionJob;
-
-/*** JOB FUNCTIONS ***/
-
-static void
-session_job_free (SessionJob *job)
-{
-  if (job->image)
-    cairo_surface_destroy (job->image);
-  if (job->region)
-    gdk_region_destroy (job->region);
-
-  g_slice_free (SessionJob, job);
-}
-
-/* UGH: This function takes ownership of region, but only if a job could be created */
-static SessionJob *
-session_job_new (ByzanzSession *rec, SessionJobType type, cairo_surface_t *surface,
-    const GTimeVal *tv, const GdkRegion *region)
-{
-  SessionJob *job;
-
-  job = g_slice_new0 (SessionJob);
-  
-  if (tv)
-    job->tv = *tv;
-  job->type = type;
-  if (region)
-    job->region = gdk_region_copy (region);
-  if (surface)
-    job->image = cairo_surface_reference (surface);
-
-  return job;
-}
-
-/*** THREAD FUNCTIONS ***/
-
-static gboolean
-byzanz_session_dither_region (ByzanzSession *rec, GdkRegion *region, cairo_surface_t *surface)
-{
-  GdkRectangle *rects;
-  GdkRegion *rev;
-  int i, line, nrects, xoffset, yoffset;
-  guint8 transparent;
-  guint width, stride;
-  guint8 *mem;
-  GdkRectangle area;
-  double xod, yod;
-  
-  width = gifenc_get_width (rec->gifenc);
-  transparent = gifenc_palette_get_alpha_index (rec->gifenc->palette);
-  gdk_region_get_clipbox (region, &rec->relevant_data);
-  /* dither changed pixels */
-  gdk_region_get_rectangles (region, &rects, &nrects);
-  rev = gdk_region_new ();
-  stride = cairo_image_surface_get_stride (surface);
-  cairo_surface_get_device_offset (surface, &xod, &yod);
-  xoffset = xod;
-  yoffset = yod;
-  for (i = 0; i < nrects; i++) {
-    mem = cairo_image_surface_get_data (surface) + (rects[i].x + xoffset) * 4
-      + (rects[i].y + yoffset) * stride;
-    if (gifenc_dither_rgb_with_full_image (
-	rec->data + width * rects[i].y + rects[i].x, width, 
-	rec->data_full + width * rects[i].y + rects[i].x, width, 
-	rec->gifenc->palette, mem, rects[i].width, rects[i].height, stride, &area)) {
-      area.x += rects[i].x;
-      area.y += rects[i].y;
-      gdk_region_union_with_rect (rev, &area);
-    }
-  }
-  g_free (rects);
-  gdk_region_get_clipbox (rev, &rec->relevant_data);
-  gdk_region_destroy (rev);
-  if (rec->relevant_data.width <= 0 && rec->relevant_data.height <= 0)
-    return TRUE;
-  
-  /* make non-relevant pixels transparent */
-  rev = gdk_region_rectangle (&rec->relevant_data);
-  gdk_region_subtract (rev, region);
-  gdk_region_get_rectangles (rev, &rects, &nrects);
-  for (i = 0; i < nrects; i++) {
-    for (line = 0; line < rects[i].height; line++) {
-      memset (rec->data + rects[i].x + width * (rects[i].y + line), 
-	  transparent, rects[i].width);
-    }
-  }
-  g_free (rects);
-  gdk_region_destroy (rev);
-  return TRUE;
-}
-
-static void
-byzanz_session_add_image (ByzanzSession *rec, const GTimeVal *tv)
-{
-  glong msecs;
-  guint width;
-
-  width = gifenc_get_width (rec->gifenc);
-  if (rec->data == NULL) {
-    guint count = width * gifenc_get_height (rec->gifenc);
-    rec->data = g_malloc (count);
-    rec->data_full = g_malloc (count);
-    memset (rec->data_full, 
-	gifenc_palette_get_alpha_index (rec->gifenc->palette), 
-	count);
-    rec->current = *tv;
-    return;
-  }
-  msecs = (tv->tv_sec - rec->current.tv_sec) * 1000 + 
-	  (tv->tv_usec - rec->current.tv_usec) / 1000 + 5;
-  if (msecs < 10)
-    g_printerr ("<10 msecs for a frame, can this be?\n");
-  msecs = MAX (msecs, 10);
-  if (rec->relevant_data.width > 0 && rec->relevant_data.height > 0) {
-    gifenc_add_image (rec->gifenc, rec->relevant_data.x, rec->relevant_data.y, 
-	rec->relevant_data.width, rec->relevant_data.height, msecs,
-	rec->data + width * rec->relevant_data.y + rec->relevant_data.x,
-	width, NULL);
-    rec->current = *tv;
-  }
-}
-
-static void
-byzanz_session_quantize (ByzanzSession *rec, cairo_surface_t *image)
-{
-  GifencPalette *palette;
-
-  palette = gifenc_quantize_image (cairo_image_surface_get_data (image),
-      cairo_image_surface_get_width (image), cairo_image_surface_get_height (image),
-      cairo_image_surface_get_stride (image), TRUE, 255);
-  
-  gifenc_initialize (rec->gifenc, palette, rec->loop, NULL);
-}
-
-static void
-byzanz_session_encode (ByzanzSession *rec, cairo_surface_t *image, GdkRegion *region)
-{
-  g_assert (!gdk_region_empty (region));
-  
-  byzanz_session_dither_region (rec, region, image);
-}
-
-static gboolean
-encoding_finished (gpointer data)
-{
-  ByzanzSession *session = data;
-
-  if (g_thread_join (session->encoder) != session)
-    g_assert_not_reached ();
-  session->encoder = NULL;
-  g_object_unref (session);
-
-  g_object_notify (data, "encoding");
-
-  return FALSE;
-}
-
-static gpointer
-byzanz_session_run_encoder (gpointer data)
-{
-  ByzanzSession *rec = data;
-  SessionJob *job;
-  GTimeVal quit_tv;
-  gboolean quit = FALSE;
-  gboolean has_quantized = FALSE;
-
-  while (!quit) {
-    job = g_async_queue_pop (rec->jobs);
-
-    switch (job->type) {
-      case SESSION_JOB_ENCODE:
-        if (!has_quantized) {
-	  byzanz_session_quantize (rec, job->image);
-          has_quantized = TRUE;
-        }
-        byzanz_session_add_image (rec, &job->tv);
-        byzanz_session_encode (rec, job->image, job->region);
-	break;
-      case SESSION_JOB_QUIT:
-	quit_tv = job->tv;
-	quit = TRUE;
-	break;
-      default:
-	g_assert_not_reached ();
-	return rec;
-    }
-    session_job_free (job);
-  }
-  
-  if (has_quantized) {
-    byzanz_session_add_image (rec, &quit_tv);
-    gifenc_close (rec->gifenc, NULL);
-  }
-
-  g_free (rec->data);
-  rec->data = NULL;
-  g_free (rec->data_full);
-  rec->data_full = NULL;
-
-  g_idle_add (encoding_finished, rec);
-  return rec;
-}
 
 /*** MAIN FUNCTIONS ***/
 
@@ -260,7 +46,10 @@ enum {
   PROP_0,
   PROP_RECORDING,
   PROP_ENCODING,
-  PROP_ERROR
+  PROP_ERROR,
+  PROP_FILE,
+  PROP_AREA,
+  PROP_WINDOW
 };
 
 G_DEFINE_TYPE (ByzanzSession, byzanz_session, G_TYPE_OBJECT)
@@ -281,6 +70,15 @@ byzanz_session_get_property (GObject *object, guint param_id, GValue *value,
     case PROP_ENCODING:
       g_value_set_boolean (value, byzanz_session_is_encoding (session));
       break;
+    case PROP_FILE:
+      g_value_set_object (value, session->file);
+      break;
+    case PROP_AREA:
+      g_value_set_boxed (value, &session->area);
+      break;
+    case PROP_WINDOW:
+      g_value_set_object (value, session->window);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
       break;
@@ -291,9 +89,18 @@ static void
 byzanz_session_set_property (GObject *object, guint param_id, const GValue *value, 
     GParamSpec * pspec)
 {
-  //ByzanzSession *session = BYZANZ_SESSION (object);
+  ByzanzSession *session = BYZANZ_SESSION (object);
 
   switch (param_id) {
+    case PROP_FILE:
+      session->file = g_value_dup_object (value);
+      break;
+    case PROP_AREA:
+      session->area = *(GdkRectangle *) g_value_get_boxed (value);
+      break;
+    case PROP_WINDOW:
+      session->window = g_value_dup_object (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
       break;
@@ -305,10 +112,7 @@ byzanz_session_dispose (GObject *object)
 {
   ByzanzSession *session = BYZANZ_SESSION (object);
 
-  if (byzanz_recorder_get_recording (session->recorder)) {
-    byzanz_session_stop (session);
-    return;
-  }
+  byzanz_session_abort (session);
 
   G_OBJECT_CLASS (byzanz_session_parent_class)->dispose (object);
 }
@@ -318,14 +122,11 @@ byzanz_session_finalize (GObject *object)
 {
   ByzanzSession *session = BYZANZ_SESSION (object);
 
-  g_assert (session->encoder == NULL);
-
-  gifenc_free (session->gifenc);
   g_object_unref (session->recorder);
-  g_object_unref (session->stream);
-
-  g_assert (g_async_queue_length (session->jobs) == 0);
-  g_async_queue_unref (session->jobs);
+  if (session->encoder)
+    g_object_unref (session->encoder);
+  g_object_unref (session->window);
+  g_object_unref (session->file);
 
   if (session->error)
     g_error_free (session->error);
@@ -333,7 +134,6 @@ byzanz_session_finalize (GObject *object)
   G_OBJECT_CLASS (byzanz_session_parent_class)->finalize (object);
 }
 
-#if 0
 static void
 byzanz_session_set_error (ByzanzSession *session, const GError *error)
 {
@@ -345,18 +145,70 @@ byzanz_session_set_error (ByzanzSession *session, const GError *error)
   session->error = g_error_copy (error);
   g_object_freeze_notify (object);
   g_object_notify (object, "error");
-  if (session->encoder != NULL)
-    g_object_notify (object, "encoding");
   if (byzanz_recorder_get_recording (session->recorder))
     byzanz_session_stop (session);
   g_object_thaw_notify (object);
 }
-#endif
+
+static void
+byzanz_session_encoder_notify_cb (ByzanzEncoder * encoder,
+                                  GParamSpec *    pspec,
+                                  ByzanzSession * session)
+{
+  if (g_str_equal (pspec->name, "running")) {
+    g_object_notify (G_OBJECT (session), "encoding");
+  } else if (g_str_equal (pspec->name, "error")) {
+    byzanz_session_set_error (session,
+        byzanz_encoder_get_error (encoder));
+  }
+}
+
+static void
+byzanz_session_recorder_notify_cb (ByzanzRecorder * recorder,
+                                   GParamSpec *     pspec,
+                                   ByzanzSession *  session)
+{
+  g_object_notify (G_OBJECT (session), "recording");
+}
+
+static void
+byzanz_session_recorder_image_cb (ByzanzRecorder *  recorder,
+                                  cairo_surface_t * surface,
+                                  const GdkRegion * region,
+                                  const GTimeVal *  tv,
+                                  ByzanzSession *   session)
+{
+  if (session->encoder) {
+    byzanz_encoder_process (session->encoder, surface, region, tv);
+  } else {
+    g_warning ("FIXME: figure out what to do now");
+  }
+}
 
 static void
 byzanz_session_constructed (GObject *object)
 {
-  //ByzanzSession *session = BYZANZ_SESSION (object);
+  ByzanzSession *session = BYZANZ_SESSION (object);
+  GOutputStream *stream;
+
+  session->recorder = byzanz_recorder_new (session->window, &session->area);
+  g_signal_connect (session->recorder, "notify::recording", 
+      G_CALLBACK (byzanz_session_recorder_notify_cb), session);
+  g_signal_connect (session->recorder, "image", 
+      G_CALLBACK (byzanz_session_recorder_image_cb), session);
+
+  /* FIXME: make async */
+  stream = G_OUTPUT_STREAM (g_file_replace (session->file, NULL, 
+        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, session->cancellable, &session->error));
+  if (stream != NULL) {
+    session->encoder = byzanz_encoder_new (stream,
+        session->area.width, session->area.height, session->cancellable);
+    g_signal_connect (session->encoder, "notify", 
+        G_CALLBACK (byzanz_session_encoder_notify_cb), session);
+    g_object_unref (stream);
+    if (byzanz_encoder_get_error (session->encoder))
+      byzanz_session_set_error (session, byzanz_encoder_get_error (session->encoder));
+  }
 
   if (G_OBJECT_CLASS (byzanz_session_parent_class)->constructed)
     G_OBJECT_CLASS (byzanz_session_parent_class)->constructed (object);
@@ -382,36 +234,26 @@ byzanz_session_class_init (ByzanzSessionClass *klass)
   g_object_class_install_property (object_class, PROP_ENCODING,
       g_param_spec_boolean ("encoding", "encoding", "TRUE while the encoder is running",
 	  TRUE, G_PARAM_READABLE));
+  g_object_class_install_property (object_class, PROP_WINDOW,
+      g_param_spec_object ("window", "window", "window to record from",
+	  GDK_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_AREA,
+      g_param_spec_boxed ("area", "area", "recorded area",
+	  GDK_TYPE_RECTANGLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_FILE,
+      g_param_spec_object ("file", "file", "file to record to",
+	  G_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 static void
 byzanz_session_init (ByzanzSession *session)
 {
-  session->jobs = g_async_queue_new ();
-}
-
-static void
-byzanz_session_recorder_image_cb (ByzanzRecorder *  recorder,
-                                  cairo_surface_t * surface,
-                                  const GdkRegion * region,
-                                  const GTimeVal *  tv,
-                                  ByzanzSession *   session)
-{
-  SessionJob *job = session_job_new (session, SESSION_JOB_ENCODE, surface, tv, region);
-  g_async_queue_push (session->jobs, job);
-}
-
-static gboolean
-session_gifenc_write (gpointer closure, const guchar *data, gsize len, GError **error)
-{
-  ByzanzSession *session = closure;
-
-  return g_output_stream_write_all (session->stream, data, len, NULL, session->cancellable, error);
+  session->cancellable = g_cancellable_new ();
 }
 
 /**
  * byzanz_session_new:
- * @filename: filename to record to
+ * @file: file to record to. Any existing file will be overwritten.
  * @window: window to record
  * @area: area of window that should be recorded
  * @loop: if the resulting animation should loop
@@ -425,13 +267,10 @@ session_gifenc_write (gpointer closure, const guchar *data, gsize len, GError **
  *          then. Another reason would be a thread creation failure.
  **/
 ByzanzSession *
-byzanz_session_new (GFile *destination, GdkWindow *window, GdkRectangle *area,
+byzanz_session_new (GFile *file, GdkWindow *window, GdkRectangle *area,
     gboolean loop, gboolean record_cursor)
 {
-  ByzanzSession *session;
-  GdkRectangle root_rect;
-
-  g_return_val_if_fail (G_IS_FILE (destination), NULL);
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
   g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
   g_return_val_if_fail (area != NULL, NULL);
   g_return_val_if_fail (area->x >= 0, NULL);
@@ -439,35 +278,9 @@ byzanz_session_new (GFile *destination, GdkWindow *window, GdkRectangle *area,
   g_return_val_if_fail (area->width > 0, NULL);
   g_return_val_if_fail (area->height > 0, NULL);
   
-  session = g_object_new (BYZANZ_TYPE_SESSION, NULL);
-
-  /* set user properties */
-  session->loop = loop;
-  
-  /* open file for writing */
-  session->stream = G_OUTPUT_STREAM (g_file_replace (destination, NULL, 
-        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, session->cancellable, &session->error));
-  if (session->stream == NULL)
-    return session;
-
-  /* prepare thread first, so we can easily error out on failure */
-  root_rect.x = root_rect.y = 0;
-  gdk_drawable_get_size (window,
-      &root_rect.width, &root_rect.height);
-  gdk_rectangle_intersect (area, &root_rect, &root_rect);
-  session->gifenc = gifenc_new (root_rect.width, root_rect.height, 
-      session_gifenc_write, session, NULL);
-
-  session->encoder = g_thread_create (byzanz_session_run_encoder, session, 
-      TRUE, &session->error);
-  if (!session->encoder)
-    return session;
-
-  session->recorder = byzanz_recorder_new (window, &root_rect);
-  g_signal_connect (session->recorder, "image", 
-      G_CALLBACK (byzanz_session_recorder_image_cb), session);
+  /* FIXME: handle looping and mouse cursor */
 
-  return session;
+  return g_object_new (BYZANZ_TYPE_SESSION, "file", file, "window", window, "area", area, NULL);
 }
 
 void
@@ -476,26 +289,21 @@ byzanz_session_start (ByzanzSession *session)
   g_return_if_fail (BYZANZ_IS_SESSION (session));
 
   byzanz_recorder_set_recording (session->recorder, TRUE);
-  g_object_notify (G_OBJECT (session), "recording");
 }
 
 void
 byzanz_session_stop (ByzanzSession *session)
 {
   GTimeVal tv;
-  SessionJob *job;
 
   g_return_if_fail (BYZANZ_IS_SESSION (session));
 
-  g_object_ref (session);
+  if (session->encoder) {
+    g_get_current_time (&tv);
+    byzanz_encoder_close (session->encoder, &tv);
+  }
 
-  /* byzanz_session_queue_image (session); - useless because last image would have a 0 time */
-  g_get_current_time (&tv);
-  job = session_job_new (session, SESSION_JOB_QUIT, NULL, &tv, NULL);
-  g_async_queue_push (session->jobs, job);
-  
   byzanz_recorder_set_recording (session->recorder, FALSE);
-  g_object_notify (G_OBJECT (session), "recording");
 }
 
 void
@@ -520,8 +328,7 @@ byzanz_session_is_encoding (ByzanzSession *session)
 {
   g_return_val_if_fail (BYZANZ_IS_SESSION (session), FALSE);
 
-  return session->error == NULL &&
-    session->encoder != NULL;
+  return session->error == NULL && byzanz_encoder_is_running (session->encoder);
 }
 
 const GError *
diff --git a/src/byzanzsession.h b/src/byzanzsession.h
index 9cc1830..c36fb24 100644
--- a/src/byzanzsession.h
+++ b/src/byzanzsession.h
@@ -20,8 +20,8 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 
+#include "byzanzencoder.h"
 #include "byzanzrecorder.h"
-#include "gifenc.h"
 
 #ifndef __HAVE_BYZANZ_SESSION_H__
 #define __HAVE_BYZANZ_SESSION_H__
@@ -39,21 +39,18 @@ typedef struct _ByzanzSessionClass ByzanzSessionClass;
 struct _ByzanzSession {
   GObject		object;
   
-  /* set by user - accessed ALSO by thread */
+  /*< private >*/
+  /* properties */
   gboolean		loop;		/* wether the resulting gif should loop */
-  ByzanzRecorder *      recorder;       /* the recorder in use */
-  GThread *		encoder;	/* encoding thread */
-  GError *              error;          /* NULL or the recording error */
+  GFile *               file;           /* file we're saving to */
+  GdkRectangle          area;           /* area of window to record */
+  GdkWindow *           window;         /* window to record */
+
+  /* internal objects */
   GCancellable *        cancellable;    /* cancellable to use for aborting the session */
-  /* accessed ALSO by thread */
-  GAsyncQueue *		jobs;		/* jobs the encoding thread has to do */
-  /* accessed ONLY by thread */
-  GOutputStream *       stream;         /* stream we write to */
-  Gifenc *		gifenc;		/* encoder used to encode the image */
-  GTimeVal		current;	/* timestamp of last encoded picture */
-  guint8 *		data;		/* data used to hold palettized data */
-  guint8 *		data_full;    	/* palettized data of full image to compare additions to */
-  GdkRectangle		relevant_data;	/* relevant area to encode */
+  ByzanzRecorder *      recorder;       /* the recorder in use */
+  ByzanzEncoder *	encoder;	/* encoding thread */
+  GError *              error;          /* NULL or the error we're in */
 };
 
 struct _ByzanzSessionClass {
@@ -63,7 +60,7 @@ struct _ByzanzSessionClass {
 GType		        byzanz_session_get_type		(void) G_GNUC_CONST;
 
 
-ByzanzSession * 	byzanz_session_new		(GFile *                destination,
+ByzanzSession * 	byzanz_session_new		(GFile *                file,
 							 GdkWindow *		window,
 							 GdkRectangle *		area,
 							 gboolean		loop,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]