Coverage Report

Created: 2026-07-12 07:12

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