blob: 9c4a17745477b135c96a9c417d04507f9ada4ae8 [file] [log] [blame]
Olivier Naudanb28313f2012-04-16 08:10:18 -04001/* GStreamer
2 * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
Sebastian Dröge78ac2892015-06-07 10:59:27 +02003 * Copyright (C) <2015> Luis de Bethencourt <luis@debethencourt.com>
Olivier Naudanb28313f2012-04-16 08:10:18 -04004 *
Sebastian Dröge63e92012012-08-09 11:32:59 +02005 * gstaudiovisualizer.h: base class for audio visualisation elements
Olivier Naudanb28313f2012-04-16 08:10:18 -04006 *
Sebastian Dröge441328f2015-05-13 15:47:04 +03007 * 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.
Olivier Naudanb28313f2012-04-16 08:10:18 -040011 *
Sebastian Dröge441328f2015-05-13 15:47:04 +030012 * This library is distributed in the hope that it will be useful,
Olivier Naudanb28313f2012-04-16 08:10:18 -040013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Sebastian Dröge441328f2015-05-13 15:47:04 +030014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
Olivier Naudanb28313f2012-04-16 08:10:18 -040016 *
Sebastian Dröge441328f2015-05-13 15:47:04 +030017 * 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.
Olivier Naudanb28313f2012-04-16 08:10:18 -040021 */
22/**
Sebastian Dröge63e92012012-08-09 11:32:59 +020023 * SECTION:gstaudiovisualizer
Olivier Naudanb28313f2012-04-16 08:10:18 -040024 *
Sebastian Dröge63e92012012-08-09 11:32:59 +020025 * A baseclass for scopes (visualizers). It takes care of re-fitting the
Olivier Naudanb28313f2012-04-16 08:10:18 -040026 * audio-rate to video-rate and handles renegotiation (downstream video size
27 * changes).
28 *
29 * It also provides several background shading effects. These effects are
30 * applied to a previous picture before the render() implementation can draw a
31 * new frame.
32 */
33
34#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
Olivier Naudanb28313f2012-04-16 08:10:18 -040038#include <string.h>
39
Sebastian Dröge5a155a02012-09-14 10:49:31 +020040#include <gst/video/video.h>
41#include <gst/video/gstvideometa.h>
42#include <gst/video/gstvideopool.h>
43
Sebastian Dröge63e92012012-08-09 11:32:59 +020044#include "gstaudiovisualizer.h"
Olivier Naudanb28313f2012-04-16 08:10:18 -040045
Sebastian Dröge63e92012012-08-09 11:32:59 +020046GST_DEBUG_CATEGORY_STATIC (audio_visualizer_debug);
47#define GST_CAT_DEFAULT (audio_visualizer_debug)
Olivier Naudanb28313f2012-04-16 08:10:18 -040048
Sebastian Dröge63e92012012-08-09 11:32:59 +020049#define DEFAULT_SHADER GST_AUDIO_VISUALIZER_SHADER_FADE
Olivier Naudanb28313f2012-04-16 08:10:18 -040050#define DEFAULT_SHADE_AMOUNT 0x000a0a0a
51
52enum
53{
54 PROP_0,
55 PROP_SHADER,
56 PROP_SHADE_AMOUNT
57};
58
59static GstBaseTransformClass *parent_class = NULL;
60
Sebastian Dröge63e92012012-08-09 11:32:59 +020061static void gst_audio_visualizer_class_init (GstAudioVisualizerClass * klass);
62static void gst_audio_visualizer_init (GstAudioVisualizer * scope,
63 GstAudioVisualizerClass * g_class);
64static void gst_audio_visualizer_set_property (GObject * object,
Olivier Naudanb28313f2012-04-16 08:10:18 -040065 guint prop_id, const GValue * value, GParamSpec * pspec);
Sebastian Dröge63e92012012-08-09 11:32:59 +020066static void gst_audio_visualizer_get_property (GObject * object,
Olivier Naudanb28313f2012-04-16 08:10:18 -040067 guint prop_id, GValue * value, GParamSpec * pspec);
Sebastian Dröge4139fce2015-03-17 09:38:41 +010068static void gst_audio_visualizer_finalize (GObject * object);
Olivier Naudanb28313f2012-04-16 08:10:18 -040069
Sebastian Dröge63e92012012-08-09 11:32:59 +020070static gboolean gst_audio_visualizer_src_negotiate (GstAudioVisualizer * scope);
71static gboolean gst_audio_visualizer_src_setcaps (GstAudioVisualizer *
Olivier Naudanb28313f2012-04-16 08:10:18 -040072 scope, GstCaps * caps);
Sebastian Dröge63e92012012-08-09 11:32:59 +020073static gboolean gst_audio_visualizer_sink_setcaps (GstAudioVisualizer *
Olivier Naudanb28313f2012-04-16 08:10:18 -040074 scope, GstCaps * caps);
75
Sebastian Dröge63e92012012-08-09 11:32:59 +020076static GstFlowReturn gst_audio_visualizer_chain (GstPad * pad,
Olivier Naudanb28313f2012-04-16 08:10:18 -040077 GstObject * parent, GstBuffer * buffer);
78
Sebastian Dröge63e92012012-08-09 11:32:59 +020079static gboolean gst_audio_visualizer_src_event (GstPad * pad,
Olivier Naudanb28313f2012-04-16 08:10:18 -040080 GstObject * parent, GstEvent * event);
Sebastian Dröge63e92012012-08-09 11:32:59 +020081static gboolean gst_audio_visualizer_sink_event (GstPad * pad,
Olivier Naudanb28313f2012-04-16 08:10:18 -040082 GstObject * parent, GstEvent * event);
83
Sebastian Dröge63e92012012-08-09 11:32:59 +020084static gboolean gst_audio_visualizer_src_query (GstPad * pad,
Olivier Naudanb28313f2012-04-16 08:10:18 -040085 GstObject * parent, GstQuery * query);
Olivier Naudanb28313f2012-04-16 08:10:18 -040086
Sebastian Dröge63e92012012-08-09 11:32:59 +020087static GstStateChangeReturn gst_audio_visualizer_change_state (GstElement *
Olivier Naudanb28313f2012-04-16 08:10:18 -040088 element, GstStateChange transition);
89
Sebastian Drögee6513c12013-07-14 12:12:42 +020090static gboolean gst_audio_visualizer_do_bufferpool (GstAudioVisualizer * scope,
91 GstCaps * outcaps);
92
93static gboolean
94default_decide_allocation (GstAudioVisualizer * scope, GstQuery * query);
95
Sebastian Dröge78ac2892015-06-07 10:59:27 +020096#define GST_AUDIO_VISUALIZER_GET_PRIVATE(obj) \
97 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_AUDIO_VISUALIZER, GstAudioVisualizerPrivate))
98
99struct _GstAudioVisualizerPrivate
100{
101 gboolean negotiated;
102
103 GstBufferPool *pool;
104 gboolean pool_active;
105 GstAllocator *allocator;
106 GstAllocationParams params;
107 GstQuery *query;
108
109 /* pads */
110 GstPad *srcpad, *sinkpad;
111
112 GstAudioVisualizerShader shader_type;
113 GstAudioVisualizerShaderFunc shader;
114 guint32 shade_amount;
115
116 GstAdapter *adapter;
117
118 GstBuffer *inbuf;
119 GstBuffer *tempbuf;
120 GstVideoFrame tempframe;
121
122 guint spf; /* samples per video frame */
123 guint64 frame_duration;
124
125 /* QoS stuff *//* with LOCK */
126 gdouble proportion;
127 GstClockTime earliest_time;
128
129 guint dropped; /* frames dropped / not dropped */
130 guint processed;
131
132 /* configuration mutex */
133 GMutex config_lock;
134
135 GstSegment segment;
136};
137
138
Olivier Naudanb28313f2012-04-16 08:10:18 -0400139/* shading functions */
140
Sebastian Dröge63e92012012-08-09 11:32:59 +0200141#define GST_TYPE_AUDIO_VISUALIZER_SHADER (gst_audio_visualizer_shader_get_type())
Olivier Naudanb28313f2012-04-16 08:10:18 -0400142static GType
Sebastian Dröge63e92012012-08-09 11:32:59 +0200143gst_audio_visualizer_shader_get_type (void)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400144{
145 static GType shader_type = 0;
146 static const GEnumValue shaders[] = {
Sebastian Dröge63e92012012-08-09 11:32:59 +0200147 {GST_AUDIO_VISUALIZER_SHADER_NONE, "None", "none"},
148 {GST_AUDIO_VISUALIZER_SHADER_FADE, "Fade", "fade"},
149 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP, "Fade and move up",
Olivier Naudanb28313f2012-04-16 08:10:18 -0400150 "fade-and-move-up"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200151 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN, "Fade and move down",
Olivier Naudanb28313f2012-04-16 08:10:18 -0400152 "fade-and-move-down"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200153 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT, "Fade and move left",
Olivier Naudanb28313f2012-04-16 08:10:18 -0400154 "fade-and-move-left"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200155 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400156 "Fade and move right",
157 "fade-and-move-right"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200158 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400159 "Fade and move horizontally out", "fade-and-move-horiz-out"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200160 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400161 "Fade and move horizontally in", "fade-and-move-horiz-in"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200162 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400163 "Fade and move vertically out", "fade-and-move-vert-out"},
Sebastian Dröge63e92012012-08-09 11:32:59 +0200164 {GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400165 "Fade and move vertically in", "fade-and-move-vert-in"},
166 {0, NULL, NULL},
167 };
168
169 if (G_UNLIKELY (shader_type == 0)) {
Sebastian Dröge63e92012012-08-09 11:32:59 +0200170 /* TODO: rename when exporting it as a library */
Olivier Naudanb28313f2012-04-16 08:10:18 -0400171 shader_type =
Sebastian Dröge63e92012012-08-09 11:32:59 +0200172 g_enum_register_static
173 ("GstAudioVisualizerShader-BadGstAudioVisualizers", shaders);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400174 }
175 return shader_type;
176}
177
178/* we're only supporting GST_VIDEO_FORMAT_xRGB right now) */
179#if G_BYTE_ORDER == G_LITTLE_ENDIAN
180
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200181#define SHADE(_d, _s, _i, _r, _g, _b) \
182G_STMT_START { \
183 _d[_i * 4 + 0] = (_s[_i * 4 + 0] > _b) ? _s[_i * 4 + 0] - _b : 0; \
184 _d[_i * 4 + 1] = (_s[_i * 4 + 1] > _g) ? _s[_i * 4 + 1] - _g : 0; \
185 _d[_i * 4 + 2] = (_s[_i * 4 + 2] > _r) ? _s[_i * 4 + 2] - _r : 0; \
186 _d[_i * 4 + 3] = 0; \
Olivier Naudanb28313f2012-04-16 08:10:18 -0400187} G_STMT_END
188
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200189#else /* G_BYTE_ORDER == G_LITTLE_ENDIAN */
Olivier Naudanb28313f2012-04-16 08:10:18 -0400190
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200191#define SHADE(_d, _s, _i, _r, _g, _b) \
192G_STMT_START { \
193 _d[_i * 4 + 0] = 0; \
194 _d[_i * 4 + 1] = (_s[_i * 4 + 1] > _r) ? _s[_i * 4 + 1] - _r : 0; \
195 _d[_i * 4 + 2] = (_s[_i * 4 + 2] > _g) ? _s[_i * 4 + 2] - _g : 0; \
196 _d[_i * 4 + 3] = (_s[_i * 4 + 3] > _b) ? _s[_i * 4 + 3] - _b : 0; \
Olivier Naudanb28313f2012-04-16 08:10:18 -0400197} G_STMT_END
198
199#endif
200
201static void
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200202shader_fade (GstAudioVisualizer * scope, const GstVideoFrame * sframe,
203 GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400204{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200205 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200206 guint32 shade_amount = scope->priv->shade_amount;
207 guint r = (shade_amount >> 16) & 0xff;
208 guint g = (shade_amount >> 8) & 0xff;
209 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200210 guint8 *s, *d;
211 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400212
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200213 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
214 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
215 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
216 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
217
218 width = GST_VIDEO_FRAME_WIDTH (sframe);
219 height = GST_VIDEO_FRAME_HEIGHT (sframe);
220
221 for (j = 0; j < height; j++) {
222 for (i = 0; i < width; i++) {
223 SHADE (d, s, i, r, g, b);
224 }
225 s += ss;
226 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400227 }
228}
229
230static void
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200231shader_fade_and_move_up (GstAudioVisualizer * scope,
232 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400233{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200234 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200235 guint32 shade_amount = scope->priv->shade_amount;
236 guint r = (shade_amount >> 16) & 0xff;
237 guint g = (shade_amount >> 8) & 0xff;
238 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200239 guint8 *s, *d;
240 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400241
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200242 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
243 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
244 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
245 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
246
247 width = GST_VIDEO_FRAME_WIDTH (sframe);
248 height = GST_VIDEO_FRAME_HEIGHT (sframe);
249
250 for (j = 1; j < height; j++) {
251 s += ss;
252 for (i = 0; i < width; i++) {
253 SHADE (d, s, i, r, g, b);
254 }
255 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400256 }
257}
258
259static void
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200260shader_fade_and_move_down (GstAudioVisualizer * scope,
261 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400262{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200263 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200264 guint32 shade_amount = scope->priv->shade_amount;
265 guint r = (shade_amount >> 16) & 0xff;
266 guint g = (shade_amount >> 8) & 0xff;
267 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200268 guint8 *s, *d;
269 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400270
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200271 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
272 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
273 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
274 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
275
276 width = GST_VIDEO_FRAME_WIDTH (sframe);
277 height = GST_VIDEO_FRAME_HEIGHT (sframe);
278
279 for (j = 1; j < height; j++) {
280 d += ds;
281 for (i = 0; i < width; i++) {
282 SHADE (d, s, i, r, g, b);
283 }
284 s += ss;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400285 }
286}
287
288static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200289shader_fade_and_move_left (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200290 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400291{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200292 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200293 guint32 shade_amount = scope->priv->shade_amount;
294 guint r = (shade_amount >> 16) & 0xff;
295 guint g = (shade_amount >> 8) & 0xff;
296 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200297 guint8 *s, *d;
298 gint ss, ds, width, height;
299
300 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
301 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
302 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
303 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
304
305 width = GST_VIDEO_FRAME_WIDTH (sframe);
306 height = GST_VIDEO_FRAME_HEIGHT (sframe);
307
308 width -= 1;
309 s += 4;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400310
311 /* move to the left */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200312 for (j = 0; j < height; j++) {
313 for (i = 0; i < width; i++) {
314 SHADE (d, s, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400315 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200316 d += ds;
317 s += ss;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400318 }
319}
320
321static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200322shader_fade_and_move_right (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200323 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400324{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200325 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200326 guint32 shade_amount = scope->priv->shade_amount;
327 guint r = (shade_amount >> 16) & 0xff;
328 guint g = (shade_amount >> 8) & 0xff;
329 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200330 guint8 *s, *d;
331 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400332
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200333 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
334 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
335 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
336 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
337
338 width = GST_VIDEO_FRAME_WIDTH (sframe);
339 height = GST_VIDEO_FRAME_HEIGHT (sframe);
340
341 width -= 1;
342 d += 4;
343
344 /* move to the right */
345 for (j = 0; j < height; j++) {
346 for (i = 0; i < width; i++) {
347 SHADE (d, s, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400348 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200349 d += ds;
350 s += ss;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400351 }
352}
353
354static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200355shader_fade_and_move_horiz_out (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200356 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400357{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200358 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200359 guint32 shade_amount = scope->priv->shade_amount;
360 guint r = (shade_amount >> 16) & 0xff;
361 guint g = (shade_amount >> 8) & 0xff;
362 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200363 guint8 *s, *d;
364 gint ss, ds, width, height;
365
366 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
367 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
368 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
369 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
370
371 width = GST_VIDEO_FRAME_WIDTH (sframe);
372 height = GST_VIDEO_FRAME_HEIGHT (sframe);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400373
374 /* move upper half up */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200375 for (j = 0; j < height / 2; j++) {
376 s += ss;
377 for (i = 0; i < width; i++) {
378 SHADE (d, s, i, r, g, b);
379 }
380 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400381 }
382 /* move lower half down */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200383 for (j = 0; j < height / 2; j++) {
384 d += ds;
385 for (i = 0; i < width; i++) {
386 SHADE (d, s, i, r, g, b);
387 }
388 s += ss;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400389 }
390}
391
392static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200393shader_fade_and_move_horiz_in (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200394 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400395{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200396 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200397 guint32 shade_amount = scope->priv->shade_amount;
398 guint r = (shade_amount >> 16) & 0xff;
399 guint g = (shade_amount >> 8) & 0xff;
400 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200401 guint8 *s, *d;
402 gint ss, ds, width, height;
403
404 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
405 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
406 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
407 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
408
409 width = GST_VIDEO_FRAME_WIDTH (sframe);
410 height = GST_VIDEO_FRAME_HEIGHT (sframe);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400411
412 /* move upper half down */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200413 for (j = 0; j < height / 2; j++) {
414 d += ds;
415 for (i = 0; i < width; i++) {
416 SHADE (d, s, i, r, g, b);
417 }
418 s += ss;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400419 }
420 /* move lower half up */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200421 for (j = 0; j < height / 2; j++) {
422 s += ss;
423 for (i = 0; i < width; i++) {
424 SHADE (d, s, i, r, g, b);
425 }
426 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400427 }
428}
429
430static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200431shader_fade_and_move_vert_out (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200432 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400433{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200434 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200435 guint32 shade_amount = scope->priv->shade_amount;
436 guint r = (shade_amount >> 16) & 0xff;
437 guint g = (shade_amount >> 8) & 0xff;
438 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200439 guint8 *s, *s1, *d, *d1;
440 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400441
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200442 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
443 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
444 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
445 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
446
447 width = GST_VIDEO_FRAME_WIDTH (sframe);
448 height = GST_VIDEO_FRAME_HEIGHT (sframe);
449
450 for (j = 0; j < height; j++) {
451 /* move left half to the left */
452 s1 = s + 1;
453 for (i = 0; i < width / 2; i++) {
454 SHADE (d, s1, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400455 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200456 /* move right half to the right */
457 d1 = d + 1;
458 for (; i < width - 1; i++) {
459 SHADE (d1, s, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400460 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200461 s += ss;
462 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400463 }
464}
465
466static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200467shader_fade_and_move_vert_in (GstAudioVisualizer * scope,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200468 const GstVideoFrame * sframe, GstVideoFrame * dframe)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400469{
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200470 guint i, j;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200471 guint32 shade_amount = scope->priv->shade_amount;
472 guint r = (shade_amount >> 16) & 0xff;
473 guint g = (shade_amount >> 8) & 0xff;
474 guint b = (shade_amount >> 0) & 0xff;
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200475 guint8 *s, *s1, *d, *d1;
476 gint ss, ds, width, height;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400477
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200478 s = GST_VIDEO_FRAME_PLANE_DATA (sframe, 0);
479 ss = GST_VIDEO_FRAME_PLANE_STRIDE (sframe, 0);
480 d = GST_VIDEO_FRAME_PLANE_DATA (dframe, 0);
481 ds = GST_VIDEO_FRAME_PLANE_STRIDE (dframe, 0);
482
483 width = GST_VIDEO_FRAME_WIDTH (sframe);
484 height = GST_VIDEO_FRAME_HEIGHT (sframe);
485
486 for (j = 0; j < height; j++) {
487 /* move left half to the right */
488 d1 = d + 1;
489 for (i = 0; i < width / 2; i++) {
490 SHADE (d1, s, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400491 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200492 /* move right half to the left */
493 s1 = s + 1;
494 for (; i < width - 1; i++) {
495 SHADE (d, s1, i, r, g, b);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400496 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200497 s += ss;
498 d += ds;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400499 }
500}
501
502static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200503gst_audio_visualizer_change_shader (GstAudioVisualizer * scope)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400504{
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200505 GstAudioVisualizerShaderFunc shader;
506
507 switch (scope->priv->shader_type) {
Sebastian Dröge63e92012012-08-09 11:32:59 +0200508 case GST_AUDIO_VISUALIZER_SHADER_NONE:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200509 shader = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400510 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200511 case GST_AUDIO_VISUALIZER_SHADER_FADE:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200512 shader = shader_fade;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400513 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200514 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_UP:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200515 shader = shader_fade_and_move_up;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400516 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200517 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_DOWN:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200518 shader = shader_fade_and_move_down;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400519 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200520 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_LEFT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200521 shader = shader_fade_and_move_left;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400522 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200523 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_RIGHT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200524 shader = shader_fade_and_move_right;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400525 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200526 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_OUT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200527 shader = shader_fade_and_move_horiz_out;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400528 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200529 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_HORIZ_IN:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200530 shader = shader_fade_and_move_horiz_in;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400531 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200532 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_OUT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200533 shader = shader_fade_and_move_vert_out;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400534 break;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200535 case GST_AUDIO_VISUALIZER_SHADER_FADE_AND_MOVE_VERT_IN:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200536 shader = shader_fade_and_move_vert_in;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400537 break;
538 default:
539 GST_ERROR ("invalid shader function");
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200540 shader = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400541 break;
542 }
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200543
544 scope->priv->shader = shader;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400545}
546
547/* base class */
548
549GType
Sebastian Dröge63e92012012-08-09 11:32:59 +0200550gst_audio_visualizer_get_type (void)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400551{
Sebastian Dröge63e92012012-08-09 11:32:59 +0200552 static volatile gsize audio_visualizer_type = 0;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400553
Sebastian Dröge63e92012012-08-09 11:32:59 +0200554 if (g_once_init_enter (&audio_visualizer_type)) {
555 static const GTypeInfo audio_visualizer_info = {
556 sizeof (GstAudioVisualizerClass),
Olivier Naudanb28313f2012-04-16 08:10:18 -0400557 NULL,
558 NULL,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200559 (GClassInitFunc) gst_audio_visualizer_class_init,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400560 NULL,
561 NULL,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200562 sizeof (GstAudioVisualizer),
Olivier Naudanb28313f2012-04-16 08:10:18 -0400563 0,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200564 (GInstanceInitFunc) gst_audio_visualizer_init,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400565 };
566 GType _type;
567
Sebastian Dröge63e92012012-08-09 11:32:59 +0200568 /* TODO: rename when exporting it as a library */
Olivier Naudanb28313f2012-04-16 08:10:18 -0400569 _type = g_type_register_static (GST_TYPE_ELEMENT,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200570 "GstAudioVisualizer-BadGstAudioVisualizers", &audio_visualizer_info,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400571 G_TYPE_FLAG_ABSTRACT);
Sebastian Dröge63e92012012-08-09 11:32:59 +0200572 g_once_init_leave (&audio_visualizer_type, _type);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400573 }
Sebastian Dröge63e92012012-08-09 11:32:59 +0200574 return (GType) audio_visualizer_type;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400575}
576
577static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200578gst_audio_visualizer_class_init (GstAudioVisualizerClass * klass)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400579{
580 GObjectClass *gobject_class = (GObjectClass *) klass;
581 GstElementClass *element_class = (GstElementClass *) klass;
582
Sebastian Drögee6513c12013-07-14 12:12:42 +0200583 g_type_class_add_private (klass, sizeof (GstAudioVisualizerPrivate));
584
Olivier Naudanb28313f2012-04-16 08:10:18 -0400585 parent_class = g_type_class_peek_parent (klass);
586
Sebastian Dröge63e92012012-08-09 11:32:59 +0200587 GST_DEBUG_CATEGORY_INIT (audio_visualizer_debug, "baseaudiovisualizer",
Olivier Naudanb28313f2012-04-16 08:10:18 -0400588 0, "scope audio visualisation base class");
589
Sebastian Dröge63e92012012-08-09 11:32:59 +0200590 gobject_class->set_property = gst_audio_visualizer_set_property;
591 gobject_class->get_property = gst_audio_visualizer_get_property;
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100592 gobject_class->finalize = gst_audio_visualizer_finalize;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400593
594 element_class->change_state =
Sebastian Dröge63e92012012-08-09 11:32:59 +0200595 GST_DEBUG_FUNCPTR (gst_audio_visualizer_change_state);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400596
Sebastian Drögee6513c12013-07-14 12:12:42 +0200597 klass->decide_allocation = GST_DEBUG_FUNCPTR (default_decide_allocation);
598
Olivier Naudanb28313f2012-04-16 08:10:18 -0400599 g_object_class_install_property (gobject_class, PROP_SHADER,
600 g_param_spec_enum ("shader", "shader type",
601 "Shader function to apply on each frame",
Sebastian Dröge63e92012012-08-09 11:32:59 +0200602 GST_TYPE_AUDIO_VISUALIZER_SHADER, DEFAULT_SHADER,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400603 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
604 g_object_class_install_property (gobject_class, PROP_SHADE_AMOUNT,
605 g_param_spec_uint ("shade-amount", "shade amount",
606 "Shading color to use (big-endian ARGB)", 0, G_MAXUINT32,
607 DEFAULT_SHADE_AMOUNT,
608 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
609}
610
611static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200612gst_audio_visualizer_init (GstAudioVisualizer * scope,
613 GstAudioVisualizerClass * g_class)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400614{
615 GstPadTemplate *pad_template;
616
Sebastian Drögee6513c12013-07-14 12:12:42 +0200617 scope->priv = GST_AUDIO_VISUALIZER_GET_PRIVATE (scope);
618
Olivier Naudanb28313f2012-04-16 08:10:18 -0400619 /* create the sink and src pads */
620 pad_template =
621 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
622 g_return_if_fail (pad_template != NULL);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200623 scope->priv->sinkpad = gst_pad_new_from_template (pad_template, "sink");
624 gst_pad_set_chain_function (scope->priv->sinkpad,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200625 GST_DEBUG_FUNCPTR (gst_audio_visualizer_chain));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200626 gst_pad_set_event_function (scope->priv->sinkpad,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200627 GST_DEBUG_FUNCPTR (gst_audio_visualizer_sink_event));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200628 gst_element_add_pad (GST_ELEMENT (scope), scope->priv->sinkpad);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400629
630 pad_template =
631 gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
632 g_return_if_fail (pad_template != NULL);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200633 scope->priv->srcpad = gst_pad_new_from_template (pad_template, "src");
634 gst_pad_set_event_function (scope->priv->srcpad,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200635 GST_DEBUG_FUNCPTR (gst_audio_visualizer_src_event));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200636 gst_pad_set_query_function (scope->priv->srcpad,
Sebastian Dröge63e92012012-08-09 11:32:59 +0200637 GST_DEBUG_FUNCPTR (gst_audio_visualizer_src_query));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200638 gst_element_add_pad (GST_ELEMENT (scope), scope->priv->srcpad);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400639
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200640 scope->priv->adapter = gst_adapter_new ();
641 scope->priv->inbuf = gst_buffer_new ();
Olivier Naudanb28313f2012-04-16 08:10:18 -0400642
643 /* properties */
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200644 scope->priv->shader_type = DEFAULT_SHADER;
Sebastian Dröge63e92012012-08-09 11:32:59 +0200645 gst_audio_visualizer_change_shader (scope);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200646 scope->priv->shade_amount = DEFAULT_SHADE_AMOUNT;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400647
648 /* reset the initial video state */
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200649 gst_video_info_init (&scope->vinfo);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200650 scope->priv->frame_duration = GST_CLOCK_TIME_NONE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400651
Sebastian Dröge63e92012012-08-09 11:32:59 +0200652 /* reset the initial state */
Olivier Naudanb28313f2012-04-16 08:10:18 -0400653 gst_audio_info_init (&scope->ainfo);
Sebastian Dröge63e92012012-08-09 11:32:59 +0200654 gst_video_info_init (&scope->vinfo);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400655
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200656 g_mutex_init (&scope->priv->config_lock);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400657}
658
659static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200660gst_audio_visualizer_set_property (GObject * object, guint prop_id,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400661 const GValue * value, GParamSpec * pspec)
662{
Sebastian Dröge63e92012012-08-09 11:32:59 +0200663 GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400664
665 switch (prop_id) {
666 case PROP_SHADER:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200667 scope->priv->shader_type = g_value_get_enum (value);
Sebastian Dröge63e92012012-08-09 11:32:59 +0200668 gst_audio_visualizer_change_shader (scope);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400669 break;
670 case PROP_SHADE_AMOUNT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200671 scope->priv->shade_amount = g_value_get_uint (value);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400672 break;
673 default:
674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
675 break;
676 }
677}
678
679static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200680gst_audio_visualizer_get_property (GObject * object, guint prop_id,
Olivier Naudanb28313f2012-04-16 08:10:18 -0400681 GValue * value, GParamSpec * pspec)
682{
Sebastian Dröge63e92012012-08-09 11:32:59 +0200683 GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400684
685 switch (prop_id) {
686 case PROP_SHADER:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200687 g_value_set_enum (value, scope->priv->shader_type);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400688 break;
689 case PROP_SHADE_AMOUNT:
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200690 g_value_set_uint (value, scope->priv->shade_amount);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400691 break;
692 default:
693 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
694 break;
695 }
696}
697
698static void
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100699gst_audio_visualizer_finalize (GObject * object)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400700{
Sebastian Dröge63e92012012-08-09 11:32:59 +0200701 GstAudioVisualizer *scope = GST_AUDIO_VISUALIZER (object);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200702 GstAudioVisualizerPrivate *priv = scope->priv;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400703
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200704 if (priv->adapter) {
705 g_object_unref (priv->adapter);
706 priv->adapter = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400707 }
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200708 if (priv->inbuf) {
709 gst_buffer_unref (priv->inbuf);
710 priv->inbuf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400711 }
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200712 if (priv->tempbuf) {
713 gst_video_frame_unmap (&priv->tempframe);
714 gst_buffer_unref (priv->tempbuf);
715 priv->tempbuf = NULL;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400716 }
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100717
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200718 g_mutex_clear (&priv->config_lock);
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100719
720 G_OBJECT_CLASS (parent_class)->finalize (object);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400721}
722
723static void
Sebastian Dröge63e92012012-08-09 11:32:59 +0200724gst_audio_visualizer_reset (GstAudioVisualizer * scope)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400725{
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200726 GstAudioVisualizerPrivate *priv = scope->priv;
727
728 gst_adapter_clear (priv->adapter);
729 gst_segment_init (&priv->segment, GST_FORMAT_UNDEFINED);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400730
731 GST_OBJECT_LOCK (scope);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200732 priv->proportion = 1.0;
733 priv->earliest_time = -1;
734 priv->dropped = 0;
735 priv->processed = 0;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400736 GST_OBJECT_UNLOCK (scope);
737}
738
739static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +0200740gst_audio_visualizer_sink_setcaps (GstAudioVisualizer * scope, GstCaps * caps)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400741{
742 GstAudioInfo info;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400743
744 if (!gst_audio_info_from_caps (&info, caps))
745 goto wrong_caps;
746
747 scope->ainfo = info;
748
749 GST_DEBUG_OBJECT (scope, "audio: channels %d, rate %d",
750 GST_AUDIO_INFO_CHANNELS (&info), GST_AUDIO_INFO_RATE (&info));
751
Sebastian Drögee6513c12013-07-14 12:12:42 +0200752 if (!gst_audio_visualizer_src_negotiate (scope)) {
753 goto not_negotiated;
754 }
755
756 return TRUE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400757
758 /* Errors */
759wrong_caps:
760 {
761 GST_WARNING_OBJECT (scope, "could not parse caps");
Sebastian Drögee6513c12013-07-14 12:12:42 +0200762 return FALSE;
763 }
764not_negotiated:
765 {
766 GST_WARNING_OBJECT (scope, "failed to negotiate");
767 return FALSE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400768 }
769}
770
771static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +0200772gst_audio_visualizer_src_setcaps (GstAudioVisualizer * scope, GstCaps * caps)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400773{
Sebastian Dröge63e92012012-08-09 11:32:59 +0200774 GstVideoInfo info;
775 GstAudioVisualizerClass *klass;
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200776 GstAudioVisualizerPrivate *priv;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400777 gboolean res;
778
Sebastian Dröge63e92012012-08-09 11:32:59 +0200779 if (!gst_video_info_from_caps (&info, caps))
780 goto wrong_caps;
781
Sebastian Dröge63e92012012-08-09 11:32:59 +0200782 klass = GST_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
Olivier Naudanb28313f2012-04-16 08:10:18 -0400783
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200784 priv = scope->priv;
785
Sebastian Dröge63e92012012-08-09 11:32:59 +0200786 scope->vinfo = info;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400787
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200788 priv->frame_duration = gst_util_uint64_scale_int (GST_SECOND,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200789 GST_VIDEO_INFO_FPS_D (&info), GST_VIDEO_INFO_FPS_N (&info));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200790 priv->spf = gst_util_uint64_scale_int (GST_AUDIO_INFO_RATE (&scope->ainfo),
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200791 GST_VIDEO_INFO_FPS_D (&info), GST_VIDEO_INFO_FPS_N (&info));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200792 scope->req_spf = priv->spf;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400793
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200794 if (priv->tempbuf) {
795 gst_video_frame_unmap (&priv->tempframe);
796 gst_buffer_unref (priv->tempbuf);
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200797 }
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200798 priv->tempbuf = gst_buffer_new_wrapped (g_malloc0 (scope->vinfo.size),
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200799 scope->vinfo.size);
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200800 gst_video_frame_map (&priv->tempframe, &scope->vinfo, priv->tempbuf,
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200801 GST_MAP_READWRITE);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400802
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100803 if (klass->setup && !klass->setup (scope))
804 goto setup_failed;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400805
806 GST_DEBUG_OBJECT (scope, "video: dimension %dx%d, framerate %d/%d",
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200807 GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info),
808 GST_VIDEO_INFO_FPS_N (&info), GST_VIDEO_INFO_FPS_D (&info));
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200809 GST_DEBUG_OBJECT (scope, "blocks: spf %u, req_spf %u", priv->spf,
810 scope->req_spf);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400811
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200812 gst_pad_set_caps (priv->srcpad, caps);
Sebastian Drögee6513c12013-07-14 12:12:42 +0200813
814 /* find a pool for the negotiated caps now */
815 res = gst_audio_visualizer_do_bufferpool (scope, caps);
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100816 gst_caps_unref (caps);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400817
818 return res;
819
820 /* ERRORS */
Sebastian Dröge63e92012012-08-09 11:32:59 +0200821wrong_caps:
Olivier Naudanb28313f2012-04-16 08:10:18 -0400822 {
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100823 gst_caps_unref (caps);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400824 GST_DEBUG_OBJECT (scope, "error parsing caps");
825 return FALSE;
826 }
Sebastian Dröge4139fce2015-03-17 09:38:41 +0100827
828setup_failed:
829 {
830 GST_WARNING_OBJECT (scope, "failed to set up");
831 return FALSE;
832 }
Olivier Naudanb28313f2012-04-16 08:10:18 -0400833}
834
835static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +0200836gst_audio_visualizer_src_negotiate (GstAudioVisualizer * scope)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400837{
838 GstCaps *othercaps, *target;
839 GstStructure *structure;
840 GstCaps *templ;
Sebastian Drögee6513c12013-07-14 12:12:42 +0200841 gboolean ret;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400842
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200843 templ = gst_pad_get_pad_template_caps (scope->priv->srcpad);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400844
845 GST_DEBUG_OBJECT (scope, "performing negotiation");
846
847 /* see what the peer can do */
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200848 othercaps = gst_pad_peer_query_caps (scope->priv->srcpad, NULL);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400849 if (othercaps) {
850 target = gst_caps_intersect (othercaps, templ);
851 gst_caps_unref (othercaps);
852 gst_caps_unref (templ);
853
854 if (gst_caps_is_empty (target))
855 goto no_format;
856
857 target = gst_caps_truncate (target);
858 } else {
859 target = templ;
860 }
861
862 target = gst_caps_make_writable (target);
863 structure = gst_caps_get_structure (target, 0);
Sebastian Dröge5a155a02012-09-14 10:49:31 +0200864 gst_structure_fixate_field_nearest_int (structure, "width", 320);
865 gst_structure_fixate_field_nearest_int (structure, "height", 200);
866 gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1);
867
Sebastian Dröge63e92012012-08-09 11:32:59 +0200868 target = gst_caps_fixate (target);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400869
870 GST_DEBUG_OBJECT (scope, "final caps are %" GST_PTR_FORMAT, target);
871
Sebastian Drögee6513c12013-07-14 12:12:42 +0200872 ret = gst_audio_visualizer_src_setcaps (scope, target);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400873
Sebastian Drögee6513c12013-07-14 12:12:42 +0200874 return ret;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400875
876no_format:
877 {
878 gst_caps_unref (target);
879 return FALSE;
880 }
881}
882
Sebastian Drögee6513c12013-07-14 12:12:42 +0200883/* takes ownership of the pool, allocator and query */
884static gboolean
885gst_audio_visualizer_set_allocation (GstAudioVisualizer * scope,
886 GstBufferPool * pool, GstAllocator * allocator,
887 GstAllocationParams * params, GstQuery * query)
Olivier Naudanb28313f2012-04-16 08:10:18 -0400888{
Sebastian Drögee6513c12013-07-14 12:12:42 +0200889 GstAllocator *oldalloc;
890 GstBufferPool *oldpool;
891 GstQuery *oldquery;
892 GstAudioVisualizerPrivate *priv = scope->priv;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400893
Sebastian Drögee6513c12013-07-14 12:12:42 +0200894 GST_OBJECT_LOCK (scope);
895 oldpool = priv->pool;
896 priv->pool = pool;
897 priv->pool_active = FALSE;
Olivier Naudanb28313f2012-04-16 08:10:18 -0400898
Sebastian Drögee6513c12013-07-14 12:12:42 +0200899 oldalloc = priv->allocator;
900 priv->allocator = allocator;
901
902 oldquery = priv->query;
903 priv->query = query;
904
905 if (params)
906 priv->params = *params;
907 else
908 gst_allocation_params_init (&priv->params);
909 GST_OBJECT_UNLOCK (scope);
910
911 if (oldpool) {
912 GST_DEBUG_OBJECT (scope, "deactivating old pool %p", oldpool);
913 gst_buffer_pool_set_active (oldpool, FALSE);
914 gst_object_unref (oldpool);
Olivier Naudanb28313f2012-04-16 08:10:18 -0400915 }
Sebastian Drögee6513c12013-07-14 12:12:42 +0200916 if (oldalloc) {
917 gst_object_unref (oldalloc);
918 }
919 if (oldquery) {
920 gst_query_unref (oldquery);
921 }
922 return TRUE;
923}
924
925static gboolean
926gst_audio_visualizer_do_bufferpool (GstAudioVisualizer * scope,
927 GstCaps * outcaps)
928{
929 GstQuery *query;
930 gboolean result = TRUE;
931 GstBufferPool *pool = NULL;
932 GstAudioVisualizerClass *klass;
933 GstAllocator *allocator;
934 GstAllocationParams params;
935
936 /* not passthrough, we need to allocate */
937 /* find a pool for the negotiated caps now */
938 GST_DEBUG_OBJECT (scope, "doing allocation query");
939 query = gst_query_new_allocation (outcaps, TRUE);
940
Sebastian Dröge78ac2892015-06-07 10:59:27 +0200941 if (!gst_pad_peer_query (scope->priv->srcpad, query)) {
Sebastian Drögee6513c12013-07-14 12:12:42 +0200942 /* not a problem, we use the query defaults */
943 GST_DEBUG_OBJECT (scope, "allocation query failed");
944 }
945
946 klass = GST_AUDIO_VISUALIZER_GET_CLASS (scope);
947
948 GST_DEBUG_OBJECT (scope, "calling decide_allocation");
949 g_assert (klass->decide_allocation != NULL);
950 result = klass->decide_allocation (scope, query);
951
952 GST_DEBUG_OBJECT (scope, "ALLOCATION (%d) params: %" GST_PTR_FORMAT, result,
953 query);
954
955 if (!result)
956 goto no_decide_allocation;
957
958 /* we got configuration from our peer or the decide_allocation method,
959 * parse them */
960 if (gst_query_get_n_allocation_params (query) > 0) {
961 gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
962 } else {
963 allocator = NULL;
964 gst_allocation_params_init (&params);
965 }
966
967 if (gst_query_get_n_allocation_pools (query) > 0)
968 gst_query_parse_nth_allocation_pool (query, 0, &pool, NULL, NULL, NULL);
969
970 /* now store */
971 result =
972 gst_audio_visualizer_set_allocation (scope, pool, allocator, &params,
973 query);
974
975 return result;
976
977 /* Errors */
978no_decide_allocation:
979 {
980 GST_WARNING_OBJECT (scope, "Subclass failed to decide allocation");
981 gst_query_unref (query);
982
983 return result;
984 }
985}
986
987static gboolean
988default_decide_allocation (GstAudioVisualizer * scope, GstQuery * query)
989{
990 GstCaps *outcaps;
991 GstBufferPool *pool;
992 guint size, min, max;
993 GstAllocator *allocator;
994 GstAllocationParams params;
995 GstStructure *config;
996 gboolean update_allocator;
997 gboolean update_pool;
998
999 gst_query_parse_allocation (query, &outcaps, NULL);
1000
1001 /* we got configuration from our peer or the decide_allocation method,
1002 * parse them */
1003 if (gst_query_get_n_allocation_params (query) > 0) {
1004 /* try the allocator */
1005 gst_query_parse_nth_allocation_param (query, 0, &allocator, &params);
1006 update_allocator = TRUE;
1007 } else {
1008 allocator = NULL;
1009 gst_allocation_params_init (&params);
1010 update_allocator = FALSE;
1011 }
1012
1013 if (gst_query_get_n_allocation_pools (query) > 0) {
1014 gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
1015 update_pool = TRUE;
1016 } else {
1017 pool = NULL;
1018 size = GST_VIDEO_INFO_SIZE (&scope->vinfo);
1019 min = max = 0;
1020 update_pool = FALSE;
1021 }
1022
1023 if (pool == NULL) {
1024 /* we did not get a pool, make one ourselves then */
1025 pool = gst_video_buffer_pool_new ();
1026 }
1027
1028 config = gst_buffer_pool_get_config (pool);
1029 gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
1030 gst_buffer_pool_config_set_allocator (config, allocator, &params);
1031 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1032 gst_buffer_pool_set_config (pool, config);
1033
1034 if (update_allocator)
1035 gst_query_set_nth_allocation_param (query, 0, allocator, &params);
1036 else
1037 gst_query_add_allocation_param (query, allocator, &params);
1038
1039 if (allocator)
1040 gst_object_unref (allocator);
1041
1042 if (update_pool)
1043 gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
1044 else
1045 gst_query_add_allocation_pool (query, pool, size, min, max);
1046
1047 if (pool)
1048 gst_object_unref (pool);
1049
1050 return TRUE;
1051}
1052
1053static GstFlowReturn
1054default_prepare_output_buffer (GstAudioVisualizer * scope, GstBuffer ** outbuf)
1055{
1056 GstAudioVisualizerPrivate *priv;
1057
1058 priv = scope->priv;
1059
1060 g_assert (priv->pool != NULL);
1061
1062 /* we can't reuse the input buffer */
1063 if (!priv->pool_active) {
1064 GST_DEBUG_OBJECT (scope, "setting pool %p active", priv->pool);
1065 if (!gst_buffer_pool_set_active (priv->pool, TRUE))
1066 goto activate_failed;
1067 priv->pool_active = TRUE;
1068 }
1069 GST_DEBUG_OBJECT (scope, "using pool alloc");
1070
1071 return gst_buffer_pool_acquire_buffer (priv->pool, outbuf, NULL);
1072
1073 /* ERRORS */
1074activate_failed:
1075 {
1076 GST_ELEMENT_ERROR (scope, RESOURCE, SETTINGS,
1077 ("failed to activate bufferpool"), ("failed to activate bufferpool"));
1078 return GST_FLOW_ERROR;
1079 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001080}
1081
1082static GstFlowReturn
Sebastian Dröge63e92012012-08-09 11:32:59 +02001083gst_audio_visualizer_chain (GstPad * pad, GstObject * parent,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001084 GstBuffer * buffer)
1085{
1086 GstFlowReturn ret = GST_FLOW_OK;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001087 GstAudioVisualizer *scope;
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001088 GstAudioVisualizerPrivate *priv;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001089 GstAudioVisualizerClass *klass;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001090 GstBuffer *inbuf;
1091 guint64 dist, ts;
1092 guint avail, sbpf;
1093 gpointer adata;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001094 gint bps, channels, rate;
1095
Sebastian Dröge63e92012012-08-09 11:32:59 +02001096 scope = GST_AUDIO_VISUALIZER (parent);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001097 priv = scope->priv;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001098 klass = GST_AUDIO_VISUALIZER_CLASS (G_OBJECT_GET_CLASS (scope));
Olivier Naudanb28313f2012-04-16 08:10:18 -04001099
Olivier Naudanb28313f2012-04-16 08:10:18 -04001100 GST_LOG_OBJECT (scope, "chainfunc called");
1101
1102 /* resync on DISCONT */
1103 if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001104 gst_adapter_clear (priv->adapter);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001105 }
1106
1107 /* Make sure have an output format */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001108 if (gst_pad_check_reconfigure (priv->srcpad)) {
Sebastian Drögee6513c12013-07-14 12:12:42 +02001109 if (!gst_audio_visualizer_src_negotiate (scope)) {
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001110 gst_pad_mark_reconfigure (priv->srcpad);
Sebastian Drögee6513c12013-07-14 12:12:42 +02001111 goto not_negotiated;
1112 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001113 }
Sebastian Drögee6513c12013-07-14 12:12:42 +02001114
Olivier Naudanb28313f2012-04-16 08:10:18 -04001115 channels = GST_AUDIO_INFO_CHANNELS (&scope->ainfo);
1116 rate = GST_AUDIO_INFO_RATE (&scope->ainfo);
1117 bps = GST_AUDIO_INFO_BPS (&scope->ainfo);
1118
1119 if (bps == 0) {
1120 ret = GST_FLOW_NOT_NEGOTIATED;
1121 goto beach;
1122 }
1123
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001124 gst_adapter_push (priv->adapter, buffer);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001125
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001126 g_mutex_lock (&priv->config_lock);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001127
1128 /* this is what we want */
1129 sbpf = scope->req_spf * channels * sizeof (gint16);
1130
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001131 inbuf = priv->inbuf;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001132 /* FIXME: the timestamp in the adapter would be different */
1133 gst_buffer_copy_into (inbuf, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
1134
1135 /* this is what we have */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001136 avail = gst_adapter_available (priv->adapter);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001137 GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
1138 while (avail >= sbpf) {
1139 GstBuffer *outbuf;
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001140 GstVideoFrame outframe;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001141
1142 /* get timestamp of the current adapter content */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001143 ts = gst_adapter_prev_pts (priv->adapter, &dist);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001144 if (GST_CLOCK_TIME_IS_VALID (ts)) {
1145 /* convert bytes to time */
1146 dist /= bps;
1147 ts += gst_util_uint64_scale_int (dist, GST_SECOND, rate);
1148 }
1149
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001150 /* check for QoS, don't compute buffers that are known to be late */
Olivier Naudanb28313f2012-04-16 08:10:18 -04001151 if (GST_CLOCK_TIME_IS_VALID (ts)) {
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001152 GstClockTime earliest_time;
1153 gdouble proportion;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001154 gint64 qostime;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001155
1156 qostime =
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001157 gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME,
1158 ts) + priv->frame_duration;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001159
1160 GST_OBJECT_LOCK (scope);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001161 earliest_time = priv->earliest_time;
1162 proportion = priv->proportion;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001163 GST_OBJECT_UNLOCK (scope);
1164
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001165 if (GST_CLOCK_TIME_IS_VALID (earliest_time) && qostime <= earliest_time) {
1166 GstClockTime stream_time, jitter;
1167 GstMessage *qos_msg;
1168
1169 GST_DEBUG_OBJECT (scope,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001170 "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001171 GST_TIME_ARGS (qostime), GST_TIME_ARGS (earliest_time));
1172
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001173 ++priv->dropped;
1174 stream_time = gst_segment_to_stream_time (&priv->segment,
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001175 GST_FORMAT_TIME, ts);
1176 jitter = GST_CLOCK_DIFF (qostime, earliest_time);
1177 qos_msg = gst_message_new_qos (GST_OBJECT (scope), FALSE, qostime,
1178 stream_time, ts, GST_BUFFER_DURATION (buffer));
1179 gst_message_set_qos_values (qos_msg, jitter, proportion, 1000000);
1180 gst_message_set_qos_stats (qos_msg, GST_FORMAT_BUFFERS,
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001181 priv->processed, priv->dropped);
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001182 gst_element_post_message (GST_ELEMENT (scope), qos_msg);
1183
Olivier Naudanb28313f2012-04-16 08:10:18 -04001184 goto skip;
1185 }
1186 }
1187
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001188 ++priv->processed;
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001189
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001190 g_mutex_unlock (&priv->config_lock);
Sebastian Drögee6513c12013-07-14 12:12:42 +02001191 ret = default_prepare_output_buffer (scope, &outbuf);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001192 g_mutex_lock (&priv->config_lock);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001193 /* recheck as the value could have changed */
1194 sbpf = scope->req_spf * channels * sizeof (gint16);
1195
1196 /* no buffer allocated, we don't care why. */
1197 if (ret != GST_FLOW_OK)
1198 break;
1199
1200 /* sync controlled properties */
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001201 if (GST_CLOCK_TIME_IS_VALID (ts))
1202 gst_object_sync_values (GST_OBJECT (scope), ts);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001203
1204 GST_BUFFER_TIMESTAMP (outbuf) = ts;
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001205 GST_BUFFER_DURATION (outbuf) = priv->frame_duration;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001206
Olivier Naudanb28313f2012-04-16 08:10:18 -04001207 /* this can fail as the data size we need could have changed */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001208 if (!(adata = (gpointer) gst_adapter_map (priv->adapter, sbpf)))
Olivier Naudanb28313f2012-04-16 08:10:18 -04001209 break;
1210
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001211 gst_video_frame_map (&outframe, &scope->vinfo, outbuf, GST_MAP_READWRITE);
1212
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001213 if (priv->shader) {
1214 gst_video_frame_copy (&outframe, &priv->tempframe);
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001215 } else {
1216 /* gst_video_frame_clear() or is output frame already cleared */
Sebastian Dröge897da272013-03-22 18:20:09 +01001217 gint i;
1218
1219 for (i = 0; i < scope->vinfo.finfo->n_planes; i++) {
1220 memset (outframe.data[i], 0, outframe.map[i].size);
1221 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001222 }
1223
Sebastian Dröge63e92012012-08-09 11:32:59 +02001224 gst_buffer_replace_all_memory (inbuf,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001225 gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, adata, sbpf, 0,
1226 sbpf, NULL, NULL));
1227
1228 /* call class->render() vmethod */
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001229 if (klass->render) {
1230 if (!klass->render (scope, inbuf, &outframe)) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001231 ret = GST_FLOW_ERROR;
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001232 gst_video_frame_unmap (&outframe);
1233 goto beach;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001234 } else {
Sebastian Drögee6513c12013-07-14 12:12:42 +02001235 /* run various post processing (shading and geometric transformation) */
1236 /* FIXME: SHADER assumes 32bpp */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001237 if (priv->shader && GST_VIDEO_INFO_COMP_PSTRIDE (&scope->vinfo, 0) == 4) {
1238 priv->shader (scope, &outframe, &priv->tempframe);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001239 }
1240 }
1241 }
Sebastian Dröge5a155a02012-09-14 10:49:31 +02001242 gst_video_frame_unmap (&outframe);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001243
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001244 g_mutex_unlock (&priv->config_lock);
1245 ret = gst_pad_push (priv->srcpad, outbuf);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001246 outbuf = NULL;
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001247 g_mutex_lock (&priv->config_lock);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001248
1249 skip:
1250 /* recheck as the value could have changed */
1251 sbpf = scope->req_spf * channels * sizeof (gint16);
1252 GST_LOG_OBJECT (scope, "avail: %u, bpf: %u", avail, sbpf);
1253 /* we want to take less or more, depending on spf : req_spf */
1254 if (avail - sbpf >= sbpf) {
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001255 gst_adapter_flush (priv->adapter, sbpf);
1256 gst_adapter_unmap (priv->adapter);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001257 } else if (avail >= sbpf) {
1258 /* just flush a bit and stop */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001259 gst_adapter_flush (priv->adapter, (avail - sbpf));
1260 gst_adapter_unmap (priv->adapter);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001261 break;
1262 }
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001263 avail = gst_adapter_available (priv->adapter);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001264
1265 if (ret != GST_FLOW_OK)
1266 break;
1267 }
1268
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001269 g_mutex_unlock (&priv->config_lock);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001270
1271beach:
1272 return ret;
Sebastian Drögee6513c12013-07-14 12:12:42 +02001273
1274 /* ERRORS */
1275not_negotiated:
1276 {
1277 GST_DEBUG_OBJECT (scope, "Failed to renegotiate");
1278 return GST_FLOW_NOT_NEGOTIATED;
1279 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001280}
1281
1282static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +02001283gst_audio_visualizer_src_event (GstPad * pad, GstObject * parent,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001284 GstEvent * event)
1285{
1286 gboolean res;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001287 GstAudioVisualizer *scope;
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001288 GstAudioVisualizerPrivate *priv;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001289
Sebastian Dröge63e92012012-08-09 11:32:59 +02001290 scope = GST_AUDIO_VISUALIZER (parent);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001291 priv = scope->priv;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001292
1293 switch (GST_EVENT_TYPE (event)) {
1294 case GST_EVENT_QOS:
1295 {
1296 gdouble proportion;
1297 GstClockTimeDiff diff;
1298 GstClockTime timestamp;
1299
1300 gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
1301
1302 /* save stuff for the _chain() function */
1303 GST_OBJECT_LOCK (scope);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001304 priv->proportion = proportion;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001305 if (diff >= 0)
1306 /* we're late, this is a good estimate for next displayable
1307 * frame (see part-qos.txt) */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001308 priv->earliest_time = timestamp + 2 * diff + priv->frame_duration;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001309 else
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001310 priv->earliest_time = timestamp + diff;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001311 GST_OBJECT_UNLOCK (scope);
1312
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001313 res = gst_pad_push_event (priv->sinkpad, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001314 break;
1315 }
Sebastian Dröge63e92012012-08-09 11:32:59 +02001316 case GST_EVENT_RECONFIGURE:
1317 /* dont't forward */
1318 gst_event_unref (event);
1319 res = TRUE;
1320 break;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001321 default:
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001322 res = gst_pad_event_default (pad, parent, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001323 break;
1324 }
1325
1326 return res;
1327}
1328
1329static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +02001330gst_audio_visualizer_sink_event (GstPad * pad, GstObject * parent,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001331 GstEvent * event)
1332{
1333 gboolean res;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001334 GstAudioVisualizer *scope;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001335
Sebastian Dröge63e92012012-08-09 11:32:59 +02001336 scope = GST_AUDIO_VISUALIZER (parent);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001337
1338 switch (GST_EVENT_TYPE (event)) {
1339 case GST_EVENT_CAPS:
1340 {
1341 GstCaps *caps;
1342
1343 gst_event_parse_caps (event, &caps);
Sebastian Dröge63e92012012-08-09 11:32:59 +02001344 res = gst_audio_visualizer_sink_setcaps (scope, caps);
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001345 gst_event_unref (event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001346 break;
1347 }
Olivier Naudanb28313f2012-04-16 08:10:18 -04001348 case GST_EVENT_FLUSH_STOP:
Sebastian Dröge63e92012012-08-09 11:32:59 +02001349 gst_audio_visualizer_reset (scope);
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001350 res = gst_pad_push_event (scope->priv->srcpad, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001351 break;
1352 case GST_EVENT_SEGMENT:
1353 {
1354 /* the newsegment values are used to clip the input samples
1355 * and to convert the incomming timestamps to running time so
1356 * we can do QoS */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001357 gst_event_copy_segment (event, &scope->priv->segment);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001358
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001359 res = gst_pad_push_event (scope->priv->srcpad, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001360 break;
1361 }
1362 default:
Sebastian Dröge4139fce2015-03-17 09:38:41 +01001363 res = gst_pad_event_default (pad, parent, event);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001364 break;
1365 }
1366
1367 return res;
1368}
1369
1370static gboolean
Sebastian Dröge63e92012012-08-09 11:32:59 +02001371gst_audio_visualizer_src_query (GstPad * pad, GstObject * parent,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001372 GstQuery * query)
1373{
1374 gboolean res = FALSE;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001375 GstAudioVisualizer *scope;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001376
Sebastian Dröge63e92012012-08-09 11:32:59 +02001377 scope = GST_AUDIO_VISUALIZER (parent);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001378
1379 switch (GST_QUERY_TYPE (query)) {
1380 case GST_QUERY_LATENCY:
1381 {
1382 /* We need to send the query upstream and add the returned latency to our
1383 * own */
1384 GstClockTime min_latency, max_latency;
1385 gboolean us_live;
1386 GstClockTime our_latency;
1387 guint max_samples;
1388 gint rate = GST_AUDIO_INFO_RATE (&scope->ainfo);
1389
1390 if (rate == 0)
1391 break;
1392
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001393 if ((res = gst_pad_peer_query (scope->priv->sinkpad, query))) {
Olivier Naudanb28313f2012-04-16 08:10:18 -04001394 gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
1395
1396 GST_DEBUG_OBJECT (scope, "Peer latency: min %"
1397 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1398 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
1399
1400 /* the max samples we must buffer buffer */
Sebastian Dröge78ac2892015-06-07 10:59:27 +02001401 max_samples = MAX (scope->req_spf, scope->priv->spf);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001402 our_latency = gst_util_uint64_scale_int (max_samples, GST_SECOND, rate);
1403
1404 GST_DEBUG_OBJECT (scope, "Our latency: %" GST_TIME_FORMAT,
1405 GST_TIME_ARGS (our_latency));
1406
1407 /* we add some latency but only if we need to buffer more than what
1408 * upstream gives us */
1409 min_latency += our_latency;
1410 if (max_latency != -1)
1411 max_latency += our_latency;
1412
1413 GST_DEBUG_OBJECT (scope, "Calculated total latency : min %"
1414 GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
1415 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
1416
1417 gst_query_set_latency (query, TRUE, min_latency, max_latency);
1418 }
1419 break;
1420 }
1421 default:
1422 res = gst_pad_query_default (pad, parent, query);
1423 break;
1424 }
1425
1426 return res;
1427}
1428
Olivier Naudanb28313f2012-04-16 08:10:18 -04001429static GstStateChangeReturn
Sebastian Dröge63e92012012-08-09 11:32:59 +02001430gst_audio_visualizer_change_state (GstElement * element,
Olivier Naudanb28313f2012-04-16 08:10:18 -04001431 GstStateChange transition)
1432{
1433 GstStateChangeReturn ret;
Sebastian Dröge63e92012012-08-09 11:32:59 +02001434 GstAudioVisualizer *scope;
Olivier Naudanb28313f2012-04-16 08:10:18 -04001435
Sebastian Dröge63e92012012-08-09 11:32:59 +02001436 scope = GST_AUDIO_VISUALIZER (element);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001437
1438 switch (transition) {
1439 case GST_STATE_CHANGE_READY_TO_PAUSED:
Sebastian Dröge63e92012012-08-09 11:32:59 +02001440 gst_audio_visualizer_reset (scope);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001441 break;
1442 default:
1443 break;
1444 }
1445
1446 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1447
1448 switch (transition) {
1449 case GST_STATE_CHANGE_PAUSED_TO_READY:
Sebastian Drögee6513c12013-07-14 12:12:42 +02001450 gst_audio_visualizer_set_allocation (scope, NULL, NULL, NULL, NULL);
Olivier Naudanb28313f2012-04-16 08:10:18 -04001451 break;
1452 case GST_STATE_CHANGE_READY_TO_NULL:
1453 break;
1454 default:
1455 break;
1456 }
1457
1458 return ret;
1459}