ext/jpeg/: Added a new simple jpeg based codec
authorWim Taymans <wim.taymans@gmail.com>
Tue, 8 Jun 2004 11:47:35 +0000 (11:47 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Tue, 8 Jun 2004 11:47:35 +0000 (11:47 +0000)
Original commit message from CVS:
* ext/jpeg/Makefile.am:
* ext/jpeg/README:
* ext/jpeg/gstjpeg.c: (plugin_init):
* ext/jpeg/gstsmokedec.c: (gst_smokedec_get_type),
(gst_smokedec_base_init), (gst_smokedec_class_init),
(gst_smokedec_init), (gst_smokedec_link), (gst_smokedec_chain):
* ext/jpeg/gstsmokedec.h:
* ext/jpeg/gstsmokeenc.c: (gst_smokeenc_get_type),
(gst_smokeenc_base_init), (gst_smokeenc_class_init),
(gst_smokeenc_init), (gst_smokeenc_getcaps), (gst_smokeenc_link),
(gst_smokeenc_resync), (gst_smokeenc_chain),
(gst_smokeenc_set_property), (gst_smokeenc_get_property):
* ext/jpeg/gstsmokeenc.h:
* ext/jpeg/smokecodec.c: (smokecodec_init_destination),
(smokecodec_flush_destination), (smokecodec_term_destination),
(smokecodec_init_source), (smokecodec_fill_input_buffer),
(smokecodec_skip_input_data), (smokecodec_resync_to_restart),
(smokecodec_term_source), (smokecodec_encode_new),
(smokecodec_decode_new), (smokecodec_info_free),
(smokecodec_set_quality), (smokecodec_get_quality),
(smokecodec_set_threshold), (smokecodec_get_threshold),
(smokecodec_set_bitrate), (smokecodec_get_bitrate),
(find_best_size), (abs_diff), (put), (smokecodec_encode),
(smokecodec_parse_header), (smokecodec_decode):
* ext/jpeg/smokecodec.h:
Added a new simple jpeg based codec

ChangeLog
ext/jpeg/Makefile.am
ext/jpeg/README [new file with mode: 0644]
ext/jpeg/gstjpeg.c
ext/jpeg/gstsmokedec.c [new file with mode: 0644]
ext/jpeg/gstsmokedec.h [new file with mode: 0644]
ext/jpeg/gstsmokeenc.c [new file with mode: 0644]
ext/jpeg/gstsmokeenc.h [new file with mode: 0644]
ext/jpeg/smokecodec.c [new file with mode: 0644]
ext/jpeg/smokecodec.h [new file with mode: 0644]

index 5e0cf8a..73e728e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,34 @@
 2004-06-08  Wim Taymans  <wim@fluendo.com>
 
+       * ext/jpeg/Makefile.am:
+       * ext/jpeg/README:
+       * ext/jpeg/gstjpeg.c: (plugin_init):
+       * ext/jpeg/gstsmokedec.c: (gst_smokedec_get_type),
+       (gst_smokedec_base_init), (gst_smokedec_class_init),
+       (gst_smokedec_init), (gst_smokedec_link), (gst_smokedec_chain):
+       * ext/jpeg/gstsmokedec.h:
+       * ext/jpeg/gstsmokeenc.c: (gst_smokeenc_get_type),
+       (gst_smokeenc_base_init), (gst_smokeenc_class_init),
+       (gst_smokeenc_init), (gst_smokeenc_getcaps), (gst_smokeenc_link),
+       (gst_smokeenc_resync), (gst_smokeenc_chain),
+       (gst_smokeenc_set_property), (gst_smokeenc_get_property):
+       * ext/jpeg/gstsmokeenc.h:
+       * ext/jpeg/smokecodec.c: (smokecodec_init_destination),
+       (smokecodec_flush_destination), (smokecodec_term_destination),
+       (smokecodec_init_source), (smokecodec_fill_input_buffer),
+       (smokecodec_skip_input_data), (smokecodec_resync_to_restart),
+       (smokecodec_term_source), (smokecodec_encode_new),
+       (smokecodec_decode_new), (smokecodec_info_free),
+       (smokecodec_set_quality), (smokecodec_get_quality),
+       (smokecodec_set_threshold), (smokecodec_get_threshold),
+       (smokecodec_set_bitrate), (smokecodec_get_bitrate),
+       (find_best_size), (abs_diff), (put), (smokecodec_encode),
+       (smokecodec_parse_header), (smokecodec_decode):
+       * ext/jpeg/smokecodec.h:
+       Added a new simple jpeg based codec
+
+2004-06-08  Wim Taymans  <wim@fluendo.com>
+
        * gst/multipart/multipartmux.c: (gst_multipart_mux_class_init),
        (gst_multipart_mux_loop):
        Fix memory leak
index 351a2e7..15ff3f7 100644 (file)
@@ -1,7 +1,7 @@
 
 plugin_LTLIBRARIES = libgstjpeg.la
 
-libgstjpeg_la_SOURCES = gstjpeg.c gstjpegdec.c gstjpegenc.c
+libgstjpeg_la_SOURCES = gstjpeg.c gstjpegdec.c gstjpegenc.c gstsmokeenc.c gstsmokedec.c smokecodec.c
 libgstjpeg_la_CFLAGS = $(GST_CFLAGS)
 libgstjpeg_la_LIBADD = $(JPEG_LIBS) 
 libgstjpeg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
diff --git a/ext/jpeg/README b/ext/jpeg/README
new file mode 100644 (file)
index 0000000..ffa1d0c
--- /dev/null
@@ -0,0 +1,20 @@
+The Smoke Codec
+---------------
+
+This is a very simple compression algorithm I was toying with when doing a
+Java based player. Decoding a JPEG in Java has acceptable speed so this codec
+tries to exploit that feature. The algorithm first compares the last and the 
+new image and finds all 16x16 blocks that have a squared difference bigger than
+a configurable threshold. Then all these blocks are compressed into an NxM JPEG.
+The quality of the JPEG is inversely proportional to the number of blocks, this
+way, the picture quality degrades with heavy motion scenes but the bitrate stays
+more or less constant.
+Decoding decompresses the JPEG and then updates the old picture with the new
+blocks.
+
+
+TODO:
+----
+- make format extensible
+- motion vectors
+- do some real bitrate control
index cf8af9f..5874953 100644 (file)
@@ -22,6 +22,8 @@
 
 #include "gstjpegdec.h"
 #include "gstjpegenc.h"
+#include "gstsmokeenc.h"
+#include "gstsmokedec.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
@@ -34,6 +36,14 @@ plugin_init (GstPlugin * plugin)
           GST_TYPE_JPEGDEC))
     return FALSE;
 
+  if (!gst_element_register (plugin, "smokeenc", GST_RANK_PRIMARY,
+          GST_TYPE_SMOKEENC))
+    return FALSE;
+
+  if (!gst_element_register (plugin, "smokedec", GST_RANK_PRIMARY,
+          GST_TYPE_SMOKEDEC))
+    return FALSE;
+
   return TRUE;
 }
 
diff --git a/ext/jpeg/gstsmokedec.c b/ext/jpeg/gstsmokedec.c
new file mode 100644 (file)
index 0000000..34a8678
--- /dev/null
@@ -0,0 +1,237 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 2 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 <string.h>
+
+/*#define DEBUG_ENABLED*/
+#include "gstsmokedec.h"
+#include <gst/video/video.h>
+
+/* elementfactory information */
+GstElementDetails gst_smokedec_details = {
+  "Smoke image decoder",
+  "Codec/Decoder/Image",
+  "Decode images from Smoke format",
+  "Wim Taymans <wim@fluendo.com>",
+};
+
+GST_DEBUG_CATEGORY (smokedec_debug);
+#define GST_CAT_DEFAULT smokedec_debug
+
+/* SmokeDec signals and args */
+enum
+{
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0
+      /* FILL ME */
+};
+
+static void gst_smokedec_base_init (gpointer g_class);
+static void gst_smokedec_class_init (GstSmokeDec * klass);
+static void gst_smokedec_init (GstSmokeDec * smokedec);
+
+static void gst_smokedec_chain (GstPad * pad, GstData * _data);
+static GstPadLinkReturn gst_smokedec_link (GstPad * pad, const GstCaps * caps);
+
+static GstElementClass *parent_class = NULL;
+
+/*static guint gst_smokedec_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+gst_smokedec_get_type (void)
+{
+  static GType smokedec_type = 0;
+
+  if (!smokedec_type) {
+    static const GTypeInfo smokedec_info = {
+      sizeof (GstSmokeDecClass),
+      gst_smokedec_base_init,
+      NULL,
+      (GClassInitFunc) gst_smokedec_class_init,
+      NULL,
+      NULL,
+      sizeof (GstSmokeDec),
+      0,
+      (GInstanceInitFunc) gst_smokedec_init,
+    };
+
+    smokedec_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeDec", &smokedec_info,
+        0);
+  }
+  return smokedec_type;
+}
+
+static GstStaticPadTemplate gst_smokedec_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
+    );
+
+static GstStaticPadTemplate gst_smokedec_sink_pad_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("image/x-smoke, "
+        "width = (int) [ 16, 4096 ], "
+        "height = (int) [ 16, 4096 ], " "framerate = (double) [ 1, MAX ]")
+    );
+
+static void
+gst_smokedec_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_smokedec_src_pad_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_smokedec_sink_pad_template));
+  gst_element_class_set_details (element_class, &gst_smokedec_details);
+}
+
+static void
+gst_smokedec_class_init (GstSmokeDec * klass)
+{
+  GstElementClass *gstelement_class;
+
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  GST_DEBUG_CATEGORY_INIT (smokedec_debug, "smokedec", 0, "Smoke decoder");
+}
+
+static void
+gst_smokedec_init (GstSmokeDec * smokedec)
+{
+  GST_DEBUG ("gst_smokedec_init: initializing");
+  /* create the sink and src pads */
+
+  smokedec->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_smokedec_sink_pad_template), "sink");
+  gst_element_add_pad (GST_ELEMENT (smokedec), smokedec->sinkpad);
+  gst_pad_set_chain_function (smokedec->sinkpad, gst_smokedec_chain);
+  gst_pad_set_link_function (smokedec->sinkpad, gst_smokedec_link);
+
+  smokedec->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_smokedec_src_pad_template), "src");
+  gst_pad_use_explicit_caps (smokedec->srcpad);
+  gst_element_add_pad (GST_ELEMENT (smokedec), smokedec->srcpad);
+
+  /* reset the initial video state */
+  smokedec->format = -1;
+  smokedec->width = -1;
+  smokedec->height = -1;
+}
+
+static GstPadLinkReturn
+gst_smokedec_link (GstPad * pad, const GstCaps * caps)
+{
+  GstSmokeDec *smokedec = GST_SMOKEDEC (gst_pad_get_parent (pad));
+  GstStructure *structure;
+  GstCaps *srccaps;
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  gst_structure_get_double (structure, "framerate", &smokedec->fps);
+  gst_structure_get_int (structure, "width", &smokedec->width);
+  gst_structure_get_int (structure, "height", &smokedec->height);
+
+  srccaps = gst_caps_new_simple ("video/x-raw-yuv",
+      "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
+      "width", G_TYPE_INT, smokedec->width,
+      "height", G_TYPE_INT, smokedec->height,
+      "framerate", G_TYPE_DOUBLE, smokedec->fps, NULL);
+
+  /* at this point, we're pretty sure that this will be the output
+   * format, so we'll set it. */
+  gst_pad_set_explicit_caps (smokedec->srcpad, srccaps);
+
+  smokecodec_decode_new (&smokedec->info);
+
+  return GST_PAD_LINK_OK;
+}
+
+static void
+gst_smokedec_chain (GstPad * pad, GstData * _data)
+{
+  GstBuffer *buf = GST_BUFFER (_data);
+  GstSmokeDec *smokedec;
+  guchar *data, *outdata;
+  gulong size, outsize;
+  GstBuffer *outbuf;
+  SmokeCodecFlags flags;
+
+  /*GstMeta *meta; */
+  gint width, height;
+
+  smokedec = GST_SMOKEDEC (GST_OBJECT_PARENT (pad));
+
+  if (!GST_PAD_IS_LINKED (smokedec->srcpad)) {
+    gst_buffer_unref (buf);
+    return;
+  }
+
+  data = (guchar *) GST_BUFFER_DATA (buf);
+  size = GST_BUFFER_SIZE (buf);
+  GST_DEBUG ("gst_smokedec_chain: got buffer of %ld bytes in '%s'", size,
+      GST_OBJECT_NAME (smokedec));
+
+  GST_DEBUG ("gst_smokedec_chain: reading header %08lx", *(gulong *) data);
+  smokecodec_parse_header (smokedec->info, data, size, &flags, &width, &height);
+
+  outbuf = gst_buffer_new ();
+  outsize = GST_BUFFER_SIZE (outbuf) = width * height + width * height / 2;
+  outdata = GST_BUFFER_DATA (outbuf) = g_malloc (outsize);
+  GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
+  GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
+
+  if (smokedec->height != height) {
+    GstCaps *caps;
+
+    smokedec->height = height;
+
+    caps = gst_caps_new_simple ("video/x-raw-yuv",
+        "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'),
+        "width", G_TYPE_INT, width,
+        "height", G_TYPE_INT, height,
+        "framerate", G_TYPE_DOUBLE, smokedec->fps, NULL);
+    gst_pad_set_explicit_caps (smokedec->srcpad, caps);
+    gst_caps_free (caps);
+  }
+
+  smokecodec_decode (smokedec->info, data, size, outdata);
+
+  GST_DEBUG ("gst_smokedec_chain: sending buffer");
+  gst_pad_push (smokedec->srcpad, GST_DATA (outbuf));
+
+  gst_buffer_unref (buf);
+}
diff --git a/ext/jpeg/gstsmokedec.h b/ext/jpeg/gstsmokedec.h
new file mode 100644 (file)
index 0000000..15cb89b
--- /dev/null
@@ -0,0 +1,78 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 2 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.
+ */
+
+
+#ifndef __GST_SMOKEDEC_H__
+#define __GST_SMOKEDEC_H__
+
+
+#include <gst/gst.h>
+#include "smokecodec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GST_TYPE_SMOKEDEC \
+  (gst_smokedec_get_type())
+#define GST_SMOKEDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMOKEDEC,GstSmokeDec))
+#define GST_SMOKEDEC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMOKEDEC,GstSmokeDec))
+#define GST_IS_SMOKEDEC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMOKEDEC))
+#define GST_IS_SMOKEDEC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMOKEDEC))
+
+typedef struct _GstSmokeDec GstSmokeDec;
+typedef struct _GstSmokeDecClass GstSmokeDecClass;
+
+struct _GstSmokeDec {
+  GstElement element;
+
+  /* pads */
+  GstPad *sinkpad,*srcpad;
+
+  /* video state */
+  gint format;
+  gint width;
+  gint height;
+  gdouble fps;
+
+  SmokeCodecInfo *info;
+
+  gint threshold;
+  gint quality;
+  gint smoothing;
+};
+
+struct _GstSmokeDecClass {
+  GstElementClass parent_class;
+};
+
+GType gst_smokedec_get_type(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_SMOKEDEC_H__ */
diff --git a/ext/jpeg/gstsmokeenc.c b/ext/jpeg/gstsmokeenc.c
new file mode 100644 (file)
index 0000000..8776b6f
--- /dev/null
@@ -0,0 +1,373 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 2 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 <string.h>
+
+#include "gstsmokeenc.h"
+#include <gst/video/video.h>
+
+/* elementfactory information */
+GstElementDetails gst_smokeenc_details = {
+  "Smoke image encoder",
+  "Codec/Encoder/Image",
+  "Encode images in the Smoke format",
+  "Wim Taymans <wim@fluendo.com>",
+};
+
+GST_DEBUG_CATEGORY (smokeenc_debug);
+#define GST_CAT_DEFAULT smokeenc_debug
+
+#define SMOKEENC_DEFAULT_MIN_QUALITY 10
+#define SMOKEENC_DEFAULT_MAX_QUALITY 85
+#define SMOKEENC_DEFAULT_THRESHOLD 3000
+#define SMOKEENC_DEFAULT_KEYFRAME 20
+
+/* SmokeEnc signals and args */
+enum
+{
+  FRAME_ENCODED,
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum
+{
+  ARG_0,
+  ARG_MIN_QUALITY,
+  ARG_MAX_QUALITY,
+  ARG_THRESHOLD,
+  ARG_KEYFRAME
+      /* FILL ME */
+};
+
+static void gst_smokeenc_base_init (gpointer g_class);
+static void gst_smokeenc_class_init (GstSmokeEnc * klass);
+static void gst_smokeenc_init (GstSmokeEnc * smokeenc);
+
+static void gst_smokeenc_chain (GstPad * pad, GstData * _data);
+static GstPadLinkReturn gst_smokeenc_link (GstPad * pad, const GstCaps * caps);
+static GstCaps *gst_smokeenc_getcaps (GstPad * pad);
+
+static void gst_smokeenc_resync (GstSmokeEnc * smokeenc);
+static void gst_smokeenc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_smokeenc_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+static GstElementClass *parent_class = NULL;
+
+//static guint gst_smokeenc_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_smokeenc_get_type (void)
+{
+  static GType smokeenc_type = 0;
+
+  if (!smokeenc_type) {
+    static const GTypeInfo smokeenc_info = {
+      sizeof (GstSmokeEncClass),
+      gst_smokeenc_base_init,
+      NULL,
+      (GClassInitFunc) gst_smokeenc_class_init,
+      NULL,
+      NULL,
+      sizeof (GstSmokeEnc),
+      0,
+      (GInstanceInitFunc) gst_smokeenc_init,
+    };
+
+    smokeenc_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstSmokeEnc", &smokeenc_info,
+        0);
+  }
+  return smokeenc_type;
+}
+
+static GstStaticPadTemplate gst_smokeenc_sink_pad_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420"))
+    );
+
+static GstStaticPadTemplate gst_smokeenc_src_pad_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("image/x-smoke, "
+        "width = (int) [ 16, 4096 ], "
+        "height = (int) [ 16, 4096 ], " "framerate = (double) [ 1, MAX ]")
+    );
+
+static void
+gst_smokeenc_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_smokeenc_sink_pad_template));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&gst_smokeenc_src_pad_template));
+  gst_element_class_set_details (element_class, &gst_smokeenc_details);
+}
+
+static void
+gst_smokeenc_class_init (GstSmokeEnc * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+
+  g_object_class_install_property (gobject_class, ARG_MIN_QUALITY,
+      g_param_spec_int ("qmin", "Qmin", "Minimum quality",
+          0, 100, SMOKEENC_DEFAULT_MIN_QUALITY, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MAX_QUALITY,
+      g_param_spec_int ("qmax", "Qmax", "Maximum quality",
+          0, 100, SMOKEENC_DEFAULT_MAX_QUALITY, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_THRESHOLD,
+      g_param_spec_int ("threshold", "Threshold", "Motion estimation threshold",
+          0, 100000000, SMOKEENC_DEFAULT_THRESHOLD, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_KEYFRAME,
+      g_param_spec_int ("keyframe", "Keyframe",
+          "Insert keyframe every N frames", 1, 100000,
+          SMOKEENC_DEFAULT_KEYFRAME, G_PARAM_READWRITE));
+
+  gobject_class->set_property = gst_smokeenc_set_property;
+  gobject_class->get_property = gst_smokeenc_get_property;
+
+  GST_DEBUG_CATEGORY_INIT (smokeenc_debug, "smokeenc", 0,
+      "Smoke encoding element");
+}
+
+static void
+gst_smokeenc_init (GstSmokeEnc * smokeenc)
+{
+  /* create the sink and src pads */
+  smokeenc->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_smokeenc_sink_pad_template), "sink");
+  gst_pad_set_chain_function (smokeenc->sinkpad, gst_smokeenc_chain);
+  gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
+  gst_pad_set_link_function (smokeenc->sinkpad, gst_smokeenc_link);
+  gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->sinkpad);
+
+  smokeenc->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&gst_smokeenc_src_pad_template), "src");
+  gst_pad_set_getcaps_function (smokeenc->sinkpad, gst_smokeenc_getcaps);
+  gst_pad_set_link_function (smokeenc->sinkpad, gst_smokeenc_link);
+  gst_element_add_pad (GST_ELEMENT (smokeenc), smokeenc->srcpad);
+
+  /* reset the initial video state */
+  smokeenc->width = 0;
+  smokeenc->height = 0;
+  smokeenc->frame = 0;
+
+  gst_smokeenc_resync (smokeenc);
+
+  smokeenc->min_quality = SMOKEENC_DEFAULT_MIN_QUALITY;
+  smokeenc->max_quality = SMOKEENC_DEFAULT_MAX_QUALITY;
+  smokeenc->threshold = SMOKEENC_DEFAULT_THRESHOLD;
+  smokeenc->keyframe = SMOKEENC_DEFAULT_KEYFRAME;
+}
+
+static GstCaps *
+gst_smokeenc_getcaps (GstPad * pad)
+{
+  GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
+  GstPad *otherpad;
+  GstCaps *caps;
+  const char *name;
+  int i;
+  GstStructure *structure = NULL;
+
+  /* we want to proxy properties like width, height and framerate from the
+     other end of the element */
+  otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
+  caps = gst_pad_get_allowed_caps (otherpad);
+  if (pad == smokeenc->srcpad) {
+    name = "image/x-smoke";
+  } else {
+    name = "video/x-raw-yuv";
+  }
+  for (i = 0; i < gst_caps_get_size (caps); i++) {
+    structure = gst_caps_get_structure (caps, i);
+
+    gst_structure_set_name (structure, name);
+    gst_structure_remove_field (structure, "format");
+    /* ... but for the sink pad, we only do I420 anyway, so add that */
+    if (pad == smokeenc->sinkpad) {
+      gst_structure_set (structure, "format", GST_TYPE_FOURCC,
+          GST_STR_FOURCC ("I420"), NULL);
+    }
+  }
+
+  return caps;
+}
+
+static GstPadLinkReturn
+gst_smokeenc_link (GstPad * pad, const GstCaps * caps)
+{
+  GstSmokeEnc *smokeenc = GST_SMOKEENC (gst_pad_get_parent (pad));
+  GstStructure *structure;
+  GstPadLinkReturn ret;
+  GstCaps *othercaps;
+  GstPad *otherpad;
+
+  otherpad = (pad == smokeenc->srcpad) ? smokeenc->sinkpad : smokeenc->srcpad;
+
+  structure = gst_caps_get_structure (caps, 0);
+  gst_structure_get_double (structure, "framerate", &smokeenc->fps);
+  gst_structure_get_int (structure, "width", &smokeenc->width);
+  gst_structure_get_int (structure, "height", &smokeenc->height);
+
+  othercaps = gst_caps_copy (gst_pad_get_pad_template_caps (otherpad));
+  gst_caps_set_simple (othercaps,
+      "width", G_TYPE_INT, smokeenc->width,
+      "height", G_TYPE_INT, smokeenc->height,
+      "framerate", G_TYPE_DOUBLE, smokeenc->fps, NULL);
+
+  ret = gst_pad_try_set_caps (smokeenc->srcpad, othercaps);
+  gst_caps_free (othercaps);
+
+  if (GST_PAD_LINK_SUCCESSFUL (ret)) {
+    gst_smokeenc_resync (smokeenc);
+  }
+
+  return ret;
+}
+
+static void
+gst_smokeenc_resync (GstSmokeEnc * smokeenc)
+{
+  GST_DEBUG ("gst_smokeenc_resync: resync");
+
+  smokecodec_encode_new (&smokeenc->info, smokeenc->width, smokeenc->height);
+  smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
+      smokeenc->max_quality);
+
+  GST_DEBUG ("gst_smokeenc_resync: resync done");
+}
+
+static void
+gst_smokeenc_chain (GstPad * pad, GstData * _data)
+{
+  GstBuffer *buf = GST_BUFFER (_data);
+  GstSmokeEnc *smokeenc;
+  guchar *data, *outdata;
+  gulong size;
+  gint outsize, encsize;
+  GstBuffer *outbuf;
+  SmokeCodecFlags flags;
+
+  smokeenc = GST_SMOKEENC (GST_OBJECT_PARENT (pad));
+
+  data = GST_BUFFER_DATA (buf);
+  size = GST_BUFFER_SIZE (buf);
+
+  GST_DEBUG ("gst_smokeenc_chain: got buffer of %ld bytes in '%s'", size,
+      GST_OBJECT_NAME (smokeenc));
+
+  outbuf = gst_buffer_new ();
+  outsize = smokeenc->width * smokeenc->height * 3;
+  outdata = GST_BUFFER_DATA (outbuf) = g_malloc (outsize);
+  GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
+  GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (buf);
+
+  flags = 0;
+  if (smokeenc->frame == 0) {
+    flags |= SMOKECODEC_KEYFRAME;
+  }
+
+  smokeenc->frame = (smokeenc->frame + 1) % smokeenc->keyframe;
+
+  smokecodec_set_quality (smokeenc->info, smokeenc->min_quality,
+      smokeenc->max_quality);
+  smokecodec_set_threshold (smokeenc->info, smokeenc->threshold);
+  smokecodec_encode (smokeenc->info, data, flags, outdata, &encsize);
+  gst_buffer_unref (buf);
+
+  GST_BUFFER_SIZE (outbuf) = encsize;
+  //memset(GST_BUFFER_DATA(outbuf)+encsize, 0, outsize - encsize); 
+
+  gst_pad_push (smokeenc->srcpad, GST_DATA (outbuf));
+}
+
+static void
+gst_smokeenc_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstSmokeEnc *smokeenc;
+
+  g_return_if_fail (GST_IS_SMOKEENC (object));
+  smokeenc = GST_SMOKEENC (object);
+
+  switch (prop_id) {
+    case ARG_MIN_QUALITY:
+      smokeenc->min_quality = g_value_get_int (value);
+      break;
+    case ARG_MAX_QUALITY:
+      smokeenc->max_quality = g_value_get_int (value);
+      break;
+    case ARG_THRESHOLD:
+      smokeenc->threshold = g_value_get_int (value);
+      break;
+    case ARG_KEYFRAME:
+      smokeenc->keyframe = g_value_get_int (value);
+      break;
+    default:
+      break;
+  }
+}
+
+static void
+gst_smokeenc_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstSmokeEnc *smokeenc;
+
+  g_return_if_fail (GST_IS_SMOKEENC (object));
+  smokeenc = GST_SMOKEENC (object);
+
+  switch (prop_id) {
+    case ARG_MIN_QUALITY:
+      g_value_set_int (value, smokeenc->min_quality);
+      break;
+    case ARG_MAX_QUALITY:
+      g_value_set_int (value, smokeenc->max_quality);
+      break;
+    case ARG_THRESHOLD:
+      g_value_set_int (value, smokeenc->threshold);
+      break;
+    case ARG_KEYFRAME:
+      g_value_set_int (value, smokeenc->keyframe);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
diff --git a/ext/jpeg/gstsmokeenc.h b/ext/jpeg/gstsmokeenc.h
new file mode 100644 (file)
index 0000000..c1658cf
--- /dev/null
@@ -0,0 +1,80 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * 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 2 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.
+ */
+
+
+#ifndef __GST_SMOKEENC_H__
+#define __GST_SMOKEENC_H__
+
+
+#include <gst/gst.h>
+#include "smokecodec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GST_TYPE_SMOKEENC \
+  (gst_smokeenc_get_type())
+#define GST_SMOKEENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMOKEENC,GstSmokeEnc))
+#define GST_SMOKEENC_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMOKEENC,GstSmokeEnc))
+#define GST_IS_SMOKEENC(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMOKEENC))
+#define GST_IS_SMOKEENC_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMOKEENC))
+
+typedef struct _GstSmokeEnc GstSmokeEnc;
+typedef struct _GstSmokeEncClass GstSmokeEncClass;
+
+struct _GstSmokeEnc {
+  GstElement element;
+
+  /* pads */
+  GstPad *sinkpad,*srcpad;
+
+  /* video state */
+  gint format;
+  gint width;
+  gint height;
+  gint frame;
+  gint keyframe;
+  gdouble fps;
+
+  SmokeCodecInfo *info;
+
+  gint threshold;
+  gint min_quality;
+  gint max_quality;
+};
+
+struct _GstSmokeEncClass {
+  GstElementClass parent_class;
+};
+
+GType gst_smokeenc_get_type(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GST_SMOKEENC_H__ */
diff --git a/ext/jpeg/smokecodec.c b/ext/jpeg/smokecodec.c
new file mode 100644 (file)
index 0000000..cb16827
--- /dev/null
@@ -0,0 +1,631 @@
+/* Smoke codec
+ * Copyright (C) <2004> Wim Taymans <wim@fluendo.com> 
+ *
+ * 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 2 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/* this is a hack hack hack to get around jpeglib header bugs... */
+#ifdef HAVE_STDLIB_H
+# undef HAVE_STDLIB_H
+#endif
+#include <jpeglib.h>
+
+#include "smokecodec.h"
+
+//#define DEBUG(a...)   printf( a );
+#define DEBUG(a,...)
+
+
+struct _SmokeCodecInfo
+{
+  unsigned int width;
+  unsigned int height;
+
+  unsigned int minquality;
+  unsigned int maxquality;
+  unsigned int bitrate;
+  unsigned int threshold;
+
+  unsigned int refdec;
+
+  unsigned char **line[3];
+  unsigned char *compbuf[3];
+
+  struct jpeg_error_mgr jerr;
+
+  struct jpeg_compress_struct cinfo;
+  struct jpeg_destination_mgr jdest;
+
+  struct jpeg_decompress_struct dinfo;
+  struct jpeg_source_mgr jsrc;
+
+  int need_keyframe;
+  unsigned char *reference;
+};
+
+static void
+smokecodec_init_destination (j_compress_ptr cinfo)
+{
+}
+
+static int
+smokecodec_flush_destination (j_compress_ptr cinfo)
+{
+  return 1;
+}
+
+static void
+smokecodec_term_destination (j_compress_ptr cinfo)
+{
+}
+
+static void
+smokecodec_init_source (j_decompress_ptr cinfo)
+{
+}
+
+static int
+smokecodec_fill_input_buffer (j_decompress_ptr cinfo)
+{
+  return 1;
+}
+
+static void
+smokecodec_skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+}
+
+static int
+smokecodec_resync_to_restart (j_decompress_ptr cinfo, int desired)
+{
+  return 1;
+}
+
+static void
+smokecodec_term_source (j_decompress_ptr cinfo)
+{
+}
+
+
+int
+smokecodec_encode_new (SmokeCodecInfo ** info,
+    const unsigned int width, const unsigned int height)
+{
+  SmokeCodecInfo *newinfo;
+  int i, j;
+  unsigned char *base[3];
+
+  if (!info)
+    return SMOKECODEC_NULLPTR;
+  if ((width & 0xf) || (height & 0xf))
+    return SMOKECODEC_WRONGSIZE;
+
+  newinfo = malloc (sizeof (SmokeCodecInfo));
+  if (!newinfo) {
+    return SMOKECODEC_NOMEM;
+  }
+  newinfo->width = width;
+  newinfo->height = height;
+
+  /* setup jpeglib */
+  memset (&newinfo->cinfo, 0, sizeof (newinfo->cinfo));
+  memset (&newinfo->jerr, 0, sizeof (newinfo->jerr));
+  newinfo->cinfo.err = jpeg_std_error (&newinfo->jerr);
+  jpeg_create_compress (&newinfo->cinfo);
+  newinfo->cinfo.input_components = 3;
+  jpeg_set_defaults (&newinfo->cinfo);
+
+  newinfo->cinfo.dct_method = JDCT_FASTEST;
+
+  newinfo->cinfo.raw_data_in = TRUE;
+  newinfo->cinfo.in_color_space = JCS_YCbCr;
+  newinfo->cinfo.comp_info[0].h_samp_factor = 2;
+  newinfo->cinfo.comp_info[0].v_samp_factor = 2;
+  newinfo->cinfo.comp_info[1].h_samp_factor = 1;
+  newinfo->cinfo.comp_info[1].v_samp_factor = 1;
+  newinfo->cinfo.comp_info[2].h_samp_factor = 1;
+  newinfo->cinfo.comp_info[2].v_samp_factor = 1;
+
+  newinfo->line[0] = malloc (DCTSIZE * 2 * sizeof (char *));
+  newinfo->line[1] = malloc (DCTSIZE * sizeof (char *));
+  newinfo->line[2] = malloc (DCTSIZE * sizeof (char *));
+  base[0] = newinfo->compbuf[0] = malloc (256 * 2 * DCTSIZE * 2 * DCTSIZE);
+  base[1] = newinfo->compbuf[1] = malloc (256 * DCTSIZE * DCTSIZE);
+  base[2] = newinfo->compbuf[2] = malloc (256 * DCTSIZE * DCTSIZE);
+
+  for (i = 0, j = 0; i < 2 * DCTSIZE; i += 2, j++) {
+    newinfo->line[0][i] = base[0];
+    base[0] += 2 * DCTSIZE * 256;
+    newinfo->line[0][i + 1] = base[0];
+    base[0] += 2 * DCTSIZE * 256;
+    newinfo->line[1][j] = base[1];
+    base[1] += DCTSIZE * 256;
+    newinfo->line[2][j] = base[2];
+    base[2] += DCTSIZE * 256;
+  }
+
+  newinfo->jdest.init_destination = smokecodec_init_destination;
+  newinfo->jdest.empty_output_buffer = smokecodec_flush_destination;
+  newinfo->jdest.term_destination = smokecodec_term_destination;
+  newinfo->cinfo.dest = &newinfo->jdest;
+
+  jpeg_suppress_tables (&newinfo->cinfo, FALSE);
+
+  memset (&newinfo->dinfo, 0, sizeof (newinfo->dinfo));
+  newinfo->dinfo.err = jpeg_std_error (&newinfo->jerr);
+  jpeg_create_decompress (&newinfo->dinfo);
+
+  newinfo->jsrc.init_source = smokecodec_init_source;
+  newinfo->jsrc.fill_input_buffer = smokecodec_fill_input_buffer;
+  newinfo->jsrc.skip_input_data = smokecodec_skip_input_data;
+  newinfo->jsrc.resync_to_restart = smokecodec_resync_to_restart;
+  newinfo->jsrc.term_source = smokecodec_term_source;
+  newinfo->dinfo.src = &newinfo->jsrc;
+
+  newinfo->need_keyframe = 1;
+  newinfo->threshold = 4000;
+  newinfo->minquality = 10;
+  newinfo->maxquality = 85;
+  newinfo->reference = malloc (3 * (width * height) / 2);
+  newinfo->refdec = 0;
+
+  *info = newinfo;
+
+  return SMOKECODEC_OK;
+}
+
+int
+smokecodec_decode_new (SmokeCodecInfo ** info)
+{
+  return smokecodec_encode_new (info, 16, 16);
+}
+
+int
+smokecodec_info_free (SmokeCodecInfo * info)
+{
+  free (info->line[0]);
+  free (info->line[1]);
+  free (info->line[2]);
+  free (info->compbuf[0]);
+  free (info->compbuf[1]);
+  free (info->compbuf[2]);
+  free (info->reference);
+  jpeg_destroy_compress (&info->cinfo);
+  jpeg_destroy_decompress (&info->dinfo);
+  free (info);
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_set_quality (SmokeCodecInfo * info,
+    const unsigned int min, const unsigned int max)
+{
+  info->minquality = min;
+  info->maxquality = max;
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_get_quality (SmokeCodecInfo * info,
+    unsigned int *min, unsigned int *max)
+{
+  *min = info->minquality;
+  *max = info->maxquality;
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_set_threshold (SmokeCodecInfo * info, const unsigned int threshold)
+{
+  info->threshold = threshold;
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_get_threshold (SmokeCodecInfo * info, unsigned int *threshold)
+{
+  *threshold = info->threshold;
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_set_bitrate (SmokeCodecInfo * info, const unsigned int bitrate)
+{
+  info->bitrate = bitrate;
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_get_bitrate (SmokeCodecInfo * info, unsigned int *bitrate)
+{
+  *bitrate = info->bitrate;
+
+  return SMOKECODEC_OK;
+}
+
+static void
+find_best_size (int blocks, int *width, int *height)
+{
+  int sqchng;
+  int w, h;
+  int best, bestw;
+  int free;
+
+  sqchng = ceil (sqrt (blocks));
+  w = sqchng;
+  h = sqchng;
+
+  DEBUG ("guess: %d %d\n", w, h);
+
+  free = w * h - blocks;
+  best = free;
+  bestw = w;
+
+  while (w < 256) {
+    DEBUG ("current: %d %d\n", w, h);
+    if (free < best) {
+      best = free;
+      bestw = w;
+      if (free == 0)
+        break;
+    }
+    // if we cannot reduce the height, increase width
+    if (free < w) {
+      w++;
+      free += h;
+    }
+    // reduce height while possible
+    while (free >= w) {
+      h--;
+      free -= w;
+    }
+  }
+  *width = bestw;
+  *height = (blocks + best) / bestw;
+}
+
+static int
+abs_diff (const unsigned char *in1, const unsigned char *in2, const int stride)
+{
+  int s;
+  int i, j, diff;
+
+  s = 0;
+
+  for (i = 0; i < 2 * DCTSIZE; i++) {
+    for (j = 0; j < 2 * DCTSIZE; j++) {
+      diff = in1[j] - in2[j];
+      s += diff * diff;
+    }
+    in1 += stride;
+    in2 += stride;
+  }
+  return s;
+}
+
+static void
+put (const unsigned char *src, unsigned char *dest,
+    int width, int height, int srcstride, int deststride)
+{
+  int i, j;
+
+  for (i = 0; i < height; i++) {
+    for (j = 0; j < width; j++) {
+      dest[j] = src[j];
+    }
+    src += srcstride;
+    dest += deststride;
+  }
+}
+
+/* encoding */
+SmokeCodecResult
+smokecodec_encode (SmokeCodecInfo * info,
+    const unsigned char *in,
+    SmokeCodecFlags flags, unsigned char *out, unsigned int *outsize)
+{
+  unsigned int i, j, s;
+  const unsigned char *ip;
+  unsigned char *op;
+  unsigned int blocks, encoding;
+  unsigned int size;
+  unsigned int width, height;
+  unsigned int blocks_w, blocks_h;
+  unsigned int threshold;
+  unsigned int max;
+
+  if (info->need_keyframe) {
+    flags |= SMOKECODEC_KEYFRAME;
+    info->need_keyframe = 0;
+  }
+
+  if (flags & SMOKECODEC_KEYFRAME)
+    threshold = 0;
+  else
+    threshold = info->threshold;
+
+  ip = in;
+  op = info->reference;
+
+  width = info->width;
+  height = info->height;
+
+  blocks_w = width / (DCTSIZE * 2);
+  blocks_h = height / (DCTSIZE * 2);
+
+  max = blocks_w * blocks_h;
+
+#define STORE16(var, pos, x) \
+   var[pos] = (x >> 8); \
+   var[pos+1] = (x & 0xff);
+
+  /* write dimension */
+  STORE16 (out, 0, width);
+  STORE16 (out, 2, height);
+
+  if (!(flags & SMOKECODEC_KEYFRAME)) {
+    int block = 0;
+
+    blocks = 0;
+    for (i = 0; i < height; i += 2 * DCTSIZE) {
+      for (j = 0; j < width; j += 2 * DCTSIZE) {
+        s = abs_diff (ip, op, width);
+        if (s >= threshold) {
+          STORE16 (out, blocks * 2 + 10, block);
+          blocks++;
+        }
+
+        ip += 2 * DCTSIZE;
+        op += 2 * DCTSIZE;
+        block++;
+      }
+      ip += (2 * DCTSIZE - 1) * width;
+      op += (2 * DCTSIZE - 1) * width;
+    }
+    if (blocks == max) {
+      flags |= SMOKECODEC_KEYFRAME;
+      blocks = 0;
+      encoding = max;
+    } else {
+      encoding = blocks;
+    }
+  } else {
+    blocks = 0;
+    encoding = max;
+  }
+  STORE16 (out, 6, blocks);
+  out[4] = (flags & 0xff);
+
+  DEBUG ("blocks %d, encoding %d\n", blocks, encoding);
+
+  info->jdest.next_output_byte = &out[blocks * 2 + 12];
+  info->jdest.free_in_buffer = (*outsize) - 12;
+
+  if (encoding > 0) {
+    int quality;
+
+    if (!(flags & SMOKECODEC_KEYFRAME))
+      find_best_size (encoding, &blocks_w, &blocks_h);
+
+    DEBUG ("best: %d %d\n", blocks_w, blocks_h);
+
+    info->cinfo.image_width = blocks_w * DCTSIZE * 2;
+    info->cinfo.image_height = blocks_h * DCTSIZE * 2;
+
+    if (flags & SMOKECODEC_KEYFRAME) {
+      quality = (info->maxquality * 60) / 100;
+    } else {
+      quality =
+          info->maxquality - ((info->maxquality -
+              info->minquality) * blocks) / max;
+    }
+
+    DEBUG ("set q %d %d %d\n", quality, encoding, max);
+    jpeg_set_quality (&info->cinfo, quality, TRUE);
+    DEBUG ("start\n");
+    jpeg_start_compress (&info->cinfo, TRUE);
+
+    for (i = 0; i < encoding; i++) {
+      int pos;
+      int x, y;
+
+      if (flags & SMOKECODEC_KEYFRAME)
+        pos = i;
+      else
+        pos = (out[i * 2 + 10] << 8) | (out[i * 2 + 11]);
+
+      x = pos % (width / (DCTSIZE * 2));
+      y = pos / (width / (DCTSIZE * 2));
+
+      ip = in + (x * (DCTSIZE * 2)) + (y * (DCTSIZE * 2) * width);
+      op = info->compbuf[0] + (i % blocks_w) * (DCTSIZE * 2);
+      put (ip, op, 2 * DCTSIZE, 2 * DCTSIZE, width, 256 * (DCTSIZE * 2));
+
+      ip = in + width * height + (x * DCTSIZE) + (y * DCTSIZE * width / 2);
+      op = info->compbuf[1] + (i % blocks_w) * (DCTSIZE);
+      put (ip, op, DCTSIZE, DCTSIZE, width / 2, 256 * DCTSIZE);
+
+      ip = in + 5 * (width * height) / 4 + (x * DCTSIZE) +
+          (y * DCTSIZE * width / 2);
+      op = info->compbuf[2] + (i % blocks_w) * (DCTSIZE);
+      put (ip, op, DCTSIZE, DCTSIZE, width / 2, 256 * DCTSIZE);
+
+      if ((i % blocks_w) == (blocks_w - 1) || (i == encoding - 1)) {
+        DEBUG ("write %d\n", pos);
+        jpeg_write_raw_data (&info->cinfo, info->line, 2 * DCTSIZE);
+      }
+    }
+    DEBUG ("finish\n");
+    jpeg_finish_compress (&info->cinfo);
+  }
+
+  size = ((((*outsize) - 12 - info->jdest.free_in_buffer) + 3) & ~3);
+  out[8] = size >> 8;
+  out[9] = size & 0xff;
+
+  *outsize = size + blocks * 2 + 12;
+  DEBUG ("outsize %d\n", *outsize);
+
+  // and decode in reference frame again
+  if (info->refdec) {
+    smokecodec_decode (info, out, *outsize, info->reference);
+  } else {
+    memcpy (info->reference, in, 3 * (width * height) / 2);
+  }
+
+  return SMOKECODEC_OK;
+}
+
+/* decoding */
+SmokeCodecResult
+smokecodec_parse_header (SmokeCodecInfo * info,
+    const unsigned char *in,
+    const unsigned int insize,
+    SmokeCodecFlags * flags, unsigned int *width, unsigned int *height)
+{
+
+  *width = in[0] << 8 | in[1];
+  *height = in[2] << 8 | in[3];
+  *flags = in[4];
+
+  if (info->width != *width || info->height != *height) {
+    DEBUG ("new width: %d %d\n", *width, *height);
+
+    info->reference = realloc (info->reference, 3 * ((*width) * (*height)) / 2);
+    info->width = *width;
+    info->height = *height;
+  }
+
+  return SMOKECODEC_OK;
+}
+
+SmokeCodecResult
+smokecodec_decode (SmokeCodecInfo * info,
+    const unsigned char *in, const unsigned int insize, unsigned char *out)
+{
+  unsigned int width, height;
+  SmokeCodecFlags flags;
+  int i, j;
+  int blocks_w, blocks_h;
+  int blockptr;
+  int blocks, decoding;
+  const unsigned char *ip;
+  unsigned char *op;
+  int res;
+
+  smokecodec_parse_header (info, in, insize, &flags, &width, &height);
+
+  blocks = in[6] << 8 | in[7];
+  DEBUG ("blocks %d\n", blocks);
+
+  if (flags & SMOKECODEC_KEYFRAME)
+    decoding = width / (DCTSIZE * 2) * height / (DCTSIZE * 2);
+  else
+    decoding = blocks;
+
+
+  if (decoding > 0) {
+    info->jsrc.next_input_byte = &in[blocks * 2 + 12];
+    info->jsrc.bytes_in_buffer = insize - (blocks * 2 + 12);
+
+    DEBUG ("header %02x %d\n", in[blocks * 2 + 12], insize);
+    res = jpeg_read_header (&info->dinfo, TRUE);
+    DEBUG ("header %d %d %d\n", res, info->dinfo.image_width,
+        info->dinfo.image_height);
+
+    blocks_w = info->dinfo.image_width / (2 * DCTSIZE);
+    blocks_h = info->dinfo.image_height / (2 * DCTSIZE);
+
+    info->dinfo.output_width = info->dinfo.image_width;
+    info->dinfo.output_height = info->dinfo.image_height;
+
+    DEBUG ("start\n");
+    info->dinfo.do_fancy_upsampling = FALSE;
+    info->dinfo.do_block_smoothing = FALSE;
+    info->dinfo.out_color_space = JCS_YCbCr;
+    info->dinfo.dct_method = JDCT_IFAST;
+    info->dinfo.raw_data_out = TRUE;
+    jpeg_start_decompress (&info->dinfo);
+
+    blockptr = 0;
+
+    for (i = 0; i < blocks_h; i++) {
+      DEBUG ("read\n");
+      jpeg_read_raw_data (&info->dinfo, info->line, 2 * DCTSIZE);
+
+      DEBUG ("copy %d\n", blocks_w);
+      for (j = 0; j < blocks_w; j++) {
+        int pos;
+        int x, y;
+
+        if (flags & SMOKECODEC_KEYFRAME)
+          pos = blockptr;
+        else
+          pos = (in[blockptr * 2 + 10] << 8) | (in[blockptr * 2 + 11]);
+
+        x = pos % (width / (DCTSIZE * 2));
+        y = pos / (width / (DCTSIZE * 2));
+
+        DEBUG ("block %d %d %d\n", pos, x, y);
+
+        ip = info->compbuf[0] + j * (DCTSIZE * 2);
+        op = info->reference + (x * (DCTSIZE * 2)) +
+            (y * (DCTSIZE * 2) * width);
+        put (ip, op, 2 * DCTSIZE, 2 * DCTSIZE, 256 * (DCTSIZE * 2), width);
+
+        ip = info->compbuf[1] + j * (DCTSIZE);
+        op = info->reference + width * height + (x * DCTSIZE) +
+            (y * DCTSIZE * width / 2);
+        put (ip, op, DCTSIZE, DCTSIZE, 256 * DCTSIZE, width / 2);
+
+        ip = info->compbuf[2] + j * (DCTSIZE);
+        op = info->reference + 5 * (width * height) / 4 + (x * DCTSIZE) +
+            (y * DCTSIZE * width / 2);
+        put (ip, op, DCTSIZE, DCTSIZE, 256 * DCTSIZE, width / 2);
+
+        DEBUG ("block done %d %d %d\n", pos, x, y);
+        blockptr++;
+        if (blockptr >= decoding)
+          break;
+      }
+    }
+    DEBUG ("finish\n");
+    jpeg_finish_decompress (&info->dinfo);
+  }
+
+  DEBUG ("copy\n");
+  if (out != info->reference)
+    memcpy (out, info->reference, 3 * (width * height) / 2);
+  DEBUG ("copy done\n");
+
+  return SMOKECODEC_OK;
+}
diff --git a/ext/jpeg/smokecodec.h b/ext/jpeg/smokecodec.h
new file mode 100644 (file)
index 0000000..6ffc5ef
--- /dev/null
@@ -0,0 +1,96 @@
+/* Smoke Codec
+ * Copyright (C) <2004> Wim Taymans <wim@fluendo.com>
+ *
+ * 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 2 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.
+ */
+
+
+#ifndef __SMOKECODEC_H__
+#define __SMOKECODEC_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+typedef struct _SmokeCodecInfo SmokeCodecInfo;
+
+typedef enum {
+  SMOKECODEC_WRONGSIZE         = -4,
+  SMOKECODEC_ERROR             = -3,
+  SMOKECODEC_NOMEM             = -2,
+  SMOKECODEC_NULLPTR           = -1,
+  SMOKECODEC_OK                =  0,
+} SmokeCodecResult;
+       
+typedef enum {
+  SMOKECODEC_KEYFRAME                  = (1<<0),
+  SMOKECODEC_MOTION_VECTORS    = (1<<1)
+} SmokeCodecFlags;
+       
+
+/* init */
+int                    smokecodec_encode_new   (SmokeCodecInfo **info,
+                                                const unsigned int width,
+                                                const unsigned int height);
+
+int                    smokecodec_decode_new   (SmokeCodecInfo **info);
+
+/* config */
+SmokeCodecResult       smokecodec_set_quality  (SmokeCodecInfo *info,
+                                                const unsigned int min,
+                                                const unsigned int max);
+SmokeCodecResult       smokecodec_get_quality  (SmokeCodecInfo *info,
+                                                unsigned int *min,
+                                                unsigned int *max);
+
+SmokeCodecResult       smokecodec_set_threshold (SmokeCodecInfo *info,
+                                                const unsigned int threshold);
+SmokeCodecResult       smokecodec_get_threshold (SmokeCodecInfo *info,
+                                                unsigned int *threshold);
+
+SmokeCodecResult       smokecodec_set_bitrate  (SmokeCodecInfo *info,
+                                                const unsigned int bitrate);
+SmokeCodecResult       smokecodec_get_bitrate  (SmokeCodecInfo *info,
+                                                unsigned int *bitrate);
+
+/* encoding */
+SmokeCodecResult       smokecodec_encode       (SmokeCodecInfo *info,
+                                                const unsigned char *in,
+                                                SmokeCodecFlags flags,
+                                                unsigned char *out,
+                                                unsigned int *outsize);
+
+/* decoding */
+SmokeCodecResult       smokecodec_parse_header (SmokeCodecInfo *info,
+                                                const unsigned char *in,
+                                                const unsigned int insize,
+                                                SmokeCodecFlags *flags,
+                                                unsigned int *width,
+                                                unsigned int *height);
+
+SmokeCodecResult       smokecodec_decode       (SmokeCodecInfo *info,
+                                                const unsigned char *in,
+                                                const unsigned int insize,
+                                                unsigned char *out);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __SMOKECODEC_H__ */