upload tizen1.0 source
[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_pad_template (element_class,
141       gst_static_pad_template_get (&sink_template));
142   gst_element_class_add_pad_template (element_class,
143       gst_static_pad_template_get (&src_template));
144 }
145
146 static void
147 gst_goom_class_init (GstGoomClass * klass)
148 {
149   GObjectClass *gobject_class;
150   GstElementClass *gstelement_class;
151
152   gobject_class = (GObjectClass *) klass;
153   gstelement_class = (GstElementClass *) klass;
154
155   parent_class = g_type_class_peek_parent (klass);
156
157   gobject_class->finalize = gst_goom_finalize;
158
159   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_goom_change_state);
160 }
161
162 static void
163 gst_goom_init (GstGoom * goom)
164 {
165   /* create the sink and src pads */
166   goom->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
167   gst_pad_set_chain_function (goom->sinkpad,
168       GST_DEBUG_FUNCPTR (gst_goom_chain));
169   gst_pad_set_event_function (goom->sinkpad,
170       GST_DEBUG_FUNCPTR (gst_goom_sink_event));
171   gst_pad_set_setcaps_function (goom->sinkpad,
172       GST_DEBUG_FUNCPTR (gst_goom_sink_setcaps));
173   gst_element_add_pad (GST_ELEMENT (goom), goom->sinkpad);
174
175   goom->srcpad = gst_pad_new_from_static_template (&src_template, "src");
176   gst_pad_set_setcaps_function (goom->srcpad,
177       GST_DEBUG_FUNCPTR (gst_goom_src_setcaps));
178   gst_pad_set_event_function (goom->srcpad,
179       GST_DEBUG_FUNCPTR (gst_goom_src_event));
180   gst_pad_set_query_function (goom->srcpad,
181       GST_DEBUG_FUNCPTR (gst_goom_src_query));
182   gst_element_add_pad (GST_ELEMENT (goom), goom->srcpad);
183
184   goom->adapter = gst_adapter_new ();
185
186   goom->width = DEFAULT_WIDTH;
187   goom->height = DEFAULT_HEIGHT;
188   goom->fps_n = DEFAULT_FPS_N;  /* desired frame rate */
189   goom->fps_d = DEFAULT_FPS_D;  /* desired frame rate */
190   goom->channels = 0;
191   goom->rate = 0;
192   goom->duration = 0;
193
194   goom->plugin = goom_init (goom->width, goom->height);
195 }
196
197 static void
198 gst_goom_finalize (GObject * object)
199 {
200   GstGoom *goom = GST_GOOM (object);
201
202   goom_close (goom->plugin);
203   goom->plugin = NULL;
204
205   g_object_unref (goom->adapter);
206
207   G_OBJECT_CLASS (parent_class)->finalize (object);
208 }
209
210 static void
211 gst_goom_reset (GstGoom * goom)
212 {
213   gst_adapter_clear (goom->adapter);
214   gst_segment_init (&goom->segment, GST_FORMAT_UNDEFINED);
215
216   GST_OBJECT_LOCK (goom);
217   goom->proportion = 1.0;
218   goom->earliest_time = -1;
219   GST_OBJECT_UNLOCK (goom);
220 }
221
222 static gboolean
223 gst_goom_sink_setcaps (GstPad * pad, GstCaps * caps)
224 {
225   GstGoom *goom;
226   GstStructure *structure;
227   gboolean res;
228
229   goom = GST_GOOM (GST_PAD_PARENT (pad));
230
231   structure = gst_caps_get_structure (caps, 0);
232
233   res = gst_structure_get_int (structure, "channels", &goom->channels);
234   res &= gst_structure_get_int (structure, "rate", &goom->rate);
235
236   goom->bps = goom->channels * sizeof (gint16);
237
238   return res;
239 }
240
241 static gboolean
242 gst_goom_src_setcaps (GstPad * pad, GstCaps * caps)
243 {
244   GstGoom *goom;
245   GstStructure *structure;
246
247   goom = GST_GOOM (GST_PAD_PARENT (pad));
248
249   structure = gst_caps_get_structure (caps, 0);
250
251   if (!gst_structure_get_int (structure, "width", &goom->width) ||
252       !gst_structure_get_int (structure, "height", &goom->height) ||
253       !gst_structure_get_fraction (structure, "framerate", &goom->fps_n,
254           &goom->fps_d))
255     return FALSE;
256
257   goom_set_resolution (goom->plugin, goom->width, goom->height);
258
259   /* size of the output buffer in bytes, depth is always 4 bytes */
260   goom->outsize = goom->width * goom->height * 4;
261   goom->duration =
262       gst_util_uint64_scale_int (GST_SECOND, goom->fps_d, goom->fps_n);
263   goom->spf = gst_util_uint64_scale_int (goom->rate, goom->fps_d, goom->fps_n);
264   goom->bpf = goom->spf * goom->bps;
265
266   GST_DEBUG_OBJECT (goom, "dimension %dx%d, framerate %d/%d, spf %d",
267       goom->width, goom->height, goom->fps_n, goom->fps_d, goom->spf);
268
269   return TRUE;
270 }
271
272 static gboolean
273 gst_goom_src_negotiate (GstGoom * goom)
274 {
275   GstCaps *othercaps, *target;
276   GstStructure *structure;
277   const GstCaps *templ;
278
279   templ = gst_pad_get_pad_template_caps (goom->srcpad);
280
281   GST_DEBUG_OBJECT (goom, "performing negotiation");
282
283   /* see what the peer can do */
284   othercaps = gst_pad_peer_get_caps (goom->srcpad);
285   if (othercaps) {
286     target = gst_caps_intersect (othercaps, templ);
287     gst_caps_unref (othercaps);
288
289     if (gst_caps_is_empty (target))
290       goto no_format;
291
292     gst_caps_truncate (target);
293   } else {
294     target = gst_caps_ref ((GstCaps *) templ);
295   }
296
297   structure = gst_caps_get_structure (target, 0);
298   gst_structure_fixate_field_nearest_int (structure, "width", DEFAULT_WIDTH);
299   gst_structure_fixate_field_nearest_int (structure, "height", DEFAULT_HEIGHT);
300   gst_structure_fixate_field_nearest_fraction (structure, "framerate",
301       DEFAULT_FPS_N, DEFAULT_FPS_D);
302
303   gst_pad_set_caps (goom->srcpad, target);
304   gst_caps_unref (target);
305
306   return TRUE;
307
308 no_format:
309   {
310     gst_caps_unref (target);
311     return FALSE;
312   }
313 }
314
315 static gboolean
316 gst_goom_src_event (GstPad * pad, GstEvent * event)
317 {
318   gboolean res;
319   GstGoom *goom;
320
321   goom = GST_GOOM (gst_pad_get_parent (pad));
322
323   switch (GST_EVENT_TYPE (event)) {
324     case GST_EVENT_QOS:
325     {
326       gdouble proportion;
327       GstClockTimeDiff diff;
328       GstClockTime timestamp;
329
330       gst_event_parse_qos (event, &proportion, &diff, &timestamp);
331
332       /* save stuff for the _chain() function */
333       GST_OBJECT_LOCK (goom);
334       goom->proportion = proportion;
335       if (diff >= 0)
336         /* we're late, this is a good estimate for next displayable
337          * frame (see part-qos.txt) */
338         goom->earliest_time = timestamp + 2 * diff + goom->duration;
339       else
340         goom->earliest_time = timestamp + diff;
341       GST_OBJECT_UNLOCK (goom);
342
343       res = gst_pad_push_event (goom->sinkpad, event);
344       break;
345     }
346     default:
347       res = gst_pad_push_event (goom->sinkpad, event);
348       break;
349   }
350   gst_object_unref (goom);
351
352   return res;
353 }
354
355 static gboolean
356 gst_goom_sink_event (GstPad * pad, GstEvent * event)
357 {
358   gboolean res;
359   GstGoom *goom;
360
361   goom = GST_GOOM (gst_pad_get_parent (pad));
362
363   switch (GST_EVENT_TYPE (event)) {
364     case GST_EVENT_FLUSH_START:
365       res = gst_pad_push_event (goom->srcpad, event);
366       break;
367     case GST_EVENT_FLUSH_STOP:
368       gst_goom_reset (goom);
369       res = gst_pad_push_event (goom->srcpad, event);
370       break;
371     case GST_EVENT_NEWSEGMENT:
372     {
373       GstFormat format;
374       gdouble rate, arate;
375       gint64 start, stop, time;
376       gboolean update;
377
378       /* the newsegment values are used to clip the input samples
379        * and to convert the incomming timestamps to running time so
380        * we can do QoS */
381       gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
382           &start, &stop, &time);
383
384       /* now configure the values */
385       gst_segment_set_newsegment_full (&goom->segment, update,
386           rate, arate, format, start, stop, time);
387
388       res = gst_pad_push_event (goom->srcpad, event);
389       break;
390     }
391     default:
392       res = gst_pad_push_event (goom->srcpad, event);
393       break;
394   }
395   gst_object_unref (goom);
396
397   return res;
398 }
399
400 static gboolean
401 gst_goom_src_query (GstPad * pad, GstQuery * query)
402 {
403   gboolean res;
404   GstGoom *goom;
405
406   goom = GST_GOOM (gst_pad_get_parent (pad));
407
408   switch (GST_QUERY_TYPE (query)) {
409     case GST_QUERY_LATENCY:
410     {
411       /* We need to send the query upstream and add the returned latency to our
412        * own */
413       GstClockTime min_latency, max_latency;
414       gboolean us_live;
415       GstClockTime our_latency;
416       guint max_samples;
417
418       if ((res = gst_pad_peer_query (goom->sinkpad, query))) {
419         gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
420
421         GST_DEBUG_OBJECT (goom, "Peer latency: min %"
422             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
423             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
424
425         /* the max samples we must buffer buffer */
426         max_samples = MAX (GOOM_SAMPLES, goom->spf);
427         our_latency =
428             gst_util_uint64_scale_int (max_samples, GST_SECOND, goom->rate);
429
430         GST_DEBUG_OBJECT (goom, "Our latency: %" GST_TIME_FORMAT,
431             GST_TIME_ARGS (our_latency));
432
433         /* we add some latency but only if we need to buffer more than what
434          * upstream gives us */
435         min_latency += our_latency;
436         if (max_latency != -1)
437           max_latency += our_latency;
438
439         GST_DEBUG_OBJECT (goom, "Calculated total latency : min %"
440             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
441             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
442
443         gst_query_set_latency (query, TRUE, min_latency, max_latency);
444       }
445       break;
446     }
447     default:
448       res = gst_pad_peer_query (goom->sinkpad, query);
449       break;
450   }
451
452   gst_object_unref (goom);
453
454   return res;
455 }
456
457 static GstFlowReturn
458 get_buffer (GstGoom * goom, GstBuffer ** outbuf)
459 {
460   GstFlowReturn ret;
461
462   if (GST_PAD_CAPS (goom->srcpad) == NULL) {
463     if (!gst_goom_src_negotiate (goom))
464       return GST_FLOW_NOT_NEGOTIATED;
465   }
466
467   GST_DEBUG_OBJECT (goom, "allocating output buffer with caps %"
468       GST_PTR_FORMAT, GST_PAD_CAPS (goom->srcpad));
469
470   ret =
471       gst_pad_alloc_buffer_and_set_caps (goom->srcpad,
472       GST_BUFFER_OFFSET_NONE, goom->outsize,
473       GST_PAD_CAPS (goom->srcpad), outbuf);
474   if (ret != GST_FLOW_OK)
475     return ret;
476
477   return GST_FLOW_OK;
478 }
479
480 static GstFlowReturn
481 gst_goom_chain (GstPad * pad, GstBuffer * buffer)
482 {
483   GstGoom *goom;
484   GstFlowReturn ret;
485   GstBuffer *outbuf = NULL;
486
487   goom = GST_GOOM (gst_pad_get_parent (pad));
488   if (goom->bps == 0) {
489     ret = GST_FLOW_NOT_NEGOTIATED;
490     goto beach;
491   }
492
493   /* If we don't have an output format yet, preallocate a buffer to try and
494    * set one */
495   if (GST_PAD_CAPS (goom->srcpad) == NULL) {
496     ret = get_buffer (goom, &outbuf);
497     if (ret != GST_FLOW_OK) {
498       gst_buffer_unref (buffer);
499       goto beach;
500     }
501   }
502
503   /* don't try to combine samples from discont buffer */
504   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
505     gst_adapter_clear (goom->adapter);
506   }
507
508   GST_DEBUG_OBJECT (goom,
509       "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
510       GST_BUFFER_SIZE (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));
511
512   /* Collect samples until we have enough for an output frame */
513   gst_adapter_push (goom->adapter, buffer);
514
515   ret = GST_FLOW_OK;
516
517   while (TRUE) {
518     const guint16 *data;
519     gboolean need_skip;
520     guchar *out_frame;
521     gint i;
522     guint avail, to_flush;
523     guint64 dist, timestamp;
524
525     avail = gst_adapter_available (goom->adapter);
526     GST_DEBUG_OBJECT (goom, "avail now %u", avail);
527
528     /* we need GOOM_SAMPLES to get a meaningful result from goom. */
529     if (avail < (GOOM_SAMPLES * goom->bps))
530       break;
531
532     /* we also need enough samples to produce one frame at least */
533     if (avail < goom->bpf)
534       break;
535
536     GST_DEBUG_OBJECT (goom, "processing buffer");
537
538     /* get timestamp of the current adapter byte */
539     timestamp = gst_adapter_prev_timestamp (goom->adapter, &dist);
540     if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
541       /* convert bytes to time */
542       dist /= goom->bps;
543       timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
544     }
545
546     if (timestamp != -1) {
547       gint64 qostime;
548
549       qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
550           timestamp);
551       qostime += goom->duration;
552
553       GST_OBJECT_LOCK (goom);
554       /* check for QoS, don't compute buffers that are known to be late */
555       need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
556       GST_OBJECT_UNLOCK (goom);
557
558       if (need_skip) {
559         GST_WARNING_OBJECT (goom,
560             "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
561             GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
562         goto skip;
563       }
564     }
565
566     /* get next GOOM_SAMPLES, we have at least this amount of samples */
567     data =
568         (const guint16 *) gst_adapter_peek (goom->adapter,
569         GOOM_SAMPLES * goom->bps);
570
571     if (goom->channels == 2) {
572       for (i = 0; i < GOOM_SAMPLES; i++) {
573         goom->datain[0][i] = *data++;
574         goom->datain[1][i] = *data++;
575       }
576     } else {
577       for (i = 0; i < GOOM_SAMPLES; i++) {
578         goom->datain[0][i] = *data;
579         goom->datain[1][i] = *data++;
580       }
581     }
582
583     /* alloc a buffer if we don't have one yet, this happens
584      * when we pushed a buffer in this while loop before */
585     if (outbuf == NULL) {
586       ret = get_buffer (goom, &outbuf);
587       if (ret != GST_FLOW_OK) {
588         goto beach;
589       }
590     }
591
592     GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
593     GST_BUFFER_DURATION (outbuf) = goom->duration;
594     GST_BUFFER_SIZE (outbuf) = goom->outsize;
595
596     out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
597     memcpy (GST_BUFFER_DATA (outbuf), out_frame, goom->outsize);
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   gst_object_unref (goom);
624
625   return ret;
626 }
627
628 static GstStateChangeReturn
629 gst_goom_change_state (GstElement * element, GstStateChange transition)
630 {
631   GstGoom *goom = GST_GOOM (element);
632   GstStateChangeReturn ret;
633
634   switch (transition) {
635     case GST_STATE_CHANGE_NULL_TO_READY:
636       break;
637     case GST_STATE_CHANGE_READY_TO_PAUSED:
638       gst_goom_reset (goom);
639       break;
640     default:
641       break;
642   }
643
644   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
645
646   switch (transition) {
647     case GST_STATE_CHANGE_PAUSED_TO_READY:
648       break;
649     case GST_STATE_CHANGE_READY_TO_NULL:
650       break;
651     default:
652       break;
653   }
654
655   return ret;
656 }
657
658 static gboolean
659 plugin_init (GstPlugin * plugin)
660 {
661   GST_DEBUG_CATEGORY_INIT (goom_debug, "goom", 0, "goom visualisation element");
662
663 #if HAVE_ORC
664   orc_init ();
665 #endif
666
667   return gst_element_register (plugin, "goom", GST_RANK_NONE, GST_TYPE_GOOM);
668 }
669
670 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
671     GST_VERSION_MINOR,
672     "goom",
673     "GOOM visualization filter",
674     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)