audiovisualizer: sync to change in base and port
[platform/upstream/gstreamer.git] / gst / audiovisualizers / gstaudiovisualizer.c
1 /* GStreamer
2  * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstaudiovisualizer.h: base class for audio visualisation elements
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 /**
21  * SECTION:gstaudiovisualizer
22  *
23  * A baseclass for scopes (visualizers). It takes care of re-fitting the
24  * audio-rate to video-rate and handles renegotiation (downstream video size
25  * changes).
26  * 
27  * It also provides several background shading effects. These effects are
28  * applied to a previous picture before the render() implementation can draw a
29  * new frame.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
37  * with newer GLib versions (>= 2.31.0) */
38 #define GLIB_DISABLE_DEPRECATION_WARNINGS
39
40 #include <string.h>
41
42 #include <gst/video/video.h>
43 #include <gst/video/gstvideometa.h>
44 #include <gst/video/gstvideopool.h>
45
46 #include "gstaudiovisualizer.h"
47
48 GST_DEBUG_CATEGORY_STATIC (audio_visualizer_debug);
49 #define GST_CAT_DEFAULT (audio_visualizer_debug)
50
51 #define DEFAULT_SHADER GST_AUDIO_VISUALIZER_SHADER_FADE
52 #define DEFAULT_SHADE_AMOUNT   0x000a0a0a
53
54 enum
55 {
56   PROP_0,
57   PROP_SHADER,
58   PROP_SHADE_AMOUNT
59 };
60
61 static GstBaseTransformClass *parent_class = NULL;
62
63 static void gst_audio_visualizer_class_init (GstAudioVisualizerClass * klass);
64 static void gst_audio_visualizer_init (GstAudioVisualizer * scope,
65     GstAudioVisualizerClass * g_class);
66 static void gst_audio_visualizer_set_property (GObject * object,
67     guint prop_id, const GValue * value, GParamSpec * pspec);
68 static void gst_audio_visualizer_get_property (GObject * object,
69     guint prop_id, GValue * value, GParamSpec * pspec);
70 static void gst_audio_visualizer_dispose (GObject * object);
71
72 static gboolean gst_audio_visualizer_src_negotiate (GstAudioVisualizer * scope);
73 static gboolean gst_audio_visualizer_src_setcaps (GstAudioVisualizer *
74     scope, GstCaps * caps);
75 static gboolean gst_audio_visualizer_sink_setcaps (GstAudioVisualizer *
76     scope, GstCaps * caps);
77
78 static GstFlowReturn gst_audio_visualizer_chain (GstPad * pad,
79     GstObject * parent, GstBuffer * buffer);
80
81 static gboolean gst_audio_visualizer_src_event (GstPad * pad,
82     GstObject * parent, GstEvent * event);
83 static gboolean gst_audio_visualizer_sink_event (GstPad * pad,
84     GstObject * parent, GstEvent * event);
85
86 static gboolean gst_audio_visualizer_src_query (GstPad * pad,
87     GstObject * parent, GstQuery * query);
88 static gboolean gst_audio_visualizer_sink_query (GstPad * pad,
89     GstObject * parent, GstQuery * query);
90
91 static GstStateChangeReturn gst_audio_visualizer_change_state (GstElement *
92     element, GstStateChange transition);
93
94 /* shading functions */
95
96 #define GST_TYPE_AUDIO_VISUALIZER_SHADER (gst_audio_visualizer_shader_get_type())
97 static GType
98 gst_audio_visualizer_shader_get_type (void)
99 {
100   static GType shader_type = 0;
101   static const GEnumValue shaders[] = {
102     {GST_AUDIO_VISUALIZER_SHADER_NONE, "None", "none"},
103     {GST_AUDIO_VISUALIZER_SHADER_FADE, "Fade", "fade"},
104     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP, "Fade and move up",
105         "fade-and-move-up"},
106     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN, "Fade and move down",
107         "fade-and-move-down"},
108     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT, "Fade and move left",
109         "fade-and-move-left"},
110     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT,
111           "Fade and move right",
112         "fade-and-move-right"},
113     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT,
114         "Fade and move horizontally out", "fade-and-move-horiz-out"},
115     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN,
116         "Fade and move horizontally in", "fade-and-move-horiz-in"},
117     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT,
118         "Fade and move vertically out", "fade-and-move-vert-out"},
119     {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN,
120         "Fade and move vertically in", "fade-and-move-vert-in"},
121     {0, NULL, NULL},
122   };
123
124   if (G_UNLIKELY (shader_type == 0)) {
125     /* TODO: rename when exporting it as a library */
126     shader_type =
127         g_enum_register_static ("GstAudioVisualizerShader-BaseExtVisual",
128         shaders);
129   }
130   return shader_type;
131 }
132
133 /* we're only supporting GST_VIDEO_FORMAT_xRGB right now) */
134 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
135
136 #define SHADE(_d, _s, _i, _r, _g, _b)                     \
137 G_STMT_START {                                            \
138     _d[_i * 4 + 0] = (_s[_i * 4 + 0] > _b) ? _s[_i * 4 + 0] - _b : 0; \
139     _d[_i * 4 + 1] = (_s[_i * 4 + 1] > _g) ? _s[_i * 4 + 1] - _g : 0; \
140     _d[_i * 4 + 2] = (_s[_i * 4 + 2] > _r) ? _s[_i * 4 + 2] - _r : 0; \
141     _d[_i * 4 + 3] = 0;                                       \
142 } G_STMT_END
143
144 #else /* G_BYTE_ORDER == G_LITTLE_ENDIAN */
145
146 #define SHADE(_d, _s, _i, _r, _g, _b)                     \
147 G_STMT_START {                                            \
148     _d[_i * 4 + 0] = 0;                                       \
149     _d[_i * 4 + 1] = (_s[_i * 4 + 1] > _r) ? _s[_i * 4 + 1] - _r : 0; \
150     _d[_i * 4 + 2] = (_s[_i * 4 + 2] > _g) ? _s[_i * 4 + 2] - _g : 0; \
151     _d[_i * 4 + 3] = (_s[_i * 4 + 3] > _b) ? _s[_i * 4 + 3] - _b : 0; \
152 } G_STMT_END
153
154 #endif
155
156 static void
157 shader_fade (GstAudioVisualizer * scope, const GstVideoFrame * sframe,
158     GstVideoFrame * dframe)
159 {
160   guint i, j;
161   guint r = (scope->shade_amount >> 16) & 0xff;
162   guint g = (scope->shade_amount >> 8) & 0xff;
163   guint b = (scope->shade_amount >> 0) & 0xff;
164   guint8 *s, *d;
165   gint ss, ds, width, height;
166
167   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
168   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
169   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
170   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
171
172   width = GST_VIDEO_FRAME_WIDTH (sframe);
173   height = GST_VIDEO_FRAME_HEIGHT (sframe);
174
175   for (j = 0; j < height; j++) {
176     for (i = 0; i < width; i++) {
177       SHADE (d, s, i, r, g, b);
178     }
179     s += ss;
180     d += ds;
181   }
182 }
183
184 static void
185 shader_fade_and_move_up (GstAudioVisualizer * scope,
186     const GstVideoFrame * sframe, GstVideoFrame * dframe)
187 {
188   guint i, j;
189   guint r = (scope->shade_amount >> 16) & 0xff;
190   guint g = (scope->shade_amount >> 8) & 0xff;
191   guint b = (scope->shade_amount >> 0) & 0xff;
192   guint8 *s, *d;
193   gint ss, ds, width, height;
194
195   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
196   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
197   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
198   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
199
200   width = GST_VIDEO_FRAME_WIDTH (sframe);
201   height = GST_VIDEO_FRAME_HEIGHT (sframe);
202
203   for (j = 1; j < height; j++) {
204     s += ss;
205     for (i = 0; i < width; i++) {
206       SHADE (d, s, i, r, g, b);
207     }
208     d += ds;
209   }
210 }
211
212 static void
213 shader_fade_and_move_down (GstAudioVisualizer * scope,
214     const GstVideoFrame * sframe, GstVideoFrame * dframe)
215 {
216   guint i, j;
217   guint r = (scope->shade_amount >> 16) & 0xff;
218   guint g = (scope->shade_amount >> 8) & 0xff;
219   guint b = (scope->shade_amount >> 0) & 0xff;
220   guint8 *s, *d;
221   gint ss, ds, width, height;
222
223   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
224   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
225   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
226   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
227
228   width = GST_VIDEO_FRAME_WIDTH (sframe);
229   height = GST_VIDEO_FRAME_HEIGHT (sframe);
230
231   for (j = 1; j < height; j++) {
232     d += ds;
233     for (i = 0; i < width; i++) {
234       SHADE (d, s, i, r, g, b);
235     }
236     s += ss;
237   }
238 }
239
240 static void
241 shader_fade_and_move_left (GstAudioVisualizer * scope,
242     const GstVideoFrame * sframe, GstVideoFrame * dframe)
243 {
244   guint i, j;
245   guint r = (scope->shade_amount >> 16) & 0xff;
246   guint g = (scope->shade_amount >> 8) & 0xff;
247   guint b = (scope->shade_amount >> 0) & 0xff;
248   guint8 *s, *d;
249   gint ss, ds, width, height;
250
251   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
252   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
253   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
254   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
255
256   width = GST_VIDEO_FRAME_WIDTH (sframe);
257   height = GST_VIDEO_FRAME_HEIGHT (sframe);
258
259   width -= 1;
260   s += 4;
261
262   /* move to the left */
263   for (j = 0; j < height; j++) {
264     for (i = 0; i < width; i++) {
265       SHADE (d, s, i, r, g, b);
266     }
267     d += ds;
268     s += ss;
269   }
270 }
271
272 static void
273 shader_fade_and_move_right (GstAudioVisualizer * scope,
274     const GstVideoFrame * sframe, GstVideoFrame * dframe)
275 {
276   guint i, j;
277   guint r = (scope->shade_amount >> 16) & 0xff;
278   guint g = (scope->shade_amount >> 8) & 0xff;
279   guint b = (scope->shade_amount >> 0) & 0xff;
280   guint8 *s, *d;
281   gint ss, ds, width, height;
282
283   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
284   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
285   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
286   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
287
288   width = GST_VIDEO_FRAME_WIDTH (sframe);
289   height = GST_VIDEO_FRAME_HEIGHT (sframe);
290
291   width -= 1;
292   d += 4;
293
294   /* move to the right */
295   for (j = 0; j < height; j++) {
296     for (i = 0; i < width; i++) {
297       SHADE (d, s, i, r, g, b);
298     }
299     d += ds;
300     s += ss;
301   }
302 }
303
304 static void
305 shader_fade_and_move_horiz_out (GstAudioVisualizer * scope,
306     const GstVideoFrame * sframe, GstVideoFrame * dframe)
307 {
308   guint i, j;
309   guint r = (scope->shade_amount >> 16) & 0xff;
310   guint g = (scope->shade_amount >> 8) & 0xff;
311   guint b = (scope->shade_amount >> 0) & 0xff;
312   guint8 *s, *d;
313   gint ss, ds, width, height;
314
315   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
316   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
317   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
318   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
319
320   width = GST_VIDEO_FRAME_WIDTH (sframe);
321   height = GST_VIDEO_FRAME_HEIGHT (sframe);
322
323   /* move upper half up */
324   for (j = 0; j < height / 2; j++) {
325     s += ss;
326     for (i = 0; i < width; i++) {
327       SHADE (d, s, i, r, g, b);
328     }
329     d += ds;
330   }
331   /* move lower half down */
332   for (j = 0; j < height / 2; j++) {
333     d += ds;
334     for (i = 0; i < width; i++) {
335       SHADE (d, s, i, r, g, b);
336     }
337     s += ss;
338   }
339 }
340
341 static void
342 shader_fade_and_move_horiz_in (GstAudioVisualizer * scope,
343     const GstVideoFrame * sframe, GstVideoFrame * dframe)
344 {
345   guint i, j;
346   guint r = (scope->shade_amount >> 16) & 0xff;
347   guint g = (scope->shade_amount >> 8) & 0xff;
348   guint b = (scope->shade_amount >> 0) & 0xff;
349   guint8 *s, *d;
350   gint ss, ds, width, height;
351
352   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
353   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
354   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
355   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
356
357   width = GST_VIDEO_FRAME_WIDTH (sframe);
358   height = GST_VIDEO_FRAME_HEIGHT (sframe);
359
360   /* move upper half down */
361   for (j = 0; j < height / 2; j++) {
362     d += ds;
363     for (i = 0; i < width; i++) {
364       SHADE (d, s, i, r, g, b);
365     }
366     s += ss;
367   }
368   /* move lower half up */
369   for (j = 0; j < height / 2; j++) {
370     s += ss;
371     for (i = 0; i < width; i++) {
372       SHADE (d, s, i, r, g, b);
373     }
374     d += ds;
375   }
376 }
377
378 static void
379 shader_fade_and_move_vert_out (GstAudioVisualizer * scope,
380     const GstVideoFrame * sframe, GstVideoFrame * dframe)
381 {
382   guint i, j;
383   guint r = (scope->shade_amount >> 16) & 0xff;
384   guint g = (scope->shade_amount >> 8) & 0xff;
385   guint b = (scope->shade_amount >> 0) & 0xff;
386   guint8 *s, *s1, *d, *d1;
387   gint ss, ds, width, height;
388
389   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
390   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
391   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
392   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
393
394   width = GST_VIDEO_FRAME_WIDTH (sframe);
395   height = GST_VIDEO_FRAME_HEIGHT (sframe);
396
397   for (j = 0; j < height; j++) {
398     /* move left half to the left */
399     s1 = s + 1;
400     for (i = 0; i < width / 2; i++) {
401       SHADE (d, s1, i, r, g, b);
402     }
403     /* move right half to the right */
404     d1 = d + 1;
405     for (; i < width - 1; i++) {
406       SHADE (d1, s, i, r, g, b);
407     }
408     s += ss;
409     d += ds;
410   }
411 }
412
413 static void
414 shader_fade_and_move_vert_in (GstAudioVisualizer * scope,
415     const GstVideoFrame * sframe, GstVideoFrame * dframe)
416 {
417   guint i, j;
418   guint r = (scope->shade_amount >> 16) & 0xff;
419   guint g = (scope->shade_amount >> 8) & 0xff;
420   guint b = (scope->shade_amount >> 0) & 0xff;
421   guint8 *s, *s1, *d, *d1;
422   gint ss, ds, width, height;
423
424   s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
425   ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
426   d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
427   ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
428
429   width = GST_VIDEO_FRAME_WIDTH (sframe);
430   height = GST_VIDEO_FRAME_HEIGHT (sframe);
431
432   for (j = 0; j < height; j++) {
433     /* move left half to the right */
434     d1 = d + 1;
435     for (i = 0; i < width / 2; i++) {
436       SHADE (d1, s, i, r, g, b);
437     }
438     /* move right half to the left */
439     s1 = s + 1;
440     for (; i < width - 1; i++) {
441       SHADE (d, s1, i, r, g, b);
442     }
443     s += ss;
444     d += ds;
445   }
446 }
447
448 static void
449 gst_audio_visualizer_change_shader (GstAudioVisualizer * scope)
450 {
451   switch (scope->shader_type) {
452     case GST_AUDIO_VISUALIZER_SHADER_NONE:
453       scope->shader = NULL;
454       break;
455     case GST_AUDIO_VISUALIZER_SHADER_FADE:
456       scope->shader = shader_fade;
457       break;
458     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP:
459       scope->shader = shader_fade_and_move_up;
460       break;
461     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN:
462       scope->shader = shader_fade_and_move_down;
463       break;
464     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT:
465       scope->shader = shader_fade_and_move_left;
466       break;
467     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT:
468       scope->shader = shader_fade_and_move_right;
469       break;
470     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT:
471       scope->shader = shader_fade_and_move_horiz_out;
472       break;
473     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN:
474       scope->shader = shader_fade_and_move_horiz_in;
475       break;
476     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT:
477       scope->shader = shader_fade_and_move_vert_out;
478       break;
479     case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN:
480       scope->shader = shader_fade_and_move_vert_in;
481       break;
482     default:
483       GST_ERROR ("invalid shader function");
484       scope->shader = NULL;
485       break;
486   }
487 }
488
489 /* base class */
490
491 GType
492 gst_audio_visualizer_get_type (void)
493 {
494   static volatile gsize audio_visualizer_type = 0;
495
496   if (g_once_init_enter (&audio_visualizer_type)) {
497     static const GTypeInfo audio_visualizer_info = {
498       sizeof (GstAudioVisualizerClass),
499       NULL,
500       NULL,
501       (GClassInitFunc) gst_audio_visualizer_class_init,
502       NULL,
503       NULL,
504       sizeof (GstAudioVisualizer),
505       0,
506       (GInstanceInitFunc) gst_audio_visualizer_init,
507     };
508     GType _type;
509
510     /* TODO: rename when exporting it as a library */
511     _type = g_type_register_static (GST_TYPE_ELEMENT,
512         "GstAudioVisualizer-BadGstAudioVisualizers", &audio_visualizer_info,
513         G_TYPE_FLAG_ABSTRACT);
514     g_once_init_leave (&audio_visualizer_type, _type);
515   }
516   return (GType) audio_visualizer_type;
517 }
518
519 static void
520 gst_audio_visualizer_class_init (GstAudioVisualizerClass * klass)
521 {
522   GObjectClass *gobject_class = (GObjectClass *) klass;
523   GstElementClass *element_class = (GstElementClass *) klass;
524
525   parent_class = g_type_class_peek_parent (klass);
526
527   GST_DEBUG_CATEGORY_INIT (audio_visualizer_debug, "baseaudiovisualizer",
528       0, "scope audio visualisation base class");
529
530   gobject_class->set_property = gst_audio_visualizer_set_property;
531   gobject_class->get_property = gst_audio_visualizer_get_property;
532   gobject_class->dispose = gst_audio_visualizer_dispose;
533
534   element_class->change_state =
535       GST_DEBUG_FUNCPTR (gst_audio_visualizer_change_state);
536
537   g_object_class_install_property (gobject_class, PROP_SHADER,
538       g_param_spec_enum ("shader", "shader type",
539           "Shader function to apply on each frame",
540           GST_TYPE_AUDIO_VISUALIZER_SHADER, DEFAULT_SHADER,
541           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
542   g_object_class_install_property (gobject_class, PROP_SHADE_AMOUNT,
543       g_param_spec_uint ("shade-amount", "shade amount",
544           "Shading color to use (big-endian ARGB)", 0, G_MAXUINT32,
545           DEFAULT_SHADE_AMOUNT,
546           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
547 }
548
549 static void
550 gst_audio_visualizer_init (GstAudioVisualizer * scope,
551     GstAudioVisualizerClass * g_class)
552 {
553   GstPadTemplate *pad_template;
554
555   /* create the sink and src pads */
556   pad_template =
557       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
558   g_return_if_fail (pad_template != NULL);
559   scope->sinkpad = gst_pad_new_from_template (pad_template, "sink");
560   gst_pad_set_chain_function (scope->sinkpad,
561       GST_DEBUG_FUNCPTR (gst_audio_visualizer_chain));
562   gst_pad_set_event_function (scope->sinkpad,
563       GST_DEBUG_FUNCPTR (gst_audio_visualizer_sink_event));
564   gst_pad_set_query_function (scope->sinkpad,
565       GST_DEBUG_FUNCPTR (gst_audio_visualizer_sink_query));
566   gst_element_add_pad (GST_ELEMENT (scope), scope->sinkpad);
567
568   pad_template =
569       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
570   g_return_if_fail (pad_template != NULL);
571   scope->srcpad = gst_pad_new_from_template (pad_template, "src");
572   gst_pad_set_event_function (scope->srcpad,
573       GST_DEBUG_FUNCPTR (gst_audio_visualizer_src_event));
574   gst_pad_set_query_function (scope->srcpad,
575       GST_DEBUG_FUNCPTR (gst_audio_visualizer_src_query));
576   gst_element_add_pad (GST_ELEMENT (scope), scope->srcpad);
577
578   scope->adapter = gst_adapter_new ();
579   scope->inbuf = gst_buffer_new ();
580
581   /* properties */
582   scope->shader_type = DEFAULT_SHADER;
583   gst_audio_visualizer_change_shader (scope);
584   scope->shade_amount = DEFAULT_SHADE_AMOUNT;
585
586   /* reset the initial video state */
587   gst_video_info_init (&scope->vinfo);
588   scope->frame_duration = GST_CLOCK_TIME_NONE;
589
590   /* reset the initial state */
591   gst_audio_info_init (&scope->ainfo);
592   gst_video_info_init (&scope->vinfo);
593
594   g_mutex_init (&scope->config_lock);
595 }
596
597 static void
598 gst_audio_visualizer_set_property (GObject * object, guint prop_id,
599     const GValue * value, GParamSpec * pspec)
600 {
601   GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
602
603   switch (prop_id) {
604     case PROP_SHADER:
605       scope->shader_type = g_value_get_enum (value);
606       gst_audio_visualizer_change_shader (scope);
607       break;
608     case PROP_SHADE_AMOUNT:
609       scope->shade_amount = g_value_get_uint (value);
610       break;
611     default:
612       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
613       break;
614   }
615 }
616
617 static void
618 gst_audio_visualizer_get_property (GObject * object, guint prop_id,
619     GValue * value, GParamSpec * pspec)
620 {
621   GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
622
623   switch (prop_id) {
624     case PROP_SHADER:
625       g_value_set_enum (value, scope->shader_type);
626       break;
627     case PROP_SHADE_AMOUNT:
628       g_value_set_uint (value, scope->shade_amount);
629       break;
630     default:
631       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
632       break;
633   }
634 }
635
636 static void
637 gst_audio_visualizer_dispose (GObject * object)
638 {
639   GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
640
641   if (scope->adapter) {
642     g_object_unref (scope->adapter);
643     scope->adapter = NULL;
644   }
645   if (scope->inbuf) {
646     gst_buffer_unref (scope->inbuf);
647     scope->inbuf = NULL;
648   }
649   if (scope->tempbuf) {
650     gst_video_frame_unmap (&scope->tempframe);
651     gst_buffer_unref (scope->tempbuf);
652     scope->tempbuf = NULL;
653   }
654   if (scope->config_lock.p) {
655     g_mutex_clear (&scope->config_lock);
656     scope->config_lock.p = NULL;
657   }
658   G_OBJECT_CLASS (parent_class)->dispose (object);
659 }
660
661 static void
662 gst_audio_visualizer_reset (GstAudioVisualizer * scope)
663 {
664   gst_adapter_clear (scope->adapter);
665   gst_segment_init (&scope->segment, GST_FORMAT_UNDEFINED);
666
667   GST_OBJECT_LOCK (scope);
668   scope->proportion = 1.0;
669   scope->earliest_time = -1;
670   GST_OBJECT_UNLOCK (scope);
671 }
672
673 static gboolean
674 gst_audio_visualizer_sink_setcaps (GstAudioVisualizer * scope, GstCaps * caps)
675 {
676   GstAudioInfo info;
677   gboolean res = TRUE;
678
679   if (!gst_audio_info_from_caps (&info, caps))
680     goto wrong_caps;
681
682   scope->ainfo = info;
683
684   GST_DEBUG_OBJECT (scope, "audio: channels %d, rate %d",
685       GST_AUDIO_INFO_CHANNELS (&info), GST_AUDIO_INFO_RATE (&info));
686
687 done:
688   return res;
689
690   /* Errors */
691 wrong_caps:
692   {
693     GST_WARNING_OBJECT (scope, "could not parse caps");
694     res = FALSE;
695     goto done;
696   }
697 }
698
699 static gboolean
700 gst_audio_visualizer_src_setcaps (GstAudioVisualizer * scope, GstCaps * caps)
701 {
702   GstVideoInfo info;
703   GstAudioVisualizerClass *klass;
704   gboolean res;
705
706   if (!gst_video_info_from_caps (&info, caps))
707     goto wrong_caps;
708
709   klass = GST_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
710
711   scope->vinfo = info;
712
713   scope->frame_duration = gst_util_uint64_scale_int (GST_SECOND,
714       GST_VIDEO_INFO_FPS_D (&info), GST_VIDEO_INFO_FPS_N (&info));
715   scope->spf = gst_util_uint64_scale_int (GST_AUDIO_INFO_RATE (&scope->ainfo),
716       GST_VIDEO_INFO_FPS_D (&info), GST_VIDEO_INFO_FPS_N (&info));
717   scope->req_spf = scope->spf;
718
719   if (scope->tempbuf) {
720     gst_video_frame_unmap (&scope->tempframe);
721     gst_buffer_unref (scope->tempbuf);
722   }
723   scope->tempbuf = gst_buffer_new_wrapped (g_malloc0 (scope->vinfo.size),
724       scope->vinfo.size);
725   gst_video_frame_map (&scope->tempframe, &scope->vinfo, scope->tempbuf,
726       GST_MAP_READWRITE);
727
728   if (klass->setup)
729     res = klass->setup (scope);
730
731   GST_DEBUG_OBJECT (scope, "video: dimension %dx%d, framerate %d/%d",
732       GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info),
733       GST_VIDEO_INFO_FPS_N (&info), GST_VIDEO_INFO_FPS_D (&info));
734   GST_DEBUG_OBJECT (scope, "blocks: spf %u, req_spf %u",
735       scope->spf, scope->req_spf);
736
737   res = gst_pad_set_caps (scope->srcpad, caps);
738
739   return res;
740
741   /* ERRORS */
742 wrong_caps:
743   {
744     GST_DEBUG_OBJECT (scope, "error parsing caps");
745     return FALSE;
746   }
747 }
748
749 static gboolean
750 gst_audio_visualizer_src_negotiate (GstAudioVisualizer * scope)
751 {
752   GstCaps *othercaps, *target;
753   GstStructure *structure;
754   GstCaps *templ;
755   GstQuery *query;
756   GstBufferPool *pool;
757   GstStructure *config;
758   guint size, min, max;
759
760   templ = gst_pad_get_pad_template_caps (scope->srcpad);
761
762   GST_DEBUG_OBJECT (scope, "performing negotiation");
763
764   /* see what the peer can do */
765   othercaps = gst_pad_peer_query_caps (scope->srcpad, NULL);
766   if (othercaps) {
767     target = gst_caps_intersect (othercaps, templ);
768     gst_caps_unref (othercaps);
769     gst_caps_unref (templ);
770
771     if (gst_caps_is_empty (target))
772       goto no_format;
773
774     target = gst_caps_truncate (target);
775   } else {
776     target = templ;
777   }
778
779   target = gst_caps_make_writable (target);
780   structure = gst_caps_get_structure (target, 0);
781   gst_structure_fixate_field_nearest_int (structure, "width", 320);
782   gst_structure_fixate_field_nearest_int (structure, "height", 200);
783   gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1);
784
785   target = gst_caps_fixate (target);
786
787   GST_DEBUG_OBJECT (scope, "final caps are %" GST_PTR_FORMAT, target);
788
789   gst_audio_visualizer_src_setcaps (scope, target);
790
791   /* try to get a bufferpool now */
792   /* find a pool for the negotiated caps now */
793   query = gst_query_new_allocation (target, TRUE);
794
795   if (!gst_pad_peer_query (scope->srcpad, query)) {
796     /* not a problem, we use the query defaults */
797     GST_DEBUG_OBJECT (scope, "allocation query failed");
798   }
799
800   if (gst_query_get_n_allocation_pools (query) > 0) {
801     /* we got configuration from our peer, parse them */
802     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
803   } else {
804     pool = NULL;
805     size = 0;
806     min = max = 0;
807   }
808
809   if (pool == NULL) {
810     /* we did not get a pool, make one ourselves then */
811     pool = gst_video_buffer_pool_new ();
812   }
813
814   config = gst_buffer_pool_get_config (pool);
815   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
816   gst_buffer_pool_config_set_params (config, target, size, min, max);
817   gst_buffer_pool_set_config (pool, config);
818
819   if (scope->pool) {
820     gst_buffer_pool_set_active (scope->pool, FALSE);
821     gst_object_unref (scope->pool);
822   }
823   scope->pool = pool;
824
825   /* and activate */
826   gst_buffer_pool_set_active (pool, TRUE);
827
828   gst_caps_unref (target);
829
830   return TRUE;
831
832 no_format:
833   {
834     gst_caps_unref (target);
835     return FALSE;
836   }
837 }
838
839 /* make sure we are negotiated */
840 static GstFlowReturn
841 gst_audio_visualizer_ensure_negotiated (GstAudioVisualizer * scope)
842 {
843   gboolean reconfigure;
844
845   reconfigure = gst_pad_check_reconfigure (scope->srcpad);
846
847   /* we don't know an output format yet, pick one */
848   if (reconfigure || !gst_pad_has_current_caps (scope->srcpad)) {
849     if (!gst_audio_visualizer_src_negotiate (scope))
850       return GST_FLOW_NOT_NEGOTIATED;
851   }
852   return GST_FLOW_OK;
853 }
854
855 static GstFlowReturn
856 gst_audio_visualizer_chain (GstPad * pad, GstObject * parent,
857     GstBuffer * buffer)
858 {
859   GstFlowReturn ret = GST_FLOW_OK;
860   GstAudioVisualizer *scope;
861   GstAudioVisualizerClass *klass;
862   GstBuffer *inbuf;
863   guint64 dist, ts;
864   guint avail, sbpf;
865   gpointer adata;
866   gint bps, channels, rate;
867
868   scope = GST_AUDIO_VISUALIZER (parent);
869   klass = GST_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
870
871   GST_LOG_OBJECT (scope, "chainfunc called");
872
873   /* resync on DISCONT */
874   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
875     gst_adapter_clear (scope->adapter);
876   }
877
878   /* Make sure have an output format */
879   ret = gst_audio_visualizer_ensure_negotiated (scope);
880   if (ret != GST_FLOW_OK) {
881     gst_buffer_unref (buffer);
882     goto beach;
883   }
884   channels = GST_AUDIO_INFO_CHANNELS (&scope->ainfo);
885   rate = GST_AUDIO_INFO_RATE (&scope->ainfo);
886   bps = GST_AUDIO_INFO_BPS (&scope->ainfo);
887
888   if (bps == 0) {
889     ret = GST_FLOW_NOT_NEGOTIATED;
890     goto beach;
891   }
892
893   gst_adapter_push (scope->adapter, buffer);
894
895   g_mutex_lock (&scope->config_lock);
896
897   /* this is what we want */
898   sbpf = scope->req_spf * channels * sizeof (gint16);
899
900   inbuf = scope->inbuf;
901   /* FIXME: the timestamp in the adapter would be different */
902   gst_buffer_copy_into (inbuf, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
903
904   /* this is what we have */
905   avail = gst_adapter_available (scope->adapter);
906   GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
907   while (avail >= sbpf) {
908     GstBuffer *outbuf;
909     GstVideoFrame outframe;
910
911     /* get timestamp of the current adapter content */
912     ts = gst_adapter_prev_timestamp (scope->adapter, &dist);
913     if (GST_CLOCK_TIME_IS_VALID (ts)) {
914       /* convert bytes to time */
915       dist /= bps;
916       ts += gst_util_uint64_scale_int (dist, GST_SECOND, rate);
917     }
918
919     if (GST_CLOCK_TIME_IS_VALID (ts)) {
920       gint64 qostime;
921       gboolean need_skip;
922
923       qostime =
924           gst_segment_to_running_time (&scope->segment, GST_FORMAT_TIME, ts) +
925           scope->frame_duration;
926
927       GST_OBJECT_LOCK (scope);
928       /* check for QoS, don't compute buffers that are known to be late */
929       need_skip = scope->earliest_time != -1 && qostime <= scope->earliest_time;
930       GST_OBJECT_UNLOCK (scope);
931
932       if (need_skip) {
933         GST_WARNING_OBJECT (scope,
934             "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
935             GST_TIME_ARGS (qostime), GST_TIME_ARGS (scope->earliest_time));
936         goto skip;
937       }
938     }
939
940     g_mutex_unlock (&scope->config_lock);
941     ret = gst_buffer_pool_acquire_buffer (scope->pool, &outbuf, NULL);
942     g_mutex_lock (&scope->config_lock);
943     /* recheck as the value could have changed */
944     sbpf = scope->req_spf * channels * sizeof (gint16);
945
946     /* no buffer allocated, we don't care why. */
947     if (ret != GST_FLOW_OK)
948       break;
949
950     /* sync controlled properties */
951     gst_object_sync_values (GST_OBJECT (scope), ts);
952
953     GST_BUFFER_TIMESTAMP (outbuf) = ts;
954     GST_BUFFER_DURATION (outbuf) = scope->frame_duration;
955
956     /* this can fail as the data size we need could have changed */
957     if (!(adata = (gpointer) gst_adapter_map (scope->adapter, sbpf)))
958       break;
959
960     gst_video_frame_map (&outframe, &scope->vinfo, outbuf, GST_MAP_READWRITE);
961
962     if (scope->shader) {
963       gst_video_frame_copy (&outframe, &scope->tempframe);
964     } else {
965       /* gst_video_frame_clear() or is output frame already cleared */
966       memset (outframe.data, 0, scope->vinfo.size);
967     }
968
969     gst_buffer_replace_all_memory (inbuf,
970         gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, adata, sbpf, 0,
971             sbpf, NULL, NULL));
972
973     /* call class->render() vmethod */
974     if (klass->render) {
975       if (!klass->render (scope, inbuf, &outframe)) {
976         ret = GST_FLOW_ERROR;
977       } else {
978         /* run various post processing (shading and geometri transformation */
979         if (scope->shader) {
980           scope->shader (scope, &outframe, &scope->tempframe);
981         }
982       }
983     }
984     gst_video_frame_unmap (&outframe);
985
986     g_mutex_unlock (&scope->config_lock);
987     ret = gst_pad_push (scope->srcpad, outbuf);
988     outbuf = NULL;
989     g_mutex_lock (&scope->config_lock);
990
991   skip:
992     /* recheck as the value could have changed */
993     sbpf = scope->req_spf * channels * sizeof (gint16);
994     GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
995     /* we want to take less or more, depending on spf : req_spf */
996     if (avail - sbpf >= sbpf) {
997       gst_adapter_flush (scope->adapter, sbpf);
998       gst_adapter_unmap (scope->adapter);
999     } else if (avail >= sbpf) {
1000       /* just flush a bit and stop */
1001       gst_adapter_flush (scope->adapter, (avail - sbpf));
1002       gst_adapter_unmap (scope->adapter);
1003       break;
1004     }
1005     avail = gst_adapter_available (scope->adapter);
1006
1007     if (ret != GST_FLOW_OK)
1008       break;
1009   }
1010
1011   g_mutex_unlock (&scope->config_lock);
1012
1013 beach:
1014   return ret;
1015 }
1016
1017 static gboolean
1018 gst_audio_visualizer_src_event (GstPad * pad, GstObject * parent,
1019     GstEvent * event)
1020 {
1021   gboolean res;
1022   GstAudioVisualizer *scope;
1023
1024   scope = GST_AUDIO_VISUALIZER (parent);
1025
1026   switch (GST_EVENT_TYPE (event)) {
1027     case GST_EVENT_QOS:
1028     {
1029       gdouble proportion;
1030       GstClockTimeDiff diff;
1031       GstClockTime timestamp;
1032
1033       gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
1034
1035       /* save stuff for the _chain() function */
1036       GST_OBJECT_LOCK (scope);
1037       scope->proportion = proportion;
1038       if (diff >= 0)
1039         /* we're late, this is a good estimate for next displayable
1040          * frame (see part-qos.txt) */
1041         scope->earliest_time = timestamp + 2 * diff + scope->frame_duration;
1042       else
1043         scope->earliest_time = timestamp + diff;
1044       GST_OBJECT_UNLOCK (scope);
1045
1046       res = gst_pad_push_event (scope->sinkpad, event);
1047       break;
1048     }
1049     case GST_EVENT_RECONFIGURE:
1050       /* dont't forward */
1051       gst_event_unref (event);
1052       res = TRUE;
1053       break;
1054     default:
1055       res = gst_pad_push_event (scope->sinkpad, event);
1056       break;
1057   }
1058
1059   return res;
1060 }
1061
1062 static gboolean
1063 gst_audio_visualizer_sink_event (GstPad * pad, GstObject * parent,
1064     GstEvent * event)
1065 {
1066   gboolean res;
1067   GstAudioVisualizer *scope;
1068
1069   scope = GST_AUDIO_VISUALIZER (parent);
1070
1071   switch (GST_EVENT_TYPE (event)) {
1072     case GST_EVENT_CAPS:
1073     {
1074       GstCaps *caps;
1075
1076       gst_event_parse_caps (event, &caps);
1077       res = gst_audio_visualizer_sink_setcaps (scope, caps);
1078       break;
1079     }
1080     case GST_EVENT_FLUSH_START:
1081       res = gst_pad_push_event (scope->srcpad, event);
1082       break;
1083     case GST_EVENT_FLUSH_STOP:
1084       gst_audio_visualizer_reset (scope);
1085       res = gst_pad_push_event (scope->srcpad, event);
1086       break;
1087     case GST_EVENT_SEGMENT:
1088     {
1089       /* the newsegment values are used to clip the input samples
1090        * and to convert the incomming timestamps to running time so
1091        * we can do QoS */
1092       gst_event_copy_segment (event, &scope->segment);
1093
1094       res = gst_pad_push_event (scope->srcpad, event);
1095       break;
1096     }
1097     default:
1098       res = gst_pad_push_event (scope->srcpad, event);
1099       break;
1100   }
1101
1102   return res;
1103 }
1104
1105 static gboolean
1106 gst_audio_visualizer_src_query (GstPad * pad, GstObject * parent,
1107     GstQuery * query)
1108 {
1109   gboolean res = FALSE;
1110   GstAudioVisualizer *scope;
1111
1112   scope = GST_AUDIO_VISUALIZER (parent);
1113
1114   switch (GST_QUERY_TYPE (query)) {
1115     case GST_QUERY_LATENCY:
1116     {
1117       /* We need to send the query upstream and add the returned latency to our
1118        * own */
1119       GstClockTime min_latency, max_latency;
1120       gboolean us_live;
1121       GstClockTime our_latency;
1122       guint max_samples;
1123       gint rate = GST_AUDIO_INFO_RATE (&scope->ainfo);
1124
1125       if (rate == 0)
1126         break;
1127
1128       if ((res = gst_pad_peer_query (scope->sinkpad, query))) {
1129         gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
1130
1131         GST_DEBUG_OBJECT (scope, "Peer latency: min %"
1132             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1133             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
1134
1135         /* the max samples we must buffer buffer */
1136         max_samples = MAX (scope->req_spf, scope->spf);
1137         our_latency = gst_util_uint64_scale_int (max_samples, GST_SECOND, rate);
1138
1139         GST_DEBUG_OBJECT (scope, "Our latency: %" GST_TIME_FORMAT,
1140             GST_TIME_ARGS (our_latency));
1141
1142         /* we add some latency but only if we need to buffer more than what
1143          * upstream gives us */
1144         min_latency += our_latency;
1145         if (max_latency != -1)
1146           max_latency += our_latency;
1147
1148         GST_DEBUG_OBJECT (scope, "Calculated total latency : min %"
1149             GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1150             GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
1151
1152         gst_query_set_latency (query, TRUE, min_latency, max_latency);
1153       }
1154       break;
1155     }
1156     default:
1157       res = gst_pad_query_default (pad, parent, query);
1158       break;
1159   }
1160
1161   return res;
1162 }
1163
1164 static gboolean
1165 gst_audio_visualizer_sink_query (GstPad * pad, GstObject * parent,
1166     GstQuery * query)
1167 {
1168   gboolean res = FALSE;
1169
1170   switch (GST_QUERY_TYPE (query)) {
1171     default:
1172       res = gst_pad_query_default (pad, parent, query);
1173       break;
1174   }
1175   return res;
1176 }
1177
1178 static GstStateChangeReturn
1179 gst_audio_visualizer_change_state (GstElement * element,
1180     GstStateChange transition)
1181 {
1182   GstStateChangeReturn ret;
1183   GstAudioVisualizer *scope;
1184
1185   scope = GST_AUDIO_VISUALIZER (element);
1186
1187   switch (transition) {
1188     case GST_STATE_CHANGE_READY_TO_PAUSED:
1189       gst_audio_visualizer_reset (scope);
1190       break;
1191     default:
1192       break;
1193   }
1194
1195   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1196
1197   switch (transition) {
1198     case GST_STATE_CHANGE_PAUSED_TO_READY:
1199       if (scope->pool) {
1200         gst_buffer_pool_set_active (scope->pool, FALSE);
1201         gst_object_replace ((GstObject **) & scope->pool, NULL);
1202       }
1203       break;
1204     case GST_STATE_CHANGE_READY_TO_NULL:
1205       break;
1206     default:
1207       break;
1208   }
1209
1210   return ret;
1211 }