1 /* gstgoom.c: implementation of goom drawing element
2 * Copyright (C) <2001> Richard Boulton <richard@tartarus.org>
3 * (C) <2006> Wim Taymans <wim at fluendo dot com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-goom2k1
23 * @see_also: goom, synaesthesia
25 * Goom2k1 is an audio visualisation element. It creates warping structures
26 * based on the incomming audio signal. Goom2k1 is the older version of the
27 * visualisation. Also available is goom2k4, with a different look.
30 * <title>Example launch line</title>
32 * gst-launch -v audiotestsrc ! goom2k1 ! ffmpegcolorspace ! xvimagesink
44 #include <gst/video/video.h>
45 #include "goom_core.h"
47 GST_DEBUG_CATEGORY_STATIC (goom_debug);
48 #define GST_CAT_DEFAULT goom_debug
50 #define DEFAULT_WIDTH 320
51 #define DEFAULT_HEIGHT 240
52 #define DEFAULT_FPS_N 25
53 #define DEFAULT_FPS_D 1
55 /* signals and args */
68 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
71 GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
74 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", /* the name of the pads */
75 GST_PAD_SINK, /* type of the pad */
76 GST_PAD_ALWAYS, /* ALWAYS/SOMETIMES */
77 GST_STATIC_CAPS ("audio/x-raw-int, "
78 "endianness = (int) BYTE_ORDER, "
79 "signed = (boolean) TRUE, "
82 "rate = (int) [ 8000, 96000 ], " "channels = (int) { 1, 2 }")
86 static void gst_goom_class_init (GstGoomClass * klass);
87 static void gst_goom_base_init (GstGoomClass * klass);
88 static void gst_goom_init (GstGoom * goom);
89 static void gst_goom_finalize (GObject * object);
91 static GstStateChangeReturn gst_goom_change_state (GstElement * element,
92 GstStateChange transition);
94 static GstFlowReturn gst_goom_chain (GstPad * pad, GstBuffer * buffer);
95 static gboolean gst_goom_src_event (GstPad * pad, GstEvent * event);
96 static gboolean gst_goom_sink_event (GstPad * pad, GstEvent * event);
98 static gboolean gst_goom_src_query (GstPad * pad, GstQuery * query);
100 static gboolean gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps);
101 static gboolean gst_goom_src_setcaps (GstPad * pad, GstCaps * caps);
103 static GstElementClass *parent_class = NULL;
106 gst_goom_get_type (void)
108 static GType type = 0;
111 static const GTypeInfo info = {
112 sizeof (GstGoomClass),
113 (GBaseInitFunc) gst_goom_base_init,
115 (GClassInitFunc) gst_goom_class_init,
120 (GInstanceInitFunc) gst_goom_init,
123 type = g_type_register_static (GST_TYPE_ELEMENT, "GstGoom2k1", &info, 0);
129 gst_goom_base_init (GstGoomClass * klass)
131 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
133 gst_element_class_set_details_simple (element_class,
134 "GOOM: what a GOOM! 2k1 edition", "Visualization",
135 "Takes frames of data and outputs video frames using the GOOM 2k1 filter",
136 "Wim Taymans <wim@fluendo.com>");
137 gst_element_class_add_pad_template (element_class,
138 gst_static_pad_template_get (&sink_template));
139 gst_element_class_add_pad_template (element_class,
140 gst_static_pad_template_get (&src_template));
144 gst_goom_class_init (GstGoomClass * klass)
146 GObjectClass *gobject_class;
147 GstElementClass *gstelement_class;
149 gobject_class = (GObjectClass *) klass;
150 gstelement_class = (GstElementClass *) klass;
152 parent_class = g_type_class_peek_parent (klass);
154 gobject_class->finalize = gst_goom_finalize;
156 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
158 GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
162 gst_goom_init (GstGoom * goom)
164 /* create the sink and src pads */
165 goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
166 gst_pad_set_chain_function (goom->sinkpad,
167 GST_DEBUG_FUNCPTR (gst_goom_chain));
168 gst_pad_set_event_function (goom->sinkpad,
169 GST_DEBUG_FUNCPTR (gst_goom_sink_event));
170 gst_pad_set_setcaps_function (goom->sinkpad,
171 GST_DEBUG_FUNCPTR (gst_goom_sink_setcaps));
172 gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
174 goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
175 gst_pad_set_setcaps_function (goom->srcpad,
176 GST_DEBUG_FUNCPTR (gst_goom_src_setcaps));
177 gst_pad_set_event_function (goom->srcpad,
178 GST_DEBUG_FUNCPTR (gst_goom_src_event));
179 gst_pad_set_query_function (goom->srcpad,
180 GST_DEBUG_FUNCPTR (gst_goom_src_query));
181 gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
183 goom->adapter = gst_adapter_new ();
185 goom->width = DEFAULT_WIDTH;
186 goom->height = DEFAULT_HEIGHT;
187 goom->fps_n = DEFAULT_FPS_N; /* desired frame rate */
188 goom->fps_d = DEFAULT_FPS_D; /* desired frame rate */
193 goom_init (&(goom->goomdata), goom->width, goom->height);
197 gst_goom_finalize (GObject * object)
199 GstGoom *goom = GST_GOOM (object);
201 goom_close (&(goom->goomdata));
203 g_object_unref (goom->adapter);
205 G_OBJECT_CLASS (parent_class)->finalize (object);
209 gst_goom_reset (GstGoom * goom)
212 gst_adapter_clear (goom->adapter);
213 gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
215 GST_OBJECT_LOCK (goom);
216 goom->proportion = 1.0;
217 goom->earliest_time = -1;
218 GST_OBJECT_UNLOCK (goom);
222 gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps)
225 GstStructure *structure;
228 goom = GST_GOOM (GST_PAD_PARENT (pad));
230 structure = gst_caps_get_structure (caps, 0);
232 res = gst_structure_get_int (structure, "channels", &goom->channels);
233 res &= gst_structure_get_int (structure, "rate", &goom->rate);
235 goom->bps = goom->channels * sizeof (gint16);
241 gst_goom_src_setcaps (GstPad * pad, GstCaps * caps)
244 GstStructure *structure;
246 goom = GST_GOOM (GST_PAD_PARENT (pad));
248 structure = gst_caps_get_structure (caps, 0);
250 if (!gst_structure_get_int (structure, "width", &goom->width) ||
251 !gst_structure_get_int (structure, "height", &goom->height) ||
252 !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
256 goom_set_resolution (&(goom->goomdata), goom->width, goom->height);
258 /* size of the output buffer in bytes, depth is always 4 bytes */
259 goom->outsize = goom->width * goom->height * 4;
261 gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
262 goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
263 goom->bpf = goom->spf * goom->bps;
265 GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
266 goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
272 gst_goom_src_negotiate (GstGoom * goom)
274 GstCaps *othercaps, *target;
275 GstStructure *structure;
276 const GstCaps *templ;
278 templ = gst_pad_get_pad_template_caps (goom->srcpad);
280 GST_DEBUG_OBJECT (goom, "performing negotiation");
282 /* see what the peer can do */
283 othercaps = gst_pad_peer_get_caps (goom->srcpad);
285 target = gst_caps_intersect (othercaps, templ);
286 gst_caps_unref (othercaps);
288 if (gst_caps_is_empty (target))
291 gst_caps_truncate (target);
293 target = gst_caps_ref ((GstCaps *) templ);
296 structure = gst_caps_get_structure (target, 0);
297 gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
298 gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
299 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
300 DEFAULT_FPS_N, DEFAULT_FPS_D);
302 gst_pad_set_caps (goom->srcpad, target);
303 gst_caps_unref (target);
309 gst_caps_unref (target);
315 gst_goom_src_event (GstPad * pad, GstEvent * event)
320 goom = GST_GOOM (gst_pad_get_parent (pad));
322 switch (GST_EVENT_TYPE (event)) {
326 GstClockTimeDiff diff;
327 GstClockTime timestamp;
329 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
331 /* save stuff for the _chain() function */
332 GST_OBJECT_LOCK (goom);
333 goom->proportion = proportion;
335 /* we're late, this is a good estimate for next displayable
336 * frame (see part-qos.txt) */
337 goom->earliest_time = timestamp + 2 * diff + goom->duration;
339 goom->earliest_time = timestamp + diff;
340 GST_OBJECT_UNLOCK (goom);
342 res = gst_pad_push_event (goom->sinkpad, event);
346 res = gst_pad_push_event (goom->sinkpad, event);
349 gst_object_unref (goom);
355 gst_goom_sink_event (GstPad * pad, GstEvent * event)
360 goom = GST_GOOM (gst_pad_get_parent (pad));
362 switch (GST_EVENT_TYPE (event)) {
363 case GST_EVENT_FLUSH_START:
364 res = gst_pad_push_event (goom->srcpad, event);
366 case GST_EVENT_FLUSH_STOP:
367 gst_goom_reset (goom);
368 res = gst_pad_push_event (goom->srcpad, event);
370 case GST_EVENT_NEWSEGMENT:
374 gint64 start, stop, time;
377 /* the newsegment values are used to clip the input samples
378 * and to convert the incomming timestamps to running time so
380 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
381 &start, &stop, &time);
383 /* now configure the values */
384 gst_segment_set_newsegment_full (&goom->segment, update,
385 rate, arate, format, start, stop, time);
387 res = gst_pad_push_event (goom->srcpad, event);
391 res = gst_pad_push_event (goom->srcpad, event);
394 gst_object_unref (goom);
400 gst_goom_src_query (GstPad * pad, GstQuery * query)
405 goom = GST_GOOM (gst_pad_get_parent (pad));
407 switch (GST_QUERY_TYPE (query)) {
408 case GST_QUERY_LATENCY:
410 /* We need to send the query upstream and add the returned latency to our
412 GstClockTime min_latency, max_latency;
414 GstClockTime our_latency;
417 if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
418 gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
420 GST_DEBUG_OBJECT (goom, "Peer latency: min %"
421 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
422 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
424 /* the max samples we must buffer buffer */
425 max_samples = MAX (GOOM_SAMPLES, goom->spf);
427 gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
429 GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
430 GST_TIME_ARGS (our_latency));
432 /* we add some latency but only if we need to buffer more than what
433 * upstream gives us */
434 min_latency = MAX (our_latency, min_latency);
435 if (max_latency != -1)
436 max_latency = MAX (our_latency, max_latency);
438 GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
439 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
440 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
442 gst_query_set_latency (query, TRUE, min_latency, max_latency);
447 res = gst_pad_peer_query (goom->sinkpad, query);
451 gst_object_unref (goom);
457 get_buffer (GstGoom * goom, GstBuffer ** outbuf)
461 if (GST_PAD_CAPS (goom->srcpad) == NULL) {
462 if (!gst_goom_src_negotiate (goom))
463 return GST_FLOW_NOT_NEGOTIATED;
466 GST_DEBUG_OBJECT (goom, "allocating output buffer with caps %"
467 GST_PTR_FORMAT, GST_PAD_CAPS (goom->srcpad));
470 gst_pad_alloc_buffer_and_set_caps (goom->srcpad,
471 GST_BUFFER_OFFSET_NONE, goom->outsize,
472 GST_PAD_CAPS (goom->srcpad), outbuf);
473 if (ret != GST_FLOW_OK)
480 gst_goom_chain (GstPad * pad, GstBuffer * buffer)
484 GstBuffer *outbuf = NULL;
486 goom = GST_GOOM (gst_pad_get_parent (pad));
488 /* If we don't have an output format yet, preallocate a buffer to try and
490 if (GST_PAD_CAPS (goom->srcpad) == NULL) {
491 ret = get_buffer (goom, &outbuf);
492 if (ret != GST_FLOW_OK) {
493 gst_buffer_unref (buffer);
498 /* don't try to combine samples from discont buffer */
499 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
500 gst_adapter_clear (goom->adapter);
504 /* Match timestamps from the incoming audio */
505 if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
506 goom->next_ts = GST_BUFFER_TIMESTAMP (buffer);
508 GST_DEBUG_OBJECT (goom,
509 "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
510 GST_BUFFER_SIZE (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
512 /* Collect samples until we have enough for an output frame */
513 gst_adapter_push (goom->adapter, buffer);
522 guint avail, to_flush;
524 avail = gst_adapter_available (goom->adapter);
525 GST_DEBUG_OBJECT (goom, "avail now %u", avail);
527 /* we need GOOM_SAMPLES to get a meaningful result from goom. */
528 if (avail < (GOOM_SAMPLES * goom->bps))
531 /* we also need enough samples to produce one frame at least */
532 if (avail < goom->bpf)
535 GST_DEBUG_OBJECT (goom, "processing buffer");
537 if (goom->next_ts != -1) {
540 qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
543 GST_OBJECT_LOCK (goom);
544 /* check for QoS, don't compute buffers that are known to be late */
545 need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
546 GST_OBJECT_UNLOCK (goom);
549 GST_WARNING_OBJECT (goom,
550 "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
551 GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
556 /* get next GOOM_SAMPLES, we have at least this amount of samples */
558 (const guint16 *) gst_adapter_peek (goom->adapter,
559 GOOM_SAMPLES * goom->bps);
561 if (goom->channels == 2) {
562 for (i = 0; i < GOOM_SAMPLES; i++) {
563 goom->datain[0][i] = *data++;
564 goom->datain[1][i] = *data++;
567 for (i = 0; i < GOOM_SAMPLES; i++) {
568 goom->datain[0][i] = *data;
569 goom->datain[1][i] = *data++;
573 /* alloc a buffer if we don't have one yet, this happens
574 * when we pushed a buffer in this while loop before */
575 if (outbuf == NULL) {
576 ret = get_buffer (goom, &outbuf);
577 if (ret != GST_FLOW_OK) {
582 GST_BUFFER_TIMESTAMP (outbuf) = goom->next_ts;
583 GST_BUFFER_DURATION (outbuf) = goom->duration;
584 GST_BUFFER_SIZE (outbuf) = goom->outsize;
586 out_frame = (guchar *) goom_update (&(goom->goomdata), goom->datain);
587 memcpy (GST_BUFFER_DATA (outbuf), out_frame, goom->outsize);
589 GST_DEBUG ("Pushing frame with time=%" GST_TIME_FORMAT ", duration=%"
590 GST_TIME_FORMAT, GST_TIME_ARGS (goom->next_ts),
591 GST_TIME_ARGS (goom->duration));
593 ret = gst_pad_push (goom->srcpad, outbuf);
597 /* interpollate next timestamp */
598 if (goom->next_ts != -1)
599 goom->next_ts += goom->duration;
601 /* Now flush the samples we needed for this frame, which might be more than
602 * the samples we used (GOOM_SAMPLES). */
603 to_flush = goom->bpf;
605 GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
607 gst_adapter_flush (goom->adapter, to_flush);
609 if (ret != GST_FLOW_OK)
614 gst_buffer_unref (outbuf);
617 gst_object_unref (goom);
622 static GstStateChangeReturn
623 gst_goom_change_state (GstElement * element, GstStateChange transition)
625 GstGoom *goom = GST_GOOM (element);
626 GstStateChangeReturn ret;
628 switch (transition) {
629 case GST_STATE_CHANGE_NULL_TO_READY:
631 case GST_STATE_CHANGE_READY_TO_PAUSED:
632 gst_goom_reset (goom);
638 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
640 switch (transition) {
641 case GST_STATE_CHANGE_PAUSED_TO_READY:
643 case GST_STATE_CHANGE_READY_TO_NULL:
653 plugin_init (GstPlugin * plugin)
655 return gst_element_register (plugin, "goom2k1", GST_RANK_NONE, GST_TYPE_GOOM);
658 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
661 "GOOM 2k1 visualization filter",
662 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)