2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wim.taymans@chello.be>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
26 #include <sys/ioctl.h>
27 #include <sys/soundcard.h>
31 #include "gstosssink.h"
33 /* elementfactory information */
34 static GstElementDetails gst_osssink_details = GST_ELEMENT_DETAILS (
37 "Output to a sound card via OSS",
38 "Erik Walthinsen <omega@cse.ogi.edu>, "
39 "Wim Taymans <wim.taymans@chello.be>"
42 static void gst_osssink_base_init (gpointer g_class);
43 static void gst_osssink_class_init (GstOssSinkClass *klass);
44 static void gst_osssink_init (GstOssSink *osssink);
45 static void gst_osssink_dispose (GObject *object);
47 static GstElementStateReturn gst_osssink_change_state (GstElement *element);
48 static void gst_osssink_set_clock (GstElement *element, GstClock *clock);
49 static GstClock* gst_osssink_get_clock (GstElement *element);
50 static GstClockTime gst_osssink_get_time (GstClock *clock, gpointer data);
52 static const GstFormat* gst_osssink_get_formats (GstPad *pad);
53 static gboolean gst_osssink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
54 GstFormat *dest_format, gint64 *dest_value);
55 static const GstQueryType* gst_osssink_get_query_types (GstPad *pad);
56 static gboolean gst_osssink_query (GstElement *element, GstQueryType type,
57 GstFormat *format, gint64 *value);
58 static gboolean gst_osssink_sink_query (GstPad *pad, GstQueryType type,
59 GstFormat *format, gint64 *value);
61 static GstPadLinkReturn gst_osssink_sinkconnect (GstPad *pad, const GstCaps *caps);
63 static void gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value,
65 static void gst_osssink_get_property (GObject *object, guint prop_id, GValue *value,
68 static void gst_osssink_chain (GstPad *pad,GstData *_data);
70 /* OssSink signals and args */
86 static GstStaticPadTemplate osssink_sink_factory =
87 GST_STATIC_PAD_TEMPLATE (
91 GST_STATIC_CAPS ("audio/x-raw-int, "
92 "endianness = (int) BYTE_ORDER, "
93 "signed = (boolean) { TRUE, FALSE }, "
94 "width = (int) { 8, 16 }, "
95 "depth = (int) { 8, 16 }, "
96 "rate = (int) [ 1000, 48000 ], "
97 "channels = (int) [ 1, 2 ]"
101 static GstElementClass *parent_class = NULL;
102 static guint gst_osssink_signals[LAST_SIGNAL] = { 0 };
105 gst_osssink_get_type (void)
107 static GType osssink_type = 0;
110 static const GTypeInfo osssink_info = {
111 sizeof(GstOssSinkClass),
112 gst_osssink_base_init,
114 (GClassInitFunc)gst_osssink_class_init,
119 (GInstanceInitFunc)gst_osssink_init,
121 osssink_type = g_type_register_static (GST_TYPE_OSSELEMENT, "GstOssSink", &osssink_info, 0);
128 gst_osssink_dispose (GObject *object)
130 GstOssSink *osssink = (GstOssSink *) object;
132 gst_object_unparent (GST_OBJECT (osssink->provided_clock));
134 G_OBJECT_CLASS (parent_class)->dispose (object);
138 gst_osssink_base_init (gpointer g_class)
140 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
142 gst_element_class_set_details (element_class, &gst_osssink_details);
143 gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&osssink_sink_factory));
146 gst_osssink_class_init (GstOssSinkClass *klass)
148 GObjectClass *gobject_class;
149 GstElementClass *gstelement_class;
151 gobject_class = (GObjectClass*)klass;
152 gstelement_class = (GstElementClass*)klass;
154 parent_class = g_type_class_ref(GST_TYPE_OSSELEMENT);
156 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MUTE,
157 g_param_spec_boolean ("mute", "Mute", "Mute the audio",
158 FALSE, G_PARAM_READWRITE));
159 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
160 g_param_spec_boolean ("sync", "Sync", "If syncing on timestamps should be enabled",
161 TRUE, G_PARAM_READWRITE));
162 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FRAGMENT,
163 g_param_spec_int ("fragment", "Fragment",
164 "The fragment as 0xMMMMSSSS (MMMM = total fragments, 2^SSSS = fragment size)",
165 0, G_MAXINT, 6, G_PARAM_READWRITE));
166 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BUFFER_SIZE,
167 g_param_spec_uint ("buffer_size", "Buffer size", "Size of buffers in osssink's bufferpool (bytes)",
168 0, G_MAXINT, 4096, G_PARAM_READWRITE));
169 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHUNK_SIZE,
170 g_param_spec_uint ("chunk_size", "Chunk size", "Write data in chunk sized buffers",
171 0, G_MAXUINT, 4096, G_PARAM_READWRITE));
173 gst_osssink_signals[SIGNAL_HANDOFF] =
174 g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
175 G_STRUCT_OFFSET (GstOssSinkClass, handoff), NULL, NULL,
176 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
178 gobject_class->set_property = gst_osssink_set_property;
179 gobject_class->get_property = gst_osssink_get_property;
180 gobject_class->dispose = gst_osssink_dispose;
182 gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_osssink_change_state);
183 gstelement_class->query = GST_DEBUG_FUNCPTR (gst_osssink_query);
184 gstelement_class->set_clock = gst_osssink_set_clock;
185 gstelement_class->get_clock = gst_osssink_get_clock;
190 gst_osssink_init (GstOssSink *osssink)
192 osssink->sinkpad = gst_pad_new_from_template (
193 gst_static_pad_template_get (&osssink_sink_factory), "sink");
194 gst_element_add_pad (GST_ELEMENT (osssink), osssink->sinkpad);
195 gst_pad_set_link_function (osssink->sinkpad, gst_osssink_sinkconnect);
196 gst_pad_set_convert_function (osssink->sinkpad, gst_osssink_convert);
197 gst_pad_set_query_function (osssink->sinkpad, gst_osssink_sink_query);
198 gst_pad_set_query_type_function (osssink->sinkpad, gst_osssink_get_query_types);
199 gst_pad_set_formats_function (osssink->sinkpad, gst_osssink_get_formats);
201 gst_pad_set_chain_function (osssink->sinkpad, gst_osssink_chain);
203 osssink->bufsize = 4096;
204 osssink->chunk_size = 4096;
205 osssink->resync = FALSE;
206 osssink->mute = FALSE;
207 osssink->sync = TRUE;
208 osssink->provided_clock = gst_audio_clock_new ("ossclock", gst_osssink_get_time, osssink);
209 gst_object_set_parent (GST_OBJECT (osssink->provided_clock), GST_OBJECT (osssink));
210 osssink->handled = 0;
212 GST_FLAG_SET (osssink, GST_ELEMENT_THREAD_SUGGESTED);
213 GST_FLAG_SET (osssink, GST_ELEMENT_EVENT_AWARE);
217 static GstPadLinkReturn
218 gst_osssink_sinkconnect (GstPad *pad, const GstCaps *caps)
220 GstOssSink *osssink = GST_OSSSINK (gst_pad_get_parent (pad));
222 if (!gst_osselement_parse_caps (GST_OSSELEMENT (osssink), caps))
223 return GST_PAD_LINK_REFUSED;
225 if (!gst_osselement_sync_parms (GST_OSSELEMENT (osssink))) {
226 return GST_PAD_LINK_REFUSED;
229 return GST_PAD_LINK_OK;
233 gst_osssink_get_delay (GstOssSink *osssink)
237 if (GST_OSSELEMENT (osssink)->fd == -1)
240 if (ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_GETODELAY, &delay) < 0) {
242 if (ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
246 delay = (info.fragstotal * info.fragsize) - info.bytes;
253 gst_osssink_get_time (GstClock *clock, gpointer data)
255 GstOssSink *osssink = GST_OSSSINK (data);
259 if (!GST_OSSELEMENT (osssink)->bps)
262 delay = gst_osssink_get_delay (osssink);
264 /* sometimes delay is bigger than the number of bytes sent to the device,
265 * which screws up this calculation, we assume that everything is still
266 * in the device then */
267 if (((guint64)delay) > osssink->handled) {
268 delay = osssink->handled;
270 res = (osssink->handled - delay) * GST_SECOND / GST_OSSELEMENT (osssink)->bps;
276 gst_osssink_get_clock (GstElement *element)
280 osssink = GST_OSSSINK (element);
282 return GST_CLOCK (osssink->provided_clock);
286 gst_osssink_set_clock (GstElement *element, GstClock *clock)
290 osssink = GST_OSSSINK (element);
292 osssink->clock = clock;
296 gst_osssink_chain (GstPad *pad, GstData *_data)
298 GstBuffer *buf = GST_BUFFER (_data);
300 GstClockTime buftime;
302 /* this has to be an audio buffer */
303 osssink = GST_OSSSINK (gst_pad_get_parent (pad));
305 if (GST_IS_EVENT (buf)) {
306 GstEvent *event = GST_EVENT (buf);
308 switch (GST_EVENT_TYPE (event)) {
310 ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_SYNC);
311 gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssink->provided_clock), FALSE);
312 gst_pad_event_default (pad, event);
314 case GST_EVENT_DISCONTINUOUS:
318 ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_RESET);
319 if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &value)) {
320 if (!gst_clock_handle_discont (osssink->clock, value))
321 gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssink->provided_clock), FALSE);
322 osssink->handled = 0;
324 osssink->resync = TRUE;
329 gst_pad_event_default (pad, event);
332 gst_event_unref (event);
336 if (!GST_OSSELEMENT (osssink)->bps) {
337 gst_buffer_unref (buf);
338 gst_element_error (GST_ELEMENT (osssink), "capsnego was never performed, unknown data type");
342 buftime = GST_BUFFER_TIMESTAMP (buf);
344 if (GST_OSSELEMENT (osssink)->fd >= 0) {
345 if (!osssink->mute) {
346 guchar *data = GST_BUFFER_DATA (buf);
347 gint size = GST_BUFFER_SIZE (buf);
350 if (osssink->clock) {
353 GstClockTimeDiff jitter;
355 delay = gst_osssink_get_delay (osssink);
356 queued = delay * GST_SECOND / GST_OSSELEMENT (osssink)->bps;
358 if (osssink->resync && osssink->sync) {
359 GstClockID id = gst_clock_new_single_shot_id (osssink->clock, buftime - queued);
361 gst_element_clock_wait (GST_ELEMENT (osssink), id, &jitter);
362 gst_clock_id_free (id);
365 gst_clock_handle_discont (osssink->clock, buftime - queued + jitter);
367 gst_audio_clock_set_active ((GstAudioClock*)osssink->provided_clock, TRUE);
368 osssink->resync = FALSE;
375 /* no clock, try to be as fast as possible */
377 audio_buf_info ospace;
379 ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_GETOSPACE, &ospace);
381 if (ospace.bytes >= size) {
386 while (to_write > 0) {
387 gint done = write (GST_OSSELEMENT (osssink)->fd, data,
388 MIN (to_write, osssink->chunk_size));
397 osssink->handled += done;
403 gst_audio_clock_update_time ((GstAudioClock*)osssink->provided_clock, buftime);
405 gst_buffer_unref (buf);
408 static const GstFormat*
409 gst_osssink_get_formats (GstPad *pad)
411 static const GstFormat formats[] = {
421 gst_osssink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
422 GstFormat *dest_format, gint64 *dest_value)
426 osssink = GST_OSSSINK (gst_pad_get_parent (pad));
428 return gst_osselement_convert (GST_OSSELEMENT (osssink),
429 src_format, src_value,
430 dest_format, dest_value);
433 static const GstQueryType*
434 gst_osssink_get_query_types (GstPad *pad)
436 static const GstQueryType query_types[] = {
445 gst_osssink_sink_query (GstPad *pad, GstQueryType type, GstFormat *format, gint64 *value)
450 osssink = GST_OSSSINK (gst_pad_get_parent (pad));
453 case GST_QUERY_LATENCY:
454 if (!gst_osssink_convert (pad,
455 GST_FORMAT_BYTES, gst_osssink_get_delay (osssink),
461 case GST_QUERY_POSITION:
462 if (!gst_osssink_convert (pad,
463 GST_FORMAT_TIME, gst_clock_get_time (osssink->provided_clock),
470 res = gst_pad_query (gst_pad_get_peer (osssink->sinkpad), type, format, value);
478 gst_osssink_query (GstElement *element, GstQueryType type, GstFormat *format, gint64 *value)
480 GstOssSink *osssink = GST_OSSSINK (element);
482 return gst_osssink_sink_query (osssink->sinkpad, type, format, value);
486 gst_osssink_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
490 osssink = GST_OSSSINK (object);
494 osssink->mute = g_value_get_boolean (value);
495 g_object_notify (G_OBJECT (osssink), "mute");
498 GST_OSSELEMENT (osssink)->fragment = g_value_get_int (value);
499 gst_osselement_sync_parms (GST_OSSELEMENT (osssink));
501 case ARG_BUFFER_SIZE:
502 osssink->bufsize = g_value_get_uint (value);
503 g_object_notify (object, "buffer_size");
506 osssink->sync = g_value_get_boolean (value);
507 g_object_notify (G_OBJECT (osssink), "sync");
510 osssink->chunk_size = g_value_get_uint (value);
513 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
519 gst_osssink_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
523 osssink = GST_OSSSINK (object);
527 g_value_set_boolean (value, osssink->mute);
530 g_value_set_int (value, GST_OSSELEMENT (osssink)->fragment);
532 case ARG_BUFFER_SIZE:
533 g_value_set_uint (value, osssink->bufsize);
536 g_value_set_boolean (value, osssink->sync);
539 g_value_set_uint (value, osssink->chunk_size);
542 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
547 static GstElementStateReturn
548 gst_osssink_change_state (GstElement *element)
552 osssink = GST_OSSSINK (element);
554 switch (GST_STATE_TRANSITION (element)) {
555 case GST_STATE_READY_TO_PAUSED:
557 case GST_STATE_PAUSED_TO_PLAYING:
558 osssink->resync = TRUE;
560 case GST_STATE_PLAYING_TO_PAUSED:
561 if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
562 ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_RESET, 0);
563 gst_audio_clock_set_active (GST_AUDIO_CLOCK (osssink->provided_clock), FALSE);
564 osssink->resync = TRUE;
566 case GST_STATE_PAUSED_TO_READY:
567 if (GST_FLAG_IS_SET (element, GST_OSSSINK_OPEN))
568 ioctl (GST_OSSELEMENT (osssink)->fd, SNDCTL_DSP_RESET, 0);
569 gst_osselement_reset (GST_OSSELEMENT (osssink));
575 if (GST_ELEMENT_CLASS (parent_class)->change_state)
576 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
578 return GST_STATE_SUCCESS;