cd9a9458d0864836991112eb467d06c4eeacf83b
[platform/upstream/gstreamer.git] / gst / scopes / gstbasescope.c
1 /* GStreamer
2  * Copyright (C) <2011> Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstbasescope.h: base class for audio visualisation elements
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 /**
21  * SECTION:gstbasescope
22  *
23  * A basclass for scopes. Takes care of re-fitting the audio-rate to video-rate.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include <string.h>
30
31 #include "gstbasescope.h"
32
33 GST_DEBUG_CATEGORY_STATIC (base_scope_debug);
34 #define GST_CAT_DEFAULT (base_scope_debug)
35
36 static GstBaseTransformClass *parent_class = NULL;
37
38 static void gst_base_scope_class_init (GstBaseScopeClass * klass);
39 static void gst_base_scope_init (GstBaseScope * scope,
40     GstBaseScopeClass * g_class);
41 static void gst_base_scope_dispose (GObject * object);
42
43 static gboolean gst_base_scope_src_negotiate (GstBaseScope * scope);
44 static gboolean gst_base_scope_src_setcaps (GstPad * pad, GstCaps * caps);
45 static gboolean gst_base_scope_sink_setcaps (GstPad * pad, GstCaps * caps);
46
47 static GstFlowReturn gst_base_scope_chain (GstPad * pad, GstBuffer * buffer);
48 static GstStateChangeReturn gst_base_scope_change_state (GstElement * element,
49     GstStateChange transition);
50
51 GType
52 gst_base_scope_get_type (void)
53 {
54   static volatile gsize base_scope_type = 0;
55
56   if (g_once_init_enter (&base_scope_type)) {
57     static const GTypeInfo base_scope_info = {
58       sizeof (GstBaseScopeClass),
59       NULL,
60       NULL,
61       (GClassInitFunc) gst_base_scope_class_init,
62       NULL,
63       NULL,
64       sizeof (GstBaseScope),
65       0,
66       (GInstanceInitFunc) gst_base_scope_init,
67     };
68     GType _type;
69
70     _type = g_type_register_static (GST_TYPE_ELEMENT,
71         "GstBaseScope", &base_scope_info, G_TYPE_FLAG_ABSTRACT);
72     g_once_init_leave (&base_scope_type, _type);
73   }
74   return (GType) base_scope_type;
75 }
76
77 static void
78 gst_base_scope_class_init (GstBaseScopeClass * klass)
79 {
80   GObjectClass *gobject_class = (GObjectClass *) klass;
81   GstElementClass *element_class = (GstElementClass *) klass;
82
83   parent_class = g_type_class_peek_parent (klass);
84
85   GST_DEBUG_CATEGORY_INIT (base_scope_debug, "basescope", 0,
86       "scope audio visualisation base class");
87
88   gobject_class->dispose = gst_base_scope_dispose;
89   element_class->change_state = GST_DEBUG_FUNCPTR (gst_base_scope_change_state);
90 }
91
92 static void
93 gst_base_scope_init (GstBaseScope * scope, GstBaseScopeClass * g_class)
94 {
95   GstPadTemplate *pad_template;
96
97   /* create the sink and src pads */
98   pad_template =
99       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "sink");
100   g_return_if_fail (pad_template != NULL);
101   scope->sinkpad = gst_pad_new_from_template (pad_template, "sink");
102   gst_pad_set_chain_function (scope->sinkpad,
103       GST_DEBUG_FUNCPTR (gst_base_scope_chain));
104   gst_pad_set_setcaps_function (scope->sinkpad,
105       GST_DEBUG_FUNCPTR (gst_base_scope_sink_setcaps));
106   gst_element_add_pad (GST_ELEMENT (scope), scope->sinkpad);
107
108   pad_template =
109       gst_element_class_get_pad_template (GST_ELEMENT_CLASS (g_class), "src");
110   g_return_if_fail (pad_template != NULL);
111   scope->srcpad = gst_pad_new_from_template (pad_template, "src");
112   gst_pad_set_setcaps_function (scope->srcpad,
113       GST_DEBUG_FUNCPTR (gst_base_scope_src_setcaps));
114   gst_element_add_pad (GST_ELEMENT (scope), scope->srcpad);
115
116   scope->adapter = gst_adapter_new ();
117   scope->inbuf = gst_buffer_new ();
118
119   /* reset the initial video state */
120   scope->width = 320;
121   scope->height = 200;
122   scope->fps_n = 25;            /* desired frame rate */
123   scope->fps_d = 1;
124   scope->frame_duration = GST_CLOCK_TIME_NONE;
125
126   /* reset the initial audio state */
127   scope->rate = GST_AUDIO_DEF_RATE;
128   scope->channels = 2;
129
130   scope->next_ts = GST_CLOCK_TIME_NONE;
131
132 }
133
134 static void
135 gst_base_scope_dispose (GObject * object)
136 {
137   GstBaseScope *scope = GST_BASE_SCOPE (object);
138
139   if (scope->adapter) {
140     g_object_unref (scope->adapter);
141     scope->adapter = NULL;
142   }
143   if (scope->inbuf) {
144     gst_buffer_unref (scope->inbuf);
145     scope->inbuf = NULL;
146   }
147
148   G_OBJECT_CLASS (parent_class)->dispose (object);
149 }
150
151 static gboolean
152 gst_base_scope_sink_setcaps (GstPad * pad, GstCaps * caps)
153 {
154   GstBaseScope *scope;
155   GstStructure *structure;
156   gint channels;
157   gint rate;
158   gboolean res = TRUE;
159
160   scope = GST_BASE_SCOPE (gst_pad_get_parent (pad));
161   structure = gst_caps_get_structure (caps, 0);
162
163   if (!gst_structure_get_int (structure, "channels", &channels) ||
164       !gst_structure_get_int (structure, "rate", &rate))
165     goto missing_caps_details;
166
167   if (channels != 2)
168     goto wrong_channels;
169
170   if (rate <= 0)
171     goto wrong_rate;
172
173   scope->channels = channels;
174   scope->rate = rate;
175
176   GST_DEBUG_OBJECT (scope, "audio: channels %d, rate %d",
177       scope->channels, scope->rate);
178
179 done:
180   gst_object_unref (scope);
181   return res;
182
183   /* Errors */
184 missing_caps_details:
185   {
186     GST_WARNING_OBJECT (scope, "missing channels or rate in the caps");
187     res = FALSE;
188     goto done;
189   }
190 wrong_channels:
191   {
192     GST_WARNING_OBJECT (scope, "number of channels must be 2, but is %d",
193         channels);
194     res = FALSE;
195     goto done;
196   }
197 wrong_rate:
198   {
199     GST_WARNING_OBJECT (scope, "sample rate must be >0, but is %d", rate);
200     res = FALSE;
201     goto done;
202   }
203 }
204
205 static gboolean
206 gst_base_scope_src_negotiate (GstBaseScope * scope)
207 {
208   GstCaps *othercaps, *target, *intersect;
209   GstStructure *structure;
210   const GstCaps *templ;
211
212   templ = gst_pad_get_pad_template_caps (scope->srcpad);
213
214   GST_DEBUG_OBJECT (scope, "performing negotiation");
215
216   /* see what the peer can do */
217   othercaps = gst_pad_peer_get_caps (scope->srcpad);
218   if (othercaps) {
219     intersect = gst_caps_intersect (othercaps, templ);
220     gst_caps_unref (othercaps);
221
222     if (gst_caps_is_empty (intersect))
223       goto no_format;
224
225     target = gst_caps_copy_nth (intersect, 0);
226     gst_caps_unref (intersect);
227   } else {
228     target = gst_caps_ref ((GstCaps *) templ);
229   }
230
231   structure = gst_caps_get_structure (target, 0);
232   gst_structure_fixate_field_nearest_int (structure, "width", scope->width);
233   gst_structure_fixate_field_nearest_int (structure, "height", scope->height);
234   gst_structure_fixate_field_nearest_fraction (structure, "framerate",
235       scope->fps_n, scope->fps_d);
236
237   GST_DEBUG_OBJECT (scope, "final caps are %" GST_PTR_FORMAT, target);
238
239   gst_pad_set_caps (scope->srcpad, target);
240   gst_caps_unref (target);
241
242   return TRUE;
243
244 no_format:
245   {
246     gst_caps_unref (intersect);
247     return FALSE;
248   }
249 }
250
251 static gboolean
252 gst_base_scope_src_setcaps (GstPad * pad, GstCaps * caps)
253 {
254   GstBaseScope *scope;
255   GstBaseScopeClass *klass;
256   gint w, h;
257   gint num, denom;
258   GstVideoFormat format;
259   gboolean res = TRUE;
260
261   scope = GST_BASE_SCOPE (gst_pad_get_parent (pad));
262   klass = GST_BASE_SCOPE_CLASS (G_OBJECT_GET_CLASS (scope));
263
264   if (!gst_video_format_parse_caps (caps, &format, &w, &h)) {
265     goto missing_caps_details;
266   }
267   if (!gst_video_parse_caps_framerate (caps, &num, &denom)) {
268     goto missing_caps_details;
269   }
270
271   scope->width = w;
272   scope->height = h;
273   scope->fps_n = num;
274   scope->fps_d = denom;
275   scope->video_format = format;
276
277   scope->frame_duration = gst_util_uint64_scale_int (GST_SECOND,
278       scope->fps_d, scope->fps_n);
279   scope->spf = gst_util_uint64_scale_int (scope->rate,
280       scope->fps_d, scope->fps_n);
281
282   if (klass->setup)
283     res = klass->setup (scope);
284
285   GST_DEBUG_OBJECT (scope, "video: dimension %dx%d, framerate %d/%d, spf %d",
286       scope->width, scope->height, scope->fps_n, scope->fps_d, scope->spf);
287
288 done:
289   gst_object_unref (scope);
290   return res;
291
292   /* Errors */
293 missing_caps_details:
294   {
295     GST_WARNING_OBJECT (scope,
296         "missing width, height or framerate in the caps");
297     res = FALSE;
298     goto done;
299   }
300 }
301
302 static GstFlowReturn
303 gst_base_scope_chain (GstPad * pad, GstBuffer * buffer)
304 {
305   GstFlowReturn ret = GST_FLOW_OK;
306   GstBaseScope *scope;
307   GstBaseScopeClass *klass;
308   GstBuffer *inbuf;
309   guint32 avail, bytesperread;
310   guint bpp;
311   gboolean (*render) (GstBaseScope * scope, GstBuffer * audio,
312       GstBuffer * video);
313
314   scope = GST_BASE_SCOPE (gst_pad_get_parent (pad));
315   klass = GST_BASE_SCOPE_CLASS (G_OBJECT_GET_CLASS (scope));
316
317   render = klass->render;
318
319   GST_LOG_OBJECT (scope, "chainfunc called");
320
321   /* resync on DISCONT */
322   if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
323     scope->next_ts = GST_CLOCK_TIME_NONE;
324     gst_adapter_clear (scope->adapter);
325   }
326
327   if (GST_PAD_CAPS (scope->srcpad) == NULL) {
328     if (!gst_base_scope_src_negotiate (scope))
329       return GST_FLOW_NOT_NEGOTIATED;
330   }
331
332   /* Match timestamps from the incoming audio */
333   if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
334     scope->next_ts = GST_BUFFER_TIMESTAMP (buffer);
335
336   gst_adapter_push (scope->adapter, buffer);
337
338   /* this is what we want */
339   bytesperread = scope->spf * scope->channels * sizeof (gint16);
340
341   bpp = gst_video_format_get_pixel_stride (scope->video_format, 0);
342
343   inbuf = scope->inbuf;
344   /* FIXME: the timestamp in the adapter would be different */
345   gst_buffer_copy_metadata (inbuf, buffer, GST_BUFFER_COPY_ALL);
346
347   /* this is what we have */
348   avail = gst_adapter_available (scope->adapter);
349   while (avail > bytesperread) {
350     GstBuffer *outbuf;
351
352     ret = gst_pad_alloc_buffer_and_set_caps (scope->srcpad,
353         GST_BUFFER_OFFSET_NONE,
354         scope->width * scope->height * bpp,
355         GST_PAD_CAPS (scope->srcpad), &outbuf);
356
357     /* no buffer allocated, we don't care why. */
358     if (ret != GST_FLOW_OK)
359       break;
360
361     GST_BUFFER_TIMESTAMP (outbuf) = scope->next_ts;
362     GST_BUFFER_DURATION (outbuf) = scope->frame_duration;
363     memset (GST_BUFFER_DATA (outbuf), 0, GST_BUFFER_SIZE (outbuf));
364
365     GST_BUFFER_DATA (inbuf) =
366         (guint8 *) gst_adapter_peek (scope->adapter, bytesperread);
367     GST_BUFFER_SIZE (inbuf) = bytesperread;
368
369     /* call class->render() vmethod */
370     if (render)
371       if (!render (scope, inbuf, outbuf)) {
372         ret = GST_FLOW_ERROR;
373       }
374
375     ret = gst_pad_push (scope->srcpad, outbuf);
376     outbuf = NULL;
377
378     /* FIXME: we want to ev. take less
379      * we need to align the audio-rate, video-rate and blocksize for render
380      */
381     gst_adapter_flush (scope->adapter, bytesperread);
382
383     if (ret != GST_FLOW_OK)
384       break;
385
386     if (scope->next_ts != GST_CLOCK_TIME_NONE)
387       scope->next_ts += scope->frame_duration;
388
389     avail = gst_adapter_available (scope->adapter);
390   }
391
392   gst_object_unref (scope);
393
394   return ret;
395 }
396
397 static GstStateChangeReturn
398 gst_base_scope_change_state (GstElement * element, GstStateChange transition)
399 {
400   GstBaseScope *scope;
401
402   scope = GST_BASE_SCOPE (element);
403
404   switch (transition) {
405     case GST_STATE_CHANGE_READY_TO_PAUSED:
406       scope->next_ts = GST_CLOCK_TIME_NONE;
407       gst_adapter_clear (scope->adapter);
408       break;
409     default:
410       break;
411   }
412
413   return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
414 }