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