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, ¶ms);
922 } else {
923 allocator = NULL;
924 gst_allocation_params_init (¶ms);
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, ¶ms,
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, ¶ms);
966 update_allocator = TRUE;
967 } else {
968 allocator = NULL;
969 gst_allocation_params_init (¶ms);
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, ¶ms);
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, ¶ms);
996 else
997 gst_query_add_allocation_param (query, allocator, ¶ms);
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, ×tamp);
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