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-goom
23 * @see_also: synaesthesia
27 * Goom is an audio visualisation element. It creates warping structures
28 * based on the incomming audio signal.
30 * <title>Example launch line</title>
33 * gst-launch -v audiotestsrc ! goom ! ffmpegcolorspace ! xvimagesink
46 #include <gst/video/video.h>
49 GST_DEBUG_CATEGORY_STATIC (goom_debug);
50 #define GST_CAT_DEFAULT goom_debug
52 /* elementfactory information */
53 static const GstElementDetails gst_goom_details =
54 GST_ELEMENT_DETAILS ("GOOM: what a GOOM!",
56 "Takes frames of data and outputs video frames using the GOOM filter",
57 "Wim Taymans <wim@fluendo.com>");
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_class_init (GstGoomClass * klass);
91 static void gst_goom_base_init (GstGoomClass * klass);
92 static void gst_goom_init (GstGoom * goom);
93 static void gst_goom_finalize (GObject * object);
95 static GstStateChangeReturn gst_goom_change_state (GstElement * element,
96 GstStateChange transition);
98 static GstFlowReturn gst_goom_chain (GstPad * pad, GstBuffer * buffer);
99 static gboolean gst_goom_src_event (GstPad * pad, GstEvent * event);
100 static gboolean gst_goom_sink_event (GstPad * pad, GstEvent * event);
102 static gboolean gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps);
103 static gboolean gst_goom_src_setcaps (GstPad * pad, GstCaps * caps);
105 static GstElementClass *parent_class = NULL;
108 gst_goom_get_type (void)
110 static GType type = 0;
113 static const GTypeInfo info = {
114 sizeof (GstGoomClass),
115 (GBaseInitFunc) gst_goom_base_init,
117 (GClassInitFunc) gst_goom_class_init,
122 (GInstanceInitFunc) gst_goom_init,
125 type = g_type_register_static (GST_TYPE_ELEMENT, "GstGoom", &info, 0);
131 gst_goom_base_init (GstGoomClass * klass)
133 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
135 gst_element_class_set_details (element_class, &gst_goom_details);
136 gst_element_class_add_pad_template (element_class,
137 gst_static_pad_template_get (&sink_template));
138 gst_element_class_add_pad_template (element_class,
139 gst_static_pad_template_get (&src_template));
143 gst_goom_class_init (GstGoomClass * klass)
145 GObjectClass *gobject_class;
146 GstElementClass *gstelement_class;
148 gobject_class = (GObjectClass *) klass;
149 gstelement_class = (GstElementClass *) klass;
151 parent_class = g_type_class_peek_parent (klass);
153 gobject_class->finalize = gst_goom_finalize;
155 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
157 GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
161 gst_goom_init (GstGoom * goom)
163 /* create the sink and src pads */
164 goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
165 gst_pad_set_chain_function (goom->sinkpad,
166 GST_DEBUG_FUNCPTR (gst_goom_chain));
167 gst_pad_set_event_function (goom->sinkpad,
168 GST_DEBUG_FUNCPTR (gst_goom_sink_event));
169 gst_pad_set_setcaps_function (goom->sinkpad,
170 GST_DEBUG_FUNCPTR (gst_goom_sink_setcaps));
171 gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
173 goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
174 gst_pad_set_setcaps_function (goom->srcpad,
175 GST_DEBUG_FUNCPTR (gst_goom_src_setcaps));
176 gst_pad_set_event_function (goom->srcpad,
177 GST_DEBUG_FUNCPTR (gst_goom_src_event));
178 gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
180 goom->adapter = gst_adapter_new ();
184 goom->fps_n = 25; /* desired frame rate */
185 goom->fps_d = 1; /* desired frame rate */
190 goom->plugin = goom_init (goom->width, goom->height);
194 gst_goom_finalize (GObject * object)
196 GstGoom *goom = GST_GOOM (object);
198 goom_close (goom->plugin);
200 g_object_unref (goom->adapter);
202 G_OBJECT_CLASS (parent_class)->finalize (object);
206 gst_goom_reset (GstGoom * goom)
209 gst_adapter_clear (goom->adapter);
210 gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
212 GST_OBJECT_LOCK (goom);
213 goom->proportion = 1.0;
214 goom->earliest_time = -1;
215 GST_OBJECT_UNLOCK (goom);
219 gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps)
222 GstStructure *structure;
225 goom = GST_GOOM (GST_PAD_PARENT (pad));
227 structure = gst_caps_get_structure (caps, 0);
229 res = gst_structure_get_int (structure, "channels", &goom->channels);
230 res &= gst_structure_get_int (structure, "rate", &goom->rate);
232 goom->bps = goom->channels * sizeof (gint16);
238 gst_goom_src_setcaps (GstPad * pad, GstCaps * caps)
241 GstStructure *structure;
243 goom = GST_GOOM (GST_PAD_PARENT (pad));
245 structure = gst_caps_get_structure (caps, 0);
247 if (!gst_structure_get_int (structure, "width", &goom->width) ||
248 !gst_structure_get_int (structure, "height", &goom->height) ||
249 !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
253 goom_set_resolution (goom->plugin, goom->width, goom->height);
255 /* size of the output buffer in bytes, depth is always 4 bytes */
256 goom->outsize = goom->width * goom->height * 4;
258 gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
259 goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
260 goom->bpf = goom->spf * goom->bps;
262 GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
263 goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
269 gst_goom_src_negotiate (GstGoom * goom)
271 GstCaps *othercaps, *target, *intersect;
272 GstStructure *structure;
273 const GstCaps *templ;
275 templ = gst_pad_get_pad_template_caps (goom->srcpad);
277 GST_DEBUG_OBJECT (goom, "performing negotiation");
279 /* see what the peer can do */
280 othercaps = gst_pad_peer_get_caps (goom->srcpad);
282 intersect = gst_caps_intersect (othercaps, templ);
283 gst_caps_unref (othercaps);
285 if (gst_caps_is_empty (intersect))
288 target = gst_caps_copy_nth (intersect, 0);
289 gst_caps_unref (intersect);
291 target = gst_caps_ref ((GstCaps *) templ);
294 structure = gst_caps_get_structure (target, 0);
295 gst_structure_fixate_field_nearest_int (structure, "width", 320);
296 gst_structure_fixate_field_nearest_int (structure, "height", 240);
297 gst_structure_fixate_field_nearest_fraction (structure, "framerate", 30, 1);
299 gst_pad_set_caps (goom->srcpad, target);
300 gst_caps_unref (target);
306 gst_caps_unref (intersect);
312 gst_goom_src_event (GstPad * pad, GstEvent * event)
317 goom = GST_GOOM (gst_pad_get_parent (pad));
319 switch (GST_EVENT_TYPE (event)) {
323 GstClockTimeDiff diff;
324 GstClockTime timestamp;
326 gst_event_parse_qos (event, &proportion, &diff, ×tamp);
328 /* save stuff for the _chain() function */
329 GST_OBJECT_LOCK (goom);
330 goom->proportion = proportion;
332 /* we're late, this is a good estimate for next displayable
333 * frame (see part-qos.txt) */
334 goom->earliest_time = timestamp + 2 * diff + goom->duration;
336 goom->earliest_time = timestamp + diff;
337 GST_OBJECT_UNLOCK (goom);
339 res = gst_pad_push_event (goom->sinkpad, event);
343 res = gst_pad_push_event (goom->sinkpad, event);
346 gst_object_unref (goom);
352 gst_goom_sink_event (GstPad * pad, GstEvent * event)
357 goom = GST_GOOM (gst_pad_get_parent (pad));
359 switch (GST_EVENT_TYPE (event)) {
360 case GST_EVENT_FLUSH_START:
361 res = gst_pad_push_event (goom->srcpad, event);
363 case GST_EVENT_FLUSH_STOP:
364 gst_goom_reset (goom);
365 res = gst_pad_push_event (goom->srcpad, event);
367 case GST_EVENT_NEWSEGMENT:
371 gint64 start, stop, time;
374 /* the newsegment values are used to clip the input samples
375 * and to convert the incomming timestamps to running time so
377 gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
378 &start, &stop, &time);
380 /* now configure the values */
381 gst_segment_set_newsegment_full (&goom->segment, update,
382 rate, arate, format, start, stop, time);
384 res = gst_pad_push_event (goom->srcpad, event);
388 res = gst_pad_push_event (goom->srcpad, event);
391 gst_object_unref (goom);
397 get_buffer (GstGoom * goom, GstBuffer ** outbuf)
401 if (GST_PAD_CAPS (goom->srcpad) == NULL) {
402 if (!gst_goom_src_negotiate (goom))
403 return GST_FLOW_NOT_NEGOTIATED;
406 GST_DEBUG_OBJECT (goom, "allocating output buffer with caps %"
407 GST_PTR_FORMAT, GST_PAD_CAPS (goom->srcpad));
410 gst_pad_alloc_buffer_and_set_caps (goom->srcpad,
411 GST_BUFFER_OFFSET_NONE, goom->outsize,
412 GST_PAD_CAPS (goom->srcpad), outbuf);
413 if (ret != GST_FLOW_OK)
420 gst_goom_chain (GstPad * pad, GstBuffer * buffer)
424 GstBuffer *outbuf = NULL;
426 goom = GST_GOOM (gst_pad_get_parent (pad));
428 /* If we don't have an output format yet, preallocate a buffer to try and
430 if (GST_PAD_CAPS (goom->srcpad) == NULL) {
431 ret = get_buffer (goom, &outbuf);
432 if (ret != GST_FLOW_OK) {
433 gst_buffer_unref (buffer);
438 /* don't try to combine samples from discont buffer */
439 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
440 gst_adapter_clear (goom->adapter);
444 /* Match timestamps from the incoming audio */
445 if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
446 goom->next_ts = GST_BUFFER_TIMESTAMP (buffer);
448 GST_DEBUG_OBJECT (goom,
449 "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
450 GST_BUFFER_SIZE (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
452 /* Collect samples until we have enough for an output frame */
453 gst_adapter_push (goom->adapter, buffer);
462 guint avail, to_flush;
464 avail = gst_adapter_available (goom->adapter);
465 GST_DEBUG_OBJECT (goom, "avail now %u", avail);
467 /* we need GOOM_SAMPLES to get a meaningful result from goom. */
468 if (avail < (GOOM_SAMPLES * goom->bps))
471 /* we also need enough samples to produce one frame at least */
472 if (avail < goom->bpf)
475 GST_DEBUG_OBJECT (goom, "processing buffer");
477 if (goom->next_ts != -1) {
480 qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
483 GST_OBJECT_LOCK (goom);
484 /* check for QoS, don't compute buffers that are known to be late */
485 need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
486 GST_OBJECT_UNLOCK (goom);
489 GST_WARNING_OBJECT (goom,
490 "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
491 GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
496 /* get next GOOM_SAMPLES, we have at least this amount of samples */
498 (const guint16 *) gst_adapter_peek (goom->adapter,
499 GOOM_SAMPLES * goom->bps);
501 if (goom->channels == 2) {
502 for (i = 0; i < GOOM_SAMPLES; i++) {
503 goom->datain[0][i] = *data++;
504 goom->datain[1][i] = *data++;
507 for (i = 0; i < GOOM_SAMPLES; i++) {
508 goom->datain[0][i] = *data;
509 goom->datain[1][i] = *data++;
513 /* alloc a buffer if we don't have one yet, this happens
514 * when we pushed a buffer in this while loop before */
515 if (outbuf == NULL) {
516 ret = get_buffer (goom, &outbuf);
517 if (ret != GST_FLOW_OK) {
522 GST_BUFFER_TIMESTAMP (outbuf) = goom->next_ts;
523 GST_BUFFER_DURATION (outbuf) = goom->duration;
524 GST_BUFFER_SIZE (outbuf) = goom->outsize;
526 out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0,
528 memcpy (GST_BUFFER_DATA (outbuf), out_frame, goom->outsize);
530 GST_DEBUG ("Pushing frame with time=%" GST_TIME_FORMAT ", duration=%"
531 GST_TIME_FORMAT, GST_TIME_ARGS (goom->next_ts),
532 GST_TIME_ARGS (goom->duration));
534 ret = gst_pad_push (goom->srcpad, outbuf);
538 /* interpollate next timestamp */
539 if (goom->next_ts != -1)
540 goom->next_ts += goom->duration;
542 /* Now flush the samples we needed for this frame, which might be more than
543 * the samples we used (GOOM_SAMPLES). */
544 to_flush = goom->bpf;
546 GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
548 gst_adapter_flush (goom->adapter, to_flush);
550 if (ret != GST_FLOW_OK)
555 gst_buffer_unref (outbuf);
558 gst_object_unref (goom);
563 static GstStateChangeReturn
564 gst_goom_change_state (GstElement * element, GstStateChange transition)
566 GstGoom *goom = GST_GOOM (element);
567 GstStateChangeReturn ret;
569 switch (transition) {
570 case GST_STATE_CHANGE_NULL_TO_READY:
572 case GST_STATE_CHANGE_READY_TO_PAUSED:
573 gst_goom_reset (goom);
579 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
581 switch (transition) {
582 case GST_STATE_CHANGE_PAUSED_TO_READY:
584 case GST_STATE_CHANGE_READY_TO_NULL:
594 plugin_init (GstPlugin * plugin)
596 return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
599 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
602 "GOOM visualization filter",
603 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)