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