6718f701fed8c9116c1d2eb9a895eb4604cea428
[platform/upstream/gst-plugins-good.git] / sys / sunaudio / gstsunaudiosink.c
1 /*
2  * GStreamer - SunAudio sink
3  * Copyright (C) 2004 David A. Schleef <ds@schleef.org>
4  * Copyright (C) 2005,2006 Sun Microsystems, Inc.,
5  *               Brian Cameron <brian.cameron@sun.com>
6  * Copyright (C) 2006 Jan Schmidt <thaytan@mad.scientist.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:element-sunaudiosink
26  *
27  * <refsect2>
28  * <para>
29  * sunaudiosink is an audio sink designed to work with the Sun Audio
30  * interface available in Solaris.
31  * </para>
32  * <title>Example launch line</title>
33  * <para>
34  * <programlisting>
35  * gst-launch -v sinesrc ! sunaudiosink
36  * </programlisting>
37  * </para>
38  * </refsect2>
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <fcntl.h>
46 #include <string.h>
47 #include <stropts.h>
48 #include <unistd.h>
49 #include <sys/mman.h>
50
51 #include "gstsunaudiosink.h"
52
53 GST_DEBUG_CATEGORY_EXTERN (sunaudio_debug);
54 #define GST_CAT_DEFAULT sunaudio_debug
55
56 /* elementfactory information */
57 static const GstElementDetails plugin_details =
58 GST_ELEMENT_DETAILS ("Sun Audio Sink",
59     "Sink/Audio",
60     "Audio sink for Sun Audio devices",
61     "David A. Schleef <ds@schleef.org>, "
62     "Brian Cameron <brian.cameron@sun.com>");
63
64 static void gst_sunaudiosink_base_init (gpointer g_class);
65 static void gst_sunaudiosink_class_init (GstSunAudioSinkClass * klass);
66 static void gst_sunaudiosink_init (GstSunAudioSink * filter);
67 static void gst_sunaudiosink_dispose (GObject * object);
68 static void gst_sunaudiosink_finalize (GObject * object);
69
70 static void gst_sunaudiosink_set_property (GObject * object, guint prop_id,
71     const GValue * value, GParamSpec * pspec);
72 static void gst_sunaudiosink_get_property (GObject * object, guint prop_id,
73     GValue * value, GParamSpec * pspec);
74
75 static GstCaps *gst_sunaudiosink_getcaps (GstBaseSink * bsink);
76
77 static gboolean gst_sunaudiosink_open (GstAudioSink * asink);
78 static gboolean gst_sunaudiosink_close (GstAudioSink * asink);
79 static gboolean gst_sunaudiosink_prepare (GstAudioSink * asink,
80     GstRingBufferSpec * spec);
81 static gboolean gst_sunaudiosink_unprepare (GstAudioSink * asink);
82 static guint gst_sunaudiosink_write (GstAudioSink * asink, gpointer data,
83     guint length);
84 static guint gst_sunaudiosink_delay (GstAudioSink * asink);
85 static void gst_sunaudiosink_reset (GstAudioSink * asink);
86
87 #define DEFAULT_DEVICE  "/dev/audio"
88 enum
89 {
90   PROP_0,
91   PROP_DEVICE,
92 };
93
94 static GstStaticPadTemplate gst_sunaudiosink_factory =
95 GST_STATIC_PAD_TEMPLATE ("sink",
96     GST_PAD_SINK,
97     GST_PAD_ALWAYS,
98     GST_STATIC_CAPS ("audio/x-raw-int, "
99         "endianness = (int) BYTE_ORDER, "
100         "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16, "
101         /* [5510,48000] seems to be a Solaris limit */
102         "rate = (int) [ 5510, 48000 ], " "channels = (int) [ 1, 2 ]")
103     );
104
105 static GstElementClass *parent_class = NULL;
106
107 GType
108 gst_sunaudiosink_get_type (void)
109 {
110   static GType plugin_type = 0;
111
112   if (!plugin_type) {
113     static const GTypeInfo plugin_info = {
114       sizeof (GstSunAudioSinkClass),
115       gst_sunaudiosink_base_init,
116       NULL,
117       (GClassInitFunc) gst_sunaudiosink_class_init,
118       NULL,
119       NULL,
120       sizeof (GstSunAudioSink),
121       0,
122       (GInstanceInitFunc) gst_sunaudiosink_init,
123     };
124
125     plugin_type = g_type_register_static (GST_TYPE_AUDIO_SINK,
126         "GstSunAudioSink", &plugin_info, 0);
127   }
128   return plugin_type;
129 }
130
131 static void
132 gst_sunaudiosink_dispose (GObject * object)
133 {
134   G_OBJECT_CLASS (parent_class)->dispose (object);
135 }
136
137 static void
138 gst_sunaudiosink_finalize (GObject * object)
139 {
140   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (object);
141
142   g_mutex_free (sunaudiosink->write_mutex);
143   g_cond_free (sunaudiosink->sleep_cond);
144
145   g_free (sunaudiosink->device);
146
147   if (sunaudiosink->fd != -1) {
148     close (sunaudiosink->fd);
149     sunaudiosink->fd = -1;
150   }
151
152   G_OBJECT_CLASS (parent_class)->finalize (object);
153 }
154
155 static void
156 gst_sunaudiosink_base_init (gpointer g_class)
157 {
158   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
159
160   gst_element_class_add_pad_template (element_class,
161       gst_static_pad_template_get (&gst_sunaudiosink_factory));
162   gst_element_class_set_details (element_class, &plugin_details);
163 }
164
165 static void
166 gst_sunaudiosink_class_init (GstSunAudioSinkClass * klass)
167 {
168   GObjectClass *gobject_class;
169   GstElementClass *gstelement_class;
170   GstBaseSinkClass *gstbasesink_class;
171   GstBaseAudioSinkClass *gstbaseaudiosink_class;
172   GstAudioSinkClass *gstaudiosink_class;
173
174   gobject_class = (GObjectClass *) klass;
175   gstelement_class = (GstElementClass *) klass;
176   gstbasesink_class = (GstBaseSinkClass *) klass;
177   gstbaseaudiosink_class = (GstBaseAudioSinkClass *) klass;
178   gstaudiosink_class = (GstAudioSinkClass *) klass;
179
180   parent_class = g_type_class_peek_parent (klass);
181
182   gobject_class->dispose = gst_sunaudiosink_dispose;
183   gobject_class->finalize = gst_sunaudiosink_finalize;
184
185   gobject_class->set_property =
186       GST_DEBUG_FUNCPTR (gst_sunaudiosink_set_property);
187   gobject_class->get_property =
188       GST_DEBUG_FUNCPTR (gst_sunaudiosink_get_property);
189
190   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_sunaudiosink_getcaps);
191
192   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_sunaudiosink_open);
193   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_sunaudiosink_close);
194   gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_sunaudiosink_prepare);
195   gstaudiosink_class->unprepare =
196       GST_DEBUG_FUNCPTR (gst_sunaudiosink_unprepare);
197   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_sunaudiosink_write);
198   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_sunaudiosink_delay);
199   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_sunaudiosink_reset);
200
201   g_object_class_install_property (gobject_class, PROP_DEVICE,
202       g_param_spec_string ("device", "Device", "Audio Device (/dev/audio)",
203           DEFAULT_DEVICE, G_PARAM_READWRITE));
204 }
205
206 static void
207 gst_sunaudiosink_init (GstSunAudioSink * sunaudiosink)
208 {
209   GstBaseAudioSink *ba_sink = GST_BASE_AUDIO_SINK (sunaudiosink);
210   const char *audiodev;
211
212   GST_DEBUG_OBJECT (sunaudiosink, "initializing sunaudiosink");
213
214   sunaudiosink->fd = -1;
215
216   audiodev = g_getenv ("AUDIODEV");
217   if (audiodev == NULL)
218     audiodev = DEFAULT_DEVICE;
219   sunaudiosink->device = g_strdup (audiodev);
220
221   /* mutex and gconf used to control the write method */
222   sunaudiosink->write_mutex = g_mutex_new ();
223   sunaudiosink->sleep_cond = g_cond_new ();
224 }
225
226 static void
227 gst_sunaudiosink_set_property (GObject * object, guint prop_id,
228     const GValue * value, GParamSpec * pspec)
229 {
230   GstSunAudioSink *sunaudiosink;
231
232   sunaudiosink = GST_SUNAUDIO_SINK (object);
233
234   switch (prop_id) {
235     case PROP_DEVICE:
236       GST_OBJECT_LOCK (sunaudiosink);
237       g_free (sunaudiosink->device);
238       sunaudiosink->device = g_strdup (g_value_get_string (value));
239       GST_OBJECT_UNLOCK (sunaudiosink);
240       break;
241     default:
242       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243       break;
244   }
245 }
246
247 static void
248 gst_sunaudiosink_get_property (GObject * object, guint prop_id,
249     GValue * value, GParamSpec * pspec)
250 {
251   GstSunAudioSink *sunaudiosink;
252
253   sunaudiosink = GST_SUNAUDIO_SINK (object);
254
255   switch (prop_id) {
256     case PROP_DEVICE:
257       GST_OBJECT_LOCK (sunaudiosink);
258       g_value_set_string (value, sunaudiosink->device);
259       GST_OBJECT_UNLOCK (sunaudiosink);
260       break;
261     default:
262       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
263       break;
264   }
265 }
266
267 static GstCaps *
268 gst_sunaudiosink_getcaps (GstBaseSink * bsink)
269 {
270   GstPadTemplate *pad_template;
271   GstCaps *caps = NULL;
272   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (bsink);
273
274   GST_DEBUG_OBJECT (sunaudiosink, "getcaps called");
275
276   pad_template = gst_static_pad_template_get (&gst_sunaudiosink_factory);
277   caps = gst_caps_copy (gst_pad_template_get_caps (pad_template));
278
279   gst_object_unref (pad_template);
280
281   return caps;
282 }
283
284 static gboolean
285 gst_sunaudiosink_open (GstAudioSink * asink)
286 {
287   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (asink);
288   int fd, ret;
289
290   /* First try to open non-blocking */
291   GST_OBJECT_LOCK (sunaudiosink);
292   fd = open (sunaudiosink->device, O_WRONLY | O_NONBLOCK);
293
294   if (fd >= 0) {
295     close (fd);
296     fd = open (sunaudiosink->device, O_WRONLY);
297   }
298
299   if (fd == -1) {
300     GST_OBJECT_UNLOCK (sunaudiosink);
301     goto open_failed;
302   }
303
304   sunaudiosink->fd = fd;
305   GST_OBJECT_UNLOCK (sunaudiosink);
306
307   ret = ioctl (fd, AUDIO_GETDEV, &sunaudiosink->dev);
308   if (ret == -1)
309     goto ioctl_error;
310
311   GST_DEBUG_OBJECT (sunaudiosink, "name %s", sunaudiosink->dev.name);
312   GST_DEBUG_OBJECT (sunaudiosink, "version %s", sunaudiosink->dev.version);
313   GST_DEBUG_OBJECT (sunaudiosink, "config %s", sunaudiosink->dev.config);
314
315   ret = ioctl (fd, AUDIO_GETINFO, &sunaudiosink->info);
316   if (ret == -1)
317     goto ioctl_error;
318
319   GST_DEBUG_OBJECT (sunaudiosink, "monitor_gain %d",
320       sunaudiosink->info.monitor_gain);
321   GST_DEBUG_OBJECT (sunaudiosink, "output_muted %d",
322       sunaudiosink->info.output_muted);
323   GST_DEBUG_OBJECT (sunaudiosink, "hw_features %08x",
324       sunaudiosink->info.hw_features);
325   GST_DEBUG_OBJECT (sunaudiosink, "sw_features %08x",
326       sunaudiosink->info.sw_features);
327   GST_DEBUG_OBJECT (sunaudiosink, "sw_features_enabled %08x",
328       sunaudiosink->info.sw_features_enabled);
329
330   return TRUE;
331
332 open_failed:
333   GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, OPEN_WRITE, (NULL),
334       ("can't open connection to Sun Audio device %s", sunaudiosink->device));
335   return FALSE;
336 ioctl_error:
337   close (sunaudiosink->fd);
338   GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
339           strerror (errno)));
340   return FALSE;
341 }
342
343 static gboolean
344 gst_sunaudiosink_close (GstAudioSink * asink)
345 {
346   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (asink);
347
348   if (sunaudiosink->fd != -1) {
349     close (sunaudiosink->fd);
350     sunaudiosink->fd = -1;
351   }
352   return TRUE;
353 }
354
355 static gboolean
356 gst_sunaudiosink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
357 {
358   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (asink);
359   audio_info_t ainfo;
360   int ret;
361   int ports;
362
363   ret = ioctl (sunaudiosink->fd, AUDIO_GETINFO, &ainfo);
364   if (ret == -1) {
365     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
366             strerror (errno)));
367     return FALSE;
368   }
369
370   if (spec->width != 16)
371     return FALSE;
372
373   ports = ainfo.play.port;
374
375   AUDIO_INITINFO (&ainfo);
376
377   ainfo.play.sample_rate = spec->rate;
378   ainfo.play.channels = spec->channels;
379   ainfo.play.precision = spec->width;
380   ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
381   ainfo.play.port = ports;
382
383   /* buffer_time for playback is not implemented in Solaris at the moment,
384      but at some point in the future, it might be */
385   ainfo.play.buffer_size =
386       gst_util_uint64_scale (spec->rate * spec->bytes_per_sample,
387       spec->buffer_time, GST_SECOND / GST_USECOND);
388
389   spec->silence_sample[0] = 0;
390   spec->silence_sample[1] = 0;
391   spec->silence_sample[2] = 0;
392   spec->silence_sample[3] = 0;
393
394   ret = ioctl (sunaudiosink->fd, AUDIO_SETINFO, &ainfo);
395   if (ret == -1) {
396     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
397             strerror (errno)));
398     return FALSE;
399   }
400
401   /* Now read back the info to find out the actual buffer size and set 
402      segtotal */
403   AUDIO_INITINFO (&ainfo);
404
405   ret = ioctl (sunaudiosink->fd, AUDIO_GETINFO, &ainfo);
406   if (ret == -1) {
407     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
408             strerror (errno)));
409     return FALSE;
410   }
411 #if 0
412   /* We don't actually use the buffer_size from the sound device, because
413    * it seems it's just bogus sometimes */
414   sunaudiosink->segtotal = spec->segtotal =
415       ainfo.play.buffer_size / spec->segsize;
416 #else
417   sunaudiosink->segtotal = spec->segtotal;
418 #endif
419   sunaudiosink->segtotal_samples =
420       spec->segtotal * spec->segsize / spec->bytes_per_sample;
421
422   sunaudiosink->segs_written = (gint) ainfo.play.eof;
423   sunaudiosink->samples_written = ainfo.play.samples;
424   sunaudiosink->bytes_per_sample = spec->bytes_per_sample;
425
426   GST_DEBUG_OBJECT (sunaudiosink, "Got device buffer_size of %u",
427       ainfo.play.buffer_size);
428
429   return TRUE;
430 }
431
432 static gboolean
433 gst_sunaudiosink_unprepare (GstAudioSink * asink)
434 {
435   return TRUE;
436 }
437
438 #define LOOP_WHILE_EINTR(v,func) do { (v) = (func); } \
439                 while ((v) == -1 && errno == EINTR);
440
441 /* Called with the write_mutex held */
442 static void
443 gst_sunaudio_sink_do_delay (GstSunAudioSink * sink)
444 {
445   GstBaseAudioSink *ba_sink = GST_BASE_AUDIO_SINK (sink);
446   GstClockTime total_sleep;
447   GstClockTime max_sleep;
448   gint sleep_usecs;
449   GTimeVal sleep_end;
450   gint err;
451   audio_info_t ainfo;
452   guint diff;
453
454   /* This code below ensures that we don't race any further than buffer_time 
455    * ahead of the audio output, by sleeping if the next write call would cause
456    * us to advance too far in the ring-buffer */
457   LOOP_WHILE_EINTR (err, ioctl (sink->fd, AUDIO_GETINFO, &ainfo));
458   if (err < 0)
459     goto write_error;
460
461   /* Compute our offset from the output (copes with overflow) */
462   diff = (guint) (sink->segs_written) - ainfo.play.eof;
463   if (diff > sink->segtotal) {
464     /* This implies that reset did a flush just as the sound device aquired
465      * some buffers internally, and it causes us to be out of sync with the
466      * eof measure. This corrects it */
467     sink->segs_written = ainfo.play.eof;
468     diff = 0;
469   }
470
471   if (diff + 1 < sink->segtotal)
472     return;                     /* no need to sleep at all */
473
474   /* Never sleep longer than the initial number of undrained segments in the 
475      device plus one */
476   total_sleep = 0;
477   max_sleep = (diff + 1) * (ba_sink->latency_time * GST_USECOND);
478   /* sleep for a segment period between .eof polls */
479   sleep_usecs = ba_sink->latency_time;
480
481   /* Current time is our reference point */
482   g_get_current_time (&sleep_end);
483
484   /* If the next segment would take us too far along the ring buffer,
485    * sleep for a bit to free up a slot. If there were a way to find out
486    * when the eof field actually increments, we could use, but the only
487    * notification mechanism seems to be SIGPOLL, which we can't use from
488    * a support library */
489   while (diff + 1 >= sink->segtotal && total_sleep < max_sleep) {
490     GST_LOG_OBJECT (sink, "need to block to drain segment(s). "
491         "Sleeping for %d us", sleep_usecs);
492
493     g_time_val_add (&sleep_end, sleep_usecs);
494
495     if (g_cond_timed_wait (sink->sleep_cond, sink->write_mutex, &sleep_end)) {
496       GST_LOG_OBJECT (sink, "Waking up early due to reset");
497       return;                   /* Got told to wake up */
498     }
499     total_sleep += (sleep_usecs * GST_USECOND);
500
501     LOOP_WHILE_EINTR (err, ioctl (sink->fd, AUDIO_GETINFO, &ainfo));
502     if (err < 0)
503       goto write_error;
504
505     /* Compute our (new) offset from the output (copes with overflow) */
506     diff = (guint) g_atomic_int_get (&sink->segs_written) - ainfo.play.eof;
507   }
508
509   return;
510
511 write_error:
512   GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
513       ("Playback error on device '%s': %s", sink->device, strerror (errno)));
514   return;
515 poll_failed:
516   GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
517       ("Playback error on device '%s': %s", sink->device, strerror (errno)));
518   return;
519 }
520
521 static guint
522 gst_sunaudiosink_write (GstAudioSink * asink, gpointer data, guint length)
523 {
524   GstSunAudioSink *sink = GST_SUNAUDIO_SINK (asink);
525
526   gint bytes_written, err;
527
528   g_mutex_lock (sink->write_mutex);
529   if (sink->flushing) {
530     /* Exit immediately if reset tells us to */
531     g_mutex_unlock (sink->write_mutex);
532     return length;
533   }
534
535   LOOP_WHILE_EINTR (bytes_written, write (sink->fd, data, length));
536   if (bytes_written < 0) {
537     err = bytes_written;
538     goto write_error;
539   }
540
541   /* Increment our sample counter, for delay calcs */
542   g_atomic_int_add (&sink->samples_written, length / sink->bytes_per_sample);
543
544   /* Don't consider the segment written if we didn't output the whole lot yet */
545   if (bytes_written < length) {
546     g_mutex_unlock (sink->write_mutex);
547     return (guint) bytes_written;
548   }
549
550   /* Write a zero length output to trigger increment of the eof field */
551   LOOP_WHILE_EINTR (err, write (sink->fd, NULL, 0));
552   if (err < 0)
553     goto write_error;
554
555   /* Count this extra segment we've written */
556   sink->segs_written += 1;
557
558   /* Now delay so we don't overrun the ring buffer */
559   gst_sunaudio_sink_do_delay (sink);
560
561   g_mutex_unlock (sink->write_mutex);
562   return length;
563
564 write_error:
565   g_mutex_unlock (sink->write_mutex);
566
567   GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
568       ("Playback error on device '%s': %s", sink->device, strerror (errno)));
569   return length;                /* Say we wrote the segment to let the ringbuffer exit */
570 }
571
572 /*
573  * Provide the current number of unplayed samples that have been written
574  * to the device */
575 static guint
576 gst_sunaudiosink_delay (GstAudioSink * asink)
577 {
578   GstSunAudioSink *sink = GST_SUNAUDIO_SINK (asink);
579   audio_info_t ainfo;
580   gint ret;
581   guint offset;
582
583   ret = ioctl (sink->fd, AUDIO_GETINFO, &ainfo);
584   if (G_UNLIKELY (ret == -1))
585     return 0;
586
587   offset = (g_atomic_int_get (&sink->samples_written) - ainfo.play.samples);
588
589   /* If the offset is larger than the total ringbuffer size, then we asked
590      between the write call and when samples_written is updated */
591   if (G_UNLIKELY (offset > sink->segtotal_samples))
592     return 0;
593
594   return offset;
595 }
596
597 static void
598 gst_sunaudiosink_reset (GstAudioSink * asink)
599 {
600   /* Get current values */
601   GstSunAudioSink *sunaudiosink = GST_SUNAUDIO_SINK (asink);
602   audio_info_t ainfo;
603   int ret;
604
605   ret = ioctl (sunaudiosink->fd, AUDIO_GETINFO, &ainfo);
606   if (ret == -1) {
607     /*
608      * Should never happen, but if we couldn't getinfo, then no point
609      * trying to setinfo
610      */
611     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
612             strerror (errno)));
613     return;
614   }
615
616   /*
617    * Pause the audio - so audio stops playing immediately rather than
618    * waiting for the ringbuffer to empty.
619    */
620   ainfo.play.pause = !NULL;
621   ret = ioctl (sunaudiosink->fd, AUDIO_SETINFO, &ainfo);
622   if (ret == -1) {
623     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
624             strerror (errno)));
625   }
626
627   /* Flush the audio */
628   ret = ioctl (sunaudiosink->fd, I_FLUSH, FLUSHW);
629   if (ret == -1) {
630     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
631             strerror (errno)));
632   }
633
634   /* Now, we take the write_mutex and signal to ensure the write thread
635    * is not busy, and we signal the condition to wake up any sleeper,
636    * then we flush again in case the write wrote something after we flushed,
637    * and finally release the lock and unpause */
638   g_mutex_lock (sunaudiosink->write_mutex);
639   sunaudiosink->flushing = TRUE;
640
641   g_cond_signal (sunaudiosink->sleep_cond);
642
643   ret = ioctl (sunaudiosink->fd, I_FLUSH, FLUSHW);
644   if (ret == -1) {
645     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
646             strerror (errno)));
647   }
648
649   /* unpause the audio */
650   ainfo.play.pause = NULL;
651   ret = ioctl (sunaudiosink->fd, AUDIO_SETINFO, &ainfo);
652   if (ret == -1) {
653     GST_ELEMENT_ERROR (sunaudiosink, RESOURCE, SETTINGS, (NULL), ("%s",
654             strerror (errno)));
655   }
656
657   /* After flushing the audio device, we need to remeasure the sample count
658    * and segments written count so we're in sync with the device */
659
660   sunaudiosink->segs_written = ainfo.play.eof;
661   g_atomic_int_set (&sunaudiosink->samples_written, ainfo.play.samples);
662
663   sunaudiosink->flushing = FALSE;
664   g_mutex_unlock (sunaudiosink->write_mutex);
665 }