Tizen 2.0 Release
[framework/multimedia/gst-plugins-good0.10.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  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-goom
23  * @see_also: synaesthesia
24  *
25  * Goom is an audio visualisation element. It creates warping structures
26  * based on the incoming audio signal.
27  *
28  * <refsect2>
29  * <title>Example launch line</title>
30  * |[
31  * gst-launch -v audiotestsrc ! goom ! ffmpegcolorspace ! xvimagesink
32  * ]|
33  * </refsect2>
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <string.h>
41 #include <gst/gst.h>
42 #include "gstgoom.h"
43 #include <gst/video/video.h>
44 #include "goom.h"
45
46 #if HAVE_ORC
47 #include <orc/orc.h>
48 #endif
49
50 GST_DEBUG_CATEGORY (goom_debug);
51 #define GST_CAT_DEFAULT goom_debug
52
53 #define DEFAULT_WIDTH  320
54 #define DEFAULT_HEIGHT 240
55 #define DEFAULT_FPS_N  25
56 #define DEFAULT_FPS_D  1
57
58 /* signals and args */
59 enum
60 {
61   /* FILL ME */
62   LAST_SIGNAL
63 };
64
65 enum
66 {
67   ARG_0
68       /* FILL ME */
69 };
70
71 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
72     GST_PAD_SRC,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
75     );
76
77 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",    /* the name of the pads */
78     GST_PAD_SINK,               /* type of the pad */
79     GST_PAD_ALWAYS,             /* ALWAYS/SOMETIMES */
80     GST_STATIC_CAPS ("audio/x-raw-int, "
81         "endianness = (int) BYTE_ORDER, "
82         "signed = (boolean) TRUE, "
83         "width = (int) 16, "
84         "depth = (int) 16, "
85         "rate = (int) [ 8000, 96000 ], " "channels = (int) { 1, 2 }")
86     );
87
88
89 static void gst_goom_class_init (GstGoomClass * klass);
90 static void gst_goom_base_init (GstGoomClass * klass);
91 static void gst_goom_init (GstGoom * goom);
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, GstBuffer * buffer);
98 static gboolean gst_goom_src_event (GstPad * pad, GstEvent * event);
99 static gboolean gst_goom_sink_event (GstPad * pad, GstEvent * event);
100
101 static gboolean gst_goom_src_query (GstPad * pad, GstQuery * query);
102
103 static gboolean gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps);
104 static gboolean gst_goom_src_setcaps (GstPad * pad, GstCaps * caps);
105
106 static GstElementClass *parent_class = NULL;
107
108 GType
109 gst_goom_get_type (void)
110 {
111   static GType type = 0;
112
113   if (!type) {
114     static const GTypeInfo info = {
115       sizeof (GstGoomClass),
116       (GBaseInitFunc) gst_goom_base_init,
117       NULL,
118       (GClassInitFunc) gst_goom_class_init,
119       NULL,
120       NULL,
121       sizeof (GstGoom),
122       0,
123       (GInstanceInitFunc) gst_goom_init,
124     };
125
126     type = g_type_register_static (GST_TYPE_ELEMENT, "GstGoom", &info, 0);
127   }
128   return type;
129 }
130
131 static void
132 gst_goom_base_init (GstGoomClass * klass)
133 {
134   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
135
136   gst_element_class_set_details_simple (element_class, "GOOM: what a GOOM!",
137       "Visualization",
138       "Takes frames of data and outputs video frames using the GOOM filter",
139       "Wim Taymans <wim@fluendo.com>");
140   gst_element_class_add_static_pad_template (element_class,
141       &sink_template);
142   gst_element_class_add_static_pad_template (element_class, &src_template);
143 }
144
145 static void
146 gst_goom_class_init (GstGoomClass * klass)
147 {
148   GObjectClass *gobject_class;
149   GstElementClass *gstelement_class;
150
151   gobject_class = (GObjectClass *) klass;
152   gstelement_class = (GstElementClass *) klass;
153
154   parent_class = g_type_class_peek_parent (klass);
155
156   gobject_class->finalize = gst_goom_finalize;
157
158   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
159 }
160
161 static void
162 gst_goom_init (GstGoom * goom)
163 {
164   /* create the sink and src pads */
165   goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
166   gst_pad_set_chain_function (goom->sinkpad,
167       GST_DEBUG_FUNCPTR (gst_goom_chain));
168   gst_pad_set_event_function (goom->sinkpad,
169       GST_DEBUG_FUNCPTR (gst_goom_sink_event));
170   gst_pad_set_setcaps_function (goom->sinkpad,
171       GST_DEBUG_FUNCPTR (gst_goom_sink_setcaps));
172   gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
173
174   goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
175   gst_pad_set_setcaps_function (goom->srcpad,
176       GST_DEBUG_FUNCPTR (gst_goom_src_setcaps));
177   gst_pad_set_event_function (goom->srcpad,
178       GST_DEBUG_FUNCPTR (gst_goom_src_event));
179   gst_pad_set_query_function (goom->srcpad,
180       GST_DEBUG_FUNCPTR (gst_goom_src_query));
181   gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
182
183   goom->adapter = gst_adapter_new ();
184
185   goom->width = DEFAULT_WIDTH;
186   goom->height = DEFAULT_HEIGHT;
187   goom->fps_n = DEFAULT_FPS_N;  /* desired frame rate */
188   goom->fps_d = DEFAULT_FPS_D;  /* desired frame rate */
189   goom->channels = 0;
190   goom->rate = 0;
191   goom->duration = 0;
192
193   goom->plugin = goom_init (goom->width, goom->height);
194 }
195
196 static void
197 gst_goom_finalize (GObject * object)
198 {
199   GstGoom *goom = GST_GOOM (object);
200
201   goom_close (goom->plugin);
202   goom->plugin = NULL;
203
204   g_object_unref (goom->adapter);
205
206   G_OBJECT_CLASS (parent_class)->finalize (object);
207 }
208
209 static void
210 gst_goom_reset (GstGoom * goom)
211 {
212   gst_adapter_clear (goom->adapter);
213   gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
214
215   GST_OBJECT_LOCK (goom);
216   goom->proportion = 1.0;
217   goom->earliest_time = -1;
218   GST_OBJECT_UNLOCK (goom);
219 }
220
221 static gboolean
222 gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps)
223 {
224   GstGoom *goom;
225   GstStructure *structure;
226   gboolean res;
227
228   goom = GST_GOOM (GST_PAD_PARENT (pad));
229
230   structure = gst_caps_get_structure (caps, 0);
231
232   res = gst_structure_get_int (structure, "channels", &goom->channels);
233   res &= gst_structure_get_int (structure, "rate", &goom->rate);
234
235   goom->bps = goom->channels * sizeof (gint16);
236
237   return res;
238 }
239
240 static gboolean
241 gst_goom_src_setcaps (GstPad * pad, GstCaps * caps)
242 {
243   GstGoom *goom;
244   GstStructure *structure;
245
246   goom = GST_GOOM (GST_PAD_PARENT (pad));
247
248   structure = gst_caps_get_structure (caps, 0);
249
250   if (!gst_structure_get_int (structure, "width", &goom->width) ||
251       !gst_structure_get_int (structure, "height", &goom->height) ||
252       !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
253           &goom->fps_d))
254     return FALSE;
255
256   goom_set_resolution (goom->plugin, goom->width, goom->height);
257
258   /* size of the output buffer in bytes, depth is always 4 bytes */
259   goom->outsize = goom->width * goom->height * 4;
260   goom->duration =
261       gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
262   goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
263   goom->bpf = goom->spf * goom->bps;
264
265   GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
266       goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
267
268   return TRUE;
269 }
270
271 static gboolean
272 gst_goom_src_negotiate (GstGoom * goom)
273 {
274   GstCaps *othercaps, *target;
275   GstStructure *structure;
276   const GstCaps *templ;
277
278   templ = gst_pad_get_pad_template_caps (goom->srcpad);
279
280   GST_DEBUG_OBJECT (goom, "performing negotiation");
281
282   /* see what the peer can do */
283   othercaps = gst_pad_peer_get_caps (goom->srcpad);
284   if (othercaps) {
285     target = gst_caps_intersect (othercaps, templ);
286     gst_caps_unref (othercaps);
287
288     if (gst_caps_is_empty (target))
289       goto no_format;
290
291     gst_caps_truncate (target);
292   } else {
293     target = gst_caps_ref ((GstCaps *) templ);
294   }
295
296   structure = gst_caps_get_structure (target, 0);
297   gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
298   gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
299   gst_structure_fixate_field_nearest_fraction (structure, "framerate",
300       DEFAULT_FPS_N, DEFAULT_FPS_D);
301
302   gst_pad_set_caps (goom->srcpad, target);
303   gst_caps_unref (target);
304
305   return TRUE;
306
307 no_format:
308   {
309     gst_caps_unref (target);
310     return FALSE;
311   }
312 }
313
314 static gboolean
315 gst_goom_src_event (GstPad * pad, GstEvent * event)
316 {
317   gboolean res;
318   GstGoom *goom;
319
320   goom = GST_GOOM (gst_pad_get_parent (pad));
321
322   switch (GST_EVENT_TYPE (event)) {
323     case GST_EVENT_QOS:
324     {
325       gdouble proportion;
326       GstClockTimeDiff diff;
327       GstClockTime timestamp;
328
329       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
330
331       /* save stuff for the _chain() function */
332       GST_OBJECT_LOCK (goom);
333       goom->proportion = proportion;
334       if (diff >= 0)
335         /* we're late, this is a good estimate for next displayable
336          * frame (see part-qos.txt) */
337         goom->earliest_time = timestamp + 2 * diff + goom->duration;
338       else
339         goom->earliest_time = timestamp + diff;
340       GST_OBJECT_UNLOCK (goom);
341
342       res = gst_pad_push_event (goom->sinkpad, event);
343       break;
344     }
345     default:
346       res = gst_pad_push_event (goom->sinkpad, event);
347       break;
348   }
349   gst_object_unref (goom);
350
351   return res;
352 }
353
354 static gboolean
355 gst_goom_sink_event (GstPad * pad, GstEvent * event)
356 {
357   gboolean res;
358   GstGoom *goom;
359
360   goom = GST_GOOM (gst_pad_get_parent (pad));
361
362   switch (GST_EVENT_TYPE (event)) {
363     case GST_EVENT_FLUSH_START:
364       res = gst_pad_push_event (goom->srcpad, event);
365       break;
366     case GST_EVENT_FLUSH_STOP:
367       gst_goom_reset (goom);
368       res = gst_pad_push_event (goom->srcpad, event);
369       break;
370     case GST_EVENT_NEWSEGMENT:
371     {
372       GstFormat format;
373       gdouble rate, arate;
374       gint64 start, stop, time;
375       gboolean update;
376
377       /* the newsegment values are used to clip the input samples
378        * and to convert the incomming timestamps to running time so
379        * we can do QoS */
380       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
381           &start, &stop, &time);
382
383       /* now configure the values */
384       gst_segment_set_newsegment_full (&goom->segment, update,
385           rate, arate, format, start, stop, time);
386
387       res = gst_pad_push_event (goom->srcpad, event);
388       break;
389     }
390     default:
391       res = gst_pad_push_event (goom->srcpad, event);
392       break;
393   }
394   gst_object_unref (goom);
395
396   return res;
397 }
398
399 static gboolean
400 gst_goom_src_query (GstPad * pad, GstQuery * query)
401 {
402   gboolean res = FALSE;
403   GstGoom *goom;
404
405   goom = GST_GOOM (gst_pad_get_parent (pad));
406
407   switch (GST_QUERY_TYPE (query)) {
408     case GST_QUERY_LATENCY:
409     {
410       /* We need to send the query upstream and add the returned latency to our
411        * own */
412       GstClockTime min_latency, max_latency;
413       gboolean us_live;
414       GstClockTime our_latency;
415       guint max_samples;
416
417       if (goom->rate == 0)
418         break;
419
420       if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
421         gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
422
423         GST_DEBUG_OBJECT (goom, "Peer latency: min %"
424             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
425             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
426
427         /* the max samples we must buffer buffer */
428         max_samples = MAX (GOOM_SAMPLES, goom->spf);
429         our_latency =
430             gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
431
432         GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
433             GST_TIME_ARGS (our_latency));
434
435         /* we add some latency but only if we need to buffer more than what
436          * upstream gives us */
437         min_latency += our_latency;
438         if (max_latency != -1)
439           max_latency += our_latency;
440
441         GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
442             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
443             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
444
445         gst_query_set_latency (query, TRUE, min_latency, max_latency);
446       }
447       break;
448     }
449     default:
450       res = gst_pad_peer_query (goom->sinkpad, query);
451       break;
452   }
453
454   gst_object_unref (goom);
455
456   return res;
457 }
458
459 static GstFlowReturn
460 get_buffer (GstGoom * goom, GstBuffer ** outbuf)
461 {
462   GstFlowReturn ret;
463
464   if (GST_PAD_CAPS (goom->srcpad) == NULL) {
465     if (!gst_goom_src_negotiate (goom))
466       return GST_FLOW_NOT_NEGOTIATED;
467   }
468
469   GST_DEBUG_OBJECT (goom, "allocating output buffer with caps %"
470       GST_PTR_FORMAT, GST_PAD_CAPS (goom->srcpad));
471
472   ret =
473       gst_pad_alloc_buffer_and_set_caps (goom->srcpad,
474       GST_BUFFER_OFFSET_NONE, goom->outsize,
475       GST_PAD_CAPS (goom->srcpad), outbuf);
476   if (ret != GST_FLOW_OK)
477     return ret;
478
479   return GST_FLOW_OK;
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   /* If we don't have an output format yet, preallocate a buffer to try and
496    * set one */
497   if (GST_PAD_CAPS (goom->srcpad) == NULL) {
498     ret = get_buffer (goom, &outbuf);
499     if (ret != GST_FLOW_OK) {
500       gst_buffer_unref (buffer);
501       goto beach;
502     }
503   }
504
505   /* don't try to combine samples from discont buffer */
506   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
507     gst_adapter_clear (goom->adapter);
508   }
509
510   GST_DEBUG_OBJECT (goom,
511       "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
512       GST_BUFFER_SIZE (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
513
514   /* Collect samples until we have enough for an output frame */
515   gst_adapter_push (goom->adapter, buffer);
516
517   ret = GST_FLOW_OK;
518
519   while (TRUE) {
520     const guint16 *data;
521     gboolean need_skip;
522     guchar *out_frame;
523     gint i;
524     guint avail, to_flush;
525     guint64 dist, timestamp;
526
527     avail = gst_adapter_available (goom->adapter);
528     GST_DEBUG_OBJECT (goom, "avail now %u", avail);
529
530     /* we need GOOM_SAMPLES to get a meaningful result from goom. */
531     if (avail < (GOOM_SAMPLES * goom->bps))
532       break;
533
534     /* we also need enough samples to produce one frame at least */
535     if (avail < goom->bpf)
536       break;
537
538     GST_DEBUG_OBJECT (goom, "processing buffer");
539
540     /* get timestamp of the current adapter byte */
541     timestamp = gst_adapter_prev_timestamp (goom->adapter, &dist);
542     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
543       /* convert bytes to time */
544       dist /= goom->bps;
545       timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
546     }
547
548     if (timestamp != -1) {
549       gint64 qostime;
550
551       qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
552           timestamp);
553       qostime += goom->duration;
554
555       GST_OBJECT_LOCK (goom);
556       /* check for QoS, don't compute buffers that are known to be late */
557       need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
558       GST_OBJECT_UNLOCK (goom);
559
560       if (need_skip) {
561         GST_WARNING_OBJECT (goom,
562             "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
563             GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
564         goto skip;
565       }
566     }
567
568     /* get next GOOM_SAMPLES, we have at least this amount of samples */
569     data =
570         (const guint16 *) gst_adapter_peek (goom->adapter,
571         GOOM_SAMPLES * goom->bps);
572
573     if (goom->channels == 2) {
574       for (i = 0; i < GOOM_SAMPLES; i++) {
575         goom->datain[0][i] = *data++;
576         goom->datain[1][i] = *data++;
577       }
578     } else {
579       for (i = 0; i < GOOM_SAMPLES; i++) {
580         goom->datain[0][i] = *data;
581         goom->datain[1][i] = *data++;
582       }
583     }
584
585     /* alloc a buffer if we don't have one yet, this happens
586      * when we pushed a buffer in this while loop before */
587     if (outbuf == NULL) {
588       ret = get_buffer (goom, &outbuf);
589       if (ret != GST_FLOW_OK) {
590         goto beach;
591       }
592     }
593
594     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
595     GST_BUFFER_DURATION (outbuf) = goom->duration;
596     GST_BUFFER_SIZE (outbuf) = goom->outsize;
597
598     out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
599     memcpy (GST_BUFFER_DATA (outbuf), out_frame, goom->outsize);
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       break;
651     case GST_STATE_CHANGE_READY_TO_NULL:
652       break;
653     default:
654       break;
655   }
656
657   return ret;
658 }
659
660 static gboolean
661 plugin_init (GstPlugin * plugin)
662 {
663   GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
664
665 #if HAVE_ORC
666   orc_init ();
667 #endif
668
669   return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
670 }
671
672 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
673     GST_VERSION_MINOR,
674     "goom",
675     "GOOM visualization filter",
676     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)