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