mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
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:
parent
e232b09d50
commit
37d98c27b9
10 changed files with 1555 additions and 1 deletions
29
ChangeLog
29
ChangeLog
|
@ -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),
|
||||||
|
|
|
@ -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
20
ext/jpeg/README
Normal 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
|
|
@ -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
237
ext/jpeg/gstsmokedec.c
Normal 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
78
ext/jpeg/gstsmokedec.h
Normal 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
373
ext/jpeg/gstsmokeenc.c
Normal 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
80
ext/jpeg/gstsmokeenc.h
Normal 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
631
ext/jpeg/smokecodec.c
Normal 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
96
ext/jpeg/smokecodec.h
Normal 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__ */
|
Loading…
Reference in a new issue