diff options
| author | Wim Taymans <wim.taymans@gmail.com> | 2004-06-08 11:47:35 +0000 | 
|---|---|---|
| committer | Wim Taymans <wim.taymans@gmail.com> | 2004-06-08 11:47:35 +0000 | 
| commit | 37d98c27b93ec62742df3b723bd3fa50695f78a2 (patch) | |
| tree | 63a090ff7dd4825abad1987cc870b95514b167f2 | |
| parent | e232b09d5040289895c078e98947648688488e84 (diff) | |
ext/jpeg/: Added a new simple jpeg based codec
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
| -rw-r--r-- | ChangeLog | 29 | ||||
| -rw-r--r-- | ext/jpeg/Makefile.am | 2 | ||||
| -rw-r--r-- | ext/jpeg/README | 20 | ||||
| -rw-r--r-- | ext/jpeg/gstjpeg.c | 10 | ||||
| -rw-r--r-- | ext/jpeg/gstsmokedec.c | 237 | ||||
| -rw-r--r-- | ext/jpeg/gstsmokedec.h | 78 | ||||
| -rw-r--r-- | ext/jpeg/gstsmokeenc.c | 373 | ||||
| -rw-r--r-- | ext/jpeg/gstsmokeenc.h | 80 | ||||
| -rw-r--r-- | ext/jpeg/smokecodec.c | 631 | ||||
| -rw-r--r-- | ext/jpeg/smokecodec.h | 96 | 
10 files changed, 1555 insertions, 1 deletions
| @@ -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 diff --git a/ext/jpeg/Makefile.am b/ext/jpeg/Makefile.am index 351a2e77..15ff3f75 100644 --- a/ext/jpeg/Makefile.am +++ b/ext/jpeg/Makefile.am @@ -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 index 00000000..ffa1d0c0 --- /dev/null +++ b/ext/jpeg/README @@ -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 diff --git a/ext/jpeg/gstjpeg.c b/ext/jpeg/gstjpeg.c index cf8af9fc..58749534 100644 --- a/ext/jpeg/gstjpeg.c +++ b/ext/jpeg/gstjpeg.c @@ -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 index 00000000..34a86785 --- /dev/null +++ b/ext/jpeg/gstsmokedec.c @@ -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 index 00000000..15cb89bb --- /dev/null +++ b/ext/jpeg/gstsmokedec.h @@ -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 index 00000000..8776b6fd --- /dev/null +++ b/ext/jpeg/gstsmokeenc.c @@ -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 index 00000000..c1658cf9 --- /dev/null +++ b/ext/jpeg/gstsmokeenc.h @@ -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 index 00000000..cb16827f --- /dev/null +++ b/ext/jpeg/smokecodec.c @@ -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 index 00000000..6ffc5ef8 --- /dev/null +++ b/ext/jpeg/smokecodec.h @@ -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__ */ | 
