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