Merge branch 'master' into 0.11
[platform/upstream/gstreamer.git] / gst / goom / gstgoom.c
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>
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 /**
23  * SECTION:element-goom
24  * @see_also: synaesthesia
25  *
26  * Goom is an audio visualisation element. It creates warping structures
27  * based on the incoming audio signal.
28  *
29  * <refsect2>
30  * <title>Example launch line</title>
31  * |[
32  * gst-launch -v audiotestsrc ! goom ! ffmpegcolorspace ! xvimagesink
33  * ]|
34  * </refsect2>
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <string.h>
42 #include <gst/gst.h>
43 #include "gstgoom.h"
44 #include <gst/video/video.h>
45 #include "goom.h"
46
47 #if HAVE_ORC
48 #include <orc/orc.h>
49 #endif
50
51 GST_DEBUG_CATEGORY (goom_debug);
52 #define GST_CAT_DEFAULT goom_debug
53
54 #define DEFAULT_WIDTH  320
55 #define DEFAULT_HEIGHT 240
56 #define DEFAULT_FPS_N  25
57 #define DEFAULT_FPS_D  1
58
59 /* signals and args */
60 enum
61 {
62   /* FILL ME */
63   LAST_SIGNAL
64 };
65
66 enum
67 {
68   ARG_0
69       /* FILL ME */
70 };
71
72 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
73     GST_PAD_SRC,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
76     );
77
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, "
84         "width = (int) 16, "
85         "depth = (int) 16, "
86         "rate = (int) [ 8000, 96000 ], " "channels = (int) { 1, 2 }")
87     );
88
89
90 static void gst_goom_finalize (GObject * object);
91
92 static GstStateChangeReturn gst_goom_change_state (GstElement * element,
93     GstStateChange transition);
94
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);
98
99 static gboolean gst_goom_src_query (GstPad * pad, GstQuery * query);
100
101 #define gst_goom_parent_class parent_class
102 G_DEFINE_TYPE (GstGoom, gst_goom, GST_TYPE_ELEMENT);
103
104 static void
105 gst_goom_class_init (GstGoomClass * klass)
106 {
107   GObjectClass *gobject_class;
108   GstElementClass *gstelement_class;
109
110   gobject_class = (GObjectClass *) klass;
111   gstelement_class = (GstElementClass *) klass;
112
113   gobject_class->finalize = gst_goom_finalize;
114
115   gst_element_class_set_details_simple (gstelement_class, "GOOM: what a GOOM!",
116       "Visualization",
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));
123
124   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
125 }
126
127 static void
128 gst_goom_init (GstGoom * goom)
129 {
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);
137
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);
144
145   goom->adapter = gst_adapter_new ();
146
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 */
151   goom->channels = 0;
152   goom->rate = 0;
153   goom->duration = 0;
154
155   goom->plugin = goom_init (goom->width, goom->height);
156 }
157
158 static void
159 gst_goom_finalize (GObject * object)
160 {
161   GstGoom *goom = GST_GOOM (object);
162
163   goom_close (goom->plugin);
164   goom->plugin = NULL;
165
166   g_object_unref (goom->adapter);
167   if (goom->pool)
168     gst_object_unref (goom->pool);
169
170   G_OBJECT_CLASS (parent_class)->finalize (object);
171 }
172
173 static void
174 gst_goom_reset (GstGoom * goom)
175 {
176   gst_adapter_clear (goom->adapter);
177   gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
178
179   GST_OBJECT_LOCK (goom);
180   goom->proportion = 1.0;
181   goom->earliest_time = -1;
182   GST_OBJECT_UNLOCK (goom);
183 }
184
185 static gboolean
186 gst_goom_sink_setcaps (GstGoom * goom, GstCaps * caps)
187 {
188   GstStructure *structure;
189
190   structure = gst_caps_get_structure (caps, 0);
191
192   gst_structure_get_int (structure, "channels", &goom->channels);
193   gst_structure_get_int (structure, "rate", &goom->rate);
194
195   goom->bps = goom->channels * sizeof (gint16);
196
197   return TRUE;
198 }
199
200 static gboolean
201 gst_goom_src_setcaps (GstGoom * goom, GstCaps * caps)
202 {
203   GstStructure *structure;
204   gboolean res;
205
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,
210           &goom->fps_d))
211     goto error;
212
213   goom_set_resolution (goom->plugin, goom->width, goom->height);
214
215   /* size of the output buffer in bytes, depth is always 4 bytes */
216   goom->outsize = goom->width * goom->height * 4;
217   goom->duration =
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;
221
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);
224
225   res = gst_pad_push_event (goom->srcpad, gst_event_new_caps (caps));
226
227   return res;
228
229   /* ERRORS */
230 error:
231   {
232     GST_DEBUG_OBJECT (goom, "error parsing caps");
233     return FALSE;
234   }
235 }
236
237 static gboolean
238 gst_goom_src_negotiate (GstGoom * goom)
239 {
240   GstCaps *othercaps, *target;
241   GstStructure *structure;
242   GstCaps *templ;
243   GstQuery *query;
244   GstBufferPool *pool = NULL;
245   guint size, min, max, prefix, alignment;
246
247   templ = gst_pad_get_pad_template_caps (goom->srcpad);
248
249   GST_DEBUG_OBJECT (goom, "performing negotiation");
250
251   /* see what the peer can do */
252   othercaps = gst_pad_peer_get_caps (goom->srcpad, NULL);
253   if (othercaps) {
254     target = gst_caps_intersect (othercaps, templ);
255     gst_caps_unref (othercaps);
256     gst_caps_unref (templ);
257
258     if (gst_caps_is_empty (target))
259       goto no_format;
260
261     gst_caps_truncate (target);
262   } else {
263     target = templ;
264   }
265
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);
271
272   gst_goom_src_setcaps (goom, target);
273
274   /* try to get a bufferpool now */
275   /* find a pool for the negotiated caps now */
276   query = gst_query_new_allocation (target, TRUE);
277
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,
281         &alignment, &pool);
282   } else {
283     size = goom->outsize;
284     min = max = 0;
285     prefix = 0;
286     alignment = 0;
287   }
288
289   if (pool == NULL) {
290     GstStructure *config;
291
292     /* we did not get a pool, make one ourselves then */
293     pool = gst_buffer_pool_new ();
294
295     config = gst_buffer_pool_get_config (pool);
296     gst_buffer_pool_config_set (config, target, size, min, max, prefix,
297         alignment);
298     gst_buffer_pool_set_config (pool, config);
299   }
300
301   if (goom->pool)
302     gst_object_unref (goom->pool);
303   goom->pool = pool;
304
305   /* and activate */
306   gst_buffer_pool_set_active (pool, TRUE);
307
308   gst_caps_unref (target);
309
310   return TRUE;
311
312 no_format:
313   {
314     gst_caps_unref (target);
315     return FALSE;
316   }
317 }
318
319 static gboolean
320 gst_goom_src_event (GstPad * pad, GstEvent * event)
321 {
322   gboolean res;
323   GstGoom *goom;
324
325   goom = GST_GOOM (gst_pad_get_parent (pad));
326
327   switch (GST_EVENT_TYPE (event)) {
328     case GST_EVENT_QOS:
329     {
330       gdouble proportion;
331       GstClockTimeDiff diff;
332       GstClockTime timestamp;
333
334       gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
335
336       /* save stuff for the _chain() function */
337       GST_OBJECT_LOCK (goom);
338       goom->proportion = proportion;
339       if (diff >= 0)
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;
343       else
344         goom->earliest_time = timestamp + diff;
345       GST_OBJECT_UNLOCK (goom);
346
347       res = gst_pad_push_event (goom->sinkpad, event);
348       break;
349     }
350     default:
351       res = gst_pad_push_event (goom->sinkpad, event);
352       break;
353   }
354   gst_object_unref (goom);
355
356   return res;
357 }
358
359 static gboolean
360 gst_goom_sink_event (GstPad * pad, GstEvent * event)
361 {
362   gboolean res;
363   GstGoom *goom;
364
365   goom = GST_GOOM (gst_pad_get_parent (pad));
366
367   switch (GST_EVENT_TYPE (event)) {
368     case GST_EVENT_CAPS:
369     {
370       GstCaps *caps;
371
372       gst_event_parse_caps (event, &caps);
373       res = gst_goom_sink_setcaps (goom, caps);
374       break;
375     }
376     case GST_EVENT_FLUSH_START:
377       res = gst_pad_push_event (goom->srcpad, event);
378       break;
379     case GST_EVENT_FLUSH_STOP:
380       gst_goom_reset (goom);
381       res = gst_pad_push_event (goom->srcpad, event);
382       break;
383     case GST_EVENT_SEGMENT:
384     {
385       /* the newsegment values are used to clip the input samples
386        * and to convert the incomming timestamps to running time so
387        * we can do QoS */
388       gst_event_copy_segment (event, &goom->segment);
389
390       res = gst_pad_push_event (goom->srcpad, event);
391       break;
392     }
393     default:
394       res = gst_pad_push_event (goom->srcpad, event);
395       break;
396   }
397   gst_object_unref (goom);
398
399   return res;
400 }
401
402 static gboolean
403 gst_goom_src_query (GstPad * pad, GstQuery * query)
404 {
405   gboolean res = FALSE;
406   GstGoom *goom;
407
408   goom = GST_GOOM (gst_pad_get_parent (pad));
409
410   switch (GST_QUERY_TYPE (query)) {
411     case GST_QUERY_LATENCY:
412     {
413       /* We need to send the query upstream and add the returned latency to our
414        * own */
415       GstClockTime min_latency, max_latency;
416       gboolean us_live;
417       GstClockTime our_latency;
418       guint max_samples;
419
420       if (goom->rate == 0)
421         break;
422
423       if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
424         gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
425
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));
429
430         /* the max samples we must buffer buffer */
431         max_samples = MAX (GOOM_SAMPLES, goom->spf);
432         our_latency =
433             gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
434
435         GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
436             GST_TIME_ARGS (our_latency));
437
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;
443
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));
447
448         gst_query_set_latency (query, TRUE, min_latency, max_latency);
449       }
450       break;
451     }
452     default:
453       res = gst_pad_peer_query (goom->sinkpad, query);
454       break;
455   }
456
457   gst_object_unref (goom);
458
459   return res;
460 }
461
462 /* make sure we are negotiated */
463 static GstFlowReturn
464 ensure_negotiated (GstGoom * goom)
465 {
466   gboolean reconfigure;
467
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);
472
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;
477   }
478   return GST_FLOW_OK;
479 }
480
481
482 static GstFlowReturn
483 gst_goom_chain (GstPad * pad, GstBuffer * buffer)
484 {
485   GstGoom *goom;
486   GstFlowReturn ret;
487   GstBuffer *outbuf = NULL;
488
489   goom = GST_GOOM (gst_pad_get_parent (pad));
490   if (goom->bps == 0) {
491     ret = GST_FLOW_NOT_NEGOTIATED;
492     goto beach;
493   }
494
495   /* Make sure have an output format */
496   ret = ensure_negotiated (goom);
497   if (ret != GST_FLOW_OK) {
498     gst_buffer_unref (buffer);
499     goto beach;
500   }
501
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);
505   }
506
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));
510
511   /* Collect samples until we have enough for an output frame */
512   gst_adapter_push (goom->adapter, buffer);
513
514   ret = GST_FLOW_OK;
515
516   while (TRUE) {
517     const guint16 *data;
518     gboolean need_skip;
519     guchar *out_frame;
520     gint i;
521     guint avail, to_flush;
522     guint64 dist, timestamp;
523
524     avail = gst_adapter_available (goom->adapter);
525     GST_DEBUG_OBJECT (goom, "avail now %u", avail);
526
527     /* we need GOOM_SAMPLES to get a meaningful result from goom. */
528     if (avail < (GOOM_SAMPLES * goom->bps))
529       break;
530
531     /* we also need enough samples to produce one frame at least */
532     if (avail < goom->bpf)
533       break;
534
535     GST_DEBUG_OBJECT (goom, "processing buffer");
536
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 */
541       dist /= goom->bps;
542       timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
543     }
544
545     if (timestamp != -1) {
546       gint64 qostime;
547
548       qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
549           timestamp);
550       qostime += goom->duration;
551
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);
556
557       if (need_skip) {
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));
561         goto skip;
562       }
563     }
564
565     /* get next GOOM_SAMPLES, we have at least this amount of samples */
566     data =
567         (const guint16 *) gst_adapter_map (goom->adapter,
568         GOOM_SAMPLES * goom->bps);
569
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++;
574       }
575     } else {
576       for (i = 0; i < GOOM_SAMPLES; i++) {
577         goom->datain[0][i] = *data;
578         goom->datain[1][i] = *data++;
579       }
580     }
581
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);
589         goto beach;
590       }
591     }
592
593     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
594     GST_BUFFER_DURATION (outbuf) = goom->duration;
595
596     out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
597     gst_buffer_fill (outbuf, 0, out_frame, goom->outsize);
598
599     gst_adapter_unmap (goom->adapter, 0);
600
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));
604
605     ret = gst_pad_push (goom->srcpad, outbuf);
606     outbuf = NULL;
607
608   skip:
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;
612
613     GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
614         to_flush);
615     gst_adapter_flush (goom->adapter, to_flush);
616
617     if (ret != GST_FLOW_OK)
618       break;
619   }
620
621   if (outbuf != NULL)
622     gst_buffer_unref (outbuf);
623
624 beach:
625   gst_object_unref (goom);
626
627   return ret;
628 }
629
630 static GstStateChangeReturn
631 gst_goom_change_state (GstElement * element, GstStateChange transition)
632 {
633   GstGoom *goom = GST_GOOM (element);
634   GstStateChangeReturn ret;
635
636   switch (transition) {
637     case GST_STATE_CHANGE_NULL_TO_READY:
638       break;
639     case GST_STATE_CHANGE_READY_TO_PAUSED:
640       gst_goom_reset (goom);
641       break;
642     default:
643       break;
644   }
645
646   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
647
648   switch (transition) {
649     case GST_STATE_CHANGE_PAUSED_TO_READY:
650       if (goom->pool) {
651         gst_buffer_pool_set_active (goom->pool, FALSE);
652         gst_object_unref (goom->pool);
653         goom->pool = NULL;
654       }
655       break;
656     case GST_STATE_CHANGE_READY_TO_NULL:
657       break;
658     default:
659       break;
660   }
661
662   return ret;
663 }
664
665 static gboolean
666 plugin_init (GstPlugin * plugin)
667 {
668   GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
669
670 #if HAVE_ORC
671   orc_init ();
672 #endif
673
674   return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
675 }
676
677 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
678     GST_VERSION_MINOR,
679     "goom",
680     "GOOM visualization filter",
681     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)