mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 01:30:38 +00:00
Initial revision
Original commit message from CVS: Initial revision
This commit is contained in:
parent
e5d9d6e2a5
commit
ad6ed7da2d
69 changed files with 12465 additions and 0 deletions
7
gst/chart/.gitignore
vendored
Normal file
7
gst/chart/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
4
gst/chart/Makefile.am
Normal file
4
gst/chart/Makefile.am
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
filterdir = $(libdir)/gst
|
||||||
|
filter_LTLIBRARIES = libchart.la
|
||||||
|
libchart_la_SOURCES = gstchart.c
|
||||||
|
libchart_la_CFLAGS = $(GST_CFLAGS)
|
456
gst/chart/gstchart.c
Normal file
456
gst/chart/gstchart.c
Normal file
|
@ -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
|
||||||
|
};
|
7
gst/deinterlace/.gitignore
vendored
Normal file
7
gst/deinterlace/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
9
gst/deinterlace/Makefile.am
Normal file
9
gst/deinterlace/Makefile.am
Normal file
|
@ -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
|
||||||
|
|
390
gst/deinterlace/gstdeinterlace.c
Normal file
390
gst/deinterlace/gstdeinterlace.c
Normal file
|
@ -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
|
||||||
|
};
|
76
gst/deinterlace/gstdeinterlace.h
Normal file
76
gst/deinterlace/gstdeinterlace.h
Normal file
|
@ -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__ */
|
9
gst/flx/Makefile.am
Normal file
9
gst/flx/Makefile.am
Normal file
|
@ -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
|
||||||
|
|
94
gst/flx/flx_color.c
Normal file
94
gst/flx/flx_color.c
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
43
gst/flx/flx_color.h
Normal file
43
gst/flx/flx_color.h
Normal file
|
@ -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);
|
||||||
|
|
136
gst/flx/flx_fmt.h
Normal file
136
gst/flx/flx_fmt.h
Normal file
|
@ -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__ */
|
644
gst/flx/gstflxdec.c
Normal file
644
gst/flx/gstflxdec.c
Normal file
|
@ -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
|
||||||
|
};
|
79
gst/flx/gstflxdec.h
Normal file
79
gst/flx/gstflxdec.h
Normal file
|
@ -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__ */
|
7
gst/mpeg1sys/.gitignore
vendored
Normal file
7
gst/mpeg1sys/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
13
gst/mpeg1sys/Makefile.am
Normal file
13
gst/mpeg1sys/Makefile.am
Normal file
|
@ -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)
|
482
gst/mpeg1sys/buffer.c
Normal file
482
gst/mpeg1sys/buffer.c
Normal file
|
@ -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;
|
||||||
|
}
|
141
gst/mpeg1sys/buffer.h
Normal file
141
gst/mpeg1sys/buffer.h
Normal file
|
@ -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__ */
|
572
gst/mpeg1sys/gstmpeg1systemencode.c
Normal file
572
gst/mpeg1sys/gstmpeg1systemencode.c
Normal file
|
@ -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
|
||||||
|
};
|
110
gst/mpeg1sys/gstmpeg1systemencode.h
Normal file
110
gst/mpeg1sys/gstmpeg1systemencode.h
Normal file
|
@ -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__ */
|
140
gst/mpeg1sys/main.h
Normal file
140
gst/mpeg1sys/main.h
Normal file
|
@ -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
|
290
gst/mpeg1sys/systems.c
Normal file
290
gst/mpeg1sys/systems.c
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
7
gst/mpeg2sub/.gitignore
vendored
Normal file
7
gst/mpeg2sub/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
18
gst/mpeg2sub/Makefile.am
Normal file
18
gst/mpeg2sub/Makefile.am
Normal file
|
@ -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
|
||||||
|
|
324
gst/mpeg2sub/Notes.txt
Normal file
324
gst/mpeg2sub/Notes.txt
Normal file
|
@ -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>
|
443
gst/mpeg2sub/gstmpeg2subt.c
Normal file
443
gst/mpeg2sub/gstmpeg2subt.c
Normal file
|
@ -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
|
||||||
|
};
|
82
gst/mpeg2sub/gstmpeg2subt.h
Normal file
82
gst/mpeg2sub/gstmpeg2subt.h
Normal file
|
@ -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__ */
|
16
gst/mpegaudioparse/Makefile.am
Normal file
16
gst/mpegaudioparse/Makefile.am
Normal file
|
@ -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?
|
12
gst/mpegaudioparse/README
Normal file
12
gst/mpegaudioparse/README
Normal file
|
@ -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.
|
77
gst/mpegaudioparse/gstmp3types.c
Normal file
77
gst/mpegaudioparse/gstmp3types.c
Normal file
|
@ -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
|
||||||
|
};
|
506
gst/mpegaudioparse/gstmpegaudioparse.c
Normal file
506
gst/mpegaudioparse/gstmpegaudioparse.c
Normal file
|
@ -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
|
||||||
|
};
|
71
gst/mpegaudioparse/gstmpegaudioparse.h
Normal file
71
gst/mpegaudioparse/gstmpegaudioparse.h
Normal file
|
@ -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__ */
|
7
gst/passthrough/.gitignore
vendored
Normal file
7
gst/passthrough/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
10
gst/passthrough/Makefile.am
Normal file
10
gst/passthrough/Makefile.am
Normal file
|
@ -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 =
|
18
gst/passthrough/filter.func
Normal file
18
gst/passthrough/filter.func
Normal file
|
@ -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;
|
||||||
|
}
|
351
gst/passthrough/gstpassthrough.c
Normal file
351
gst/passthrough/gstpassthrough.c
Normal file
|
@ -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
|
||||||
|
};
|
103
gst/passthrough/gstpassthrough.h
Normal file
103
gst/passthrough/gstpassthrough.h
Normal file
|
@ -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__ */
|
10
gst/playondemand/Makefile.am
Normal file
10
gst/playondemand/Makefile.am
Normal file
|
@ -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
|
120
gst/playondemand/filter.func
Normal file
120
gst/playondemand/filter.func
Normal file
|
@ -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));
|
448
gst/playondemand/gstplayondemand.c
Normal file
448
gst/playondemand/gstplayondemand.c
Normal file
|
@ -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
|
||||||
|
};
|
112
gst/playondemand/gstplayondemand.h
Normal file
112
gst/playondemand/gstplayondemand.h
Normal file
|
@ -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__ */
|
7
gst/rtjpeg/.gitignore
vendored
Normal file
7
gst/rtjpeg/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
8
gst/rtjpeg/Makefile.am
Normal file
8
gst/rtjpeg/Makefile.am
Normal file
|
@ -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
|
12
gst/rtjpeg/README
Normal file
12
gst/rtjpeg/README
Normal file
|
@ -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.
|
3434
gst/rtjpeg/RTjpeg.c
Normal file
3434
gst/rtjpeg/RTjpeg.c
Normal file
File diff suppressed because it is too large
Load diff
53
gst/rtjpeg/RTjpeg.h
Normal file
53
gst/rtjpeg/RTjpeg.h
Normal file
|
@ -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);
|
||||||
|
|
62
gst/rtjpeg/gstrtjpeg.c
Normal file
62
gst/rtjpeg/gstrtjpeg.c
Normal file
|
@ -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
|
||||||
|
};
|
114
gst/rtjpeg/gstrtjpegdec.c
Normal file
114
gst/rtjpeg/gstrtjpegdec.c
Normal file
|
@ -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);
|
||||||
|
}
|
71
gst/rtjpeg/gstrtjpegdec.h
Normal file
71
gst/rtjpeg/gstrtjpegdec.h
Normal file
|
@ -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__ */
|
112
gst/rtjpeg/gstrtjpegenc.c
Normal file
112
gst/rtjpeg/gstrtjpegenc.c
Normal file
|
@ -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);
|
||||||
|
}
|
72
gst/rtjpeg/gstrtjpegenc.h
Normal file
72
gst/rtjpeg/gstrtjpegenc.h
Normal file
|
@ -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__ */
|
7
gst/smooth/.gitignore
vendored
Normal file
7
gst/smooth/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
8
gst/smooth/Makefile.am
Normal file
8
gst/smooth/Makefile.am
Normal file
|
@ -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
|
362
gst/smooth/gstsmooth.c
Normal file
362
gst/smooth/gstsmooth.c
Normal file
|
@ -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
|
||||||
|
};
|
||||||
|
|
75
gst/smooth/gstsmooth.h
Normal file
75
gst/smooth/gstsmooth.h
Normal file
|
@ -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__ */
|
7
gst/spectrum/.gitignore
vendored
Normal file
7
gst/spectrum/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
10
gst/spectrum/Makefile.am
Normal file
10
gst/spectrum/Makefile.am
Normal file
|
@ -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
|
5
gst/spectrum/README
Normal file
5
gst/spectrum/README
Normal file
|
@ -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.
|
452
gst/spectrum/fix_fft.c
Normal file
452
gst/spectrum/fix_fft.c
Normal file
|
@ -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,
|
||||||
|
};
|
242
gst/spectrum/gstspectrum.c
Normal file
242
gst/spectrum/gstspectrum.c
Normal file
|
@ -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
|
||||||
|
};
|
67
gst/spectrum/gstspectrum.h
Normal file
67
gst/spectrum/gstspectrum.h
Normal file
|
@ -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__ */
|
8
gst/speed/Makefile.am
Normal file
8
gst/speed/Makefile.am
Normal file
|
@ -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
|
66
gst/speed/filter.func
Normal file
66
gst/speed/filter.func
Normal file
|
@ -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));
|
347
gst/speed/gstspeed.c
Normal file
347
gst/speed/gstspeed.c
Normal file
|
@ -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
|
||||||
|
};
|
106
gst/speed/gstspeed.h
Normal file
106
gst/speed/gstspeed.h
Normal file
|
@ -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__ */
|
7
gst/stereo/.gitignore
vendored
Normal file
7
gst/stereo/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Makefile
|
||||||
|
Makefile.in
|
||||||
|
*.o
|
||||||
|
*.lo
|
||||||
|
*.la
|
||||||
|
.deps
|
||||||
|
.libs
|
10
gst/stereo/Makefile.am
Normal file
10
gst/stereo/Makefile.am
Normal file
|
@ -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
|
3
gst/stereo/README
Normal file
3
gst/stereo/README
Normal file
|
@ -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 ;-)
|
226
gst/stereo/gststereo.c
Normal file
226
gst/stereo/gststereo.c
Normal file
|
@ -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
|
||||||
|
};
|
||||||
|
|
68
gst/stereo/gststereo.h
Normal file
68
gst/stereo/gststereo.h
Normal file
|
@ -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__ */
|
Loading…
Reference in a new issue