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-interaudiosink
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 GST_ELEMENT_REGISTER_DEFINE (interaudiosink, "interaudiosink",
93 GST_RANK_NONE, GST_TYPE_INTER_AUDIO_SINK);
94
95 static void
gst_inter_audio_sink_class_init(GstInterAudioSinkClass * klass)96 gst_inter_audio_sink_class_init (GstInterAudioSinkClass * klass)
97 {
98 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
99 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
100 GstBaseSinkClass *base_sink_class = GST_BASE_SINK_CLASS (klass);
101
102 GST_DEBUG_CATEGORY_INIT (gst_inter_audio_sink_debug_category,
103 "interaudiosink", 0, "debug category for interaudiosink element");
104 gst_element_class_add_static_pad_template (element_class,
105 &gst_inter_audio_sink_sink_template);
106
107 gst_element_class_set_static_metadata (element_class,
108 "Internal audio sink",
109 "Sink/Audio",
110 "Virtual audio sink for internal process communication",
111 "David Schleef <ds@schleef.org>");
112
113 gobject_class->set_property = gst_inter_audio_sink_set_property;
114 gobject_class->get_property = gst_inter_audio_sink_get_property;
115 gobject_class->finalize = gst_inter_audio_sink_finalize;
116 base_sink_class->get_times =
117 GST_DEBUG_FUNCPTR (gst_inter_audio_sink_get_times);
118 base_sink_class->start = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_start);
119 base_sink_class->stop = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_stop);
120 base_sink_class->event = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_event);
121 base_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_set_caps);
122 base_sink_class->render = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_render);
123 base_sink_class->query = GST_DEBUG_FUNCPTR (gst_inter_audio_sink_query);
124
125 g_object_class_install_property (gobject_class, PROP_CHANNEL,
126 g_param_spec_string ("channel", "Channel",
127 "Channel name to match inter src and sink elements",
128 DEFAULT_CHANNEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
129 }
130
131 static void
gst_inter_audio_sink_init(GstInterAudioSink * interaudiosink)132 gst_inter_audio_sink_init (GstInterAudioSink * interaudiosink)
133 {
134 interaudiosink->channel = g_strdup (DEFAULT_CHANNEL);
135 interaudiosink->input_adapter = gst_adapter_new ();
136 }
137
138 void
gst_inter_audio_sink_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)139 gst_inter_audio_sink_set_property (GObject * object, guint property_id,
140 const GValue * value, GParamSpec * pspec)
141 {
142 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
143
144 switch (property_id) {
145 case PROP_CHANNEL:
146 g_free (interaudiosink->channel);
147 interaudiosink->channel = g_value_dup_string (value);
148 break;
149 default:
150 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
151 break;
152 }
153 }
154
155 void
gst_inter_audio_sink_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)156 gst_inter_audio_sink_get_property (GObject * object, guint property_id,
157 GValue * value, GParamSpec * pspec)
158 {
159 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
160
161 switch (property_id) {
162 case PROP_CHANNEL:
163 g_value_set_string (value, interaudiosink->channel);
164 break;
165 default:
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
167 break;
168 }
169 }
170
171 void
gst_inter_audio_sink_finalize(GObject * object)172 gst_inter_audio_sink_finalize (GObject * object)
173 {
174 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
175
176 /* clean up object here */
177 g_free (interaudiosink->channel);
178 gst_object_unref (interaudiosink->input_adapter);
179
180 G_OBJECT_CLASS (gst_inter_audio_sink_parent_class)->finalize (object);
181 }
182
183 static void
gst_inter_audio_sink_get_times(GstBaseSink * sink,GstBuffer * buffer,GstClockTime * start,GstClockTime * end)184 gst_inter_audio_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
185 GstClockTime * start, GstClockTime * end)
186 {
187 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
188
189 if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
190 *start = GST_BUFFER_TIMESTAMP (buffer);
191 if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
192 *end = *start + GST_BUFFER_DURATION (buffer);
193 } else {
194 if (interaudiosink->info.rate > 0) {
195 *end = *start +
196 gst_util_uint64_scale_int (gst_buffer_get_size (buffer), GST_SECOND,
197 interaudiosink->info.rate * interaudiosink->info.bpf);
198 }
199 }
200 }
201 }
202
203 static gboolean
gst_inter_audio_sink_start(GstBaseSink * sink)204 gst_inter_audio_sink_start (GstBaseSink * sink)
205 {
206 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
207
208 GST_DEBUG_OBJECT (interaudiosink, "start");
209
210 interaudiosink->surface = gst_inter_surface_get (interaudiosink->channel);
211 g_mutex_lock (&interaudiosink->surface->mutex);
212 memset (&interaudiosink->surface->audio_info, 0, sizeof (GstAudioInfo));
213
214 /* We want to write latency-time before syncing has happened */
215 /* FIXME: The other side can change this value when it starts */
216 gst_base_sink_set_render_delay (sink,
217 interaudiosink->surface->audio_latency_time);
218 g_mutex_unlock (&interaudiosink->surface->mutex);
219
220 return TRUE;
221 }
222
223 static gboolean
gst_inter_audio_sink_stop(GstBaseSink * sink)224 gst_inter_audio_sink_stop (GstBaseSink * sink)
225 {
226 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
227
228 GST_DEBUG_OBJECT (interaudiosink, "stop");
229
230 g_mutex_lock (&interaudiosink->surface->mutex);
231 gst_adapter_clear (interaudiosink->surface->audio_adapter);
232 memset (&interaudiosink->surface->audio_info, 0, sizeof (GstAudioInfo));
233 g_mutex_unlock (&interaudiosink->surface->mutex);
234
235 gst_inter_surface_unref (interaudiosink->surface);
236 interaudiosink->surface = NULL;
237
238 gst_adapter_clear (interaudiosink->input_adapter);
239
240 return TRUE;
241 }
242
243 static gboolean
gst_inter_audio_sink_set_caps(GstBaseSink * sink,GstCaps * caps)244 gst_inter_audio_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
245 {
246 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
247 GstAudioInfo info;
248
249 if (!gst_audio_info_from_caps (&info, caps)) {
250 GST_ERROR_OBJECT (sink, "Failed to parse caps %" GST_PTR_FORMAT, caps);
251 return FALSE;
252 }
253
254 g_mutex_lock (&interaudiosink->surface->mutex);
255 interaudiosink->surface->audio_info = info;
256 interaudiosink->info = info;
257 /* TODO: Ideally we would drain the source here */
258 gst_adapter_clear (interaudiosink->surface->audio_adapter);
259 g_mutex_unlock (&interaudiosink->surface->mutex);
260
261 return TRUE;
262 }
263
264 static gboolean
gst_inter_audio_sink_event(GstBaseSink * sink,GstEvent * event)265 gst_inter_audio_sink_event (GstBaseSink * sink, GstEvent * event)
266 {
267 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
268
269 switch (GST_EVENT_TYPE (event)) {
270 case GST_EVENT_EOS:{
271 GstBuffer *tmp;
272 guint n;
273
274 if ((n = gst_adapter_available (interaudiosink->input_adapter)) > 0) {
275 g_mutex_lock (&interaudiosink->surface->mutex);
276 tmp = gst_adapter_take_buffer (interaudiosink->input_adapter, n);
277 gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
278 g_mutex_unlock (&interaudiosink->surface->mutex);
279 }
280 break;
281 }
282 default:
283 break;
284 }
285
286 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
287 }
288
289 static GstFlowReturn
gst_inter_audio_sink_render(GstBaseSink * sink,GstBuffer * buffer)290 gst_inter_audio_sink_render (GstBaseSink * sink, GstBuffer * buffer)
291 {
292 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
293 guint n, bpf;
294 guint64 period_time, buffer_time;
295 guint64 period_samples, buffer_samples;
296
297 GST_DEBUG_OBJECT (interaudiosink, "render %" G_GSIZE_FORMAT,
298 gst_buffer_get_size (buffer));
299 bpf = interaudiosink->info.bpf;
300
301 g_mutex_lock (&interaudiosink->surface->mutex);
302
303 buffer_time = interaudiosink->surface->audio_buffer_time;
304 period_time = interaudiosink->surface->audio_period_time;
305
306 if (buffer_time < period_time) {
307 GST_ERROR_OBJECT (interaudiosink,
308 "Buffer time smaller than period time (%" GST_TIME_FORMAT " < %"
309 GST_TIME_FORMAT ")", GST_TIME_ARGS (buffer_time),
310 GST_TIME_ARGS (period_time));
311 g_mutex_unlock (&interaudiosink->surface->mutex);
312 return GST_FLOW_ERROR;
313 }
314
315 buffer_samples =
316 gst_util_uint64_scale (buffer_time, interaudiosink->info.rate,
317 GST_SECOND);
318 period_samples =
319 gst_util_uint64_scale (period_time, interaudiosink->info.rate,
320 GST_SECOND);
321
322 n = gst_adapter_available (interaudiosink->surface->audio_adapter) / bpf;
323 while (n > buffer_samples) {
324 GST_DEBUG_OBJECT (interaudiosink, "flushing %" GST_TIME_FORMAT,
325 GST_TIME_ARGS (period_time));
326 gst_adapter_flush (interaudiosink->surface->audio_adapter,
327 period_samples * bpf);
328 n -= period_samples;
329 }
330
331 n = gst_adapter_available (interaudiosink->input_adapter);
332 if (period_samples * bpf > gst_buffer_get_size (buffer) + n) {
333 gst_adapter_push (interaudiosink->input_adapter, gst_buffer_ref (buffer));
334 } else {
335 GstBuffer *tmp;
336
337 if (n > 0) {
338 tmp = gst_adapter_take_buffer (interaudiosink->input_adapter, n);
339 gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
340 }
341 gst_adapter_push (interaudiosink->surface->audio_adapter,
342 gst_buffer_ref (buffer));
343 }
344 g_mutex_unlock (&interaudiosink->surface->mutex);
345
346 return GST_FLOW_OK;
347 }
348
349 static gboolean
gst_inter_audio_sink_query(GstBaseSink * sink,GstQuery * query)350 gst_inter_audio_sink_query (GstBaseSink * sink, GstQuery * query)
351 {
352 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
353 gboolean ret;
354
355 GST_DEBUG_OBJECT (sink, "query");
356
357 switch (GST_QUERY_TYPE (query)) {
358 case GST_QUERY_LATENCY:{
359 gboolean live, us_live;
360 GstClockTime min_l, max_l;
361
362 GST_DEBUG_OBJECT (sink, "latency query");
363
364 if ((ret =
365 gst_base_sink_query_latency (GST_BASE_SINK_CAST (sink), &live,
366 &us_live, &min_l, &max_l))) {
367 GstClockTime base_latency, min_latency, max_latency;
368
369 /* we and upstream are both live, adjust the min_latency */
370 if (live && us_live) {
371 /* FIXME: The other side can change this value when it starts */
372 base_latency = interaudiosink->surface->audio_latency_time;
373
374 /* we cannot go lower than the buffer size and the min peer latency */
375 min_latency = base_latency + min_l;
376 /* the max latency is the max of the peer, we can delay an infinite
377 * amount of time. */
378 max_latency = (max_l == -1) ? -1 : (base_latency + max_l);
379
380 GST_DEBUG_OBJECT (sink,
381 "peer min %" GST_TIME_FORMAT ", our min latency: %"
382 GST_TIME_FORMAT, GST_TIME_ARGS (min_l),
383 GST_TIME_ARGS (min_latency));
384 GST_DEBUG_OBJECT (sink,
385 "peer max %" GST_TIME_FORMAT ", our max latency: %"
386 GST_TIME_FORMAT, GST_TIME_ARGS (max_l),
387 GST_TIME_ARGS (max_latency));
388 } else {
389 GST_DEBUG_OBJECT (sink,
390 "peer or we are not live, don't care about latency");
391 min_latency = min_l;
392 max_latency = max_l;
393 }
394 gst_query_set_latency (query, live, min_latency, max_latency);
395 }
396 break;
397 }
398 default:
399 ret =
400 GST_BASE_SINK_CLASS (gst_inter_audio_sink_parent_class)->query (sink,
401 query);
402 break;
403 }
404
405 return ret;
406 }
407