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