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
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
shader_fade_and_move_horiz_in(GstAudioVisualizer * scope,const GstVideoFrame * sframe,GstVideoFrame * dframe)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
shader_fade_and_move_vert_out(GstAudioVisualizer * scope,const GstVideoFrame * sframe,GstVideoFrame * dframe)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
shader_fade_and_move_vert_in(GstAudioVisualizer * scope,const GstVideoFrame * sframe,GstVideoFrame * dframe)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
gst_audio_visualizer_change_shader(GstAudioVisualizer * scope)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
gst_audio_visualizer_get_type(void)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 *
gst_audio_visualizer_get_instance_private(GstAudioVisualizer * self)535 gst_audio_visualizer_get_instance_private (GstAudioVisualizer * self)
536 {
537 return (G_STRUCT_MEMBER_P (self, private_offset));
538 }
539
540 static void
gst_audio_visualizer_class_init(GstAudioVisualizerClass * klass)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
gst_audio_visualizer_init(GstAudioVisualizer * scope,GstAudioVisualizerClass * g_class)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
gst_audio_visualizer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)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
gst_audio_visualizer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)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
gst_audio_visualizer_dispose(GObject * object)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
gst_audio_visualizer_reset(GstAudioVisualizer * scope)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
gst_audio_visualizer_sink_setcaps(GstAudioVisualizer * scope,GstCaps * caps)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
gst_audio_visualizer_src_setcaps(GstAudioVisualizer * scope,GstCaps * caps)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
gst_audio_visualizer_src_negotiate(GstAudioVisualizer * scope)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
gst_audio_visualizer_set_allocation(GstAudioVisualizer * scope,GstBufferPool * pool,GstAllocator * allocator,const GstAllocationParams * params,GstQuery * query)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
gst_audio_visualizer_do_bufferpool(GstAudioVisualizer * scope,GstCaps * outcaps)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, ¶ms);
926 } else {
927 allocator = NULL;
928 gst_allocation_params_init (¶ms);
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, ¶ms,
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
default_decide_allocation(GstAudioVisualizer * scope,GstQuery * query)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, ¶ms);
970 update_allocator = TRUE;
971 } else {
972 allocator = NULL;
973 gst_allocation_params_init (¶ms);
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, ¶ms);
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, ¶ms);
1000 else
1001 gst_query_add_allocation_param (query, allocator, ¶ms);
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
default_prepare_output_buffer(GstAudioVisualizer * scope,GstBuffer ** outbuf)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
gst_audio_visualizer_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)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
gst_audio_visualizer_src_event(GstPad * pad,GstObject * parent,GstEvent * event)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, ×tamp);
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
gst_audio_visualizer_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)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
gst_audio_visualizer_src_query(GstPad * pad,GstObject * parent,GstQuery * query)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
gst_audio_visualizer_change_state(GstElement * element,GstStateChange transition)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 }
1420