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
This commit is contained in:
Wim Taymans 2004-06-08 11:47:35 +00:00
parent e232b09d50
commit 37d98c27b9
10 changed files with 1555 additions and 1 deletions

View file

@ -1,3 +1,32 @@
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> 2004-06-08 Wim Taymans <wim@fluendo.com>
* gst/multipart/multipartmux.c: (gst_multipart_mux_class_init), * gst/multipart/multipartmux.c: (gst_multipart_mux_class_init),

View file

@ -1,7 +1,7 @@
plugin_LTLIBRARIES = libgstjpeg.la 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_CFLAGS = $(GST_CFLAGS)
libgstjpeg_la_LIBADD = $(JPEG_LIBS) libgstjpeg_la_LIBADD = $(JPEG_LIBS)
libgstjpeg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstjpeg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)

20
ext/jpeg/README Normal file
View file

@ -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

View file

@ -22,6 +22,8 @@
#include "gstjpegdec.h" #include "gstjpegdec.h"
#include "gstjpegenc.h" #include "gstjpegenc.h"
#include "gstsmokeenc.h"
#include "gstsmokedec.h"
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
@ -34,6 +36,14 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_JPEGDEC)) GST_TYPE_JPEGDEC))
return FALSE; 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; return TRUE;
} }

237
ext/jpeg/gstsmokedec.c Normal file
View file

@ -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);
}

78
ext/jpeg/gstsmokedec.h Normal file
View file

@ -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__ */

373
ext/jpeg/gstsmokeenc.c Normal file
View file

@ -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;
}
}

80
ext/jpeg/gstsmokeenc.h Normal file
View file

@ -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__ */

631
ext/jpeg/smokecodec.c Normal file
View file

@ -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;
}

96
ext/jpeg/smokecodec.h Normal file
View file

@ -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__ */