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