Merge remote-tracking branch 'origin/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 ! videoconvert ! 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 <gst/audio/audio.h>
46 #include "goom.h"
47
48 #if HAVE_ORC
49 #include <orc/orc.h>
50 #endif
51
52 GST_DEBUG_CATEGORY (goom_debug);
53 #define GST_CAT_DEFAULT goom_debug
54
55 #define DEFAULT_WIDTH  320
56 #define DEFAULT_HEIGHT 240
57 #define DEFAULT_FPS_N  25
58 #define DEFAULT_FPS_D  1
59
60 /* signals and args */
61 enum
62 {
63   /* FILL ME */
64   LAST_SIGNAL
65 };
66
67 enum
68 {
69   ARG_0
70       /* FILL ME */
71 };
72
73 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
74     GST_PAD_SRC,
75     GST_PAD_ALWAYS,
76 #if G_BYTE_ORDER == G_BIG_ENDIAN
77     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("xRGB"))
78 #else
79     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("BGRx"))
80 #endif
81     );
82
83 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",    /* the name of the pads */
84     GST_PAD_SINK,               /* type of the pad */
85     GST_PAD_ALWAYS,             /* ALWAYS/SOMETIMES */
86     GST_STATIC_CAPS ("audio/x-raw, "
87         "format = (string) " GST_AUDIO_NE (S16) ", "
88         "rate = (int) [ 8000, 96000 ], " "channels = (int) { 1, 2 }")
89     );
90
91
92 static void gst_goom_finalize (GObject * object);
93
94 static GstStateChangeReturn gst_goom_change_state (GstElement * element,
95     GstStateChange transition);
96
97 static GstFlowReturn gst_goom_chain (GstPad * pad, GstObject * parent,
98     GstBuffer * buffer);
99 static gboolean gst_goom_src_event (GstPad * pad, GstObject * parent,
100     GstEvent * event);
101 static gboolean gst_goom_sink_event (GstPad * pad, GstObject * parent,
102     GstEvent * event);
103
104 static gboolean gst_goom_src_query (GstPad * pad, GstObject * parent,
105     GstQuery * query);
106
107 #define gst_goom_parent_class parent_class
108 G_DEFINE_TYPE (GstGoom, gst_goom, GST_TYPE_ELEMENT);
109
110 static void
111 gst_goom_class_init (GstGoomClass * klass)
112 {
113   GObjectClass *gobject_class;
114   GstElementClass *gstelement_class;
115
116   gobject_class = (GObjectClass *) klass;
117   gstelement_class = (GstElementClass *) klass;
118
119   gobject_class->finalize = gst_goom_finalize;
120
121   gst_element_class_set_details_simple (gstelement_class, "GOOM: what a GOOM!",
122       "Visualization",
123       "Takes frames of data and outputs video frames using the GOOM filter",
124       "Wim Taymans <wim@fluendo.com>");
125   gst_element_class_add_pad_template (gstelement_class,
126       gst_static_pad_template_get (&sink_template));
127   gst_element_class_add_pad_template (gstelement_class,
128       gst_static_pad_template_get (&src_template));
129
130   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
131 }
132
133 static void
134 gst_goom_init (GstGoom * goom)
135 {
136   /* create the sink and src pads */
137   goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
138   gst_pad_set_chain_function (goom->sinkpad,
139       GST_DEBUG_FUNCPTR (gst_goom_chain));
140   gst_pad_set_event_function (goom->sinkpad,
141       GST_DEBUG_FUNCPTR (gst_goom_sink_event));
142   gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
143
144   goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
145   gst_pad_set_event_function (goom->srcpad,
146       GST_DEBUG_FUNCPTR (gst_goom_src_event));
147   gst_pad_set_query_function (goom->srcpad,
148       GST_DEBUG_FUNCPTR (gst_goom_src_query));
149   gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
150
151   goom->adapter = gst_adapter_new ();
152
153   goom->width = DEFAULT_WIDTH;
154   goom->height = DEFAULT_HEIGHT;
155   goom->fps_n = DEFAULT_FPS_N;  /* desired frame rate */
156   goom->fps_d = DEFAULT_FPS_D;  /* desired frame rate */
157   goom->channels = 0;
158   goom->rate = 0;
159   goom->duration = 0;
160
161   goom->plugin = goom_init (goom->width, goom->height);
162 }
163
164 static void
165 gst_goom_finalize (GObject * object)
166 {
167   GstGoom *goom = GST_GOOM (object);
168
169   goom_close (goom->plugin);
170   goom->plugin = NULL;
171
172   g_object_unref (goom->adapter);
173   if (goom->pool)
174     gst_object_unref (goom->pool);
175
176   G_OBJECT_CLASS (parent_class)->finalize (object);
177 }
178
179 static void
180 gst_goom_reset (GstGoom * goom)
181 {
182   gst_adapter_clear (goom->adapter);
183   gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
184
185   GST_OBJECT_LOCK (goom);
186   goom->proportion = 1.0;
187   goom->earliest_time = -1;
188   GST_OBJECT_UNLOCK (goom);
189 }
190
191 static gboolean
192 gst_goom_sink_setcaps (GstGoom * goom, GstCaps * caps)
193 {
194   GstStructure *structure;
195
196   structure = gst_caps_get_structure (caps, 0);
197
198   gst_structure_get_int (structure, "channels", &goom->channels);
199   gst_structure_get_int (structure, "rate", &goom->rate);
200
201   goom->bps = goom->channels * sizeof (gint16);
202
203   return TRUE;
204 }
205
206 static gboolean
207 gst_goom_src_setcaps (GstGoom * goom, GstCaps * caps)
208 {
209   GstStructure *structure;
210   gboolean res;
211
212   structure = gst_caps_get_structure (caps, 0);
213   if (!gst_structure_get_int (structure, "width", &goom->width) ||
214       !gst_structure_get_int (structure, "height", &goom->height) ||
215       !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
216           &goom->fps_d))
217     goto error;
218
219   goom_set_resolution (goom->plugin, goom->width, goom->height);
220
221   /* size of the output buffer in bytes, depth is always 4 bytes */
222   goom->outsize = goom->width * goom->height * 4;
223   goom->duration =
224       gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
225   goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
226   goom->bpf = goom->spf * goom->bps;
227
228   GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
229       goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
230
231   res = gst_pad_push_event (goom->srcpad, gst_event_new_caps (caps));
232
233   return res;
234
235   /* ERRORS */
236 error:
237   {
238     GST_DEBUG_OBJECT (goom, "error parsing caps");
239     return FALSE;
240   }
241 }
242
243 static gboolean
244 gst_goom_src_negotiate (GstGoom * goom)
245 {
246   GstCaps *othercaps, *target;
247   GstStructure *structure;
248   GstCaps *templ;
249   GstQuery *query;
250   GstBufferPool *pool = NULL;
251   guint size, min, max, prefix, alignment;
252
253   templ = gst_pad_get_pad_template_caps (goom->srcpad);
254
255   GST_DEBUG_OBJECT (goom, "performing negotiation");
256
257   /* see what the peer can do */
258   othercaps = gst_pad_peer_query_caps (goom->srcpad, NULL);
259   if (othercaps) {
260     target = gst_caps_intersect (othercaps, templ);
261     gst_caps_unref (othercaps);
262     gst_caps_unref (templ);
263
264     if (gst_caps_is_empty (target))
265       goto no_format;
266
267     gst_caps_truncate (target);
268   } else {
269     target = templ;
270   }
271
272   structure = gst_caps_get_structure (target, 0);
273   gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
274   gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
275   gst_structure_fixate_field_nearest_fraction (structure, "framerate",
276       DEFAULT_FPS_N, DEFAULT_FPS_D);
277
278   gst_goom_src_setcaps (goom, target);
279
280   /* try to get a bufferpool now */
281   /* find a pool for the negotiated caps now */
282   query = gst_query_new_allocation (target, TRUE);
283
284   if (gst_pad_peer_query (goom->srcpad, query)) {
285     /* we got configuration from our peer, parse them */
286     gst_query_parse_allocation_params (query, &size, &min, &max, &prefix,
287         &alignment, &pool);
288   } else {
289     size = goom->outsize;
290     min = max = 0;
291     prefix = 0;
292     alignment = 0;
293   }
294
295   if (pool == NULL) {
296     GstStructure *config;
297
298     /* we did not get a pool, make one ourselves then */
299     pool = gst_buffer_pool_new ();
300
301     config = gst_buffer_pool_get_config (pool);
302     gst_buffer_pool_config_set (config, target, size, min, max, prefix,
303         alignment);
304     gst_buffer_pool_set_config (pool, config);
305   }
306
307   if (goom->pool)
308     gst_object_unref (goom->pool);
309   goom->pool = pool;
310
311   /* and activate */
312   gst_buffer_pool_set_active (pool, TRUE);
313
314   gst_caps_unref (target);
315
316   return TRUE;
317
318 no_format:
319   {
320     gst_caps_unref (target);
321     return FALSE;
322   }
323 }
324
325 static gboolean
326 gst_goom_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
327 {
328   gboolean res;
329   GstGoom *goom;
330
331   goom = GST_GOOM (parent);
332
333   switch (GST_EVENT_TYPE (event)) {
334     case GST_EVENT_QOS:
335     {
336       gdouble proportion;
337       GstClockTimeDiff diff;
338       GstClockTime timestamp;
339
340       gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
341
342       /* save stuff for the _chain() function */
343       GST_OBJECT_LOCK (goom);
344       goom->proportion = proportion;
345       if (diff >= 0)
346         /* we're late, this is a good estimate for next displayable
347          * frame (see part-qos.txt) */
348         goom->earliest_time = timestamp + 2 * diff + goom->duration;
349       else
350         goom->earliest_time = timestamp + diff;
351       GST_OBJECT_UNLOCK (goom);
352
353       res = gst_pad_push_event (goom->sinkpad, event);
354       break;
355     }
356     default:
357       res = gst_pad_push_event (goom->sinkpad, event);
358       break;
359   }
360
361   return res;
362 }
363
364 static gboolean
365 gst_goom_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
366 {
367   gboolean res;
368   GstGoom *goom;
369
370   goom = GST_GOOM (parent);
371
372   switch (GST_EVENT_TYPE (event)) {
373     case GST_EVENT_CAPS:
374     {
375       GstCaps *caps;
376
377       gst_event_parse_caps (event, &caps);
378       res = gst_goom_sink_setcaps (goom, caps);
379       break;
380     }
381     case GST_EVENT_FLUSH_START:
382       res = gst_pad_push_event (goom->srcpad, event);
383       break;
384     case GST_EVENT_FLUSH_STOP:
385       gst_goom_reset (goom);
386       res = gst_pad_push_event (goom->srcpad, event);
387       break;
388     case GST_EVENT_SEGMENT:
389     {
390       /* the newsegment values are used to clip the input samples
391        * and to convert the incomming timestamps to running time so
392        * we can do QoS */
393       gst_event_copy_segment (event, &goom->segment);
394
395       res = gst_pad_push_event (goom->srcpad, event);
396       break;
397     }
398     default:
399       res = gst_pad_push_event (goom->srcpad, event);
400       break;
401   }
402
403   return res;
404 }
405
406 static gboolean
407 gst_goom_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
408 {
409   gboolean res = FALSE;
410   GstGoom *goom;
411
412   goom = GST_GOOM (parent);
413
414   switch (GST_QUERY_TYPE (query)) {
415     case GST_QUERY_LATENCY:
416     {
417       /* We need to send the query upstream and add the returned latency to our
418        * own */
419       GstClockTime min_latency, max_latency;
420       gboolean us_live;
421       GstClockTime our_latency;
422       guint max_samples;
423
424       if (goom->rate == 0)
425         break;
426
427       if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
428         gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
429
430         GST_DEBUG_OBJECT (goom, "Peer latency: min %"
431             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
432             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
433
434         /* the max samples we must buffer buffer */
435         max_samples = MAX (GOOM_SAMPLES, goom->spf);
436         our_latency =
437             gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
438
439         GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
440             GST_TIME_ARGS (our_latency));
441
442         /* we add some latency but only if we need to buffer more than what
443          * upstream gives us */
444         min_latency += our_latency;
445         if (max_latency != -1)
446           max_latency += our_latency;
447
448         GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
449             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
450             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
451
452         gst_query_set_latency (query, TRUE, min_latency, max_latency);
453       }
454       break;
455     }
456     default:
457       res = gst_pad_query_default (pad, parent, query);
458       break;
459   }
460
461   return res;
462 }
463
464 /* make sure we are negotiated */
465 static GstFlowReturn
466 ensure_negotiated (GstGoom * goom)
467 {
468   gboolean reconfigure;
469
470   reconfigure = gst_pad_check_reconfigure (goom->srcpad);
471
472   /* we don't know an output format yet, pick one */
473   if (reconfigure || !gst_pad_has_current_caps (goom->srcpad)) {
474     if (!gst_goom_src_negotiate (goom))
475       return GST_FLOW_NOT_NEGOTIATED;
476   }
477   return GST_FLOW_OK;
478 }
479
480
481 static GstFlowReturn
482 gst_goom_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
483 {
484   GstGoom *goom;
485   GstFlowReturn ret;
486   GstBuffer *outbuf = NULL;
487
488   goom = GST_GOOM (parent);
489   if (goom->bps == 0) {
490     ret = GST_FLOW_NOT_NEGOTIATED;
491     goto beach;
492   }
493
494   /* Make sure have an output format */
495   ret = ensure_negotiated (goom);
496   if (ret != GST_FLOW_OK) {
497     gst_buffer_unref (buffer);
498     goto beach;
499   }
500
501   /* don't try to combine samples from discont buffer */
502   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
503     gst_adapter_clear (goom->adapter);
504   }
505
506   GST_DEBUG_OBJECT (goom,
507       "Input buffer has %" G_GSIZE_FORMAT " samples, time=%" G_GUINT64_FORMAT,
508       gst_buffer_get_size (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
509
510   /* Collect samples until we have enough for an output frame */
511   gst_adapter_push (goom->adapter, buffer);
512
513   ret = GST_FLOW_OK;
514
515   while (TRUE) {
516     const guint16 *data;
517     guchar *out_frame;
518     gint i;
519     guint avail, to_flush;
520     guint64 dist, timestamp;
521
522     avail = gst_adapter_available (goom->adapter);
523     GST_DEBUG_OBJECT (goom, "avail now %u", avail);
524
525     /* we need GOOM_SAMPLES to get a meaningful result from goom. */
526     if (avail < (GOOM_SAMPLES * goom->bps))
527       break;
528
529     /* we also need enough samples to produce one frame at least */
530     if (avail < goom->bpf)
531       break;
532
533     GST_DEBUG_OBJECT (goom, "processing buffer");
534
535     /* get timestamp of the current adapter byte */
536     timestamp = gst_adapter_prev_timestamp (goom->adapter, &dist);
537     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
538       /* convert bytes to time */
539       dist /= goom->bps;
540       timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
541     }
542
543     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
544       gint64 qostime;
545       gboolean need_skip;
546
547       qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
548           timestamp) + goom->duration;
549
550       GST_OBJECT_LOCK (goom);
551       /* check for QoS, don't compute buffers that are known to be late */
552       need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
553       GST_OBJECT_UNLOCK (goom);
554
555       if (need_skip) {
556         GST_WARNING_OBJECT (goom,
557             "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
558             GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
559         goto skip;
560       }
561     }
562
563     /* get next GOOM_SAMPLES, we have at least this amount of samples */
564     data =
565         (const guint16 *) gst_adapter_map (goom->adapter,
566         GOOM_SAMPLES * goom->bps);
567
568     if (goom->channels == 2) {
569       for (i = 0; i < GOOM_SAMPLES; i++) {
570         goom->datain[0][i] = *data++;
571         goom->datain[1][i] = *data++;
572       }
573     } else {
574       for (i = 0; i < GOOM_SAMPLES; i++) {
575         goom->datain[0][i] = *data;
576         goom->datain[1][i] = *data++;
577       }
578     }
579
580     /* alloc a buffer if we don't have one yet, this happens
581      * when we pushed a buffer in this while loop before */
582     if (outbuf == NULL) {
583       GST_DEBUG_OBJECT (goom, "allocating output buffer");
584       ret = gst_buffer_pool_acquire_buffer (goom->pool, &outbuf, NULL);
585       if (ret != GST_FLOW_OK) {
586         gst_adapter_unmap (goom->adapter);
587         goto beach;
588       }
589     }
590
591     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
592     GST_BUFFER_DURATION (outbuf) = goom->duration;
593
594     out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
595     gst_buffer_fill (outbuf, 0, out_frame, goom->outsize);
596
597     gst_adapter_unmap (goom->adapter);
598
599     GST_DEBUG ("Pushing frame with time=%" GST_TIME_FORMAT ", duration=%"
600         GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
601         GST_TIME_ARGS (goom->duration));
602
603     ret = gst_pad_push (goom->srcpad, outbuf);
604     outbuf = NULL;
605
606   skip:
607     /* Now flush the samples we needed for this frame, which might be more than
608      * the samples we used (GOOM_SAMPLES). */
609     to_flush = goom->bpf;
610
611     GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
612         to_flush);
613     gst_adapter_flush (goom->adapter, to_flush);
614
615     if (ret != GST_FLOW_OK)
616       break;
617   }
618
619   if (outbuf != NULL)
620     gst_buffer_unref (outbuf);
621
622 beach:
623
624   return ret;
625 }
626
627 static GstStateChangeReturn
628 gst_goom_change_state (GstElement * element, GstStateChange transition)
629 {
630   GstGoom *goom = GST_GOOM (element);
631   GstStateChangeReturn ret;
632
633   switch (transition) {
634     case GST_STATE_CHANGE_NULL_TO_READY:
635       break;
636     case GST_STATE_CHANGE_READY_TO_PAUSED:
637       gst_goom_reset (goom);
638       break;
639     default:
640       break;
641   }
642
643   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
644
645   switch (transition) {
646     case GST_STATE_CHANGE_PAUSED_TO_READY:
647       if (goom->pool) {
648         gst_buffer_pool_set_active (goom->pool, FALSE);
649         gst_object_replace ((GstObject **) & goom->pool, NULL);
650       }
651       break;
652     case GST_STATE_CHANGE_READY_TO_NULL:
653       break;
654     default:
655       break;
656   }
657
658   return ret;
659 }
660
661 static gboolean
662 plugin_init (GstPlugin * plugin)
663 {
664   GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
665
666 #if HAVE_ORC
667   orc_init ();
668 #endif
669
670   return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
671 }
672
673 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
674     GST_VERSION_MINOR,
675     "goom",
676     "GOOM visualization filter",
677     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)