Commit 8756b6a9 authored by Luis de Bethencourt's avatar Luis de Bethencourt
Browse files

goom2k1: rebase to use the audiovisualizer class

Rebase to have goom2k1 using the common GstAudioVisualizer class

https://bugzilla.gnome.org/show_bug.cgi?id=742875
parent 89903bf6
......@@ -3,9 +3,11 @@ plugin_LTLIBRARIES = libgstgoom2k1.la
GOOM_FILTER_FILES = filters.c
GOOM_FILTER_CFLAGS = -UMMX -UUSE_ASM
noinst_HEADERS = gstgoom.h filters.h goom_core.h goom_tools.h graphic.h lines.h
noinst_HEADERS = gstgoom.h filters.h goom_core.h goom_tools.h graphic.h lines.h \
gstaudiovisualizer.h
libgstgoom2k1_la_SOURCES = gstgoom.c goom_core.c $(GOOM_FILTER_FILES) graphic.c lines.c
libgstgoom2k1_la_SOURCES = gstgoom.c goom_core.c $(GOOM_FILTER_FILES) graphic.c \
lines.c gstaudiovisualizer.c
libgstgoom2k1_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GOOM_FILTER_CFLAGS) \
-Dgst_goom_get_type=gst_goom2k1_get_type \
......@@ -22,7 +24,7 @@ libgstgoom2k1_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CF
-DzoomFilterDestroy=zoomFilterDestroy2k1 \
-DzoomFilterNew=zoomFilterNew2k1
libgstgoom2k1_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM)
libgstgoom2k1_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(LIBM) -lgstvideo-$(GST_API_VERSION) -lgstaudio-$(GST_API_VERSION)
libgstgoom2k1_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstgoom2k1_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
......
This diff is collapsed.
/* GStreamer
* Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
* Copyright (C) <2015> Luis de Bethencourt <luis@debethencourt.com>
*
* gstaudiovisualizer.c: base class for audio visualisation elements
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __GST_AUDIO_VISUALIZER_H__
#define __GST_AUDIO_VISUALIZER_H__
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/video/video.h>
#include <gst/audio/audio.h>
#include <gst/base/gstadapter.h>
G_BEGIN_DECLS
#define GST_TYPE_AUDIO_VISUALIZER (gst_audio_visualizer_get_type())
#define GST_AUDIO_VISUALIZER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_VISUALIZER,GstAudioVisualizer))
#define GST_AUDIO_VISUALIZER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_VISUALIZER,GstAudioVisualizerClass))
#define GST_AUDIO_VISUALIZER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_AUDIO_VISUALIZER,GstAudioVisualizerClass))
#define GST_IS_SYNAESTHESIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_VISUALIZER))
#define GST_IS_SYNAESTHESIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_VISUALIZER))
typedef struct _GstAudioVisualizer GstAudioVisualizer;
typedef struct _GstAudioVisualizerClass GstAudioVisualizerClass;
typedef struct _GstAudioVisualizerPrivate GstAudioVisualizerPrivate;
typedef void (*GstAudioVisualizerShaderFunc)(GstAudioVisualizer *scope, const GstVideoFrame *s, GstVideoFrame *d);
/**
* GstAudioVisualizerShader:
* @GST_AUDIO_VISUALIZER_SHADER_NONE: no shading
* @GST_AUDIO_VISUALIZER_SHADER_FADE: plain fading
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP: fade and move up
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN: fade and move down
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT: fade and move left
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT: fade and move right
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT: fade and move horizontally out
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN: fade and move horizontally in
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT: fade and move vertically out
* @GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN: fade and move vertically in
*
* Different types of supported background shading functions.
*/
typedef enum {
GST_AUDIO_VISUALIZER_SHADER_NONE,
GST_AUDIO_VISUALIZER_SHADER_FADE,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT,
GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN
} GstAudioVisualizerShader;
struct _GstAudioVisualizer
{
GstElement parent;
guint req_spf; /* min samples per frame wanted by the subclass */
/* video state */
GstVideoInfo vinfo;
/* audio state */
GstAudioInfo ainfo;
/* <private> */
GstAudioVisualizerPrivate *priv;
};
struct _GstAudioVisualizerClass
{
GstElementClass parent_class;
/* virtual function, called whenever the format changes */
gboolean (*setup) (GstAudioVisualizer * scope);
/* virtual function for rendering a frame */
gboolean (*render) (GstAudioVisualizer * scope, GstBuffer * audio, GstVideoFrame * video);
gboolean (*decide_allocation) (GstAudioVisualizer * scope, GstQuery *query);
};
GType gst_audio_visualizer_get_type (void);
G_END_DECLS
#endif /* __GST_AUDIO_VISUALIZER_H__ */
/* gstgoom.c: implementation of goom drawing element
* Copyright (C) <2001> Richard Boulton <richard@tartarus.org>
* (C) <2006> Wim Taymans <wim at fluendo dot com>
* (C) <2015> Luis de Bethencourt <luis@debethencourt.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -39,10 +40,7 @@
#endif
#include <string.h>
#include <gst/gst.h>
#include "gstgoom.h"
#include <gst/video/video.h>
#include <gst/audio/audio.h>
#include "goom_core.h"
GST_DEBUG_CATEGORY_STATIC (goom_debug);
......@@ -53,19 +51,6 @@ GST_DEBUG_CATEGORY_STATIC (goom_debug);
#define DEFAULT_FPS_N 25
#define DEFAULT_FPS_D 1
/* signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
PROP_0
/* FILL ME */
};
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
......@@ -93,41 +78,28 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
static void gst_goom_finalize (GObject * object);
static GstStateChangeReturn gst_goom_change_state (GstElement * element,
GstStateChange transition);
static gboolean gst_goom_setup (GstAudioVisualizer * base);
static gboolean gst_goom_render (GstAudioVisualizer * base, GstBuffer * audio,
GstVideoFrame * video);
static GstFlowReturn gst_goom_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static gboolean gst_goom_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_goom_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_goom_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static gboolean gst_goom_src_negotiate (GstGoom * goom);
#define gst_goom_parent_class parent_class
typedef GstGoom GstGoom2k1;
typedef GstGoomClass GstGoom2k1Class;
G_DEFINE_TYPE (GstGoom2k1, gst_goom, GST_TYPE_ELEMENT);
G_DEFINE_TYPE (GstGoom2k1, gst_goom, GST_TYPE_AUDIO_VISUALIZER);
static void
gst_goom_class_init (GstGoomClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstAudioVisualizerClass *visualizer_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_peek_parent (klass);
visualizer_class = (GstAudioVisualizerClass *) klass;
gobject_class->finalize = gst_goom_finalize;
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
gst_element_class_set_static_metadata (gstelement_class,
"GOOM: what a GOOM! 2k1 edition", "Visualization",
"Takes frames of data and outputs video frames using the GOOM 2k1 filter",
......@@ -138,28 +110,14 @@ gst_goom_class_init (GstGoomClass * klass)
gst_static_pad_template_get (&src_template));
GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
visualizer_class->setup = GST_DEBUG_FUNCPTR (gst_goom_setup);
visualizer_class->render = GST_DEBUG_FUNCPTR (gst_goom_render);
}
static void
gst_goom_init (GstGoom * goom)
{
/* create the sink and src pads */
goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
gst_pad_set_chain_function (goom->sinkpad,
GST_DEBUG_FUNCPTR (gst_goom_chain));
gst_pad_set_event_function (goom->sinkpad,
GST_DEBUG_FUNCPTR (gst_goom_sink_event));
gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
gst_pad_set_event_function (goom->srcpad,
GST_DEBUG_FUNCPTR (gst_goom_src_event));
gst_pad_set_query_function (goom->srcpad,
GST_DEBUG_FUNCPTR (gst_goom_src_query));
gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
goom->adapter = gst_adapter_new ();
goom->width = DEFAULT_WIDTH;
goom->height = DEFAULT_HEIGHT;
goom->fps_n = DEFAULT_FPS_N; /* desired frame rate */
......@@ -178,509 +136,51 @@ gst_goom_finalize (GObject * object)
goom_close (&(goom->goomdata));
g_object_unref (goom->adapter);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_goom_reset (GstGoom * goom)
{
gst_adapter_clear (goom->adapter);
gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
GST_OBJECT_LOCK (goom);
goom->proportion = 1.0;
goom->earliest_time = GST_CLOCK_TIME_NONE;
GST_OBJECT_UNLOCK (goom);
goom->dropped = 0;
goom->processed = 0;
}
static gboolean
gst_goom_sink_setcaps (GstGoom * goom, GstCaps * caps)
{
GstStructure *structure;
gboolean res;
structure = gst_caps_get_structure (caps, 0);
res = gst_structure_get_int (structure, "channels", &goom->channels);
res &= gst_structure_get_int (structure, "rate", &goom->rate);
if (!res)
return FALSE;
goom->bps = goom->channels * sizeof (gint16);
return gst_goom_src_negotiate (goom);
G_OBJECT_CLASS (gst_goom_parent_class)->finalize (object);
}
static gboolean
gst_goom_src_setcaps (GstGoom * goom, GstCaps * caps)
gst_goom_setup (GstAudioVisualizer * base)
{
GstStructure *structure;
structure = gst_caps_get_structure (caps, 0);
GstGoom *goom = GST_GOOM (base);
if (!gst_structure_get_int (structure, "width", &goom->width) ||
!gst_structure_get_int (structure, "height", &goom->height) ||
!gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
&goom->fps_d))
goto error;
goom->width = GST_VIDEO_INFO_WIDTH (&base->vinfo);
goom->height = GST_VIDEO_INFO_HEIGHT (&base->vinfo);
goom_set_resolution (&(goom->goomdata), goom->width, goom->height);
/* size of the output buffer in bytes, depth is always 4 bytes */
goom->outsize = goom->width * goom->height * 4;
goom->duration =
gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
goom->bpf = goom->spf * goom->bps;
GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
return gst_pad_set_caps (goom->srcpad, caps);
/* ERRORS */
error:
{
GST_DEBUG_OBJECT (goom, "error parsing caps");
return FALSE;
}
}
static gboolean
gst_goom_src_negotiate (GstGoom * goom)
{
GstCaps *othercaps, *target;
GstStructure *structure;
GstCaps *templ;
GstQuery *query;
GstBufferPool *pool = NULL;
GstStructure *config;
guint size, min, max;
templ = gst_pad_get_pad_template_caps (goom->srcpad);
GST_DEBUG_OBJECT (goom, "performing negotiation");
/* see what the peer can do */
othercaps = gst_pad_peer_query_caps (goom->srcpad, NULL);
if (othercaps) {
target = gst_caps_intersect (othercaps, templ);
gst_caps_unref (templ);
gst_caps_unref (othercaps);
if (gst_caps_is_empty (target))
goto no_format;
target = gst_caps_truncate (target);
} else {
target = gst_caps_copy (templ);
}
structure = gst_caps_get_structure (target, 0);
gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
gst_structure_fixate_field_nearest_fraction (structure, "framerate",
DEFAULT_FPS_N, DEFAULT_FPS_D);
gst_goom_src_setcaps (goom, target);
/* find a pool for the negotiated caps now */
query = gst_query_new_allocation (target, TRUE);
if (!gst_pad_peer_query (goom->srcpad, query)) {
/* no problem, we use the query defaults */
GST_DEBUG_OBJECT (goom, "ALLOCATION query failed");
}
if (gst_query_get_n_allocation_pools (query) > 0) {
/* we got configuration from our peer, parse them */
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
} else {
pool = NULL;
size = goom->outsize;
min = max = 0;
}
if (pool == NULL) {
/* we did not get a pool, make one ourselves then */
pool = gst_buffer_pool_new ();
}
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, target, size, min, max);
gst_buffer_pool_set_config (pool, config);
if (goom->pool) {
gst_buffer_pool_set_active (goom->pool, FALSE);
gst_object_unref (goom->pool);
}
goom->pool = pool;
/* and activate */
gst_buffer_pool_set_active (pool, TRUE);
gst_caps_unref (target);
return TRUE;
no_format:
{
gst_caps_unref (target);
return FALSE;
}
}
static gboolean
gst_goom_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res;
GstGoom *goom;
goom = GST_GOOM (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_QOS:
{
gdouble proportion;
GstClockTimeDiff diff;
GstClockTime timestamp;
gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
/* save stuff for the _chain() function */
GST_OBJECT_LOCK (goom);
goom->proportion = proportion;
if (diff >= 0)
/* we're late, this is a good estimate for next displayable
* frame (see part-qos.txt) */
goom->earliest_time = timestamp + 2 * diff + goom->duration;
else
goom->earliest_time = timestamp + diff;
GST_OBJECT_UNLOCK (goom);
res = gst_pad_event_default (pad, parent, event);
break;
gst_goom_render (GstAudioVisualizer * base, GstBuffer * audio,
GstVideoFrame * video)
{
GstGoom *goom = GST_GOOM (base);
GstMapInfo amap;
gint16 *adata;
gint i;
/* get next GOOM_SAMPLES, we have at least this amount of samples */
gst_buffer_map (audio, &amap, GST_MAP_READ);
adata = (gint16 *) amap.data;
if (goom->channels == 2) {
for (i = 0; i < GOOM_SAMPLES; i++) {
goom->datain[0][i] = *adata++;
goom->datain[1][i] = *adata++;
}
default:
res = gst_pad_event_default (pad, parent, event);
break;
}
return res;
}
static gboolean
gst_goom_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
gboolean res;
GstGoom *goom;
goom = GST_GOOM (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
res = gst_goom_sink_setcaps (goom, caps);
gst_event_unref (event);
break;
}
case GST_EVENT_FLUSH_STOP:
gst_goom_reset (goom);
res = gst_pad_event_default (pad, parent, event);
break;
case GST_EVENT_SEGMENT:
{
/* the newsegment values are used to clip the input samples
* and to convert the incomming timestamps to running time so
* we can do QoS */
gst_event_copy_segment (event, &goom->segment);
res = gst_pad_event_default (pad, parent, event);
break;
}
default:
res = gst_pad_event_default (pad, parent, event);
break;
}
return res;
}
static gboolean
gst_goom_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
gboolean res;
GstGoom *goom;
goom = GST_GOOM (parent);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_LATENCY:
{
/* We need to send the query upstream and add the returned latency to our
* own */
GstClockTime min_latency, max_latency;
gboolean us_live;
GstClockTime our_latency;
guint max_samples;
if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
GST_DEBUG_OBJECT (goom, "Peer latency: min %"
GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
/* the max samples we must buffer buffer */
max_samples = MAX (GOOM_SAMPLES, goom->spf);
our_latency =
gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
GST_TIME_ARGS (our_latency));
/* we add some latency but only if we need to buffer more than what
* upstream gives us */
min_latency += our_latency;
if (max_latency != -1)
max_latency += our_latency;
GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
gst_query_set_latency (query, TRUE, min_latency, max_latency);
}
break;
}
default:
res = gst_pad_query_default (pad, parent, query);
break;
}
return res;
}
/* make sure we are negotiated */
static GstFlowReturn
ensure_negotiated (GstGoom * goom)
{
if (gst_pad_check_reconfigure (goom->srcpad)) {
if (!gst_goom_src_negotiate (goom))
return GST_FLOW_NOT_NEGOTIATED;
}
return GST_FLOW_OK;
}
static GstFlowReturn
gst_goom_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstGoom *goom;
GstFlowReturn ret;
GstBuffer *outbuf = NULL;
goom = GST_GOOM (parent);
/* If we don't have an output format yet, preallocate a buffer to try and
* set one */
if (goom->bps == 0) {
gst_buffer_unref (buffer);
ret = GST_FLOW_NOT_NEGOTIATED;
goto beach;
}
/* Make sure have an output format */
ret = ensure_negotiated (goom);
if (ret != GST_FLOW_OK) {
gst_buffer_unref (buffer);
goto beach;
}
/* don't try to combine samples from discont buffer */
if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
gst_adapter_clear (goom->adapter);
}
GST_DEBUG_OBJECT (goom,
"Input buffer has %" G_GSIZE_FORMAT " samples, time=%" G_GUINT64_FORMAT,
gst_buffer_get_size (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
/* Collect samples until we have enough for an output frame */
gst_adapter_push (goom->adapter, buffer);
ret = GST_FLOW_OK;
while (TRUE) {
const guint16 *data;
guchar *out_frame;
gint i;
guint avail, to_flush;
guint64 dist, timestamp;
avail = gst_adapter_available (goom->adapter);