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