Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / gst / audiovisualizers / gstbaseaudiovisualizer.c
1 /* GStreamer
2  * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstbaseaudiovisualizer.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:gstbaseaudiovisualizer
22  *
23  * A basclass 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 #include <string.h>
36 #include <gst/controller/gstcontroller.h>
37
38 #include "gstbaseaudiovisualizer.h"
39
40 GST_DEBUG_CATEGORY_STATIC (base_audio_visualizer_debug);
41 #define GST_CAT_DEFAULT (base_audio_visualizer_debug)
42
43 #define DEFAULT_SHADER GST_BASE_AUDIO_VISUALIZER_SHADER_FADE
44 #define DEFAULT_SHADE_AMOUNT   0x000a0a0a
45
46 enum
47 {
48   PROP_0,
49   PROP_SHADER,
50   PROP_SHADE_AMOUNT
51 };
52
53 static GstBaseTransformClass *parent_class = NULL;
54
55 static void gst_base_audio_visualizer_class_init (GstBaseAudioVisualizerClass *
56     klass);
57 static void gst_base_audio_visualizer_init (GstBaseAudioVisualizer * scope,
58     GstBaseAudioVisualizerClass * g_class);
59 static void gst_base_audio_visualizer_set_property (GObject * object,
60     guint prop_id, const GValue * value, GParamSpec * pspec);
61 static void gst_base_audio_visualizer_get_property (GObject * object,
62     guint prop_id, GValue * value, GParamSpec * pspec);
63 static void gst_base_audio_visualizer_dispose (GObject * object);
64
65 static gboolean gst_base_audio_visualizer_src_negotiate (GstBaseAudioVisualizer
66     * scope);
67 static gboolean gst_base_audio_visualizer_src_setcaps (GstPad * pad,
68     GstCaps * caps);
69 static gboolean gst_base_audio_visualizer_sink_setcaps (GstPad * pad,
70     GstCaps * caps);
71
72 static GstFlowReturn gst_base_audio_visualizer_chain (GstPad * pad,
73     GstBuffer * buffer);
74 static GstStateChangeReturn gst_base_audio_visualizer_change_state (GstElement *
75     element, GstStateChange transition);
76
77 /* shading functions */
78
79 #define GST_TYPE_BASE_AUDIO_VISUALIZER_SHADER (gst_base_audio_visualizer_shader_get_type())
80 static GType
81 gst_base_audio_visualizer_shader_get_type (void)
82 {
83   static GType shader_type = 0;
84   static const GEnumValue shaders[] = {
85     {GST_BASE_AUDIO_VISUALIZER_SHADER_NONE, "None", "none"},
86     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE, "Fade", "fade"},
87     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP, "Fade and move up",
88         "fade-and-move-up"},
89     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN, "Fade and move down",
90         "fade-and-move-down"},
91     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT, "Fade and move left",
92         "fade-and-move-left"},
93     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT,
94           "Fade and move right",
95         "fade-and-move-right"},
96     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT,
97         "Fade and move horizontally out", "fade-and-move-horiz-out"},
98     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN,
99         "Fade and move horizontally in", "fade-and-move-horiz-in"},
100     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT,
101         "Fade and move vertically out", "fade-and-move-vert-out"},
102     {GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN,
103         "Fade and move vertically in", "fade-and-move-vert-in"},
104     {0, NULL, NULL},
105   };
106
107   if (G_UNLIKELY (shader_type == 0)) {
108     shader_type =
109         g_enum_register_static ("GstBaseAudioVisualizerShader", shaders);
110   }
111   return shader_type;
112 }
113
114 /* we're only supporting GST_VIDEO_FORMAT_xRGB right now) */
115 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
116
117 #define SHADE1(_d, _s, _i, _r, _g, _b)          \
118 G_STMT_START {                                  \
119     _d[_i] = (_s[_i] > _b) ? _s[_i] - _b : 0;   \
120     _i++;                                       \
121     _d[_i] = (_s[_i] > _g) ? _s[_i] - _g : 0;   \
122     _i++;                                       \
123     _d[_i] = (_s[_i] > _r) ? _s[_i] - _r : 0;   \
124     _i++;                                       \
125     _d[_i++] = 0;                               \
126 } G_STMT_END
127
128 #define SHADE2(_d, _s, _j, _i, _r, _g, _b)      \
129 G_STMT_START {                                  \
130     _d[_j++] = (_s[_i] > _b) ? _s[_i] - _b : 0; \
131     _i++;                                       \
132     _d[_j++] = (_s[_i] > _g) ? _s[_i] - _g : 0; \
133     _i++;                                       \
134     _d[_j++] = (_s[_i] > _r) ? _s[_i] - _r : 0; \
135     _i++;                                       \
136     _d[_j++] = 0;                               \
137     _i++;                                       \
138 } G_STMT_END
139
140 #else
141
142 #define SHADE1(_d, _s, _i, _r, _g, _b)          \
143 G_STMT_START {                                  \
144     _d[_i++] = 0;                               \
145     _d[_i] = (_s[_i] > _r) ? _s[_i] - _r : 0;   \
146     _i++;                                       \
147     _d[_i] = (_s[_i] > _g) ? _s[_i] - _g : 0;   \
148     _i++;                                       \
149     _d[_i] = (_s[_i] > _b) ? _s[_i] - _b : 0;   \
150     _i++;                                       \
151 } G_STMT_END
152
153 #define SHADE2(_d, _s, _j, _i, _r, _g, _b)      \
154 G_STMT_START {                                  \
155     _d[_j++] = 0;                               \
156     _i++;                                       \
157     _d[_j++] = (_s[_i] > _r) ? _s[_i] - _r : 0; \
158     _i++;                                       \
159     _d[_j++] = (_s[_i] > _g) ? _s[_i] - _g : 0; \
160     _i++;                                       \
161     _d[_j++] = (_s[_i] > _b) ? _s[_i] - _b : 0; \
162     _i++;                                       \
163 } G_STMT_END
164
165 #endif
166
167 static void
168 shader_fade (GstBaseAudioVisualizer * scope, const guint8 * s, guint8 * d)
169 {
170   guint i, bpf = scope->bpf;
171   guint r = (scope->shade_amount >> 16) & 0xff;
172   guint g = (scope->shade_amount >> 8) & 0xff;
173   guint b = (scope->shade_amount >> 0) & 0xff;
174
175   for (i = 0; i < bpf;) {
176     SHADE1 (d, s, i, r, g, b);
177   }
178 }
179
180 static void
181 shader_fade_and_move_up (GstBaseAudioVisualizer * scope, const guint8 * s,
182     guint8 * d)
183 {
184   guint i, j, bpf = scope->bpf;
185   guint bpl = 4 * scope->width;
186   guint r = (scope->shade_amount >> 16) & 0xff;
187   guint g = (scope->shade_amount >> 8) & 0xff;
188   guint b = (scope->shade_amount >> 0) & 0xff;
189
190   for (j = 0, i = bpl; i < bpf;) {
191     SHADE2 (d, s, j, i, r, g, b);
192   }
193 }
194
195 static void
196 shader_fade_and_move_down (GstBaseAudioVisualizer * scope, const guint8 * s,
197     guint8 * d)
198 {
199   guint i, j, bpf = scope->bpf;
200   guint bpl = 4 * scope->width;
201   guint r = (scope->shade_amount >> 16) & 0xff;
202   guint g = (scope->shade_amount >> 8) & 0xff;
203   guint b = (scope->shade_amount >> 0) & 0xff;
204
205   for (j = bpl, i = 0; j < bpf;) {
206     SHADE2 (d, s, j, i, r, g, b);
207   }
208 }
209
210 static void
211 shader_fade_and_move_left (GstBaseAudioVisualizer * scope,
212     const guint8 * s, guint8 * d)
213 {
214   guint i, j, k, bpf = scope->bpf;
215   guint w = scope->width;
216   guint r = (scope->shade_amount >> 16) & 0xff;
217   guint g = (scope->shade_amount >> 8) & 0xff;
218   guint b = (scope->shade_amount >> 0) & 0xff;
219
220   /* move to the left */
221   for (j = 0, i = 4; i < bpf;) {
222     for (k = 0; k < w - 1; k++) {
223       SHADE2 (d, s, j, i, r, g, b);
224     }
225     i += 4;
226     j += 4;
227   }
228 }
229
230 static void
231 shader_fade_and_move_right (GstBaseAudioVisualizer * scope,
232     const guint8 * s, guint8 * d)
233 {
234   guint i, j, k, bpf = scope->bpf;
235   guint w = scope->width;
236   guint r = (scope->shade_amount >> 16) & 0xff;
237   guint g = (scope->shade_amount >> 8) & 0xff;
238   guint b = (scope->shade_amount >> 0) & 0xff;
239
240   /* move to the left */
241   for (j = 4, i = 0; i < bpf;) {
242     for (k = 0; k < w - 1; k++) {
243       SHADE2 (d, s, j, i, r, g, b);
244     }
245     i += 4;
246     j += 4;
247   }
248 }
249
250 static void
251 shader_fade_and_move_horiz_out (GstBaseAudioVisualizer * scope,
252     const guint8 * s, guint8 * d)
253 {
254   guint i, j, bpf = scope->bpf / 2;
255   guint bpl = 4 * scope->width;
256   guint r = (scope->shade_amount >> 16) & 0xff;
257   guint g = (scope->shade_amount >> 8) & 0xff;
258   guint b = (scope->shade_amount >> 0) & 0xff;
259
260   /* move upper half up */
261   for (j = 0, i = bpl; i < bpf;) {
262     SHADE2 (d, s, j, i, r, g, b);
263   }
264   /* move lower half down */
265   for (j = bpf + bpl, i = bpf; j < bpf + bpf;) {
266     SHADE2 (d, s, j, i, r, g, b);
267   }
268 }
269
270 static void
271 shader_fade_and_move_horiz_in (GstBaseAudioVisualizer * scope,
272     const guint8 * s, guint8 * d)
273 {
274   guint i, j, bpf = scope->bpf / 2;
275   guint bpl = 4 * scope->width;
276   guint r = (scope->shade_amount >> 16) & 0xff;
277   guint g = (scope->shade_amount >> 8) & 0xff;
278   guint b = (scope->shade_amount >> 0) & 0xff;
279
280   /* move upper half down */
281   for (i = 0, j = bpl; i < bpf;) {
282     SHADE2 (d, s, j, i, r, g, b);
283   }
284   /* move lower half up */
285   for (i = bpf + bpl, j = bpf; i < bpf + bpf;) {
286     SHADE2 (d, s, j, i, r, g, b);
287   }
288 }
289
290 static void
291 shader_fade_and_move_vert_out (GstBaseAudioVisualizer * scope,
292     const guint8 * s, guint8 * d)
293 {
294   guint i, j, k, bpf = scope->bpf;
295   guint m = scope->width / 2;
296   guint r = (scope->shade_amount >> 16) & 0xff;
297   guint g = (scope->shade_amount >> 8) & 0xff;
298   guint b = (scope->shade_amount >> 0) & 0xff;
299
300   /* move left half to the left */
301   for (j = 0, i = 4; i < bpf;) {
302     for (k = 0; k < m; k++) {
303       SHADE2 (d, s, j, i, r, g, b);
304     }
305     j += 4 * m;
306     i += 4 * m;
307   }
308   /* move right half to the right */
309   for (j = 4 * (m + 1), i = 4 * m; j < bpf;) {
310     for (k = 0; k < m; k++) {
311       SHADE2 (d, s, j, i, r, g, b);
312     }
313     j += 4 * m;
314     i += 4 * m;
315   }
316 }
317
318 static void
319 shader_fade_and_move_vert_in (GstBaseAudioVisualizer * scope,
320     const guint8 * s, guint8 * d)
321 {
322   guint i, j, k, bpf = scope->bpf;
323   guint m = scope->width / 2;
324   guint r = (scope->shade_amount >> 16) & 0xff;
325   guint g = (scope->shade_amount >> 8) & 0xff;
326   guint b = (scope->shade_amount >> 0) & 0xff;
327
328   /* move left half to the right */
329   for (j = 4, i = 0; j < bpf;) {
330     for (k = 0; k < m; k++) {
331       SHADE2 (d, s, j, i, r, g, b);
332     }
333     j += 4 * m;
334     i += 4 * m;
335   }
336   /* move right half to the left */
337   for (j = 4 * m, i = 4 * (m + 1); i < bpf;) {
338     for (k = 0; k < m; k++) {
339       SHADE2 (d, s, j, i, r, g, b);
340     }
341     j += 4 * m;
342     i += 4 * m;
343   }
344 }
345
346 static void
347 gst_base_audio_visualizer_change_shader (GstBaseAudioVisualizer * scope)
348 {
349   switch (scope->shader_type) {
350     case GST_BASE_AUDIO_VISUALIZER_SHADER_NONE:
351       scope->shader = NULL;
352       break;
353     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE:
354       scope->shader = shader_fade;
355       break;
356     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP:
357       scope->shader = shader_fade_and_move_up;
358       break;
359     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN:
360       scope->shader = shader_fade_and_move_down;
361       break;
362     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT:
363       scope->shader = shader_fade_and_move_left;
364       break;
365     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT:
366       scope->shader = shader_fade_and_move_right;
367       break;
368     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT:
369       scope->shader = shader_fade_and_move_horiz_out;
370       break;
371     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN:
372       scope->shader = shader_fade_and_move_horiz_in;
373       break;
374     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT:
375       scope->shader = shader_fade_and_move_vert_out;
376       break;
377     case GST_BASE_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN:
378       scope->shader = shader_fade_and_move_vert_in;
379       break;
380     default:
381       GST_ERROR ("invalid shader function");
382       scope->shader = NULL;
383       break;
384   }
385 }
386
387 /* base class */
388
389 GType
390 gst_base_audio_visualizer_get_type (void)
391 {
392   static volatile gsize base_audio_visualizer_type = 0;
393
394   if (g_once_init_enter (&base_audio_visualizer_type)) {
395     static const GTypeInfo base_audio_visualizer_info = {
396       sizeof (GstBaseAudioVisualizerClass),
397       NULL,
398       NULL,
399       (GClassInitFunc) gst_base_audio_visualizer_class_init,
400       NULL,
401       NULL,
402       sizeof (GstBaseAudioVisualizer),
403       0,
404       (GInstanceInitFunc) gst_base_audio_visualizer_init,
405     };
406     GType _type;
407
408     _type = g_type_register_static (GST_TYPE_ELEMENT,
409         "GstBaseAudioVisualizer", &base_audio_visualizer_info,
410         G_TYPE_FLAG_ABSTRACT);
411     g_once_init_leave (&base_audio_visualizer_type, _type);
412   }
413   return (GType) base_audio_visualizer_type;
414 }
415
416 static void
417 gst_base_audio_visualizer_class_init (GstBaseAudioVisualizerClass * klass)
418 {
419   GObjectClass *gobject_class = (GObjectClass *) klass;
420   GstElementClass *element_class = (GstElementClass *) klass;
421
422   parent_class = g_type_class_peek_parent (klass);
423
424   GST_DEBUG_CATEGORY_INIT (base_audio_visualizer_debug, "baseaudiovisualizer",
425       0, "scope audio visualisation base class");
426
427   gobject_class->set_property = gst_base_audio_visualizer_set_property;
428   gobject_class->get_property = gst_base_audio_visualizer_get_property;
429   gobject_class->dispose = gst_base_audio_visualizer_dispose;
430
431   element_class->change_state =
432       GST_DEBUG_FUNCPTR (gst_base_audio_visualizer_change_state);
433
434   g_object_class_install_property (gobject_class, PROP_SHADER,
435       g_param_spec_enum ("shader", "shader type",
436           "Shader function to apply on each frame",
437           GST_TYPE_BASE_AUDIO_VISUALIZER_SHADER, DEFAULT_SHADER,
438           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
439   g_object_class_install_property (gobject_class, PROP_SHADE_AMOUNT,
440       g_param_spec_uint ("shade-amount", "shade amount",
441           "Shading color to use (big-endian ARGB)", 0, G_MAXUINT32,
442           DEFAULT_SHADE_AMOUNT,
443           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
444 }
445
446 static void
447 gst_base_audio_visualizer_init (GstBaseAudioVisualizer * scope,
448     GstBaseAudioVisualizerClass * g_class)
449 {
450   GstPadTemplate *pad_template;
451
452   /* create the sink and src pads */
453   pad_template =
454       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
455   g_return_if_fail (pad_template != NULL);
456   scope->sinkpad = gst_pad_new_from_template (pad_template, "sink");
457   gst_pad_set_chain_function (scope->sinkpad,
458       GST_DEBUG_FUNCPTR (gst_base_audio_visualizer_chain));
459   gst_pad_set_setcaps_function (scope->sinkpad,
460       GST_DEBUG_FUNCPTR (gst_base_audio_visualizer_sink_setcaps));
461   gst_element_add_pad (GST_ELEMENT (scope), scope->sinkpad);
462
463   pad_template =
464       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
465   g_return_if_fail (pad_template != NULL);
466   scope->srcpad = gst_pad_new_from_template (pad_template, "src");
467   gst_pad_set_setcaps_function (scope->srcpad,
468       GST_DEBUG_FUNCPTR (gst_base_audio_visualizer_src_setcaps));
469   gst_element_add_pad (GST_ELEMENT (scope), scope->srcpad);
470
471   scope->adapter = gst_adapter_new ();
472   scope->inbuf = gst_buffer_new ();
473
474   /* properties */
475   scope->shader_type = DEFAULT_SHADER;
476   gst_base_audio_visualizer_change_shader (scope);
477   scope->shade_amount = DEFAULT_SHADE_AMOUNT;
478
479   /* reset the initial video state */
480   scope->width = 320;
481   scope->height = 200;
482   scope->fps_n = 25;            /* desired frame rate */
483   scope->fps_d = 1;
484   scope->frame_duration = GST_CLOCK_TIME_NONE;
485
486   /* reset the initial audio state */
487   scope->rate = GST_AUDIO_DEF_RATE;
488   scope->channels = 2;
489
490   scope->next_ts = GST_CLOCK_TIME_NONE;
491
492   scope->config_lock = g_mutex_new ();
493 }
494
495 static void
496 gst_base_audio_visualizer_set_property (GObject * object, guint prop_id,
497     const GValue * value, GParamSpec * pspec)
498 {
499   GstBaseAudioVisualizer *scope = GST_BASE_AUDIO_VISUALIZER (object);
500
501   switch (prop_id) {
502     case PROP_SHADER:
503       scope->shader_type = g_value_get_enum (value);
504       gst_base_audio_visualizer_change_shader (scope);
505       break;
506     case PROP_SHADE_AMOUNT:
507       scope->shade_amount = g_value_get_uint (value);
508       break;
509     default:
510       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
511       break;
512   }
513 }
514
515 static void
516 gst_base_audio_visualizer_get_property (GObject * object, guint prop_id,
517     GValue * value, GParamSpec * pspec)
518 {
519   GstBaseAudioVisualizer *scope = GST_BASE_AUDIO_VISUALIZER (object);
520
521   switch (prop_id) {
522     case PROP_SHADER:
523       g_value_set_enum (value, scope->shader_type);
524       break;
525     case PROP_SHADE_AMOUNT:
526       g_value_set_uint (value, scope->shade_amount);
527       break;
528     default:
529       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
530       break;
531   }
532 }
533
534 static void
535 gst_base_audio_visualizer_dispose (GObject * object)
536 {
537   GstBaseAudioVisualizer *scope = GST_BASE_AUDIO_VISUALIZER (object);
538
539   if (scope->adapter) {
540     g_object_unref (scope->adapter);
541     scope->adapter = NULL;
542   }
543   if (scope->inbuf) {
544     gst_buffer_unref (scope->inbuf);
545     scope->inbuf = NULL;
546   }
547   if (scope->pixelbuf) {
548     g_free (scope->pixelbuf);
549     scope->pixelbuf = NULL;
550   }
551   if (scope->config_lock) {
552     g_mutex_free (scope->config_lock);
553     scope->config_lock = NULL;
554   }
555   G_OBJECT_CLASS (parent_class)->dispose (object);
556 }
557
558 static gboolean
559 gst_base_audio_visualizer_sink_setcaps (GstPad * pad, GstCaps * caps)
560 {
561   GstBaseAudioVisualizer *scope;
562   GstStructure *structure;
563   gint channels;
564   gint rate;
565   gboolean res = TRUE;
566
567   scope = GST_BASE_AUDIO_VISUALIZER (gst_pad_get_parent (pad));
568   structure = gst_caps_get_structure (caps, 0);
569
570   if (!gst_structure_get_int (structure, "channels", &channels) ||
571       !gst_structure_get_int (structure, "rate", &rate))
572     goto missing_caps_details;
573
574   if (channels != 2)
575     goto wrong_channels;
576
577   if (rate <= 0)
578     goto wrong_rate;
579
580   scope->channels = channels;
581   scope->rate = rate;
582
583   GST_DEBUG_OBJECT (scope, "audio: channels %d, rate %d",
584       scope->channels, scope->rate);
585
586 done:
587   gst_object_unref (scope);
588   return res;
589
590   /* Errors */
591 missing_caps_details:
592   {
593     GST_WARNING_OBJECT (scope, "missing channels or rate in the caps");
594     res = FALSE;
595     goto done;
596   }
597 wrong_channels:
598   {
599     GST_WARNING_OBJECT (scope, "number of channels must be 2, but is %d",
600         channels);
601     res = FALSE;
602     goto done;
603   }
604 wrong_rate:
605   {
606     GST_WARNING_OBJECT (scope, "sample rate must be >0, but is %d", rate);
607     res = FALSE;
608     goto done;
609   }
610 }
611
612 static gboolean
613 gst_base_audio_visualizer_src_negotiate (GstBaseAudioVisualizer * scope)
614 {
615   GstCaps *othercaps, *target, *intersect;
616   GstStructure *structure;
617   const GstCaps *templ;
618
619   templ = gst_pad_get_pad_template_caps (scope->srcpad);
620
621   GST_DEBUG_OBJECT (scope, "performing negotiation");
622
623   /* see what the peer can do */
624   othercaps = gst_pad_peer_get_caps (scope->srcpad);
625   if (othercaps) {
626     intersect = gst_caps_intersect (othercaps, templ);
627     gst_caps_unref (othercaps);
628
629     if (gst_caps_is_empty (intersect))
630       goto no_format;
631
632     target = gst_caps_copy_nth (intersect, 0);
633     gst_caps_unref (intersect);
634   } else {
635     target = gst_caps_ref ((GstCaps *) templ);
636   }
637
638   structure = gst_caps_get_structure (target, 0);
639   gst_structure_fixate_field_nearest_int (structure, "width", scope->width);
640   gst_structure_fixate_field_nearest_int (structure, "height", scope->height);
641   gst_structure_fixate_field_nearest_fraction (structure, "framerate",
642       scope->fps_n, scope->fps_d);
643
644   GST_DEBUG_OBJECT (scope, "final caps are %" GST_PTR_FORMAT, target);
645
646   gst_pad_set_caps (scope->srcpad, target);
647   gst_caps_unref (target);
648
649   return TRUE;
650
651 no_format:
652   {
653     gst_caps_unref (intersect);
654     return FALSE;
655   }
656 }
657
658 static gboolean
659 gst_base_audio_visualizer_src_setcaps (GstPad * pad, GstCaps * caps)
660 {
661   GstBaseAudioVisualizer *scope;
662   GstBaseAudioVisualizerClass *klass;
663   gint w, h;
664   gint num, denom;
665   GstVideoFormat format;
666   gboolean res = TRUE;
667
668   scope = GST_BASE_AUDIO_VISUALIZER (gst_pad_get_parent (pad));
669   klass = GST_BASE_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
670
671   if (!gst_video_format_parse_caps (caps, &format, &w, &h)) {
672     goto missing_caps_details;
673   }
674   if (!gst_video_parse_caps_framerate (caps, &num, &denom)) {
675     goto missing_caps_details;
676   }
677
678   g_mutex_lock (scope->config_lock);
679
680   scope->width = w;
681   scope->height = h;
682   scope->fps_n = num;
683   scope->fps_d = denom;
684   scope->video_format = format;
685
686   scope->frame_duration = gst_util_uint64_scale_int (GST_SECOND,
687       scope->fps_d, scope->fps_n);
688   scope->spf = gst_util_uint64_scale_int (scope->rate,
689       scope->fps_d, scope->fps_n);
690   scope->req_spf = scope->spf;
691
692   scope->bpf = w * h * 4;
693
694   if (scope->pixelbuf)
695     g_free (scope->pixelbuf);
696   scope->pixelbuf = g_malloc0 (scope->bpf);
697
698   if (klass->setup)
699     res = klass->setup (scope);
700
701   GST_DEBUG_OBJECT (scope, "video: dimension %dx%d, framerate %d/%d",
702       scope->width, scope->height, scope->fps_n, scope->fps_d);
703   GST_DEBUG_OBJECT (scope, "blocks: spf %u, req_spf %u",
704       scope->spf, scope->req_spf);
705
706   g_mutex_unlock (scope->config_lock);
707
708 done:
709   gst_object_unref (scope);
710   return res;
711
712   /* Errors */
713 missing_caps_details:
714   {
715     GST_WARNING_OBJECT (scope,
716         "missing width, height or framerate in the caps");
717     res = FALSE;
718     goto done;
719   }
720 }
721
722 static GstFlowReturn
723 gst_base_audio_visualizer_chain (GstPad * pad, GstBuffer * buffer)
724 {
725   GstFlowReturn ret = GST_FLOW_OK;
726   GstBaseAudioVisualizer *scope;
727   GstBaseAudioVisualizerClass *klass;
728   GstBuffer *inbuf;
729   guint avail, sbpf;
730   guint8 *adata;
731   gboolean (*render) (GstBaseAudioVisualizer * scope, GstBuffer * audio,
732       GstBuffer * video);
733
734   scope = GST_BASE_AUDIO_VISUALIZER (gst_pad_get_parent (pad));
735   klass = GST_BASE_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
736
737   render = klass->render;
738
739   GST_LOG_OBJECT (scope, "chainfunc called");
740
741   /* resync on DISCONT */
742   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
743     scope->next_ts = GST_CLOCK_TIME_NONE;
744     gst_adapter_clear (scope->adapter);
745   }
746
747   if (GST_PAD_CAPS (scope->srcpad) == NULL) {
748     if (!gst_base_audio_visualizer_src_negotiate (scope))
749       return GST_FLOW_NOT_NEGOTIATED;
750   }
751
752   /* Match timestamps from the incoming audio */
753   if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
754     scope->next_ts = GST_BUFFER_TIMESTAMP (buffer);
755
756   gst_adapter_push (scope->adapter, buffer);
757
758   g_mutex_lock (scope->config_lock);
759
760   /* this is what we want */
761   sbpf = scope->req_spf * scope->channels * sizeof (gint16);
762
763   inbuf = scope->inbuf;
764   /* FIXME: the timestamp in the adapter would be different */
765   gst_buffer_copy_metadata (inbuf, buffer, GST_BUFFER_COPY_ALL);
766
767   /* this is what we have */
768   avail = gst_adapter_available (scope->adapter);
769   GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
770   while (avail >= sbpf) {
771     GstBuffer *outbuf;
772
773     g_mutex_unlock (scope->config_lock);
774     ret = gst_pad_alloc_buffer_and_set_caps (scope->srcpad,
775         GST_BUFFER_OFFSET_NONE,
776         scope->bpf, GST_PAD_CAPS (scope->srcpad), &outbuf);
777     g_mutex_lock (scope->config_lock);
778     /* recheck as the value could have changed */
779     sbpf = scope->req_spf * scope->channels * sizeof (gint16);
780
781     /* no buffer allocated, we don't care why. */
782     if (ret != GST_FLOW_OK)
783       break;
784
785     /* sync controlled properties */
786     gst_object_sync_values (G_OBJECT (scope), scope->next_ts);
787
788     GST_BUFFER_TIMESTAMP (outbuf) = scope->next_ts;
789     GST_BUFFER_DURATION (outbuf) = scope->frame_duration;
790     if (scope->shader) {
791       memcpy (GST_BUFFER_DATA (outbuf), scope->pixelbuf, scope->bpf);
792     } else {
793       memset (GST_BUFFER_DATA (outbuf), 0, scope->bpf);
794     }
795
796     /* this can fail as the data size we need could have changed */
797     if (!(adata = (guint8 *) gst_adapter_peek (scope->adapter, sbpf)))
798       break;
799
800     GST_BUFFER_DATA (inbuf) = adata;
801     GST_BUFFER_SIZE (inbuf) = sbpf;
802
803     /* call class->render() vmethod */
804     if (render) {
805       if (!render (scope, inbuf, outbuf)) {
806         ret = GST_FLOW_ERROR;
807       } else {
808         /* run various post processing (shading and geometri transformation */
809         if (scope->shader) {
810           scope->shader (scope, GST_BUFFER_DATA (outbuf), scope->pixelbuf);
811         }
812       }
813     }
814
815     g_mutex_unlock (scope->config_lock);
816     ret = gst_pad_push (scope->srcpad, outbuf);
817     outbuf = NULL;
818     g_mutex_lock (scope->config_lock);
819
820     /* recheck as the value could have changed */
821     sbpf = scope->req_spf * scope->channels * sizeof (gint16);
822     GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
823     /* we want to take less or more, depending on spf : req_spf */
824     if (avail - sbpf >= sbpf) {
825       gst_adapter_flush (scope->adapter, sbpf);
826     } else if (avail - sbpf >= 0) {
827       /* just flush a bit and stop */
828       gst_adapter_flush (scope->adapter, (avail - sbpf));
829       break;
830     }
831     avail = gst_adapter_available (scope->adapter);
832
833     if (ret != GST_FLOW_OK)
834       break;
835
836     if (scope->next_ts != GST_CLOCK_TIME_NONE)
837       scope->next_ts += scope->frame_duration;
838   }
839
840   g_mutex_unlock (scope->config_lock);
841
842   gst_object_unref (scope);
843
844   return ret;
845 }
846
847 static GstStateChangeReturn
848 gst_base_audio_visualizer_change_state (GstElement * element,
849     GstStateChange transition)
850 {
851   GstBaseAudioVisualizer *scope;
852
853   scope = GST_BASE_AUDIO_VISUALIZER (element);
854
855   switch (transition) {
856     case GST_STATE_CHANGE_READY_TO_PAUSED:
857       scope->next_ts = GST_CLOCK_TIME_NONE;
858       gst_adapter_clear (scope->adapter);
859       break;
860     default:
861       break;
862   }
863
864   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
865 }