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