rtptwcc: don't map the buffer twice
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-good / gst / monoscope / gstmonoscope.c
1 /* gstmonoscope.c: implementation of monoscope drawing element
2  * Copyright (C) <2002> Richard Boulton <richard@tartarus.org>
3  * Copyright (C) <2006> Tim-Philipp Müller <tim centricular net>
4  * Copyright (C) <2006> Wim Taymans <wim at fluendo dot com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 /**
23  * SECTION:element-monoscope
24  * @title: monoscope
25  * @see_also: goom
26  *
27  * Monoscope is an audio visualisation element. It creates a coloured
28  * curve of the audio signal like on an oscilloscope.
29  *
30  * ## Example launch line
31  * |[
32  * gst-launch-1.0 -v audiotestsrc ! audioconvert ! monoscope ! videoconvert ! ximagesink
33  * ]|
34  *
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <gst/video/video.h>
42 #include <gst/audio/audio.h>
43 #include <string.h>
44 #include "gstmonoscope.h"
45 #include "monoscope.h"
46
47 GST_DEBUG_CATEGORY_STATIC (monoscope_debug);
48 #define GST_CAT_DEFAULT monoscope_debug
49
50 #if G_BYTE_ORDER == G_BIG_ENDIAN
51 #define RGB_ORDER "xRGB"
52 #else
53 #define RGB_ORDER "BGRx"
54 #endif
55
56 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
59     GST_STATIC_CAPS ("video/x-raw, "
60         "format = (string) " RGB_ORDER ", "
61         "width = " G_STRINGIFY (scope_width) ", "
62         "height = " G_STRINGIFY (scope_height) ", "
63         "framerate = " GST_VIDEO_FPS_RANGE)
64     );
65
66 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
67     GST_PAD_SINK,
68     GST_PAD_ALWAYS,
69     GST_STATIC_CAPS ("audio/x-raw, "
70         "format = (string) " GST_AUDIO_NE (S16) ", "
71         "rate = (int) [ 8000, 96000 ], "
72         "channels = (int) 1, " "layout = (string) interleaved")
73     );
74
75
76 #define gst_monoscope_parent_class parent_class
77 G_DEFINE_TYPE (GstMonoscope, gst_monoscope, GST_TYPE_ELEMENT);
78 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (monoscope, "monoscope",
79     GST_RANK_NONE, GST_TYPE_MONOSCOPE,
80     GST_DEBUG_CATEGORY_INIT (monoscope_debug, "monoscope", 0,
81         "monoscope element"););
82
83 static void gst_monoscope_finalize (GObject * object);
84 static GstFlowReturn gst_monoscope_chain (GstPad * pad, GstObject * parent,
85     GstBuffer * buf);
86 static gboolean gst_monoscope_src_setcaps (GstMonoscope * mono, GstCaps * caps);
87 static gboolean gst_monoscope_sink_setcaps (GstMonoscope * mono,
88     GstCaps * caps);
89 static void gst_monoscope_reset (GstMonoscope * monoscope);
90 static gboolean gst_monoscope_sink_event (GstPad * pad, GstObject * parent,
91     GstEvent * event);
92 static gboolean gst_monoscope_src_event (GstPad * pad, GstObject * parent,
93     GstEvent * event);
94 static GstStateChangeReturn gst_monoscope_change_state (GstElement * element,
95     GstStateChange transition);
96
97 static void
98 gst_monoscope_class_init (GstMonoscopeClass * klass)
99 {
100   GObjectClass *gobject_class;
101   GstElementClass *gstelement_class;
102
103   gobject_class = (GObjectClass *) klass;
104   gstelement_class = (GstElementClass *) klass;
105
106   gobject_class->finalize = gst_monoscope_finalize;
107
108   gstelement_class->change_state =
109       GST_DEBUG_FUNCPTR (gst_monoscope_change_state);
110
111   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
112   gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
113   gst_element_class_set_static_metadata (gstelement_class, "Monoscope",
114       "Visualization",
115       "Displays a highly stabilised waveform of audio input",
116       "Richard Boulton <richard@tartarus.org>");
117 }
118
119 static void
120 gst_monoscope_init (GstMonoscope * monoscope)
121 {
122   monoscope->sinkpad =
123       gst_pad_new_from_static_template (&sink_template, "sink");
124   gst_pad_set_chain_function (monoscope->sinkpad,
125       GST_DEBUG_FUNCPTR (gst_monoscope_chain));
126   gst_pad_set_event_function (monoscope->sinkpad,
127       GST_DEBUG_FUNCPTR (gst_monoscope_sink_event));
128   gst_element_add_pad (GST_ELEMENT (monoscope), monoscope->sinkpad);
129
130   monoscope->srcpad = gst_pad_new_from_static_template (&src_template, "src");
131   gst_pad_set_event_function (monoscope->srcpad,
132       GST_DEBUG_FUNCPTR (gst_monoscope_src_event));
133   gst_element_add_pad (GST_ELEMENT (monoscope), monoscope->srcpad);
134
135   monoscope->adapter = gst_adapter_new ();
136   monoscope->next_ts = GST_CLOCK_TIME_NONE;
137   monoscope->bps = sizeof (gint16);
138
139   /* reset the initial video state */
140   monoscope->width = scope_width;
141   monoscope->height = scope_height;
142   monoscope->fps_num = 25;      /* desired frame rate */
143   monoscope->fps_denom = 1;
144   monoscope->visstate = NULL;
145
146   /* reset the initial audio state */
147   monoscope->rate = GST_AUDIO_DEF_RATE;
148 }
149
150 static void
151 gst_monoscope_finalize (GObject * object)
152 {
153   GstMonoscope *monoscope = GST_MONOSCOPE (object);
154
155   if (monoscope->visstate)
156     monoscope_close (monoscope->visstate);
157
158   g_object_unref (monoscope->adapter);
159
160   G_OBJECT_CLASS (parent_class)->finalize (object);
161 }
162
163 static void
164 gst_monoscope_reset (GstMonoscope * monoscope)
165 {
166   monoscope->next_ts = GST_CLOCK_TIME_NONE;
167
168   gst_adapter_clear (monoscope->adapter);
169   gst_segment_init (&monoscope->segment, GST_FORMAT_UNDEFINED);
170   monoscope->segment_pending = FALSE;
171
172   GST_OBJECT_LOCK (monoscope);
173   monoscope->proportion = 1.0;
174   monoscope->earliest_time = -1;
175   GST_OBJECT_UNLOCK (monoscope);
176 }
177
178 static gboolean
179 gst_monoscope_sink_setcaps (GstMonoscope * monoscope, GstCaps * caps)
180 {
181   GstStructure *structure;
182
183   structure = gst_caps_get_structure (caps, 0);
184
185   gst_structure_get_int (structure, "rate", &monoscope->rate);
186
187   GST_DEBUG_OBJECT (monoscope, "sample rate = %d", monoscope->rate);
188   return TRUE;
189 }
190
191 static gboolean
192 gst_monoscope_src_setcaps (GstMonoscope * monoscope, GstCaps * caps)
193 {
194   GstStructure *structure;
195   gboolean res;
196
197   structure = gst_caps_get_structure (caps, 0);
198
199   gst_structure_get_int (structure, "width", &monoscope->width);
200   gst_structure_get_int (structure, "height", &monoscope->height);
201   gst_structure_get_fraction (structure, "framerate", &monoscope->fps_num,
202       &monoscope->fps_denom);
203
204   monoscope->outsize = monoscope->width * monoscope->height * 4;
205   monoscope->frame_duration = gst_util_uint64_scale_int (GST_SECOND,
206       monoscope->fps_denom, monoscope->fps_num);
207   monoscope->spf =
208       gst_util_uint64_scale_int (monoscope->rate, monoscope->fps_denom,
209       monoscope->fps_num);
210
211   GST_DEBUG_OBJECT (monoscope, "dimension %dx%d, framerate %d/%d, spf %d",
212       monoscope->width, monoscope->height, monoscope->fps_num,
213       monoscope->fps_denom, monoscope->spf);
214
215   if (monoscope->visstate) {
216     monoscope_close (monoscope->visstate);
217     monoscope->visstate = NULL;
218   }
219
220   monoscope->visstate = monoscope_init (monoscope->width, monoscope->height);
221
222   res = gst_pad_set_caps (monoscope->srcpad, caps);
223
224   return res && (monoscope->visstate != NULL);
225 }
226
227 static gboolean
228 gst_monoscope_src_negotiate (GstMonoscope * monoscope)
229 {
230   GstCaps *othercaps, *target;
231   GstStructure *structure;
232   GstCaps *templ;
233   GstQuery *query;
234   GstBufferPool *pool;
235   GstStructure *config;
236   guint size, min, max;
237
238   templ = gst_pad_get_pad_template_caps (monoscope->srcpad);
239
240   GST_DEBUG_OBJECT (monoscope, "performing negotiation");
241
242   /* see what the peer can do */
243   othercaps = gst_pad_peer_query_caps (monoscope->srcpad, NULL);
244   if (othercaps) {
245     target = gst_caps_intersect (othercaps, templ);
246     gst_caps_unref (othercaps);
247     gst_caps_unref (templ);
248
249     if (gst_caps_is_empty (target))
250       goto no_format;
251
252     target = gst_caps_truncate (target);
253   } else {
254     target = templ;
255   }
256
257   target = gst_caps_make_writable (target);
258   structure = gst_caps_get_structure (target, 0);
259   gst_structure_fixate_field_nearest_int (structure, "width", 320);
260   gst_structure_fixate_field_nearest_int (structure, "height", 240);
261   gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1);
262   if (gst_structure_has_field (structure, "pixel-aspect-ratio"))
263     gst_structure_fixate_field_nearest_fraction (structure,
264         "pixel-aspect-ratio", 1, 1);
265   target = gst_caps_fixate (target);
266
267   gst_monoscope_src_setcaps (monoscope, target);
268
269   /* try to get a bufferpool now */
270   /* find a pool for the negotiated caps now */
271   query = gst_query_new_allocation (target, TRUE);
272
273   if (!gst_pad_peer_query (monoscope->srcpad, query)) {
274   }
275
276   if (gst_query_get_n_allocation_pools (query) > 0) {
277     /* we got configuration from our peer, parse them */
278     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
279   } else {
280     pool = NULL;
281     size = monoscope->outsize;
282     min = max = 0;
283   }
284
285   if (pool == NULL) {
286     /* we did not get a pool, make one ourselves then */
287     pool = gst_buffer_pool_new ();
288   }
289
290   config = gst_buffer_pool_get_config (pool);
291   gst_buffer_pool_config_set_params (config, target, size, min, max);
292   gst_buffer_pool_set_config (pool, config);
293
294   if (monoscope->pool) {
295     gst_buffer_pool_set_active (monoscope->pool, TRUE);
296     gst_object_unref (monoscope->pool);
297   }
298   monoscope->pool = pool;
299
300   /* and activate */
301   gst_buffer_pool_set_active (pool, TRUE);
302
303   gst_query_unref (query);
304   gst_caps_unref (target);
305
306   return TRUE;
307
308 no_format:
309   {
310     gst_caps_unref (target);
311     return FALSE;
312   }
313 }
314
315 /* make sure we are negotiated */
316 static GstFlowReturn
317 ensure_negotiated (GstMonoscope * monoscope)
318 {
319   gboolean reconfigure;
320
321   reconfigure = gst_pad_check_reconfigure (monoscope->srcpad);
322
323   /* we don't know an output format yet, pick one */
324   if (reconfigure || !gst_pad_has_current_caps (monoscope->srcpad)) {
325     if (!gst_monoscope_src_negotiate (monoscope)) {
326       gst_pad_mark_reconfigure (monoscope->srcpad);
327       if (GST_PAD_IS_FLUSHING (monoscope->srcpad))
328         return GST_FLOW_FLUSHING;
329       else
330         return GST_FLOW_NOT_NEGOTIATED;
331     }
332   }
333   return GST_FLOW_OK;
334 }
335
336 static GstFlowReturn
337 gst_monoscope_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
338 {
339   GstFlowReturn flow_ret = GST_FLOW_OK;
340   GstMonoscope *monoscope;
341
342   monoscope = GST_MONOSCOPE (parent);
343
344   if (monoscope->rate == 0) {
345     gst_buffer_unref (inbuf);
346     flow_ret = GST_FLOW_NOT_NEGOTIATED;
347     goto out;
348   }
349
350   /* Make sure have an output format */
351   flow_ret = ensure_negotiated (monoscope);
352   if (flow_ret != GST_FLOW_OK) {
353     gst_buffer_unref (inbuf);
354     goto out;
355   }
356
357   if (monoscope->segment_pending) {
358     gst_pad_push_event (monoscope->srcpad,
359         gst_event_new_segment (&monoscope->segment));
360     monoscope->segment_pending = FALSE;
361   }
362
363   /* don't try to combine samples from discont buffer */
364   if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
365     gst_adapter_clear (monoscope->adapter);
366     monoscope->next_ts = GST_CLOCK_TIME_NONE;
367   }
368
369   /* Match timestamps from the incoming audio */
370   if (GST_BUFFER_TIMESTAMP (inbuf) != GST_CLOCK_TIME_NONE)
371     monoscope->next_ts = GST_BUFFER_TIMESTAMP (inbuf);
372
373   GST_LOG_OBJECT (monoscope,
374       "in buffer has %" G_GSIZE_FORMAT " samples, ts=%" GST_TIME_FORMAT,
375       gst_buffer_get_size (inbuf) / monoscope->bps,
376       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));
377
378   gst_adapter_push (monoscope->adapter, inbuf);
379   inbuf = NULL;
380
381   /* Collect samples until we have enough for an output frame */
382   while (flow_ret == GST_FLOW_OK) {
383     gint16 *samples;
384     GstBuffer *outbuf = NULL;
385     guint32 *pixels, avail, bytesperframe;
386
387     avail = gst_adapter_available (monoscope->adapter);
388     GST_LOG_OBJECT (monoscope, "bytes avail now %u", avail);
389
390     bytesperframe = monoscope->spf * monoscope->bps;
391     if (avail < bytesperframe)
392       break;
393
394     /* FIXME: something is wrong with QoS, we are skipping way too much
395      * stuff even with very low CPU loads */
396 #if 0
397     if (monoscope->next_ts != -1) {
398       gboolean need_skip;
399       gint64 qostime;
400
401       qostime = gst_segment_to_running_time (&monoscope->segment,
402           GST_FORMAT_TIME, monoscope->next_ts);
403
404       GST_OBJECT_LOCK (monoscope);
405       /* check for QoS, don't compute buffers that are known to be late */
406       need_skip =
407           GST_CLOCK_TIME_IS_VALID (monoscope->earliest_time) &&
408           qostime <= monoscope->earliest_time;
409       GST_OBJECT_UNLOCK (monoscope);
410
411       if (need_skip) {
412         GST_WARNING_OBJECT (monoscope,
413             "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
414             GST_TIME_ARGS (qostime), GST_TIME_ARGS (monoscope->earliest_time));
415         goto skip;
416       }
417     }
418 #endif
419
420     samples = (gint16 *) gst_adapter_map (monoscope->adapter, bytesperframe);
421
422     if (monoscope->spf < convolver_big) {
423       gint16 in_data[convolver_big], i;
424       gdouble scale = (gdouble) monoscope->spf / (gdouble) convolver_big;
425
426       for (i = 0; i < convolver_big; ++i) {
427         gdouble off = (gdouble) i * scale;
428         in_data[i] = samples[MIN ((guint) off, monoscope->spf)];
429       }
430       pixels = monoscope_update (monoscope->visstate, in_data);
431     } else {
432       /* not really correct, but looks much prettier */
433       pixels = monoscope_update (monoscope->visstate, samples);
434     }
435
436     GST_LOG_OBJECT (monoscope, "allocating output buffer");
437     flow_ret = gst_buffer_pool_acquire_buffer (monoscope->pool, &outbuf, NULL);
438     if (flow_ret != GST_FLOW_OK) {
439       gst_adapter_unmap (monoscope->adapter);
440       goto out;
441     }
442
443     gst_buffer_fill (outbuf, 0, pixels, monoscope->outsize);
444
445     GST_BUFFER_TIMESTAMP (outbuf) = monoscope->next_ts;
446     GST_BUFFER_DURATION (outbuf) = monoscope->frame_duration;
447
448     flow_ret = gst_pad_push (monoscope->srcpad, outbuf);
449
450 #if 0
451   skip:
452 #endif
453
454     if (GST_CLOCK_TIME_IS_VALID (monoscope->next_ts))
455       monoscope->next_ts += monoscope->frame_duration;
456
457     gst_adapter_flush (monoscope->adapter, bytesperframe);
458   }
459
460 out:
461
462   return flow_ret;
463 }
464
465 static gboolean
466 gst_monoscope_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
467 {
468   GstMonoscope *monoscope;
469   gboolean res;
470
471   monoscope = GST_MONOSCOPE (parent);
472
473   switch (GST_EVENT_TYPE (event)) {
474     case GST_EVENT_FLUSH_START:
475       res = gst_pad_push_event (monoscope->srcpad, event);
476       break;
477     case GST_EVENT_FLUSH_STOP:
478       gst_monoscope_reset (monoscope);
479       res = gst_pad_push_event (monoscope->srcpad, event);
480       break;
481     case GST_EVENT_SEGMENT:
482     {
483       /* the newsegment values are used to clip the input samples
484        * and to convert the incoming timestamps to running time so
485        * we can do QoS */
486       gst_event_copy_segment (event, &monoscope->segment);
487
488       /* We forward the event from the chain function after caps are
489        * negotiated. Otherwise we would potentially break the event order and
490        * send the segment event before the caps event */
491       monoscope->segment_pending = TRUE;
492       gst_event_unref (event);
493       res = TRUE;
494       break;
495     }
496     case GST_EVENT_CAPS:
497     {
498       GstCaps *caps;
499
500       gst_event_parse_caps (event, &caps);
501       gst_monoscope_sink_setcaps (monoscope, caps);
502       gst_event_unref (event);
503       res = TRUE;
504       break;
505     }
506     default:
507       res = gst_pad_push_event (monoscope->srcpad, event);
508       break;
509   }
510
511   return res;
512 }
513
514 static gboolean
515 gst_monoscope_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
516 {
517   GstMonoscope *monoscope;
518   gboolean res;
519
520   monoscope = GST_MONOSCOPE (parent);
521
522   switch (GST_EVENT_TYPE (event)) {
523     case GST_EVENT_QOS:{
524       gdouble proportion;
525       GstClockTimeDiff diff;
526       GstClockTime timestamp;
527
528       gst_event_parse_qos (event, NULL, &proportion, &diff, &timestamp);
529
530       /* save stuff for the _chain() function */
531       GST_OBJECT_LOCK (monoscope);
532       monoscope->proportion = proportion;
533       if (diff >= 0)
534         /* we're late, this is a good estimate for next displayable
535          * frame (see part-qos.txt) */
536         monoscope->earliest_time =
537             timestamp + 2 * diff + monoscope->frame_duration;
538       else
539         monoscope->earliest_time = timestamp + diff;
540       GST_OBJECT_UNLOCK (monoscope);
541
542       res = gst_pad_push_event (monoscope->sinkpad, event);
543       break;
544     }
545     default:
546       res = gst_pad_push_event (monoscope->sinkpad, event);
547       break;
548   }
549
550   return res;
551 }
552
553 static GstStateChangeReturn
554 gst_monoscope_change_state (GstElement * element, GstStateChange transition)
555 {
556   GstMonoscope *monoscope = GST_MONOSCOPE (element);
557   GstStateChangeReturn ret;
558
559   switch (transition) {
560     case GST_STATE_CHANGE_NULL_TO_READY:
561       break;
562     case GST_STATE_CHANGE_READY_TO_PAUSED:
563       gst_monoscope_reset (monoscope);
564       break;
565     default:
566       break;
567   }
568
569   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
570
571   switch (transition) {
572     case GST_STATE_CHANGE_PAUSED_TO_READY:
573       if (monoscope->pool) {
574         gst_buffer_pool_set_active (monoscope->pool, FALSE);
575         gst_object_replace ((GstObject **) & monoscope->pool, NULL);
576       }
577       break;
578     case GST_STATE_CHANGE_READY_TO_NULL:
579       break;
580     default:
581       break;
582   }
583
584   return ret;
585 }
586
587 static gboolean
588 plugin_init (GstPlugin * plugin)
589 {
590   return GST_ELEMENT_REGISTER (monoscope, plugin);
591
592 }
593
594 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
595     GST_VERSION_MINOR,
596     monoscope,
597     "Monoscope visualization",
598     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);