[byzanz] Create a separate encoder class for encoding
- From: Benjamin Otte <otte src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [byzanz] Create a separate encoder class for encoding
- Date: Wed, 26 Aug 2009 10:22:38 +0000 (UTC)
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]