• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2011 David A. Schleef <ds@schleef.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
17  * Boston, MA 02110-1335, USA.
18  */
19 /**
20  * SECTION:element-gstinteraudiosink
21  * @title: gstinteraudiosink
22  *
23  * The interaudiosink element is an audio sink element.  It is used
24  * in connection with a interaudiosrc element in a different pipeline,
25  * similar to intervideosink and intervideosrc.
26  *
27  * ## Example launch line
28  * |[
29  * gst-launch-1.0 -v audiotestsrc ! queue ! interaudiosink
30  * ]|
31  *
32  * The interaudiosink element cannot be used effectively with gst-launch-1.0,
33  * as it requires a second pipeline in the application to receive the
34  * audio.
35  * See the gstintertest.c example in the gst-plugins-bad source code for
36  * more details.
37  *
38  */
39 
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43 
44 #include <gst/gst.h>
45 #include <gst/base/gstbasesink.h>
46 #include <gst/audio/audio.h>
47 #include "gstinteraudiosink.h"
48 #include <string.h>
49 
50 GST_DEBUG_CATEGORY_STATIC (gst_inter_audio_sink_debug_category);
51 #define GST_CAT_DEFAULT gst_inter_audio_sink_debug_category
52 
53 /* prototypes */
54 static void gst_inter_audio_sink_set_property (GObject * object,
55     guint property_id, const GValue * value, GParamSpec * pspec);
56 static void gst_inter_audio_sink_get_property (GObject * object,
57     guint property_id, GValue * value, GParamSpec * pspec);
58 static void gst_inter_audio_sink_finalize (GObject * object);
59 
60 static void gst_inter_audio_sink_get_times (GstBaseSink * sink,
61     GstBuffer * buffer, GstClockTime * start, GstClockTime * end);
62 static gboolean gst_inter_audio_sink_start (GstBaseSink * sink);
63 static gboolean gst_inter_audio_sink_stop (GstBaseSink * sink);
64 static gboolean gst_inter_audio_sink_set_caps (GstBaseSink * sink,
65     GstCaps * caps);
66 static gboolean gst_inter_audio_sink_event (GstBaseSink * sink,
67     GstEvent * event);
68 static GstFlowReturn gst_inter_audio_sink_render (GstBaseSink * sink,
69     GstBuffer * buffer);
70 static gboolean gst_inter_audio_sink_query (GstBaseSink * sink,
71     GstQuery * query);
72 
73 enum
74 {
75   PROP_0,
76   PROP_CHANNEL
77 };
78 
79 #define DEFAULT_CHANNEL ("default")
80 
81 /* pad templates */
82 static GstStaticPadTemplate gst_inter_audio_sink_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
87     );
88 
89 /* class initialization */
90 #define parent_class gst_inter_audio_sink_parent_class
91 G_DEFINE_TYPE (GstInterAudioSink, gst_inter_audio_sink, GST_TYPE_BASE_SINK);
92 
93 static void
gst_inter_audio_sink_class_init(GstInterAudioSinkClass * klass)94 gst_inter_audio_sink_class_init (GstInterAudioSinkClass * klass)
95 {
96   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
98   GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
99 
100   GST_DEBUG_CATEGORY_INIT (gst_inter_audio_sink_debug_category,
101       "interaudiosink", 0, "debug category for interaudiosink element");
102   gst_element_class_add_static_pad_template (element_class,
103       &gst_inter_audio_sink_sink_template);
104 
105   gst_element_class_set_static_metadata (element_class,
106       "Internal audio sink",
107       "Sink/Audio",
108       "Virtual audio sink for internal process communication",
109       "David Schleef <ds@schleef.org>");
110 
111   gobject_class->set_property = gst_inter_audio_sink_set_property;
112   gobject_class->get_property = gst_inter_audio_sink_get_property;
113   gobject_class->finalize = gst_inter_audio_sink_finalize;
114   base_sink_class->get_times =
115       GST_DEBUG_FUNCPTR (gst_inter_audio_sink_get_times);
116   base_sink_class->start = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_start);
117   base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_stop);
118   base_sink_class->event = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_event);
119   base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_set_caps);
120   base_sink_class->render = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_render);
121   base_sink_class->query = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_query);
122 
123   g_object_class_install_property (gobject_class, PROP_CHANNEL,
124       g_param_spec_string ("channel", "Channel",
125           "Channel name to match inter src and sink elements",
126           DEFAULT_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
127 }
128 
129 static void
gst_inter_audio_sink_init(GstInterAudioSink * interaudiosink)130 gst_inter_audio_sink_init (GstInterAudioSink * interaudiosink)
131 {
132   interaudiosink->channel = g_strdup (DEFAULT_CHANNEL);
133   interaudiosink->input_adapter = gst_adapter_new ();
134 }
135 
136 void
gst_inter_audio_sink_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)137 gst_inter_audio_sink_set_property (GObject * object, guint property_id,
138     const GValue * value, GParamSpec * pspec)
139 {
140   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
141 
142   switch (property_id) {
143     case PROP_CHANNEL:
144       g_free (interaudiosink->channel);
145       interaudiosink->channel = g_value_dup_string (value);
146       break;
147     default:
148       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
149       break;
150   }
151 }
152 
153 void
gst_inter_audio_sink_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)154 gst_inter_audio_sink_get_property (GObject * object, guint property_id,
155     GValue * value, GParamSpec * pspec)
156 {
157   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
158 
159   switch (property_id) {
160     case PROP_CHANNEL:
161       g_value_set_string (value, interaudiosink->channel);
162       break;
163     default:
164       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
165       break;
166   }
167 }
168 
169 void
gst_inter_audio_sink_finalize(GObject * object)170 gst_inter_audio_sink_finalize (GObject * object)
171 {
172   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
173 
174   /* clean up object here */
175   g_free (interaudiosink->channel);
176   gst_object_unref (interaudiosink->input_adapter);
177 
178   G_OBJECT_CLASS (gst_inter_audio_sink_parent_class)->finalize (object);
179 }
180 
181 static void
gst_inter_audio_sink_get_times(GstBaseSink * sink,GstBuffer * buffer,GstClockTime * start,GstClockTime * end)182 gst_inter_audio_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
183     GstClockTime * start, GstClockTime * end)
184 {
185   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
186 
187   if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
188     *start = GST_BUFFER_TIMESTAMP (buffer);
189     if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
190       *end = *start + GST_BUFFER_DURATION (buffer);
191     } else {
192       if (interaudiosink->info.rate > 0) {
193         *end = *start +
194             gst_util_uint64_scale_int (gst_buffer_get_size (buffer), GST_SECOND,
195             interaudiosink->info.rate * interaudiosink->info.bpf);
196       }
197     }
198   }
199 }
200 
201 static gboolean
gst_inter_audio_sink_start(GstBaseSink * sink)202 gst_inter_audio_sink_start (GstBaseSink * sink)
203 {
204   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
205 
206   GST_DEBUG_OBJECT (interaudiosink, "start");
207 
208   interaudiosink->surface = gst_inter_surface_get (interaudiosink->channel);
209   g_mutex_lock (&interaudiosink->surface->mutex);
210   memset (&interaudiosink->surface->audio_info, 0, sizeof (GstAudioInfo));
211 
212   /* We want to write latency-time before syncing has happened */
213   /* FIXME: The other side can change this value when it starts */
214   gst_base_sink_set_render_delay (sink,
215       interaudiosink->surface->audio_latency_time);
216   g_mutex_unlock (&interaudiosink->surface->mutex);
217 
218   return TRUE;
219 }
220 
221 static gboolean
gst_inter_audio_sink_stop(GstBaseSink * sink)222 gst_inter_audio_sink_stop (GstBaseSink * sink)
223 {
224   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
225 
226   GST_DEBUG_OBJECT (interaudiosink, "stop");
227 
228   g_mutex_lock (&interaudiosink->surface->mutex);
229   gst_adapter_clear (interaudiosink->surface->audio_adapter);
230   memset (&interaudiosink->surface->audio_info, 0, sizeof (GstAudioInfo));
231   g_mutex_unlock (&interaudiosink->surface->mutex);
232 
233   gst_inter_surface_unref (interaudiosink->surface);
234   interaudiosink->surface = NULL;
235 
236   gst_adapter_clear (interaudiosink->input_adapter);
237 
238   return TRUE;
239 }
240 
241 static gboolean
gst_inter_audio_sink_set_caps(GstBaseSink * sink,GstCaps * caps)242 gst_inter_audio_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
243 {
244   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
245   GstAudioInfo info;
246 
247   if (!gst_audio_info_from_caps (&info, caps)) {
248     GST_ERROR_OBJECT (sink, "Failed to parse caps %" GST_PTR_FORMAT, caps);
249     return FALSE;
250   }
251 
252   g_mutex_lock (&interaudiosink->surface->mutex);
253   interaudiosink->surface->audio_info = info;
254   interaudiosink->info = info;
255   /* TODO: Ideally we would drain the source here */
256   gst_adapter_clear (interaudiosink->surface->audio_adapter);
257   g_mutex_unlock (&interaudiosink->surface->mutex);
258 
259   return TRUE;
260 }
261 
262 static gboolean
gst_inter_audio_sink_event(GstBaseSink * sink,GstEvent * event)263 gst_inter_audio_sink_event (GstBaseSink * sink, GstEvent * event)
264 {
265   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
266 
267   switch (GST_EVENT_TYPE (event)) {
268     case GST_EVENT_EOS:{
269       GstBuffer *tmp;
270       guint n;
271 
272       if ((n = gst_adapter_available (interaudiosink->input_adapter)) > 0) {
273         g_mutex_lock (&interaudiosink->surface->mutex);
274         tmp = gst_adapter_take_buffer (interaudiosink->input_adapter, n);
275         gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
276         g_mutex_unlock (&interaudiosink->surface->mutex);
277       }
278       break;
279     }
280     default:
281       break;
282   }
283 
284   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
285 }
286 
287 static GstFlowReturn
gst_inter_audio_sink_render(GstBaseSink * sink,GstBuffer * buffer)288 gst_inter_audio_sink_render (GstBaseSink * sink, GstBuffer * buffer)
289 {
290   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
291   guint n, bpf;
292   guint64 period_time, buffer_time;
293   guint64 period_samples, buffer_samples;
294 
295   GST_DEBUG_OBJECT (interaudiosink, "render %" G_GSIZE_FORMAT,
296       gst_buffer_get_size (buffer));
297   bpf = interaudiosink->info.bpf;
298 
299   g_mutex_lock (&interaudiosink->surface->mutex);
300 
301   buffer_time = interaudiosink->surface->audio_buffer_time;
302   period_time = interaudiosink->surface->audio_period_time;
303 
304   if (buffer_time < period_time) {
305     GST_ERROR_OBJECT (interaudiosink,
306         "Buffer time smaller than period time (%" GST_TIME_FORMAT " < %"
307         GST_TIME_FORMAT ")", GST_TIME_ARGS (buffer_time),
308         GST_TIME_ARGS (period_time));
309     g_mutex_unlock (&interaudiosink->surface->mutex);
310     return GST_FLOW_ERROR;
311   }
312 
313   buffer_samples =
314       gst_util_uint64_scale (buffer_time, interaudiosink->info.rate,
315       GST_SECOND);
316   period_samples =
317       gst_util_uint64_scale (period_time, interaudiosink->info.rate,
318       GST_SECOND);
319 
320   n = gst_adapter_available (interaudiosink->surface->audio_adapter) / bpf;
321   while (n > buffer_samples) {
322     GST_DEBUG_OBJECT (interaudiosink, "flushing %" GST_TIME_FORMAT,
323         GST_TIME_ARGS (period_time));
324     gst_adapter_flush (interaudiosink->surface->audio_adapter,
325         period_samples * bpf);
326     n -= period_samples;
327   }
328 
329   n = gst_adapter_available (interaudiosink->input_adapter);
330   if (period_samples * bpf > gst_buffer_get_size (buffer) + n) {
331     gst_adapter_push (interaudiosink->input_adapter, gst_buffer_ref (buffer));
332   } else {
333     GstBuffer *tmp;
334 
335     if (n > 0) {
336       tmp = gst_adapter_take_buffer (interaudiosink->input_adapter, n);
337       gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
338     }
339     gst_adapter_push (interaudiosink->surface->audio_adapter,
340         gst_buffer_ref (buffer));
341   }
342   g_mutex_unlock (&interaudiosink->surface->mutex);
343 
344   return GST_FLOW_OK;
345 }
346 
347 static gboolean
gst_inter_audio_sink_query(GstBaseSink * sink,GstQuery * query)348 gst_inter_audio_sink_query (GstBaseSink * sink, GstQuery * query)
349 {
350   GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
351   gboolean ret;
352 
353   GST_DEBUG_OBJECT (sink, "query");
354 
355   switch (GST_QUERY_TYPE (query)) {
356     case GST_QUERY_LATENCY:{
357       gboolean live, us_live;
358       GstClockTime min_l, max_l;
359 
360       GST_DEBUG_OBJECT (sink, "latency query");
361 
362       if ((ret =
363               gst_base_sink_query_latency (GST_BASE_SINK_CAST (sink), &live,
364                   &us_live, &min_l, &max_l))) {
365         GstClockTime base_latency, min_latency, max_latency;
366 
367         /* we and upstream are both live, adjust the min_latency */
368         if (live && us_live) {
369           /* FIXME: The other side can change this value when it starts */
370           base_latency = interaudiosink->surface->audio_latency_time;
371 
372           /* we cannot go lower than the buffer size and the min peer latency */
373           min_latency = base_latency + min_l;
374           /* the max latency is the max of the peer, we can delay an infinite
375            * amount of time. */
376           max_latency = (max_l == -1) ? -1 : (base_latency + max_l);
377 
378           GST_DEBUG_OBJECT (sink,
379               "peer min %" GST_TIME_FORMAT ", our min latency: %"
380               GST_TIME_FORMAT, GST_TIME_ARGS (min_l),
381               GST_TIME_ARGS (min_latency));
382           GST_DEBUG_OBJECT (sink,
383               "peer max %" GST_TIME_FORMAT ", our max latency: %"
384               GST_TIME_FORMAT, GST_TIME_ARGS (max_l),
385               GST_TIME_ARGS (max_latency));
386         } else {
387           GST_DEBUG_OBJECT (sink,
388               "peer or we are not live, don't care about latency");
389           min_latency = min_l;
390           max_latency = max_l;
391         }
392         gst_query_set_latency (query, live, min_latency, max_latency);
393       }
394       break;
395     }
396     default:
397       ret =
398           GST_BASE_SINK_CLASS (gst_inter_audio_sink_parent_class)->query (sink,
399           query);
400       break;
401   }
402 
403   return ret;
404 }
405