[byzanz] Add an intermediae serialization format and tools to handle it



commit 579c760b13f7c3ac06dac5e5833a02abce41aa49
Author: Benjamin Otte <otte gnome org>
Date:   Tue Sep 1 14:41:50 2009 +0200

    Add an intermediae serialization format and tools to handle it
    
    The intermediate format is intended to quickly dump data to a file for
    later processing. It does not aim at reducing file size, being
    compatible to any other format.
    The intended use is as a quick storage in /tmp that ensures recording
    can continue even when the CPU cannot keep up with encoding the image
    stream. This rarely happens on modern CPUs, but can often happen on
    older or embedded devices.
    
    Additions:
    - byzanzserialize.[ch], code to (de)serialize internal data to/from a
      stream.
    - ByzanzEncoderByzanz, an encoder that writes out that intermediate
      format to a file. Extension is .byzanz.
    - byzanz-playback, a simple application that reads files in .byzanz
      format and encodes them using the Byzanz encoders.
    
    This allows to encode a file for later processing, possibly even
    multiple times. The main intended use however is benchmarking the
    performance-critical parts of the Byzanz pipeline.

 .gitignore                |    1 +
 src/Makefile.am           |   20 +++-
 src/byzanzencoder.c       |    5 +-
 src/byzanzencoderbyzanz.c |  108 ++++++++++++++++++++
 src/byzanzencoderbyzanz.h |   48 +++++++++
 src/byzanzserialize.c     |  243 +++++++++++++++++++++++++++++++++++++++++++++
 src/byzanzserialize.h     |   53 ++++++++++
 src/playback.c            |  167 +++++++++++++++++++++++++++++++
 8 files changed, 640 insertions(+), 5 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index c689136..4acdf85 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@ po/stamp-it
 src/ByzanzApplet.server
 src/ByzanzApplet.server.in
 src/byzanz-applet
+src/byzanz-playback
 src/byzanz-record
 src/byzanz.schemas
 src/byzanzmarshal.c
diff --git a/src/Makefile.am b/src/Makefile.am
index b5e718d..5f50ccb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
 noinst_LTLIBRARIES = libbyzanz.la
-bin_PROGRAMS = byzanz-record
+bin_PROGRAMS = byzanz-record byzanz-playback
 libexec_PROGRAMS = byzanz-applet
 man_MANS = byzanz-record.1
 
@@ -9,6 +9,7 @@ BUILT_SOURCES = \
 
 noinst_HEADERS = \
 	byzanzencoder.h \
+	byzanzencoderbyzanz.h \
 	byzanzencoderflv.h \
 	byzanzencodergif.h \
 	byzanzencodergstreamer.h \
@@ -19,11 +20,13 @@ noinst_HEADERS = \
 	byzanzrecorder.h \
 	byzanzsession.h \
 	byzanzselect.h \
+	byzanzserialize.h \
 	paneltogglebutton.h \
 	screenshot-utils.h
 
 libbyzanz_la_SOURCES = \
 	byzanzencoder.c \
+	byzanzencoderbyzanz.c \
 	byzanzencoderflv.c \
 	byzanzencodergif.c \
 	byzanzencodergstreamer.c \
@@ -34,15 +37,23 @@ libbyzanz_la_SOURCES = \
 	byzanzmarshal.c \
 	byzanzrecorder.c \
 	byzanzsession.c \
-	byzanzselect.c
+	byzanzselect.c \
+	byzanzserialize.c
 
 libbyzanz_la_CFLAGS = $(BYZANZ_CFLAGS) -I$(top_srcdir)/gifenc
 libbyzanz_la_LIBADD = $(BYZANZ_LIBS) $(top_builddir)/gifenc/libgifenc.la
 		 
+byzanz_playback_SOURCES = \
+	playback.c
+
+byzanz_playback_CFLAGS = $(BYZANZ_CFLAGS)
+byzanz_playback_LDADD = $(BYZANZ_LIBS) ./libbyzanz.la
+
+
 byzanz_record_SOURCES = \
 	record.c
 
-byzanz_record_CFLAGS = $(BYZANZ_CFLAGS) -I$(top_srcdir)/gifenc
+byzanz_record_CFLAGS = $(BYZANZ_CFLAGS)
 byzanz_record_LDADD = $(BYZANZ_LIBS) ./libbyzanz.la
 
 
@@ -51,9 +62,10 @@ byzanz_applet_SOURCES = \
 	paneltogglebutton.c \
 	screenshot-utils.c
 
-byzanz_applet_CFLAGS = $(APPLET_CFLAGS) -I$(top_srcdir)/gifenc
+byzanz_applet_CFLAGS = $(APPLET_CFLAGS)
 byzanz_applet_LDADD = $(APPLET_LIBS) ./libbyzanz.la
 
+
 byzanzmarshal.h: byzanzmarshal.list
 	$(GLIB_GENMARSHAL) --prefix=byzanz_marshal $(srcdir)/byzanzmarshal.list --header > byzanzmarshal.h
 
diff --git a/src/byzanzencoder.c b/src/byzanzencoder.c
index ffbcfa5..b49d213 100644
--- a/src/byzanzencoder.c
+++ b/src/byzanzencoder.c
@@ -396,6 +396,7 @@ byzanz_encoder_type_get_filter (GType encoder_type)
 }
 
 /* all the encoders */
+#include "byzanzencoderbyzanz.h"
 #include "byzanzencoderflv.h"
 #include "byzanzencodergif.h"
 #include "byzanzencoderogv.h"
@@ -404,7 +405,9 @@ typedef GType (* TypeFunc) (void);
 static const TypeFunc functions[] = {
   byzanz_encoder_gif_get_type,
   byzanz_encoder_ogv_get_type,
-  byzanz_encoder_flv_get_type
+  byzanz_encoder_flv_get_type,
+  /* debug types */
+  byzanz_encoder_byzanz_get_type,
 };
 #define BYZANZ_ENCODER_DEFAULT_TYPE (functions[0] ())
 
diff --git a/src/byzanzencoderbyzanz.c b/src/byzanzencoderbyzanz.c
new file mode 100644
index 0000000..ea45a97
--- /dev/null
+++ b/src/byzanzencoderbyzanz.c
@@ -0,0 +1,108 @@
+/* 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 "byzanzencoderbyzanz.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "byzanzserialize.h"
+
+G_DEFINE_TYPE (ByzanzEncoderByzanz, byzanz_encoder_byzanz, BYZANZ_TYPE_ENCODER)
+
+static guint64
+byzanz_encoder_byzanz_time (ByzanzEncoderByzanz * byzanz,
+                            const GTimeVal *      tv)
+{
+  guint64 result;
+
+  result = tv->tv_sec - byzanz->start_time.tv_sec;
+  result *= 1000;
+  result += (tv->tv_usec - byzanz->start_time.tv_usec) / 1000;
+
+  return result;
+}
+
+static gboolean
+byzanz_encoder_byzanz_setup (ByzanzEncoder * encoder,
+                             GOutputStream * stream,
+                             guint           width,
+                             guint           height,
+                             GCancellable *  cancellable,
+                             GError **	     error)
+{
+  return byzanz_serialize_header (stream, width, height, cancellable, error);
+}
+
+static gboolean
+byzanz_encoder_byzanz_process (ByzanzEncoder *   encoder,
+                               GOutputStream *   stream,
+                               cairo_surface_t * surface,
+                               const GdkRegion * region,
+                               const GTimeVal *  total_elapsed,
+                               GCancellable *    cancellable,
+                               GError **	 error)
+{
+  ByzanzEncoderByzanz *byzanz = BYZANZ_ENCODER_BYZANZ (encoder);
+
+  if (byzanz->start_time.tv_sec == 0 && byzanz->start_time.tv_usec)
+    byzanz->start_time = *total_elapsed;
+
+  return byzanz_serialize (stream, byzanz_encoder_byzanz_time (byzanz, total_elapsed),
+      surface, region, cancellable, error);
+}
+
+static gboolean
+byzanz_encoder_byzanz_close (ByzanzEncoder *  encoder,
+                          GOutputStream *  stream,
+                          const GTimeVal * total_elapsed,
+                          GCancellable *   cancellable,
+                          GError **	   error)
+{
+  ByzanzEncoderByzanz *byzanz = BYZANZ_ENCODER_BYZANZ (encoder);
+
+  return byzanz_serialize (stream, byzanz_encoder_byzanz_time (byzanz, total_elapsed),
+      NULL, NULL, cancellable, error);
+}
+
+static void
+byzanz_encoder_byzanz_class_init (ByzanzEncoderByzanzClass *klass)
+{
+  ByzanzEncoderClass *encoder_class = BYZANZ_ENCODER_CLASS (klass);
+
+  encoder_class->setup = byzanz_encoder_byzanz_setup;
+  encoder_class->process = byzanz_encoder_byzanz_process;
+  encoder_class->close = byzanz_encoder_byzanz_close;
+
+  encoder_class->filter = gtk_file_filter_new ();
+  g_object_ref_sink (encoder_class->filter);
+  gtk_file_filter_set_name (encoder_class->filter, _("Byzanz debug files"));
+  gtk_file_filter_add_mime_type (encoder_class->filter, "application/x-byzanz");
+  gtk_file_filter_add_pattern (encoder_class->filter, "*.byzanz");
+}
+
+static void
+byzanz_encoder_byzanz_init (ByzanzEncoderByzanz *encoder_byzanz)
+{
+}
+
diff --git a/src/byzanzencoderbyzanz.h b/src/byzanzencoderbyzanz.h
new file mode 100644
index 0000000..64e5f51
--- /dev/null
+++ b/src/byzanzencoderbyzanz.h
@@ -0,0 +1,48 @@
+/* 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"
+
+#ifndef __HAVE_BYZANZ_ENCODER_BYZANZ_H__
+#define __HAVE_BYZANZ_ENCODER_BYZANZ_H__
+
+typedef struct _ByzanzEncoderByzanz ByzanzEncoderByzanz;
+typedef struct _ByzanzEncoderByzanzClass ByzanzEncoderByzanzClass;
+
+#define BYZANZ_TYPE_ENCODER_BYZANZ                    (byzanz_encoder_byzanz_get_type())
+#define BYZANZ_IS_ENCODER_BYZANZ(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BYZANZ_TYPE_ENCODER_BYZANZ))
+#define BYZANZ_IS_ENCODER_BYZANZ_CLASS(klass)         (G_TYPE_CHECK_CLASS_TYPE ((klass), BYZANZ_TYPE_ENCODER_BYZANZ))
+#define BYZANZ_ENCODER_BYZANZ(obj)                    (G_TYPE_CHECK_INSTANCE_CAST ((obj), BYZANZ_TYPE_ENCODER_BYZANZ, ByzanzEncoderByzanz))
+#define BYZANZ_ENCODER_BYZANZ_CLASS(klass)            (G_TYPE_CHECK_CLASS_CAST ((klass), BYZANZ_TYPE_ENCODER_BYZANZ, ByzanzEncoderByzanzClass))
+#define BYZANZ_ENCODER_BYZANZ_GET_CLASS(obj)          (G_TYPE_INSTANCE_GET_CLASS ((obj), BYZANZ_TYPE_ENCODER_BYZANZ, ByzanzEncoderByzanzClass))
+
+struct _ByzanzEncoderByzanz {
+  ByzanzEncoder         encoder;
+
+  GTimeVal              start_time;
+};
+
+struct _ByzanzEncoderByzanzClass {
+  ByzanzEncoderClass    encoder_class;
+};
+
+GType		byzanz_encoder_byzanz_get_type		(void) G_GNUC_CONST;
+
+
+#endif /* __HAVE_BYZANZ_ENCODER_BYZANZ_H__ */
diff --git a/src/byzanzserialize.c b/src/byzanzserialize.c
new file mode 100644
index 0000000..ad1232b
--- /dev/null
+++ b/src/byzanzserialize.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 "byzanzserialize.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#define IDENTIFICATION "ByzanzRecording"
+
+static guchar
+byte_order_to_uchar (void)
+{
+  switch (G_BYTE_ORDER) {
+    case G_BIG_ENDIAN:
+      return 'B';
+    case G_LITTLE_ENDIAN:
+      return 'L';
+    default:
+      g_assert_not_reached ();
+      return 0;
+  }
+}
+
+gboolean
+byzanz_serialize_header (GOutputStream * stream,
+                         guint           width,
+                         guint           height,
+                         GCancellable *  cancellable,
+                         GError **       error)
+{
+  guint32 w, h;
+  guchar endian;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (width <= G_MAXUINT32, FALSE);
+  g_return_val_if_fail (height <= G_MAXUINT32, FALSE);
+
+  w = width;
+  h = height;
+  endian = byte_order_to_uchar ();
+
+  return g_output_stream_write_all (stream, IDENTIFICATION, strlen (IDENTIFICATION), NULL, cancellable, error) &&
+    g_output_stream_write_all (stream, &endian, sizeof (guchar), NULL, cancellable, error) &&
+    g_output_stream_write_all (stream, &w, sizeof (guint32), NULL, cancellable, error) &&
+    g_output_stream_write_all (stream, &h, sizeof (guint32), NULL, cancellable, error);
+}
+
+gboolean
+byzanz_deserialize_header (GInputStream * stream,
+                           guint *        width,
+                           guint *        height,
+                           GCancellable * cancellable,
+                           GError **      error)
+{
+  char result[strlen (IDENTIFICATION) + 1];
+  guint32 size[2];
+  guchar endian;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (width != NULL, FALSE);
+  g_return_val_if_fail (height != NULL, FALSE);
+
+  if (!g_input_stream_read_all (stream, result, sizeof (result), NULL, cancellable, error))
+    return FALSE;
+
+  if (strncmp (result, IDENTIFICATION, strlen (IDENTIFICATION)) != 0) {
+    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+        _("Not a Byzanz recording"));
+    return FALSE;
+  }
+  endian = result[strlen (IDENTIFICATION)];
+  if (endian != byte_order_to_uchar ()) {
+    g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+        _("Unsupported byte order"));
+    return FALSE;
+  }
+
+  if (!g_input_stream_read_all (stream, &size, sizeof (size), NULL, cancellable, error))
+    return FALSE;
+
+  *width = size[0];
+  *height = size[1];
+
+  return TRUE;
+} 
+
+gboolean
+byzanz_serialize (GOutputStream *   stream,
+                  guint64           msecs,
+                  cairo_surface_t * surface,
+                  const GdkRegion * region,
+                  GCancellable *    cancellable,
+                  GError **         error)
+{
+  guint i, stride;
+  GdkRectangle *rects, extents;
+  guchar *data;
+  guint32 n;
+  int y, n_rects;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail ((surface == NULL) == (region == NULL), FALSE);
+  g_return_val_if_fail (region == NULL || !gdk_region_empty (region), FALSE);
+
+  if (!g_output_stream_write_all (stream, &msecs, sizeof (guint64), NULL, cancellable, error))
+    return FALSE;
+
+  if (surface == 0) {
+    n = 0;
+    return g_output_stream_write_all (stream, &n, sizeof (guint32), NULL, cancellable, error);
+  }
+
+  gdk_region_get_rectangles (region, &rects, &n_rects);
+  n = n_rects;
+  if (!g_output_stream_write_all (stream, &n, sizeof (guint32), NULL, cancellable, error))
+    goto fail;
+  for (i = 0; i < n; i++) {
+    gint32 ints[4] = { rects[i].x, rects[i].y, rects[i].width, rects[i].height };
+
+    g_assert (sizeof (ints) == 16);
+    if (!g_output_stream_write_all (stream, ints, sizeof (ints), NULL, cancellable, error))
+      goto fail;
+  }
+
+  stride = cairo_image_surface_get_stride (surface);
+  gdk_region_get_clipbox (region, &extents);
+  for (i = 0; i < n; i++) {
+    data = cairo_image_surface_get_data (surface) 
+      + stride * (rects[i].y - extents.y) 
+      + sizeof (guint32) * (rects[i].x - extents.x);
+    for (y = 0; y < rects[i].height; y++) {
+      if (!g_output_stream_write_all (G_OUTPUT_STREAM (stream), data, 
+            rects[i].width * sizeof (guint32), NULL, cancellable, error))
+        goto fail;
+      data += stride;
+    }
+  }
+
+  g_free (rects);
+  return TRUE;
+
+fail:
+  g_free (rects);
+  return FALSE;
+}
+
+gboolean
+byzanz_deserialize (GInputStream *     stream,
+                    guint64 *          msecs_out,
+                    cairo_surface_t ** surface_out,
+                    GdkRegion **       region_out,
+                    GCancellable *     cancellable,
+                    GError **          error)
+{
+  guint i, stride;
+  GdkRectangle extents, *rects;
+  GdkRegion *region;
+  cairo_surface_t *surface;
+  guchar *data;
+  guint32 n;
+  int y;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (msecs_out != NULL, FALSE);
+  g_return_val_if_fail (surface_out != NULL, FALSE);
+  g_return_val_if_fail (region_out != NULL, FALSE);
+
+  if (!g_input_stream_read_all (stream, msecs_out, sizeof (guint64), NULL, cancellable, error) ||
+      !g_input_stream_read_all (stream, &n, sizeof (guint32), NULL, cancellable, error))
+    return FALSE;
+
+  if (n == 0) {
+    /* end of stream */
+    *surface_out = NULL;
+    *region_out = NULL;
+    return TRUE;
+  }
+
+  region = gdk_region_new ();
+  rects = g_new (GdkRectangle, n);
+  surface = NULL;
+  for (i = 0; i < n; i++) {
+    gint ints[4];
+    if (!g_input_stream_read_all (stream, ints, sizeof (ints), NULL, cancellable, error))
+      goto fail;
+
+    rects[i].x = ints[0];
+    rects[i].y = ints[1];
+    rects[i].width = ints[2];
+    rects[i].height = ints[3];
+    gdk_region_union_with_rect (region, &rects[i]);
+  }
+
+  gdk_region_get_clipbox (region, &extents);
+  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, extents.width, extents.height);
+  cairo_surface_set_device_offset (surface, -extents.x, -extents.y);
+  stride = cairo_image_surface_get_stride (surface);
+  for (i = 0; i < n; i++) {
+    data = cairo_image_surface_get_data (surface) 
+      + stride * (rects[i].y - extents.y) 
+      + sizeof (guint32) * (rects[i].x - extents.x);
+    for (y = 0; y < rects[i].height; y++) {
+      if (!g_input_stream_read_all (stream, data, 
+            rects[i].width * sizeof (guint32), NULL, cancellable, error))
+        goto fail;
+      data += stride;
+    }
+  }
+
+  g_free (rects);
+  *region_out = region;
+  *surface_out = surface;
+  return TRUE;
+
+fail:
+  if (surface)
+    cairo_surface_destroy (surface);
+  gdk_region_destroy (region);
+  g_free (rects);
+  return FALSE;
+}
+
diff --git a/src/byzanzserialize.h b/src/byzanzserialize.h
new file mode 100644
index 0000000..8ab5881
--- /dev/null
+++ b/src/byzanzserialize.h
@@ -0,0 +1,53 @@
+/* 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 <gio/gio.h>
+#include <gdk/gdk.h>
+#include <cairo.h>
+
+#ifndef __HAVE_BYZANZ_SERIALIZE_H__
+#define __HAVE_BYZANZ_SERIALIZE_H__
+
+
+gboolean                byzanz_serialize_header         (GOutputStream *        stream,
+                                                         guint                  width,
+                                                         guint                  height,
+                                                         GCancellable *         cancellable,
+                                                         GError **              error);
+gboolean                byzanz_serialize                (GOutputStream *        stream,
+                                                         guint64                msecs,
+                                                         cairo_surface_t *      surface,
+                                                         const GdkRegion *      region,
+                                                         GCancellable *         cancellable,
+                                                         GError **              error);
+
+gboolean                byzanz_deserialize_header       (GInputStream *         stream,
+                                                         guint *                width,
+                                                         guint *                height,
+                                                         GCancellable *         cancellable,
+                                                         GError **              error);
+gboolean                byzanz_deserialize              (GInputStream *         stream,
+                                                         guint64 *              msecs_out,
+                                                         cairo_surface_t **     surface_out,
+                                                         GdkRegion **           region_out,
+                                                         GCancellable *         cancellable,
+                                                         GError **              error);
+
+
+#endif /* __HAVE_BYZANZ_SERIALIZE_H__ */
diff --git a/src/playback.c b/src/playback.c
new file mode 100644
index 0000000..b2d29d5
--- /dev/null
+++ b/src/playback.c
@@ -0,0 +1,167 @@
+/* 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 <glib/gi18n.h>
+
+#include "byzanzencoder.h"
+#include "byzanzserialize.h"
+
+static GOptionEntry entries[] = 
+{
+  { NULL }
+};
+
+static void
+usage (void)
+{
+  g_print (_("usage: %s [OPTIONS] INFILE OUTFILE\n"), g_get_prgname ());
+  g_print (_("       %s --help\n"), g_get_prgname ());
+}
+
+typedef struct {
+  GFile *               infile;
+  GFile *               outfile;
+  GInputStream *        instream;
+  GOutputStream *       outstream;
+  GMainLoop *           loop;
+  ByzanzEncoder *       encoder;
+} Operation;
+
+static gboolean
+add_more_data (gpointer data)
+{
+  Operation *op = data;
+  cairo_surface_t *surface;
+  GdkRegion *region;
+  GTimeVal tv;
+  guint64 elapsed;
+  GError *error = NULL;
+
+  if (!byzanz_deserialize (op->instream, &elapsed, &surface, &region, NULL, &error)) {
+    g_print ("%s\n", error->message);
+    g_error_free (error);
+    g_main_loop_quit (op->loop);
+    return FALSE;
+  }
+
+  tv.tv_sec = elapsed / 1000;
+  tv.tv_usec = (elapsed % 1000) * 1000;
+  if (surface) {
+    byzanz_encoder_process (op->encoder, surface, region, &tv);
+    cairo_surface_destroy (surface);
+    gdk_region_destroy (region);
+    return TRUE;
+  } else {
+    byzanz_encoder_close (op->encoder, &tv);
+    return FALSE;
+  }
+  return surface ? TRUE : FALSE;
+}
+
+static void
+encoder_notify (ByzanzEncoder *encoder, GParamSpec *pspec, Operation *op)
+{
+  const GError *error;
+
+  error = byzanz_encoder_get_error (encoder);
+  if (error) {
+    g_print ("%s\n", error->message);
+    g_main_loop_quit (op->loop);
+  } else if (!byzanz_encoder_is_running (encoder)) {
+    g_main_loop_quit (op->loop);
+  }
+}
+
+int
+main (int argc, char **argv)
+{
+  GOptionContext* context;
+  GError *error = NULL;
+  Operation op;
+  guint width, height;
+  
+  g_set_prgname (argv[0]);
+#ifdef GETTEXT_PACKAGE
+  bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+#endif
+
+  g_thread_init (NULL);
+  g_type_init ();
+
+  context = g_option_context_new (_("Process a byzanz debug file"));
+#ifdef GETTEXT_PACKAGE
+  g_option_context_set_translation_domain(context, GETTEXT_PACKAGE);
+#endif
+
+  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+  if (!g_option_context_parse (context, &argc, &argv, &error)) {
+    g_print (_("Wrong option: %s\n"), error->message);
+    usage ();
+    g_error_free (error);
+    return 1;
+  }
+  if (argc != 3) {
+    usage ();
+    return 0;
+  }
+
+  op.infile = g_file_new_for_commandline_arg (argv[1]);
+  op.outfile = g_file_new_for_commandline_arg (argv[2]);
+  op.loop = g_main_loop_new (NULL, FALSE);
+
+  op.instream = G_INPUT_STREAM (g_file_read (op.infile, NULL, &error));
+  if (op.instream == NULL) {
+    g_print ("%s\n", error->message);
+    g_error_free (error);
+    return 1;
+  }
+  if (!byzanz_deserialize_header (op.instream, &width, &height, NULL, &error)) {
+    g_print ("%s\n", error->message);
+    g_error_free (error);
+    return 1;
+  }
+  op.outstream = G_OUTPUT_STREAM (g_file_replace (op.outfile, NULL, 
+        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, &error));
+  if (op.outstream == NULL) {
+    g_print ("%s\n", error->message);
+    g_error_free (error);
+    return 1;
+  }
+  op.encoder = byzanz_encoder_new (byzanz_encoder_get_type_from_file (op.outfile),
+      op.outstream, width, height, NULL);
+  
+  g_signal_connect (op.encoder, "notify", G_CALLBACK (encoder_notify), &op);
+  g_idle_add (add_more_data, &op);
+  
+  g_main_loop_run (op.loop);
+
+  g_main_loop_unref (op.loop);
+  g_object_unref (op.instream);
+  g_object_unref (op.outstream);
+  g_object_unref (op.infile);
+  g_object_unref (op.outfile);
+
+  return 0;
+}



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