2 * Copyright (C) 2011 David A. Schleef <ds@schleef.org>
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.
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.
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.
20 * SECTION:element-interaudiosink
21 * @title: gstinteraudiosink
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.
27 * ## Example launch line
29 * gst-launch-1.0 -v audiotestsrc ! queue ! interaudiosink
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
35 * See the gstintertest.c example in the gst-plugins-bad source code for
45 #include <gst/base/gstbasesink.h>
46 #include <gst/audio/audio.h>
47 #include "gstinteraudiosink.h"
50 GST_DEBUG_CATEGORY_STATIC (gst_inter_audio_sink_debug_category);
51 #define GST_CAT_DEFAULT gst_inter_audio_sink_debug_category
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);
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,
66 static gboolean gst_inter_audio_sink_event (GstBaseSink * sink,
68 static GstFlowReturn gst_inter_audio_sink_render (GstBaseSink * sink,
70 static gboolean gst_inter_audio_sink_query (GstBaseSink * sink,
79 #define DEFAULT_CHANNEL ("default")
82 static GstStaticPadTemplate gst_inter_audio_sink_sink_template =
83 GST_STATIC_PAD_TEMPLATE ("sink",
86 GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
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);
96 gst_inter_audio_sink_class_init (GstInterAudioSinkClass * klass)
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);
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);
107 gst_element_class_set_static_metadata (element_class,
108 "Internal audio sink",
110 "Virtual audio sink for internal process communication",
111 "David Schleef <ds@schleef.org>");
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);
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));
132 gst_inter_audio_sink_init (GstInterAudioSink * interaudiosink)
134 interaudiosink->channel = g_strdup (DEFAULT_CHANNEL);
135 interaudiosink->input_adapter = gst_adapter_new ();
139 gst_inter_audio_sink_set_property (GObject * object, guint property_id,
140 const GValue * value, GParamSpec * pspec)
142 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
144 switch (property_id) {
146 g_free (interaudiosink->channel);
147 interaudiosink->channel = g_value_dup_string (value);
150 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
156 gst_inter_audio_sink_get_property (GObject * object, guint property_id,
157 GValue * value, GParamSpec * pspec)
159 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
161 switch (property_id) {
163 g_value_set_string (value, interaudiosink->channel);
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
172 gst_inter_audio_sink_finalize (GObject * object)
174 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (object);
176 /* clean up object here */
177 g_free (interaudiosink->channel);
178 gst_object_unref (interaudiosink->input_adapter);
180 G_OBJECT_CLASS (gst_inter_audio_sink_parent_class)->finalize (object);
184 gst_inter_audio_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
185 GstClockTime * start, GstClockTime * end)
187 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
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);
194 if (interaudiosink->info.rate > 0) {
196 gst_util_uint64_scale_int (gst_buffer_get_size (buffer), GST_SECOND,
197 interaudiosink->info.rate * interaudiosink->info.bpf);
204 gst_inter_audio_sink_start (GstBaseSink * sink)
206 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
208 GST_DEBUG_OBJECT (interaudiosink, "start");
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));
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);
224 gst_inter_audio_sink_stop (GstBaseSink * sink)
226 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
228 GST_DEBUG_OBJECT (interaudiosink, "stop");
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);
235 gst_inter_surface_unref (interaudiosink->surface);
236 interaudiosink->surface = NULL;
238 gst_adapter_clear (interaudiosink->input_adapter);
244 gst_inter_audio_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
246 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
249 if (!gst_audio_info_from_caps (&info, caps)) {
250 GST_ERROR_OBJECT (sink, "Failed to parse caps %" GST_PTR_FORMAT, caps);
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);
265 gst_inter_audio_sink_event (GstBaseSink * sink, GstEvent * event)
267 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
269 switch (GST_EVENT_TYPE (event)) {
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);
286 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
290 gst_inter_audio_sink_render (GstBaseSink * sink, GstBuffer * buffer)
292 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
294 guint64 period_time, buffer_time;
295 guint64 period_samples, buffer_samples;
298 GST_DEBUG_OBJECT (interaudiosink, "render %" G_GSIZE_FORMAT,
299 gst_buffer_get_size (buffer));
300 bpf = interaudiosink->info.bpf;
302 g_mutex_lock (&interaudiosink->surface->mutex);
304 buffer_time = interaudiosink->surface->audio_buffer_time;
305 period_time = interaudiosink->surface->audio_period_time;
307 if (buffer_time < period_time) {
308 GST_ERROR_OBJECT (interaudiosink,
309 "Buffer time smaller than period time (%" GST_TIME_FORMAT " < %"
310 GST_TIME_FORMAT ")", GST_TIME_ARGS (buffer_time),
311 GST_TIME_ARGS (period_time));
312 g_mutex_unlock (&interaudiosink->surface->mutex);
313 return GST_FLOW_ERROR;
317 gst_util_uint64_scale (buffer_time, interaudiosink->info.rate,
320 gst_util_uint64_scale (period_time, interaudiosink->info.rate,
323 n = gst_adapter_available (interaudiosink->surface->audio_adapter) / bpf;
324 while (n > buffer_samples) {
325 GST_DEBUG_OBJECT (interaudiosink, "flushing %" GST_TIME_FORMAT,
326 GST_TIME_ARGS (period_time));
327 gst_adapter_flush (interaudiosink->surface->audio_adapter,
328 period_samples * bpf);
332 n = gst_adapter_available (interaudiosink->input_adapter);
333 if (period_samples * bpf > gst_buffer_get_size (buffer) + n) {
334 GstAudioMeta *audio_meta = NULL;
336 tmp = gst_buffer_copy_deep (buffer);
337 audio_meta = gst_buffer_get_audio_meta (tmp);
338 if (audio_meta != NULL)
339 gst_buffer_remove_meta (tmp, GST_META_CAST (audio_meta));
341 gst_adapter_push (interaudiosink->input_adapter, tmp);
343 GstAudioMeta *audio_meta = NULL;
346 tmp = gst_adapter_take_buffer (interaudiosink->input_adapter, n);
347 gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
349 tmp = gst_buffer_copy_deep (buffer);
350 audio_meta = gst_buffer_get_audio_meta (tmp);
351 if (audio_meta != NULL)
352 gst_buffer_remove_meta (tmp, GST_META_CAST (audio_meta));
353 gst_adapter_push (interaudiosink->surface->audio_adapter, tmp);
355 g_mutex_unlock (&interaudiosink->surface->mutex);
361 gst_inter_audio_sink_query (GstBaseSink * sink, GstQuery * query)
363 GstInterAudioSink *interaudiosink = GST_INTER_AUDIO_SINK (sink);
366 GST_DEBUG_OBJECT (sink, "query");
368 switch (GST_QUERY_TYPE (query)) {
369 case GST_QUERY_LATENCY:{
370 gboolean live, us_live;
371 GstClockTime min_l, max_l;
373 GST_DEBUG_OBJECT (sink, "latency query");
376 gst_base_sink_query_latency (GST_BASE_SINK_CAST (sink), &live,
377 &us_live, &min_l, &max_l))) {
378 GstClockTime base_latency, min_latency, max_latency;
380 /* we and upstream are both live, adjust the min_latency */
381 if (live && us_live) {
382 /* FIXME: The other side can change this value when it starts */
383 base_latency = interaudiosink->surface->audio_latency_time;
385 /* we cannot go lower than the buffer size and the min peer latency */
386 min_latency = base_latency + min_l;
387 /* the max latency is the max of the peer, we can delay an infinite
389 max_latency = (max_l == -1) ? -1 : (base_latency + max_l);
391 GST_DEBUG_OBJECT (sink,
392 "peer min %" GST_TIME_FORMAT ", our min latency: %"
393 GST_TIME_FORMAT, GST_TIME_ARGS (min_l),
394 GST_TIME_ARGS (min_latency));
395 GST_DEBUG_OBJECT (sink,
396 "peer max %" GST_TIME_FORMAT ", our max latency: %"
397 GST_TIME_FORMAT, GST_TIME_ARGS (max_l),
398 GST_TIME_ARGS (max_latency));
400 GST_DEBUG_OBJECT (sink,
401 "peer or we are not live, don't care about latency");
405 gst_query_set_latency (query, live, min_latency, max_latency);
411 GST_BASE_SINK_CLASS (gst_inter_audio_sink_parent_class)->query (sink,