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