diff options
69 files changed, 12465 insertions, 0 deletions
diff --git a/gst/chart/.gitignore b/gst/chart/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/chart/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/chart/Makefile.am b/gst/chart/Makefile.am new file mode 100644 index 000000000..c85c60af3 --- /dev/null +++ b/gst/chart/Makefile.am @@ -0,0 +1,4 @@ +filterdir = $(libdir)/gst +filter_LTLIBRARIES = libchart.la +libchart_la_SOURCES = gstchart.c +libchart_la_CFLAGS = $(GST_CFLAGS) diff --git a/gst/chart/gstchart.c b/gst/chart/gstchart.c new file mode 100644 index 000000000..1ec259705 --- /dev/null +++ b/gst/chart/gstchart.c @@ -0,0 +1,456 @@ +/* gstchart.c: implementation of chart drawing element + * Copyright (C) <2001> Richard Boulton <richard@tartarus.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 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. + */ + +#include <config.h> +#include <gst/gst.h> + +#define GST_TYPE_CHART (gst_chart_get_type()) +#define GST_CHART(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CHART,GstChart)) +#define GST_CHART_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CHART,GstChart)) +#define GST_IS_CHART(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CHART)) +#define GST_IS_CHART_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CHART)) + +typedef struct _GstChart GstChart; +typedef struct _GstChartClass GstChartClass; + +struct _GstChart { + GstElement element; + + /* pads */ + GstPad *sinkpad,*srcpad; + GstBufferPool *peerpool; + + // the timestamp of the next frame + guint64 next_time; + + // video state + gint bpp; + gint depth; + gint width; + gint height; + + gint samplerate; + gint framerate; // desired frame rate + gint samples_between_frames; // number of samples between start of successive frames + gint samples_since_last_frame; // number of samples between start of successive frames +}; + +struct _GstChartClass { + GstElementClass parent_class; +}; + +GType gst_chart_get_type(void); + + +/* elementfactory information */ +static GstElementDetails gst_chart_details = { + "chart drawer", + "Filter/Visualization", + "Takes frames of data and outputs video frames of a chart of data", + VERSION, + "Richard Boulton <richard@tartarus.org>", + "(C) 2001", +}; + +/* signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + /* FILL ME */ +}; + +static GstPadTemplate* +src_template_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_padtemplate_new ( + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + gst_caps_new ( + "chartsrc", + "video/raw", + /*gst_props_new ( + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), + "bpp", GST_PROPS_INT (32), + "depth", GST_PROPS_INT (32), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "red_mask", GST_PROPS_INT (0xff0000), + "green_mask", GST_PROPS_INT (0xff00), + "blue_mask", GST_PROPS_INT (0xff), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096), + NULL),*/ + gst_props_new ( + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), + "bpp", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "red_mask", GST_PROPS_INT (0xf800), + "green_mask", GST_PROPS_INT (0x07e0), + "blue_mask", GST_PROPS_INT (0x001f), + "width", GST_PROPS_INT_RANGE (16, 4096), + "height", GST_PROPS_INT_RANGE (16, 4096), + NULL) + ), + NULL); + } + return template; +} + +static GstPadTemplate* +sink_template_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_padtemplate_new ( + "sink", /* the name of the pads */ + GST_PAD_SINK, /* type of the pad */ + GST_PAD_ALWAYS, /* ALWAYS/SOMETIMES */ + gst_caps_new ( + "chartsink", /* the name of the caps */ + "audio/raw", /* the mime type of the caps */ + gst_props_new ( + /* Properties follow: */ + "format", GST_PROPS_STRING ("int"), + "law", GST_PROPS_INT (0), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "signed", GST_PROPS_BOOLEAN (TRUE), + "width", GST_PROPS_INT (16), + "depth", GST_PROPS_INT (16), + "rate", GST_PROPS_INT_RANGE (8000, 96000), + "channels", GST_PROPS_INT (1), + NULL) + ), + NULL); + } + + return template; +} + + + + +static void gst_chart_class_init (GstChartClass *klass); +static void gst_chart_init (GstChart *chart); + +static void gst_chart_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_chart_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static void gst_chart_chain (GstPad *pad, GstBuffer *buf); + +static GstElementClass *parent_class = NULL; + +static void gst_chart_newsinkcaps (GstPad *pad, GstCaps *caps); +static void gst_chart_newsrccaps (GstPad *pad, GstCaps *caps); + +GType +gst_chart_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof(GstChartClass), NULL, NULL, (GClassInitFunc)gst_chart_class_init, + NULL, + NULL, + sizeof(GstChart), + 0, + (GInstanceInitFunc)gst_chart_init, + }; + type = g_type_register_static(GST_TYPE_ELEMENT, "GstChart", &info, 0); + } + return type; +} + +static void +gst_chart_class_init(GstChartClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_chart_set_property; + gobject_class->get_property = gst_chart_get_property; +} + +static void +gst_chart_init (GstChart *chart) +{ + /* create the sink and src pads */ + chart->sinkpad = gst_pad_new_from_template (sink_template_factory (), "sink"); + chart->srcpad = gst_pad_new_from_template (src_template_factory (), "src"); + gst_element_add_pad (GST_ELEMENT (chart), chart->sinkpad); + gst_element_add_pad (GST_ELEMENT (chart), chart->srcpad); + + gst_pad_set_chain_function (chart->sinkpad, gst_chart_chain); + gst_pad_set_newcaps_function (chart->sinkpad, gst_chart_newsinkcaps); + gst_pad_set_newcaps_function (chart->srcpad, gst_chart_newsrccaps); + + + chart->next_time = 0; + chart->peerpool = NULL; + + // reset the initial video state + chart->bpp = 16; + chart->depth = 16; + chart->width = -1; + chart->height = -1; + + chart->samplerate = -1; + chart->framerate = 25; // desired frame rate + chart->samples_between_frames = 0; // number of samples between start of successive frames + chart->samples_since_last_frame = 0; +} + +static void +gst_chart_newsinkcaps (GstPad *pad, GstCaps *caps) +{ + GstChart *chart; + chart = GST_CHART (gst_pad_get_parent (pad)); + + chart->samplerate = gst_caps_get_int (caps, "rate"); + chart->samples_between_frames = chart->samplerate / chart->framerate; + + GST_DEBUG (0, "CHART: new sink caps: rate %d\n", + chart->samplerate); + //gst_chart_sync_parms (chart); +} + +static void +gst_chart_newsrccaps (GstPad *pad, GstCaps *caps) +{ + GstChart *chart; + chart = GST_CHART (gst_pad_get_parent (pad)); + + chart->bpp = gst_caps_get_int (caps, "bpp"); + chart->depth = gst_caps_get_int (caps, "depth"); + chart->width = gst_caps_get_int (caps, "width"); + chart->height = gst_caps_get_int (caps, "height"); + + GST_DEBUG (0, "CHART: new src caps: bpp %d, depth %d, width %d, height %d\n", + chart->bpp, chart->depth, chart->width, chart->height); + //gst_chart_sync_parms (chart); +} + +static void +gst_chart_free (GstChart *chart) +{ + g_free (chart); +} + +static void +draw_chart_16bpp(guchar * output, gint width, gint height, + gint16 * src_data, gint src_size) +{ + gint i; + guint16 *colstart; + gint16 * in; + + GST_DEBUG (0, "CHART: drawing frame to %p, width = %d, height = %d, src_data = %p, src_size = %d\n", + output, width, height, src_data, src_size); + + for (colstart = (guint16 *)output, in = (gint16 *)src_data, i = 0; + i < width; + colstart++, in++, i++) { + guint16 * pos = colstart; + gint h1; + + h1 = (((gint)(*in)) * height / (1 << 16)) + height / 2; + if (h1 >= height) h1 = height; + + if (h1 < height / 2) { + while (pos < colstart + h1 * width) { + *pos = 0x0000; + pos += width; + } + while (pos < colstart + height / 2 * width) { + *pos = 0x07e0; + pos += width; + } + while (pos < colstart + height * width) { + *pos = 0x0000; + pos += width; + } + } else { + while (pos < colstart + height / 2 * width) { + *pos = 0x0000; + pos += width; + } + while (pos < colstart + h1 * width) { + *pos = 0x07e0; + pos += width; + } + while (pos < colstart + height * width) { + *pos = 0x0000; + pos += width; + } + } + } +} + +static void +gst_chart_chain (GstPad *pad, GstBuffer *bufin) +{ + GstChart *chart; + GstBuffer *bufout; + guint32 samples_in; + guint32 sizeout; + gint16 *datain; + guchar *dataout; + + g_return_if_fail (bufin != NULL); + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD(pad)); + g_return_if_fail (GST_IS_CHART(GST_OBJECT_PARENT(pad))); + chart = GST_CHART(GST_OBJECT_PARENT (pad)); + g_return_if_fail (chart != NULL); + + GST_DEBUG (0, "CHART: chainfunc called\n"); + + samples_in = GST_BUFFER_SIZE (bufin) / sizeof(gint16); + datain = (gint16 *) (GST_BUFFER_DATA (bufin)); + GST_DEBUG (0, "input buffer has %d samples\n", samples_in); + if (chart->next_time <= GST_BUFFER_TIMESTAMP (bufin)) { + chart->next_time = GST_BUFFER_TIMESTAMP (bufin); + GST_DEBUG (0, "in: %lld\n", GST_BUFFER_TIMESTAMP (bufin)); + } + + chart->samples_since_last_frame += samples_in; + if (chart->samples_between_frames <= chart->samples_since_last_frame) { + chart->samples_since_last_frame = 0; + + // Check if we need to renegotiate size. + if (chart->width == -1 || chart->height == -1) { + chart->width = 256; + chart->height = 128; + GST_DEBUG (0, "making new pad\n"); + gst_pad_set_caps (chart->srcpad, + gst_caps_new ( + "chartsrc", + "video/raw", + gst_props_new ( + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R','G','B',' ')), + "bpp", GST_PROPS_INT (chart->bpp), + "depth", GST_PROPS_INT (chart->depth), + "endianness", GST_PROPS_INT (G_BYTE_ORDER), + "red_mask", GST_PROPS_INT (0xf800), + "green_mask", GST_PROPS_INT (0x07e0), + "blue_mask", GST_PROPS_INT (0x001f), + "width", GST_PROPS_INT (chart->width), + "height", GST_PROPS_INT (chart->height), + NULL))); + } + + // get data to draw into buffer + if (samples_in >= chart->width) { + // make a new buffer for the output + bufout = gst_buffer_new (); + sizeout = chart->bpp / 8 * chart->width * chart->height; + dataout = g_malloc (sizeout); + GST_BUFFER_SIZE(bufout) = sizeout; + GST_BUFFER_DATA(bufout) = dataout; + GST_DEBUG (0, "CHART: made new buffer: size %d, width %d, height %d\n", + sizeout, chart->width, chart->height); + + // take data and draw to new buffer + // FIXME: call different routines for different properties + draw_chart_16bpp(dataout, chart->width, chart->height, (gint16 *)datain, samples_in); + + // set timestamp + GST_BUFFER_TIMESTAMP (bufout) = chart->next_time; + + GST_DEBUG (0, "CHART: outputting buffer\n"); + // output buffer + GST_BUFFER_FLAG_SET (bufout, GST_BUFFER_READONLY); + gst_pad_push (chart->srcpad, bufout); + } + } else { + GST_DEBUG (0, "CHART: skipping buffer\n"); + } + + gst_buffer_unref(bufin); + GST_DEBUG (0, "CHART: exiting chainfunc\n"); +} + +static void +gst_chart_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstChart *chart; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_CHART (object)); + chart = GST_CHART (object); + + switch (prop_id) { + default: + break; + } +} + +static void +gst_chart_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstChart *chart; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail (GST_IS_CHART (object)); + chart = GST_CHART (object); + + switch (prop_id) { + default: + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the chart element */ + factory = gst_elementfactory_new("chart",GST_TYPE_CHART, + &gst_chart_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, src_template_factory ()); + gst_elementfactory_add_padtemplate (factory, sink_template_factory ()); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "chart", + plugin_init +}; diff --git a/gst/deinterlace/.gitignore b/gst/deinterlace/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/deinterlace/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/deinterlace/Makefile.am b/gst/deinterlace/Makefile.am new file mode 100644 index 000000000..eafa47d58 --- /dev/null +++ b/gst/deinterlace/Makefile.am @@ -0,0 +1,9 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstdeinterlace.la + +libgstdeinterlace_la_SOURCES = gstdeinterlace.c +libgstdeinterlace_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstdeinterlace.h + diff --git a/gst/deinterlace/gstdeinterlace.c b/gst/deinterlace/gstdeinterlace.c new file mode 100644 index 000000000..99a70c84e --- /dev/null +++ b/gst/deinterlace/gstdeinterlace.c @@ -0,0 +1,390 @@ +/* Gnome-Streamer + * 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. + */ +/* based on the Area Based Deinterlacer (for RGB frames) */ +/* (a VirtualDub filter) from Gunnar Thalin <guth@home.se> */ + +#include <string.h> +#include <gst/gst.h> +#include "gstdeinterlace.h" + +static GstElementDetails deinterlace_details = { + "DeInterlace", + "Filter/Effect", + "Deinterlace video", + VERSION, + "Wim Taymans <wim.taymans@chello.be>", + "(C) 2001", +}; + + +/* Filter signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_DI_ONLY, + ARG_BLEND, + ARG_THRESHOLD, + ARG_EDGE_DETECT, +}; + +GST_PADTEMPLATE_FACTORY (deinterlace_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "deinterlace_src", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")) + ) +) + +GST_PADTEMPLATE_FACTORY (deinterlace_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "deinterlace_src", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")) + ) +) + +static GType gst_deinterlace_get_type (void); + +static void gst_deinterlace_class_init (GstDeInterlaceClass *klass); +static void gst_deinterlace_init (GstDeInterlace *filter); + +static void gst_deinterlace_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_deinterlace_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static void gst_deinterlace_chain (GstPad *pad, GstBuffer *buf); + +static GstElementClass *parent_class = NULL; +//static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; + +static GstPadNegotiateReturn +deinterlace_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstDeInterlace* filter = GST_DEINTERLACE (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->sinkpad,caps); +} + +static GstPadNegotiateReturn +deinterlace_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstDeInterlace* filter = GST_DEINTERLACE (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->srcpad,caps); +} + +static GType +gst_deinterlace_get_type(void) { + static GType deinterlace_type = 0; + + if (!deinterlace_type) { + static const GTypeInfo deinterlace_info = { + sizeof(GstDeInterlaceClass), NULL, + NULL, + (GClassInitFunc)gst_deinterlace_class_init, + NULL, + NULL, + sizeof(GstDeInterlace), + 0, + (GInstanceInitFunc)gst_deinterlace_init, + }; + deinterlace_type = g_type_register_static(GST_TYPE_ELEMENT, "GstDeInterlace", &deinterlace_info, 0); + } + return deinterlace_type; +} + +static void +gst_deinterlace_class_init (GstDeInterlaceClass *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(G_OBJECT_CLASS(klass), ARG_DI_ONLY, + g_param_spec_boolean("di_area_only","di_area_only","di_area_only", + TRUE,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BLEND, + g_param_spec_boolean("blend","blend","blend", + TRUE,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_THRESHOLD, + g_param_spec_int("threshold","threshold","threshold", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_EDGE_DETECT, + g_param_spec_int("edge_detect","edge_detect","edge_detect", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + + gobject_class->set_property = gst_deinterlace_set_property; + gobject_class->get_property = gst_deinterlace_get_property; +} + +static void +gst_deinterlace_newcaps (GstPad *pad, GstCaps *caps) +{ + GstDeInterlace *filter; + + filter = GST_DEINTERLACE(gst_pad_get_parent (pad)); + + filter->width = gst_caps_get_int (caps, "width"); + filter->height = gst_caps_get_int (caps, "height"); + + if (filter->picsize != (filter->width*filter->height)) { + if (filter->src) + g_free(filter->src); + filter->picsize = filter->width*filter->height; + filter->src = g_malloc(filter->picsize); + } +} + +static void +gst_deinterlace_init (GstDeInterlace *filter) +{ + filter->sinkpad = gst_pad_new_from_template(deinterlace_sink_factory (),"sink"); + gst_pad_set_negotiate_function(filter->sinkpad,deinterlace_negotiate_sink); + gst_pad_set_chain_function(filter->sinkpad,gst_deinterlace_chain); + gst_pad_set_newcaps_function(filter->sinkpad,gst_deinterlace_newcaps); + gst_element_add_pad(GST_ELEMENT(filter),filter->sinkpad); + + filter->srcpad = gst_pad_new_from_template(deinterlace_src_factory (),"src"); + gst_pad_set_negotiate_function(filter->srcpad,deinterlace_negotiate_src); + gst_element_add_pad(GST_ELEMENT(filter),filter->srcpad); + + filter->show_deinterlaced_area_only = FALSE; + filter->blend = FALSE; + //filter->threshold_blend = 0; + filter->threshold = 50; + filter->edge_detect = 25; + + filter->src = NULL; + filter->picsize = 0; +} + +static void +gst_deinterlace_chain (GstPad *pad, GstBuffer *buf) +{ + GstDeInterlace *filter; + gint y0, y1, y2, y3; + guchar *psrc1, *psrc2, *psrc3, *pdst1, *yuvptr, *src; + gint iInterlaceValue0, iInterlaceValue1, iInterlaceValue2; + gint x, y; + gint y_line; + guchar *y_dst, *y_src; + gboolean bBlend; + gint iThreshold; + gint iEdgeDetect; + gint width, height; + gboolean bShowDeinterlacedAreaOnly; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + filter = GST_DEINTERLACE (gst_pad_get_parent (pad)); + + bBlend = filter->blend; + iThreshold = filter->threshold; + iEdgeDetect = filter->edge_detect; + width = filter->width; + height = filter->height; + src = filter->src; + yuvptr = GST_BUFFER_DATA (buf); + bShowDeinterlacedAreaOnly = filter->show_deinterlaced_area_only; + + memcpy(filter->src, yuvptr, filter->picsize); + + y_dst = yuvptr; // dst y pointer + // we should not change u,v because one u, v value stands for + // 2 pixels per 2 lines = 4 pixel and we don't want to change + // the color of + + y_line = width; + y_src = src; + + iThreshold = iThreshold * iThreshold * 4; + // We don't want an integer overflow in the interlace calculation. + if (iEdgeDetect > 180) + iEdgeDetect = 180; + iEdgeDetect = iEdgeDetect * iEdgeDetect; + + y1 = 0; // Avoid compiler warning. The value is not used. + for (x = 0; x < width; x++) { + psrc3 = y_src + x; + y3 = *psrc3; + psrc2 = psrc3 + y_line; + y2 = *psrc2; + pdst1 = y_dst + x; + iInterlaceValue1 = iInterlaceValue2 = 0; + for (y = 0; y <= height; y++) { + psrc1 = psrc2; + psrc2 = psrc3; + psrc3 = psrc3 + y_line; + y0 = y1; + y1 = y2; + y2 = y3; + if (y < height - 1) { + y3 = *psrc3; + } else { + y3 = y1; + } + + iInterlaceValue0 = iInterlaceValue1; + iInterlaceValue1 = iInterlaceValue2; + + if (y < height) + iInterlaceValue2 = ((y1 - y2) * (y3 - y2) - + ((iEdgeDetect * (y1 - y3) * (y1 - y3)) >> 12))*10; + else + iInterlaceValue2 = 0; + + if (y > 0) { + if (iInterlaceValue0 + 2 * iInterlaceValue1 + iInterlaceValue2 > iThreshold) { + if (bBlend) { + *pdst1 = (unsigned char)((y0 + 2*y1 + y2) >> 2); + } else { + // this method seems to work better than blending if the + // quality is pretty bad and the half pics don't fit together + if ((y % 2)==1) { // if odd simply copy the value + *pdst1 = *psrc1; + //*pdst1 = 0; // FIXME this is for adjusting an initial iThreshold + } else { // even interpolate the even line (upper + lower)/2 + *pdst1 = (unsigned char)((y0 + y2) >> 1); + //*pdst1 = 0; // FIXME this is for adjusting an initial iThreshold + } + } + } else { + // so we went below the treshold and therefore we don't have to + // change anything + if (bShowDeinterlacedAreaOnly) { + // this is for testing to see how we should tune the treshhold + // and shows as the things that haven't change because the + // threshhold was to low?? (or shows that everything is ok :-) + *pdst1 = 0; // blank the point and so the interlac area + } else { + *pdst1 = *psrc1; + } + } + pdst1 = pdst1 + y_line; + } + } + } + + gst_pad_push (filter->srcpad, buf); +} + +static void +gst_deinterlace_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstDeInterlace *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_DEINTERLACE(object)); + + filter = GST_DEINTERLACE(object); + + switch (prop_id) + { + case ARG_DI_ONLY: + filter->show_deinterlaced_area_only = g_value_get_boolean (value); + break; + case ARG_BLEND: + filter->blend = g_value_get_boolean (value); + break; + case ARG_THRESHOLD: + filter->threshold = g_value_get_int (value); + break; + case ARG_EDGE_DETECT: + filter->edge_detect = g_value_get_int (value); + break; + default: + break; + } +} + +static void +gst_deinterlace_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstDeInterlace *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_DEINTERLACE(object)); + + filter = GST_DEINTERLACE(object); + + switch (prop_id) { + case ARG_DI_ONLY: + g_value_set_boolean (value, filter->show_deinterlaced_area_only); + break; + case ARG_BLEND: + g_value_set_boolean (value, filter->blend); + break; + case ARG_THRESHOLD: + g_value_set_int (value, filter->threshold); + break; + case ARG_EDGE_DETECT: + g_value_set_int (value, filter->edge_detect); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("deinterlace",GST_TYPE_DEINTERLACE, + &deinterlace_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (deinterlace_src_factory)); + gst_elementfactory_add_padtemplate (factory, + GST_PADTEMPLATE_GET (deinterlace_sink_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "deinterlace", + plugin_init +}; diff --git a/gst/deinterlace/gstdeinterlace.h b/gst/deinterlace/gstdeinterlace.h new file mode 100644 index 000000000..c46c3af6c --- /dev/null +++ b/gst/deinterlace/gstdeinterlace.h @@ -0,0 +1,76 @@ +/* Gnome-Streamer + * 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_DEINTERLACE_H__ +#define __GST_DEINTERLACE_H__ + + +#include <config.h> +#include <gst/gst.h> +// #include <gst/meta/audioraw.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_DEINTERLACE \ + (gst_deinterlace_get_type()) +#define GST_DEINTERLACE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DEINTERLACE,GstDeInterlace)) +#define GST_DEINTERLACE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstDeInterlace)) +#define GST_IS_DEINTERLACE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DEINTERLACE)) +#define GST_IS_DEINTERLACE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DEINTERLACE)) + +typedef struct _GstDeInterlace GstDeInterlace; +typedef struct _GstDeInterlaceClass GstDeInterlaceClass; + +struct _GstDeInterlace { + GstElement element; + + GstPad *sinkpad, *srcpad; + + gint width, height; + + gboolean show_deinterlaced_area_only; + gboolean blend; + gint threshold_blend; // here we start blending + gint threshold; // here we start interpolating TODO FIXME + gint edge_detect; + + gint picsize; + guchar *src; + +}; + +struct _GstDeInterlaceClass { + GstElementClass parent_class; +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_STEREO_H__ */ diff --git a/gst/flx/Makefile.am b/gst/flx/Makefile.am new file mode 100644 index 000000000..79a6fba7f --- /dev/null +++ b/gst/flx/Makefile.am @@ -0,0 +1,9 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstflxdec.la + +libgstflxdec_la_SOURCES = gstflxdec.c flx_color.c +libgstflxdec_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = flx_fmt.h flx_color.h gstflxdec.h + diff --git a/gst/flx/flx_color.c b/gst/flx/flx_color.c new file mode 100644 index 000000000..c61052d0e --- /dev/null +++ b/gst/flx/flx_color.c @@ -0,0 +1,94 @@ +/* Gnome-Streamer + * 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. + */ + +#include <string.h> +#include <gst/gst.h> + + +#include "flx_color.h" + +FlxColorSpaceConverter * +flx_colorspace_converter_new(gint width, gint height) +{ + FlxColorSpaceConverter *new = g_malloc(sizeof(FlxColorSpaceConverter)); + + new->width = width; + new->height = height; + + memset(new->palvec, 0, sizeof(new->palvec)); + return new; +} + +void +flx_colorspace_converter_destroy(FlxColorSpaceConverter *flxpal) +{ + g_return_if_fail(flxpal != NULL); + + g_free(flxpal); +} + +void +flx_colorspace_convert(FlxColorSpaceConverter *flxpal, guchar *src, guchar *dest) +{ + guint size, col; + + g_return_if_fail(flxpal != NULL); + g_return_if_fail(src != dest); + + + size = flxpal->width * flxpal->height; + + while(size--) { + col = (*src++ * 3); + *dest++ = flxpal->palvec[col+2]; + *dest++ = flxpal->palvec[col+1]; + *dest++ = flxpal->palvec[col]; + *dest++ = 0; + } + +} + + +void +flx_set_palette_vector(FlxColorSpaceConverter *flxpal, guint start, guint num, guchar *newpal) +{ + guint grab; + + g_return_if_fail(flxpal != NULL); + g_return_if_fail(start < 0x100); + + grab = ((start + num) > 0x100 ? 0x100 - start : num); + + memcpy(&flxpal->palvec[start * 3], newpal, grab*3); + +} + +void +flx_set_color(FlxColorSpaceConverter *flxpal, guint colr, guint red, guint green, guint blue) +{ + + g_return_if_fail(flxpal != NULL); + g_return_if_fail(colr < 0x100); + + flxpal->palvec[(colr * 3)] = red; + flxpal->palvec[(colr * 3) + 1] = green; + flxpal->palvec[(colr * 3) + 2] = blue; +} + + diff --git a/gst/flx/flx_color.h b/gst/flx/flx_color.h new file mode 100644 index 000000000..5676c878e --- /dev/null +++ b/gst/flx/flx_color.h @@ -0,0 +1,43 @@ +/* Gnome-Streamer + * 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. + */ + +typedef enum { + FLX_COLORSPACE_RGB8, + FLX_COLORSPACE_RGB32, +} FlxColorSpaceType; + + +typedef struct _FlxColorSpaceConverter FlxColorSpaceConverter; + +struct _FlxColorSpaceConverter { + guint width; + guint height; + guchar palvec[768]; +}; + + +void flx_colorspace_converter_destroy(FlxColorSpaceConverter *flxpal); +void flx_colorspace_convert(FlxColorSpaceConverter *flxpal, guchar *src, guchar *dest); +FlxColorSpaceConverter * flx_colorspace_converter_new(gint width, gint height); + +void flx_set_palette_vector(FlxColorSpaceConverter *flxpal, guint start, guint num, + guchar *newpal); +void flx_set_color(FlxColorSpaceConverter *flxpal, guint colr, guint red, guint green, + guint blue); + diff --git a/gst/flx/flx_fmt.h b/gst/flx/flx_fmt.h new file mode 100644 index 000000000..5323de63b --- /dev/null +++ b/gst/flx/flx_fmt.h @@ -0,0 +1,136 @@ +/* Gnome-Streamer + * 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_FLX_FMT__H__ +#define __GST_FLX_FMT_H__ + +#include <gst/gst.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +enum Flx_TypeChunk +{ + /* frame chunks */ + FLX_PREFIX_TYPE = 0xf100, + FLX_SCRIPT_CHUNK = 0xf1e0, + FLX_FRAME_TYPE = 0xf1fa, + FLX_SEGMENT_TABLE = 0xf1fb, + FLX_HUFFMAN_TABLE = 0xf1fc, + + /* sub chunks */ + FLX_CEL_DATA = 3, + FLX_COLOR256 = 4, + FLX_SS2 = 7, + FLX_COLOR64 = 11, + FLX_LC = 12, + FLX_BLACK = 13, + FLX_BRUN = 15, + FLX_COPY = 16, + FLX_MINI = 18, + FLX_DTA_RUN = 25, + FLX_DTA_COPY = 26, + FLX_DTA_LC = 27, + FLX_LABEL = 31, + FLX_BMP_MASK = 32, + FLX_MLEV_MASK = 33, + FLX_SEGMENT = 34, + FLX_KEY_IMAGE = 35, + FLX_KEY_PAL = 36, + FLX_REGION = 37, + FLX_WAVE = 38, + FLX_USERSTRING = 39, + FLX_RGN_MASK = 40, + +}; + +enum Flx_MagicHdr +{ + FLX_MAGICHDR_FLI = 0xaf11, + FLX_MAGICHDR_FLC = 0xaf12, + FLX_MAGICHDR_FLX = 0xaf44, + FLX_MAGICHDR_HUFFBWT = 0xaf30, +}; + + + +typedef struct _FlxHeader +{ + guint32 size; + guint16 type; + guint16 frames; + guint16 width,height,depth,flags; + guint32 speed; + guint16 reserved1; + /* FLC */ + guint32 created,creator,updated,updater; + guint16 aspect_dx, aspect_dy; + /* EGI */ + guint16 ext_flags,keyframes,totalframes; + guint32 req_memory; + guint16 max_regions,transp_num; + guchar reserved2[24]; + /* FLC */ + guint32 oframe1,oframe2; + guchar reserved3[40]; +} FlxHeader; +#define FlxHeaderSize 128 + +typedef struct _FlxFrameChunk +{ + guint32 size; + guint16 id; +} FlxFrameChunk; +#define FlxFrameChunkSize 6 + +typedef struct _FlxPrefixChunk +{ + guint16 chunks; + guchar reserved[8]; +} FlxPrefixChunk; + +typedef struct _FlxSegmentTable +{ + guint16 segments; +} FlxSegmentTable; + +typedef struct _FlxHuffmanTable +{ + guint16 codelength; + guint16 numcodes; + guchar reserved[6]; +} FlxHuffmanTable; + +typedef struct _FlxFrameType +{ + guint16 chunks; + guint16 delay; + guchar reserved[6]; +} FlxFrameType; +#define FlxFrameTypeSize 10 + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_FLX_FMT_H__ */ diff --git a/gst/flx/gstflxdec.c b/gst/flx/gstflxdec.c new file mode 100644 index 000000000..d6052bb6a --- /dev/null +++ b/gst/flx/gstflxdec.c @@ -0,0 +1,644 @@ +/* Gnome-Streamer + * 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. + */ + +#include <string.h> + +#include "flx_fmt.h" +#include "gstflxdec.h" + +static GstCaps* flxdec_typefind(GstBuffer *buf, gpointer private); + +/* flx element information */ +static GstElementDetails flxdec_details = { + "FLX Decoder", + "flxdec", + "FLX decoder", + VERSION, + "Sepp Wijnands <mrrazz@garbage-coderz.net>" + "(C) 2001", +}; + +static GstTypeDefinition flxdec_definition = { + "flxdec_video/fli", + "video/fli", + ".flc .fli", + flxdec_typefind, +}; + +/* Flx signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0 +}; + +/* input */ +GST_PADTEMPLATE_FACTORY (sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "flxdec_sink", + "video/fli", + NULL + ) +) + +/* output */ +GST_PADTEMPLATE_FACTORY (src_video_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "src_video", + "video/raw", + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R', 'G', 'B', ' ')), + "bpp", GST_PROPS_INT (32), + "depth", GST_PROPS_INT (32), + "endianness", GST_PROPS_INT (G_LITTLE_ENDIAN), + "red_mask", GST_PROPS_INT (0x00ff0000), + "green_mask", GST_PROPS_INT (0x0000ff00), + "blue_mask", GST_PROPS_INT (0x000000ff), + "width", GST_PROPS_INT_RANGE(320, 1280), + "height", GST_PROPS_INT_RANGE(200, 1024) + ) +) + + +static void gst_flxdec_class_init (GstFlxDecClass *klass); +static void gst_flxdec_init (GstFlxDec *flxdec); + +static void gst_flxdec_loop (GstElement *element); + +static void gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + + +static void flx_decode_color(GstFlxDec *, guchar *, guchar *); +static void flx_decode_brun(GstFlxDec *, guchar *, guchar *); +static void flx_decode_delta_fli(GstFlxDec *, guchar *, guchar *); +static void flx_decode_delta_flc(GstFlxDec *, guchar *, guchar *); + +#define rndalign(off) ((off) + ((off) % 2)) + +static GstElementClass *parent_class = NULL; + +static GstCaps* +flxdec_typefind (GstBuffer *buf, gpointer private) +{ + guchar *data = GST_BUFFER_DATA(buf); + GstCaps *new; + + // check magic + if ((data[4] == 0x11 || data[4] == 0x12 + || data[4] == 0x30 || data[4] == 0x44) && data[5] == 0xaf) { + // check the frame type of the first frame + if ((data[132] == 0x00 || data[132] == 0xfa) && data[133] == 0xf1) { + g_print("GstFlxDec: found supported flx format\n"); + new = gst_caps_new("flxdec_typefind","video/fli", NULL); + return new; + } + } + + return NULL; +} + + +GType +gst_flxdec_get_type(void) +{ + static GType flxdec_type = 0; + + if (!flxdec_type) { + static const GTypeInfo flxdec_info = { + sizeof(GstFlxDecClass), NULL, + NULL, + (GClassInitFunc)gst_flxdec_class_init, + NULL, + NULL, + sizeof(GstFlxDec), + 0, + (GInstanceInitFunc)gst_flxdec_init, + }; + flxdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstFlxDec", &flxdec_info, 0); + } + return flxdec_type; +} + +static void +gst_flxdec_class_init (GstFlxDecClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = NULL; + gobject_class->get_property = NULL; + +} + + + +static void +gst_flxdec_init(GstFlxDec *flxdec) +{ + flxdec->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (sink_factory), "sink"); + gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->sinkpad); + gst_element_set_loop_function(GST_ELEMENT(flxdec),gst_flxdec_loop); + + flxdec->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (src_video_factory), "src"); + gst_element_add_pad(GST_ELEMENT(flxdec),flxdec->srcpad); + + flxdec->buf = NULL; + flxdec->offset = 0; + flxdec->new_buf = TRUE; + +} + +static void +flx_decode_chunks (GstFlxDec *flxdec , gulong count, gchar *data, gchar *dest) +{ + FlxFrameChunk *hdr; + + g_return_if_fail(data != NULL); + + while (count--) { + hdr = (FlxFrameChunk *) data; + data += FlxFrameChunkSize; + + switch(hdr->id) + { + case FLX_COLOR64: + case FLX_COLOR256: + flx_decode_color(flxdec, data, dest); + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + + case FLX_BRUN: + flx_decode_brun(flxdec, data, dest); + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + + case FLX_LC: + flx_decode_delta_fli(flxdec, data, dest); + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + + case FLX_SS2: + flx_decode_delta_flc(flxdec, data, dest); + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + + case FLX_BLACK: + memset(dest, 0, flxdec->size); + break; + + case FLX_MINI: + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + + default: + g_print("GstFlxDec: Unimplented chunk type: 0x%02x size: %d\n", + hdr->id, hdr->size); + g_print("GstFlxDec: Skipping...\n"); + data += rndalign(hdr->size) - FlxFrameChunkSize; + break; + } + } +} + + +static void +flx_decode_color(GstFlxDec *flxdec, guchar *data, guchar *dest) +{ + guint packs, count, indx; + + g_return_if_fail(flxdec != NULL); + + packs = (data[0] + (data[1] << 8)); + + data += 2; + indx = 0; + + g_print("GstFlxDec: cmap packs: %d\n", packs); + while (packs--) { + /* color map index + skip count */ + indx += *data++; + + /* number of rgb triplets */ + count = *data++ & 0xff; + if (count == 0) + count = 256; + + g_print("GstFlxDec: cmap count: %d (indx: %d)\n", count, indx); + flx_set_palette_vector(flxdec->converter, indx, count, data); + + data += (count * 3); + } +} + +static void +flx_decode_brun(GstFlxDec *flxdec, guchar *data, guchar *dest) +{ + gulong count, lines, row; + guchar x; + + g_return_if_fail(flxdec != NULL); + + lines = flxdec->hdr.height; + while(lines--) { + /* packet count. + * should not be used anymore, since the flc format can + * contain more then 255 RLE packets. we use the frame + * width instead. + */ + data++; + + row = flxdec->hdr.width; + while(row) { + count = *data++; + + if (count > 0x7f) { + /* literal run */ + count = 0x100 - count; + row -= count; + + while(count--) + *dest++ = *data++; + + } else { + /* replicate run */ + row -= count; + x = *data++; + + while(count--) + *dest++ = x; + } + } + } +} + +static void +flx_decode_delta_fli(GstFlxDec *flxdec, guchar *data, guchar *dest) +{ + gulong count, packets, lines, start_line, start_l; + guchar *start_p, x; + + g_return_if_fail(flxdec != NULL); + g_return_if_fail(flxdec->delta != NULL); + + + /* use last frame for delta */ + memcpy(dest, GST_BUFFER_DATA(flxdec->delta), + GST_BUFFER_SIZE(flxdec->delta)); + + start_line = (data[0] + (data[1] << 8)); + lines = (data[2] + (data[3] << 8)); + data += 4; + + /* start position of delta */ + dest += (flxdec->hdr.width * start_line); + start_p = dest; + start_l = lines; + + while(lines--) { + /* packet count */ + packets = *data++; + + dest = start_p + (flxdec->hdr.width * (start_l - lines)); + + while(packets--) { + /* skip count */ + dest += *data++; + + /* RLE count */ + count = *data++; + + if (count > 0x7f) { + /* literal run */ + count = 0x100 - count; + x = *data++; + + while (count--) + *dest++ = x; + + } else { + /* replicate run */ + while (count--) + *dest++ = *data++; + } + } + } +} + +static void +flx_decode_delta_flc(GstFlxDec *flxdec, guchar *data, guchar *dest) +{ + gulong count, lines, start_l, opcode; + guchar *start_p; + + g_return_if_fail(flxdec != NULL); + g_return_if_fail(flxdec->delta != NULL); + + + /* use last frame for delta */ + memcpy(dest, GST_BUFFER_DATA(flxdec->delta), + GST_BUFFER_SIZE(flxdec->delta)); + + lines = (data[0] + (data[1] << 8)); + data += 2; + + start_p = dest; + start_l = lines; + + while(lines--) { + dest = start_p + (flxdec->hdr.width * (start_l - lines)); + + /* process opcode(s) */ + while ((opcode = (data[0] + (data[1] << 8))) & 0xc000) { + data += 2; + if ((opcode & 0xc000) == 0xc000) { + /* skip count */ + start_l += (0x10000 - opcode); + dest += flxdec->hdr.width * (0x10000 - opcode); + } else { + /* last pixel */ + dest += flxdec->hdr.width; + *dest++ = (opcode & 0xff); + } + } + data += 2; + + /* last opcode is the packet count */ + while(opcode--) { + /* skip count */ + dest += *data++; + + /* RLE count */ + count = *data++; + + if (count > 0x7f) { + /* replicate word run */ + count = 0x100 - count; + while (count--) { + *dest++ = data[0]; + *dest++ = data[1]; + } + data += 2; + } else { + /* literal word run */ + while (count--) { + *dest++ = *data++; + *dest++ = *data++; + } + } + } + } +} + +static GstBuffer* +flx_get_data(GstFlxDec *flxdec, gulong size) +{ + GstBuffer *retbuf; + + g_return_val_if_fail (flxdec != NULL, NULL); + + if (flxdec->new_buf) { + retbuf = gst_pad_pullregion(flxdec->sinkpad, + GST_REGION_OFFSET_LEN, 0, size); + flxdec->new_buf = FALSE; + flxdec->offset = size; + } else { + retbuf = gst_pad_pullregion(flxdec->sinkpad, GST_REGION_OFFSET_LEN, + flxdec->offset, size); + flxdec->offset += size; + } + + return retbuf; +} + + +static void +gst_flxdec_loop (GstElement *element) +{ + GstBuffer *buf; + GstBuffer *databuf; + guchar *data, *chunk; + + GstFlxDec *flxdec; + FlxHeader *flxh; + FlxFrameChunk *flxfh; + + g_return_if_fail (element != NULL); + g_return_if_fail (GST_IS_FLXDEC(element)); + + GST_DEBUG (0, "entering loop function\n"); + + flxdec = GST_FLXDEC(element); + + databuf = flx_get_data(flxdec, FlxHeaderSize); + + g_return_if_fail (databuf != NULL); + + data = GST_BUFFER_DATA(databuf); + + memcpy((char *) &flxdec->hdr, data, sizeof(FlxHeader)); + + gst_buffer_unref (databuf); + + flxh = &flxdec->hdr; + + // check header + if (flxh->type != FLX_MAGICHDR_FLI && + flxh->type != FLX_MAGICHDR_FLC && + flxh->type != FLX_MAGICHDR_FLX) + return; + + + g_print("GstFlxDec: size : %d\n", flxh->size); + g_print("GstFlxDec: frames : %d\n", flxh->frames); + g_print("GstFlxDec: width : %d\n", flxh->width); + g_print("GstFlxDec: height : %d\n", flxh->height); + g_print("GstFlxDec: depth : %d\n", flxh->depth); + g_print("GstFlxDec: speed : %d\n", flxh->speed); + + gst_pad_set_caps (flxdec->srcpad, + gst_caps_new ( + "src_video", + "video/raw", + gst_props_new ( + "format", GST_PROPS_FOURCC (GST_MAKE_FOURCC ('R', 'G', 'B', ' ')), + "bpp", GST_PROPS_INT (32), + "depth", GST_PROPS_INT (32), + "endianness", GST_PROPS_INT (G_LITTLE_ENDIAN), + "red_mask", GST_PROPS_INT (0x00ff0000), + "green_mask", GST_PROPS_INT (0x0000ff00), + "blue_mask", GST_PROPS_INT (0x000000ff), + "width", GST_PROPS_INT (flxh->width), + "height", GST_PROPS_INT (flxh->height), + NULL))); + + if (flxh->depth <= 8) + flxdec->converter = flx_colorspace_converter_new(flxh->width, flxh->height); + + if (flxh->type == FLX_MAGICHDR_FLC || + flxh->type == FLX_MAGICHDR_FLX) { + g_print("GstFlxDec: (FLC) aspect_dx : %d\n", + flxh->aspect_dx); + g_print("GstFlxDec: (FLC) aspect_dy : %d\n", + flxh->aspect_dy); + g_print("GstFlxDec: (FLC) oframe1 : 0x%08x\n", + flxh->oframe1); + g_print("GstFlxDec: (FLC) oframe2 : 0x%08x\n", + flxh->oframe2); + } + + + flxdec->size = (flxh->width * flxh->height); + + // create delta and output frame + flxdec->frame = gst_buffer_new(); + flxdec->delta = gst_buffer_new(); + GST_BUFFER_DATA(flxdec->frame) = g_malloc(flxdec->size); + GST_BUFFER_SIZE(flxdec->frame) = flxdec->size; + GST_BUFFER_DATA(flxdec->delta) = g_malloc(flxdec->size); + GST_BUFFER_SIZE(flxdec->delta) = flxdec->size; + + do + { + + databuf = flx_get_data(flxdec, FlxFrameChunkSize); + + flxfh = (FlxFrameChunk *) GST_BUFFER_DATA(databuf); + + switch(flxfh->id) + { + case FLX_FRAME_TYPE: + buf = flx_get_data(flxdec, flxfh->size-FlxFrameChunkSize); + + chunk = GST_BUFFER_DATA(buf); + + if (((FlxFrameType *)chunk)->chunks == 0) + break; + + // create 32 bits output frame + flxdec->out = gst_buffer_new(); + GST_BUFFER_DATA(flxdec->out) = g_malloc(flxdec->size * 4); + GST_BUFFER_SIZE(flxdec->out) = flxdec->size * 4; + + + // decode chunks + flx_decode_chunks(flxdec, + ((FlxFrameType *)chunk)->chunks, + GST_BUFFER_DATA(buf) + FlxFrameTypeSize, + GST_BUFFER_DATA(flxdec->frame)); + + // destroy input buffer + gst_buffer_unref(buf); + + // save copy of the current frame for possible delta. + memcpy(GST_BUFFER_DATA(flxdec->delta), + GST_BUFFER_DATA(flxdec->frame), + GST_BUFFER_SIZE(flxdec->delta)); + + // convert current frame. + flx_colorspace_convert(flxdec->converter, + GST_BUFFER_DATA(flxdec->frame), + GST_BUFFER_DATA(flxdec->out)); + + //GST_BUFFER_FLAG_SET(flxdec->out, GST_BUFFER_FLUSH); + gst_pad_push(flxdec->srcpad, flxdec->out); + + break; + } + + // destroy header buffer + gst_buffer_unref(databuf); + + } + while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element)); + +} + +static void +gst_flxdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstFlxDec *flxdec; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_FLXDEC(object)); + flxdec = GST_FLXDEC(object); + + switch (prop_id) { + default: + break; + } +} + +static void +gst_flxdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstFlxDec *flxdec; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_FLXDEC(object)); + flxdec = GST_FLXDEC(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + GstTypeFactory *type; + + factory = gst_elementfactory_new("flxdec", GST_TYPE_FLXDEC, &flxdec_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (sink_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_video_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + type = gst_typefactory_new (&flxdec_definition); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "flxdec", + plugin_init +}; diff --git a/gst/flx/gstflxdec.h b/gst/flx/gstflxdec.h new file mode 100644 index 000000000..cc4c94dbe --- /dev/null +++ b/gst/flx/gstflxdec.h @@ -0,0 +1,79 @@ +/* Gnome-Streamer + * 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_FLX_DECODER_H__ +#define __GST_FLX_DECODER_H__ + +#include <gst/gst.h> + +#include "flx_color.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Definition of structure storing data for this element. */ +typedef struct _GstFlxDec GstFlxDec; +struct _GstFlxDec { + GstElement element; + + GstPad *sinkpad,*srcpad; + + gboolean active, new_meta, new_buf; + + GstBuffer *buf, *out, *delta, *frame; + gulong offset, size; + + FlxColorSpaceConverter *converter; + + FlxHeader hdr; +}; + +/* Standard definition defining a class for this element. */ +typedef struct _GstFlxDecClass GstFlxDecClass; +struct _GstFlxDecClass { + GstElementClass parent_class; +}; + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_FLXDEC \ + (gst_flxdec_get_type()) +#define GST_FLXDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLXDEC,GstFlxDec)) +#define GST_FLXDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLXDEC,GstFlxDec)) +#define GST_IS_FLXDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLXDEC)) +#define GST_IS_FLXDEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLXDEC)) + +#define FLXDEC_BUFSIZE(buf, offset) \ + ((GST_BUFFER_OFFSET(buf) + GST_BUFFER_SIZE(buf)) - offset) + +/* Standard function returning type information. */ +GType gst_flxdec_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_FLX_DECODER_H__ */ diff --git a/gst/mpeg1sys/.gitignore b/gst/mpeg1sys/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/mpeg1sys/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/mpeg1sys/Makefile.am b/gst/mpeg1sys/Makefile.am new file mode 100644 index 000000000..5e28e2be6 --- /dev/null +++ b/gst/mpeg1sys/Makefile.am @@ -0,0 +1,13 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstmpeg1systemencode.la + +libgstmpeg1systemencode_la_SOURCES = gstmpeg1systemencode.c \ + buffer.c \ + systems.c + +noinst_HEADERS = gstmpeg1systemencode.h \ + main.h \ + buffer.h + +libsystem_encode_la_CFLAGS = -O2 $(FOMIT_FRAME_POINTER) -funroll-all-loops -finline-functions -ffast-math $(GST_CFLAGS) diff --git a/gst/mpeg1sys/buffer.c b/gst/mpeg1sys/buffer.c new file mode 100644 index 000000000..933b76e64 --- /dev/null +++ b/gst/mpeg1sys/buffer.c @@ -0,0 +1,482 @@ +/* Gnome-Streamer + * 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. + */ + +#include <string.h> + +/*#define DEBUG_ENABLED */ +#include <gst/gst.h> +#include <libs/getbits/gstgetbits.h> + +#include "buffer.h" + +#define SEQUENCE_HEADER 0x000001b3 +#define SEQUENCE_END 0x000001b7 +#define PICTURE_START 0x00000100 +#define GROUP_START 0x000001b8 +#define SYNCWORD_START 0x000001 + +#define AUDIO_SYNCWORD 0xfff + +#define CLOCKS 90000.0 + +#define DEBUG(a, b...) g_print (##b) + +/* This must match decoder and encoder tables */ +static double picture_rates [16] = +{ + 0.0, + 24000.0/1001., + 24.0, + 25.0, + 30000.0/1001., + 30.0, + 50.0, + 60000.0/1001., + 60.0, + + 1, + 5, + 10, + 12, + 15, + 0, + 0 +}; + +static double ratio [16] = { 0., 1., 0.6735, 0.7031, 0.7615, 0.8055, + 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, + 1.2015, 0.}; + +static char picture_types [4][3] = + { "I", "P", "B", "D" }; + +static int bitrate_index[2][3][16] = +{ { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, }, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, }, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, } }, + { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, } }, +}; + +static long frequency[9] = +{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000}; + +static double dfrequency[9] = +{44.1, 48, 32, 22.05, 24, 16, 11.025, 12, 8}; + +static unsigned int samples [4] = {192, 384, 1152, 1152}; + +static char mode [4][15] = + { "stereo", "joint stereo", "dual channel", "single channel" }; +static char copyright [2][20] = + { "no copyright","copyright protected" }; +static char original [2][10] = + { "copy","original" }; +static char emphasis [4][20] = + { "none", "50/15 microseconds", "reserved", "CCITT J.17" }; + +static void mpeg1mux_buffer_update_video_info(Mpeg1MuxBuffer *mb); +static void mpeg1mux_buffer_update_audio_info(Mpeg1MuxBuffer *mb); + +Mpeg1MuxBuffer *mpeg1mux_buffer_new(guchar type, guchar id) { + Mpeg1MuxBuffer *new = g_malloc(sizeof(Mpeg1MuxBuffer)); + + new->buffer = NULL; + new->length = 0; + new->base = 0; + new->buffer_type = type; + new->stream_id = id; + new->scan_pos = 0; + new->new_frame = TRUE; + new->current_start = 0; + new->timecode_list = NULL; + new->queued_list = NULL; + new->next_frame_time = 0; + + return new; +} + +void mpeg1mux_buffer_queue(Mpeg1MuxBuffer *mb, GstBuffer *buf) { + + if (mb->buffer == NULL) { + mb->buffer = g_malloc(GST_BUFFER_SIZE(buf)); + mb->length = GST_BUFFER_SIZE(buf); + memcpy(mb->buffer, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); + } + else { + mb->buffer = g_realloc(mb->buffer, mb->length + GST_BUFFER_SIZE(buf)); + memcpy(mb->buffer+mb->length, GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); + mb->length += GST_BUFFER_SIZE(buf); + } + + GST_DEBUG (0,"queuing buffer %lu\n", mb->length); + if (mb->buffer_type == BUFFER_TYPE_VIDEO) { + mpeg1mux_buffer_update_video_info(mb); + } + else { + mpeg1mux_buffer_update_audio_info(mb); + } +} + +gulong mpeg1mux_buffer_update_queued(Mpeg1MuxBuffer *mb, guint64 scr) { + GList *queued_list; + Mpeg1MuxTimecode *tc; + gulong total_queued = 0; + + GST_DEBUG (0,"queued in buffer on SCR=%llu\n", scr); + queued_list = g_list_first(mb->queued_list); + + while (queued_list) { + tc = (Mpeg1MuxTimecode *) queued_list->data; + if (tc->DTS < scr) { + /* this buffer should be sent out */ + mb->queued_list = g_list_remove(mb->queued_list, tc); + queued_list = g_list_first(mb->queued_list); + } + else { + GST_DEBUG (0,"queued in buffer %ld, %llu\n", tc->original_length, tc->DTS); + total_queued += tc->original_length; + queued_list = g_list_next(queued_list); + } + } + GST_DEBUG (0,"queued in buffer %lu\n", total_queued); + + return total_queued; +} + +void mpeg1mux_buffer_shrink(Mpeg1MuxBuffer *mb, gulong size) { + GList *timecode_list; + Mpeg1MuxTimecode *tc; + gulong consumed = 0; + gulong count; + + GST_DEBUG (0,"shrinking buffer %lu\n", size); + + g_assert(mb->length >= size); + + memcpy(mb->buffer, mb->buffer+size, mb->length-size); + mb->buffer = g_realloc(mb->buffer, mb->length-size); + + mb->length -= size; + mb->scan_pos -= size; + mb->current_start -= size; + + timecode_list = g_list_first(mb->timecode_list); + tc = (Mpeg1MuxTimecode *) timecode_list->data; + + if (tc->length > size) { + tc->length -= size; + mb->new_frame = FALSE; + } + else { + consumed += tc->length; + while (size >= consumed) { + GST_DEBUG (0,"removing timecode: %llu %llu %lu %lu\n", tc->DTS, tc->PTS, tc->length, consumed); + mb->timecode_list = g_list_remove_link(mb->timecode_list, timecode_list); + mb->queued_list = g_list_append(mb->queued_list, tc); + timecode_list = g_list_first(mb->timecode_list); + tc = (Mpeg1MuxTimecode *) timecode_list->data; + consumed += tc->length; + GST_DEBUG (0,"next timecode: %llu %llu %lu %lu\n", tc->DTS, tc->PTS, tc->length, consumed); + } + mb->new_frame = TRUE; + GST_DEBUG (0,"leftover frame size from %lu to %lu \n", tc->length, consumed-size); + tc->length = consumed - size; + } + + if (mb->buffer_type == BUFFER_TYPE_VIDEO) { + mb->info.video.DTS = tc->DTS; + mb->info.video.PTS = tc->PTS; + mb->next_frame_time = tc->DTS; + } + else { + mb->info.audio.PTS = tc->PTS; + mb->next_frame_time = tc->PTS; + } + GST_DEBUG (0,"next frame time timecode: %llu %lu\n", mb->next_frame_time, tc->length); + + /* check buffer consistency */ + timecode_list = g_list_first(mb->timecode_list); + count = 0; + + while (timecode_list) { + tc = (Mpeg1MuxTimecode *) timecode_list->data; + count += tc->length; + + timecode_list = g_list_next(timecode_list); + } + + if (count != mb->current_start) g_print("********** error %lu != %lu\n", count, mb->current_start); + + mb->base += size; +} + +static void mpeg1mux_buffer_update_video_info(Mpeg1MuxBuffer *mb) { + gboolean have_sync = FALSE; + guchar *data = mb->buffer; + gulong offset = mb->scan_pos; + guint sync_zeros = 0; + gulong id=0; + guint temporal_reference, temp; + gst_getbits_t gb; + + + GST_DEBUG (0,"mpeg1mux::update_video_info %lu %lu\n", mb->base, mb->scan_pos); + if (mb->base == 0 && mb->scan_pos == 0) { + if ((SYNCWORD_START<<8)+*(mb->buffer+3) == SEQUENCE_HEADER) { + + gst_getbits_init(&gb, NULL, NULL); + gst_getbits_newbuf(&gb, data+4, mb->length); + mb->info.video.horizontal_size = gst_getbits12(&gb); + mb->info.video.vertical_size = gst_getbits12(&gb); + mb->info.video.aspect_ratio = gst_getbits4(&gb); + mb->info.video.picture_rate = gst_getbits4(&gb); + mb->info.video.bit_rate = gst_getbits18(&gb); + if (gst_getbits1(&gb) != 1) { + g_print("mpeg1mux::update_video_info: marker bit error\n"); + } + mb->info.video.vbv_buffer_size = gst_getbits10(&gb); + mb->info.video.CSPF = gst_getbits1(&gb); + + mb->info.video.secs_per_frame = 1. / picture_rates[mb->info.video.picture_rate]; + mb->info.video.decoding_order=0; + mb->info.video.group_order=0; + GST_DEBUG (0,"mpeg1mux::update_video_info: secs per frame %g\n", mb->info.video.secs_per_frame); + } + else { + g_print("mpeg1mux::update_video_info: Invalid MPEG Video header\n"); + } + } + while (offset < mb->length-6) { + if (!have_sync) { + guchar byte = *(data+offset); + /*GST_DEBUG (0,"mpeg1mux::update_video_info: found #%d at %lu\n",byte,offset); */ + offset++; + /* if it's zero, increment the zero count */ + if (byte == 0) { + sync_zeros++; + /*GST_DEBUG (0,"mpeg1mux::update_video_info: found zero #%d at %lu\n",sync_zeros,offset-1); */ + } + /* if it's a one and we have two previous zeros, we have sync */ + else if ((byte == 1) && (sync_zeros >= 2)) { + GST_DEBUG (0,"mpeg1mux::update_video_info: synced at %lu\n",offset-1); + have_sync = TRUE; + sync_zeros = 0; + } + /* if it's anything else, we've lost it completely */ + else sync_zeros = 0; + /* then snag the chunk ID */ + } else if (id == 0) { + id = *(data+offset); + GST_DEBUG (0,"mpeg1mux::update_video_info: got id 0x%02lX\n",id); + id = (SYNCWORD_START<<8)+id; + switch (id) { + case SEQUENCE_HEADER: + GST_DEBUG (0,"mpeg1mux::update_video_info: sequence header\n"); + break; + case GROUP_START: + GST_DEBUG (0,"mpeg1mux::update_video_info: group start\n"); + mb->info.video.group_order=0; + break; + case PICTURE_START: + /* skip the first access unit */ + if (mb->info.video.decoding_order != 0) { + Mpeg1MuxTimecode *tc; + GST_DEBUG (0,"mpeg1mux::update_video_info: PTS %llu, DTS %llu, length %lu\n", mb->info.video.current_PTS, + mb->info.video.current_DTS, offset - mb->current_start-3); + + tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode)); + tc->length = offset - mb->current_start-3; + tc->original_length = tc->length; + tc->frame_type = mb->info.video.current_type; + tc->DTS = mb->info.video.current_DTS; + tc->PTS = mb->info.video.current_PTS; + + mb->timecode_list = g_list_append(mb->timecode_list, tc); + + if (mb->info.video.decoding_order == 0) { + mb->next_frame_time = tc->DTS; + } + + mb->current_start = offset-3; + } + + temp= (*(data+offset+1)<<8)+*(data+offset+2); + temporal_reference = (temp & 0xffc0) >> 6; + mb->info.video.current_type = (temp & 0x0038) >> 3; + GST_DEBUG (0,"mpeg1mux::update_video_info: picture start temporal_ref:%d type:%s Frame\n", temporal_reference, + picture_types[mb->info.video.current_type-1]); + + mb->info.video.current_DTS = mb->info.video.decoding_order * mb->info.video.secs_per_frame * CLOCKS; + mb->info.video.current_PTS = (temporal_reference - mb->info.video.group_order + 1 + + mb->info.video.decoding_order) *mb->info.video.secs_per_frame*CLOCKS; + + mb->info.video.decoding_order++; + mb->info.video.group_order++; + + + offset++; + break; + case SEQUENCE_END: + GST_DEBUG (0,"mpeg1mux::update_video_info: sequence end\n"); + break; + } + /* prepare for next sync */ + offset++; + have_sync = FALSE; + id = 0; + sync_zeros = 0; + } + } + mb->scan_pos = offset; +} + +static void mpeg1mux_buffer_update_audio_info(Mpeg1MuxBuffer *mb) { + guchar *data = mb->buffer; + gulong offset = mb->scan_pos; + gulong id=0; + guint padding_bit; + gst_getbits_t gb; + guint startup_delay = 0; + int layer_index,lsf,samplerate_index,padding; + long bpf; + Mpeg1MuxTimecode *tc; + + + GST_DEBUG (0,"mpeg1mux::update_audio_info %lu %lu\n", mb->base, mb->scan_pos); + if (mb->base == 0 && mb->scan_pos == 0) { + id = GULONG_FROM_BE(*((gulong *)(data))); + + printf("MPEG audio id = %08lx\n", id); + if ((id & 0xfff00000) == AUDIO_SYNCWORD<<20) { + + /*mpegver = (header >> 19) & 0x3; // don't need this for bpf */ + layer_index = (id >> 17) & 0x3; + mb->info.audio.layer = 4 - layer_index; + lsf = (id & (1 << 20)) ? ((id & (1 << 19)) ? 0 : 1) : 1; + mb->info.audio.bit_rate = bitrate_index[lsf][mb->info.audio.layer - 1][((id >> 12) & 0xf)]; + samplerate_index = (id >> 10) & 0x3; + padding = (id >> 9) & 0x1; + + if (mb->info.audio.layer == 1) { + bpf = mb->info.audio.bit_rate * 12000; + bpf /= frequency[samplerate_index]; + bpf = ((bpf + padding) << 2); + } else { + bpf = mb->info.audio.bit_rate * 144000; + bpf /= frequency[samplerate_index]; + bpf += padding; + } + mb->info.audio.framesize = bpf; + + GST_DEBUG (0,"mpeg1mux::update_audio_info: samples per second %d\n", samplerate_index); + + gst_getbits_init(&gb, NULL, NULL); + gst_getbits_newbuf(&gb, data, mb->length); + + gst_flushbitsn(&gb, 12); + if (gst_getbits1(&gb) != 1) { + g_print("mpeg1mux::update_audio_info: marker bit error\n"); + } + gst_flushbitsn(&gb, 2); + mb->info.audio.protection = gst_getbits1(&gb); + gst_flushbitsn(&gb, 4); + mb->info.audio.frequency = gst_getbits2(&gb); + padding_bit = gst_getbits1(&gb); + gst_flushbitsn(&gb, 1); + mb->info.audio.mode = gst_getbits2(&gb); + mb->info.audio.mode_extension = gst_getbits2(&gb); + mb->info.audio.copyright = gst_getbits1(&gb); + mb->info.audio.original_copy = gst_getbits1(&gb); + mb->info.audio.emphasis = gst_getbits2(&gb); + + GST_DEBUG (0,"mpeg1mux::update_audio_info: layer %d\n", mb->info.audio.layer); + GST_DEBUG (0,"mpeg1mux::update_audio_info: bit_rate %d\n", mb->info.audio.bit_rate); + GST_DEBUG (0,"mpeg1mux::update_audio_info: frequency %d\n", mb->info.audio.frequency); + + mb->info.audio.samples_per_second = (double)dfrequency [mb->info.audio.frequency]; + + GST_DEBUG (0,"mpeg1mux::update_audio_info: samples per second %g\n", mb->info.audio.samples_per_second); + + mb->info.audio.decoding_order=0; + + tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode)); + tc->length = mb->info.audio.framesize; + tc->original_length = tc->length; + tc->frame_type = FRAME_TYPE_AUDIO; + + mb->info.audio.current_PTS = mb->info.audio.decoding_order * samples [mb->info.audio.layer] / + mb->info.audio.samples_per_second * 90. + startup_delay; + + GST_DEBUG (0,"mpeg1mux::update_audio_info: PTS %llu, length %u\n", mb->info.audio.current_PTS, mb->info.audio.framesize); + tc->PTS = mb->info.audio.current_PTS; + tc->DTS = mb->info.audio.current_PTS; + mb->timecode_list = g_list_append(mb->timecode_list, tc); + + mb->next_frame_time = tc->PTS; + + mb->info.audio.decoding_order++; + offset += tc->length; + } + else { + g_print("mpeg1mux::update_audio_info: Invalid MPEG Video header\n"); + } + } + while (offset < mb->length-4) { + id = GULONG_FROM_BE(*((gulong *)(data+offset))); + + /*mpegver = (header >> 19) & 0x3; // don't need this for bpf */ + layer_index = (id >> 17) & 0x3; + mb->info.audio.layer = 4 - layer_index; + lsf = (id & (1 << 20)) ? ((id & (1 << 19)) ? 0 : 1) : 1; + mb->info.audio.bit_rate = bitrate_index[lsf][mb->info.audio.layer - 1][((id >> 12) & 0xf)]; + samplerate_index = (id >> 10) & 0x3; + padding = (id >> 9) & 0x1; + + if (mb->info.audio.layer == 1) { + bpf = mb->info.audio.bit_rate * 12000; + bpf /= frequency[samplerate_index]; + bpf = ((bpf + padding) << 2); + } else { + bpf = mb->info.audio.bit_rate * 144000; + bpf /= frequency[samplerate_index]; + bpf += padding; + } + tc = (Mpeg1MuxTimecode *) g_malloc(sizeof(Mpeg1MuxTimecode)); + tc->length = bpf; + tc->original_length = tc->length; + tc->frame_type = FRAME_TYPE_AUDIO; + + mb->current_start = offset + bpf; + + mb->info.audio.samples_per_second = (double)dfrequency [mb->info.audio.frequency]; + + mb->info.audio.current_PTS = (mb->info.audio.decoding_order * samples [mb->info.audio.layer]) / + mb->info.audio.samples_per_second * 90. ; + + tc->DTS = tc->PTS = mb->info.audio.current_PTS; + GST_DEBUG (0,"mpeg1mux::update_audio_info: PTS %llu, %llu length %lu\n", mb->info.audio.current_PTS, tc->PTS, tc->length); + mb->timecode_list = g_list_append(mb->timecode_list, tc); + + mb->info.audio.decoding_order++; + offset += tc->length; + } + mb->scan_pos = offset; +} diff --git a/gst/mpeg1sys/buffer.h b/gst/mpeg1sys/buffer.h new file mode 100644 index 000000000..f3eba4f7a --- /dev/null +++ b/gst/mpeg1sys/buffer.h @@ -0,0 +1,141 @@ +/* Gnome-Streamer + * 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 __BUFFER_H__ +#define __BUFFER_H__ + +#include <gst/gst.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define MPEG1MUX_BUFFER_QUEUED(mb) (g_list_length((mb)->timecode_list)) +#define MPEG1MUX_BUFFER_SPACE(mb) ((mb)->length) +#define MPEG1MUX_BUFFER_DATA(mb) ((mb)->buffer) +#define MPEG1MUX_BUFFER_TYPE(mb) ((mb)->buffer) +#define MPEG1MUX_BUFFER_FIRST_TIMECODE(mb) (g_list_first((mb)->timecode_list)->data) + +#define BUFFER_TYPE_VIDEO 1 +#define BUFFER_TYPE_AUDIO 2 + +#define FRAME_TYPE_IFRAME 1 +#define FRAME_TYPE_BFRAME 2 +#define FRAME_TYPE_PFRAME 3 +#define FRAME_TYPE_AUDIO 4 + +typedef struct _Mpeg1MuxBuffer Mpeg1MuxBuffer; +typedef struct _Mpeg1MuxTimecode Mpeg1MuxTimecode; + +typedef struct video_struc /* Informationen ueber Video Stream */ +{ + unsigned int stream_length ; + unsigned int num_sequence ; + unsigned int num_seq_end ; + unsigned int num_pictures ; + unsigned int num_groups ; + unsigned int num_frames[4] ; + unsigned int avg_frames[4] ; + + unsigned int horizontal_size; + unsigned int vertical_size ; + unsigned int aspect_ratio ; + unsigned int picture_rate ; + unsigned int bit_rate ; + unsigned int comp_bit_rate ; + unsigned int vbv_buffer_size; + unsigned int CSPF ; + + guint64 PTS; + guint64 DTS; + + guint64 current_PTS; + guint64 current_DTS; + guchar current_type; + + double secs_per_frame; + gulong group_order, decoding_order; +} Video_struc; + +typedef struct audio_struc /* Informationen ueber Audio Stream */ +{ + unsigned int stream_length ; + unsigned int num_syncword ; + unsigned int num_frames [2] ; + unsigned int framesize ; + unsigned int layer ; + unsigned int protection ; + unsigned int bit_rate ; + unsigned int frequency ; + unsigned int mode ; + unsigned int mode_extension ; + unsigned int copyright ; + unsigned int original_copy ; + unsigned int emphasis ; + + guint64 PTS; + + guint64 current_PTS; + + double samples_per_second; + gulong decoding_order; +} Audio_struc; + +struct _Mpeg1MuxTimecode { + gulong length; + gulong original_length; + guchar frame_type; + guint64 PTS; + guint64 DTS; +}; + +struct _Mpeg1MuxBuffer { + unsigned char *buffer; + gulong length; + gulong base; + gulong scan_pos; + gulong last_pos; + gulong current_start; + guchar buffer_type; + guchar stream_id; + gboolean new_frame; + guint64 next_frame_time; + + union { + Video_struc video; + Audio_struc audio; + } info; + + GList *timecode_list; + GList *queued_list; +}; + +Mpeg1MuxBuffer *mpeg1mux_buffer_new(guchar type, guchar id); + +void mpeg1mux_buffer_queue(Mpeg1MuxBuffer *mb, GstBuffer *buf); +void mpeg1mux_buffer_shrink(Mpeg1MuxBuffer *mb, gulong size); +gulong mpeg1mux_buffer_update_queued(Mpeg1MuxBuffer *mb, guint64 scr); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __BUFFER_H__ */ diff --git a/gst/mpeg1sys/gstmpeg1systemencode.c b/gst/mpeg1sys/gstmpeg1systemencode.c new file mode 100644 index 000000000..d8927d0fe --- /dev/null +++ b/gst/mpeg1sys/gstmpeg1systemencode.c @@ -0,0 +1,572 @@ +/* Gnome-Streamer + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +/*#define DEBUG_ENABLED */ +#include "gstmpeg1systemencode.h" +#include "main.h" + +/*#define GST_DEBUG(a, b...) g_print (##b) */ + +/* elementfactory information */ +static GstElementDetails system_encode_details = { + "MPEG1 Multiplexer", + "Filter/Multiplexer/System", + "Multiplexes MPEG-1 Streams", + VERSION, + "Wim Taymans <wim.taymans@chello.be>", + "(C) 2000", +}; + +/* GstMPEG1SystemEncode signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + /* FILL ME */ +}; + +GST_PADTEMPLATE_FACTORY (src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "src_video", + "video/mpeg", + "mpegversion", GST_PROPS_INT (1), + "systemstream", GST_PROPS_BOOLEAN (TRUE) + ) +) +GST_PADTEMPLATE_FACTORY (video_sink_factory, + "video_%02d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_CAPS_NEW ( + "sink_video", + "video/mpeg", + "mpegversion", GST_PROPS_INT (1), + "systemstream", GST_PROPS_BOOLEAN (FALSE) + ) +) + +GST_PADTEMPLATE_FACTORY (audio_sink_factory, + "audio_%02d", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_CAPS_NEW ( + "sink_audio", + "audio/mp3", + NULL + ) +) + +static void gst_system_encode_class_init (GstMPEG1SystemEncodeClass *klass); +static void gst_system_encode_init (GstMPEG1SystemEncode *system_encode); + +static GstPad* gst_system_encode_request_new_pad (GstElement *element, GstPadTemplate *templ, + const gchar *unused); +static void gst_system_encode_chain (GstPad *pad, GstBuffer *buf); + +static void gst_system_encode_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec); +static void gst_system_encode_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +/*static guint gst_system_encode_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_mpeg1_system_encode_get_type (void) +{ + static GType system_encode_type = 0; + + if (!system_encode_type) { + static const GTypeInfo system_encode_info = { + sizeof(GstMPEG1SystemEncodeClass), + NULL, + NULL, + (GClassInitFunc)gst_system_encode_class_init, + NULL, + NULL, + sizeof(GstMPEG1SystemEncode), + 0, + (GInstanceInitFunc)gst_system_encode_init, + NULL + }; + system_encode_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMPEG1SystemEncode", &system_encode_info, 0); + } + return system_encode_type; +} + +static void +gst_system_encode_class_init (GstMPEG1SystemEncodeClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_system_encode_set_property; + gobject_class->get_property = gst_system_encode_get_property; + + gstelement_class->request_new_pad = gst_system_encode_request_new_pad; +} + +static void +gst_system_encode_init (GstMPEG1SystemEncode *system_encode) +{ + system_encode->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (src_factory), "src"); + gst_element_add_pad (GST_ELEMENT (system_encode), system_encode->srcpad); + + system_encode->video_buffer = mpeg1mux_buffer_new (BUFFER_TYPE_VIDEO, 0xE0); + system_encode->audio_buffer = mpeg1mux_buffer_new (BUFFER_TYPE_AUDIO, 0xC0); + system_encode->have_setup = FALSE; + system_encode->mta = NULL; + system_encode->packet_size = 2048; + system_encode->lock = g_mutex_new(); + system_encode->current_pack = system_encode->packets_per_pack = 3; + system_encode->video_delay_ms = 0; + system_encode->audio_delay_ms = 0; + system_encode->sectors_delay = 0; + system_encode->startup_delay = ~1; + system_encode->which_streams = 0; + system_encode->num_audio_pads = 0; + system_encode->num_video_pads = 0; + system_encode->pack = g_malloc (sizeof (Pack_struc)); + system_encode->sys_header = g_malloc (sizeof (Sys_header_struc)); + system_encode->sector = g_malloc (sizeof (Sector_struc)); + +} + +static GstPad* +gst_system_encode_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused) +{ + GstMPEG1SystemEncode *system_encode; + gchar *name = NULL; + GstPad *newpad; + + g_return_val_if_fail (templ != NULL, NULL); + + if (templ->direction != GST_PAD_SINK) { + g_warning ("system_encode: request pad that is not a SINK pad\n"); + return NULL; + } + system_encode = GST_SYSTEM_ENCODE (element); + + if (templ == GST_PADTEMPLATE_GET (audio_sink_factory)) { + name = g_strdup_printf ("audio_%02d", system_encode->num_audio_pads); + g_print ("%s\n", name); + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (newpad, GINT_TO_POINTER (system_encode->num_audio_pads)); + + system_encode->audio_pad[system_encode->num_audio_pads] = newpad; + system_encode->num_audio_pads++; + system_encode->which_streams |= STREAMS_AUDIO; + } + else if (templ == GST_PADTEMPLATE_GET (video_sink_factory)) { + name = g_strdup_printf ("video_%02d", system_encode->num_video_pads); + g_print ("%s\n", name); + newpad = gst_pad_new_from_template (templ, name); + gst_pad_set_element_private (newpad, GINT_TO_POINTER (system_encode->num_video_pads)); + + system_encode->video_pad[system_encode->num_video_pads] = newpad; + system_encode->num_video_pads++; + system_encode->which_streams |= STREAMS_VIDEO; + } + else { + g_warning ("system_encode: this is not our template!\n"); + return NULL; + } + + gst_pad_set_chain_function (newpad, gst_system_encode_chain); + gst_element_add_pad (GST_ELEMENT (system_encode), newpad); + + return newpad; +} + +/* return a list of all the highest prioripty streams */ +static GList* +gst_system_encode_pick_streams (GList *mta, GstMPEG1SystemEncode *system_encode) +{ + guint64 lowest = ~1; + + GST_DEBUG (0, "pick_streams: %lld, %lld\n", system_encode->video_buffer->next_frame_time, + system_encode->audio_buffer->next_frame_time); + + if (system_encode->which_streams & STREAMS_VIDEO) { + if (system_encode->video_buffer->next_frame_time < lowest-system_encode->video_delay) { + lowest = system_encode->video_buffer->next_frame_time; + } + } + if (system_encode->which_streams & STREAMS_AUDIO) { + if (system_encode->audio_buffer->next_frame_time < lowest-system_encode->audio_delay) { + lowest = system_encode->audio_buffer->next_frame_time; + } + } + + if (system_encode->which_streams & STREAMS_VIDEO) { + if (system_encode->video_buffer->next_frame_time == lowest) { + mta = g_list_append(mta, system_encode->video_buffer); + } + } + if (system_encode->which_streams & STREAMS_AUDIO) { + if (system_encode->audio_buffer->next_frame_time == lowest) { + mta = g_list_append(mta, system_encode->audio_buffer); + } + } + return mta; +} + +static gboolean +gst_system_encode_have_data (GstMPEG1SystemEncode *system_encode) +{ + + if (system_encode->which_streams == (STREAMS_VIDEO | STREAMS_AUDIO)) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->audio_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer) > system_encode->packet_size*2 && + MPEG1MUX_BUFFER_QUEUED(system_encode->video_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + if (system_encode->which_streams == STREAMS_VIDEO) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->video_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + if (system_encode->which_streams == STREAMS_VIDEO) { + if (MPEG1MUX_BUFFER_QUEUED(system_encode->audio_buffer) > 2 && + MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer) > system_encode->packet_size*2) { + return TRUE; + } + } + + return FALSE; +} + +static GList* +gst_system_encode_update_mta (GstMPEG1SystemEncode *system_encode, GList *mta, gulong size) +{ + GList *streams = g_list_first(mta); + Mpeg1MuxBuffer *mb = (Mpeg1MuxBuffer *)streams->data; + + GST_DEBUG (0,"system_encode::multiplex: update mta\n"); + + mpeg1mux_buffer_shrink(mb, size); + + mta = g_list_remove(mta, mb); + + return mta; +} + +static void +gst_system_setup_multiplex (GstMPEG1SystemEncode *system_encode) +{ + Mpeg1MuxTimecode *video_tc, *audio_tc; + + system_encode->audio_buffer_size = 4*1024; + system_encode->video_buffer_size = 46*1024; + system_encode->bytes_output = 0; + system_encode->min_packet_data = system_encode->packet_size - PACK_HEADER_SIZE - SYS_HEADER_SIZE - + PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH; + system_encode->max_packet_data = system_encode->packet_size - PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH; + + if (system_encode->which_streams & STREAMS_VIDEO) { + system_encode->video_rate = system_encode->video_buffer->info.video.bit_rate * 50; + } + else system_encode->video_rate = 0; + if (system_encode->which_streams & STREAMS_AUDIO) + system_encode->audio_rate = system_encode->audio_buffer->info.audio.bit_rate * 128; + else system_encode->audio_rate = 0; + + system_encode->data_rate = system_encode->video_rate + system_encode->audio_rate; + + system_encode->dmux_rate = ceil((double)(system_encode->data_rate) * + ((double)(system_encode->packet_size)/(double)(system_encode->min_packet_data) + + ((double)(system_encode->packet_size)/(double)(system_encode->max_packet_data) * + (double)(system_encode->packets_per_pack-1.))) / (double)(system_encode->packets_per_pack) ); + system_encode->data_rate = ceil(system_encode->dmux_rate/50.)*50; + + GST_DEBUG (0,"system_encode::multiplex: data_rate %u, video_rate: %u, audio_rate: %u\n", system_encode->data_rate, + system_encode->video_rate, system_encode->audio_rate); + + system_encode->video_delay = (double)system_encode->video_delay_ms*(double)(CLOCKS/1000); + system_encode->audio_delay = (double)system_encode->audio_delay_ms*(double)(CLOCKS/1000); + + system_encode->mux_rate = ceil(system_encode->dmux_rate/50.); + system_encode->dmux_rate= system_encode->mux_rate * 50.; + + video_tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(system_encode->video_buffer); + audio_tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(system_encode->audio_buffer); + + GST_DEBUG (0,"system_encode::video tc %lld, audio tc %lld:\n", video_tc->DTS, audio_tc->DTS); + + system_encode->delay = ((double)system_encode->sectors_delay + + ceil((double)video_tc->length/(double)system_encode->min_packet_data) + + ceil((double)video_tc->length/(double)system_encode->min_packet_data )) * + (double)system_encode->packet_size/system_encode->dmux_rate*(double)CLOCKS; + + system_encode->audio_delay += system_encode->delay; + system_encode->video_delay += system_encode->delay; + + system_encode->audio_delay = 0; + system_encode->video_delay = 0; + system_encode->delay = 0; + + GST_DEBUG (0,"system_encode::multiplex: delay %g, mux_rate: %lu\n", system_encode->delay, system_encode->mux_rate); +} + +static void +gst_system_encode_multiplex(GstMPEG1SystemEncode *system_encode) +{ + GList *streams; + Mpeg1MuxBuffer *mb = (Mpeg1MuxBuffer *)streams->data; + guchar timestamps; + guchar buffer_scale; + GstBuffer *outbuf; + Pack_struc *pack; + Sys_header_struc *sys_header; + Mpeg1MuxTimecode *tc; + gulong buffer_size, non_scaled_buffer_size, total_queued; + guint64 PTS, DTS; + + g_mutex_lock(system_encode->lock); + + while (gst_system_encode_have_data(system_encode)) { + GST_DEBUG (0,"system_encode::multiplex: multiplexing\n"); + + if (!system_encode->have_setup) { + gst_system_setup_multiplex(system_encode); + system_encode->have_setup = TRUE; + } + + if (system_encode->mta == NULL) { + system_encode->mta = gst_system_encode_pick_streams(system_encode->mta, system_encode); + } + if (system_encode->mta == NULL) break; + + + system_encode->SCR = (guint64)(system_encode->bytes_output+LAST_SCR_BYTE_IN_PACK)*CLOCKS/system_encode->dmux_rate; + + + streams = g_list_first(system_encode->mta); + mb = (Mpeg1MuxBuffer *)streams->data; + + if (system_encode->current_pack == system_encode->packets_per_pack) { + create_pack(system_encode->pack, system_encode->SCR, system_encode->mux_rate); + create_sys_header (system_encode->sys_header, system_encode->mux_rate, 1, 1, 1, 1, 1, 1, + AUDIO_STR_0, 0, system_encode->audio_buffer_size/128, + VIDEO_STR_0, 1, system_encode->video_buffer_size/1024, system_encode->which_streams ); + system_encode->current_pack = 0; + pack = system_encode->pack; + sys_header = system_encode->sys_header; + } + else { + system_encode->current_pack++; + pack = NULL; + sys_header = NULL; + } + + tc = MPEG1MUX_BUFFER_FIRST_TIMECODE(mb); + if (mb->new_frame) { + GST_DEBUG (0,"system_encode::multiplex: new frame\n"); + if (tc->frame_type == FRAME_TYPE_AUDIO || tc->frame_type == FRAME_TYPE_IFRAME || tc->frame_type == FRAME_TYPE_PFRAME) { + timestamps = TIMESTAMPS_PTS; + } + else { + timestamps = TIMESTAMPS_PTS_DTS; + } + } + else { + timestamps = TIMESTAMPS_NO; + } + + if (tc->frame_type != FRAME_TYPE_AUDIO) { + if (tc->PTS<system_encode->startup_delay) + system_encode->startup_delay = tc->PTS; + } + + if (tc->frame_type == FRAME_TYPE_AUDIO) { + buffer_scale = 0; + non_scaled_buffer_size = system_encode->audio_buffer_size; + buffer_size = system_encode->audio_buffer_size/128; + PTS = tc->PTS + system_encode->audio_delay + system_encode->startup_delay; + DTS = tc->PTS + system_encode->audio_delay + system_encode->startup_delay; + } + else { + buffer_scale = 1; + non_scaled_buffer_size = system_encode->video_buffer_size; + buffer_size = system_encode->video_buffer_size/1024; + PTS = tc->PTS + system_encode->video_delay; + DTS = tc->DTS + system_encode->video_delay; + } + + total_queued = mpeg1mux_buffer_update_queued(mb, system_encode->SCR); + + if (non_scaled_buffer_size - total_queued >= system_encode->packet_size) { + + /* write the pack/packet here */ + create_sector (system_encode->sector, pack, sys_header, + system_encode->packet_size, + MPEG1MUX_BUFFER_DATA(mb), mb->stream_id, buffer_scale, + buffer_size, TRUE, PTS, DTS, + timestamps, system_encode->which_streams); + /* update mta */ + system_encode->mta = gst_system_encode_update_mta(system_encode, system_encode->mta, + system_encode->sector->length_of_packet_data); + } + else { + /* write a padding packet */ + create_sector (system_encode->sector, pack, sys_header, + system_encode->packet_size, NULL, PADDING_STR, 0, + 0, FALSE, 0, 0, + TIMESTAMPS_NO, system_encode->which_streams); + } + + outbuf = gst_buffer_new(); + GST_BUFFER_DATA(outbuf) = g_malloc(system_encode->sector->length_of_sector); + GST_BUFFER_SIZE(outbuf) = system_encode->sector->length_of_sector; + memcpy(GST_BUFFER_DATA(outbuf),system_encode->sector->buf, system_encode->sector->length_of_sector); + system_encode->bytes_output += GST_BUFFER_SIZE(outbuf); + gst_pad_push(system_encode->srcpad,outbuf); + + GST_DEBUG (0,"system_encode::multiplex: writing %02x\n", mb->stream_id); + + } + gst_info("system_encode::multiplex: data left in video buffer %lu\n", MPEG1MUX_BUFFER_SPACE(system_encode->video_buffer)); + gst_info("system_encode::multiplex: data left in audio buffer %lu\n", MPEG1MUX_BUFFER_SPACE(system_encode->audio_buffer)); + + g_mutex_unlock(system_encode->lock); +} + +static void +gst_system_encode_chain (GstPad *pad, GstBuffer *buf) +{ + GstMPEG1SystemEncode *system_encode; + guchar *data; + gulong size; + const gchar *padname; + gint channel; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + system_encode = GST_SYSTEM_ENCODE (GST_OBJECT_PARENT (pad)); + data = GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf); + + GST_DEBUG (0,"system_encode::chain: system_encode: have buffer of size %lu\n",size); + padname = GST_OBJECT_NAME (pad); + + if (strncmp(padname, "audio_", 6) == 0) { + channel = atoi(&padname[6]); + GST_DEBUG (0,"gst_system_encode_chain: got audio buffer in from audio channel %02d\n", channel); + + mpeg1mux_buffer_queue(system_encode->audio_buffer, buf); + } + else if (strncmp(padname, "video_", 6) == 0) { + channel = atoi(&padname[6]); + GST_DEBUG (0,"gst_system_encode_chain: got video buffer in from video channel %02d\n", channel); + + mpeg1mux_buffer_queue(system_encode->video_buffer, buf); + + } + else { + g_assert_not_reached (); + } + gst_system_encode_multiplex(system_encode); + + gst_buffer_unref(buf); +} + +static void +gst_system_encode_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstMPEG1SystemEncode *system_encode; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SYSTEM_ENCODE(object)); + system_encode = GST_SYSTEM_ENCODE(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_system_encode_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMPEG1SystemEncode *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SYSTEM_ENCODE(object)); + src = GST_SYSTEM_ENCODE(object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* this filter needs the getbits functions */ + if (!gst_library_load("gstgetbits")) { + gst_info("system_encode:: could not load support library: 'gstgetbits'\n"); + return FALSE; + } + + /* create an elementfactory for the system_encode element */ + factory = gst_elementfactory_new("system_encode",GST_TYPE_SYSTEM_ENCODE, + &system_encode_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (audio_sink_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (video_sink_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "system_encode", + plugin_init +}; diff --git a/gst/mpeg1sys/gstmpeg1systemencode.h b/gst/mpeg1sys/gstmpeg1systemencode.h new file mode 100644 index 000000000..bb24f01d1 --- /dev/null +++ b/gst/mpeg1sys/gstmpeg1systemencode.h @@ -0,0 +1,110 @@ +/* Gnome-Streamer + * 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 __SYSTEM_ENCODE_H__ +#define __SYSTEM_ENCODE_H__ + + +#include <config.h> +#include <gst/gst.h> +#include <libs/getbits/gstgetbits.h> + +#include "buffer.h" +#include "main.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_SYSTEM_ENCODE \ + (gst_mpeg1_system_encode_get_type()) +#define GST_SYSTEM_ENCODE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SYSTEM_ENCODE,GstMPEG1SystemEncode)) +#define GST_SYSTEM_ENCODE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SYSTEM_ENCODE,GstMPEG1SystemEncode)) +#define GST_IS_SYSTEM_ENCODE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SYSTEM_ENCODE)) +#define GST_IS_SYSTEM_ENCODE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SYSTEM_ENCODE)) + +typedef struct _GstMPEG1SystemEncode GstMPEG1SystemEncode; +typedef struct _GstMPEG1SystemEncodeClass GstMPEG1SystemEncodeClass; + +struct _GstMPEG1SystemEncode { + GstElement element; + + GstPad *srcpad; + + gboolean have_setup; + + GMutex *lock; + + guint num_audio_pads; + guint num_video_pads; + + Mpeg1MuxBuffer *audio_buffer; + Mpeg1MuxBuffer *video_buffer; + + Pack_struc *pack; + Sys_header_struc *sys_header; + Sector_struc *sector; + + guint data_rate, video_rate, audio_rate; + gdouble delay, audio_delay, video_delay; + gdouble clock_cycles; + gulong sectors_delay, video_delay_ms, audio_delay_ms; + gulong startup_delay; + gulong audio_buffer_size; + gulong video_buffer_size; + gulong mux_rate, dmux_rate; + guint64 SCR; + gint which_streams; + + gint current_pack; + gulong min_packet_data; + gulong max_packet_data; + gint packets_per_pack; + gulong packet_size; + gulong bytes_output; + + GList *mta; + + /* stream input pads */ + GstPad *private_1_pad[8]; /* up to 8 ac3 audio tracks <grumble> */ + GstPad *private_2_pad; + GstPad *video_pad[16]; + GstPad *audio_pad[32]; +}; + +struct _GstMPEG1SystemEncodeClass { + GstElementClass parent_class; +}; + +GType gst_mpeg1_system_encode_get_type(void); + +/* multplex.c */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __SYSTEM_ENCODE_H__ */ diff --git a/gst/mpeg1sys/main.h b/gst/mpeg1sys/main.h new file mode 100644 index 000000000..434f57f5d --- /dev/null +++ b/gst/mpeg1sys/main.h @@ -0,0 +1,140 @@ +/************************************************************************* +* Generating a MPEG/SYSTEMS * +* MULTIPLEXED VIDEO/AUDIO STREAM * +* from two MPEG source streams * +* Christoph Moar * +* SIEMENS CORPORATE RESEARCH AND DEVELOPMENT ST SN 11 / T SN 6 * +* (C) 1994 1995 * +************************************************************************** +* Restrictions apply. Will not support the whole MPEG/SYSTEM Standard. * +* Basically, will generate Constrained System Parameter Files. * +* Mixes only one audio and/or one video stream. Might be expanded. * +*************************************************************************/ + +/************************************************************************* +* mplex - MPEG/SYSTEMS multiplexer * +* Copyright (C) 1994 1995 Christoph Moar * +* Siemens ZFE ST SN 11 / T SN 6 * +* * +* moar@informatik.tu-muenchen.de * +* (Christoph Moar) * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program 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 General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the Free Software * +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * +*************************************************************************/ + + +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include <glib.h> + +#define PACK_START 0x000001ba +#define SYS_HEADER_START 0x000001bb +#define ISO11172_END 0x000001b9 +#define PACKET_START 0x000001 + +#define CLOCKS 90000.0 /* System Clock Hertz */ + +#define AFTER_PACKET_LENGTH 15 /* No of non-data-bytes */ + /* following the packet */ + /* length field */ +#define LAST_SCR_BYTE_IN_PACK 9 /* No of bytes in pack */ + /* preceding, and */ + /* including, the SCR */ + +/* The following values for sys_header_length & size are only valid for */ +/* System streams consisting of two basic streams. When wrapping around */ +/* the system layer on a single video or a single audio stream, those */ +/* values get decreased by 3. */ + +#define SYS_HEADER_LENGTH 12 /* length of Sys Header */ + /* after start code and */ + /* length field */ + +#define SYS_HEADER_SIZE 18 /* incl. start code and */ + /* length field */ +#define PACK_HEADER_SIZE 12 + +#define PACKET_HEADER_SIZE 6 + +#define MAX_SECTOR_SIZE 0x20000 /* Max Sektor Groesse */ + +#define STREAMS_VIDEO 1 +#define STREAMS_AUDIO 2 +#define STREAMS_BOTH 3 + +#define AUDIO_STREAMS 0xb8 /* Marker Audio Streams */ +#define VIDEO_STREAMS 0xb9 /* Marker Video Streams */ +#define AUDIO_STR_0 0xc0 /* Marker Audio Stream0 */ +#define VIDEO_STR_0 0xe0 /* Marker Video Stream0 */ +#define PADDING_STR 0xbe /* Marker Padding Stream*/ + +#define ZERO_STUFFING_BYTE 0 +#define STUFFING_BYTE 0xff +#define RESERVED_BYTE 0xff +#define TIMESTAMPS_NO 0 /* Flag NO timestamps */ +#define TIMESTAMPS_PTS 1 /* Flag PTS timestamp */ +#define TIMESTAMPS_PTS_DTS 2 /* Flag BOTH timestamps */ + +#define MARKER_SCR 2 /* Marker SCR */ +#define MARKER_JUST_PTS 2 /* Marker only PTS */ +#define MARKER_PTS 3 /* Marker PTS */ +#define MARKER_DTS 1 /* Marker DTS */ +#define MARKER_NO_TIMESTAMPS 0x0f /* Marker NO timestamps */ + +#define STATUS_AUDIO_END 0 /* Statusmessage A end */ +#define STATUS_VIDEO_END 1 /* Statusmessage V end */ +#define STATUS_AUDIO_TIME_OUT 2 /* Statusmessage A out */ +#define STATUS_VIDEO_TIME_OUT 3 /* Statusmessage V out */ + +/************************************************************************* + Typ- und Strukturdefinitionen +*************************************************************************/ + +typedef struct sector_struc /* A sector, can contain pack, sys header */ + /* and packet. */ +{ unsigned char buf [MAX_SECTOR_SIZE] ; + unsigned int length_of_sector ; + unsigned int length_of_packet_data ; + guint64 TS ; +} Sector_struc; + +typedef struct pack_struc /* Pack Info */ +{ unsigned char buf [PACK_HEADER_SIZE]; + guint64 SCR; +} Pack_struc; + +typedef struct sys_header_struc /* System Header Info */ +{ unsigned char buf [SYS_HEADER_SIZE]; +} Sys_header_struc; + +/************************************************************************* + Funktionsprototypen, keine Argumente, K&R Style +*************************************************************************/ + +/* systems.c */ +void create_sector (Sector_struc *sector, Pack_struc *pack, Sys_header_struc *sys_header, + unsigned int packet_size, unsigned char *inputbuffer, unsigned char type, unsigned char buffer_scale, + unsigned int buffer_size, unsigned char buffers, guint64 PTS, guint64 DTS, + unsigned char timestamps, unsigned int which_streams); + +void create_pack (Pack_struc *pack, guint64 SCR, unsigned int mux_rate); + +void create_sys_header (Sys_header_struc *sys_header, unsigned int rate_bound, unsigned char audio_bound, + unsigned char fixed, unsigned char CSPS, unsigned char audio_lock, unsigned char video_lock, + unsigned char video_bound, unsigned char stream1, unsigned char buffer1_scale, unsigned int buffer1_size, + unsigned char stream2, unsigned char buffer2_scale, unsigned int buffer2_size, unsigned int which_streams); + +#endif diff --git a/gst/mpeg1sys/systems.c b/gst/mpeg1sys/systems.c new file mode 100644 index 000000000..d0b3388a4 --- /dev/null +++ b/gst/mpeg1sys/systems.c @@ -0,0 +1,290 @@ +#include <string.h> + +#include "main.h" + +static void buffer_timecode (timecode, marker, buffer) +guint64 timecode; +unsigned char marker; +unsigned char **buffer; + +{ + unsigned char temp; + + temp = (marker << 4) | ((timecode>>29) & 0x38) | + ((timecode >> 29) & 0x6) | 1; + *((*buffer)++)=temp; + temp = (timecode & 0x3fc00000) >> 22; + *((*buffer)++)=temp; + temp = ((timecode & 0x003f8000) >> 14) | 1; + *((*buffer)++)=temp; + temp = (timecode & 0x7f80) >> 7; + *((*buffer)++)=temp; + temp = ((timecode & 0x007f) << 1) | 1; + *((*buffer)++)=temp; +} + +/************************************************************************* + creates a complete sector. + Also copies Pack and Sys_Header informations into the + sector buffer, then reads a packet full of data from + the input stream into the sector buffer. +*************************************************************************/ + + +void create_sector (sector, pack, sys_header, + packet_size, inputbuffer, type, + buffer_scale, buffer_size, buffers, + PTS, DTS, timestamps, which_streams ) + +Sector_struc *sector; +Pack_struc *pack; +Sys_header_struc *sys_header; +unsigned int packet_size; +unsigned char *inputbuffer; + +unsigned char type; +unsigned char buffer_scale; +unsigned int buffer_size; +unsigned char buffers; +guint64 PTS; +guint64 DTS; +unsigned char timestamps; +unsigned int which_streams; + +{ + int i,j,tmp; + unsigned char *index; + unsigned char *size_offset; + + /* printf("creating sector\n"); */ + + index = sector->buf; + sector->length_of_sector=0; + +/* Should we copy Pack Header information ? */ + + if (pack != NULL) + { + i = sizeof(pack->buf); + bcopy (pack->buf, index, i); + index += i; + sector->length_of_sector += i; + } + +/* Should we copy System Header information ? */ + + if (sys_header != NULL) + { + i = sizeof(sys_header->buf); + + /* only one stream? 3 bytes less in sys header */ + if (which_streams != STREAMS_BOTH) i -= 3; + + bcopy (sys_header->buf, index, i); + index += i; + sector->length_of_sector += i; + } + + /* write constant packet header data */ + + *(index++) = (unsigned char)(PACKET_START)>>16; + *(index++) = (unsigned char)(PACKET_START & 0x00ffff)>>8; + *(index++) = (unsigned char)(PACKET_START & 0x0000ff); + *(index++) = type; + + /* we remember this offset in case we will have to shrink this packet */ + + size_offset = index; + *(index++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)>>8); + *(index++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)&0xff); + + *(index++) = STUFFING_BYTE; + *(index++) = STUFFING_BYTE; + *(index++) = STUFFING_BYTE; + + i = 0; + + if (!buffers) i +=2; + if (timestamps == TIMESTAMPS_NO) i+=9; + else if (timestamps == TIMESTAMPS_PTS) i+=5; + + /* printf("%i stuffing %d\n", i, timestamps); */ + + for (j=0; j<i; j++) + *(index++) = STUFFING_BYTE; + + /* should we write buffer info ? */ + + if (buffers) + { + *(index++) = (unsigned char) (0x40 | + (buffer_scale << 5) | (buffer_size >> 8)); + *(index++) = (unsigned char) (buffer_size & 0xff); + } + + /* should we write PTS, PTS & DTS or nothing at all ? */ + + switch (timestamps) + { + case TIMESTAMPS_NO: + *(index++) = MARKER_NO_TIMESTAMPS; + break; + case TIMESTAMPS_PTS: + buffer_timecode (PTS, MARKER_JUST_PTS, &index); + sector->TS = PTS; + break; + case TIMESTAMPS_PTS_DTS: + buffer_timecode (PTS, MARKER_PTS, &index); + buffer_timecode (DTS, MARKER_DTS, &index); + sector->TS = DTS; + break; + } + +/* read in packet data */ + + i = (packet_size - PACKET_HEADER_SIZE - AFTER_PACKET_LENGTH); + + if (type == PADDING_STR) + { + for (j=0; j<i; j++) + *(index++)=(unsigned char) STUFFING_BYTE; + tmp = i; + } + else + { + /*tmp = fread (index, sizeof (unsigned char), i, inputstream);*/ + memcpy(index, inputbuffer, i); tmp = i; + index += tmp; + + /* if we did not get enough data bytes, shorten the Packet length */ + + if (tmp != i) + { + packet_size -= (i-tmp); + *(size_offset++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)>>8); + *(size_offset++) = (unsigned char)((packet_size - PACKET_HEADER_SIZE)&0xff); + +/* zero byte stuffing in the last Packet of a stream */ +/* we don't need this any more, since we shortenend the packet */ +/* for (j=tmp; j<i; j++) */ +/* *(index++)=(unsigned char) ZERO_STUFFING_BYTE; */ + } + } + + + /* write other struct data */ + + sector->length_of_sector += packet_size; + sector->length_of_packet_data = tmp; + +} + +/************************************************************************* + writes specifical pack header information into a buffer + later this will be copied from the sector routine into + the sector buffer +*************************************************************************/ + +void create_pack (pack, SCR, mux_rate) + +Pack_struc *pack; +unsigned int mux_rate; +guint64 SCR; + +{ + unsigned char *index; + + index = pack->buf; + + *(index++) = (unsigned char)((PACK_START)>>24); + *(index++) = (unsigned char)((PACK_START & 0x00ff0000)>>16); + *(index++) = (unsigned char)((PACK_START & 0x0000ff00)>>8); + *(index++) = (unsigned char)(PACK_START & 0x000000ff); + buffer_timecode (SCR, MARKER_SCR, &index); + *(index++) = (unsigned char)(0x80 | (mux_rate >>15)); + *(index++) = (unsigned char)(0xff & (mux_rate >> 7)); + *(index++) = (unsigned char)(0x01 | ((mux_rate & 0x7f)<<1)); + pack->SCR = SCR; +} + + +/************************************************************************* + writes specifical system header information into a buffer + later this will be copied from the sector routine into + the sector buffer +*************************************************************************/ + +void create_sys_header (sys_header, rate_bound, audio_bound, + fixed, CSPS, audio_lock, video_lock, + video_bound, + stream1, buffer1_scale, buffer1_size, + stream2, buffer2_scale, buffer2_size, + which_streams) + +Sys_header_struc *sys_header; +unsigned int rate_bound; +unsigned char audio_bound; +unsigned char fixed; +unsigned char CSPS; +unsigned char audio_lock; +unsigned char video_lock; +unsigned char video_bound; + +unsigned char stream1; +unsigned char buffer1_scale; +unsigned int buffer1_size; +unsigned char stream2; +unsigned char buffer2_scale; +unsigned int buffer2_size; +unsigned int which_streams; + +{ + unsigned char *index; + + index = sys_header->buf; + + /* if we are not using both streams, we should clear some + options here */ + + if (!(which_streams & STREAMS_AUDIO)) + audio_bound = 0; + if (!(which_streams & STREAMS_VIDEO)) + video_bound = 0; + + *(index++) = (unsigned char)((SYS_HEADER_START)>>24); + *(index++) = (unsigned char)((SYS_HEADER_START & 0x00ff0000)>>16); + *(index++) = (unsigned char)((SYS_HEADER_START & 0x0000ff00)>>8); + *(index++) = (unsigned char)(SYS_HEADER_START & 0x000000ff); + + if (which_streams == STREAMS_BOTH) { + *(index++) = (unsigned char)(SYS_HEADER_LENGTH >> 8); + *(index++) = (unsigned char)(SYS_HEADER_LENGTH & 0xff); + } else { + *(index++) = (unsigned char)((SYS_HEADER_LENGTH-3) >> 8); + *(index++) = (unsigned char)((SYS_HEADER_LENGTH-3) & 0xff); + } + + *(index++) = (unsigned char)(0x80 | (rate_bound >>15)); + *(index++) = (unsigned char)(0xff & (rate_bound >> 7)); + *(index++) = (unsigned char)(0x01 | ((rate_bound & 0x7f)<<1)); + *(index++) = (unsigned char)((audio_bound << 2)|(fixed << 1)|CSPS); + *(index++) = (unsigned char)((audio_lock << 7)| + (video_lock << 6)|0x20|video_bound); + + *(index++) = (unsigned char)RESERVED_BYTE; + + if (which_streams & STREAMS_AUDIO) { + *(index++) = stream1; + *(index++) = (unsigned char) (0xc0 | + (buffer1_scale << 5) | (buffer1_size >> 8)); + *(index++) = (unsigned char) (buffer1_size & 0xff); + } + + if (which_streams & STREAMS_VIDEO) { + *(index++) = stream2; + *(index++) = (unsigned char) (0xc0 | + (buffer2_scale << 5) | (buffer2_size >> 8)); + *(index++) = (unsigned char) (buffer2_size & 0xff); + } + +} diff --git a/gst/mpeg2sub/.gitignore b/gst/mpeg2sub/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/mpeg2sub/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/mpeg2sub/Makefile.am b/gst/mpeg2sub/Makefile.am new file mode 100644 index 000000000..d8bfe888c --- /dev/null +++ b/gst/mpeg2sub/Makefile.am @@ -0,0 +1,18 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstmpeg2subt.la + +libgstmpeg2subt_la_SOURCES = gstmpeg2subt.c + +if HAVE_CPU_I386 +ARCHCFLAGS = -m486 +else +ARCHCFLAGS = +endif + +libgstmpeg2subt_la_CFLAGS = -O3 $(ARCHCFLAGS) -fschedule-insns2 $(FOMIT_FRAME_POINTER) -finline-functions -ffast-math $(GST_CFLAGS) + +noinst_HEADERS = gstmpeg2subt.h + +EXTRA_DIST = Notes.txt + diff --git a/gst/mpeg2sub/Notes.txt b/gst/mpeg2sub/Notes.txt new file mode 100644 index 000000000..a0e56e3c1 --- /dev/null +++ b/gst/mpeg2sub/Notes.txt @@ -0,0 +1,324 @@ +
+ DVD subtitles
+ ---------------
+
+
+ 0. Introduction
+ 1. Basics
+ 2. The data structure
+ 3. Reading the control header
+ 4. Decoding the graphics
+ 5. What I do not know yet / What I need
+ 6. Thanks
+ 7. Changes
+
+
+
+
+
+The latest version of this document can be found here:
+http://www.via.ecp.fr/~sam/doc/dvd/
+
+
+
+
+
+0. Introduction
+
+ One of the last things we missed in DVD decoding under my system was the
+decoding of subtitles. I found no information on the web or Usenet about them,
+apart from a few words on them being run-length encoded in the DVD FAQ.
+
+ So we decided to reverse-engineer their format (it's completely legal in
+France, since we did it on interoperability purposes), and managed to get
+almost all of it.
+
+
+
+
+
+1. Basics
+
+ DVD subtitles are hidden in private PS packets (0x000001ba), just like AC3
+streams are.
+
+ Within the PS packet, there are PES packets, and like AC3, the header for the
+ones containing subtitles have a 0x000001bd header.
+ As for AC3, where there's an ID like (0x80 + x), there's a subtitle ID equal
+to (0x20 + x), where x is the subtitle ID. Thus there seems to be only
+16 possible different subtitles on a DVD (my Taxi Driver copy has 16).
+
+ I'll suppose you know how to extract AC3 from a DVD, and jump to the
+interesting part of this documentation. Anyway you're unlikely to have
+understood what I said without already being familiar with MPEG2.
+
+
+
+
+
+2. The data structure
+
+A subtitle packet, after its parts have been collected and appended, looks
+like this :
+
+ +----------------------------------------------------------+
+ | |
+ | 0 2 size |
+ | +----+------------------------+-----------------+ |
+ | |size| data packet | control | |
+ | +----+------------------------+-----------------+ |
+ | |
+ | a subtitle packet |
+ | |
+ +----------------------------------------------------------+
+
+size is a 2 bytes word, and data packet and control may have any size.
+
+
+Here is the structure of the data packet :
+
+ +----------------------------------------------------------+
+ | |
+ | 2 4 S0+2 |
+ | +----+------------------------------------------+ |
+ | | S0 | data | |
+ | +----+------------------------------------------+ |
+ | |
+ | the data packet |
+ | |
+ +----------------------------------------------------------+
+
+S0, the data packet size, is a 2 bytes word.
+
+
+Finally, here's the structure of the control packet :
+
+ +----------------------------------------------------------+
+ | |
+ | S0+2 S0+4 S1 size |
+ | +----+---------+---------+--+---------+--+---------+ |
+ | | S1 |ctrl seq |ctrl seq |..|ctrl seq |ff| end seq | |
+ | +----+---------+---------+--+---------+--+---------+ |
+ | |
+ | the control packet |
+ | |
+ +----------------------------------------------------------+
+
+To summarize :
+
+ - S1, at offset S0+2, the position of the end sequence
+ - several control sequences
+ - the 'ff' byte
+ - the end sequence
+
+
+
+
+
+3. Reading the control header
+
+The first thing to read is the control sequences. There are several
+types of them, and each type is determined by its first byte. As far
+as I know, each type has a fixed length.
+
+ * type 0x01 : '01' - 1 byte
+ it seems to be an empty control sequence.
+
+ * type 0x03 : '03wxyz' - 3 bytes
+ this one has the palette information ; it basically says 'encoded color 0
+ is the wth color of the palette, encoded color 1 is the xth color, aso.
+
+ * type 0x04 : '04wxyz' - 3 bytes
+ I *think* this is the alpha channel information ; I only saw values of 0 or f
+ for those nibbles, so I can't really be sure, but it seems plausable.
+
+ * type 0x05 : '05xxxXXXyyyYYY' - 7 bytes
+ the coordinates of the subtitle on the screen :
+ xxx is the first column of the subtitle
+ XXX is the last column of the subtitle
+ yyy is the first line of the subtitle
+ YYY is the last line of the subtitle
+ thus the subtitle's size is (XXX-xxx+1) x (YYY-yyy+1)
+
+ * type 0x06 : '06xxxxyyyy' - 5 bytes
+ xxxx is the position of the first graphic line, and yyyy is the position of
+ the second one (the graphics are interlaced, so it helps a lot :p)
+
+The end sequence has this structure:
+
+ xxxx yyyy 02 ff (ff)
+
+ it ends with 'ff' or 'ffff', to make the whole packet have an even length.
+
+FIXME: I absolutely don't know what xxxx is. I suppose it may be some date
+information since I found it nowhere else, but I can't be sure.
+
+ yyyy is equal to S1 (see picture).
+
+
+Example of a control header :
+----
+0A 0C 01 03 02 31 04 0F F0 05 00 02 CF 00 22 3E 06 00 06 04 E9 FF 00 93 0A 0C 02 FF
+----
+Let's decode it. First of all, S1 = 0x0a0c.
+
+The control sequences are :
+ 01
+ Nothing to say about this one
+ 03 02 31
+ Color 0 is 0, color 1 is 2, color 2 is 3, and color 3 is 1.
+ 04 0F F0
+ Colors 0 and 3 are transparent, and colors 2 and 3 are opaque (not sure of this one)
+ 05 00 02 CF 00 22 3E
+ The first column is 0x000, the last one is 0x2cf, the first line is 0x002, and
+ the last line is 0x23e. Thus the subtitle's size is 0x2d0 x 0x23d.
+ 06 00 06 04 E9
+ The first encoded image starts at offset 0x006, and the second one starts at 0x04e9.
+
+And the end sequence is :
+ 00 93 0A 0C 02 FF
+ Which means... well, not many things now. We can at least verify that S1 (0x0a0c) is
+ there.
+
+
+
+
+
+4. Decoding the graphics
+
+ The graphics are rather easy to decode (at least, when you know how to do it - it
+ took us one whole week to figure out what the encoding was :p).
+
+ The picture is interlaced, for instance for a 40 lines picture :
+
+ line 0 ---------------#----------
+ line 2 ------#-------------------
+ ...
+ line 38 ------------#-------------
+ line 1 ------------------#-------
+ line 3 --------#-----------------
+ ...
+ line 39 -------------#------------
+
+ When decoding you should get:
+
+ line 0 ---------------#----------
+ line 1 ------------------#-------
+ line 2 ------#-------------------
+ line 3 --------#-----------------
+ ...
+ line 38 ------------#-------------
+ line 39 -------------#------------
+
+ Computers with weak processors could choose only to decode even lines
+ in order to gain some time, for instance.
+
+
+ The encoding is run-length encoded, with the following alphabet:
+
+ 0xf
+ 0xe
+ 0xd
+ 0xc
+ 0xb
+ 0xa
+ 0x9
+ 0x8
+ 0x7
+ 0x6
+ 0x5
+ 0x4
+ 0x3-
+ 0x2-
+ 0x1-
+ 0x0f-
+ 0x0e-
+ 0x0d-
+ 0x0c-
+ 0x0b-
+ 0x0a-
+ 0x09-
+ 0x08-
+ 0x07-
+ 0x06-
+ 0x05-
+ 0x04-
+ 0x03--
+ 0x02--
+ 0x01--
+ 0x0000
+
+ '-' stands for any other nibble. Once a sequence X of this alphabet has
+ been read, the pixels can be displayed : (X >> 2) is the number of pixels
+ to display, and (X & 0x3) is the color of the pixel.
+
+ For instance, 0x23 means "8 pixels of color 3".
+
+ "0000" has a special meaning : it's a carriage return. The decoder should
+ do a carriage return when reaching the end of the line, or when encountering
+ this "0000" sequence. When doing a carriage return, the parser should be
+ reset to the next even position (it cannot be nibble-aligned at the start
+ of a line).
+
+ After a carriage return, the parser should read a line on the other
+ interlaced picture, and swap like this after each carriage return.
+
+ Perhaps I don't explain this very well, so you'd better have a look at
+ the enclosed source.
+
+
+
+
+
+5. What I do not know yet / What I need
+
+I don't know what's in the end sequence yet.
+
+Also, I don't know exactly when to display subtitles, and when to remove them.
+
+I don't know if there are other types of control sequences (in my programs I consider
+0xff as a control sequence type, as well as 0x02. I don't know if it's correct or not,
+so please comment on this).
+
+I don't know what the "official" color palette is.
+
+I don't know how to handle transparency information.
+
+I don't know if this document is generic enough.
+
+So what I need is you :
+
+ - if you can, patch this document or my programs to fix strange behaviour with your subtitles.
+
+ - send me your subtitles (there's a program to extract them enclosed) ; the first 10 KB
+ of subtitles in a VOB should be enough, but it would be cool if you sent me one subtitle
+ file per language.
+
+
+
+
+
+6. Thanks
+
+ Thanks to Michel Lespinasse <walken@via.ecp.fr> for his great help on understanding
+the RLE stuff, and for all the ideas he had.
+
+ Thanks to mass (David Waite) and taaz (David I. Lehn) from irc at
+openprojects.net for sending me their subtitles.
+
+
+
+
+
+7. Changes
+
+ 20000116: added the 'changes' section.
+ 20000116: added David Waite's and David I. Lehn's name.
+ 20000116: changed "x0" and "x1" to "S0" and "S1" to make it less confusing.
+
+
+
+
+--
+Paris, January 16th 2000
+Samuel Hocevar <sam@via.ecp.fr>
diff --git a/gst/mpeg2sub/gstmpeg2subt.c b/gst/mpeg2sub/gstmpeg2subt.c new file mode 100644 index 000000000..ee51d0678 --- /dev/null +++ b/gst/mpeg2sub/gstmpeg2subt.c @@ -0,0 +1,443 @@ +/* Gnome-Streamer + * 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. + */ + + +/*#define DEBUG_ENABLED */ +#include <mpeg2subt.h> + +static void gst_mpeg2subt_class_init (GstMpeg2SubtClass *klass); +static void gst_mpeg2subt_init (GstMpeg2Subt *mpeg2subt); + +static void gst_mpeg2subt_chain_video (GstPad *pad,GstBuffer *buf); +static void gst_mpeg2subt_chain_subtitle (GstPad *pad,GstBuffer *buf); + +static void gst_mpeg2subt_merge_title (GstMpeg2Subt *mpeg2subt, GstBuffer *buf); + +static void gst_mpeg2subt_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_mpeg2subt_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +/* elementfactory information */ +static GstElementDetails mpeg2subt_details = { + "MPEG2 subtitle Decoder", + "Filter/Decoder/Video", + "Decodes and merges MPEG2 subtitles into a video frame", + VERSION, + "Samuel Hocevar <sam@via.ecp.fr>\n" + "Michel Lespinasse <walken@via.ecp.fr>\n" + "Wim Taymans <wim.taymans@chello.be>", + "(C) 2000", +}; + +static GstTypeDefinition mpeg2subtitledefinition = { + "mpeg2subt_video/mpeg2ubtitle", + "video/mpeg2subtitle", + NULL, + NULL, +}; + +/* GstMpeg2Subt signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_SKIP, + /* FILL ME */ +}; + +static guchar yuv_color[16] = { + 0x99, + 0x00, + 0xFF, + 0x00, + 0x40, + 0x50, + 0x60, + 0x70, + 0x80, + 0x90, + 0xA0, + 0xB0, + 0xC0, + 0xD0, + 0xE0, + 0xF0 +}; + + + + +static GstElementClass *parent_class = NULL; +/*static guint gst_mpeg2subt_signals[LAST_SIGNAL] = { 0 };*/ + +GType +gst_mpeg2subt_get_type (void) +{ + static GType mpeg2subt_type = 0; + + if (!mpeg2subt_type) { + static const GTypeInfo mpeg2subt_info = { + sizeof(GstMpeg2SubtClass), NULL, + NULL, + (GClassInitFunc)gst_mpeg2subt_class_init, + NULL, + NULL, + sizeof(GstMpeg2Subt), + 0, + (GInstanceInitFunc)gst_mpeg2subt_init, + }; + mpeg2subt_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMpeg2Subt", &mpeg2subt_info, 0); + } + return mpeg2subt_type; +} + +static void +gst_mpeg2subt_class_init (GstMpeg2SubtClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SKIP, + g_param_spec_int("skip","skip","skip", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); /* CHECKME */ + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_mpeg2subt_set_property; + gobject_class->get_property = gst_mpeg2subt_get_property; + +} + +static void +gst_mpeg2subt_init (GstMpeg2Subt *mpeg2subt) +{ + mpeg2subt->videopad = gst_pad_new("video",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(mpeg2subt),mpeg2subt->videopad); + gst_pad_set_chain_function(mpeg2subt->videopad,gst_mpeg2subt_chain_video); + + mpeg2subt->subtitlepad = gst_pad_new("subtitle",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(mpeg2subt),mpeg2subt->subtitlepad); + gst_pad_set_chain_function(mpeg2subt->subtitlepad,gst_mpeg2subt_chain_subtitle); + + mpeg2subt->srcpad = gst_pad_new("src",GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(mpeg2subt),mpeg2subt->srcpad); + + mpeg2subt->partialbuf = NULL; + mpeg2subt->have_title = FALSE; +} + +static void +gst_mpeg2subt_chain_video (GstPad *pad, GstBuffer *buf) +{ + GstMpeg2Subt *mpeg2subt; + guchar *data; + glong size; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + mpeg2subt = GST_MPEG2SUBT (GST_OBJECT_PARENT (pad)); + + data = GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf); + + if (mpeg2subt->have_title && mpeg2subt->duration != 0) { + gst_mpeg2subt_merge_title(mpeg2subt, buf); + mpeg2subt->duration--; + } + + gst_pad_push(mpeg2subt->srcpad, buf); +} + + +static void +gst_mpeg2subt_parse_header (GstMpeg2Subt *mpeg2subt) +{ + guchar *buffer = GST_BUFFER_DATA(mpeg2subt->partialbuf); + guchar dummy; + guint i; + + i = mpeg2subt->data_size + 4; + while (i < mpeg2subt->packet_size) + { + dummy = buffer [i]; + switch (dummy) + { + case 0x01: /* null packet ? */ + i++; + break; + case 0x02: /* 02 ff (ff) is the end of the packet */ + i = mpeg2subt->packet_size; + break; + case 0x03: /* palette */ + mpeg2subt->color[0] = yuv_color[buffer [i+1] >> 4]; + mpeg2subt->color[1] = yuv_color[buffer [i+1] & 0xf]; + mpeg2subt->color[2] = yuv_color[buffer [i+2] >> 4]; + mpeg2subt->color[3] = yuv_color[buffer [i+2] & 0xf]; + mpeg2subt->color[4] = yuv_color[0xf]; + GST_DEBUG (0,"mpeg2subt: colors %d %d %d %d\n", mpeg2subt->color[0],mpeg2subt->color[1],mpeg2subt->color[2],mpeg2subt->color[3]); + i += 3; + break; + case 0x04: /* transparency palette */ + mpeg2subt->trans[3] = buffer [i+1] >> 4; + mpeg2subt->trans[2] = buffer [i+1] & 0xf; + mpeg2subt->trans[1] = buffer [i+2] >> 4; + mpeg2subt->trans[0] = buffer [i+2] & 0xf; + GST_DEBUG (0,"mpeg2subt: transparency %d %d %d %d\n", mpeg2subt->trans[0],mpeg2subt->trans[1],mpeg2subt->trans[2],mpeg2subt->trans[3]); + i += 3; + break; + case 0x05: /* image coordinates */ + mpeg2subt->width = 1 + ( ((buffer[i+2] & 0x0f) << 8) + buffer[i+3] ) + - ( (((unsigned int)buffer[i+1]) << 4) + (buffer[i+2] >> 4) ); + mpeg2subt->height = 1 + ( ((buffer[i+5] & 0x0f) << 8) + buffer[i+6] ) + - ( (((unsigned int)buffer[i+4]) << 4) + (buffer[i+5] >> 4) ); + i += 7; + break; + case 0x06: /* image 1 / image 2 offsets */ + mpeg2subt->offset[0] = (((unsigned int)buffer[i+1]) << 8) + buffer[i+2]; + mpeg2subt->offset[1] = (((unsigned int)buffer[i+3]) << 8) + buffer[i+4]; + i += 5; + break; + case 0xff: /* "ff xx yy zz uu" with 'zz uu' == start of control packet + * xx and yy are the end time in 90th/sec + */ + mpeg2subt->duration = (((buffer[i+1] << 8) + buffer[i+2]) * 25)/90; + + GST_DEBUG (0,"duration %d\n", mpeg2subt->duration); + + if ( (buffer[i+3] != buffer[mpeg2subt->data_size+2]) + || (buffer[i+4] != buffer[mpeg2subt->data_size+3]) ) + { + g_print("mpeg2subt: invalid control header (%.2x%.2x != %.2x%.2x) !\n", + buffer[i+3], buffer[i+4], buffer[mpeg2subt->data_size+2], buffer[mpeg2subt->data_size+3] ); +/* FIXME */ +/* exit(1); */ + } + i += 5; + break; + default: + g_print("mpeg2subt: invalid sequence in control header (%.2x) !\n", dummy); + break; + } + } +} + +static int +get_nibble (guchar *buffer, gint *offset, gint id, gint *aligned) +{ + static int next; + + if (*aligned) + { + next = buffer[offset[id]]; + offset[id]++; + + *aligned = 0; + return next >> 4; + } + else + { + *aligned = 1; + return next & 0xf; + } +} + +static void +gst_mpeg2subt_merge_title (GstMpeg2Subt *mpeg2subt, GstBuffer *buf) +{ + gint x=0, y=0; + gint width = mpeg2subt->width; + gint height = mpeg2subt->height; + guchar *buffer = GST_BUFFER_DATA(mpeg2subt->partialbuf); + guchar *target = GST_BUFFER_DATA(buf); + gint id=0, aligned=1; + gint offset[2]; + + offset[0] = mpeg2subt->offset[0]; + offset[1] = mpeg2subt->offset[1]; +#define get_nibble() get_nibble (buffer, offset, id, &aligned) + + GST_DEBUG (0,"mpeg2subt: merging subtitle\n"); + + while ((offset[1] < mpeg2subt->data_size + 2) && (y < height)) + { + gint code; + gint length, colorid; + + code = get_nibble(); + if (code >= 0x4) /* 4 .. f */ + { +found_code: + length = code >> 2; + colorid = code & 3; + while (length--) + if (x++ < width) { + if (mpeg2subt->trans[colorid] != 0x0) { + *target++ = mpeg2subt->color[colorid]; + } + else target++; + } + + if (x >= width) + { + if (!aligned) + get_nibble (); + goto next_line; + } + continue; + } + + code = (code << 4) + get_nibble(); + if (code >= 0x10) /* 1x .. 3x */ + goto found_code; + + code = (code << 4) + get_nibble(); + if (code >= 0x40) /* 04x .. 0fx */ + goto found_code; + + code = (code << 4) + get_nibble(); + if (code >= 0x100) /* 01xx .. 03xx */ + goto found_code; + + /* 00xx - should only happen for 00 00 */ + if (!aligned) + code = (code << 4) + get_nibble(); /* 0 0x xx */ + + if (code) + { + g_print("mpeg2subt: got unknown code 00%x (offset %x side %x, x=%d, y=%d)\n", code, mpeg2subt->offset[id], id, x, y); + goto next_line; + } +next_line: + /* aligned 00 00 */ + if (y < height) { + target+=(width-x); + x = 0; + y++; + id = 1 - id; + } + } +} + +static void +gst_mpeg2subt_chain_subtitle (GstPad *pad, GstBuffer *buf) +{ + GstMpeg2Subt *mpeg2subt; + guchar *data; + glong size = 0; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); +/* g_return_if_fail(GST_IS_BUFFER(buf)); */ + + mpeg2subt = GST_MPEG2SUBT (GST_OBJECT_PARENT (pad)); + + if (mpeg2subt->have_title) { + gst_buffer_unref(mpeg2subt->partialbuf); + mpeg2subt->partialbuf = NULL; + mpeg2subt->have_title = FALSE; + } + + GST_DEBUG (0,"presentation time %llu\n", GST_BUFFER_TIMESTAMP(buf)); + + /* deal with partial frame from previous buffer */ + if (mpeg2subt->partialbuf) { + + mpeg2subt->partialbuf = gst_buffer_append(mpeg2subt->partialbuf, buf);; + /* and the one we received.. */ + gst_buffer_unref(buf); + } + else { + mpeg2subt->partialbuf = buf; + } + + data = GST_BUFFER_DATA(mpeg2subt->partialbuf); + size = GST_BUFFER_SIZE(mpeg2subt->partialbuf); + + mpeg2subt->packet_size = GUINT16_FROM_BE(*(guint16 *)data); + + if (mpeg2subt->packet_size == size) { + + GST_DEBUG (0,"mpeg2subt: subtitle packet size %d, current size %ld\n", mpeg2subt->packet_size, size); + + mpeg2subt->data_size = GUINT16_FROM_BE(*(guint16 *)(data+2)); + + gst_mpeg2subt_parse_header(mpeg2subt); + mpeg2subt->have_title = TRUE; + } +} + +static void +gst_mpeg2subt_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstMpeg2Subt *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_MPEG2SUBT(object)); + src = GST_MPEG2SUBT(object); + + switch (prop_id) { + default: + break; + } +} + +static void +gst_mpeg2subt_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMpeg2Subt *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_MPEG2SUBT(object)); + src = GST_MPEG2SUBT(object); + + switch (prop_id) { + default: + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the mpeg2subt element */ + factory = gst_elementfactory_new("mpeg2subt",GST_TYPE_MPEG2SUBT, + &mpeg2subt_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "mpeg2subt", + plugin_init +}; diff --git a/gst/mpeg2sub/gstmpeg2subt.h b/gst/mpeg2sub/gstmpeg2subt.h new file mode 100644 index 000000000..71e88909b --- /dev/null +++ b/gst/mpeg2sub/gstmpeg2subt.h @@ -0,0 +1,82 @@ +/* Gnome-Streamer + * 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_MPEG2SUBT_H__ +#define __GST_MPEG2SUBT_H__ + + +#include <config.h> +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_MPEG2SUBT \ + (gst_mpeg2subt_get_type()) +#define GST_MPEG2SUBT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPEG2SUBT,GstMpeg2Subt)) +#define GST_MPEG2SUBT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPEG2SUBT,GstMpeg2Subt)) +#define GST_IS_MPEG2SUBT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPEG2SUBT)) +#define GST_IS_MPEG2SUBT_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPEG2SUBT)) + +typedef struct _GstMpeg2Subt GstMpeg2Subt; +typedef struct _GstMpeg2SubtClass GstMpeg2SubtClass; + +struct _GstMpeg2Subt { + GstElement element; + + GstPad *videopad,*subtitlepad,*srcpad; + + GstBuffer *partialbuf; /* previous buffer (if carryover) */ + + gboolean have_title; + + guint16 packet_size; + guint16 data_size; + + gint offset[2]; + guchar color[5]; + guchar trans[4]; + + guint duration; + + gint width, height; + +}; + +struct _GstMpeg2SubtClass { + GstElementClass parent_class; +}; + +GType gst_mpeg2subt_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_MPEG2SUBT_H__ */ diff --git a/gst/mpegaudioparse/Makefile.am b/gst/mpegaudioparse/Makefile.am new file mode 100644 index 000000000..9d670bd31 --- /dev/null +++ b/gst/mpegaudioparse/Makefile.am @@ -0,0 +1,16 @@ +#FIXME clean me up a bit + +filterdir = $(libdir)/gst +filter_LTLIBRARIES = libgstmpegaudioparse.la libgstmp3types.la + +libgstmpegaudioparse_la_SOURCES = gstmpegaudioparse.c gstmp3types.c +# FIXME is this useful? +libgstmpegaudioparse_la_CFLAGS = -O3 $(FOMIT_FRAME_POINTER) -ffast-math -finline-functions $(GST_CFLAGS) + +libgstmp3types_la_SOURCES = gstmp3types.c +libgstmp3types_la_CFLAGS = -O3 $(FOMIT_FRAME_POINTER) -ffast-math -finline-functions $(GST_CFLAGS) + +noinst_HEADERS = gstmpegaudioparse.h +EXTRA_DIST = README + +# FIXME is this needed? diff --git a/gst/mpegaudioparse/README b/gst/mpegaudioparse/README new file mode 100644 index 000000000..8a803d3e9 --- /dev/null +++ b/gst/mpegaudioparse/README @@ -0,0 +1,12 @@ +MP3 Audio Parser +================ + +This element acts as a parser for mpeg audio data. It's called 'mp3' but +in reality will work for any MPEG-1, MPEG-2, or MPEG-2.5 elemental audio +stream of any of Layers I, II, and III. It will not (currently, ever?) +handle MPEG-2 BC or NBC streams, as those have rather specialized needs +best served be a different filter. + +It will take an mpeg audio stream in any form on its 'src' input, with any +buffer size, and split it into buffers containing a single frame each. +NOTE: ancillary data is not dealt with right now. diff --git a/gst/mpegaudioparse/gstmp3types.c b/gst/mpegaudioparse/gstmp3types.c new file mode 100644 index 000000000..efb9fd5bc --- /dev/null +++ b/gst/mpegaudioparse/gstmp3types.c @@ -0,0 +1,77 @@ +/* Gnome-Streamer + * 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. + */ + +//#define DEBUG_ENABLED +#include <gst/gst.h> + +static GstCaps* mp3_typefind(GstBuffer *buf, gpointer private); + +static GstTypeDefinition mp3type_definitions[] = { + { "mp3types_audio/mp3", "audio/mp3", ".mp3 .mp2 .mp1 .mpga", mp3_typefind }, + { NULL, NULL, NULL, NULL }, +}; + +static GstCaps* +mp3_typefind(GstBuffer *buf, gpointer private) +{ + gulong head = GULONG_FROM_BE(*((gulong *)GST_BUFFER_DATA(buf))); + GstCaps *caps; + + GST_DEBUG (0,"mp3typefind: typefind\n"); + if ((head & 0xffe00000) != 0xffe00000) + return NULL; + if (!((head >> 17) & 3)) + return NULL; + if (((head >> 12) & 0xf) == 0xf) + return NULL; + if (!((head >> 12) & 0xf)) + return NULL; + if (((head >> 10) & 0x3) == 0x3) + return NULL; + + caps = gst_caps_new ("mp3_typefind", "audio/mp3", NULL); +// gst_caps_set(caps,"layer",GST_PROPS_INT(4-((head>>17)&0x3))); + + return caps; +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + gint i=0; + + while (mp3type_definitions[i].name) { + GstTypeFactory *type; + + type = gst_typefactory_new (&mp3type_definitions[i]); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type)); + i++; + } + +// gst_info("gsttypes: loaded %d mp3 types\n",i); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "mp3types", + plugin_init +}; diff --git a/gst/mpegaudioparse/gstmpegaudioparse.c b/gst/mpegaudioparse/gstmpegaudioparse.c new file mode 100644 index 000000000..b1431c731 --- /dev/null +++ b/gst/mpegaudioparse/gstmpegaudioparse.c @@ -0,0 +1,506 @@ +/* Gnome-Streamer + * 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. + */ + +//#define GST_DEBUG_ENABLED +#include <gstmpegaudioparse.h> + + +/* elementfactory information */ +static GstElementDetails mp3parse_details = { + "MP3 Parser", + "Filter/Parser/Audio", + "Parses and frames MP3 audio streams, provides seek", + VERSION, + "Erik Walthinsen <omega@cse.ogi.edu>", + "(C) 1999", +}; + +static GstPadTemplate* +mp3_src_factory (void) +{ + return + gst_padtemplate_new ( + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + gst_caps_new ( + "mp3parse_src", + "audio/mp3", + gst_props_new ( + "layer", GST_PROPS_INT_RANGE (1, 3), + "bitrate", GST_PROPS_INT_RANGE (8, 320), + "framed", GST_PROPS_BOOLEAN (TRUE), + NULL)), + NULL); +} + +static GstPadTemplate* +mp3_sink_factory (void) +{ + return + gst_padtemplate_new ( + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + gst_caps_new ( + "mp3parse_sink", + "audio/mp3", + NULL), + NULL); +}; + +/* GstMPEGAudioParse signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_SKIP, + ARG_BIT_RATE, + /* FILL ME */ +}; + +static GstPadTemplate *sink_temp, *src_temp; + +static void gst_mp3parse_class_init (GstMPEGAudioParseClass *klass); +static void gst_mp3parse_init (GstMPEGAudioParse *mp3parse); + +static void gst_mp3parse_loop (GstElement *element); +static void gst_mp3parse_chain (GstPad *pad,GstBuffer *buf); +static long bpf_from_header (GstMPEGAudioParse *parse, unsigned long header); +static int head_check (unsigned long head); + +static void gst_mp3parse_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_mp3parse_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +//static guint gst_mp3parse_signals[LAST_SIGNAL] = { 0 }; + +GType +mp3parse_get_type(void) { + static GType mp3parse_type = 0; + + if (!mp3parse_type) { + static const GTypeInfo mp3parse_info = { + sizeof(GstMPEGAudioParseClass), NULL, + NULL, + (GClassInitFunc)gst_mp3parse_class_init, + NULL, + NULL, + sizeof(GstMPEGAudioParse), + 0, + (GInstanceInitFunc)gst_mp3parse_init, + }; + mp3parse_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMPEGAudioParse", &mp3parse_info, 0); + } + return mp3parse_type; +} + +static void +gst_mp3parse_class_init (GstMPEGAudioParseClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SKIP, + g_param_spec_int("skip","skip","skip", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BIT_RATE, + g_param_spec_int("bit_rate","bit_rate","bit_rate", + G_MININT,G_MAXINT,0,G_PARAM_READABLE)); // CHECKME + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gobject_class->set_property = gst_mp3parse_set_property; + gobject_class->get_property = gst_mp3parse_get_property; +} + +static void +gst_mp3parse_init (GstMPEGAudioParse *mp3parse) +{ + mp3parse->sinkpad = gst_pad_new_from_template(sink_temp, "sink"); + gst_pad_set_caps(mp3parse->sinkpad, gst_pad_get_padtemplate_caps (mp3parse->sinkpad)); + gst_element_add_pad(GST_ELEMENT(mp3parse),mp3parse->sinkpad); +// gst_pad_set_type_id(mp3parse->sinkpad, mp3type); + +#if 1 // set this to one to use the old chaining code + gst_pad_set_chain_function(mp3parse->sinkpad,gst_mp3parse_chain); +#else // else you get the new loop-based code, which isn't complete yet + gst_element_set_loop_function (GST_ELEMENT(mp3parse),gst_mp3parse_loop); +#endif + + mp3parse->srcpad = gst_pad_new_from_template(src_temp, "src"); + gst_element_add_pad(GST_ELEMENT(mp3parse),mp3parse->srcpad); + //gst_pad_set_type_id(mp3parse->srcpad, mp3frametype); + + mp3parse->partialbuf = NULL; + mp3parse->skip = 0; + mp3parse->in_flush = FALSE; +} + +static guint32 +gst_mp3parse_next_header (guchar *buf,guint32 len,guint32 start) +{ + guint32 offset = start; + int f = 0; + + while (offset < (len - 4)) { + fprintf(stderr,"%02x ",buf[offset]); + if (buf[offset] == 0xff) + f = 1; + else if (f && ((buf[offset] >> 4) == 0x0f)) + return offset - 1; + else + f = 0; + offset++; + } + return -1; +} + +static void +gst_mp3parse_loop (GstElement *element) +{ + GstMPEGAudioParse *parse = GST_MP3PARSE(element); + GstBuffer *inbuf, *outbuf; + guint32 size, offset; + guchar *data; + guint32 start; + guint32 header; + gint bpf; + + while (1) { + // get a new buffer + inbuf = gst_pad_pull (parse->sinkpad); + size = GST_BUFFER_SIZE (inbuf); + data = GST_BUFFER_DATA (inbuf); + offset = 0; +fprintf(stderr, "have buffer of %d bytes\n",size); + + // loop through it and find all the frames + while (offset < (size - 4)) { + start = gst_mp3parse_next_header (data,size,offset); +fprintf(stderr, "skipped %d bytes searching for the next header\n",start-offset); + header = GULONG_FROM_BE(*((guint32 *)(data+start))); +fprintf(stderr, "header is 0x%08x\n",header); + + // figure out how big the frame is supposed to be + bpf = bpf_from_header (parse, header); + + // see if there are enough bytes in this buffer for the whole frame + if ((start + bpf) <= size) { + outbuf = gst_buffer_create_sub (inbuf,start,bpf); +fprintf(stderr, "sending buffer of %d bytes\n",bpf); + gst_pad_push (parse->srcpad, outbuf); + offset = start + bpf; + + // if not, we have to deal with it somehow + } else { +fprintf(stderr,"don't have enough data for this frame\n"); + + break; + } + } + } +} + +static void +gst_mp3parse_chain (GstPad *pad, GstBuffer *buf) +{ + GstMPEGAudioParse *mp3parse; + guchar *data; + glong size,offset = 0; + unsigned long header; + int bpf; + GstBuffer *outbuf; + guint64 last_ts; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); +// g_return_if_fail(GST_IS_BUFFER(buf)); + + mp3parse = GST_MP3PARSE (gst_pad_get_parent (pad)); + + GST_DEBUG (0,"mp3parse: received buffer of %d bytes\n",GST_BUFFER_SIZE(buf)); + + last_ts = GST_BUFFER_TIMESTAMP(buf); + + if (GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLUSH)) { + if (mp3parse->partialbuf) { + gst_buffer_unref(mp3parse->partialbuf); + mp3parse->partialbuf = NULL; + } + mp3parse->in_flush = TRUE; + } + + // if we have something left from the previous frame + if (mp3parse->partialbuf) { + + mp3parse->partialbuf = gst_buffer_append(mp3parse->partialbuf, buf); + // and the one we received.. + gst_buffer_unref(buf); + } + else { + mp3parse->partialbuf = buf; + } + + size = GST_BUFFER_SIZE(mp3parse->partialbuf); + data = GST_BUFFER_DATA(mp3parse->partialbuf); + + // while we still have bytes left -4 for the header + while (offset < size-4) { + int skipped = 0; + + GST_DEBUG (0,"mp3parse: offset %ld, size %ld \n",offset, size); + + // search for a possible start byte + for (;((data[offset] != 0xff) && (offset < size));offset++) skipped++; + if (skipped && !mp3parse->in_flush) { + GST_DEBUG (0,"mp3parse: **** now at %ld skipped %d bytes\n",offset,skipped); + } + // construct the header word + header = GULONG_FROM_BE(*((gulong *)(data+offset))); + // if it's a valid header, go ahead and send off the frame + if (head_check(header)) { + // calculate the bpf of the frame + bpf = bpf_from_header(mp3parse, header); + + /******************************************************************************** + * robust seek support + * - This performs additional frame validation if the in_flush flag is set + * (indicating a discontinuous stream). + * - The current frame header is not accepted as valid unless the NEXT frame + * header has the same values for most fields. This significantly increases + * the probability that we aren't processing random data. + * - It is not clear if this is sufficient for robust seeking of Layer III + * streams which utilize the concept of a "bit reservoir" by borrow bitrate + * from previous frames. In this case, seeking may be more complicated because + * the frames are not independently coded. + ********************************************************************************/ + if ( mp3parse->in_flush ) { + unsigned long header2; + + if ((size-offset)<(bpf+4)) { if (mp3parse->in_flush) break; } // wait until we have the the entire current frame as well as the next frame header + + header2 = GULONG_FROM_BE(*((gulong *)(data+offset+bpf))); + GST_DEBUG(0,"mp3parse: header=%08lX, header2=%08lX, bpf=%d\n", header, header2, bpf ); + + #define HDRMASK ~( (0xF<<12)/*bitrate*/ | (1<<9)/*padding*/ | (3<<4)/*mode extension*/ ) // mask the bits which are allowed to differ between frames + + if ( (header2&HDRMASK) != (header&HDRMASK) ) { // require 2 matching headers in a row + GST_DEBUG(0,"mp3parse: next header doesn't match (header=%08lX, header2=%08lX, bpf=%d)\n", header, header2, bpf ); + offset++; // This frame is invalid. Start looking for a valid frame at the next position in the stream + continue; + } + + } + + // if we don't have the whole frame... + if ((size - offset) < bpf) { + GST_DEBUG (0,"mp3parse: partial buffer needed %ld < %d \n",(size-offset), bpf); + break; + } else { + + outbuf = gst_buffer_create_sub(mp3parse->partialbuf,offset,bpf); + + offset += bpf; + if (mp3parse->skip == 0) { + GST_DEBUG (0,"mp3parse: pushing buffer of %d bytes\n",GST_BUFFER_SIZE(outbuf)); + if (mp3parse->in_flush) { + GST_BUFFER_FLAG_SET(outbuf, GST_BUFFER_FLUSH); + mp3parse->in_flush = FALSE; + } + else { + GST_BUFFER_FLAG_UNSET(outbuf, GST_BUFFER_FLUSH); + } + GST_BUFFER_TIMESTAMP(outbuf) = last_ts; + gst_pad_push(mp3parse->srcpad,outbuf); + } + else { + GST_DEBUG (0,"mp3parse: skipping buffer of %d bytes\n",GST_BUFFER_SIZE(outbuf)); + gst_buffer_unref(outbuf); + mp3parse->skip--; + } + } + } else { + offset++; + if (!mp3parse->in_flush) GST_DEBUG (0,"mp3parse: *** wrong header, skipping byte (FIXME?)\n"); + } + } + // if we have processed this block and there are still + // bytes left not in a partial block, copy them over. + if (size-offset > 0) { + glong remainder = (size - offset); + GST_DEBUG (0,"mp3parse: partial buffer needed %ld for trailing bytes\n",remainder); + + outbuf = gst_buffer_create_sub(mp3parse->partialbuf,offset,remainder); + gst_buffer_unref(mp3parse->partialbuf); + mp3parse->partialbuf = outbuf; + } + else { + gst_buffer_unref(mp3parse->partialbuf); + mp3parse->partialbuf = NULL; + } +} + +static int mp3parse_tabsel[2][3][16] = +{ { {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, }, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, }, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, } }, + { {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, }, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, } }, +}; + +static long mp3parse_freqs[9] = +{44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000}; + + +static long +bpf_from_header (GstMPEGAudioParse *parse, unsigned long header) +{ + int layer_index,layer,lsf,samplerate_index,padding; + long bpf; + + //mpegver = (header >> 19) & 0x3; // don't need this for bpf + layer_index = (header >> 17) & 0x3; + layer = 4 - layer_index; + lsf = (header & (1 << 20)) ? ((header & (1 << 19)) ? 0 : 1) : 1; + parse->bit_rate = mp3parse_tabsel[lsf][layer - 1][((header >> 12) & 0xf)]; + samplerate_index = (header >> 10) & 0x3; + padding = (header >> 9) & 0x1; + + if (layer == 1) { + bpf = parse->bit_rate * 12000; + bpf /= mp3parse_freqs[samplerate_index]; + bpf = ((bpf + padding) << 2); + } else { + bpf = parse->bit_rate * 144000; + bpf /= mp3parse_freqs[samplerate_index]; + bpf += padding; + } + + //g_print("%08x: layer %d lsf %d bitrate %d samplerate_index %d padding %d - bpf %d\n", +//header,layer,lsf,bitrate,samplerate_index,padding,bpf); + + return bpf; +} + +static gboolean +head_check (unsigned long head) +{ + GST_DEBUG (0,"checking mp3 header 0x%08lx\n",head); + /* if it's not a valid sync */ + if ((head & 0xffe00000) != 0xffe00000) { + GST_DEBUG (0,"invalid sync\n");return FALSE; } + /* if it's an invalid MPEG version */ + if (((head >> 19) & 3) == 0x1) { + GST_DEBUG (0,"invalid MPEG version\n");return FALSE; } + /* if it's an invalid layer */ + if (!((head >> 17) & 3)) { + GST_DEBUG (0,"invalid layer\n");return FALSE; } + /* if it's an invalid bitrate */ + if (((head >> 12) & 0xf) == 0x0) { + GST_DEBUG (0,"invalid bitrate\n");return FALSE; } + if (((head >> 12) & 0xf) == 0xf) { + GST_DEBUG (0,"invalid bitrate\n");return FALSE; } + /* if it's an invalid samplerate */ + if (((head >> 10) & 0x3) == 0x3) { + GST_DEBUG (0,"invalid samplerate\n");return FALSE; } + if ((head & 0xffff0000) == 0xfffe0000) { + GST_DEBUG (0,"invalid sync\n");return FALSE; } + if (head & 0x00000002) { + GST_DEBUG (0,"invalid emphasis\n");return FALSE; } + + return TRUE; +} + +static void +gst_mp3parse_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstMPEGAudioParse *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_MP3PARSE(object)); + src = GST_MP3PARSE(object); + + switch (prop_id) { + case ARG_SKIP: + src->skip = g_value_get_int (value); + break; + default: + break; + } +} + +static void +gst_mp3parse_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstMPEGAudioParse *src; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_MP3PARSE(object)); + src = GST_MP3PARSE(object); + + switch (prop_id) { + case ARG_SKIP: + g_value_set_int (value, src->skip); + break; + case ARG_BIT_RATE: + g_value_set_int (value, src->bit_rate * 1000); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the mp3parse element */ + factory = gst_elementfactory_new ("mp3parse", + GST_TYPE_MP3PARSE, + &mp3parse_details); + g_return_val_if_fail (factory != NULL, FALSE); + + sink_temp = mp3_sink_factory (); + gst_elementfactory_add_padtemplate (factory, sink_temp); + + src_temp = mp3_src_factory (); + gst_elementfactory_add_padtemplate (factory, src_temp); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "mp3parse", + plugin_init +}; diff --git a/gst/mpegaudioparse/gstmpegaudioparse.h b/gst/mpegaudioparse/gstmpegaudioparse.h new file mode 100644 index 000000000..fbd1047ef --- /dev/null +++ b/gst/mpegaudioparse/gstmpegaudioparse.h @@ -0,0 +1,71 @@ +/* Gnome-Streamer + * 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 __MP3PARSE_H__ +#define __MP3PARSE_H__ + + +#include <config.h> +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_MP3PARSE \ + (gst_mp3parse_get_type()) +#define GST_MP3PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MP3PARSE,GstMPEGAudioParse)) +#define GST_MP3PARSE_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MP3PARSE,GstMPEGAudioParse)) +#define GST_IS_MP3PARSE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MP3PARSE)) +#define GST_IS_MP3PARSE_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MP3PARSE)) + +typedef struct _GstMPEGAudioParse GstMPEGAudioParse; +typedef struct _GstMPEGAudioParseClass GstMPEGAudioParseClass; + +struct _GstMPEGAudioParse { + GstElement element; + + GstPad *sinkpad,*srcpad; + + GstBuffer *partialbuf; // previous buffer (if carryover) + guint skip; /* number of frames to skip */ + guint bit_rate; + gboolean in_flush; +}; + +struct _GstMPEGAudioParseClass { + GstElementClass parent_class; +}; + +GType gst_mp3parse_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __MP3PARSE_H__ */ diff --git a/gst/passthrough/.gitignore b/gst/passthrough/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/passthrough/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/passthrough/Makefile.am b/gst/passthrough/Makefile.am new file mode 100644 index 000000000..500d4679e --- /dev/null +++ b/gst/passthrough/Makefile.am @@ -0,0 +1,10 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstpassthrough.la + +libgstpassthrough_la_SOURCES = gstpassthrough.c +libgstpassthrough_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstpassthrough.h filter.func + +EXTRA_DIST = diff --git a/gst/passthrough/filter.func b/gst/passthrough/filter.func new file mode 100644 index 000000000..99a9308bb --- /dev/null +++ b/gst/passthrough/filter.func @@ -0,0 +1,18 @@ +{ + guint j; + static long int sample = 0; /* you can use this to count samples */ + + /* + * process data here + * *data contains the original 8 or 16 bit samples and is modified in place + * channels are interleaved in input data + */ + + /* do nothing */ + + for (j = 0; j < num_samples; j++) + { + data[j] = data[j]; + } + sample += num_samples; +} diff --git a/gst/passthrough/gstpassthrough.c b/gst/passthrough/gstpassthrough.c new file mode 100644 index 000000000..dc50e6260 --- /dev/null +++ b/gst/passthrough/gstpassthrough.c @@ -0,0 +1,351 @@ +/* -*- c-basic-offset: 2 -*- + * GStreamer + * Copyright (C) 1999-2001 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. + */ + +#include <string.h> +#include <gst/gst.h> +#include <libs/audio/gstaudio.h> +#include "gstpassthrough.h" + + + +static GstElementDetails passthrough_details = { + "Passthrough", + "Filter/Effect", + "Transparent filter for audio/raw (boilerplate for effects)", + VERSION, + "Thomas <thomas@apestaart.org>, "\ + "Andy Wingo <apwingo@eos.ncsu.edu>", + "(C) 2001", +}; + + +/* Filter signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_SILENT +}; + +static GstPadTemplate* +passthrough_sink_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_padtemplate_new + ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_append(gst_caps_new ("sink_int", "audio/raw", + GST_AUDIO_INT_PAD_TEMPLATE_PROPS), + gst_caps_new ("sink_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)), + NULL); + } + return template; +} + +static GstPadTemplate* +passthrough_src_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) + template = gst_padtemplate_new + ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_append (gst_caps_new ("src_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS), + gst_caps_new ("src_int", "audio/raw", + GST_AUDIO_INT_PAD_TEMPLATE_PROPS)), + NULL); + + return template; +} + +static void passthrough_class_init (GstPassthroughClass *klass); +static void passthrough_init (GstPassthrough *filter); + +static void passthrough_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void passthrough_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static gint passthrough_parse_caps (GstPassthrough *filter, GstCaps *caps); + +static void passthrough_chain (GstPad *pad, GstBuffer *buf); +static void inline passthrough_fast_float_chain (gfloat* data, guint numsamples); +static void inline passthrough_fast_16bit_chain (gint16* data, guint numsamples); +static void inline passthrough_fast_8bit_chain (gint8* data, guint numsamples); + +static GstElementClass *parent_class = NULL; +//static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; + +static GstBufferPool* +passthrough_get_bufferpool (GstPad *pad) +{ + GstPassthrough *filter; + + filter = GST_PASSTHROUGH (gst_pad_get_parent (pad)); + + return gst_pad_get_bufferpool (filter->srcpad); +} + +static GstPadNegotiateReturn +passthrough_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstPassthrough* filter = GST_PASSTHROUGH (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (passthrough_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->sinkpad,caps); +} + +static GstPadNegotiateReturn +passthrough_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstPassthrough* filter = GST_PASSTHROUGH (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (passthrough_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->srcpad,caps); +} + +static gint +passthrough_parse_caps (GstPassthrough *filter, GstCaps *caps) +{ + const gchar *format; + + g_return_val_if_fail(filter!=NULL,-1); + g_return_val_if_fail(caps!=NULL,-1); + + format = gst_caps_get_string(caps, "format"); + + filter->rate = gst_caps_get_int (caps, "rate"); + filter->channels = gst_caps_get_int (caps, "channels"); + + if (strcmp(format, "int")==0) { + filter->format = GST_PASSTHROUGH_FORMAT_INT; + filter->width = gst_caps_get_int (caps, "width"); + filter->depth = gst_caps_get_int (caps, "depth"); + filter->law = gst_caps_get_int (caps, "law"); + filter->endianness = gst_caps_get_int (caps, "endianness"); + filter->is_signed = gst_caps_get_int (caps, "signed"); + if (!filter->silent) { + g_print ("Passthrough : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("Passthrough : format int, bit width %d, endianness %d, signed %s\n", + filter->width, filter->endianness, filter->is_signed ? "yes" : "no"); + } + } else if (strcmp(format, "float")==0) { + filter->format = GST_PASSTHROUGH_FORMAT_FLOAT; + filter->layout = gst_caps_get_string(caps, "layout"); + filter->intercept = gst_caps_get_float(caps, "intercept"); + filter->slope = gst_caps_get_float(caps, "slope"); + if (!filter->silent) { + g_print ("Passthrough : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("Passthrough : format float, layout %s, intercept %f, slope %f\n", + filter->layout, filter->intercept, filter->slope); + } + } else { + return -1; + } + return 0; +} + + +GType +gst_passthrough_get_type(void) { + static GType passthrough_type = 0; + + if (!passthrough_type) { + static const GTypeInfo passthrough_info = { + sizeof(GstPassthroughClass), NULL, + NULL, + (GClassInitFunc)passthrough_class_init, + NULL, + NULL, + sizeof(GstPassthrough), + 0, + (GInstanceInitFunc)passthrough_init, + }; + passthrough_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPassthrough", &passthrough_info, 0); + } + return passthrough_type; +} + +static void +passthrough_class_init (GstPassthroughClass *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(G_OBJECT_CLASS(klass), ARG_SILENT, + g_param_spec_boolean("silent","silent","silent", + TRUE,G_PARAM_READWRITE)); // CHECKME + + gobject_class->set_property = passthrough_set_property; + gobject_class->get_property = passthrough_get_property; +} + +static void +passthrough_init (GstPassthrough *filter) +{ + filter->sinkpad = gst_pad_new_from_template(passthrough_sink_factory (),"sink"); + gst_pad_set_negotiate_function(filter->sinkpad,passthrough_negotiate_sink); + gst_pad_set_bufferpool_function(filter->sinkpad,passthrough_get_bufferpool); + filter->srcpad = gst_pad_new_from_template(passthrough_src_factory (),"src"); + gst_pad_set_negotiate_function(filter->srcpad,passthrough_negotiate_src); + + gst_element_add_pad(GST_ELEMENT(filter),filter->sinkpad); + gst_element_add_pad(GST_ELEMENT(filter),filter->srcpad); + gst_pad_set_chain_function(filter->sinkpad,passthrough_chain); + filter->silent = FALSE; +} + +static void +passthrough_chain (GstPad *pad, GstBuffer *buf) +{ + GstPassthrough *filter; + gint16 *int_data; + gfloat *float_data; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + filter = GST_PASSTHROUGH(GST_OBJECT_PARENT (pad)); + g_return_if_fail(filter != NULL); + g_return_if_fail(GST_IS_PASSTHROUGH(filter)); + + switch (filter->format) { + case GST_PASSTHROUGH_FORMAT_INT: + int_data = (gint16 *)GST_BUFFER_DATA(buf); + + switch (filter->width) { + case 16: + passthrough_fast_16bit_chain(int_data,GST_BUFFER_SIZE(buf)/2); + break; + case 8: + passthrough_fast_8bit_chain((gint8*)int_data,GST_BUFFER_SIZE(buf)); + break; + } + + break; + case GST_PASSTHROUGH_FORMAT_FLOAT: + float_data = (gfloat *)GST_BUFFER_DATA(buf); + + passthrough_fast_float_chain(float_data,GST_BUFFER_SIZE(buf)/sizeof(float)); + + break; + } + + gst_pad_push(filter->srcpad,buf); +} + +static void inline +passthrough_fast_float_chain(gfloat* data, + guint num_samples) +#include "filter.func" + +static void inline +passthrough_fast_16bit_chain(gint16* data, + guint num_samples) +#include "filter.func" + +static void inline +passthrough_fast_8bit_chain(gint8* data, + guint num_samples) +#include "filter.func" + +static void +passthrough_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstPassthrough *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_PASSTHROUGH(object)); + filter = GST_PASSTHROUGH(object); + + switch (prop_id) + { + case ARG_SILENT: + filter->silent = g_value_get_boolean (value); + break; + default: + break; + } +} + +static void +passthrough_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstPassthrough *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_PASSTHROUGH(object)); + filter = GST_PASSTHROUGH(object); + + switch (prop_id) { + case ARG_SILENT: + g_value_set_boolean (value, filter->silent); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("passthrough",GST_TYPE_PASSTHROUGH, + &passthrough_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, passthrough_src_factory ()); + gst_elementfactory_add_padtemplate (factory, passthrough_sink_factory ()); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "passthrough", + plugin_init +}; diff --git a/gst/passthrough/gstpassthrough.h b/gst/passthrough/gstpassthrough.h new file mode 100644 index 000000000..d8c2f63e2 --- /dev/null +++ b/gst/passthrough/gstpassthrough.h @@ -0,0 +1,103 @@ +/* -*- c-basic-offset: 2 -*- + * 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_PASSTHROUGH_H__ +#define __GST_PASSTHROUGH_H__ + + +#include <config.h> +#include <gst/gst.h> +// #include <gst/meta/audioraw.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_PASSTHROUGH \ + (gst_passthrough_get_type()) +#define GST_PASSTHROUGH(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PASSTHROUGH,GstPassthrough)) +#define GST_PASSTHROUGH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstPassthrough)) +#define GST_IS_PASSTHROUGH(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PASSTHROUGH)) +#define GST_IS_PASSTHROUGH_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PASSTHROUGH)) + +typedef struct _GstPassthrough GstPassthrough; +typedef struct _GstPassthroughClass GstPassthroughClass; +typedef enum _GstPassthroughFormat GstPassthroughFormat; + +enum _GstPassthroughFormat { + GST_PASSTHROUGH_FORMAT_INT, + GST_PASSTHROUGH_FORMAT_FLOAT +}; + +struct _GstPassthrough { + GstElement element; + + GstPad *sinkpad, *srcpad; + + gboolean silent; + + /* the next three are valid for both int and float */ + + GstPassthroughFormat format; + + guint rate; + + guint channels; + + /* the next five are valid only for format==GST_PASSTHROUGH_FORMAT_INT */ + + guint width; + + guint depth; + + guint endianness; + + guint law; + + gboolean is_signed; + + /* the next three are valid only for format==GST_PASSTHROUGH_FORMAT_FLOAT */ + + const gchar *layout; + + gfloat slope; + + gfloat intercept; +}; + +struct _GstPassthroughClass { + GstElementClass parent_class; +}; + +GType gst_passthrough_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_PASSTHROUGH_H__ */ diff --git a/gst/playondemand/Makefile.am b/gst/playondemand/Makefile.am new file mode 100644 index 000000000..fc2b93b9e --- /dev/null +++ b/gst/playondemand/Makefile.am @@ -0,0 +1,10 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstplayondemand.la + +libgstplayondemand_la_SOURCES = gstplayondemand.c +libgstplayondemand_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstplayondemand.h filter.func + +# EXTRA_DIST = README diff --git a/gst/playondemand/filter.func b/gst/playondemand/filter.func new file mode 100644 index 000000000..6f81c9757 --- /dev/null +++ b/gst/playondemand/filter.func @@ -0,0 +1,120 @@ +/* -*- C -*- */ + +_TYPE_ *data_in, *data_out, *filter_data; + +filter_data = (_TYPE_ *) filter->buffer; +num_filter = filter->buffer_size / sizeof(_TYPE_); + +/******************************************************************************/ +/* see if we've got any events coming through ... */ + +do { + GST_DEBUG(0, "--- going to events\n"); + + while (! filter->eos && GST_IS_EVENT(in)) { + if (GST_EVENT_TYPE(in) == GST_EVENT_EOS) { + filter->eos = TRUE; + } else { + gst_pad_push(filter->srcpad, in); + } + + in = gst_pad_pull(filter->sinkpad); + } + + /******************************************************************************/ + /* first handle data from the input buffer. */ + + GST_DEBUG(0, "--- done with events, going to input\n"); + + /* only update the input if there hasn't been an eos yet. */ + if (! filter->eos) { + data_in = (_TYPE_ *) GST_BUFFER_DATA(in); + num_in = GST_BUFFER_SIZE(in) / sizeof(_TYPE_); + + w = filter->write; + + /* copy the input data to the filter's internal buffer. */ + if (filter->follow_stream_tail) { + for (j = 0; j < num_in; j++) { + filter_data[(w + j) % num_filter] = data_in[j]; + } + + filter->write = (w + j) % num_filter; + + /* update the start pointer */ + if ((filter->start != 0) || ((w + j) >= num_filter)) { + filter->start = (filter->write + 1) % num_filter; + } + } else { + for (j = 0; (j < num_in) && ((w + j) < num_filter); j++) { + filter_data[w + j] = data_in[j]; + } + + filter->write += j; + + /* if we're not following the stream tail, the buffer is just a straight + buffer. so we need to set eos if we've passed the limit of the internal + buffer size. */ + if ((w + j) >= num_filter) { + filter->eos = TRUE; + } + } + + out = in; + } else { + j = 0; + + if (filter->srcpool) { + out = gst_buffer_new_from_pool(filter->srcpool, 0, 0); + } else { + out = gst_buffer_new(); + + GST_BUFFER_DATA(out) = (gchar *) g_new(_TYPE_, POD_GSTBUFSIZE / sizeof(_TYPE_)); + GST_BUFFER_SIZE(out) = POD_GSTBUFSIZE; + } + } + + /******************************************************************************/ + /* now handle output data. */ + + GST_DEBUG(0, "--- done with input, going to output\n"); + + data_out = (_TYPE_ *) GST_BUFFER_DATA(out); + num_out = GST_BUFFER_SIZE(out) / sizeof(_TYPE_); + + for (k = 0; k < num_out; k++) { + data_out[k] = zero; + } + + /* output play pointer data. */ + for (t = 0; t < POD_MAX_PLAYS; t++) { + offset = filter->plays[t]; + + if (offset != G_MAXUINT) { + if (filter->follow_stream_tail) { + for (k = 0; k < num_out; k++) { + data_out[k] = CLAMP(data_out[k] + filter_data[(offset + k) % num_filter], min, max); + } + } else { + for (k = 0; (k < num_out) && ((offset + k) < (w + j)); k++) { + data_out[k] = CLAMP(data_out[k] + filter_data[offset + k], min, max); + } + } + + if ((offset < w) && ((offset + k) >= (w + j))) { + filter->plays[t] = G_MAXUINT; + } else { + filter->plays[t] = (filter->plays[t] + k) % num_filter; + } + } + } + + GST_DEBUG(0, "--- done with output, pushing buffer %p\n", out); + + gst_pad_push(filter->srcpad, out); + + if (! filter->eos) { + in = gst_pad_pull(filter->sinkpad); + } + +} while (! GST_ELEMENT_IS_COTHREAD_STOPPING(elem)); diff --git a/gst/playondemand/gstplayondemand.c b/gst/playondemand/gstplayondemand.c new file mode 100644 index 000000000..9d2ebff31 --- /dev/null +++ b/gst/playondemand/gstplayondemand.c @@ -0,0 +1,448 @@ +/* GStreamer + * Copyright (C) 1999-2001 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. + */ + +#include <string.h> +#include <gst/gst.h> +#include <libs/audio/gstaudio.h> +#include "gstplayondemand.h" + + +#define POD_MAX_PLAYS 192 /* maximum number of simultaneous plays */ +#define POD_GSTBUFSIZE 4096 /* gstreamer buffer size to make if no + bufferpool is available, must be divisible + by sizeof(gfloat) */ +#define POD_BUFSPERCHUNK 6 /* number of buffers to allocate per chunk in + sink buffer pool */ +#define POD_BUFFER_SIZE 882000 /* enough space for 10 seconds of 16-bit audio + at 44100 samples per second ... */ + +static GstElementDetails play_on_demand_details = { + "Play On Demand", + "Filter/Effect", + "Plays a stream whenever it receives a certain signal", + VERSION, + "Leif Morgan Johnson <lmjohns3@eos.ncsu.edu>", + "(C) 2001", +}; + + +/* Filter signals and args */ +enum { + /* FILL ME */ + PLAY_SIGNAL, + RESET_SIGNAL, + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_SILENT, + ARG_FOLLOWTAIL, + ARG_BUFFERSIZE +}; + +static GstPadTemplate* +play_on_demand_sink_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_padtemplate_new + ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_append(gst_caps_new ("sink_int", "audio/raw", + GST_AUDIO_INT_PAD_TEMPLATE_PROPS), + gst_caps_new ("sink_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)), + NULL); + } + return template; +} + +static GstPadTemplate* +play_on_demand_src_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) + template = gst_padtemplate_new + ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_append (gst_caps_new ("src_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS), + gst_caps_new ("src_int", "audio/raw", + GST_AUDIO_INT_PAD_TEMPLATE_PROPS)), + NULL); + + return template; +} + +static void play_on_demand_class_init (GstPlayOnDemandClass *klass); +static void play_on_demand_init (GstPlayOnDemand *filter); + +static void play_on_demand_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void play_on_demand_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static gint play_on_demand_parse_caps (GstPlayOnDemand *filter, GstCaps *caps); + +static void play_on_demand_loop (GstElement *elem); + +static void play_on_demand_play_handler (GstElement *elem); +static void play_on_demand_reset_handler (GstElement *elem); + +static GstElementClass *parent_class = NULL; +static guint gst_pod_filter_signals[LAST_SIGNAL] = { 0 }; + +static GstBufferPool* +play_on_demand_get_bufferpool (GstPad *pad) +{ + GstPlayOnDemand *filter; + + filter = GST_PLAYONDEMAND(gst_pad_get_parent(pad)); + + return gst_pad_get_bufferpool(filter->srcpad); +} + +static GstPadNegotiateReturn +play_on_demand_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstPlayOnDemand* filter = GST_PLAYONDEMAND(gst_pad_get_parent(pad)); + + if (*caps == NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (play_on_demand_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad, filter->sinkpad, caps); +} + +static GstPadNegotiateReturn +play_on_demand_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstPlayOnDemand* filter = GST_PLAYONDEMAND(gst_pad_get_parent(pad)); + + if (*caps == NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (play_on_demand_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad, filter->srcpad, caps); +} + +static gint +play_on_demand_parse_caps (GstPlayOnDemand *filter, GstCaps *caps) +{ + const gchar *format; + + g_return_val_if_fail(filter != NULL, -1); + g_return_val_if_fail(caps != NULL, -1); + + format = gst_caps_get_string(caps, "format"); + + filter->rate = gst_caps_get_int(caps, "rate"); + filter->channels = gst_caps_get_int(caps, "channels"); + + if (strcmp(format, "int") == 0) { + filter->format = GST_PLAYONDEMAND_FORMAT_INT; + filter->width = gst_caps_get_int(caps, "width"); + filter->depth = gst_caps_get_int(caps, "depth"); + filter->law = gst_caps_get_int(caps, "law"); + filter->endianness = gst_caps_get_int(caps, "endianness"); + filter->is_signed = gst_caps_get_int(caps, "signed"); + if (!filter->silent) { + g_print ("PlayOnDemand : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("PlayOnDemand : format int, bit width %d, endianness %d, signed %s\n", + filter->width, filter->endianness, filter->is_signed ? "yes" : "no"); + } + } else if (strcmp(format, "float")==0) { + filter->format = GST_PLAYONDEMAND_FORMAT_FLOAT; + filter->layout = gst_caps_get_string(caps, "layout"); + filter->intercept = gst_caps_get_float(caps, "intercept"); + filter->slope = gst_caps_get_float(caps, "slope"); + if (!filter->silent) { + g_print ("PlayOnDemand : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("PlayOnDemand : format float, layout %s, intercept %f, slope %f\n", + filter->layout, filter->intercept, filter->slope); + } + } else { + return -1; + } + return 0; +} + + +GType +gst_play_on_demand_get_type(void) { + static GType play_on_demand_type = 0; + + if (! play_on_demand_type) { + static const GTypeInfo play_on_demand_info = { + sizeof(GstPlayOnDemandClass), + NULL, + NULL, + (GClassInitFunc) play_on_demand_class_init, + NULL, + NULL, + sizeof(GstPlayOnDemand), + 0, + (GInstanceInitFunc) play_on_demand_init, + }; + play_on_demand_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPlayOnDemand", &play_on_demand_info, 0); + } + return play_on_demand_type; +} + +static void +play_on_demand_class_init (GstPlayOnDemandClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gst_pod_filter_signals[PLAY_SIGNAL] = + g_signal_new("play", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GstPlayOnDemandClass, play), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gst_pod_filter_signals[RESET_SIGNAL] = + g_signal_new("reset", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GstPlayOnDemandClass, reset), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + klass->play = play_on_demand_play_handler; + klass->reset = play_on_demand_reset_handler; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SILENT, + g_param_spec_boolean("silent","silent","silent", + TRUE, G_PARAM_READWRITE)); // CHECKME + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOLLOWTAIL, + g_param_spec_boolean("follow-stream-tail","follow-stream-tail","follow-stream-tail", + FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFERSIZE, + g_param_spec_uint("buffer-size","buffer-size","buffer-size", + 0, G_MAXUINT - 1, POD_BUFFER_SIZE, G_PARAM_READWRITE)); + + gobject_class->set_property = play_on_demand_set_property; + gobject_class->get_property = play_on_demand_get_property; +} + +static void +play_on_demand_init (GstPlayOnDemand *filter) +{ + guint i; + + filter->sinkpad = gst_pad_new_from_template(play_on_demand_sink_factory(), "sink"); + gst_pad_set_negotiate_function(filter->sinkpad, play_on_demand_negotiate_sink); + gst_pad_set_bufferpool_function(filter->sinkpad, play_on_demand_get_bufferpool); + + filter->srcpad = gst_pad_new_from_template(play_on_demand_src_factory(), "src"); + gst_pad_set_negotiate_function(filter->srcpad, play_on_demand_negotiate_src); + + gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad); + gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad); + + gst_element_set_loop_function(GST_ELEMENT(filter), play_on_demand_loop); + + filter->sinkpool = gst_buffer_pool_get_default(POD_GSTBUFSIZE, POD_BUFSPERCHUNK); + + filter->follow_stream_tail = FALSE; + filter->silent = TRUE; + + filter->buffer = g_new(gchar, POD_BUFFER_SIZE); + filter->buffer_size = POD_BUFFER_SIZE; + filter->start = 0; + filter->write = 0; + + filter->eos = FALSE; + + /* the plays are stored as an array of buffer offsets. this initializes the + array to `blank' values (G_MAXUINT is an invalid index for this filter). */ + filter->plays = g_new(guint, POD_MAX_PLAYS); + for (i = 0; i < POD_MAX_PLAYS; i++) { + filter->plays[i] = G_MAXUINT; + } +} + +static void +play_on_demand_loop (GstElement *elem) +{ + GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); + guint num_in, num_out, num_filter; + GstBuffer *in, *out; + register guint j, k, t; + guint w, offset; + + g_return_if_fail(filter != NULL); + g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); + + filter->srcpool = gst_pad_get_bufferpool(filter->srcpad); + + in = gst_pad_pull(filter->sinkpad); + + if (filter->format == GST_PLAYONDEMAND_FORMAT_INT) { + if (filter->width == 16) { + gint16 min = -32768; + gint16 max = 32767; + gint16 zero = 0; +#define _TYPE_ gint16 +#include "filter.func" +#undef _TYPE_ + } else if (filter->width == 8) { + gint8 min = -128; + gint8 max = 127; + gint8 zero = 0; +#define _TYPE_ gint8 +#include "filter.func" +#undef _TYPE_ + } + } else if (filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT) { + gfloat min = -1.0; + gfloat max = 1.0; + gfloat zero = 0.0; +#define _TYPE_ gfloat +#include "filter.func" +#undef _TYPE_ + } +} + +static void +play_on_demand_play_handler(GstElement *elem) +{ + GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); + register guint i; + + for (i = 0; i < POD_MAX_PLAYS; i++) { + if (filter->plays[i] == G_MAXUINT) { + filter->plays[i] = filter->start; + break; + } + } +} + +static void +play_on_demand_reset_handler(GstElement *elem) +{ + GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); + register guint i; + + for (i = 0; i < POD_MAX_PLAYS; i++) { + filter->plays[i] = G_MAXUINT; + } + + filter->start = 0; + filter->write = 0; +} + +static void +play_on_demand_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstPlayOnDemand *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_PLAYONDEMAND(object)); + filter = GST_PLAYONDEMAND(object); + + switch (prop_id) { + case ARG_BUFFERSIZE: + filter->buffer_size = g_value_get_uint(value); + + /* reallocate space for the buffer with the new size values. */ + g_free(filter->buffer); + filter->buffer = g_new(gchar, filter->buffer_size); + + /* reset the play pointers and read/write indexes. */ + play_on_demand_reset_handler(GST_ELEMENT(filter)); + break; + case ARG_SILENT: + filter->silent = g_value_get_boolean(value); + break; + case ARG_FOLLOWTAIL: + filter->follow_stream_tail = g_value_get_boolean(value); + play_on_demand_reset_handler(GST_ELEMENT(filter)); + break; + default: + break; + } +} + +static void +play_on_demand_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstPlayOnDemand *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_PLAYONDEMAND(object)); + filter = GST_PLAYONDEMAND(object); + + switch (prop_id) { + case ARG_BUFFERSIZE: + g_value_set_uint(value, filter->buffer_size); + break; + case ARG_SILENT: + g_value_set_boolean(value, filter->silent); + break; + case ARG_FOLLOWTAIL: + g_value_set_boolean(value, filter->follow_stream_tail); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("playondemand", + GST_TYPE_PLAYONDEMAND, + &play_on_demand_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate(factory, play_on_demand_src_factory()); + gst_elementfactory_add_padtemplate(factory, play_on_demand_sink_factory()); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE(factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "playondemand", + plugin_init +}; diff --git a/gst/playondemand/gstplayondemand.h b/gst/playondemand/gstplayondemand.h new file mode 100644 index 000000000..7ca8206ed --- /dev/null +++ b/gst/playondemand/gstplayondemand.h @@ -0,0 +1,112 @@ +/* -*- c-basic-offset: 2 -*- + * 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_PLAYONDEMAND_H__ +#define __GST_PLAYONDEMAND_H__ + + +#include <config.h> +#include <gst/gst.h> +/* #include <gst/meta/audioraw.h> */ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_PLAYONDEMAND \ + (gst_play_on_demand_get_type()) +#define GST_PLAYONDEMAND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAYONDEMAND,GstPlayOnDemand)) +#define GST_PLAYONDEMAND_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstPlayOnDemand)) +#define GST_IS_PLAYONDEMAND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAYONDEMAND)) +#define GST_IS_PLAYONDEMAND_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAYONDEMAND)) + +typedef struct _GstPlayOnDemand GstPlayOnDemand; +typedef struct _GstPlayOnDemandClass GstPlayOnDemandClass; +typedef enum _GstPlayOnDemandFormat GstPlayOnDemandFormat; + +enum _GstPlayOnDemandFormat { + GST_PLAYONDEMAND_FORMAT_INT, + GST_PLAYONDEMAND_FORMAT_FLOAT +}; + +struct _GstPlayOnDemand { + GstElement element; + + GstPad *sinkpad, *srcpad; + GstBufferPool *sinkpool, *srcpool; + + /* these next data elements are for the filter's internal buffers and list of + play pointers (offsets in the internal buffers). there are also flags for + repeating from the beginning or end of the input stream, and a max buffer + size. */ + gchar *buffer; + guint buffer_size; + + guint write; + guint start; + + guint *plays; + + gboolean eos; + + gboolean follow_stream_tail; + + gboolean silent; + + /* the next three are valid for both int and float */ + GstPlayOnDemandFormat format; + guint rate; + guint channels; + + /* the next five are valid only for format == GST_PLAYONDEMAND_FORMAT_INT */ + guint width; + guint depth; + guint endianness; + guint law; + gboolean is_signed; + + /* the next three are valid only for format == GST_PLAYONDEMAND_FORMAT_FLOAT */ + const gchar *layout; + gfloat slope; + gfloat intercept; +}; + +struct _GstPlayOnDemandClass { + GstElementClass parent_class; + + void (*play) (GstElement *elem); + void (*reset) (GstElement *elem); +}; + +GType gst_play_on_demand_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_PLAYONDEMAND_H__ */ diff --git a/gst/rtjpeg/.gitignore b/gst/rtjpeg/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/rtjpeg/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/rtjpeg/Makefile.am b/gst/rtjpeg/Makefile.am new file mode 100644 index 000000000..157ac7607 --- /dev/null +++ b/gst/rtjpeg/Makefile.am @@ -0,0 +1,8 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstrtjpeg.la + +libgstrtjpeg_la_SOURCES = gstrtjpeg.c rtjpegenc.c rtjpegdec.c RTjpeg.c +libgstrtjpeg_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstrtjpegenc.h gstrtjpegdec.h RTjpeg.h diff --git a/gst/rtjpeg/README b/gst/rtjpeg/README new file mode 100644 index 000000000..34796867d --- /dev/null +++ b/gst/rtjpeg/README @@ -0,0 +1,12 @@ +This plugin contains elements necessary for doing RTjpeg compression and +decompression, as well as conversion from RGB to YUV and back, based +entirely on functions supplied by RTjpeg.c. + +You can find RTjpeg at Justin Schoeman's site: +http://www.ee.up.ac.za/~justin/bttv/ + +The idea is to start out with the code elements reading and writing YUV420 +images, with external elements to do the conversion. Eventually (when +typing is a bit more sane/globally used) the codec elements will +automatically convert images to/from the proper format based on what their +peer wants to be dealing with. diff --git a/gst/rtjpeg/RTjpeg.c b/gst/rtjpeg/RTjpeg.c new file mode 100644 index 000000000..38d3ce12e --- /dev/null +++ b/gst/rtjpeg/RTjpeg.c @@ -0,0 +1,3434 @@ +/* + This program is free software; you can redristibute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file is a modified version of RTjpeg 0.1.2, (C) Justin Schoeman 1998 + + (991101) Wim Taymans : added MMX dct and idct from intels site. +*/ + + +/* + +Main Routines + +This file contains most of the initialisation and control functions + +(C) Justin Schoeman 1998 + +*/ + +#include <config.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef unsigned char __u8; +typedef signed char __s8; +typedef unsigned short __u16; +typedef signed short __s16; +typedef unsigned long __u32; +typedef signed long __s32; +typedef unsigned long long __u64; + +//#define MMX_TRACE + + +#ifdef HAVE_LIBMMX +#include "mmx.h" +#endif + +static const unsigned char RTjpeg_ZZ[64]={ +0, +8, 1, +2, 9, 16, +24, 17, 10, 3, +4, 11, 18, 25, 32, +40, 33, 26, 19, 12, 5, +6, 13, 20, 27, 34, 41, 48, +56, 49, 42, 35, 28, 21, 14, 7, +15, 22, 29, 36, 43, 50, 57, +58, 51, 44, 37, 30, 23, +31, 38, 45, 52, 59, +60, 53, 46, 39, +47, 54, 61, +62, 55, +63 }; + +static const __u64 RTjpeg_aan_tab[64]={ +4294967296ULL, 5957222912ULL, 5611718144ULL, 5050464768ULL, 4294967296ULL, 3374581504ULL, 2324432128ULL, 1184891264ULL, +5957222912ULL, 8263040512ULL, 7783580160ULL, 7005009920ULL, 5957222912ULL, 4680582144ULL, 3224107520ULL, 1643641088ULL, +5611718144ULL, 7783580160ULL, 7331904512ULL, 6598688768ULL, 5611718144ULL, 4408998912ULL, 3036936960ULL, 1548224000ULL, +5050464768ULL, 7005009920ULL, 6598688768ULL, 5938608128ULL, 5050464768ULL, 3968072960ULL, 2733115392ULL, 1393296000ULL, +4294967296ULL, 5957222912ULL, 5611718144ULL, 5050464768ULL, 4294967296ULL, 3374581504ULL, 2324432128ULL, 1184891264ULL, +3374581504ULL, 4680582144ULL, 4408998912ULL, 3968072960ULL, 3374581504ULL, 2651326208ULL, 1826357504ULL, 931136000ULL, +2324432128ULL, 3224107520ULL, 3036936960ULL, 2733115392ULL, 2324432128ULL, 1826357504ULL, 1258030336ULL, 641204288ULL, +1184891264ULL, 1643641088ULL, 1548224000ULL, 1393296000ULL, 1184891264ULL, 931136000ULL, 641204288ULL, 326894240ULL, +}; + +#ifndef HAVE_LIBMMX +static __s32 RTjpeg_ws[64+31]; +#endif +__u8 RTjpeg_alldata[2*64+4*64+4*64+4*64+4*64+32]; + +__s16 *RTjpeg_block; +__s32 *RTjpeg_lqt; +__s32 *RTjpeg_cqt; +__u32 *RTjpeg_liqt; +__u32 *RTjpeg_ciqt; + +unsigned char RTjpeg_lb8; +unsigned char RTjpeg_cb8; +int RTjpeg_width, RTjpeg_height; +int RTjpeg_Ywidth, RTjpeg_Cwidth; +int RTjpeg_Ysize, RTjpeg_Csize; + +__s16 *RTjpeg_old=NULL; + +#ifdef HAVE_LIBMMX +mmx_t RTjpeg_lmask; +mmx_t RTjpeg_cmask; +#else +__u16 RTjpeg_lmask; +__u16 RTjpeg_cmask; +#endif +int RTjpeg_mtest=0; + +static const unsigned char RTjpeg_lum_quant_tbl[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 + }; + +static const unsigned char RTjpeg_chrom_quant_tbl[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }; + +int RTjpeg_b2s(__s16 *data, __s8 *strm, __u8 bt8) +{ + register int ci, co=1, tmp; + register __s16 ZZvalue; + + (__u8)strm[0]=(__u8)(data[RTjpeg_ZZ[0]]>254) ? 254:((data[RTjpeg_ZZ[0]]<0)?0:data[RTjpeg_ZZ[0]]); + + for(ci=1; ci<=bt8; ci++) + { + ZZvalue = data[RTjpeg_ZZ[ci]]; + + if(ZZvalue>0) + { + strm[co++]=(__s8)(ZZvalue>127)?127:ZZvalue; + } + else + { + strm[co++]=(__s8)(ZZvalue<-128)?-128:ZZvalue; + } + } + + for(; ci<64; ci++) + { + ZZvalue = data[RTjpeg_ZZ[ci]]; + + if(ZZvalue>0) + { + strm[co++]=(__s8)(ZZvalue>63)?63:ZZvalue; + } + else if(ZZvalue<0) + { + strm[co++]=(__s8)(ZZvalue<-64)?-64:ZZvalue; + } + else /* compress zeros */ + { + tmp=ci; + do + { + ci++; + } + while((ci<64)&&(data[RTjpeg_ZZ[ci]]==0)); + + strm[co++]=(__s8)(63+(ci-tmp)); + ci--; + } + } + return (int)co; +} + +int RTjpeg_s2b(__s16 *data, __s8 *strm, __u8 bt8, __u32 *qtbl) +{ + int ci=1, co=1, tmp; + register int i; + + i=RTjpeg_ZZ[0]; + data[i]=((__u8)strm[0])*qtbl[i]; + + for(co=1; co<=bt8; co++) + { + i=RTjpeg_ZZ[co]; + data[i]=strm[ci++]*qtbl[i]; + } + + for(; co<64; co++) + { + if(strm[ci]>63) + { + tmp=co+strm[ci]-63; + for(; co<tmp; co++)data[RTjpeg_ZZ[co]]=0; + co--; + } else + { + i=RTjpeg_ZZ[co]; + data[i]=strm[ci]*qtbl[i]; + } + ci++; + } + return (int)ci; +} + +#if defined(HAVE_LIBMMX) +void RTjpeg_quant_init(void) +{ + int i; + __s16 *qtbl; + + qtbl=(__s16 *)RTjpeg_lqt; + for(i=0; i<64; i++)qtbl[i]=(__s16)RTjpeg_lqt[i]; + + qtbl=(__s16 *)RTjpeg_cqt; + for(i=0; i<64; i++)qtbl[i]=(__s16)RTjpeg_cqt[i]; +} + +static mmx_t RTjpeg_ones=(mmx_t)(long long)0x0001000100010001LL; +static mmx_t RTjpeg_half=(mmx_t)(long long)0x7fff7fff7fff7fffLL; + +void RTjpeg_quant(__s16 *block, __s32 *qtbl) +{ + int i; + mmx_t *bl, *ql; + + ql=(mmx_t *)qtbl; + bl=(mmx_t *)block; + + movq_m2r(RTjpeg_ones, mm6); + movq_m2r(RTjpeg_half, mm7); + + for(i=16; i; i--) + { + movq_m2r(*(ql++), mm0); /* quant vals (4) */ + movq_m2r(*bl, mm2); /* block vals (4) */ + movq_r2r(mm0, mm1); + movq_r2r(mm2, mm3); + + punpcklwd_r2r(mm6, mm0); /* 1 qb 1 qa */ + punpckhwd_r2r(mm6, mm1); /* 1 qd 1 qc */ + + punpcklwd_r2r(mm7, mm2); /* 32767 bb 32767 ba */ + punpckhwd_r2r(mm7, mm3); /* 32767 bd 32767 bc */ + + pmaddwd_r2r(mm2, mm0); /* 32767+bb*qb 32767+ba*qa */ + pmaddwd_r2r(mm3, mm1); /* 32767+bd*qd 32767+bc*qc */ + + psrad_i2r(16, mm0); + psrad_i2r(16, mm1); + + packssdw_r2r(mm1, mm0); + + movq_r2m(mm0, *(bl++)); + + } +} +#else +void RTjpeg_quant_init(void) +{ +} + +void RTjpeg_quant(__s16 *block, __s32 *qtbl) +{ + int i; + + for(i=0; i<64; i++) + block[i]=(__s16)((block[i]*qtbl[i]+32767)>>16); +} +#endif + +/* + * Perform the forward DCT on one block of samples. + */ +#ifdef HAVE_LIBMMX +static mmx_t RTjpeg_C4 =(mmx_t)(long long)0x2D412D412D412D41LL; +static mmx_t RTjpeg_C6 =(mmx_t)(long long)0x187E187E187E187ELL; +static mmx_t RTjpeg_C2mC6=(mmx_t)(long long)0x22A322A322A322A3LL; +static mmx_t RTjpeg_C2pC6=(mmx_t)(long long)0x539F539F539F539FLL; +static mmx_t RTjpeg_zero =(mmx_t)(long long)0x0000000000000000LL; + +#else + +#define FIX_0_382683433 ((__s32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((__s32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((__s32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((__s32) 334) /* FIX(1.306562965) */ + +#define DESCALE10(x) (__s16)( ((x)+128) >> 8) +#define DESCALE20(x) (__s16)(((x)+32768) >> 16) +#define D_MULTIPLY(var,const) ((__s32) ((var) * (const))) +#endif + +void RTjpeg_dct_init(void) +{ + int i; + + for(i=0; i<64; i++) + { + RTjpeg_lqt[i]=(((__u64)RTjpeg_lqt[i]<<32)/RTjpeg_aan_tab[i]); + RTjpeg_cqt[i]=(((__u64)RTjpeg_cqt[i]<<32)/RTjpeg_aan_tab[i]); + } +} + +void RTjpeg_dctY(__u8 *idata, __s16 *odata, int rskip) +{ +#ifndef HAVE_LIBMMX + __s32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + __s32 tmp10, tmp11, tmp12, tmp13; + __s32 z1, z2, z3, z4, z5, z11, z13; + __u8 *idataptr; + __s16 *odataptr; + __s32 *wsptr; + int ctr; + + idataptr = idata; + wsptr = RTjpeg_ws; + for (ctr = 7; ctr >= 0; ctr--) { + tmp0 = idataptr[0] + idataptr[7]; + tmp7 = idataptr[0] - idataptr[7]; + tmp1 = idataptr[1] + idataptr[6]; + tmp6 = idataptr[1] - idataptr[6]; + tmp2 = idataptr[2] + idataptr[5]; + tmp5 = idataptr[2] - idataptr[5]; + tmp3 = idataptr[3] + idataptr[4]; + tmp4 = idataptr[3] - idataptr[4]; + + tmp10 = (tmp0 + tmp3); /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = (tmp1 + tmp2); + tmp12 = tmp1 - tmp2; + + wsptr[0] = (tmp10 + tmp11)<<8; /* phase 3 */ + wsptr[4] = (tmp10 - tmp11)<<8; + + z1 = D_MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + wsptr[2] = (tmp13<<8) + z1; /* phase 5 */ + wsptr[6] = (tmp13<<8) - z1; + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + z5 = D_MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = D_MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = D_MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = D_MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = (tmp7<<8) + z3; /* phase 5 */ + z13 = (tmp7<<8) - z3; + + wsptr[5] = z13 + z2; /* phase 6 */ + wsptr[3] = z13 - z2; + wsptr[1] = z11 + z4; + wsptr[7] = z11 - z4; + + idataptr += rskip<<3; /* advance pointer to next row */ + wsptr += 8; + } + + wsptr = RTjpeg_ws; + odataptr=odata; + for (ctr = 7; ctr >= 0; ctr--) { + tmp0 = wsptr[0] + wsptr[56]; + tmp7 = wsptr[0] - wsptr[56]; + tmp1 = wsptr[8] + wsptr[48]; + tmp6 = wsptr[8] - wsptr[48]; + tmp2 = wsptr[16] + wsptr[40]; + tmp5 = wsptr[16] - wsptr[40]; + tmp3 = wsptr[24] + wsptr[32]; + tmp4 = wsptr[24] - wsptr[32]; + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + odataptr[0] = DESCALE10(tmp10 + tmp11); /* phase 3 */ + odataptr[32] = DESCALE10(tmp10 - tmp11); + + z1 = D_MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + odataptr[16] = DESCALE20((tmp13<<8) + z1); /* phase 5 */ + odataptr[48] = DESCALE20((tmp13<<8) - z1); + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + z5 = D_MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = D_MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = D_MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = D_MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = (tmp7<<8) + z3; /* phase 5 */ + z13 = (tmp7<<8) - z3; + + odataptr[40] = DESCALE20(z13 + z2); /* phase 6 */ + odataptr[24] = DESCALE20(z13 - z2); + odataptr[8] = DESCALE20(z11 + z4); + odataptr[56] = DESCALE20(z11 - z4); + + odataptr++; /* advance pointer to next column */ + wsptr++; + } +#else + mmx_t tmp6, tmp7; + register mmx_t *dataptr = (mmx_t *)odata; + mmx_t *idata2 = (mmx_t *)idata; + + // first copy the input 8 bit to the destination 16 bits + + movq_m2r(RTjpeg_zero, mm2); + + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+1)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+2)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+3)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+4)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+5)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+6)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+7)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+8)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+9)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+10)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+11)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+12)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+13)); + + idata2 += rskip; + + movq_m2r(*idata2, mm0); + movq_r2r(mm0, mm1); + + punpcklbw_r2r(mm2, mm0); + movq_r2m(mm0, *(dataptr+14)); + + punpckhbw_r2r(mm2, mm1); + movq_r2m(mm1, *(dataptr+15)); + +/* Start Transpose to do calculations on rows */ + + movq_m2r(*(dataptr+9), mm7); // m03:m02|m01:m00 - first line (line 4)and copy into m5 + + movq_m2r(*(dataptr+13), mm6); // m23:m22|m21:m20 - third line (line 6)and copy into m2 + movq_r2r(mm7, mm5); + + punpcklwd_m2r(*(dataptr+11), mm7); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm6, mm2); + + punpcklwd_m2r(*(dataptr+15), mm6); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm7, mm1); + + movq_m2r(*(dataptr+11), mm3); // m13:m13|m11:m10 - second line + punpckldq_r2r(mm6, mm7); // m30:m20|m10:m00 - interleave to produce result 1 + + movq_m2r(*(dataptr+15), mm0); // m13:m13|m11:m10 - fourth line + punpckhdq_r2r(mm6, mm1); // m31:m21|m11:m01 - interleave to produce result 2 + + movq_r2m(mm7,*(dataptr+9)); // write result 1 + punpckhwd_r2r(mm3, mm5); // m13:m03|m12:m02 - interleave first and second lines + + movq_r2m(mm1,*(dataptr+11)); // write result 2 + punpckhwd_r2r(mm0, mm2); // m33:m23|m32:m22 - interleave third and fourth lines + + movq_r2r(mm5, mm1); + punpckldq_r2r(mm2, mm5); // m32:m22|m12:m02 - interleave to produce result 3 + + movq_m2r(*(dataptr+1), mm0); // m03:m02|m01:m00 - first line, 4x4 + punpckhdq_r2r(mm2, mm1); // m33:m23|m13:m03 - interleave to produce result 4 + + movq_r2m(mm5,*(dataptr+13)); // write result 3 + + // last 4x4 done + + movq_r2m(mm1, *(dataptr+15)); // write result 4, last 4x4 + + movq_m2r(*(dataptr+5), mm2); // m23:m22|m21:m20 - third line + movq_r2r(mm0, mm6); + + punpcklwd_m2r(*(dataptr+3), mm0); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm2, mm7); + + punpcklwd_m2r(*(dataptr+7), mm2); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm0, mm4); + + // + movq_m2r(*(dataptr+8), mm1); // n03:n02|n01:n00 - first line + punpckldq_r2r(mm2, mm0); // m30:m20|m10:m00 - interleave to produce first result + + movq_m2r(*(dataptr+12), mm3); // n23:n22|n21:n20 - third line + punpckhdq_r2r(mm2, mm4); // m31:m21|m11:m01 - interleave to produce second result + + punpckhwd_m2r(*(dataptr+3), mm6); // m13:m03|m12:m02 - interleave first and second lines + movq_r2r(mm1, mm2); // copy first line + + punpckhwd_m2r(*(dataptr+7), mm7); // m33:m23|m32:m22 - interleave third and fourth lines + movq_r2r(mm6, mm5); // copy first intermediate result + + movq_r2m(mm0, *(dataptr+8)); // write result 1 + punpckhdq_r2r(mm7, mm5); // m33:m23|m13:m03 - produce third result + + punpcklwd_m2r(*(dataptr+10), mm1); // n11:n01|n10:n00 - interleave first and second lines + movq_r2r(mm3, mm0); // copy third line + + punpckhwd_m2r(*(dataptr+10), mm2); // n13:n03|n12:n02 - interleave first and second lines + + movq_r2m(mm4, *(dataptr+10)); // write result 2 out + punpckldq_r2r(mm7, mm6); // m32:m22|m12:m02 - produce fourth result + + punpcklwd_m2r(*(dataptr+14), mm3); // n31:n21|n30:n20 - interleave third and fourth lines + movq_r2r(mm1, mm4); + + movq_r2m(mm6, *(dataptr+12)); // write result 3 out + punpckldq_r2r(mm3, mm1); // n30:n20|n10:n00 - produce first result + + punpckhwd_m2r(*(dataptr+14), mm0); // n33:n23|n32:n22 - interleave third and fourth lines + movq_r2r(mm2, mm6); + + movq_r2m(mm5, *(dataptr+14)); // write result 4 out + punpckhdq_r2r(mm3, mm4); // n31:n21|n11:n01- produce second result + + movq_r2m(mm1, *(dataptr+1)); // write result 5 out - (first result for other 4 x 4 block) + punpckldq_r2r(mm0, mm2); // n32:n22|n12:n02- produce third result + + movq_r2m(mm4, *(dataptr+3)); // write result 6 out + punpckhdq_r2r(mm0, mm6); // n33:n23|n13:n03 - produce fourth result + + movq_r2m(mm2, *(dataptr+5)); // write result 7 out + + movq_m2r(*dataptr, mm0); // m03:m02|m01:m00 - first line, first 4x4 + + movq_r2m(mm6, *(dataptr+7)); // write result 8 out + + +// Do first 4x4 quadrant, which is used in the beginning of the DCT: + + movq_m2r(*(dataptr+4), mm7); // m23:m22|m21:m20 - third line + movq_r2r(mm0, mm2); + + punpcklwd_m2r(*(dataptr+2), mm0); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm7, mm4); + + punpcklwd_m2r(*(dataptr+6), mm7); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm0, mm1); + + movq_m2r(*(dataptr+2), mm6); // m13:m12|m11:m10 - second line + punpckldq_r2r(mm7, mm0); // m30:m20|m10:m00 - interleave to produce result 1 + + movq_m2r(*(dataptr+6), mm5); // m33:m32|m31:m30 - fourth line + punpckhdq_r2r(mm7, mm1); // m31:m21|m11:m01 - interleave to produce result 2 + + movq_r2r(mm0, mm7); // write result 1 + punpckhwd_r2r(mm6, mm2); // m13:m03|m12:m02 - interleave first and second lines + + psubw_m2r(*(dataptr+14), mm7); // tmp07=x0-x7 /* Stage 1 */ + movq_r2r(mm1, mm6); // write result 2 + + paddw_m2r(*(dataptr+14), mm0); // tmp00=x0+x7 /* Stage 1 */ + punpckhwd_r2r(mm5, mm4); // m33:m23|m32:m22 - interleave third and fourth lines + + paddw_m2r(*(dataptr+12), mm1); // tmp01=x1+x6 /* Stage 1 */ + movq_r2r(mm2, mm3); // copy first intermediate result + + psubw_m2r(*(dataptr+12), mm6); // tmp06=x1-x6 /* Stage 1 */ + punpckldq_r2r(mm4, mm2); // m32:m22|m12:m02 - interleave to produce result 3 + + movq_r2m(mm7, tmp7); + movq_r2r(mm2, mm5); // write result 3 + + movq_r2m(mm6, tmp6); + punpckhdq_r2r(mm4, mm3); // m33:m23|m13:m03 - interleave to produce result 4 + + paddw_m2r(*(dataptr+10), mm2); // tmp02=x2+5 /* Stage 1 */ + movq_r2r(mm3, mm4); // write result 4 + +/************************************************************************************************ + End of Transpose +************************************************************************************************/ + + + paddw_m2r(*(dataptr+8), mm3); // tmp03=x3+x4 /* stage 1*/ + movq_r2r(mm0, mm7); + + psubw_m2r(*(dataptr+8), mm4); // tmp04=x3-x4 /* stage 1*/ + movq_r2r(mm1, mm6); + + paddw_r2r(mm3, mm0); // tmp10 = tmp00 + tmp03 /* even 2 */ + psubw_r2r(mm3, mm7); // tmp13 = tmp00 - tmp03 /* even 2 */ + + psubw_r2r(mm2, mm6); // tmp12 = tmp01 - tmp02 /* even 2 */ + paddw_r2r(mm2, mm1); // tmp11 = tmp01 + tmp02 /* even 2 */ + + psubw_m2r(*(dataptr+10), mm5); // tmp05=x2-x5 /* stage 1*/ + paddw_r2r(mm7, mm6); // tmp12 + tmp13 + + /* stage 3 */ + + movq_m2r(tmp6, mm2); + movq_r2r(mm0, mm3); + + psllw_i2r(2, mm6); // m8 * 2^2 + paddw_r2r(mm1, mm0); + + pmulhw_m2r(RTjpeg_C4, mm6); // z1 + psubw_r2r(mm1, mm3); + + movq_r2m(mm0, *dataptr); + movq_r2r(mm7, mm0); + + /* Odd part */ + movq_r2m(mm3, *(dataptr+8)); + paddw_r2r(mm5, mm4); // tmp10 + + movq_m2r(tmp7, mm3); + paddw_r2r(mm6, mm0); // tmp32 + + paddw_r2r(mm2, mm5); // tmp11 + psubw_r2r(mm6, mm7); // tmp33 + + movq_r2m(mm0, *(dataptr+4)); + paddw_r2r(mm3, mm2); // tmp12 + + /* stage 4 */ + + movq_r2m(mm7, *(dataptr+12)); + movq_r2r(mm4, mm1); // copy of tmp10 + + psubw_r2r(mm2, mm1); // tmp10 - tmp12 + psllw_i2r(2, mm4); // m8 * 2^2 + + movq_m2r(RTjpeg_C2mC6, mm0); + psllw_i2r(2, mm1); + + pmulhw_m2r(RTjpeg_C6, mm1); // z5 + psllw_i2r(2, mm2); + + pmulhw_r2r(mm0, mm4); // z5 + + /* stage 5 */ + + pmulhw_m2r(RTjpeg_C2pC6, mm2); + psllw_i2r(2, mm5); + + pmulhw_m2r(RTjpeg_C4, mm5); // z3 + movq_r2r(mm3, mm0); // copy tmp7 + + movq_m2r(*(dataptr+1), mm7); + paddw_r2r(mm1, mm4); // z2 + + paddw_r2r(mm1, mm2); // z4 + + paddw_r2r(mm5, mm0); // z11 + psubw_r2r(mm5, mm3); // z13 + + /* stage 6 */ + + movq_r2r(mm3, mm5); // copy z13 + psubw_r2r(mm4, mm3); // y3=z13 - z2 + + paddw_r2r(mm4, mm5); // y5=z13 + z2 + movq_r2r(mm0, mm6); // copy z11 + + movq_r2m(mm3, *(dataptr+6)); //save y3 + psubw_r2r(mm2, mm0); // y7=z11 - z4 + + movq_r2m(mm5, *(dataptr+10)); //save y5 + paddw_r2r(mm2, mm6); // y1=z11 + z4 + + movq_r2m(mm0, *(dataptr+14)); //save y7 + + /************************************************ + * End of 1st 4 rows + ************************************************/ + + movq_m2r(*(dataptr+3), mm1); // load x1 /* stage 1 */ + movq_r2r(mm7, mm0); // copy x0 + + movq_r2m(mm6, *(dataptr+2)); //save y1 + + movq_m2r(*(dataptr+5), mm2); // load x2 /* stage 1 */ + movq_r2r(mm1, mm6); // copy x1 + + paddw_m2r(*(dataptr+15), mm0); // tmp00 = x0 + x7 + + movq_m2r(*(dataptr+7), mm3); // load x3 /* stage 1 */ + movq_r2r(mm2, mm5); // copy x2 + + psubw_m2r(*(dataptr+15), mm7); // tmp07 = x0 - x7 + movq_r2r(mm3, mm4); // copy x3 + + paddw_m2r(*(dataptr+13), mm1); // tmp01 = x1 + x6 + + movq_r2m(mm7, tmp7); // save tmp07 + movq_r2r(mm0, mm7); // copy tmp00 + + psubw_m2r(*(dataptr+13), mm6); // tmp06 = x1 - x6 + + /* stage 2, Even Part */ + + paddw_m2r(*(dataptr+9), mm3); // tmp03 = x3 + x4 + + movq_r2m(mm6, tmp6); // save tmp07 + movq_r2r(mm1, mm6); // copy tmp01 + + paddw_m2r(*(dataptr+11), mm2); // tmp02 = x2 + x5 + paddw_r2r(mm3, mm0); // tmp10 = tmp00 + tmp03 + + psubw_r2r(mm3, mm7); // tmp13 = tmp00 - tmp03 + + psubw_m2r(*(dataptr+9), mm4); // tmp04 = x3 - x4 + psubw_r2r(mm2, mm6); // tmp12 = tmp01 - tmp02 + + paddw_r2r(mm2, mm1); // tmp11 = tmp01 + tmp02 + + psubw_m2r(*(dataptr+11), mm5); // tmp05 = x2 - x5 + paddw_r2r(mm7, mm6); // tmp12 + tmp13 + + /* stage 3, Even and stage 4 & 5 even */ + + movq_m2r(tmp6, mm2); // load tmp6 + movq_r2r(mm0, mm3); // copy tmp10 + + psllw_i2r(2, mm6); // shift z1 + paddw_r2r(mm1, mm0); // y0=tmp10 + tmp11 + + pmulhw_m2r(RTjpeg_C4, mm6); // z1 + psubw_r2r(mm1, mm3); // y4=tmp10 - tmp11 + + movq_r2m(mm0, *(dataptr+1)); //save y0 + movq_r2r(mm7, mm0); // copy tmp13 + + /* odd part */ + + movq_r2m(mm3, *(dataptr+9)); //save y4 + paddw_r2r(mm5, mm4); // tmp10 = tmp4 + tmp5 + + movq_m2r(tmp7, mm3); // load tmp7 + paddw_r2r(mm6, mm0); // tmp32 = tmp13 + z1 + + paddw_r2r(mm2, mm5); // tmp11 = tmp5 + tmp6 + psubw_r2r(mm6, mm7); // tmp33 = tmp13 - z1 + + movq_r2m(mm0, *(dataptr+5)); //save y2 + paddw_r2r(mm3, mm2); // tmp12 = tmp6 + tmp7 + + /* stage 4 */ + + movq_r2m(mm7, *(dataptr+13)); //save y6 + movq_r2r(mm4, mm1); // copy tmp10 + + psubw_r2r(mm2, mm1); // tmp10 - tmp12 + psllw_i2r(2, mm4); // shift tmp10 + + movq_m2r(RTjpeg_C2mC6, mm0); // load C2mC6 + psllw_i2r(2, mm1); // shift (tmp10-tmp12) + + pmulhw_m2r(RTjpeg_C6, mm1); // z5 + psllw_i2r(2, mm5); // prepare for multiply + + pmulhw_r2r(mm0, mm4); // multiply by converted real + + /* stage 5 */ + + pmulhw_m2r(RTjpeg_C4, mm5); // z3 + psllw_i2r(2, mm2); // prepare for multiply + + pmulhw_m2r(RTjpeg_C2pC6, mm2); // multiply + movq_r2r(mm3, mm0); // copy tmp7 + + movq_m2r(*(dataptr+9), mm7); // m03:m02|m01:m00 - first line (line 4)and copy into mm7 + paddw_r2r(mm1, mm4); // z2 + + paddw_r2r(mm5, mm0); // z11 + psubw_r2r(mm5, mm3); // z13 + + /* stage 6 */ + + movq_r2r(mm3, mm5); // copy z13 + paddw_r2r(mm1, mm2); // z4 + + movq_r2r(mm0, mm6); // copy z11 + psubw_r2r(mm4, mm5); // y3 + + paddw_r2r(mm2, mm6); // y1 + paddw_r2r(mm4, mm3); // y5 + + movq_r2m(mm5, *(dataptr+7)); //save y3 + + movq_r2m(mm6, *(dataptr+3)); //save y1 + psubw_r2r(mm2, mm0); // y7 + +/************************************************************************************************ + Start of Transpose +************************************************************************************************/ + + movq_m2r(*(dataptr+13), mm6); // m23:m22|m21:m20 - third line (line 6)and copy into m2 + movq_r2r(mm7, mm5); // copy first line + + punpcklwd_r2r(mm3, mm7); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm6, mm2); // copy third line + + punpcklwd_r2r(mm0, mm6); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm7, mm1); // copy first intermediate result + + punpckldq_r2r(mm6, mm7); // m30:m20|m10:m00 - interleave to produce result 1 + + punpckhdq_r2r(mm6, mm1); // m31:m21|m11:m01 - interleave to produce result 2 + + movq_r2m(mm7, *(dataptr+9)); // write result 1 + punpckhwd_r2r(mm3, mm5); // m13:m03|m12:m02 - interleave first and second lines + + movq_r2m(mm1, *(dataptr+11)); // write result 2 + punpckhwd_r2r(mm0, mm2); // m33:m23|m32:m22 - interleave third and fourth lines + + movq_r2r(mm5, mm1); // copy first intermediate result + punpckldq_r2r(mm2, mm5); // m32:m22|m12:m02 - interleave to produce result 3 + + movq_m2r(*(dataptr+1), mm0); // m03:m02|m01:m00 - first line, 4x4 + punpckhdq_r2r(mm2, mm1); // m33:m23|m13:m03 - interleave to produce result 4 + + movq_r2m(mm5, *(dataptr+13)); // write result 3 + + /****** last 4x4 done */ + + movq_r2m(mm1, *(dataptr+15)); // write result 4, last 4x4 + + movq_m2r(*(dataptr+5), mm2); // m23:m22|m21:m20 - third line + movq_r2r(mm0, mm6); // copy first line + + punpcklwd_m2r(*(dataptr+3), mm0); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm2, mm7); // copy third line + + punpcklwd_m2r(*(dataptr+7), mm2); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm0, mm4); // copy first intermediate result + + + + movq_m2r(*(dataptr+8), mm1); // n03:n02|n01:n00 - first line + punpckldq_r2r(mm2, mm0); // m30:m20|m10:m00 - interleave to produce first result + + movq_m2r(*(dataptr+12), mm3); // n23:n22|n21:n20 - third line + punpckhdq_r2r(mm2, mm4); // m31:m21|m11:m01 - interleave to produce second result + + punpckhwd_m2r(*(dataptr+3), mm6); // m13:m03|m12:m02 - interleave first and second lines + movq_r2r(mm1, mm2); // copy first line + + punpckhwd_m2r(*(dataptr+7), mm7); // m33:m23|m32:m22 - interleave third and fourth lines + movq_r2r(mm6, mm5); // copy first intermediate result + + movq_r2m(mm0, *(dataptr+8)); // write result 1 + punpckhdq_r2r(mm7, mm5); // m33:m23|m13:m03 - produce third result + + punpcklwd_m2r(*(dataptr+10), mm1); // n11:n01|n10:n00 - interleave first and second lines + movq_r2r(mm3, mm0); // copy third line + + punpckhwd_m2r(*(dataptr+10), mm2); // n13:n03|n12:n02 - interleave first and second lines + + movq_r2m(mm4, *(dataptr+10)); // write result 2 out + punpckldq_r2r(mm7, mm6); // m32:m22|m12:m02 - produce fourth result + + punpcklwd_m2r(*(dataptr+14), mm3); // n33:n23|n32:n22 - interleave third and fourth lines + movq_r2r(mm1, mm4); // copy second intermediate result + + movq_r2m(mm6, *(dataptr+12)); // write result 3 out + punpckldq_r2r(mm3, mm1); // + + punpckhwd_m2r(*(dataptr+14), mm0); // n33:n23|n32:n22 - interleave third and fourth lines + movq_r2r(mm2, mm6); // copy second intermediate result + + movq_r2m(mm5, *(dataptr+14)); // write result 4 out + punpckhdq_r2r(mm3, mm4); // n31:n21|n11:n01- produce second result + + movq_r2m(mm1, *(dataptr+1)); // write result 5 out - (first result for other 4 x 4 block) + punpckldq_r2r(mm0, mm2); // n32:n22|n12:n02- produce third result + + movq_r2m(mm4, *(dataptr+3)); // write result 6 out + punpckhdq_r2r(mm0, mm6); // n33:n23|n13:n03 - produce fourth result + + movq_r2m(mm2, *(dataptr+5)); // write result 7 out + + movq_m2r(*dataptr, mm0); // m03:m02|m01:m00 - first line, first 4x4 + + movq_r2m(mm6, *(dataptr+7)); // write result 8 out + +// Do first 4x4 quadrant, which is used in the beginning of the DCT: + + movq_m2r(*(dataptr+4), mm7); // m23:m22|m21:m20 - third line + movq_r2r(mm0, mm2); // copy first line + + punpcklwd_m2r(*(dataptr+2), mm0); // m11:m01|m10:m00 - interleave first and second lines + movq_r2r(mm7, mm4); // copy third line + + punpcklwd_m2r(*(dataptr+6), mm7); // m31:m21|m30:m20 - interleave third and fourth lines + movq_r2r(mm0, mm1); // copy first intermediate result + + movq_m2r(*(dataptr+2), mm6); // m13:m12|m11:m10 - second line + punpckldq_r2r(mm7, mm0); // m30:m20|m10:m00 - interleave to produce result 1 + + movq_m2r(*(dataptr+6), mm5); // m33:m32|m31:m30 - fourth line + punpckhdq_r2r(mm7, mm1); // m31:m21|m11:m01 - interleave to produce result 2 + + movq_r2r(mm0, mm7); // write result 1 + punpckhwd_r2r(mm6, mm2); // m13:m03|m12:m02 - interleave first and second lines + + psubw_m2r(*(dataptr+14), mm7); // tmp07=x0-x7 /* Stage 1 */ + movq_r2r(mm1, mm6); // write result 2 + + paddw_m2r(*(dataptr+14), mm0); // tmp00=x0+x7 /* Stage 1 */ + punpckhwd_r2r(mm5, mm4); // m33:m23|m32:m22 - interleave third and fourth lines + + paddw_m2r(*(dataptr+12), mm1); // tmp01=x1+x6 /* Stage 1 */ + movq_r2r(mm2, mm3); // copy first intermediate result + + psubw_m2r(*(dataptr+12), mm6); // tmp06=x1-x6 /* Stage 1 */ + punpckldq_r2r(mm4, mm2); // m32:m22|m12:m02 - interleave to produce result 3 + + movq_r2m(mm7, tmp7); // save tmp07 + movq_r2r(mm2, mm5); // write result 3 + + movq_r2m(mm6, tmp6); // save tmp06 + + punpckhdq_r2r(mm4, mm3); // m33:m23|m13:m03 - interleave to produce result 4 + + paddw_m2r(*(dataptr+10), mm2); // tmp02=x2+x5 /* stage 1 */ + movq_r2r(mm3, mm4); // write result 4 + +/************************************************************************************************ + End of Transpose 2 +************************************************************************************************/ + + paddw_m2r(*(dataptr+8), mm3); // tmp03=x3+x4 /* stage 1*/ + movq_r2r(mm0, mm7); + + psubw_m2r(*(dataptr+8), mm4); // tmp04=x3-x4 /* stage 1*/ + movq_r2r(mm1, mm6); + + paddw_r2r(mm3, mm0); // tmp10 = tmp00 + tmp03 /* even 2 */ + psubw_r2r(mm3, mm7); // tmp13 = tmp00 - tmp03 /* even 2 */ + + psubw_r2r(mm2, mm6); // tmp12 = tmp01 - tmp02 /* even 2 */ + paddw_r2r(mm2, mm1); // tmp11 = tmp01 + tmp02 /* even 2 */ + + psubw_m2r(*(dataptr+10), mm5); // tmp05=x2-x5 /* stage 1*/ + paddw_r2r(mm7, mm6); // tmp12 + tmp13 + + /* stage 3 */ + + movq_m2r(tmp6, mm2); + movq_r2r(mm0, mm3); + + psllw_i2r(2, mm6); // m8 * 2^2 + paddw_r2r(mm1, mm0); + + pmulhw_m2r(RTjpeg_C4, mm6); // z1 + psubw_r2r(mm1, mm3); + + movq_r2m(mm0, *dataptr); + movq_r2r(mm7, mm0); + + /* Odd part */ + movq_r2m(mm3, *(dataptr+8)); + paddw_r2r(mm5, mm4); // tmp10 + + movq_m2r(tmp7, mm3); + paddw_r2r(mm6, mm0); // tmp32 + + paddw_r2r(mm2, mm5); // tmp11 + psubw_r2r(mm6, mm7); // tmp33 + + movq_r2m(mm0, *(dataptr+4)); + paddw_r2r(mm3, mm2); // tmp12 + + /* stage 4 */ + movq_r2m(mm7, *(dataptr+12)); + movq_r2r(mm4, mm1); // copy of tmp10 + + psubw_r2r(mm2, mm1); // tmp10 - tmp12 + psllw_i2r(2, mm4); // m8 * 2^2 + + movq_m2r(RTjpeg_C2mC6, mm0); + psllw_i2r(2, mm1); + + pmulhw_m2r(RTjpeg_C6, mm1); // z5 + psllw_i2r(2, mm2); + + pmulhw_r2r(mm0, mm4); // z5 + + /* stage 5 */ + + pmulhw_m2r(RTjpeg_C2pC6, mm2); + psllw_i2r(2, mm5); + + pmulhw_m2r(RTjpeg_C4, mm5); // z3 + movq_r2r(mm3, mm0); // copy tmp7 + + movq_m2r(*(dataptr+1), mm7); + paddw_r2r(mm1, mm4); // z2 + + paddw_r2r(mm1, mm2); // z4 + + paddw_r2r(mm5, mm0); // z11 + psubw_r2r(mm5, mm3); // z13 + + /* stage 6 */ + + movq_r2r(mm3, mm5); // copy z13 + psubw_r2r(mm4, mm3); // y3=z13 - z2 + + paddw_r2r(mm4, mm5); // y5=z13 + z2 + movq_r2r(mm0, mm6); // copy z11 + + movq_r2m(mm3, *(dataptr+6)); //save y3 + psubw_r2r(mm2, mm0); // y7=z11 - z4 + + movq_r2m(mm5, *(dataptr+10)); //save y5 + paddw_r2r(mm2, mm6); // y1=z11 + z4 + + movq_r2m(mm0, *(dataptr+14)); //save y7 + + /************************************************ + * End of 1st 4 rows + ************************************************/ + + movq_m2r(*(dataptr+3), mm1); // load x1 /* stage 1 */ + movq_r2r(mm7, mm0); // copy x0 + + movq_r2m(mm6, *(dataptr+2)); //save y1 + + movq_m2r(*(dataptr+5), mm2); // load x2 /* stage 1 */ + movq_r2r(mm1, mm6); // copy x1 + + paddw_m2r(*(dataptr+15), mm0); // tmp00 = x0 + x7 + + movq_m2r(*(dataptr+7), mm3); // load x3 /* stage 1 */ + movq_r2r(mm2, mm5); // copy x2 + + psubw_m2r(*(dataptr+15), mm7); // tmp07 = x0 - x7 + movq_r2r(mm3, mm4); // copy x3 + + paddw_m2r(*(dataptr+13), mm1); // tmp01 = x1 + x6 + + movq_r2m(mm7, tmp7); // save tmp07 + movq_r2r(mm0, mm7); // copy tmp00 + + psubw_m2r(*(dataptr+13), mm6); // tmp06 = x1 - x6 + + /* stage 2, Even Part */ + + paddw_m2r(*(dataptr+9), mm3); // tmp03 = x3 + x4 + + movq_r2m(mm6, tmp6); // save tmp07 + movq_r2r(mm1, mm6); // copy tmp01 + + paddw_m2r(*(dataptr+11), mm2); // tmp02 = x2 + x5 + paddw_r2r(mm3, mm0); // tmp10 = tmp00 + tmp03 + + psubw_r2r(mm3, mm7); // tmp13 = tmp00 - tmp03 + + psubw_m2r(*(dataptr+9), mm4); // tmp04 = x3 - x4 + psubw_r2r(mm2, mm6); // tmp12 = tmp01 - tmp02 + + paddw_r2r(mm2, mm1); // tmp11 = tmp01 + tmp02 + + psubw_m2r(*(dataptr+11), mm5); // tmp05 = x2 - x5 + paddw_r2r(mm7, mm6); // tmp12 + tmp13 + + /* stage 3, Even and stage 4 & 5 even */ + + movq_m2r(tmp6, mm2); // load tmp6 + movq_r2r(mm0, mm3); // copy tmp10 + + psllw_i2r(2, mm6); // shift z1 + paddw_r2r(mm1, mm0); // y0=tmp10 + tmp11 + + pmulhw_m2r(RTjpeg_C4, mm6); // z1 + psubw_r2r(mm1, mm3); // y4=tmp10 - tmp11 + + movq_r2m(mm0, *(dataptr+1)); //save y0 + movq_r2r(mm7, mm0); // copy tmp13 + + /* odd part */ + + movq_r2m(mm3, *(dataptr+9)); //save y4 + paddw_r2r(mm5, mm4); // tmp10 = tmp4 + tmp5 + + movq_m2r(tmp7, mm3); // load tmp7 + paddw_r2r(mm6, mm0); // tmp32 = tmp13 + z1 + + paddw_r2r(mm2, mm5); // tmp11 = tmp5 + tmp6 + psubw_r2r(mm6, mm7); // tmp33 = tmp13 - z1 + + movq_r2m(mm0, *(dataptr+5)); //save y2 + paddw_r2r(mm3, mm2); // tmp12 = tmp6 + tmp7 + + /* stage 4 */ + + movq_r2m(mm7, *(dataptr+13)); //save y6 + movq_r2r(mm4, mm1); // copy tmp10 + + psubw_r2r(mm2, mm1); // tmp10 - tmp12 + psllw_i2r(2, mm4); // shift tmp10 + + movq_m2r(RTjpeg_C2mC6, mm0); // load C2mC6 + psllw_i2r(2, mm1); // shift (tmp10-tmp12) + + pmulhw_m2r(RTjpeg_C6, mm1); // z5 + psllw_i2r(2, mm5); // prepare for multiply + + pmulhw_r2r(mm0, mm4); // multiply by converted real + + /* stage 5 */ + + pmulhw_m2r(RTjpeg_C4, mm5); // z3 + psllw_i2r(2, mm2); // prepare for multiply + + pmulhw_m2r(RTjpeg_C2pC6, mm2); // multiply + movq_r2r(mm3, mm0); // copy tmp7 + + movq_m2r(*(dataptr+9), mm7); // m03:m02|m01:m00 - first line (line 4)and copy into mm7 + paddw_r2r(mm1, mm4); // z2 + + paddw_r2r(mm5, mm0); // z11 + psubw_r2r(mm5, mm3); // z13 + + /* stage 6 */ + + movq_r2r(mm3, mm5); // copy z13 + paddw_r2r(mm1, mm2); // z4 + + movq_r2r(mm0, mm6); // copy z11 + psubw_r2r(mm4, mm5); // y3 + + paddw_r2r(mm2, mm6); // y1 + paddw_r2r(mm4, mm3); // y5 + + movq_r2m(mm5, *(dataptr+7)); //save y3 + psubw_r2r(mm2, mm0); // yè=z11 - z4 + + movq_r2m(mm3, *(dataptr+11)); //save y5 + + movq_r2m(mm6, *(dataptr+3)); //save y1 + + movq_r2m(mm0, *(dataptr+15)); //save y7 + + +#endif +} + +#define FIX_1_082392200 ((__s32) 277) /* FIX(1.082392200) */ +#define FIX_1_414213562 ((__s32) 362) /* FIX(1.414213562) */ +#define FIX_1_847759065 ((__s32) 473) /* FIX(1.847759065) */ +#define FIX_2_613125930 ((__s32) 669) /* FIX(2.613125930) */ + +#define DESCALE(x) (__s16)( ((x)+4) >> 3) + +/* clip yuv to 16..235 (should be 16..240 for cr/cb but ... */ + +#define RL(x) ((x)>235) ? 235 : (((x)<16) ? 16 : (x)) +#define MULTIPLY(var,const) (((__s32) ((var) * (const)) + 128)>>8) + +void RTjpeg_idct_init(void) +{ + int i; + + for(i=0; i<64; i++) + { + RTjpeg_liqt[i]=((__u64)RTjpeg_liqt[i]*RTjpeg_aan_tab[i])>>32; + RTjpeg_ciqt[i]=((__u64)RTjpeg_ciqt[i]*RTjpeg_aan_tab[i])>>32; + } +} + +void RTjpeg_idct(__u8 *odata, __s16 *data, int rskip) +{ +#ifdef HAVE_LIBMMX + +static mmx_t fix_141 = (mmx_t)(long long)0x5a825a825a825a82LL; +static mmx_t fix_184n261 = (mmx_t)(long long)0xcf04cf04cf04cf04LL; +static mmx_t fix_184 = (mmx_t)(long long)0x7641764176417641LL; +static mmx_t fix_n184 = (mmx_t)(long long)0x896f896f896f896fLL; +static mmx_t fix_108n184 = (mmx_t)(long long)0xcf04cf04cf04cf04LL; + + mmx_t workspace[64]; + mmx_t *wsptr = workspace; + register mmx_t *dataptr = (mmx_t *)odata; + mmx_t *idata = (mmx_t *)data; + + rskip = rskip>>3; +/* + * Perform inverse DCT on one block of coefficients. + */ + + /* Odd part */ + + movq_m2r(*(idata+10), mm1); // load idata[DCTSIZE*5] + + movq_m2r(*(idata+6), mm0); // load idata[DCTSIZE*3] + + movq_m2r(*(idata+2), mm3); // load idata[DCTSIZE*1] + + movq_r2r(mm1, mm2); // copy tmp6 /* phase 6 */ + + movq_m2r(*(idata+14), mm4); // load idata[DCTSIZE*7] + + paddw_r2r(mm0, mm1); // z13 = tmp6 + tmp5; + + psubw_r2r(mm0, mm2); // z10 = tmp6 - tmp5 + + psllw_i2r(2, mm2); // shift z10 + movq_r2r(mm2, mm0); // copy z10 + + pmulhw_m2r(fix_184n261, mm2); // MULTIPLY( z12, FIX_1_847759065); /* 2*c2 */ + movq_r2r(mm3, mm5); // copy tmp4 + + pmulhw_m2r(fix_n184, mm0); // MULTIPLY(z10, -FIX_1_847759065); /* 2*c2 */ + paddw_r2r(mm4, mm3); // z11 = tmp4 + tmp7; + + movq_r2r(mm3, mm6); // copy z11 /* phase 5 */ + psubw_r2r(mm4, mm5); // z12 = tmp4 - tmp7; + + psubw_r2r(mm1, mm6); // z11-z13 + psllw_i2r(2, mm5); // shift z12 + + movq_m2r(*(idata+12), mm4); // load idata[DCTSIZE*6], even part + movq_r2r(mm5, mm7); // copy z12 + + pmulhw_m2r(fix_108n184, mm5); // MULT(z12, (FIX_1_08-FIX_1_84)) //- z5; /* 2*(c2-c6) */ even part + paddw_r2r(mm1, mm3); // tmp7 = z11 + z13; + + //ok + + /* Even part */ + pmulhw_m2r(fix_184, mm7); // MULTIPLY(z10,(FIX_1_847759065 - FIX_2_613125930)) //+ z5; /* -2*(c2+c6) */ + psllw_i2r(2, mm6); + + movq_m2r(*(idata+4), mm1); // load idata[DCTSIZE*2] + + paddw_r2r(mm5, mm0); // tmp10 + + paddw_r2r(mm7, mm2); // tmp12 + + pmulhw_m2r(fix_141, mm6); // tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + psubw_r2r(mm3, mm2); // tmp6 = tmp12 - tmp7 + + movq_r2r(mm1, mm5); // copy tmp1 + paddw_r2r(mm4, mm1); // tmp13= tmp1 + tmp3; /* phases 5-3 */ + + psubw_r2r(mm4, mm5); // tmp1-tmp3 + psubw_r2r(mm2, mm6); // tmp5 = tmp11 - tmp6; + + movq_r2m(mm1, *(wsptr)); // save tmp13 in workspace + psllw_i2r(2, mm5); // shift tmp1-tmp3 + + movq_m2r(*(idata), mm7); // load idata[DCTSIZE*0] + + pmulhw_m2r(fix_141, mm5); // MULTIPLY(tmp1 - tmp3, FIX_1_414213562) + paddw_r2r(mm6, mm0); // tmp4 = tmp10 + tmp5; + + movq_m2r(*(idata+8), mm4); // load idata[DCTSIZE*4] + + psubw_r2r(mm1, mm5); // tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + movq_r2m(mm0, *(wsptr+4)); // save tmp4 in workspace + movq_r2r(mm7, mm1); // copy tmp0 /* phase 3 */ + + movq_r2m(mm5, *(wsptr+2)); // save tmp12 in workspace + psubw_r2r(mm4, mm1); // tmp11 = tmp0 - tmp2; + + paddw_r2r(mm4, mm7); // tmp10 = tmp0 + tmp2; + movq_r2r(mm1, mm5); // copy tmp11 + + paddw_m2r(*(wsptr+2), mm1); // tmp1 = tmp11 + tmp12; + movq_r2r(mm7, mm4); // copy tmp10 /* phase 2 */ + + paddw_m2r(*(wsptr), mm7); // tmp0 = tmp10 + tmp13; + + psubw_m2r(*(wsptr), mm4); // tmp3 = tmp10 - tmp13; + movq_r2r(mm7, mm0); // copy tmp0 + + psubw_m2r(*(wsptr+2), mm5); // tmp2 = tmp11 - tmp12; + paddw_r2r(mm3, mm7); // wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + + psubw_r2r(mm3, mm0); // wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + + movq_r2m(mm7, *(wsptr)); // wsptr[DCTSIZE*0] + movq_r2r(mm1, mm3); // copy tmp1 + + movq_r2m(mm0, *(wsptr+14)); // wsptr[DCTSIZE*7] + paddw_r2r(mm2, mm1); // wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + + psubw_r2r(mm2, mm3); // wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + + movq_r2m(mm1, *(wsptr+2)); // wsptr[DCTSIZE*1] + movq_r2r(mm4, mm1); // copy tmp3 + + movq_r2m(mm3, *(wsptr+12)); // wsptr[DCTSIZE*6] + + paddw_m2r(*(wsptr+4), mm4); // wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + + psubw_m2r(*(wsptr+4), mm1); // wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + movq_r2m(mm4, *(wsptr+8)); + movq_r2r(mm5, mm7); // copy tmp2 + + paddw_r2r(mm6, mm5); // wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5) + + movq_r2m(mm1, *(wsptr+6)); + psubw_r2r(mm6, mm7); // wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + + movq_r2m(mm5, *(wsptr+4)); + + movq_r2m(mm7, *(wsptr+10)); + + //ok + + +/*****************************************************************/ + + idata++; + wsptr++; + +/*****************************************************************/ + + movq_m2r(*(idata+10), mm1); // load idata[DCTSIZE*5] + + movq_m2r(*(idata+6), mm0); // load idata[DCTSIZE*3] + + movq_m2r(*(idata+2), mm3); // load idata[DCTSIZE*1] + movq_r2r(mm1, mm2); // copy tmp6 /* phase 6 */ + + movq_m2r(*(idata+14), mm4); // load idata[DCTSIZE*7] + paddw_r2r(mm0, mm1); // z13 = tmp6 + tmp5; + + psubw_r2r(mm0, mm2); // z10 = tmp6 - tmp5 + + psllw_i2r(2, mm2); // shift z10 + movq_r2r(mm2, mm0); // copy z10 + + pmulhw_m2r(fix_184n261, mm2); // MULTIPLY( z12, FIX_1_847759065); /* 2*c2 */ + movq_r2r(mm3, mm5); // copy tmp4 + + pmulhw_m2r(fix_n184, mm0); // MULTIPLY(z10, -FIX_1_847759065); /* 2*c2 */ + paddw_r2r(mm4, mm3); // z11 = tmp4 + tmp7; + + movq_r2r(mm3, mm6); // copy z11 /* phase 5 */ + psubw_r2r(mm4, mm5); // z12 = tmp4 - tmp7; + + psubw_r2r(mm1, mm6); // z11-z13 + psllw_i2r(2, mm5); // shift z12 + + movq_m2r(*(idata+12), mm4); // load idata[DCTSIZE*6], even part + movq_r2r(mm5, mm7); // copy z12 + + pmulhw_m2r(fix_108n184, mm5); // MULT(z12, (FIX_1_08-FIX_1_84)) //- z5; /* 2*(c2-c6) */ even part + paddw_r2r(mm1, mm3); // tmp7 = z11 + z13; + + //ok + + /* Even part */ + pmulhw_m2r(fix_184, mm7); // MULTIPLY(z10,(FIX_1_847759065 - FIX_2_613125930)) //+ z5; /* -2*(c2+c6) */ + psllw_i2r(2, mm6); + + movq_m2r(*(idata+4), mm1); // load idata[DCTSIZE*2] + + paddw_r2r(mm5, mm0); // tmp10 + + paddw_r2r(mm7, mm2); // tmp12 + + pmulhw_m2r(fix_141, mm6); // tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + psubw_r2r(mm3, mm2); // tmp6 = tmp12 - tmp7 + + movq_r2r(mm1, mm5); // copy tmp1 + paddw_r2r(mm4, mm1); // tmp13= tmp1 + tmp3; /* phases 5-3 */ + + psubw_r2r(mm4, mm5); // tmp1-tmp3 + psubw_r2r(mm2, mm6); // tmp5 = tmp11 - tmp6; + + movq_r2m(mm1, *(wsptr)); // save tmp13 in workspace + psllw_i2r(2, mm5); // shift tmp1-tmp3 + + movq_m2r(*(idata), mm7); // load idata[DCTSIZE*0] + paddw_r2r(mm6, mm0); // tmp4 = tmp10 + tmp5; + + pmulhw_m2r(fix_141, mm5); // MULTIPLY(tmp1 - tmp3, FIX_1_414213562) + + movq_m2r(*(idata+8), mm4); // load idata[DCTSIZE*4] + + psubw_r2r(mm1, mm5); // tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + movq_r2m(mm0, *(wsptr+4)); // save tmp4 in workspace + movq_r2r(mm7, mm1); // copy tmp0 /* phase 3 */ + + movq_r2m(mm5, *(wsptr+2)); // save tmp12 in workspace + psubw_r2r(mm4, mm1); // tmp11 = tmp0 - tmp2; + + paddw_r2r(mm4, mm7); // tmp10 = tmp0 + tmp2; + movq_r2r(mm1, mm5); // copy tmp11 + + paddw_m2r(*(wsptr+2), mm1); // tmp1 = tmp11 + tmp12; + movq_r2r(mm7, mm4); // copy tmp10 /* phase 2 */ + + paddw_m2r(*(wsptr), mm7); // tmp0 = tmp10 + tmp13; + + psubw_m2r(*(wsptr), mm4); // tmp3 = tmp10 - tmp13; + movq_r2r(mm7, mm0); // copy tmp0 + + psubw_m2r(*(wsptr+2), mm5); // tmp2 = tmp11 - tmp12; + paddw_r2r(mm3, mm7); // wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + + psubw_r2r(mm3, mm0); // wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + + movq_r2m(mm7, *(wsptr)); // wsptr[DCTSIZE*0] + movq_r2r(mm1, mm3); // copy tmp1 + + movq_r2m(mm0, *(wsptr+14)); // wsptr[DCTSIZE*7] + paddw_r2r(mm2, mm1); // wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + + psubw_r2r(mm2, mm3); // wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + + movq_r2m(mm1, *(wsptr+2)); // wsptr[DCTSIZE*1] + movq_r2r(mm4, mm1); // copy tmp3 + + movq_r2m(mm3, *(wsptr+12)); // wsptr[DCTSIZE*6] + + paddw_m2r(*(wsptr+4), mm4); // wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + + psubw_m2r(*(wsptr+4), mm1); // wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + movq_r2m(mm4, *(wsptr+8)); + movq_r2r(mm5, mm7); // copy tmp2 + + paddw_r2r(mm6, mm5); // wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5) + + movq_r2m(mm1, *(wsptr+6)); + psubw_r2r(mm6, mm7); // wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + + movq_r2m(mm5, *(wsptr+4)); + + movq_r2m(mm7, *(wsptr+10)); + +/*****************************************************************/ + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + +/*****************************************************************/ + /* Even part */ + + wsptr--; + +// tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); +// tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); +// tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); +// tmp14 = ((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6]); + movq_m2r(*(wsptr), mm0); // wsptr[0,0],[0,1],[0,2],[0,3] + + movq_m2r(*(wsptr+1), mm1); // wsptr[0,4],[0,5],[0,6],[0,7] + movq_r2r(mm0, mm2); + + movq_m2r(*(wsptr+2), mm3); // wsptr[1,0],[1,1],[1,2],[1,3] + paddw_r2r(mm1, mm0); // wsptr[0,tmp10],[xxx],[0,tmp13],[xxx] + + movq_m2r(*(wsptr+3), mm4); // wsptr[1,4],[1,5],[1,6],[1,7] + psubw_r2r(mm1, mm2); // wsptr[0,tmp11],[xxx],[0,tmp14],[xxx] + + movq_r2r(mm0, mm6); + movq_r2r(mm3, mm5); + + paddw_r2r(mm4, mm3); // wsptr[1,tmp10],[xxx],[1,tmp13],[xxx] + movq_r2r(mm2, mm1); + + psubw_r2r(mm4, mm5); // wsptr[1,tmp11],[xxx],[1,tmp14],[xxx] + punpcklwd_r2r(mm3, mm0); // wsptr[0,tmp10],[1,tmp10],[xxx],[xxx] + + movq_m2r(*(wsptr+7), mm7); // wsptr[3,4],[3,5],[3,6],[3,7] + punpckhwd_r2r(mm3, mm6); // wsptr[0,tmp13],[1,tmp13],[xxx],[xxx] + + movq_m2r(*(wsptr+4), mm3); // wsptr[2,0],[2,1],[2,2],[2,3] + punpckldq_r2r(mm6, mm0); // wsptr[0,tmp10],[1,tmp10],[0,tmp13],[1,tmp13] + + punpcklwd_r2r(mm5, mm1); // wsptr[0,tmp11],[1,tmp11],[xxx],[xxx] + movq_r2r(mm3, mm4); + + movq_m2r(*(wsptr+6), mm6); // wsptr[3,0],[3,1],[3,2],[3,3] + punpckhwd_r2r(mm5, mm2); // wsptr[0,tmp14],[1,tmp14],[xxx],[xxx] + + movq_m2r(*(wsptr+5), mm5); // wsptr[2,4],[2,5],[2,6],[2,7] + punpckldq_r2r(mm2, mm1); // wsptr[0,tmp11],[1,tmp11],[0,tmp14],[1,tmp14] + + + paddw_r2r(mm5, mm3); // wsptr[2,tmp10],[xxx],[2,tmp13],[xxx] + movq_r2r(mm6, mm2); + + psubw_r2r(mm5, mm4); // wsptr[2,tmp11],[xxx],[2,tmp14],[xxx] + paddw_r2r(mm7, mm6); // wsptr[3,tmp10],[xxx],[3,tmp13],[xxx] + + movq_r2r(mm3, mm5); + punpcklwd_r2r(mm6, mm3); // wsptr[2,tmp10],[3,tmp10],[xxx],[xxx] + + psubw_r2r(mm7, mm2); // wsptr[3,tmp11],[xxx],[3,tmp14],[xxx] + punpckhwd_r2r(mm6, mm5); // wsptr[2,tmp13],[3,tmp13],[xxx],[xxx] + + movq_r2r(mm4, mm7); + punpckldq_r2r(mm5, mm3); // wsptr[2,tmp10],[3,tmp10],[2,tmp13],[3,tmp13] + + punpcklwd_r2r(mm2, mm4); // wsptr[2,tmp11],[3,tmp11],[xxx],[xxx] + + punpckhwd_r2r(mm2, mm7); // wsptr[2,tmp14],[3,tmp14],[xxx],[xxx] + + punpckldq_r2r(mm7, mm4); // wsptr[2,tmp11],[3,tmp11],[2,tmp14],[3,tmp14] + movq_r2r(mm1, mm6); + + //ok + +// mm0 = ;wsptr[0,tmp10],[1,tmp10],[0,tmp13],[1,tmp13] +// mm1 = ;wsptr[0,tmp11],[1,tmp11],[0,tmp14],[1,tmp14] + + + movq_r2r(mm0, mm2); + punpckhdq_r2r(mm4, mm6); // wsptr[0,tmp14],[1,tmp14],[2,tmp14],[3,tmp14] + + punpckldq_r2r(mm4, mm1); // wsptr[0,tmp11],[1,tmp11],[2,tmp11],[3,tmp11] + psllw_i2r(2, mm6); + + pmulhw_m2r(fix_141, mm6); + punpckldq_r2r(mm3, mm0); // wsptr[0,tmp10],[1,tmp10],[2,tmp10],[3,tmp10] + + punpckhdq_r2r(mm3, mm2); // wsptr[0,tmp13],[1,tmp13],[2,tmp13],[3,tmp13] + movq_r2r(mm0, mm7); + +// tmp0 = tmp10 + tmp13; +// tmp3 = tmp10 - tmp13; + paddw_r2r(mm2, mm0); // [0,tmp0],[1,tmp0],[2,tmp0],[3,tmp0] + psubw_r2r(mm2, mm7); // [0,tmp3],[1,tmp3],[2,tmp3],[3,tmp3] + +// tmp12 = MULTIPLY(tmp14, FIX_1_414213562) - tmp13; + psubw_r2r(mm2, mm6); // wsptr[0,tmp12],[1,tmp12],[2,tmp12],[3,tmp12] +// tmp1 = tmp11 + tmp12; +// tmp2 = tmp11 - tmp12; + movq_r2r(mm1, mm5); + + //OK + + /* Odd part */ + +// z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; +// z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; +// z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; +// z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + movq_m2r(*(wsptr), mm3); // wsptr[0,0],[0,1],[0,2],[0,3] + paddw_r2r(mm6, mm1); // [0,tmp1],[1,tmp1],[2,tmp1],[3,tmp1] + + movq_m2r(*(wsptr+1), mm4); // wsptr[0,4],[0,5],[0,6],[0,7] + psubw_r2r(mm6, mm5); // [0,tmp2],[1,tmp2],[2,tmp2],[3,tmp2] + + movq_r2r(mm3, mm6); + punpckldq_r2r(mm4, mm3); // wsptr[0,0],[0,1],[0,4],[0,5] + + punpckhdq_r2r(mm6, mm4); // wsptr[0,6],[0,7],[0,2],[0,3] + movq_r2r(mm3, mm2); + +//Save tmp0 and tmp1 in wsptr + movq_r2m(mm0, *(wsptr)); // save tmp0 + paddw_r2r(mm4, mm2); // wsptr[xxx],[0,z11],[xxx],[0,z13] + + +//Continue with z10 --- z13 + movq_m2r(*(wsptr+2), mm6); // wsptr[1,0],[1,1],[1,2],[1,3] + psubw_r2r(mm4, mm3); // wsptr[xxx],[0,z12],[xxx],[0,z10] + + movq_m2r(*(wsptr+3), mm0); // wsptr[1,4],[1,5],[1,6],[1,7] + movq_r2r(mm6, mm4); + + movq_r2m(mm1, *(wsptr+1)); // save tmp1 + punpckldq_r2r(mm0, mm6); // wsptr[1,0],[1,1],[1,4],[1,5] + + punpckhdq_r2r(mm4, mm0); // wsptr[1,6],[1,7],[1,2],[1,3] + movq_r2r(mm6, mm1); + +//Save tmp2 and tmp3 in wsptr + paddw_r2r(mm0, mm6); // wsptr[xxx],[1,z11],[xxx],[1,z13] + movq_r2r(mm2, mm4); + +//Continue with z10 --- z13 + movq_r2m(mm5, *(wsptr+2)); // save tmp2 + punpcklwd_r2r(mm6, mm2); // wsptr[xxx],[xxx],[0,z11],[1,z11] + + psubw_r2r(mm0, mm1); // wsptr[xxx],[1,z12],[xxx],[1,z10] + punpckhwd_r2r(mm6, mm4); // wsptr[xxx],[xxx],[0,z13],[1,z13] + + movq_r2r(mm3, mm0); + punpcklwd_r2r(mm1, mm3); // wsptr[xxx],[xxx],[0,z12],[1,z12] + + movq_r2m(mm7, *(wsptr+3)); // save tmp3 + punpckhwd_r2r(mm1, mm0); // wsptr[xxx],[xxx],[0,z10],[1,z10] + + movq_m2r(*(wsptr+4), mm6); // wsptr[2,0],[2,1],[2,2],[2,3] + punpckhdq_r2r(mm2, mm0); // wsptr[0,z10],[1,z10],[0,z11],[1,z11] + + movq_m2r(*(wsptr+5), mm7); // wsptr[2,4],[2,5],[2,6],[2,7] + punpckhdq_r2r(mm4, mm3); // wsptr[0,z12],[1,z12],[0,z13],[1,z13] + + movq_m2r(*(wsptr+6), mm1); // wsptr[3,0],[3,1],[3,2],[3,3] + movq_r2r(mm6, mm4); + + punpckldq_r2r(mm7, mm6); // wsptr[2,0],[2,1],[2,4],[2,5] + movq_r2r(mm1, mm5); + + punpckhdq_r2r(mm4, mm7); // wsptr[2,6],[2,7],[2,2],[2,3] + movq_r2r(mm6, mm2); + + movq_m2r(*(wsptr+7), mm4); // wsptr[3,4],[3,5],[3,6],[3,7] + paddw_r2r(mm7, mm6); // wsptr[xxx],[2,z11],[xxx],[2,z13] + + psubw_r2r(mm7, mm2); // wsptr[xxx],[2,z12],[xxx],[2,z10] + punpckldq_r2r(mm4, mm1); // wsptr[3,0],[3,1],[3,4],[3,5] + + punpckhdq_r2r(mm5, mm4); // wsptr[3,6],[3,7],[3,2],[3,3] + movq_r2r(mm1, mm7); + + paddw_r2r(mm4, mm1); // wsptr[xxx],[3,z11],[xxx],[3,z13] + psubw_r2r(mm4, mm7); // wsptr[xxx],[3,z12],[xxx],[3,z10] + + movq_r2r(mm6, mm5); + punpcklwd_r2r(mm1, mm6); // wsptr[xxx],[xxx],[2,z11],[3,z11] + + punpckhwd_r2r(mm1, mm5); // wsptr[xxx],[xxx],[2,z13],[3,z13] + movq_r2r(mm2, mm4); + + punpcklwd_r2r(mm7, mm2); // wsptr[xxx],[xxx],[2,z12],[3,z12] + + punpckhwd_r2r(mm7, mm4); // wsptr[xxx],[xxx],[2,z10],[3,z10] + + punpckhdq_r2r(mm6, mm4); /// wsptr[2,z10],[3,z10],[2,z11],[3,z11] + + punpckhdq_r2r(mm5, mm2); // wsptr[2,z12],[3,z12],[2,z13],[3,z13] + movq_r2r(mm0, mm5); + + punpckldq_r2r(mm4, mm0); // wsptr[0,z10],[1,z10],[2,z10],[3,z10] + + punpckhdq_r2r(mm4, mm5); // wsptr[0,z11],[1,z11],[2,z11],[3,z11] + movq_r2r(mm3, mm4); + + punpckhdq_r2r(mm2, mm4); // wsptr[0,z13],[1,z13],[2,z13],[3,z13] + movq_r2r(mm5, mm1); + + punpckldq_r2r(mm2, mm3); // wsptr[0,z12],[1,z12],[2,z12],[3,z12] +// tmp7 = z11 + z13; /* phase 5 */ +// tmp8 = z11 - z13; /* phase 5 */ + psubw_r2r(mm4, mm1); // tmp8 + + paddw_r2r(mm4, mm5); // tmp7 +// tmp21 = MULTIPLY(tmp8, FIX_1_414213562); /* 2*c4 */ + psllw_i2r(2, mm1); + + psllw_i2r(2, mm0); + + pmulhw_m2r(fix_141, mm1); // tmp21 +// tmp20 = MULTIPLY(z12, (FIX_1_082392200- FIX_1_847759065)) /* 2*(c2-c6) */ +// + MULTIPLY(z10, - FIX_1_847759065); /* 2*c2 */ + psllw_i2r(2, mm3); + movq_r2r(mm0, mm7); + + pmulhw_m2r(fix_n184, mm7); + movq_r2r(mm3, mm6); + + movq_m2r(*(wsptr), mm2); // tmp0,final1 + + pmulhw_m2r(fix_108n184, mm6); +// tmp22 = MULTIPLY(z10,(FIX_1_847759065 - FIX_2_613125930)) /* -2*(c2+c6) */ +// + MULTIPLY(z12, FIX_1_847759065); /* 2*c2 */ + movq_r2r(mm2, mm4); // final1 + + pmulhw_m2r(fix_184n261, mm0); + paddw_r2r(mm5, mm2); // tmp0+tmp7,final1 + + pmulhw_m2r(fix_184, mm3); + psubw_r2r(mm5, mm4); // tmp0-tmp7,final1 + +// tmp6 = tmp22 - tmp7; /* phase 2 */ + psraw_i2r(3, mm2); // outptr[0,0],[1,0],[2,0],[3,0],final1 + + paddw_r2r(mm6, mm7); // tmp20 + psraw_i2r(3, mm4); // outptr[0,7],[1,7],[2,7],[3,7],final1 + + paddw_r2r(mm0, mm3); // tmp22 + +// tmp5 = tmp21 - tmp6; + psubw_r2r(mm5, mm3); // tmp6 + +// tmp4 = tmp20 + tmp5; + movq_m2r(*(wsptr+1), mm0); // tmp1,final2 + psubw_r2r(mm3, mm1); // tmp5 + + movq_r2r(mm0, mm6); // final2 + paddw_r2r(mm3, mm0); // tmp1+tmp6,final2 + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + +// outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) +// & RANGE_MASK]; final1 + + +// outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) +// & RANGE_MASK]; final2 + psubw_r2r(mm3, mm6); // tmp1-tmp6,final2 + psraw_i2r(3, mm0); // outptr[0,1],[1,1],[2,1],[3,1] + + psraw_i2r(3, mm6); // outptr[0,6],[1,6],[2,6],[3,6] + + packuswb_r2r(mm4, mm0); // out[0,1],[1,1],[2,1],[3,1],[0,7],[1,7],[2,7],[3,7] + + movq_m2r(*(wsptr+2), mm5); // tmp2,final3 + packuswb_r2r(mm6, mm2); // out[0,0],[1,0],[2,0],[3,0],[0,6],[1,6],[2,6],[3,6] + +// outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) +// & RANGE_MASK]; final3 + paddw_r2r(mm1, mm7); // tmp4 + movq_r2r(mm5, mm3); + + paddw_r2r(mm1, mm5); // tmp2+tmp5 + psubw_r2r(mm1, mm3); // tmp2-tmp5 + + psraw_i2r(3, mm5); // outptr[0,2],[1,2],[2,2],[3,2] + + movq_m2r(*(wsptr+3), mm4); // tmp3,final4 + psraw_i2r(3, mm3); // outptr[0,5],[1,5],[2,5],[3,5] + + + +// outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) +// & RANGE_MASK]; final4 + movq_r2r(mm4, mm6); + paddw_r2r(mm7, mm4); // tmp3+tmp4 + + psubw_r2r(mm7, mm6); // tmp3-tmp4 + psraw_i2r(3, mm4); // outptr[0,4],[1,4],[2,4],[3,4] + + // mov ecx, [dataptr] + + psraw_i2r(3, mm6); // outptr[0,3],[1,3],[2,3],[3,3] + + packuswb_r2r(mm4, mm5); // out[0,2],[1,2],[2,2],[3,2],[0,4],[1,4],[2,4],[3,4] + + packuswb_r2r(mm3, mm6); // out[0,3],[1,3],[2,3],[3,3],[0,5],[1,5],[2,5],[3,5] + movq_r2r(mm2, mm4); + + movq_r2r(mm5, mm7); + punpcklbw_r2r(mm0, mm2); // out[0,0],[0,1],[1,0],[1,1],[2,0],[2,1],[3,0],[3,1] + + punpckhbw_r2r(mm0, mm4); // out[0,6],[0,7],[1,6],[1,7],[2,6],[2,7],[3,6],[3,7] + movq_r2r(mm2, mm1); + + punpcklbw_r2r(mm6, mm5); // out[0,2],[0,3],[1,2],[1,3],[2,2],[2,3],[3,2],[3,3] + + // add dataptr, 4 + + punpckhbw_r2r(mm6, mm7); // out[0,4],[0,5],[1,4],[1,5],[2,4],[2,5],[3,4],[3,5] + + punpcklwd_r2r(mm5, mm2); // out[0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3] + + // add ecx, output_col + + movq_r2r(mm7, mm6); + punpckhwd_r2r(mm5, mm1); // out[2,0],[2,1],[2,2],[2,3],[3,0],[3,1],[3,2],[3,3] + + movq_r2r(mm2, mm0); + punpcklwd_r2r(mm4, mm6); // out[0,4],[0,5],[0,6],[0,7],[1,4],[1,5],[1,6],[1,7] + + // mov idata, [dataptr] + + punpckldq_r2r(mm6, mm2); // out[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7] + + // add dataptr, 4 + + movq_r2r(mm1, mm3); + + // add idata, output_col + + punpckhwd_r2r(mm4, mm7); // out[2,4],[2,5],[2,6],[2,7],[3,4],[3,5],[3,6],[3,7] + + movq_r2m(mm2, *(dataptr)); + + punpckhdq_r2r(mm6, mm0); // out[1,0],[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7] + + dataptr += rskip; + movq_r2m(mm0, *(dataptr)); + + punpckldq_r2r(mm7, mm1); // out[2,0],[2,1],[2,2],[2,3],[2,4],[2,5],[2,6],[2,7] + punpckhdq_r2r(mm7, mm3); // out[3,0],[3,1],[3,2],[3,3],[3,4],[3,5],[3,6],[3,7] + + dataptr += rskip; + movq_r2m(mm1, *(dataptr)); + + dataptr += rskip; + movq_r2m(mm3, *(dataptr)); + +/*******************************************************************/ + + wsptr += 8; + +/*******************************************************************/ + +// tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); +// tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); +// tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); +// tmp14 = ((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6]); + movq_m2r(*(wsptr), mm0); // wsptr[0,0],[0,1],[0,2],[0,3] + + movq_m2r(*(wsptr+1), mm1); // wsptr[0,4],[0,5],[0,6],[0,7] + movq_r2r(mm0, mm2); + + movq_m2r(*(wsptr+2), mm3); // wsptr[1,0],[1,1],[1,2],[1,3] + paddw_r2r(mm1, mm0); // wsptr[0,tmp10],[xxx],[0,tmp13],[xxx] + + movq_m2r(*(wsptr+3), mm4); // wsptr[1,4],[1,5],[1,6],[1,7] + psubw_r2r(mm1, mm2); // wsptr[0,tmp11],[xxx],[0,tmp14],[xxx] + + movq_r2r(mm0, mm6); + movq_r2r(mm3, mm5); + + paddw_r2r(mm4, mm3); // wsptr[1,tmp10],[xxx],[1,tmp13],[xxx] + movq_r2r(mm2, mm1); + + psubw_r2r(mm4, mm5); // wsptr[1,tmp11],[xxx],[1,tmp14],[xxx] + punpcklwd_r2r(mm3, mm0); // wsptr[0,tmp10],[1,tmp10],[xxx],[xxx] + + movq_m2r(*(wsptr+7), mm7); // wsptr[3,4],[3,5],[3,6],[3,7] + punpckhwd_r2r(mm3, mm6); // wsptr[0,tmp13],[1,tmp13],[xxx],[xxx] + + movq_m2r(*(wsptr+4), mm3); // wsptr[2,0],[2,1],[2,2],[2,3] + punpckldq_r2r(mm6, mm0); // wsptr[0,tmp10],[1,tmp10],[0,tmp13],[1,tmp13] + + punpcklwd_r2r(mm5, mm1); // wsptr[0,tmp11],[1,tmp11],[xxx],[xxx] + movq_r2r(mm3, mm4); + + movq_m2r(*(wsptr+6), mm6); // wsptr[3,0],[3,1],[3,2],[3,3] + punpckhwd_r2r(mm5, mm2); // wsptr[0,tmp14],[1,tmp14],[xxx],[xxx] + + movq_m2r(*(wsptr+5), mm5); // wsptr[2,4],[2,5],[2,6],[2,7] + punpckldq_r2r(mm2, mm1); // wsptr[0,tmp11],[1,tmp11],[0,tmp14],[1,tmp14] + + paddw_r2r(mm5, mm3); // wsptr[2,tmp10],[xxx],[2,tmp13],[xxx] + movq_r2r(mm6, mm2); + + psubw_r2r(mm5, mm4); // wsptr[2,tmp11],[xxx],[2,tmp14],[xxx] + paddw_r2r(mm7, mm6); // wsptr[3,tmp10],[xxx],[3,tmp13],[xxx] + + movq_r2r(mm3, mm5); + punpcklwd_r2r(mm6, mm3); // wsptr[2,tmp10],[3,tmp10],[xxx],[xxx] + + psubw_r2r(mm7, mm2); // wsptr[3,tmp11],[xxx],[3,tmp14],[xxx] + punpckhwd_r2r(mm6, mm5); // wsptr[2,tmp13],[3,tmp13],[xxx],[xxx] + + movq_r2r(mm4, mm7); + punpckldq_r2r(mm5, mm3); // wsptr[2,tmp10],[3,tmp10],[2,tmp13],[3,tmp13] + + punpcklwd_r2r(mm2, mm4); // wsptr[2,tmp11],[3,tmp11],[xxx],[xxx] + + punpckhwd_r2r(mm2, mm7); // wsptr[2,tmp14],[3,tmp14],[xxx],[xxx] + + punpckldq_r2r(mm7, mm4); // wsptr[2,tmp11],[3,tmp11],[2,tmp14],[3,tmp14] + movq_r2r(mm1, mm6); + + //OK + +// mm0 = ;wsptr[0,tmp10],[1,tmp10],[0,tmp13],[1,tmp13] +// mm1 = ;wsptr[0,tmp11],[1,tmp11],[0,tmp14],[1,tmp14] + + movq_r2r(mm0, mm2); + punpckhdq_r2r(mm4, mm6); // wsptr[0,tmp14],[1,tmp14],[2,tmp14],[3,tmp14] + + punpckldq_r2r(mm4, mm1); // wsptr[0,tmp11],[1,tmp11],[2,tmp11],[3,tmp11] + psllw_i2r(2, mm6); + + pmulhw_m2r(fix_141, mm6); + punpckldq_r2r(mm3, mm0); // wsptr[0,tmp10],[1,tmp10],[2,tmp10],[3,tmp10] + + punpckhdq_r2r(mm3, mm2); // wsptr[0,tmp13],[1,tmp13],[2,tmp13],[3,tmp13] + movq_r2r(mm0, mm7); + +// tmp0 = tmp10 + tmp13; +// tmp3 = tmp10 - tmp13; + paddw_r2r(mm2, mm0); // [0,tmp0],[1,tmp0],[2,tmp0],[3,tmp0] + psubw_r2r(mm2, mm7); // [0,tmp3],[1,tmp3],[2,tmp3],[3,tmp3] + +// tmp12 = MULTIPLY(tmp14, FIX_1_414213562) - tmp13; + psubw_r2r(mm2, mm6); // wsptr[0,tmp12],[1,tmp12],[2,tmp12],[3,tmp12] +// tmp1 = tmp11 + tmp12; +// tmp2 = tmp11 - tmp12; + movq_r2r(mm1, mm5); + + //OK + + + /* Odd part */ + +// z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; +// z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; +// z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; +// z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + movq_m2r(*(wsptr), mm3); // wsptr[0,0],[0,1],[0,2],[0,3] + paddw_r2r(mm6, mm1); // [0,tmp1],[1,tmp1],[2,tmp1],[3,tmp1] + + movq_m2r(*(wsptr+1), mm4); // wsptr[0,4],[0,5],[0,6],[0,7] + psubw_r2r(mm6, mm5); // [0,tmp2],[1,tmp2],[2,tmp2],[3,tmp2] + + movq_r2r(mm3, mm6); + punpckldq_r2r(mm4, mm3); // wsptr[0,0],[0,1],[0,4],[0,5] + + punpckhdq_r2r(mm6, mm4); // wsptr[0,6],[0,7],[0,2],[0,3] + movq_r2r(mm3, mm2); + +//Save tmp0 and tmp1 in wsptr + movq_r2m(mm0, *(wsptr)); // save tmp0 + paddw_r2r(mm4, mm2); // wsptr[xxx],[0,z11],[xxx],[0,z13] + + +//Continue with z10 --- z13 + movq_m2r(*(wsptr+2), mm6); // wsptr[1,0],[1,1],[1,2],[1,3] + psubw_r2r(mm4, mm3); // wsptr[xxx],[0,z12],[xxx],[0,z10] + + movq_m2r(*(wsptr+3), mm0); // wsptr[1,4],[1,5],[1,6],[1,7] + movq_r2r(mm6, mm4); + + movq_r2m(mm1, *(wsptr+1)); // save tmp1 + punpckldq_r2r(mm0, mm6); // wsptr[1,0],[1,1],[1,4],[1,5] + + punpckhdq_r2r(mm4, mm0); // wsptr[1,6],[1,7],[1,2],[1,3] + movq_r2r(mm6, mm1); + +//Save tmp2 and tmp3 in wsptr + paddw_r2r(mm0, mm6); // wsptr[xxx],[1,z11],[xxx],[1,z13] + movq_r2r(mm2, mm4); + +//Continue with z10 --- z13 + movq_r2m(mm5, *(wsptr+2)); // save tmp2 + punpcklwd_r2r(mm6, mm2); // wsptr[xxx],[xxx],[0,z11],[1,z11] + + psubw_r2r(mm0, mm1); // wsptr[xxx],[1,z12],[xxx],[1,z10] + punpckhwd_r2r(mm6, mm4); // wsptr[xxx],[xxx],[0,z13],[1,z13] + + movq_r2r(mm3, mm0); + punpcklwd_r2r(mm1, mm3); // wsptr[xxx],[xxx],[0,z12],[1,z12] + + movq_r2m(mm7, *(wsptr+3)); // save tmp3 + punpckhwd_r2r(mm1, mm0); // wsptr[xxx],[xxx],[0,z10],[1,z10] + + movq_m2r(*(wsptr+4), mm6); // wsptr[2,0],[2,1],[2,2],[2,3] + punpckhdq_r2r(mm2, mm0); // wsptr[0,z10],[1,z10],[0,z11],[1,z11] + + movq_m2r(*(wsptr+5), mm7); // wsptr[2,4],[2,5],[2,6],[2,7] + punpckhdq_r2r(mm4, mm3); // wsptr[0,z12],[1,z12],[0,z13],[1,z13] + + movq_m2r(*(wsptr+6), mm1); // wsptr[3,0],[3,1],[3,2],[3,3] + movq_r2r(mm6, mm4); + + punpckldq_r2r(mm7, mm6); // wsptr[2,0],[2,1],[2,4],[2,5] + movq_r2r(mm1, mm5); + + punpckhdq_r2r(mm4, mm7); // wsptr[2,6],[2,7],[2,2],[2,3] + movq_r2r(mm6, mm2); + + movq_m2r(*(wsptr+7), mm4); // wsptr[3,4],[3,5],[3,6],[3,7] + paddw_r2r(mm7, mm6); // wsptr[xxx],[2,z11],[xxx],[2,z13] + + psubw_r2r(mm7, mm2); // wsptr[xxx],[2,z12],[xxx],[2,z10] + punpckldq_r2r(mm4, mm1); // wsptr[3,0],[3,1],[3,4],[3,5] + + punpckhdq_r2r(mm5, mm4); // wsptr[3,6],[3,7],[3,2],[3,3] + movq_r2r(mm1, mm7); + + paddw_r2r(mm4, mm1); // wsptr[xxx],[3,z11],[xxx],[3,z13] + psubw_r2r(mm4, mm7); // wsptr[xxx],[3,z12],[xxx],[3,z10] + + movq_r2r(mm6, mm5); + punpcklwd_r2r(mm1, mm6); // wsptr[xxx],[xxx],[2,z11],[3,z11] + + punpckhwd_r2r(mm1, mm5); // wsptr[xxx],[xxx],[2,z13],[3,z13] + movq_r2r(mm2, mm4); + + punpcklwd_r2r(mm7, mm2); // wsptr[xxx],[xxx],[2,z12],[3,z12] + + punpckhwd_r2r(mm7, mm4); // wsptr[xxx],[xxx],[2,z10],[3,z10] + + punpckhdq_r2r(mm6, mm4); // wsptr[2,z10],[3,z10],[2,z11],[3,z11] + + punpckhdq_r2r(mm5, mm2); // wsptr[2,z12],[3,z12],[2,z13],[3,z13] + movq_r2r(mm0, mm5); + + punpckldq_r2r(mm4, mm0); // wsptr[0,z10],[1,z10],[2,z10],[3,z10] + + punpckhdq_r2r(mm4, mm5); // wsptr[0,z11],[1,z11],[2,z11],[3,z11] + movq_r2r(mm3, mm4); + + punpckhdq_r2r(mm2, mm4); // wsptr[0,z13],[1,z13],[2,z13],[3,z13] + movq_r2r(mm5, mm1); + + punpckldq_r2r(mm2, mm3); // wsptr[0,z12],[1,z12],[2,z12],[3,z12] +// tmp7 = z11 + z13; /* phase 5 */ +// tmp8 = z11 - z13; /* phase 5 */ + psubw_r2r(mm4, mm1); // tmp8 + + paddw_r2r(mm4, mm5); // tmp7 +// tmp21 = MULTIPLY(tmp8, FIX_1_414213562); /* 2*c4 */ + psllw_i2r(2, mm1); + + psllw_i2r(2, mm0); + + pmulhw_m2r(fix_141, mm1); // tmp21 +// tmp20 = MULTIPLY(z12, (FIX_1_082392200- FIX_1_847759065)) /* 2*(c2-c6) */ +// + MULTIPLY(z10, - FIX_1_847759065); /* 2*c2 */ + psllw_i2r(2, mm3); + movq_r2r(mm0, mm7); + + pmulhw_m2r(fix_n184, mm7); + movq_r2r(mm3, mm6); + + movq_m2r(*(wsptr), mm2); // tmp0,final1 + + pmulhw_m2r(fix_108n184, mm6); +// tmp22 = MULTIPLY(z10,(FIX_1_847759065 - FIX_2_613125930)) /* -2*(c2+c6) */ +// + MULTIPLY(z12, FIX_1_847759065); /* 2*c2 */ + movq_r2r(mm2, mm4); // final1 + + pmulhw_m2r(fix_184n261, mm0); + paddw_r2r(mm5, mm2); // tmp0+tmp7,final1 + + pmulhw_m2r(fix_184, mm3); + psubw_r2r(mm5, mm4); // tmp0-tmp7,final1 + +// tmp6 = tmp22 - tmp7; /* phase 2 */ + psraw_i2r(3, mm2); // outptr[0,0],[1,0],[2,0],[3,0],final1 + + paddw_r2r(mm6, mm7); // tmp20 + psraw_i2r(3, mm4); // outptr[0,7],[1,7],[2,7],[3,7],final1 + + paddw_r2r(mm0, mm3); // tmp22 + +// tmp5 = tmp21 - tmp6; + psubw_r2r(mm5, mm3); // tmp6 + +// tmp4 = tmp20 + tmp5; + movq_m2r(*(wsptr+1), mm0); // tmp1,final2 + psubw_r2r(mm3, mm1); // tmp5 + + movq_r2r(mm0, mm6); // final2 + paddw_r2r(mm3, mm0); // tmp1+tmp6,final2 + + /* Final output stage: scale down by a factor of 8 and range-limit */ + +// outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) +// & RANGE_MASK]; final1 + + +// outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) +// & RANGE_MASK]; final2 + psubw_r2r(mm3, mm6); // tmp1-tmp6,final2 + psraw_i2r(3, mm0); // outptr[0,1],[1,1],[2,1],[3,1] + + psraw_i2r(3, mm6); // outptr[0,6],[1,6],[2,6],[3,6] + + packuswb_r2r(mm4, mm0); // out[0,1],[1,1],[2,1],[3,1],[0,7],[1,7],[2,7],[3,7] + + movq_m2r(*(wsptr+2), mm5); // tmp2,final3 + packuswb_r2r(mm6, mm2); // out[0,0],[1,0],[2,0],[3,0],[0,6],[1,6],[2,6],[3,6] + +// outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) +// & RANGE_MASK]; final3 + paddw_r2r(mm1, mm7); // tmp4 + movq_r2r(mm5, mm3); + + paddw_r2r(mm1, mm5); // tmp2+tmp5 + psubw_r2r(mm1, mm3); // tmp2-tmp5 + + psraw_i2r(3, mm5); // outptr[0,2],[1,2],[2,2],[3,2] + + movq_m2r(*(wsptr+3), mm4); // tmp3,final4 + psraw_i2r(3, mm3); // outptr[0,5],[1,5],[2,5],[3,5] + + + +// outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) +// & RANGE_MASK]; +// outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) +// & RANGE_MASK]; final4 + movq_r2r(mm4, mm6); + paddw_r2r(mm7, mm4); // tmp3+tmp4 + + psubw_r2r(mm7, mm6); // tmp3-tmp4 + psraw_i2r(3, mm4); // outptr[0,4],[1,4],[2,4],[3,4] + + psraw_i2r(3, mm6); // outptr[0,3],[1,3],[2,3],[3,3] + + /* + movq_r2m(mm4, *dummy); + fprintf(stderr, "3-4 %016llx\n", dummy); + movq_r2m(mm4, *dummy); + fprintf(stderr, "3+4 %016llx\n", dummy); + */ + + + packuswb_r2r(mm4, mm5); // out[0,2],[1,2],[2,2],[3,2],[0,4],[1,4],[2,4],[3,4] + + packuswb_r2r(mm3, mm6); // out[0,3],[1,3],[2,3],[3,3],[0,5],[1,5],[2,5],[3,5] + movq_r2r(mm2, mm4); + + movq_r2r(mm5, mm7); + punpcklbw_r2r(mm0, mm2); // out[0,0],[0,1],[1,0],[1,1],[2,0],[2,1],[3,0],[3,1] + + punpckhbw_r2r(mm0, mm4); // out[0,6],[0,7],[1,6],[1,7],[2,6],[2,7],[3,6],[3,7] + movq_r2r(mm2, mm1); + + punpcklbw_r2r(mm6, mm5); // out[0,2],[0,3],[1,2],[1,3],[2,2],[2,3],[3,2],[3,3] + + punpckhbw_r2r(mm6, mm7); // out[0,4],[0,5],[1,4],[1,5],[2,4],[2,5],[3,4],[3,5] + + punpcklwd_r2r(mm5, mm2); // out[0,0],[0,1],[0,2],[0,3],[1,0],[1,1],[1,2],[1,3] + + movq_r2r(mm7, mm6); + punpckhwd_r2r(mm5, mm1); // out[2,0],[2,1],[2,2],[2,3],[3,0],[3,1],[3,2],[3,3] + + movq_r2r(mm2, mm0); + punpcklwd_r2r(mm4, mm6); // out[0,4],[0,5],[0,6],[0,7],[1,4],[1,5],[1,6],[1,7] + + punpckldq_r2r(mm6, mm2); // out[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7] + + movq_r2r(mm1, mm3); + + punpckhwd_r2r(mm4, mm7); // out[2,4],[2,5],[2,6],[2,7],[3,4],[3,5],[3,6],[3,7] + + dataptr += rskip; + movq_r2m(mm2, *(dataptr)); + + punpckhdq_r2r(mm6, mm0); // out[1,0],[1,1],[1,2],[1,3],[1,4],[1,5],[1,6],[1,7] + + dataptr += rskip; + movq_r2m(mm0, *(dataptr)); + + punpckldq_r2r(mm7, mm1); // out[2,0],[2,1],[2,2],[2,3],[2,4],[2,5],[2,6],[2,7] + + punpckhdq_r2r(mm7, mm3); // out[3,0],[3,1],[3,2],[3,3],[3,4],[3,5],[3,6],[3,7] + + dataptr += rskip; + movq_r2m(mm1, *(dataptr)); + + dataptr += rskip; + movq_r2m(mm3, *(dataptr)); + +#else + __s32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + __s32 tmp10, tmp11, tmp12, tmp13; + __s32 z5, z10, z11, z12, z13; + __s16 *inptr; + __s32 *wsptr; + __u8 *outptr; + int ctr; + __s32 dcval; + __s32 workspace[64]; + + inptr = data; + wsptr = workspace; + for (ctr = 8; ctr > 0; ctr--) { + + if ((inptr[8] | inptr[16] | inptr[24] | + inptr[32] | inptr[40] | inptr[48] | inptr[56]) == 0) { + dcval = inptr[0]; + wsptr[0] = dcval; + wsptr[8] = dcval; + wsptr[16] = dcval; + wsptr[24] = dcval; + wsptr[32] = dcval; + wsptr[40] = dcval; + wsptr[48] = dcval; + wsptr[56] = dcval; + + inptr++; + wsptr++; + continue; + } + + tmp0 = inptr[0]; + tmp1 = inptr[16]; + tmp2 = inptr[32]; + tmp3 = inptr[48]; + + tmp10 = tmp0 + tmp2; + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + tmp4 = inptr[8]; + tmp5 = inptr[24]; + tmp6 = inptr[40]; + tmp7 = inptr[56]; + + z13 = tmp6 + tmp5; + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[0] = (__s32) (tmp0 + tmp7); + wsptr[56] = (__s32) (tmp0 - tmp7); + wsptr[8] = (__s32) (tmp1 + tmp6); + wsptr[48] = (__s32) (tmp1 - tmp6); + wsptr[16] = (__s32) (tmp2 + tmp5); + wsptr[40] = (__s32) (tmp2 - tmp5); + wsptr[32] = (__s32) (tmp3 + tmp4); + wsptr[24] = (__s32) (tmp3 - tmp4); + + inptr++; + wsptr++; + } + + wsptr = workspace; + for (ctr = 0; ctr < 8; ctr++) { + outptr = &(odata[ctr*rskip]); + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = MULTIPLY(wsptr[2] - wsptr[6], FIX_1_414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + outptr[0] = RL(DESCALE(tmp0 + tmp7)); + outptr[7] = RL(DESCALE(tmp0 - tmp7)); + outptr[1] = RL(DESCALE(tmp1 + tmp6)); + outptr[6] = RL(DESCALE(tmp1 - tmp6)); + outptr[2] = RL(DESCALE(tmp2 + tmp5)); + outptr[5] = RL(DESCALE(tmp2 - tmp5)); + outptr[4] = RL(DESCALE(tmp3 + tmp4)); + outptr[3] = RL(DESCALE(tmp3 - tmp4)); + + wsptr += 8; + } +#endif +} +/* + +Main Routines + +This file contains most of the initialisation and control functions + +(C) Justin Schoeman 1998 + +*/ + +/* + +Private function + +Initialise all the cache-aliged data blocks + +*/ + +void RTjpeg_init_data(void) +{ + unsigned long dptr; + + dptr=(unsigned long)&(RTjpeg_alldata[0]); + dptr+=32; + dptr=dptr>>5; + dptr=dptr<<5; /* cache align data */ + + RTjpeg_block=(__s16 *)dptr; + dptr+=sizeof(__s16)*64; + RTjpeg_lqt=(__s32 *)dptr; + dptr+=sizeof(__s32)*64; + RTjpeg_cqt=(__s32 *)dptr; + dptr+=sizeof(__s32)*64; + RTjpeg_liqt=(__u32 *)dptr; + dptr+=sizeof(__u32)*64; + RTjpeg_ciqt=(__u32 *)dptr; +} + +/* + +External Function + +Re-set quality factor + +Input: buf -> pointer to 128 ints for quant values store to pass back to + init_decompress. + Q -> quality factor (192=best, 32=worst) +*/ + +void RTjpeg_init_Q(__u8 Q) +{ + int i; + __u64 qual; + + qual=(__u64)Q<<(32-7); /* 32 bit FP, 255=2, 0=0 */ + + for(i=0; i<64; i++) + { + RTjpeg_lqt[i]=(__s32)((qual/((__u64)RTjpeg_lum_quant_tbl[i]<<16))>>3); + if(RTjpeg_lqt[i]==0)RTjpeg_lqt[i]=1; + RTjpeg_cqt[i]=(__s32)((qual/((__u64)RTjpeg_chrom_quant_tbl[i]<<16))>>3); + if(RTjpeg_cqt[i]==0)RTjpeg_cqt[i]=1; + RTjpeg_liqt[i]=(1<<16)/(RTjpeg_lqt[i]<<3); + RTjpeg_ciqt[i]=(1<<16)/(RTjpeg_cqt[i]<<3); + RTjpeg_lqt[i]=((1<<16)/RTjpeg_liqt[i])>>3; + RTjpeg_cqt[i]=((1<<16)/RTjpeg_ciqt[i])>>3; + } + + RTjpeg_lb8=0; + while(RTjpeg_liqt[RTjpeg_ZZ[++RTjpeg_lb8]]<=8); + RTjpeg_lb8--; + RTjpeg_cb8=0; + while(RTjpeg_ciqt[RTjpeg_ZZ[++RTjpeg_cb8]]<=8); + RTjpeg_cb8--; + + RTjpeg_dct_init(); + RTjpeg_idct_init(); + RTjpeg_quant_init(); +} + +/* + +External Function + +Initialise compression. + +Input: buf -> pointer to 128 ints for quant values store to pass back to + init_decompress. + width -> width of image + height -> height of image + Q -> quality factor (192=best, 32=worst) + +*/ + +void RTjpeg_init_compress(__u32 *buf, int width, int height, __u8 Q) +{ + int i; + __u64 qual; + + RTjpeg_init_data(); + + RTjpeg_width=width; + RTjpeg_height=height; + RTjpeg_Ywidth = RTjpeg_width>>3; + RTjpeg_Ysize=width * height; + RTjpeg_Cwidth = RTjpeg_width>>4; + RTjpeg_Csize= (width>>1) * height; + + qual=(__u64)Q<<(32-7); /* 32 bit FP, 255=2, 0=0 */ + + for(i=0; i<64; i++) + { + RTjpeg_lqt[i]=(__s32)((qual/((__u64)RTjpeg_lum_quant_tbl[i]<<16))>>3); + if(RTjpeg_lqt[i]==0)RTjpeg_lqt[i]=1; + RTjpeg_cqt[i]=(__s32)((qual/((__u64)RTjpeg_chrom_quant_tbl[i]<<16))>>3); + if(RTjpeg_cqt[i]==0)RTjpeg_cqt[i]=1; + RTjpeg_liqt[i]=(1<<16)/(RTjpeg_lqt[i]<<3); + RTjpeg_ciqt[i]=(1<<16)/(RTjpeg_cqt[i]<<3); + RTjpeg_lqt[i]=((1<<16)/RTjpeg_liqt[i])>>3; + RTjpeg_cqt[i]=((1<<16)/RTjpeg_ciqt[i])>>3; + } + + RTjpeg_lb8=0; + while(RTjpeg_liqt[RTjpeg_ZZ[++RTjpeg_lb8]]<=8); + RTjpeg_lb8--; + RTjpeg_cb8=0; + while(RTjpeg_ciqt[RTjpeg_ZZ[++RTjpeg_cb8]]<=8); + RTjpeg_cb8--; + + RTjpeg_dct_init(); + RTjpeg_quant_init(); + + for(i=0; i<64; i++) + buf[i]=RTjpeg_liqt[i]; + for(i=0; i<64; i++) + buf[64+i]=RTjpeg_ciqt[i]; +} + +void RTjpeg_init_decompress(__u32 *buf, int width, int height) +{ + int i; + + RTjpeg_init_data(); + + RTjpeg_width=width; + RTjpeg_height=height; + RTjpeg_Ywidth = RTjpeg_width>>3; + RTjpeg_Ysize=width * height; + RTjpeg_Cwidth = RTjpeg_width>>4; + RTjpeg_Csize= (width>>1) * height; + + for(i=0; i<64; i++) + { + RTjpeg_liqt[i]=buf[i]; + RTjpeg_ciqt[i]=buf[i+64]; + } + + RTjpeg_lb8=0; + while(RTjpeg_liqt[RTjpeg_ZZ[++RTjpeg_lb8]]<=8); + RTjpeg_lb8--; + RTjpeg_cb8=0; + while(RTjpeg_ciqt[RTjpeg_ZZ[++RTjpeg_cb8]]<=8); + RTjpeg_cb8--; + + RTjpeg_idct_init(); + +// RTjpeg_color_init(); +} + +int RTjpeg_compressYUV420(__s8 *sp, unsigned char *bp) +{ + __s8 * sb; + register __s8 * bp1 = bp + (RTjpeg_width<<3); + register __s8 * bp2 = bp + RTjpeg_Ysize; + register __s8 * bp3 = bp2 + (RTjpeg_Csize>>1); + register int i, j, k; + +#ifdef HAVE_LIBMMX + emms(); +#endif + sb=sp; +/* Y */ + for(i=RTjpeg_height>>1; i; i-=8) + { + for(j=0, k=0; j<RTjpeg_width; j+=16, k+=8) + { + RTjpeg_dctY(bp+j, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp+j+8, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp1+j, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp1+j+8, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp2+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + + RTjpeg_dctY(bp3+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + + } + bp+=RTjpeg_width<<4; + bp1+=RTjpeg_width<<4; + bp2+=RTjpeg_width<<2; + bp3+=RTjpeg_width<<2; + + } +#ifdef HAVE_LIBMMX + emms(); +#endif + return (sp-sb); +} + +int RTjpeg_compressYUV422(__s8 *sp, unsigned char *bp) +{ + __s8 * sb; + register __s8 * bp2 = bp + RTjpeg_Ysize; + register __s8 * bp3 = bp2 + RTjpeg_Csize; + register int i, j, k; + +#ifdef HAVE_LIBMMX + emms(); +#endif + sb=sp; +/* Y */ + for(i=RTjpeg_height; i; i-=8) + { + for(j=0, k=0; j<RTjpeg_width; j+=16, k+=8) + { + RTjpeg_dctY(bp+j, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp+j+8, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + + RTjpeg_dctY(bp2+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + + RTjpeg_dctY(bp3+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + + } + bp+=RTjpeg_width<<3; + bp2+=RTjpeg_width<<2; + bp3+=RTjpeg_width<<2; + + } +#ifdef HAVE_LIBMMX + emms(); +#endif + return (sp-sb); +} + +int RTjpeg_compress8(__s8 *sp, unsigned char *bp) +{ + __s8 * sb; + int i, j; + +#ifdef HAVE_LIBMMX + emms(); +#endif + + sb=sp; +/* Y */ + for(i=0; i<RTjpeg_height; i+=8) + { + for(j=0; j<RTjpeg_width; j+=8) + { + RTjpeg_dctY(bp+j, RTjpeg_block, RTjpeg_width); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + } + bp+=RTjpeg_width; + } + +#ifdef HAVE_LIBMMX + emms(); +#endif + return (sp-sb); +} + +void RTjpeg_decompressYUV422(__s8 *sp, __u8 *bp) +{ + register __s8 * bp2 = bp + RTjpeg_Ysize; + register __s8 * bp3 = bp2 + (RTjpeg_Csize); + int i, j,k; + +#ifdef HAVE_LIBMMX + emms(); +#endif + +/* Y */ + for(i=RTjpeg_height; i; i-=8) + { + for(k=0, j=0; j<RTjpeg_width; j+=16, k+=8) { + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp+j, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp+j+8, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_cb8, RTjpeg_ciqt); + RTjpeg_idct(bp2+k, RTjpeg_block, RTjpeg_width>>1); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_cb8, RTjpeg_ciqt); + RTjpeg_idct(bp3+k, RTjpeg_block, RTjpeg_width>>1); + } + } + bp+=RTjpeg_width<<3; + bp2+=RTjpeg_width<<2; + bp3+=RTjpeg_width<<2; + } +#ifdef HAVE_LIBMMX + emms(); +#endif +} + +void RTjpeg_decompressYUV420(__s8 *sp, __u8 *bp) +{ + register __s8 * bp1 = bp + (RTjpeg_width<<3); + register __s8 * bp2 = bp + RTjpeg_Ysize; + register __s8 * bp3 = bp2 + (RTjpeg_Csize>>1); + int i, j,k; + +#ifdef HAVE_LIBMMX + emms(); +#endif + +/* Y */ + for(i=RTjpeg_height>>1; i; i-=8) + { + for(k=0, j=0; j<RTjpeg_width; j+=16, k+=8) { + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp+j, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp+j+8, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp1+j, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp1+j+8, RTjpeg_block, RTjpeg_width); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_cb8, RTjpeg_ciqt); + RTjpeg_idct(bp2+k, RTjpeg_block, RTjpeg_width>>1); + } + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_cb8, RTjpeg_ciqt); + RTjpeg_idct(bp3+k, RTjpeg_block, RTjpeg_width>>1); + } + } + bp+=RTjpeg_width<<4; + bp1+=RTjpeg_width<<4; + bp2+=RTjpeg_width<<2; + bp3+=RTjpeg_width<<2; + } +#ifdef HAVE_LIBMMX + emms(); +#endif +} + +void RTjpeg_decompress8(__s8 *sp, __u8 *bp) +{ + int i, j; + +#ifdef HAVE_LIBMMX + emms(); +#endif + +/* Y */ + for(i=0; i<RTjpeg_height; i+=8) + { + for(j=0; j<RTjpeg_width; j+=8) + if(*sp==-1)sp++; + else + { + sp+=RTjpeg_s2b(RTjpeg_block, sp, RTjpeg_lb8, RTjpeg_liqt); + RTjpeg_idct(bp+j, RTjpeg_block, RTjpeg_width); + } + bp+=RTjpeg_width<<3; + } +} + +/* +External Function + +Initialise additional data structures for motion compensation + +*/ + +void RTjpeg_init_mcompress(void) +{ + unsigned long tmp; + + if(!RTjpeg_old) + { + RTjpeg_old=malloc((4*RTjpeg_width*RTjpeg_height)+32); + tmp=(unsigned long)RTjpeg_old; + tmp+=32; + tmp=tmp>>5; + RTjpeg_old=(__s16 *)(tmp<<5); + } + if (!RTjpeg_old) + { + fprintf(stderr, "RTjpeg: Could not allocate memory\n"); + exit(-1); + } + bzero(RTjpeg_old, ((4*RTjpeg_width*RTjpeg_height))); +} + +#ifdef HAVE_LIBMMX + +int RTjpeg_bcomp(__s16 *old, mmx_t *mask) +{ + int i; + mmx_t *mold=(mmx_t *)old; + mmx_t *mblock=(mmx_t *)RTjpeg_block; + mmx_t result; + static mmx_t neg=(mmx_t)(unsigned long long)0xffffffffffffffffULL; + + movq_m2r(*mask, mm7); + movq_m2r(neg, mm6); + pxor_r2r(mm5, mm5); + + for(i=0; i<8; i++) + { + movq_m2r(*(mblock++), mm0); + movq_m2r(*(mblock++), mm2); + movq_m2r(*(mold++), mm1); + movq_m2r(*(mold++), mm3); + psubsw_r2r(mm1, mm0); + psubsw_r2r(mm3, mm2); + movq_r2r(mm0, mm1); + movq_r2r(mm2, mm3); + pcmpgtw_r2r(mm7, mm0); + pcmpgtw_r2r(mm7, mm2); + pxor_r2r(mm6, mm1); + pxor_r2r(mm6, mm3); + pcmpgtw_r2r(mm7, mm1); + pcmpgtw_r2r(mm7, mm3); + por_r2r(mm0, mm5); + por_r2r(mm2, mm5); + por_r2r(mm1, mm5); + por_r2r(mm3, mm5); + } + movq_r2m(mm5, result); + + if(result.q) + { + if(!RTjpeg_mtest) + for(i=0; i<16; i++)((__u64 *)old)[i]=((__u64 *)RTjpeg_block)[i]; + return 0; + } +// printf("."); + return 1; +} + +#else +int RTjpeg_bcomp(__s16 *old, __u16 *mask) +{ + int i; + + for(i=0; i<64; i++) + if(abs(old[i]-RTjpeg_block[i])>*mask) + { + if(!RTjpeg_mtest) + for(i=0; i<16; i++)((__u64 *)old)[i]=((__u64 *)RTjpeg_block)[i]; + return 0; + } + return 1; +} +#endif + +void RTjpeg_set_test(int i) +{ + RTjpeg_mtest=i; +} + +int RTjpeg_mcompress(__s8 *sp, unsigned char *bp, __u16 lmask, __u16 cmask) +{ + __s8 * sb; + __s16 *block; + register __s8 * bp2; + register __s8 * bp3; + register int i, j, k; + +#ifdef HAVE_LIBMMX + emms(); + RTjpeg_lmask=(mmx_t)(((__u64)lmask<<48)|((__u64)lmask<<32)|((__u64)lmask<<16)|lmask); + RTjpeg_cmask=(mmx_t)(((__u64)cmask<<48)|((__u64)cmask<<32)|((__u64)cmask<<16)|cmask); +#else + RTjpeg_lmask=lmask; + RTjpeg_cmask=cmask; +#endif + + bp = bp - RTjpeg_width*0; + bp2 = bp + RTjpeg_Ysize-RTjpeg_width*0; + bp3 = bp2 + RTjpeg_Csize; + + sb=sp; + block=RTjpeg_old; +/* Y */ + for(i=RTjpeg_height; i; i-=8) + { + for(j=0, k=0; j<RTjpeg_width; j+=16, k+=8) + { + RTjpeg_dctY(bp+j, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + if(RTjpeg_bcomp(block, &RTjpeg_lmask)) + { + *((__u8 *)sp++)=255; + } + else sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + block+=64; + + RTjpeg_dctY(bp+j+8, RTjpeg_block, RTjpeg_Ywidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + if(RTjpeg_bcomp(block, &RTjpeg_lmask)) + { + *((__u8 *)sp++)=255; + } + else sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + block+=64; + + RTjpeg_dctY(bp2+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + if(RTjpeg_bcomp(block, &RTjpeg_cmask)) + { + *((__u8 *)sp++)=255; + } + else sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + block+=64; + + RTjpeg_dctY(bp3+k, RTjpeg_block, RTjpeg_Cwidth); + RTjpeg_quant(RTjpeg_block, RTjpeg_cqt); + if(RTjpeg_bcomp(block, &RTjpeg_cmask)) + { + *((__u8 *)sp++)=255; + } + else sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_cb8); + block+=64; + + } + bp+=RTjpeg_width<<3; + bp2+=RTjpeg_width<<2; + bp3+=RTjpeg_width<<2; + } + //printf ("%d\n", block - RTjpeg_old); +#ifdef HAVE_LIBMMX + emms(); +#endif + return (sp-sb); +} + +int RTjpeg_mcompress8(__s8 *sp, unsigned char *bp, __u16 lmask) +{ + __s8 * sb; + __s16 *block; + int i, j; + +#ifdef HAVE_LIBMMX + emms(); + RTjpeg_lmask=(mmx_t)(((__u64)lmask<<48)|((__u64)lmask<<32)|((__u64)lmask<<16)|lmask); +#else + RTjpeg_lmask=lmask; +#endif + + + sb=sp; + block=RTjpeg_old; +/* Y */ + for(i=0; i<RTjpeg_height; i+=8) + { + for(j=0; j<RTjpeg_width; j+=8) + { + RTjpeg_dctY(bp+j, RTjpeg_block, RTjpeg_width); + RTjpeg_quant(RTjpeg_block, RTjpeg_lqt); + if(RTjpeg_bcomp(block, &RTjpeg_lmask)) + { + *((__u8 *)sp++)=255; +// printf("* %d ", sp[-1]); + } else sp+=RTjpeg_b2s(RTjpeg_block, sp, RTjpeg_lb8); + block+=64; + } + bp+=RTjpeg_width<<3; + } +#ifdef HAVE_LIBMMX + emms(); +#endif + return (sp-sb); +} + +void RTjpeg_color_init(void) +{ +} + +#define KcrR 76284 +#define KcrG 53281 +#define KcbG 25625 +#define KcbB 132252 +#define Ky 76284 + +void RTjpeg_yuv422rgb(__u8 *buf, __u8 *rgb) +{ + int tmp; + int i, j; + __s32 y, crR, crG, cbG, cbB; + __u8 *bufcr, *bufcb, *bufy, *bufoute; + int yskip; + + yskip=RTjpeg_width; + + bufcb=&buf[RTjpeg_width*RTjpeg_height]; + bufcr=&buf[RTjpeg_width*RTjpeg_height+(RTjpeg_width*RTjpeg_height)/2]; + bufy=&buf[0]; + bufoute=rgb; + + for(i=0; i<(RTjpeg_height); i++) + { + for(j=0; j<RTjpeg_width; j+=2) + { + crR=(*bufcr-128)*KcrR; + crG=(*(bufcr++)-128)*KcrG; + cbG=(*bufcb-128)*KcbG; + cbB=(*(bufcb++)-128)*KcbB; + + y=(bufy[j]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+1]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + } + bufy+=yskip; + } +} + + +void RTjpeg_yuv420rgb(__u8 *buf, __u8 *rgb) +{ + int tmp; + int i, j; + __s32 y, crR, crG, cbG, cbB; + __u8 *bufcr, *bufcb, *bufy, *bufoute, *bufouto; + int oskip, yskip; + + oskip=RTjpeg_width*3; + yskip=RTjpeg_width; + + bufcb=&buf[RTjpeg_width*RTjpeg_height]; + bufcr=&buf[RTjpeg_width*RTjpeg_height+(RTjpeg_width*RTjpeg_height)/4]; + bufy=&buf[0]; + bufoute=rgb; + bufouto=rgb+oskip; + + for(i=0; i<(RTjpeg_height>>1); i++) + { + for(j=0; j<RTjpeg_width; j+=2) + { + crR=(*bufcr-128)*KcrR; + crG=(*(bufcr++)-128)*KcrG; + cbG=(*bufcb-128)*KcbG; + cbB=(*(bufcb++)-128)*KcbB; + + y=(bufy[j]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+1]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+yskip]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+1+yskip]-16)*Ky; + + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + + } + bufoute+=oskip; + bufouto+=oskip; + bufy+=yskip<<1; + } +} + + +void RTjpeg_yuvrgb32(__u8 *buf, __u8 *rgb) +{ + int tmp; + int i, j; + __s32 y, crR, crG, cbG, cbB; + __u8 *bufcr, *bufcb, *bufy, *bufoute, *bufouto; + int oskip, yskip; + + oskip=RTjpeg_width*4; + yskip=RTjpeg_width; + + bufcb=&buf[RTjpeg_width*RTjpeg_height]; + bufcr=&buf[RTjpeg_width*RTjpeg_height+(RTjpeg_width*RTjpeg_height)/2]; + bufy=&buf[0]; + bufoute=rgb; + bufouto=rgb+oskip; + + for(i=0; i<(RTjpeg_height>>1); i++) + { + for(j=0; j<RTjpeg_width; j+=2) + { + crR=(*bufcr-128)*KcrR; + crG=(*(bufcr++)-128)*KcrG; + cbG=(*bufcb-128)*KcbG; + cbB=(*(bufcb++)-128)*KcbB; + + y=(bufy[j]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + bufoute++; + + y=(bufy[j+1]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + bufoute++; + + y=(bufy[j+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + bufouto++; + + y=(bufy[j+1+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + bufouto++; + + } + bufoute+=oskip; + bufouto+=oskip; + bufy+=yskip<<1; + } +} + +void RTjpeg_yuvrgb24(__u8 *buf, __u8 *rgb) +{ + int tmp; + int i, j; + __s32 y, crR, crG, cbG, cbB; + __u8 *bufcr, *bufcb, *bufy, *bufoute, *bufouto; + int oskip, yskip; + + oskip=RTjpeg_width*3; + yskip=RTjpeg_width; + + bufcb=&buf[RTjpeg_width*RTjpeg_height]; + bufcr=&buf[RTjpeg_width*RTjpeg_height+(RTjpeg_width*RTjpeg_height)/4]; + bufy=&buf[0]; + bufoute=rgb; + bufouto=rgb+oskip; + + for(i=0; i<(RTjpeg_height>>1); i++) + { + for(j=0; j<RTjpeg_width; j+=2) + { + crR=(*bufcr-128)*KcrR; + crG=(*(bufcr++)-128)*KcrG; + cbG=(*bufcb-128)*KcbG; + cbB=(*(bufcb++)-128)*KcbB; + + y=(bufy[j]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+1]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufoute++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + + y=(bufy[j+1+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + *(bufouto++)=(tmp>255)?255:((tmp<0)?0:tmp); + + } + bufoute+=oskip; + bufouto+=oskip; + bufy+=yskip<<1; + } +} + +void RTjpeg_yuvrgb16(__u8 *buf, __u8 *rgb) +{ + int tmp; + int i, j; + __s32 y, crR, crG, cbG, cbB; + __u8 *bufcr, *bufcb, *bufy, *bufoute, *bufouto; + int oskip, yskip; + unsigned char r, g, b; + + oskip=RTjpeg_width*2; + yskip=RTjpeg_width; + + bufcb=&buf[RTjpeg_width*RTjpeg_height]; + bufcr=&buf[RTjpeg_width*RTjpeg_height+(RTjpeg_width*RTjpeg_height)/4]; + bufy=&buf[0]; + bufoute=rgb; + bufouto=rgb+oskip; + + for(i=0; i<(RTjpeg_height>>1); i++) + { + for(j=0; j<RTjpeg_width; j+=2) + { + crR=(*bufcr-128)*KcrR; + crG=(*(bufcr++)-128)*KcrG; + cbG=(*bufcb-128)*KcbG; + cbB=(*(bufcb++)-128)*KcbB; + + y=(bufy[j]-16)*Ky; + + tmp=(y+cbB)>>16; + b=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + g=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + r=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(int)((int)b >> 3); + tmp|=(int)(((int)g >> 2) << 5); + tmp|=(int)(((int)r >> 3) << 11); + *(bufoute++)=tmp&0xff; + *(bufoute++)=tmp>>8; + + + y=(bufy[j+1]-16)*Ky; + + tmp=(y+cbB)>>16; + b=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + g=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + r=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(int)((int)b >> 3); + tmp|=(int)(((int)g >> 2) << 5); + tmp|=(int)(((int)r >> 3) << 11); + *(bufoute++)=tmp&0xff; + *(bufoute++)=tmp>>8; + + y=(bufy[j+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + b=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + g=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + r=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(int)((int)b >> 3); + tmp|=(int)(((int)g >> 2) << 5); + tmp|=(int)(((int)r >> 3) << 11); + *(bufouto++)=tmp&0xff; + *(bufouto++)=tmp>>8; + + y=(bufy[j+1+yskip]-16)*Ky; + + tmp=(y+cbB)>>16; + b=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y-crG-cbG)>>16; + g=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(y+crR)>>16; + r=(tmp>255)?255:((tmp<0)?0:tmp); + tmp=(int)((int)b >> 3); + tmp|=(int)(((int)g >> 2) << 5); + tmp|=(int)(((int)r >> 3) << 11); + *(bufouto++)=tmp&0xff; + *(bufouto++)=tmp>>8; + + } + bufoute+=oskip; + bufouto+=oskip; + bufy+=yskip<<1; + } +} + +void RTjpeg_yuvrgb8(__u8 *buf, __u8 *rgb) +{ + bcopy(buf, rgb, RTjpeg_width*RTjpeg_height); +} + +void RTjpeg_double32(__u32 *buf) +{ + int i, j; + + __u32 *iptr, *optr1, *optr2; + + iptr=buf+(RTjpeg_width*RTjpeg_height)-1; + optr1=buf+(RTjpeg_width*RTjpeg_height*4)-1; + optr2=optr1-(2*RTjpeg_width); + + for(i=0; i<RTjpeg_height; i++) + { + for(j=0; j<RTjpeg_width; j++) + { + *(optr1--)=*iptr; + *(optr1--)=*iptr; + *(optr2--)=*iptr; + *(optr2--)=*(iptr--); + } + optr2=optr2-2*RTjpeg_width; + optr1=optr1-2*RTjpeg_width; + } +} + +void RTjpeg_double24(__u8 *buf) +{ +} + +void RTjpeg_double16(__u16 *buf) +{ + int i, j; + + __u16 *iptr, *optr1, *optr2; + + iptr=buf+(RTjpeg_width*RTjpeg_height)-1; + optr1=buf+(RTjpeg_width*RTjpeg_height*4)-1; + optr2=optr1-(2*RTjpeg_width); + + for(i=0; i<RTjpeg_height; i++) + { + for(j=0; j<RTjpeg_width; j++) + { + *(optr1--)=*iptr; + *(optr1--)=*iptr; + *(optr2--)=*iptr; + *(optr2--)=*(iptr--); + } + optr2=optr2-2*RTjpeg_width; + optr1=optr1-2*RTjpeg_width; + } +} + +void RTjpeg_double8(__u8 *buf) +{ + int i, j; + + __u8 *iptr, *optr1, *optr2; + + iptr=buf+(RTjpeg_width*RTjpeg_height)-1; + optr1=buf+(RTjpeg_width*RTjpeg_height*4)-1; + optr2=optr1-(2*RTjpeg_width); + + for(i=0; i<RTjpeg_height; i++) + { + for(j=0; j<RTjpeg_width; j++) + { + *(optr1--)=*iptr; + *(optr1--)=*iptr; + *(optr2--)=*iptr; + *(optr2--)=*(iptr--); + } + optr2=optr2-2*RTjpeg_width; + optr1=optr1-2*RTjpeg_width; + } +} + diff --git a/gst/rtjpeg/RTjpeg.h b/gst/rtjpeg/RTjpeg.h new file mode 100644 index 000000000..90e2a8d50 --- /dev/null +++ b/gst/rtjpeg/RTjpeg.h @@ -0,0 +1,53 @@ +/* + bttvgrab 0.15.4 [1999-03-23] + (c) 1998, 1999 by Joerg Walter <trouble@moes.pmnet.uni-oldenburg.de> + Maintained by: Joerg Walter + Current version at http://moes.pmnet.uni-oldenburg.de/bttvgrab/ + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + This file is a modified version of RTjpeg 0.1.2, (C) Justin Schoeman 1998 +*/ + +#include <config.h> +#include <inttypes.h> + +typedef uint8_t __u8; +typedef uint32_t __u32; +typedef int8_t __s8; +typedef uint16_t __u16; + +extern void RTjpeg_init_Q(__u8 Q); +extern void RTjpeg_init_compress(long unsigned int *buf, int width, int height, __u8 Q); +extern void RTjpeg_init_decompress(long unsigned int *buf, int width, int height); +extern int RTjpeg_compressYUV420(__s8 *sp, unsigned char *bp); +extern int RTjpeg_compressYUV422(__s8 *sp, unsigned char *bp); +extern void RTjpeg_decompressYUV420(__s8 *sp, __u8 *bp); +extern void RTjpeg_decompressYUV422(__s8 *sp, __u8 *bp); +extern int RTjpeg_compress8(__s8 *sp, unsigned char *bp); +extern void RTjpeg_decompress8(__s8 *sp, __u8 *bp); + +extern void RTjpeg_init_mcompress(void); +extern int RTjpeg_mcompress(__s8 *sp, unsigned char *bp, __u16 lmask, __u16 cmask); +extern int RTjpeg_mcompress8(__s8 *sp, unsigned char *bp, __u16 lmask); +extern void RTjpeg_set_test(int i); + +extern void RTjpeg_yuv420rgb(__u8 *buf, __u8 *rgb); +extern void RTjpeg_yuv422rgb(__u8 *buf, __u8 *rgb); +extern void RTjpeg_yuvrgb8(__u8 *buf, __u8 *rgb); +extern void RTjpeg_yuvrgb16(__u8 *buf, __u8 *rgb); +extern void RTjpeg_yuvrgb24(__u8 *buf, __u8 *rgb); +extern void RTjpeg_yuvrgb32(__u8 *buf, __u8 *rgb); + diff --git a/gst/rtjpeg/gstrtjpeg.c b/gst/rtjpeg/gstrtjpeg.c new file mode 100644 index 000000000..335914f62 --- /dev/null +++ b/gst/rtjpeg/gstrtjpeg.c @@ -0,0 +1,62 @@ +/* Gnome-Streamer + * 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. + */ + + +#include <gstrtjpegenc.h> +#include <gstrtjpegdec.h> + +extern GstElementDetails gst_rtjpegenc_details; +extern GstElementDetails gst_rtjpegdec_details; + +GstTypeDefinition rtjpegdefinition = { + "rtjpeg_video/rtjpeg", + "video/rtjpeg", + ".rtj", + NULL, +}; + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *enc, *dec; + + gst_plugin_set_longname(plugin,"Justin Schoeman's RTjpeg codec and \ +conversion utilities"); + + /* create an elementfactory for the rtjpegenc element */ + enc = gst_elementfactory_new("rtjpegenc",GST_TYPE_RTJPEGENC, + &rtjpegenc_details); + g_return_val_if_fail(enc != NULL, FALSE); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (enc)); + + /* create an elementfactory for the rtjpegdec element */ + dec = gst_elementfactory_new("rtjpegdec",GST_TYPE_RTJPEGDEC, + &rtjpegdec_details); + g_return_val_if_fail(dec != NULL, FALSE); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (dec)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "rtjpeg", + plugin_init +}; diff --git a/gst/rtjpeg/gstrtjpegdec.c b/gst/rtjpeg/gstrtjpegdec.c new file mode 100644 index 000000000..f5dc58254 --- /dev/null +++ b/gst/rtjpeg/gstrtjpegdec.c @@ -0,0 +1,114 @@ +/* Gnome-Streamer + * 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. + */ + + +#include <gstrtjpegdec.h> + + + +/* elementfactory information */ +GstElementDetails gst_rtjpegdec_details = { + "RTjpeg decoder", + "Filter/Video/Decoder", + "Decodes video in RTjpeg format", + VERSION, + "Erik Walthinsen <omega@cse.ogi.edu>", + "(C) 1999", +}; + +/* GstRTJpegDec signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_QUALITY, +}; + + +static void gst_rtjpegdec_class_init (GstRTJpegDecClass *klass); +static void gst_rtjpegdec_init (GstRTJpegDec *rtjpegdec); + +static void gst_rtjpegdec_chain (GstPad *pad, GstBuffer *buf); + +static GstElementClass *parent_class = NULL; +//static guint gst_rtjpegdec_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_rtjpegdec_get_type (void) +{ + static GType rtjpegdec_type = 0; + + if (!rtjpegdec_type) { + static const GTypeInfo rtjpegdec_info = { + sizeof(GstRTJpegDecClass), NULL, + NULL, + (GClassInitFunc)gst_rtjpegdec_class_init, + NULL, + NULL, + sizeof(GstRTJpegDec), + 0, + (GInstanceInitFunc)gst_rtjpegdec_init, + }; + rtjpegdec_type = g_type_register_static(GST_TYPE_ELEMENT, "GstRTJpegDec", &rtjpegdec_info, 0); + } + return rtjpegdec_type; +} + +static void +gst_rtjpegdec_class_init (GstRTJpegDecClass *klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); +} + +static void +gst_rtjpegdec_init (GstRTJpegDec *rtjpegdec) +{ + rtjpegdec->sinkpad = gst_pad_new("sink",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(rtjpegdec),rtjpegdec->sinkpad); + gst_pad_set_chain_function(rtjpegdec->sinkpad,gst_rtjpegdec_chain); + rtjpegdec->srcpad = gst_pad_new("src",GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(rtjpegdec),rtjpegdec->srcpad); +} + +static void +gst_rtjpegdec_chain (GstPad *pad, GstBuffer *buf) +{ + GstRTJpegDec *rtjpegdec; + guchar *data; + gulong size; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + rtjpegdec = GST_RTJPEGDEC (GST_OBJECT_PARENT (pad)); + data = GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf); + + gst_info("would be encoding frame here\n"); + + gst_pad_push(rtjpegdec->srcpad,buf); +} diff --git a/gst/rtjpeg/gstrtjpegdec.h b/gst/rtjpeg/gstrtjpegdec.h new file mode 100644 index 000000000..853bf3454 --- /dev/null +++ b/gst/rtjpeg/gstrtjpegdec.h @@ -0,0 +1,71 @@ +/* Gnome-Streamer + * 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 __RTJPEGDEC_H__ +#define __RTJPEGDEC_H__ + + +#include <config.h> +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_RTJPEGDEC \ + (gst_rtjpegdec_get_type()) +#define GST_RTJPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTJPEGDEC,GstRTJpegDec)) +#define GST_RTJPEGDEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTJPEGDEC,GstRTJpegDec)) +#define GST_IS_RTJPEGDEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTJPEGDEC)) +#define GST_IS_RTJPEGDEC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTJPEGDEC))) + +typedef struct _GstRTJpegDec GstRTJpegDec; +typedef struct _GstRTJpegDecClass GstRTJpegDecClass; + +struct _GstRTJpegDec { + GstElement element; + + GstPad *sinkpad,*srcpad; + + gint width,height; + gint quality; + gint quant[128]; + +}; + +struct _GstRTJpegDecClass { + GstElementClass parent_class; +}; + +GType gst_rtjpegdec_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __RTJPEGDEC_H__ */ diff --git a/gst/rtjpeg/gstrtjpegenc.c b/gst/rtjpeg/gstrtjpegenc.c new file mode 100644 index 000000000..68b1e843b --- /dev/null +++ b/gst/rtjpeg/gstrtjpegenc.c @@ -0,0 +1,112 @@ +/* Gnome-Streamer + * 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. + */ + + +#include <gstrtjpegenc.h> + +/* elementfactory information */ +GstElementDetails gst_rtjpegenc_details = { + "RTjpeg encoder", + "Filter/Video/Encoder", + "Encodes video in RTjpeg format", + VERSION, + "Erik Walthinsen <omega@cse.ogi.edu>", + "(C) 1999", +}; + +/* GstRTJpegEnc signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_QUALITY, +}; + + +static void gst_rtjpegenc_class_init (GstRTJpegEncClass *klass); +static void gst_rtjpegenc_init (GstRTJpegEnc *rtjpegenc); + +static void gst_rtjpegenc_chain (GstPad *pad, GstBuffer *buf); + +static GstElementClass *parent_class = NULL; +//static guint gst_rtjpegenc_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_rtjpegenc_get_type (void) +{ + static GType rtjpegenc_type = 0; + + if (!rtjpegenc_type) { + static const GTypeInfo rtjpegenc_info = { + sizeof(GstRTJpegEncClass), NULL, + NULL, + (GClassInitFunc)gst_rtjpegenc_class_init, + NULL, + NULL, + sizeof(GstRTJpegEnc), + 0, + (GInstanceInitFunc)gst_rtjpegenc_init, + }; + rtjpegenc_type = g_type_register_static(GST_TYPE_ELEMENT, "GstRTJpegEnc", &rtjpegenc_info, 0); + } + return rtjpegenc_type; +} + +static void +gst_rtjpegenc_class_init (GstRTJpegEncClass *klass) +{ + GstElementClass *gstelement_class; + + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); +} + +static void +gst_rtjpegenc_init (GstRTJpegEnc *rtjpegenc) +{ + rtjpegenc->sinkpad = gst_pad_new("sink",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(rtjpegenc),rtjpegenc->sinkpad); + gst_pad_set_chain_function(rtjpegenc->sinkpad,gst_rtjpegenc_chain); + rtjpegenc->srcpad = gst_pad_new("src",GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(rtjpegenc),rtjpegenc->srcpad); +} + +static void +gst_rtjpegenc_chain (GstPad *pad, GstBuffer *buf) +{ + GstRTJpegEnc *rtjpegenc; + guchar *data; + gulong size; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + rtjpegenc = GST_RTJPEGENC (GST_OBJECT_PARENT (pad)); + data = GST_BUFFER_DATA(buf); + size = GST_BUFFER_SIZE(buf); + + gst_info("would be encoding frame here\n"); + + gst_pad_push(rtjpegenc->srcpad,buf); +} diff --git a/gst/rtjpeg/gstrtjpegenc.h b/gst/rtjpeg/gstrtjpegenc.h new file mode 100644 index 000000000..837616ab9 --- /dev/null +++ b/gst/rtjpeg/gstrtjpegenc.h @@ -0,0 +1,72 @@ +/* Gnome-Streamer + * 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 __RTJPEGENC_H__ +#define __RTJPEGENC_H__ + + +#include <config.h> +#include <gst/gst.h> + +#include "RTjpeg.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_RTJPEGENC \ + (gst_rtjpegenc_get_type()) +#define GST_RTJPEGENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTJPEGENC,GstRTJpegEnc)) +#define GST_RTJPEGENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTJPEGENC,GstRTJpegEnc)) +#define GST_IS_RTJPEGENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTJPEGENC)) +#define GST_IS_RTJPEGENC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTJPEGENC)) + +typedef struct _GstRTJpegEnc GstRTJpegEnc; +typedef struct _GstRTJpegEncClass GstRTJpegEncClass; + +struct _GstRTJpegEnc { + GstElement element; + + GstPad *sinkpad,*srcpad; + + gint width,height; + gint quality; + gint quant[128]; +}; + +struct _GstRTJpegEncClass { + GstElementClass parent_class; +}; + +GType gst_rtjpegenc_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __RTJPEGENC_H__ */ diff --git a/gst/smooth/.gitignore b/gst/smooth/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/smooth/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/smooth/Makefile.am b/gst/smooth/Makefile.am new file mode 100644 index 000000000..f4856a14f --- /dev/null +++ b/gst/smooth/Makefile.am @@ -0,0 +1,8 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstsmooth.la + +libgstsmooth_la_SOURCES = gstsmooth.c +libgstsmooth_la_CFLAGS = -O3 $(FOMIT_FRAME_POINTER) -funroll-all-loops -finline-functions -ffast-math $(GST_CFLAGS) + +noinst_HEADERS = gstsmooth.h diff --git a/gst/smooth/gstsmooth.c b/gst/smooth/gstsmooth.c new file mode 100644 index 000000000..5f1f507ca --- /dev/null +++ b/gst/smooth/gstsmooth.c @@ -0,0 +1,362 @@ +/* Gnome-Streamer + * 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. + */ + +#include <string.h> +#include <gstsmooth.h> + + +static GstElementDetails smooth_details = { + "Smooth effect", + "Filter/Effect", + "apply a smooth filter to an image", + VERSION, + "Wim Taymans <wim.taymans@chello.be>", + "(C) 2000", +}; + + +/* Smooth signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_ACTIVE, + ARG_TOLERANCE, + ARG_FILTERSIZE, + ARG_LUM_ONLY +}; + +GST_PADTEMPLATE_FACTORY (smooth_src_factory, + "src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "smooth_src", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")) + ) +) + +GST_PADTEMPLATE_FACTORY (smooth_sink_factory, + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_CAPS_NEW ( + "smooth_src", + "video/raw", + "format", GST_PROPS_FOURCC (GST_STR_FOURCC ("I420")) + ) +) + +static void gst_smooth_class_init (GstSmoothClass *klass); +static void gst_smooth_init (GstSmooth *smooth); + +static void gst_smooth_chain (GstPad *pad, GstBuffer *buf); +static void smooth_filter (unsigned char* dest, unsigned char* src, + int width, int height, int tolerance, int filtersize); + +static void gst_smooth_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_smooth_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static GstElementClass *parent_class = NULL; +//static guint gst_smooth_signals[LAST_SIGNAL] = { 0 }; + +static GstPadNegotiateReturn +smooth_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstSmooth* filter = GST_SMOOTH (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy (pad, filter->sinkpad, caps); +} + +static GstPadNegotiateReturn +smooth_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstSmooth* filter = GST_SMOOTH (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy (pad, filter->srcpad, caps); +} + +GType +gst_smooth_get_type (void) +{ + static GType smooth_type = 0; + + if (!smooth_type) { + static const GTypeInfo smooth_info = { + sizeof(GstSmoothClass), NULL, + NULL, + (GClassInitFunc)gst_smooth_class_init, + NULL, + NULL, + sizeof(GstSmooth), + 0, + (GInstanceInitFunc)gst_smooth_init, + }; + smooth_type = g_type_register_static(GST_TYPE_ELEMENT, "GstSmooth", &smooth_info, 0); + } + return smooth_type; +} + +static void +gst_smooth_class_init (GstSmoothClass *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(G_OBJECT_CLASS(klass), ARG_ACTIVE, + g_param_spec_boolean("active","active","active", + TRUE,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TOLERANCE, + g_param_spec_int("tolerance","tolerance","tolerance", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FILTERSIZE, + g_param_spec_int("filtersize","filtersize","filtersize", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + + gobject_class->set_property = gst_smooth_set_property; + gobject_class->get_property = gst_smooth_get_property; + +} + +static void +gst_smooth_newcaps (GstPad *pad, GstCaps *caps) +{ + GstSmooth *filter; + + filter = GST_SMOOTH (gst_pad_get_parent (pad)); + + filter->width = gst_caps_get_int (caps, "width"); + filter->height = gst_caps_get_int (caps, "height"); +} + +static void +gst_smooth_init (GstSmooth *smooth) +{ + smooth->sinkpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (smooth_sink_factory), "sink"); + gst_pad_set_negotiate_function (smooth->sinkpad, smooth_negotiate_sink); + gst_pad_set_newcaps_function (smooth->sinkpad, gst_smooth_newcaps); + gst_pad_set_chain_function (smooth->sinkpad, gst_smooth_chain); + gst_element_add_pad (GST_ELEMENT (smooth), smooth->sinkpad); + + smooth->srcpad = gst_pad_new_from_template ( + GST_PADTEMPLATE_GET (smooth_src_factory), "src"); + gst_pad_set_negotiate_function (smooth->srcpad, smooth_negotiate_src); + gst_element_add_pad (GST_ELEMENT (smooth), smooth->srcpad); + + smooth->active = TRUE; + smooth->tolerance = 8; + smooth->filtersize = 3; + smooth->lum_only = TRUE; +} + +static void +smooth_filter (unsigned char* dest, unsigned char* src, int width, int height, int tolerance, int filtersize) +{ + int refval, aktval, upperval, lowerval, numvalues, sum; + int x, y, fx, fy, fy1, fy2, fx1, fx2; + unsigned char *srcp = src; + + fy1 = 0; + fy2 = MIN(filtersize+1, height) * width; + + for(y = 0; y < height; y++) + { + if (y>(filtersize+1)) fy1 += width; + if (y<height-(filtersize+1)) fy2 += width; + + for(x = 0; x < width; x++) + { + refval = *src; + upperval = refval + tolerance; + lowerval = refval - tolerance; + + numvalues = 1; + sum = refval; + + fx1 = MAX(x-filtersize, 0) + fy1; + fx2 = MIN(x+filtersize+1, width) + fy1; + + for (fy = fy1; fy<fy2; fy+=width) + { + for (fx = fx1; fx<fx2; fx++) + { + aktval = srcp[fx]; + if ((lowerval-aktval)*(upperval-aktval)<0) + { + numvalues ++; + sum += aktval; + } + } //for fx + fx1 += width; + fx2 += width; + } //for fy + + src++; + *dest++ = sum/numvalues; + } + } +} + +static void +gst_smooth_chain (GstPad *pad, GstBuffer *buf) +{ + GstSmooth *smooth; + guchar *data; + gulong size; + GstBuffer *outbuf; + gint lumsize, chromsize; + + g_return_if_fail (pad != NULL); + g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (buf != NULL); + + smooth = GST_SMOOTH (GST_OBJECT_PARENT (pad)); + + if (!smooth->active) { + gst_pad_push(smooth->srcpad,buf); + return; + } + + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + GST_DEBUG (0,"smooth: have buffer of %d\n", GST_BUFFER_SIZE (buf)); + + outbuf = gst_buffer_new(); + GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (buf)); + GST_BUFFER_SIZE (outbuf) = GST_BUFFER_SIZE (buf); + + lumsize = smooth->width*smooth->height; + chromsize = lumsize/4; + + smooth_filter (GST_BUFFER_DATA (outbuf), data, smooth->width, smooth->height, + smooth->tolerance, smooth->filtersize); + if (!smooth->lum_only) { + smooth_filter (GST_BUFFER_DATA (outbuf)+lumsize, data+lumsize, smooth->width/2, smooth->height/2, + smooth->tolerance, smooth->filtersize/2); + smooth_filter (GST_BUFFER_DATA (outbuf)+lumsize+chromsize, data+lumsize+chromsize, smooth->width/2, + smooth->height/2, smooth->tolerance, smooth->filtersize/2); + } + else { + memcpy (GST_BUFFER_DATA (outbuf)+lumsize, data+lumsize, chromsize*2); + } + + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); + + gst_buffer_unref (buf); + + gst_pad_push (smooth->srcpad, outbuf); +} + +static void +gst_smooth_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstSmooth *smooth; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SMOOTH(object)); + smooth = GST_SMOOTH(object); + + switch (prop_id) { + case ARG_ACTIVE: + smooth->active = g_value_get_boolean (value); + break; + case ARG_TOLERANCE: + smooth->tolerance = g_value_get_int (value); + break; + case ARG_FILTERSIZE: + smooth->filtersize = g_value_get_int (value); + break; + case ARG_LUM_ONLY: + smooth->lum_only = g_value_get_boolean (value); + break; + default: + break; + } +} + +static void +gst_smooth_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstSmooth *smooth; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SMOOTH(object)); + smooth = GST_SMOOTH(object); + + switch (prop_id) { + case ARG_ACTIVE: + g_value_set_boolean (value, smooth->active); + break; + case ARG_TOLERANCE: + g_value_set_int (value, smooth->tolerance); + break; + case ARG_FILTERSIZE: + g_value_set_int (value, smooth->filtersize); + break; + case ARG_LUM_ONLY: + g_value_set_boolean (value, smooth->lum_only); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("smooth",GST_TYPE_SMOOTH, + &smooth_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (smooth_sink_factory)); + gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (smooth_src_factory)); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "smooth", + plugin_init +}; + diff --git a/gst/smooth/gstsmooth.h b/gst/smooth/gstsmooth.h new file mode 100644 index 000000000..239e074f5 --- /dev/null +++ b/gst/smooth/gstsmooth.h @@ -0,0 +1,75 @@ +/* Gnome-Streamer + * 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_SMOOTH_H__ +#define __GST_SMOOTH_H__ + + +#include <config.h> +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_SMOOTH \ + (gst_smooth_get_type()) +#define GST_SMOOTH(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SMOOTH,GstSmooth)) +#define GST_SMOOTH_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SMOOTH,GstSmooth)) +#define GST_IS_SMOOTH(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SMOOTH)) +#define GST_IS_SMOOTH_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SMOOTH)) + +typedef struct _GstSmooth GstSmooth; +typedef struct _GstSmoothClass GstSmoothClass; + +struct _GstSmooth { + GstElement element; + + int format; + int width; + int height; + + gboolean active; + int tolerance; + int filtersize; + gboolean lum_only; + + GstPad *sinkpad,*srcpad; +}; + +struct _GstSmoothClass { + GstElementClass parent_class; +}; + +GType gst_smooth_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_SMOOTH_H__ */ diff --git a/gst/spectrum/.gitignore b/gst/spectrum/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/spectrum/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/spectrum/Makefile.am b/gst/spectrum/Makefile.am new file mode 100644 index 000000000..476c3fd15 --- /dev/null +++ b/gst/spectrum/Makefile.am @@ -0,0 +1,10 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstspectrum.la + +libgstspectrum_la_SOURCES = gstspectrum.c fix_fft.c +libgstspectrum_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstspectrum.h + +EXTRA_DIST = README diff --git a/gst/spectrum/README b/gst/spectrum/README new file mode 100644 index 000000000..87555712f --- /dev/null +++ b/gst/spectrum/README @@ -0,0 +1,5 @@ +This is a simple, rather lame spectrum analyzer made from the fix_fft.c +code, as found I think in xmms-0.9.1 (the 75-wide output sounds like xmms +to me), which is actually written by other people (see fix_fft.c for +credits). It worked last time I had GiST working, which was a while ago. +Yes, GiST is not included here yet, it will be in 0.1.0. diff --git a/gst/spectrum/fix_fft.c b/gst/spectrum/fix_fft.c new file mode 100644 index 000000000..ecd703031 --- /dev/null +++ b/gst/spectrum/fix_fft.c @@ -0,0 +1,452 @@ +/* fix_fft.c - Fixed-point Fast Fourier Transform */ +/* + fix_fft() perform FFT or inverse FFT + window() applies a Hanning window to the (time) input + fix_loud() calculates the loudness of the signal, for + each freq point. Result is an integer array, + units are dB (values will be negative). + iscale() scale an integer value by (numer/denom). + fix_mpy() perform fixed-point multiplication. + Sinewave[1024] sinewave normalized to 32767 (= 1.0). + Loudampl[100] Amplitudes for lopudnesses from 0 to -99 dB. + Low_pass Low-pass filter, cutoff at sample_freq / 4. + + All data are fixed-point short integers, in which + -32768 to +32768 represent -1.0 to +1.0. Integer arithmetic + is used for speed, instead of the more natural floating-point. + + For the forward FFT (time -> freq), fixed scaling is + performed to prevent arithmetic overflow, and to map a 0dB + sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq + coefficients; the one in the lower half is reported as 0dB + by fix_loud(). The return value is always 0. + + For the inverse FFT (freq -> time), fixed scaling cannot be + done, as two 0dB coefficients would sum to a peak amplitude of + 64K, overflowing the 32k range of the fixed-point integers. + Thus, the fix_fft() routine performs variable scaling, and + returns a value which is the number of bits LEFT by which + the output must be shifted to get the actual amplitude + (i.e. if fix_fft() returns 3, each value of fr[] and fi[] + must be multiplied by 8 (2**3) for proper scaling. + Clearly, this cannot be done within the fixed-point short + integers. In practice, if the result is to be used as a + filter, the scale_shift can usually be ignored, as the + result will be approximately correctly normalized as is. + + TURBO C, any memory model; uses inline assembly for speed + and for carefully-scaled arithmetic. + + Written by: Tom Roberts 11/8/89 + Made portable: Malcolm Slaney 12/15/94 malcolm@interval.com + + Timing on a Macintosh PowerBook 180.... (using Symantec C6.0) + fix_fft (1024 points) 8 ticks + fft (1024 points - Using SANE) 112 Ticks + fft (1024 points - Using FPU) 11 + + */ + +#define fixed short + +/* FIX_MPY() - fixed-point multiplication macro. + This macro is a statement, not an expression (uses asm). + BEWARE: make sure _DX is not clobbered by evaluating (A) or DEST. + args are all of type fixed. + Scaling ensures that 32767*32767 = 32767. */ + +#define FIX_MPY(DEST,A,B) DEST = ((long)(A) * (long)(B))>>15 + +#define N_WAVE 1024 /* dimension of Sinewave[] */ +#define LOG2_N_WAVE 10 /* log2(N_WAVE) */ +#define N_LOUD 100 /* dimension of Loudampl[] */ + +extern fixed gst_spectrum_Sinewave[N_WAVE]; /* placed at end of this file for clarity */ +extern fixed gst_spectrum_Loudampl[N_LOUD]; +static int gst_spectrum_db_from_ampl(fixed re, fixed im); +static fixed gst_spectrum_fix_mpy(fixed a, fixed b); + +/* + fix_fft() - perform fast Fourier transform. + + if n>0 FFT is done, if n<0 inverse FFT is done + fr[n],fi[n] are real,imaginary arrays, INPUT AND RESULT. + size of data = 2**m + set inverse to 0=dft, 1=idft + */ +int gst_spectrum_fix_fft(fixed fr[], fixed fi[], int m, int inverse) { + int mr, nn, i, j, l, k, istep, n, scale, shift; + fixed qr, qi, tr, ti, wr, wi, t; + + n = 1 << m; + + if (n > N_WAVE) + return -1; + + mr = 0; + nn = n - 1; + scale = 0; + + /* decimation in time - re-order data */ + for (m = 1; m <= nn; ++m) + { + l = n; + do + { + l >>= 1; + } + while (mr + l > nn); + mr = (mr & (l - 1)) + l; + + if (mr <= m) + continue; + tr = fr[m]; + fr[m] = fr[mr]; + fr[mr] = tr; + ti = fi[m]; + fi[m] = fi[mr]; + fi[mr] = ti; + } + + l = 1; + k = LOG2_N_WAVE - 1; + while (l < n) + { + if (inverse) + { + /* variable scaling, depending upon data */ + shift = 0; + for (i = 0; i < n; ++i) + { + j = fr[i]; + if (j < 0) + j = -j; + m = fi[i]; + if (m < 0) + m = -m; + if (j > 16383 || m > 16383) + { + shift = 1; + break; + } + } + if (shift) + ++scale; + } + else + { + /* fixed scaling, for proper normalization - + there will be log2(n) passes, so this + results in an overall factor of 1/n, + distributed to maximize arithmetic accuracy. */ + shift = 1; + } + /* it may not be obvious, but the shift will be performed + on each data point exactly once, during this pass. */ + istep = l << 1; + for (m = 0; m < l; ++m) + { + j = m << k; + /* 0 <= j < N_WAVE/2 */ + wr = gst_spectrum_Sinewave[j + N_WAVE / 4]; + wi = -gst_spectrum_Sinewave[j]; + if (inverse) + wi = -wi; + if (shift) + { + wr >>= 1; + wi >>= 1; + } + for (i = m; i < n; i += istep) + { + j = i + l; + tr = gst_spectrum_fix_mpy(wr, fr[j]) - + gst_spectrum_fix_mpy(wi, fi[j]); + ti = gst_spectrum_fix_mpy(wr, fi[j]) + + gst_spectrum_fix_mpy(wi, fr[j]); + qr = fr[i]; + qi = fi[i]; + if (shift) + { + qr >>= 1; + qi >>= 1; + } + fr[j] = qr - tr; + fi[j] = qi - ti; + fr[i] = qr + tr; + fi[i] = qi + ti; + } + } + --k; + l = istep; + } + + return scale; +} + +/* window() - apply a Hanning window */ +void gst_spectrum_window(fixed fr[], int n) { + int i, j, k; + + j = N_WAVE / n; + n >>= 1; + for (i = 0, k = N_WAVE / 4; i < n; ++i, k += j) + FIX_MPY(fr[i], fr[i], 16384 - (gst_spectrum_Sinewave[k] >> 1)); + n <<= 1; + for (k -= j; i < n; ++i, k -= j) + FIX_MPY(fr[i], fr[i], 16384 - (gst_spectrum_Sinewave[k] >> 1)); +} + +/* fix_loud() - compute loudness of freq-vis components. + n should be ntot/2, where ntot was passed to fix_fft(); + 6 dB is added to account for the omitted alias components. + scale_shift should be the result of fix_fft(), if the time-series + was obtained from an inverse FFT, 0 otherwise. + loud[] is the loudness, in dB wrt 32767; will be +10 to -N_LOUD. + */ +void gst_spectrum_fix_loud(fixed loud[], fixed fr[], fixed fi[], int n, int scale_shift) { + int i, max; + + max = 0; + if (scale_shift > 0) + max = 10; + scale_shift = (scale_shift + 1) * 6; + + for (i = 0; i < n; ++i) + { + loud[i] = gst_spectrum_db_from_ampl(fr[i], fi[i]) + scale_shift; + if (loud[i] > max) + loud[i] = max; + } +} + +/* db_from_ampl() - find loudness (in dB) from + the complex amplitude. + */ +int gst_spectrum_db_from_ampl(fixed re, fixed im) { + static long loud2[N_LOUD] = + {0}; + long v; + int i; + + if (loud2[0] == 0) + { + loud2[0] = (long) gst_spectrum_Loudampl[0] * (long) gst_spectrum_Loudampl[0]; + for (i = 1; i < N_LOUD; ++i) + { + v = (long) gst_spectrum_Loudampl[i] * (long) gst_spectrum_Loudampl[i]; + loud2[i] = v; + loud2[i - 1] = (loud2[i - 1] + v) / 2; + } + } + + v = (long) re *(long) re + (long) im *(long) im; + + for (i = 0; i < N_LOUD; ++i) + if (loud2[i] <= v) + break; + + return (-i); +} + +/* + fix_mpy() - fixed-point multiplication + */ +fixed gst_spectrum_fix_mpy(fixed a, fixed b) { + FIX_MPY(a, a, b); + return a; +} + +/* + iscale() - scale an integer value by (numer/denom) + */ +int gst_spectrum_iscale(int value, int numer, int denom) { + return (long) value *(long) numer / (long) denom; +} + +/* + fix_dot() - dot product of two fixed arrays + */ +fixed gst_spectrum_fix_dot(fixed * hpa, fixed * pb, int n) { + fixed *pa; + long sum; + register fixed a, b; + unsigned int seg, off; + +/* seg = FP_SEG(hpa); + off = FP_OFF(hpa); + seg += off>>4; + off &= 0x000F; + pa = MK_FP(seg,off); + */ + sum = 0L; + while (n--) + { + a = *pa++; + b = *pb++; + FIX_MPY(a, a, b); + sum += a; + } + + if (sum > 0x7FFF) + sum = 0x7FFF; + else if (sum < -0x7FFF) + sum = -0x7FFF; + + return (fixed) sum; + +} + +#if N_WAVE != 1024 +ERROR:N_WAVE != 1024 +#endif +fixed gst_spectrum_Sinewave[1024] = { + 0, 201, 402, 603, 804, 1005, 1206, 1406, + 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011, + 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608, + 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195, + 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766, + 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319, + 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849, + 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353, + 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827, + 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268, + 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672, + 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036, + 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357, + 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631, + 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855, + 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027, + 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143, + 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201, + 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198, + 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132, + 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001, + 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802, + 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534, + 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195, + 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783, + 30851, 30918, 30984, 31049, + 31113, 31175, 31236, 31297, + 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735, + 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097, + 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382, + 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588, + 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717, + 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766, + 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736, + 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628, + 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441, + 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176, + 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833, + 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413, + 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918, + 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349, + 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706, + 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992, + 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208, + 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355, + 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437, + 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456, + 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413, + 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311, + 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153, + 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942, + 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680, + 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371, + 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017, + 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623, + 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191, + 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724, + 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227, + 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703, + 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156, + 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589, + 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006, + 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411, + 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808, + 1607, 1406, 1206, 1005, 804, 603, 402, 201, + 0, -201, -402, -603, -804, -1005, -1206, -1406, + -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011, + -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608, + -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195, + -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766, + -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319, + -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849, + -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353, + -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827, + -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268, + -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672, + -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036, + -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357, + -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631, + -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855, + -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027, + -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143, + -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201, + -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198, + -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132, + -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001, + -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802, + -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534, + -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195, + -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783, + -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297, + -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735, + -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097, + -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382, + -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588, + -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717, + -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766, + -32767, -32766, -32764, -32761, -32757, -32751, -32744, -32736, + -32727, -32717, -32705, -32692, -32678, -32662, -32646, -32628, + -32609, -32588, -32567, -32544, -32520, -32495, -32468, -32441, + -32412, -32382, -32350, -32318, -32284, -32249, -32213, -32176, + -32137, -32097, -32056, -32014, -31970, -31926, -31880, -31833, + -31785, -31735, -31684, -31633, -31580, -31525, -31470, -31413, + -31356, -31297, -31236, -31175, -31113, -31049, -30984, -30918, + -30851, -30783, -30713, -30643, -30571, -30498, -30424, -30349, + -30272, -30195, -30116, -30036, -29955, -29873, -29790, -29706, + -29621, -29534, -29446, -29358, -29268, -29177, -29085, -28992, + -28897, -28802, -28706, -28608, -28510, -28410, -28309, -28208, + -28105, -28001, -27896, -27790, -27683, -27575, -27466, -27355, + -27244, -27132, -27019, -26905, -26789, -26673, -26556, -26437, + -26318, -26198, -26077, -25954, -25831, -25707, -25582, -25456, + -25329, -25201, -25072, -24942, -24811, -24679, -24546, -24413, + -24278, -24143, -24006, -23869, -23731, -23592, -23452, -23311, + -23169, -23027, -22883, -22739, -22594, -22448, -22301, -22153, + -22004, -21855, -21705, -21554, -21402, -21249, -21096, -20942, + -20787, -20631, -20474, -20317, -20159, -20000, -19840, -19680, + -19519, -19357, -19194, -19031, -18867, -18702, -18537, -18371, + -18204, -18036, -17868, -17699, -17530, -17360, -17189, -17017, + -16845, -16672, -16499, -16325, -16150, -15975, -15799, -15623, + -15446, -15268, -15090, -14911, -14732, -14552, -14372, -14191, + -14009, -13827, -13645, -13462, -13278, -13094, -12909, -12724, + -12539, -12353, -12166, -11980, -11792, -11604, -11416, -11227, + -11038, -10849, -10659, -10469, -10278, -10087, -9895, -9703, + -9511, -9319, -9126, -8932, -8739, -8545, -8351, -8156, + -7961, -7766, -7571, -7375, -7179, -6982, -6786, -6589, + -6392, -6195, -5997, -5799, -5601, -5403, -5205, -5006, + -4807, -4608, -4409, -4210, -4011, -3811, -3611, -3411, + -3211, -3011, -2811, -2610, -2410, -2209, -2009, -1808, + -1607, -1406, -1206, -1005, -804, -603, -402, -201, +}; + +#if N_LOUD != 100 +ERROR:N_LOUD != 100 +#endif +fixed gst_spectrum_Loudampl[100] = { + 32767, 29203, 26027, 23197, 20674, 18426, 16422, 14636, + 13044, 11626, 10361, 9234, 8230, 7335, 6537, 5826, + 5193, 4628, 4125, 3676, 3276, 2920, 2602, 2319, + 2067, 1842, 1642, 1463, 1304, 1162, 1036, 923, + 823, 733, 653, 582, 519, 462, 412, 367, + 327, 292, 260, 231, 206, 184, 164, 146, + 130, 116, 103, 92, 82, 73, 65, 58, + 51, 46, 41, 36, 32, 29, 26, 23, + 20, 18, 16, 14, 13, 11, 10, 9, + 8, 7, 6, 5, 5, 4, 4, 3, + 3, 2, 2, 2, 2, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +}; diff --git a/gst/spectrum/gstspectrum.c b/gst/spectrum/gstspectrum.c new file mode 100644 index 000000000..0821545c8 --- /dev/null +++ b/gst/spectrum/gstspectrum.c @@ -0,0 +1,242 @@ +/* Gnome-Streamer + * 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. + */ + +#include <string.h> + +#include "gstspectrum.h" + +static GstElementDetails gst_spectrum_details = { + "Spectrum analyzer", + "Filter/Analysis", + "Run an FFT on the audio signal, output spectrum data", + VERSION, + "Erik Walthinsen <omega@cse.ogi.edu>", + "(C) 1999", +}; + + +static GstTypeDefinition spectrumdefinition = { + "spectrum_spectrum_raw", + "spectrum/raw", + NULL, + NULL, +}; + + +/* Spectrum signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_WIDTH, +}; + + +static void gst_spectrum_class_init (GstSpectrumClass *klass); +static void gst_spectrum_init (GstSpectrum *spectrum); + +static void gst_spectrum_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); + +static void gst_spectrum_chain (GstPad *pad, GstBuffer *buf); + +#define fixed short +int gst_spectrum_fix_fft(fixed fr[], fixed fi[], int m, int inverse); +void gst_spectrum_fix_loud(fixed loud[], fixed fr[], fixed fi[], int n, int scale_shift); +void gst_spectrum_window(fixed fr[], int n); + + +static GstElementClass *parent_class = NULL; +//static guint gst_spectrum_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_spectrum_get_type (void) +{ + static GType spectrum_type = 0; + + if (!spectrum_type) { + static const GTypeInfo spectrum_info = { + sizeof(GstSpectrumClass), NULL, + NULL, + (GClassInitFunc)gst_spectrum_class_init, + NULL, + NULL, + sizeof(GstSpectrum), + 0, + (GInstanceInitFunc)gst_spectrum_init, + }; + spectrum_type = g_type_register_static(GST_TYPE_ELEMENT, "GstSpectrum", &spectrum_info, 0); + } + return spectrum_type; +} + +static void +gst_spectrum_class_init (GstSpectrumClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_WIDTH, + g_param_spec_int("width","width","width", + G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); // CHECKME + + gobject_class->set_property = gst_spectrum_set_property; +} + +static void +gst_spectrum_init (GstSpectrum *spectrum) +{ + spectrum->sinkpad = gst_pad_new("sink",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(spectrum),spectrum->sinkpad); + gst_pad_set_chain_function(spectrum->sinkpad,gst_spectrum_chain); + spectrum->srcpad = gst_pad_new("src",GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(spectrum),spectrum->srcpad); + + spectrum->width = 75; +} + +static void +gst_spectrum_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstSpectrum *spectrum; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SPECTRUM(object)); + spectrum = GST_SPECTRUM(object); + + switch (prop_id) { + case ARG_WIDTH: + spectrum->width = g_value_get_int (value); + break; + default: + break; + } +} + +static void +gst_spectrum_chain (GstPad *pad, GstBuffer *buf) +{ + GstSpectrum *spectrum; + gint spec_base, spec_len; + gint16 *re, *im, *loud; + gint16 *samples; + gint samplecount,step,pos,i; + guchar *spect; + GstBuffer *newbuf; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + spectrum = GST_SPECTRUM (GST_OBJECT_PARENT (pad)); + + /* first deal with audio metadata */ +// FIXME +// if (buf->meta) { +// if (spectrum->meta != NULL) { +// /* FIXME: need to unref the old metadata so it goes away */ +// } +// /* we just make a copy of the pointer */ +// spectrum->meta = (MetaAudioRaw *)(buf->data); +// /* FIXME: now we have to ref the metadata so it does go away */ +// } + + //g_return_if_fail(spectrum->meta != NULL); + + //samplecount = GST_BUFFER_SIZE(buf) / + // (spectrum->meta->channels * sizeof(gint16)); +// samples = (gint16 *)g_malloc(buf->datasize); +// g_return_if_fail(samples != NULL); +// memcpy(samples,(gint16 +//*)GST_BUFFER_DATA(buf),GST_BUFFER_DATASIZE(buf)); +// gst_buffer_unref(buf); + samples = (gint16 *)GST_BUFFER_DATA(buf); + +// return; +// spec_base = (gint) (log(samplecount) / log(2)); +// if (spec_base > 10) spec_base = 10; +// spec_len = (gint) pow(2, spec_base); + spec_base = 8; + spec_len = 1024; + + im = g_malloc(spec_len * sizeof(gint16)); + g_return_if_fail(im != NULL); + loud = g_malloc(spec_len * sizeof(gint16)); + g_return_if_fail(loud != NULL); + + memset(im,0,spec_len * sizeof(gint16)); + //if (spectrum->meta->channels == 2) { + re = g_malloc(spec_len * sizeof(gint16)); + for (i=0;i<spec_len;i++) + re[i] = (samples[(i*2)] + samples[(i*2)+1]) >> 1; + //} else + // re = samples; + gst_spectrum_window(re,spec_len); + gst_spectrum_fix_fft(re,im,spec_base,FALSE); + gst_spectrum_fix_loud(loud,re,im,spec_len,0); + if (re != samples) g_free(re); + g_free(im); + step = spec_len / (spectrum->width*2); + spect = (guchar *)g_malloc(spectrum->width); + for (i=0,pos=0;i<spectrum->width;i++,pos += step) { + if (loud[pos] > -60) + spect[i] = (loud[pos] + 60) / 2; + else + spect[i] = 0; +// if (spect[i] > 15); +// spect[i] = 15; + } + g_free(loud); + gst_buffer_unref(buf); +// g_free(samples); + + newbuf = gst_buffer_new(); + g_return_if_fail(newbuf != NULL); + GST_BUFFER_DATA(newbuf) = spect; + GST_BUFFER_SIZE(newbuf) = spectrum->width; + + gst_pad_push(spectrum->srcpad,newbuf); +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the spectrum element */ + factory = gst_elementfactory_new ("spectrum",GST_TYPE_SPECTRUM, + &gst_spectrum_details); + g_return_val_if_fail (factory != NULL, FALSE); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "spectrum", + plugin_init +}; diff --git a/gst/spectrum/gstspectrum.h b/gst/spectrum/gstspectrum.h new file mode 100644 index 000000000..f7a395ec8 --- /dev/null +++ b/gst/spectrum/gstspectrum.h @@ -0,0 +1,67 @@ +/* Gnome-Streamer + * 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_SPECTRUM_H__ +#define __GST_SPECTRUM_H__ + + +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_SPECTRUM \ + (gst_spectrum_get_type()) +#define GST_SPECTRUM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPECTRUM,GstSpectrum)) +#define GST_SPECTRUM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPECTRUM,GstSpectrum)) +#define GST_IS_SPECTRUM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPECTRUM)) +#define GST_IS_SPECTRUM_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPECTRUM)) + +typedef struct _GstSpectrum GstSpectrum; +typedef struct _GstSpectrumClass GstSpectrumClass; + +struct _GstSpectrum { + GstElement element; + + GstPad *sinkpad,*srcpad; + + gint width; +}; + +struct _GstSpectrumClass { + GstElementClass parent_class; +}; + +GType gst_spectrum_get_type(void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_SPECTRUM_H__ */ diff --git a/gst/speed/Makefile.am b/gst/speed/Makefile.am new file mode 100644 index 000000000..35aed2079 --- /dev/null +++ b/gst/speed/Makefile.am @@ -0,0 +1,8 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgstspeed.la + +libgstspeed_la_SOURCES = gstspeed.c +libgstspeed_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gstspeed.h filter.func diff --git a/gst/speed/filter.func b/gst/speed/filter.func new file mode 100644 index 000000000..89429528a --- /dev/null +++ b/gst/speed/filter.func @@ -0,0 +1,66 @@ +/* -*- Mode: c; c-basic-offset: 2 -*- */ + _FORMAT *in_data, *out_data; + + /* get a buffer here so that we can have something to interpolate + * against for the first few samples if speed < 0.5 */ + in_data = (_FORMAT*) GST_BUFFER_DATA(in); + nin = GST_BUFFER_SIZE(in)/sizeof(_FORMAT); + lower = in_data[0]; + i_float = 0.5 * (speed - 1.0); + i = i_float + 1.0; /* ciel(i_float) for ints */ + + do { + speed = filter->speed; /* update this, it might have changed */ + + if (filter->srcpool) { + out = gst_buffer_new_from_pool(filter->srcpool, 0, 0); + out_data = (_FORMAT*) GST_BUFFER_DATA(out); + } else { + out = gst_buffer_new(); + GST_BUFFER_DATA(out) = (gchar*) g_new(_FORMAT,SPEED_BUFSIZE/sizeof(_FORMAT)); + GST_BUFFER_SIZE(out) = SPEED_BUFSIZE; + out_data = (_FORMAT*) GST_BUFFER_DATA(out); + } + nout = GST_BUFFER_SIZE(out) / sizeof(_FORMAT); + + for (j=0; j<nout; j++) { + /* index of upper bounds of interpolation for + * new sample, got it by trial&error on the chalkboard */ + i_float += speed; + i = i_float + 1.0; /* ciel(i_float) for ints */ + + while (i >= nin) { + i = i % nin; + i_float = i_float - nin; + lower = in_data[nin-1]; + gst_buffer_unref(in); + in = gst_pad_pull (filter->sinkpad); + + while (GST_IS_EVENT(in)) { + switch (GST_EVENT_TYPE(in)) { + case GST_EVENT_EOS: + gst_element_set_state((GstElement*)filter, GST_STATE_PAUSED); + gst_pad_push(filter->srcpad, in); + return; + default: + gst_pad_push(filter->srcpad, in); + in = gst_pad_pull (filter->sinkpad); + } + } + + in_data = (_FORMAT*) GST_BUFFER_DATA(in); + nin = GST_BUFFER_SIZE(in) / sizeof(_FORMAT); + } + + if (i>0) + lower = in_data[i-1]; + + interp = i_float - floor(i_float); + + out_data[j] = lower*(1-interp) + in_data[i]*interp; + + lower = in_data[i]; + } + + gst_pad_push(filter->srcpad, out); + } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element)); diff --git a/gst/speed/gstspeed.c b/gst/speed/gstspeed.c new file mode 100644 index 000000000..51811227e --- /dev/null +++ b/gst/speed/gstspeed.c @@ -0,0 +1,347 @@ +/* -*- c-basic-offset: 2 -*- + * GStreamer + * Copyright (C) 1999-2001 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. + */ + +#include <string.h> +#include <gst/gst.h> +#include <math.h> +#include <libs/audio/gstaudio.h> +#include "gstspeed.h" + +/* buffer size to make if no bufferpool is available, must be divisible by + * sizeof(gfloat) */ +#define SPEED_BUFSIZE 4096 +/* number of buffers to allocate per chunk in sink buffer pool */ +#define SPEED_NUMBUF 6 + +static GstElementDetails speed_details = { + "Speed", + "Filter/Effect", + "Set speed/pitch on audio/raw streams (resampler)", + VERSION, + "Andy Wingo <apwingo@eos.ncsu.edu>", + "(C) 2001" +}; + + +/* Filter signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_SILENT, + ARG_SPEED +}; + +static GstPadTemplate* +speed_sink_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_padtemplate_new + ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + gst_caps_append(gst_caps_new ("sink_int", "audio/raw", + GST_AUDIO_INT_MONO_PAD_TEMPLATE_PROPS), + gst_caps_new ("sink_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)), + NULL); + } + return template; +} + +static GstPadTemplate* +speed_src_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) + template = gst_padtemplate_new + ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_caps_append (gst_caps_new ("src_float", "audio/raw", + GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS), + gst_caps_new ("src_int", "audio/raw", + GST_AUDIO_INT_MONO_PAD_TEMPLATE_PROPS)), + NULL); + + return template; +} + +static GstBufferPool* +speed_sink_get_bufferpool (GstPad *pad) +{ + GstSpeed *filter; + + filter = GST_SPEED (gst_pad_get_parent(pad)); + + return filter->sinkpool; +} + +static void speed_class_init (GstSpeedClass *klass); +static void speed_init (GstSpeed *filter); + +static void speed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void speed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static gint speed_parse_caps (GstSpeed *filter, GstCaps *caps); + +static void speed_loop (GstElement *element); + +static GstElementClass *parent_class = NULL; +//static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; + +static GstPadNegotiateReturn +speed_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstSpeed* filter = GST_SPEED (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (speed_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->sinkpad,caps); +} + +static GstPadNegotiateReturn +speed_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data) +{ + GstSpeed* filter = GST_SPEED (gst_pad_get_parent (pad)); + + if (*caps==NULL) + return GST_PAD_NEGOTIATE_FAIL; + + if (speed_parse_caps(filter, *caps)) + return GST_PAD_NEGOTIATE_FAIL; + + return gst_pad_negotiate_proxy(pad,filter->srcpad,caps); +} + +static gint +speed_parse_caps (GstSpeed *filter, GstCaps *caps) +{ + const gchar *format; + + g_return_val_if_fail(filter!=NULL,-1); + g_return_val_if_fail(caps!=NULL,-1); + + format = gst_caps_get_string(caps, "format"); + + filter->rate = gst_caps_get_int (caps, "rate"); + filter->channels = gst_caps_get_int (caps, "channels"); + + if (strcmp(format, "int")==0) { + filter->format = GST_SPEED_FORMAT_INT; + filter->width = gst_caps_get_int (caps, "width"); + filter->depth = gst_caps_get_int (caps, "depth"); + filter->law = gst_caps_get_int (caps, "law"); + filter->endianness = gst_caps_get_int (caps, "endianness"); + filter->is_signed = gst_caps_get_int (caps, "signed"); + if (!filter->silent) { + g_print ("Speed : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("Speed : format int, bit width %d, endianness %d, signed %s\n", + filter->width, filter->endianness, filter->is_signed ? "yes" : "no"); + } + } else if (strcmp(format, "float")==0) { + filter->format = GST_SPEED_FORMAT_FLOAT; + filter->layout = gst_caps_get_string(caps, "layout"); + filter->intercept = gst_caps_get_float(caps, "intercept"); + filter->slope = gst_caps_get_float(caps, "slope"); + if (!filter->silent) { + g_print ("Speed : channels %d, rate %d\n", + filter->channels, filter->rate); + g_print ("Speed : format float, layout %s, intercept %f, slope %f\n", + filter->layout, filter->intercept, filter->slope); + } + } else { + return -1; + } + return 0; +} + + +GType +gst_speed_get_type(void) { + static GType speed_type = 0; + + if (!speed_type) { + static const GTypeInfo speed_info = { + sizeof(GstSpeedClass), NULL, + NULL, + (GClassInitFunc)speed_class_init, + NULL, + NULL, + sizeof(GstSpeed), + 0, + (GInstanceInitFunc)speed_init, + }; + speed_type = g_type_register_static(GST_TYPE_ELEMENT, "GstSpeed", &speed_info, 0); + } + return speed_type; +} + +static void +speed_class_init (GstSpeedClass *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(G_OBJECT_CLASS(klass), ARG_SILENT, + g_param_spec_boolean("silent","silent","silent", + TRUE,G_PARAM_READWRITE)); // CHECKME + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SPEED, + g_param_spec_float("speed","speed","speed", + 0.1,40.0,1.0,G_PARAM_READWRITE)); + + gobject_class->set_property = speed_set_property; + gobject_class->get_property = speed_get_property; +} + +static void +speed_init (GstSpeed *filter) +{ + filter->sinkpad = gst_pad_new_from_template(speed_sink_factory (),"sink"); + gst_pad_set_negotiate_function(filter->sinkpad,speed_negotiate_sink); + gst_pad_set_bufferpool_function (filter->sinkpad, speed_sink_get_bufferpool); + filter->srcpad = gst_pad_new_from_template(speed_src_factory (),"src"); + gst_pad_set_negotiate_function(filter->srcpad,speed_negotiate_src); + + gst_element_add_pad(GST_ELEMENT(filter),filter->sinkpad); + gst_element_add_pad(GST_ELEMENT(filter),filter->srcpad); + gst_element_set_loop_function(GST_ELEMENT(filter),speed_loop); + filter->silent = FALSE; + filter->speed = 1.0; + + filter->sinkpool = gst_buffer_pool_get_default(SPEED_BUFSIZE, + SPEED_NUMBUF); +} + +static void +speed_loop (GstElement *element) +{ + GstSpeed *filter = GST_SPEED(element); + GstBuffer *in, *out; + guint i, j, nin, nout; + gfloat interp, speed, lower, i_float; + + g_return_if_fail(filter != NULL); + g_return_if_fail(GST_IS_SPEED(filter)); + + filter->srcpool = gst_pad_get_bufferpool(filter->srcpad); + + i = j = 0; + speed = filter->speed; + + in = gst_pad_pull(filter->sinkpad); + + /* this is a bit nasty, but hey, it's what you've got to do to keep the same + * algorithm and multiple data types in c. */ + if (filter->format==GST_SPEED_FORMAT_FLOAT) { +#define _FORMAT gfloat +#include "filter.func" +#undef _FORMAT + } else if (filter->format==GST_SPEED_FORMAT_INT && filter->width==16) { +#define _FORMAT gint16 +#include "filter.func" +#undef _FORMAT + } else if (filter->format==GST_SPEED_FORMAT_INT && filter->width==8) { +#define _FORMAT gint8 +#include "filter.func" +#undef _FORMAT + } +} + +static void +speed_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstSpeed *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SPEED(object)); + filter = GST_SPEED(object); + + switch (prop_id) + { + case ARG_SILENT: + filter->silent = g_value_get_boolean (value); + break; + case ARG_SPEED: + filter->speed = g_value_get_float (value); + break; + default: + break; + } +} + +static void +speed_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstSpeed *filter; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_SPEED(object)); + filter = GST_SPEED(object); + + switch (prop_id) { + case ARG_SILENT: + g_value_set_boolean (value, filter->silent); + break; + case ARG_SPEED: + g_value_set_float (value, filter->speed); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("speed",GST_TYPE_SPEED, + &speed_details); + g_return_val_if_fail(factory != NULL, FALSE); + + gst_elementfactory_add_padtemplate (factory, speed_src_factory ()); + gst_elementfactory_add_padtemplate (factory, speed_sink_factory ()); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "speed", + plugin_init +}; diff --git a/gst/speed/gstspeed.h b/gst/speed/gstspeed.h new file mode 100644 index 000000000..530a9dc88 --- /dev/null +++ b/gst/speed/gstspeed.h @@ -0,0 +1,106 @@ +/* -*- c-basic-offset: 2 -*- + * 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_SPEED_H__ +#define __GST_SPEED_H__ + + +#include <config.h> +#include <gst/gst.h> +// #include <gst/meta/audioraw.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_SPEED \ + (gst_speed_get_type()) +#define GST_SPEED(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPEED,GstSpeed)) +#define GST_SPEED_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ULAW,GstSpeed)) +#define GST_IS_SPEED(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPEED)) +#define GST_IS_SPEED_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPEED)) + +typedef struct _GstSpeed GstSpeed; +typedef struct _GstSpeedClass GstSpeedClass; +typedef enum _GstSpeedFormat GstSpeedFormat; + +enum _GstSpeedFormat { + GST_SPEED_FORMAT_INT, + GST_SPEED_FORMAT_FLOAT +}; + +struct _GstSpeed { + GstElement element; + + GstPad *sinkpad, *srcpad; + GstBufferPool *sinkpool, *srcpool; + + gboolean silent; + + gfloat speed; + + /* the next three are valid for both int and float */ + + GstSpeedFormat format; + + guint rate; + + guint channels; + + /* the next five are valid only for format==GST_SPEED_FORMAT_INT */ + + guint width; + + guint depth; + + guint endianness; + + guint law; + + gboolean is_signed; + + /* the next three are valid only for format==GST_SPEED_FORMAT_FLOAT */ + + const gchar *layout; + + gfloat slope; + + gfloat intercept; +}; + +struct _GstSpeedClass { + GstElementClass parent_class; +}; + +GType gst_speed_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_SPEED_H__ */ diff --git a/gst/stereo/.gitignore b/gst/stereo/.gitignore new file mode 100644 index 000000000..08f5ed37d --- /dev/null +++ b/gst/stereo/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst/stereo/Makefile.am b/gst/stereo/Makefile.am new file mode 100644 index 000000000..69e18c720 --- /dev/null +++ b/gst/stereo/Makefile.am @@ -0,0 +1,10 @@ +filterdir = $(libdir)/gst + +filter_LTLIBRARIES = libgststereo.la + +libgststereo_la_SOURCES = gststereo.c +libgststereo_la_CFLAGS = $(GST_CFLAGS) + +noinst_HEADERS = gststereo.h + +EXTRA_DIST = README diff --git a/gst/stereo/README b/gst/stereo/README new file mode 100644 index 000000000..a61e66aea --- /dev/null +++ b/gst/stereo/README @@ -0,0 +1,3 @@ +This effect is borrowed from xmms-0.6.1, though I mangled it so badly in +the process of copying it over that the xmms people probably won't want +any credit for it ;-) diff --git a/gst/stereo/gststereo.c b/gst/stereo/gststereo.c new file mode 100644 index 000000000..622dd5d44 --- /dev/null +++ b/gst/stereo/gststereo.c @@ -0,0 +1,226 @@ +/* Gnome-Streamer + * 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. + */ + +#include <gststereo.h> + + +static GstElementDetails stereo_details = { + "Stereo effect", + "Filter/Effect", + "Muck with the stereo signal, enhance it's 'stereo-ness'", + VERSION, + "Erik Walthinsen <omega@cse.ogi.edu>", + "(C) 1999", +}; + + +/* Stereo signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_ACTIVE, + ARG_STEREO +}; + + +static void gst_stereo_class_init (GstStereoClass *klass); +static void gst_stereo_init (GstStereo *stereo); + +static void gst_stereo_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_stereo_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); + +static void gst_stereo_chain (GstPad *pad, GstBuffer *buf); + +static GstElementClass *parent_class = NULL; +//static guint gst_stereo_signals[LAST_SIGNAL] = { 0 }; + +GType +gst_stereo_get_type(void) { + static GType stereo_type = 0; + + if (!stereo_type) { + static const GTypeInfo stereo_info = { + sizeof(GstStereoClass), NULL, + NULL, + (GClassInitFunc)gst_stereo_class_init, + NULL, + NULL, + sizeof(GstStereo), + 0, + (GInstanceInitFunc)gst_stereo_init, + }; + stereo_type = g_type_register_static(GST_TYPE_ELEMENT, "GstStereo", &stereo_info, 0); + } + return stereo_type; +} + +static void +gst_stereo_class_init (GstStereoClass *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(G_OBJECT_CLASS(klass), ARG_ACTIVE, + g_param_spec_int("active","active","active", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_STEREO, + g_param_spec_float("stereo","stereo","stereo", + 0.0,1.0,0.0,G_PARAM_READWRITE)); // CHECKME + + gobject_class->set_property = gst_stereo_set_property; + gobject_class->get_property = gst_stereo_get_property; +} + +static void +gst_stereo_init (GstStereo *stereo) +{ + stereo->sinkpad = gst_pad_new("sink",GST_PAD_SINK); + gst_element_add_pad(GST_ELEMENT(stereo),stereo->sinkpad); + gst_pad_set_chain_function(stereo->sinkpad,gst_stereo_chain); + stereo->srcpad = gst_pad_new("src",GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(stereo),stereo->srcpad); + + stereo->active = FALSE; + stereo->stereo = 2.5; +} + +static void +gst_stereo_chain (GstPad *pad,GstBuffer *buf) +{ + GstStereo *stereo; + gint16 *data; + gint samples; + gint i; + gdouble avg,ldiff,rdiff,tmp,mul; + + g_return_if_fail(pad != NULL); + g_return_if_fail(GST_IS_PAD(pad)); + g_return_if_fail(buf != NULL); + + stereo = GST_STEREO(GST_OBJECT_PARENT (pad)); + g_return_if_fail(stereo != NULL); + g_return_if_fail(GST_IS_STEREO(stereo)); + +// FIXME +// if (buf->meta) +// memcpy(&stereo->meta,buf->meta,sizeof(stereo->meta)); + + if (stereo->active) { + + //if (stereo->meta.channels == 2 && stereo->meta.format == AFMT_S16_LE) { + data = (gint16 *)GST_BUFFER_DATA(buf); + samples = GST_BUFFER_SIZE(buf) / 2; + mul = stereo->stereo; + for (i = 0; i < samples / 2; i += 2) { + avg = (data[i] + data[i + 1]) / 2; + ldiff = data[i] - avg; + rdiff = data[i + 1] - avg; + + tmp = avg + ldiff * mul; + if (tmp < -32768) + tmp = -32768; + if (tmp > 32767) + tmp = 32767; + data[i] = tmp; + + tmp = avg + rdiff * mul; + if (tmp < -32768) + tmp = -32768; + if (tmp > 32767) + tmp = 32767; + data[i + 1] = tmp; + } + //} + } + + gst_pad_push(stereo->srcpad,buf); +} + +static void +gst_stereo_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GstStereo *stereo; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_STEREO(object)); + stereo = GST_STEREO(object); + + switch (prop_id) { + case ARG_ACTIVE: + stereo->active = g_value_get_int (value); + break; + case ARG_STEREO: + stereo->stereo = g_value_get_float (value) * 10.0; + break; + default: + break; + } +} + +static void +gst_stereo_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GstStereo *stereo; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_STEREO(object)); + stereo = GST_STEREO(object); + + switch (prop_id) { + case ARG_ACTIVE: + g_value_set_int (value, stereo->active); + break; + case ARG_STEREO: + g_value_set_float (value, stereo->stereo / 10.0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + factory = gst_elementfactory_new("stereo",GST_TYPE_STEREO, + &stereo_details); + g_return_val_if_fail(factory != NULL, FALSE); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + + return TRUE; +} + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "stereo", + plugin_init +}; + diff --git a/gst/stereo/gststereo.h b/gst/stereo/gststereo.h new file mode 100644 index 000000000..f775f850d --- /dev/null +++ b/gst/stereo/gststereo.h @@ -0,0 +1,68 @@ +/* Gnome-Streamer + * 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_STEREO_H__ +#define __GST_STEREO_H__ + + +#include <config.h> +#include <gst/gst.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GST_TYPE_STEREO \ + (gst_stereo_get_type()) +#define GST_STEREO(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_STEREO,GstStereo)) +#define GST_STEREO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_STEREO,GstStereo)) +#define GST_IS_STEREO(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_STEREO)) +#define GST_IS_STEREO_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_STEREO)) + +typedef struct _GstStereo GstStereo; +typedef struct _GstStereoClass GstStereoClass; + +struct _GstStereo { + GstElement element; + + GstPad *sinkpad,*srcpad; + + gint8 active; + gfloat stereo; +}; + +struct _GstStereoClass { + GstElementClass parent_class; +}; + +GType gst_stereo_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GST_STEREO_H__ */ |