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