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>
4 * (C) <2011> Wim Taymans <wim.taymans at gmail dot com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
23 * SECTION:element-goom
24 * @see_also: synaesthesia
26 * Goom is an audio visualisation element. It creates warping structures
27 * based on the incoming audio signal.
30 * <title>Example launch line</title>
32 * gst-launch -v audiotestsrc ! goom ! ffmpegcolorspace ! xvimagesink
44 #include <gst/video/video.h>
51 GST_DEBUG_CATEGORY (goom_debug);
52 #define GST_CAT_DEFAULT goom_debug
54 #define DEFAULT_WIDTH 320
55 #define DEFAULT_HEIGHT 240
56 #define DEFAULT_FPS_N 25
57 #define DEFAULT_FPS_D 1
59 /* signals and args */
72 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
75 GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
78 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", /* the name of the pads */
79 GST_PAD_SINK, /* type of the pad */
80 GST_PAD_ALWAYS, /* ALWAYS/SOMETIMES */
81 GST_STATIC_CAPS ("audio/x-raw-int, "
82 "endianness = (int) BYTE_ORDER, "
83 "signed = (boolean) TRUE, "
86 "rate = (int) [ 8000, 96000 ], " "channels = (int) { 1, 2 }")
90 static void gst_goom_finalize (GObject * object);
92 static GstStateChangeReturn gst_goom_change_state (GstElement * element,
93 GstStateChange transition);
95 static GstFlowReturn gst_goom_chain (GstPad * pad, GstBuffer * buffer);
96 static gboolean gst_goom_src_event (GstPad * pad, GstEvent * event);
97 static gboolean gst_goom_sink_event (GstPad * pad, GstEvent * event);
99 static gboolean gst_goom_src_query (GstPad * pad, GstQuery * query);
101 #define gst_goom_parent_class parent_class
102 G_DEFINE_TYPE (GstGoom, gst_goom, GST_TYPE_ELEMENT);
105 gst_goom_class_init (GstGoomClass * klass)
107 GObjectClass *gobject_class;
108 GstElementClass *gstelement_class;
110 gobject_class = (GObjectClass *) klass;
111 gstelement_class = (GstElementClass *) klass;
113 gobject_class->finalize = gst_goom_finalize;
115 gst_element_class_set_details_simple (gstelement_class, "GOOM: what a GOOM!",
117 "Takes frames of data and outputs video frames using the GOOM filter",
118 "Wim Taymans <wim@fluendo.com>");
119 gst_element_class_add_pad_template (gstelement_class,
120 gst_static_pad_template_get (&sink_template));
121 gst_element_class_add_pad_template (gstelement_class,
122 gst_static_pad_template_get (&src_template));
124 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
128 gst_goom_init (GstGoom * goom)
130 /* create the sink and src pads */
131 goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
132 gst_pad_set_chain_function (goom->sinkpad,
133 GST_DEBUG_FUNCPTR (gst_goom_chain));
134 gst_pad_set_event_function (goom->sinkpad,
135 GST_DEBUG_FUNCPTR (gst_goom_sink_event));
136 gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
138 goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
139 gst_pad_set_event_function (goom->srcpad,
140 GST_DEBUG_FUNCPTR (gst_goom_src_event));
141 gst_pad_set_query_function (goom->srcpad,
142 GST_DEBUG_FUNCPTR (gst_goom_src_query));
143 gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
145 goom->adapter = gst_adapter_new ();
147 goom->width = DEFAULT_WIDTH;
148 goom->height = DEFAULT_HEIGHT;
149 goom->fps_n = DEFAULT_FPS_N; /* desired frame rate */
150 goom->fps_d = DEFAULT_FPS_D; /* desired frame rate */
155 goom->plugin = goom_init (goom->width, goom->height);
159 gst_goom_finalize (GObject * object)
161 GstGoom *goom = GST_GOOM (object);
163 goom_close (goom->plugin);
166 g_object_unref (goom->adapter);
168 gst_object_unref (goom->pool);
170 G_OBJECT_CLASS (parent_class)->finalize (object);
174 gst_goom_reset (GstGoom * goom)
176 gst_adapter_clear (goom->adapter);
177 gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
179 GST_OBJECT_LOCK (goom);
180 goom->proportion = 1.0;
181 goom->earliest_time = -1;
182 GST_OBJECT_UNLOCK (goom);
186 gst_goom_sink_setcaps (GstGoom * goom, GstCaps * caps)
188 GstStructure *structure;
190 structure = gst_caps_get_structure (caps, 0);
192 gst_structure_get_int (structure, "channels", &goom->channels);
193 gst_structure_get_int (structure, "rate", &goom->rate);
195 goom->bps = goom->channels * sizeof (gint16);
201 gst_goom_src_setcaps (GstGoom * goom, GstCaps * caps)
203 GstStructure *structure;
206 structure = gst_caps_get_structure (caps, 0);
207 if (!gst_structure_get_int (structure, "width", &goom->width) ||
208 !gst_structure_get_int (structure, "height", &goom->height) ||
209 !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
213 goom_set_resolution (goom->plugin, goom->width, goom->height);
215 /* size of the output buffer in bytes, depth is always 4 bytes */
216 goom->outsize = goom->width * goom->height * 4;
218 gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
219 goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
220 goom->bpf = goom->spf * goom->bps;
222 GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
223 goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
225 res = gst_pad_push_event (goom->srcpad, gst_event_new_caps (caps));
232 GST_DEBUG_OBJECT (goom, "error parsing caps");
238 gst_goom_src_negotiate (GstGoom * goom)
240 GstCaps *othercaps, *target;
241 GstStructure *structure;
244 GstBufferPool *pool = NULL;
245 guint size, min, max, prefix, alignment;
247 templ = gst_pad_get_pad_template_caps (goom->srcpad);
249 GST_DEBUG_OBJECT (goom, "performing negotiation");
251 /* see what the peer can do */
252 othercaps = gst_pad_peer_get_caps (goom->srcpad, NULL);
254 target = gst_caps_intersect (othercaps, templ);
255 gst_caps_unref (othercaps);
256 gst_caps_unref (templ);
258 if (gst_caps_is_empty (target))
261 gst_caps_truncate (target);
266 structure = gst_caps_get_structure (target, 0);
267 gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
268 gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
269 gst_structure_fixate_field_nearest_fraction (structure, "framerate",
270 DEFAULT_FPS_N, DEFAULT_FPS_D);
272 gst_goom_src_setcaps (goom, target);
274 /* try to get a bufferpool now */
275 /* find a pool for the negotiated caps now */
276 query = gst_query_new_allocation (target, TRUE);
278 if (gst_pad_peer_query (goom->srcpad, query)) {
279 /* we got configuration from our peer, parse them */
280 gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
283 size = goom->outsize;
290 GstStructure *config;
292 /* we did not get a pool, make one ourselves then */
293 pool = gst_buffer_pool_new ();
295 config = gst_buffer_pool_get_config (pool);
296 gst_buffer_pool_config_set (config, target, size, min, max, prefix,
298 gst_buffer_pool_set_config (pool, config);
302 gst_object_unref (goom->pool);
306 gst_buffer_pool_set_active (pool, TRUE);
308 gst_caps_unref (target);
314 gst_caps_unref (target);
320 gst_goom_src_event (GstPad * pad, GstEvent * event)
325 goom = GST_GOOM (gst_pad_get_parent (pad));
327 switch (GST_EVENT_TYPE (event)) {
331 GstClockTimeDiff diff;
332 GstClockTime timestamp;
334 gst_event_parse_qos (event, NULL, &proportion, &diff, ×tamp);
336 /* save stuff for the _chain() function */
337 GST_OBJECT_LOCK (goom);
338 goom->proportion = proportion;
340 /* we're late, this is a good estimate for next displayable
341 * frame (see part-qos.txt) */
342 goom->earliest_time = timestamp + 2 * diff + goom->duration;
344 goom->earliest_time = timestamp + diff;
345 GST_OBJECT_UNLOCK (goom);
347 res = gst_pad_push_event (goom->sinkpad, event);
351 res = gst_pad_push_event (goom->sinkpad, event);
354 gst_object_unref (goom);
360 gst_goom_sink_event (GstPad * pad, GstEvent * event)
365 goom = GST_GOOM (gst_pad_get_parent (pad));
367 switch (GST_EVENT_TYPE (event)) {
372 gst_event_parse_caps (event, &caps);
373 res = gst_goom_sink_setcaps (goom, caps);
376 case GST_EVENT_FLUSH_START:
377 res = gst_pad_push_event (goom->srcpad, event);
379 case GST_EVENT_FLUSH_STOP:
380 gst_goom_reset (goom);
381 res = gst_pad_push_event (goom->srcpad, event);
383 case GST_EVENT_SEGMENT:
385 /* the newsegment values are used to clip the input samples
386 * and to convert the incomming timestamps to running time so
388 gst_event_copy_segment (event, &goom->segment);
390 res = gst_pad_push_event (goom->srcpad, event);
394 res = gst_pad_push_event (goom->srcpad, event);
397 gst_object_unref (goom);
403 gst_goom_src_query (GstPad * pad, GstQuery * query)
405 gboolean res = FALSE;
408 goom = GST_GOOM (gst_pad_get_parent (pad));
410 switch (GST_QUERY_TYPE (query)) {
411 case GST_QUERY_LATENCY:
413 /* We need to send the query upstream and add the returned latency to our
415 GstClockTime min_latency, max_latency;
417 GstClockTime our_latency;
423 if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
424 gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
426 GST_DEBUG_OBJECT (goom, "Peer latency: min %"
427 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
428 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
430 /* the max samples we must buffer buffer */
431 max_samples = MAX (GOOM_SAMPLES, goom->spf);
433 gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
435 GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
436 GST_TIME_ARGS (our_latency));
438 /* we add some latency but only if we need to buffer more than what
439 * upstream gives us */
440 min_latency += our_latency;
441 if (max_latency != -1)
442 max_latency += our_latency;
444 GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
445 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
446 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
448 gst_query_set_latency (query, TRUE, min_latency, max_latency);
453 res = gst_pad_peer_query (goom->sinkpad, query);
457 gst_object_unref (goom);
462 /* make sure we are negotiated */
464 ensure_negotiated (GstGoom * goom)
466 gboolean reconfigure;
468 GST_OBJECT_LOCK (goom->srcpad);
469 reconfigure = GST_PAD_NEEDS_RECONFIGURE (goom->srcpad);
470 GST_OBJECT_FLAG_UNSET (goom->srcpad, GST_PAD_NEED_RECONFIGURE);
471 GST_OBJECT_UNLOCK (goom->srcpad);
473 /* we don't know an output format yet, pick one */
474 if (reconfigure || !gst_pad_has_current_caps (goom->srcpad)) {
475 if (!gst_goom_src_negotiate (goom))
476 return GST_FLOW_NOT_NEGOTIATED;
483 gst_goom_chain (GstPad * pad, GstBuffer * buffer)
487 GstBuffer *outbuf = NULL;
489 goom = GST_GOOM (gst_pad_get_parent (pad));
490 if (goom->bps == 0) {
491 ret = GST_FLOW_NOT_NEGOTIATED;
495 /* Make sure have an output format */
496 ret = ensure_negotiated (goom);
497 if (ret != GST_FLOW_OK) {
498 gst_buffer_unref (buffer);
502 /* don't try to combine samples from discont buffer */
503 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
504 gst_adapter_clear (goom->adapter);
507 GST_DEBUG_OBJECT (goom,
508 "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
509 gst_buffer_get_size (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
511 /* Collect samples until we have enough for an output frame */
512 gst_adapter_push (goom->adapter, buffer);
521 guint avail, to_flush;
522 guint64 dist, timestamp;
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 /* get timestamp of the current adapter byte */
538 timestamp = gst_adapter_prev_timestamp (goom->adapter, &dist);
539 if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
540 /* convert bytes to time */
542 timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
545 if (timestamp != -1) {
548 qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
550 qostime += goom->duration;
552 GST_OBJECT_LOCK (goom);
553 /* check for QoS, don't compute buffers that are known to be late */
554 need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
555 GST_OBJECT_UNLOCK (goom);
558 GST_WARNING_OBJECT (goom,
559 "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
560 GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
565 /* get next GOOM_SAMPLES, we have at least this amount of samples */
567 (const guint16 *) gst_adapter_map (goom->adapter,
568 GOOM_SAMPLES * goom->bps);
570 if (goom->channels == 2) {
571 for (i = 0; i < GOOM_SAMPLES; i++) {
572 goom->datain[0][i] = *data++;
573 goom->datain[1][i] = *data++;
576 for (i = 0; i < GOOM_SAMPLES; i++) {
577 goom->datain[0][i] = *data;
578 goom->datain[1][i] = *data++;
582 /* alloc a buffer if we don't have one yet, this happens
583 * when we pushed a buffer in this while loop before */
584 if (outbuf == NULL) {
585 GST_DEBUG_OBJECT (goom, "allocating output buffer");
586 ret = gst_buffer_pool_acquire_buffer (goom->pool, &outbuf, NULL);
587 if (ret != GST_FLOW_OK) {
588 gst_adapter_unmap (goom->adapter, 0);
593 GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
594 GST_BUFFER_DURATION (outbuf) = goom->duration;
596 out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
597 gst_buffer_fill (outbuf, 0, out_frame, goom->outsize);
599 gst_adapter_unmap (goom->adapter, 0);
601 GST_DEBUG ("Pushing frame with time=%" GST_TIME_FORMAT ", duration=%"
602 GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
603 GST_TIME_ARGS (goom->duration));
605 ret = gst_pad_push (goom->srcpad, outbuf);
609 /* Now flush the samples we needed for this frame, which might be more than
610 * the samples we used (GOOM_SAMPLES). */
611 to_flush = goom->bpf;
613 GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
615 gst_adapter_flush (goom->adapter, to_flush);
617 if (ret != GST_FLOW_OK)
622 gst_buffer_unref (outbuf);
625 gst_object_unref (goom);
630 static GstStateChangeReturn
631 gst_goom_change_state (GstElement * element, GstStateChange transition)
633 GstGoom *goom = GST_GOOM (element);
634 GstStateChangeReturn ret;
636 switch (transition) {
637 case GST_STATE_CHANGE_NULL_TO_READY:
639 case GST_STATE_CHANGE_READY_TO_PAUSED:
640 gst_goom_reset (goom);
646 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
648 switch (transition) {
649 case GST_STATE_CHANGE_PAUSED_TO_READY:
651 gst_buffer_pool_set_active (goom->pool, FALSE);
652 gst_object_unref (goom->pool);
656 case GST_STATE_CHANGE_READY_TO_NULL:
666 plugin_init (GstPlugin * plugin)
668 GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
674 return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
677 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
680 "GOOM visualization filter",
681 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)