1 /* GStreamer
2 * Copyright (C) 2005 Sebastien Moutte <sebastien@moutte.net>
3 * Copyright (C) 2007 Pioneers of the Inevitable <songbird@songbirdnest.com>
4 * Copyright (C) 2010 Fluendo S.A. <support@fluendo.com>
5 *
6 * gstdirectsoundsink.c:
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 * The development of this code was made possible due to the involvement
25 * of Pioneers of the Inevitable, the creators of the Songbird Music player
26 *
27 */
28 
29 /**
30  * SECTION:element-directsoundsink
31  *
32  * This element lets you output sound using the DirectSound API.
33  *
34  * Note that you should almost always use generic audio conversion elements
35  * like audioconvert and audioresample in front of an audiosink to make sure
36  * your pipeline works under all circumstances (those conversion elements will
37  * act in passthrough-mode if no conversion is necessary).
38  *
39  * <refsect2>
40  * <title>Example pipelines</title>
41  * |[
42  * gst-launch-1.0 -v audiotestsrc ! audioconvert ! volume volume=0.1 ! directsoundsink
43  * ]| will output a sine wave (continuous beep sound) to your sound card (with
44  * a very low volume as precaution).
45  * |[
46  * gst-launch-1.0 -v filesrc location=music.ogg ! decodebin ! audioconvert ! audioresample ! directsoundsink
47  * ]| will play an Ogg/Vorbis audio file and output it.
48  * </refsect2>
49  */
50 
51 #ifdef HAVE_CONFIG_H
52 #include "config.h"
53 #endif
54 
55 #include <gst/base/gstbasesink.h>
56 #include "gstdirectsoundsink.h"
57 #include <gst/audio/gstaudioiec61937.h>
58 
59 #include <math.h>
60 
61 #ifdef __CYGWIN__
62 #include <unistd.h>
63 #ifndef _swab
64 #define _swab swab
65 #endif
66 #endif
67 
68 #define DEFAULT_MUTE FALSE
69 
70 GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug);
71 #define GST_CAT_DEFAULT directsoundsink_debug
72 
73 static void gst_directsound_sink_finalize (GObject * object);
74 
75 static void gst_directsound_sink_set_property (GObject * object, guint prop_id,
76     const GValue * value, GParamSpec * pspec);
77 static void gst_directsound_sink_get_property (GObject * object, guint prop_id,
78     GValue * value, GParamSpec * pspec);
79 
80 static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink,
81     GstCaps * filter);
82 static GstBuffer *gst_directsound_sink_payload (GstAudioBaseSink * sink,
83     GstBuffer * buf);
84 static gboolean gst_directsound_sink_prepare (GstAudioSink * asink,
85     GstAudioRingBufferSpec * spec);
86 static gboolean gst_directsound_sink_unprepare (GstAudioSink * asink);
87 static gboolean gst_directsound_sink_open (GstAudioSink * asink);
88 static gboolean gst_directsound_sink_close (GstAudioSink * asink);
89 static gint gst_directsound_sink_write (GstAudioSink * asink,
90     gpointer data, guint length);
91 static guint gst_directsound_sink_delay (GstAudioSink * asink);
92 static void gst_directsound_sink_reset (GstAudioSink * asink);
93 static GstCaps *gst_directsound_probe_supported_formats (GstDirectSoundSink *
94     dsoundsink, const GstCaps * template_caps);
95 static gboolean gst_directsound_sink_query (GstBaseSink * pad,
96     GstQuery * query);
97 
98 static void gst_directsound_sink_set_volume (GstDirectSoundSink * sink,
99     gdouble volume, gboolean store);
100 static gdouble gst_directsound_sink_get_volume (GstDirectSoundSink * sink);
101 static void gst_directsound_sink_set_mute (GstDirectSoundSink * sink,
102     gboolean mute);
103 static gboolean gst_directsound_sink_get_mute (GstDirectSoundSink * sink);
104 static const gchar *gst_directsound_sink_get_device (GstDirectSoundSink *
105     dsoundsink);
106 static void gst_directsound_sink_set_device (GstDirectSoundSink * dsoundsink,
107     const gchar * device_id);
108 
109 static gboolean gst_directsound_sink_is_spdif_format (GstAudioRingBufferSpec *
110     spec);
111 
112 static gchar *gst_hres_to_string (HRESULT hRes);
113 
114 static GstStaticPadTemplate directsoundsink_sink_factory =
115 GST_STATIC_PAD_TEMPLATE ("sink",
116     GST_PAD_SINK,
117     GST_PAD_ALWAYS,
118     GST_STATIC_CAPS (GST_DIRECTSOUND_SINK_CAPS));
119 
120 enum
121 {
122   PROP_0,
123   PROP_VOLUME,
124   PROP_MUTE,
125   PROP_DEVICE
126 };
127 
128 #define gst_directsound_sink_parent_class parent_class
129 G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSink, gst_directsound_sink,
130     GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL)
131     );
132 
133 static void
gst_directsound_sink_finalize(GObject * object)134 gst_directsound_sink_finalize (GObject * object)
135 {
136   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (object);
137 
138   g_free (dsoundsink->device_id);
139   dsoundsink->device_id = NULL;
140 
141   g_mutex_clear (&dsoundsink->dsound_lock);
142   gst_object_unref (dsoundsink->system_clock);
143   if (dsoundsink->write_wait_clock_id != NULL) {
144     gst_clock_id_unref (dsoundsink->write_wait_clock_id);
145   }
146 
147   G_OBJECT_CLASS (parent_class)->finalize (object);
148 }
149 
150 static void
gst_directsound_sink_class_init(GstDirectSoundSinkClass * klass)151 gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass)
152 {
153   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
154   GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
155   GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS (klass);
156   GstAudioBaseSinkClass *gstaudiobasesink_class =
157       GST_AUDIO_BASE_SINK_CLASS (klass);
158   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
159 
160   GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0,
161       "DirectSound sink");
162 
163   gobject_class->finalize = gst_directsound_sink_finalize;
164   gobject_class->set_property = gst_directsound_sink_set_property;
165   gobject_class->get_property = gst_directsound_sink_get_property;
166 
167   gstbasesink_class->get_caps =
168       GST_DEBUG_FUNCPTR (gst_directsound_sink_getcaps);
169 
170   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_directsound_sink_query);
171 
172   gstaudiobasesink_class->payload =
173       GST_DEBUG_FUNCPTR (gst_directsound_sink_payload);
174 
175   gstaudiosink_class->prepare =
176       GST_DEBUG_FUNCPTR (gst_directsound_sink_prepare);
177   gstaudiosink_class->unprepare =
178       GST_DEBUG_FUNCPTR (gst_directsound_sink_unprepare);
179   gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_directsound_sink_open);
180   gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_directsound_sink_close);
181   gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_directsound_sink_write);
182   gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_sink_delay);
183   gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_sink_reset);
184 
185   g_object_class_install_property (gobject_class,
186       PROP_VOLUME,
187       g_param_spec_double ("volume", "Volume",
188           "Volume of this stream", 0.0, 1.0, 1.0,
189           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
190 
191   g_object_class_install_property (gobject_class,
192       PROP_MUTE,
193       g_param_spec_boolean ("mute", "Mute",
194           "Mute state of this stream", DEFAULT_MUTE,
195           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
196 
197   g_object_class_install_property (gobject_class,
198       PROP_DEVICE,
199       g_param_spec_string ("device", "Device",
200           "DirectSound playback device as a GUID string",
201           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
202 
203   gst_element_class_set_static_metadata (element_class,
204       "Direct Sound Audio Sink", "Sink/Audio",
205       "Output to a sound card via Direct Sound",
206       "Sebastien Moutte <sebastien@moutte.net>");
207 
208   gst_element_class_add_static_pad_template (element_class,
209       &directsoundsink_sink_factory);
210 }
211 
212 static void
gst_directsound_sink_init(GstDirectSoundSink * dsoundsink)213 gst_directsound_sink_init (GstDirectSoundSink * dsoundsink)
214 {
215   dsoundsink->volume = 100;
216   dsoundsink->mute = FALSE;
217   dsoundsink->device_id = NULL;
218   dsoundsink->pDS = NULL;
219   dsoundsink->cached_caps = NULL;
220   dsoundsink->pDSBSecondary = NULL;
221   dsoundsink->current_circular_offset = 0;
222   dsoundsink->buffer_size = DSBSIZE_MIN;
223   g_mutex_init (&dsoundsink->dsound_lock);
224   dsoundsink->system_clock = gst_system_clock_obtain ();
225   dsoundsink->write_wait_clock_id = NULL;
226   dsoundsink->first_buffer_after_reset = FALSE;
227 }
228 
229 static void
gst_directsound_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)230 gst_directsound_sink_set_property (GObject * object,
231     guint prop_id, const GValue * value, GParamSpec * pspec)
232 {
233   GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
234 
235   switch (prop_id) {
236     case PROP_VOLUME:
237       gst_directsound_sink_set_volume (sink, g_value_get_double (value), TRUE);
238       break;
239     case PROP_MUTE:
240       gst_directsound_sink_set_mute (sink, g_value_get_boolean (value));
241       break;
242     case PROP_DEVICE:
243       gst_directsound_sink_set_device (sink, g_value_get_string (value));
244       break;
245     default:
246       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
247       break;
248   }
249 }
250 
251 static void
gst_directsound_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)252 gst_directsound_sink_get_property (GObject * object,
253     guint prop_id, GValue * value, GParamSpec * pspec)
254 {
255   GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);
256 
257   switch (prop_id) {
258     case PROP_VOLUME:
259       g_value_set_double (value, gst_directsound_sink_get_volume (sink));
260       break;
261     case PROP_MUTE:
262       g_value_set_boolean (value, gst_directsound_sink_get_mute (sink));
263       break;
264     case PROP_DEVICE:
265       g_value_set_string (value, gst_directsound_sink_get_device (sink));
266       break;
267     default:
268       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269       break;
270   }
271 }
272 
273 static GstCaps *
gst_directsound_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)274 gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
275 {
276   GstElementClass *element_class;
277   GstPadTemplate *pad_template;
278   GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (bsink);
279   GstCaps *caps;
280 
281   if (dsoundsink->pDS == NULL) {
282     GST_DEBUG_OBJECT (dsoundsink, "device not open, using template caps");
283     return NULL;                /* base class will get template caps for us */
284   }
285 
286   if (dsoundsink->cached_caps) {
287     caps = gst_caps_ref (dsoundsink->cached_caps);
288   } else {
289     element_class = GST_ELEMENT_GET_CLASS (dsoundsink);
290     pad_template = gst_element_class_get_pad_template (element_class, "sink");
291     g_return_val_if_fail (pad_template != NULL, NULL);
292 
293     caps = gst_directsound_probe_supported_formats (dsoundsink,
294         gst_pad_template_get_caps (pad_template));
295     if (caps)
296       dsoundsink->cached_caps = gst_caps_ref (caps);
297   }
298 
299   if (caps && filter) {
300     GstCaps *tmp =
301         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
302     gst_caps_unref (caps);
303     caps = tmp;
304   }
305 
306   if (caps) {
307     gchar *caps_string = gst_caps_to_string (caps);
308     GST_DEBUG_OBJECT (dsoundsink, "returning caps %s", caps_string);
309     g_free (caps_string);
310   }
311 
312   return caps;
313 }
314 
315 static gboolean
gst_directsound_sink_acceptcaps(GstBaseSink * sink,GstQuery * query)316 gst_directsound_sink_acceptcaps (GstBaseSink * sink, GstQuery * query)
317 {
318   GstDirectSoundSink *dsink = GST_DIRECTSOUND_SINK (sink);
319   GstPad *pad;
320   GstCaps *caps;
321   GstCaps *pad_caps;
322   GstStructure *st;
323   gboolean ret = FALSE;
324   GstAudioRingBufferSpec spec = { 0 };
325 
326   if (G_UNLIKELY (dsink == NULL))
327     return FALSE;
328 
329   pad = sink->sinkpad;
330 
331   gst_query_parse_accept_caps (query, &caps);
332   GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);
333 
334   pad_caps = gst_pad_query_caps (pad, NULL);
335   if (pad_caps) {
336     gboolean cret = gst_caps_is_subset (caps, pad_caps);
337     gst_caps_unref (pad_caps);
338     if (!cret) {
339       GST_DEBUG_OBJECT (dsink,
340           "Caps are not a subset of the pad caps, not accepting caps");
341       goto done;
342     }
343   }
344 
345   /* If we've not got fixed caps, creating a stream might fail, so let's just
346    * return from here with default acceptcaps behaviour */
347   if (!gst_caps_is_fixed (caps)) {
348     GST_DEBUG_OBJECT (dsink, "Caps are not fixed, not accepting caps");
349     goto done;
350   }
351 
352   spec.latency_time = GST_SECOND;
353   if (!gst_audio_ring_buffer_parse_caps (&spec, caps)) {
354     GST_DEBUG_OBJECT (dsink, "Failed to parse caps, not accepting");
355     goto done;
356   }
357 
358   /* Make sure input is framed (one frame per buffer) and can be payloaded */
359   switch (spec.type) {
360     case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3:
361     case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS:
362     {
363       gboolean framed = FALSE, parsed = FALSE;
364       st = gst_caps_get_structure (caps, 0);
365 
366       gst_structure_get_boolean (st, "framed", &framed);
367       gst_structure_get_boolean (st, "parsed", &parsed);
368       if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) {
369         GST_DEBUG_OBJECT (dsink, "Wrong AC3/DTS caps, not accepting");
370         goto done;
371       }
372     }
373     default:
374       break;
375   }
376   ret = TRUE;
377   GST_DEBUG_OBJECT (dsink, "Accepting caps");
378 
379 done:
380   gst_query_set_accept_caps_result (query, ret);
381   return TRUE;
382 }
383 
384 static gboolean
gst_directsound_sink_query(GstBaseSink * sink,GstQuery * query)385 gst_directsound_sink_query (GstBaseSink * sink, GstQuery * query)
386 {
387   gboolean res = TRUE;
388 
389   switch (GST_QUERY_TYPE (query)) {
390     case GST_QUERY_ACCEPT_CAPS:
391       res = gst_directsound_sink_acceptcaps (sink, query);
392       break;
393     default:
394       res = GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
395   }
396 
397   return res;
398 }
399 
400 static LPGUID
string_to_guid(const gchar * str)401 string_to_guid (const gchar * str)
402 {
403   HRESULT ret;
404   gunichar2 *wstr;
405   LPGUID out;
406 
407   wstr = g_utf8_to_utf16 (str, -1, NULL, NULL, NULL);
408   if (!wstr)
409     return NULL;
410 
411   out = g_new (GUID, 1);
412   ret = CLSIDFromString ((LPOLESTR) wstr, out);
413   g_free (wstr);
414   if (ret != NOERROR) {
415     g_free (out);
416     return NULL;
417   }
418 
419   return out;
420 }
421 
422 static gboolean
gst_directsound_sink_open(GstAudioSink * asink)423 gst_directsound_sink_open (GstAudioSink * asink)
424 {
425   GstDirectSoundSink *dsoundsink;
426   HRESULT hRes;
427   LPGUID lpGuid = NULL;
428 
429   dsoundsink = GST_DIRECTSOUND_SINK (asink);
430 
431   if (dsoundsink->device_id) {
432     lpGuid = string_to_guid (dsoundsink->device_id);
433     if (lpGuid == NULL) {
434       GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
435           ("device set but guid not found: %s", dsoundsink->device_id), (NULL));
436       return FALSE;
437     }
438   }
439 
440   /* create and initialize a DirectSound object */
441   if (FAILED (hRes = DirectSoundCreate (lpGuid, &dsoundsink->pDS, NULL))) {
442     gchar *error_text = gst_hres_to_string (hRes);
443     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
444         ("DirectSoundCreate: %s", error_text), (NULL));
445     g_free (lpGuid);
446     g_free (error_text);
447     return FALSE;
448   }
449 
450   g_free (lpGuid);
451 
452   if (FAILED (hRes = IDirectSound_SetCooperativeLevel (dsoundsink->pDS,
453               GetDesktopWindow (), DSSCL_PRIORITY))) {
454     gchar *error_text = gst_hres_to_string (hRes);
455     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
456         ("IDirectSound_SetCooperativeLevel: %s", error_text), (NULL));
457     g_free (error_text);
458     return FALSE;
459   }
460 
461   return TRUE;
462 }
463 
464 static gboolean
gst_directsound_sink_is_spdif_format(GstAudioRingBufferSpec * spec)465 gst_directsound_sink_is_spdif_format (GstAudioRingBufferSpec * spec)
466 {
467   return spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3 ||
468       spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS;
469 }
470 
471 static gboolean
gst_directsound_sink_prepare(GstAudioSink * asink,GstAudioRingBufferSpec * spec)472 gst_directsound_sink_prepare (GstAudioSink * asink,
473     GstAudioRingBufferSpec * spec)
474 {
475   GstDirectSoundSink *dsoundsink;
476   HRESULT hRes;
477   DSBUFFERDESC descSecondary;
478   WAVEFORMATEX wfx;
479 
480   dsoundsink = GST_DIRECTSOUND_SINK (asink);
481 
482   /*save number of bytes per sample and buffer format */
483   dsoundsink->bytes_per_sample = spec->info.bpf;
484   dsoundsink->type = spec->type;
485 
486   /* fill the WAVEFORMATEX structure with spec params */
487   memset (&wfx, 0, sizeof (wfx));
488   if (!gst_directsound_sink_is_spdif_format (spec)) {
489     wfx.cbSize = sizeof (wfx);
490     wfx.wFormatTag = WAVE_FORMAT_PCM;
491     wfx.nChannels = spec->info.channels;
492     wfx.nSamplesPerSec = spec->info.rate;
493     wfx.wBitsPerSample = (spec->info.bpf * 8) / wfx.nChannels;
494     wfx.nBlockAlign = spec->info.bpf;
495     wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
496 
497     /* Create directsound buffer with size based on our configured
498      * buffer_size (which is 200 ms by default) */
499     dsoundsink->buffer_size =
500         gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->buffer_time,
501         GST_MSECOND);
502     /* Make sure we make those numbers multiple of our sample size in bytes */
503     dsoundsink->buffer_size -= dsoundsink->buffer_size % spec->info.bpf;
504 
505     spec->segsize =
506         gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->latency_time,
507         GST_MSECOND);
508     spec->segsize -= spec->segsize % spec->info.bpf;
509     spec->segtotal = dsoundsink->buffer_size / spec->segsize;
510   } else {
511 #ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF
512     wfx.cbSize = 0;
513     wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
514     wfx.nChannels = 2;
515     wfx.nSamplesPerSec = 48000;
516     wfx.wBitsPerSample = 16;
517     wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
518     wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
519 
520     spec->segsize = 6144;
521     spec->segtotal = 10;
522 #else
523     g_assert_not_reached ();
524 #endif
525   }
526 
527   // Make the final buffer size be an integer number of segments
528   dsoundsink->buffer_size = spec->segsize * spec->segtotal;
529 
530   GST_INFO_OBJECT (dsoundsink, "channels: %d, rate: %d, bytes_per_sample: %d"
531       " WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d,"
532       " WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n"
533       "Size of dsound circular buffer=>%d\n",
534       GST_AUDIO_INFO_CHANNELS (&spec->info), GST_AUDIO_INFO_RATE (&spec->info),
535       GST_AUDIO_INFO_BPF (&spec->info), wfx.nSamplesPerSec, wfx.wBitsPerSample,
536       wfx.nBlockAlign, wfx.nAvgBytesPerSec, dsoundsink->buffer_size);
537 
538   /* create a secondary directsound buffer */
539   memset (&descSecondary, 0, sizeof (DSBUFFERDESC));
540   descSecondary.dwSize = sizeof (DSBUFFERDESC);
541   descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
542   if (!gst_directsound_sink_is_spdif_format (spec))
543     descSecondary.dwFlags |= DSBCAPS_CTRLVOLUME;
544 
545   descSecondary.dwBufferBytes = dsoundsink->buffer_size;
546   descSecondary.lpwfxFormat = (WAVEFORMATEX *) & wfx;
547 
548   hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
549       &dsoundsink->pDSBSecondary, NULL);
550   if (FAILED (hRes)) {
551     gchar *error_text = gst_hres_to_string (hRes);
552     GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
553         ("IDirectSound_CreateSoundBuffer: %s", error_text), (NULL));
554     g_free (error_text);
555     return FALSE;
556   }
557 
558   gst_directsound_sink_set_volume (dsoundsink,
559       gst_directsound_sink_get_volume (dsoundsink), FALSE);
560   gst_directsound_sink_set_mute (dsoundsink, dsoundsink->mute);
561 
562   return TRUE;
563 }
564 
565 static gboolean
gst_directsound_sink_unprepare(GstAudioSink * asink)566 gst_directsound_sink_unprepare (GstAudioSink * asink)
567 {
568   GstDirectSoundSink *dsoundsink;
569 
570   dsoundsink = GST_DIRECTSOUND_SINK (asink);
571 
572   /* release secondary DirectSound buffer */
573   if (dsoundsink->pDSBSecondary) {
574     IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary);
575     dsoundsink->pDSBSecondary = NULL;
576   }
577 
578   return TRUE;
579 }
580 
581 static gboolean
gst_directsound_sink_close(GstAudioSink * asink)582 gst_directsound_sink_close (GstAudioSink * asink)
583 {
584   GstDirectSoundSink *dsoundsink = NULL;
585 
586   dsoundsink = GST_DIRECTSOUND_SINK (asink);
587 
588   /* release DirectSound object */
589   g_return_val_if_fail (dsoundsink->pDS != NULL, FALSE);
590   IDirectSound_Release (dsoundsink->pDS);
591   dsoundsink->pDS = NULL;
592 
593   gst_caps_replace (&dsoundsink->cached_caps, NULL);
594 
595   return TRUE;
596 }
597 
598 static gint
gst_directsound_sink_write(GstAudioSink * asink,gpointer data,guint length)599 gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length)
600 {
601   GstDirectSoundSink *dsoundsink;
602   DWORD dwStatus = 0;
603   HRESULT hRes, hRes2;
604   LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL;
605   DWORD dwSizeBuffer1, dwSizeBuffer2;
606   DWORD dwCurrentPlayCursor;
607 
608   dsoundsink = GST_DIRECTSOUND_SINK (asink);
609 
610   GST_DSOUND_LOCK (dsoundsink);
611 
612   /* get current buffer status */
613   hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
614 
615   /* get current play cursor position */
616   hRes2 = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
617       &dwCurrentPlayCursor, NULL);
618 
619   if (SUCCEEDED (hRes) && SUCCEEDED (hRes2) && (dwStatus & DSBSTATUS_PLAYING)) {
620     DWORD dwFreeBufferSize = 0;
621     GstClockTime sleep_time_ms = 0, sleep_until;
622     GstClockID clock_id;
623 
624   calculate_freesize:
625     /* Calculate the free space in the circular buffer */
626     if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
627       dwFreeBufferSize =
628           dsoundsink->buffer_size - (dsoundsink->current_circular_offset -
629           dwCurrentPlayCursor);
630     else
631       dwFreeBufferSize =
632           dwCurrentPlayCursor - dsoundsink->current_circular_offset;
633 
634     /* Not enough free space, wait for some samples to be played out. We could
635      * write out partial data, but that will result in a tight loop in the
636      * audioringbuffer write thread, and lead to high CPU usage. */
637     if (length > dwFreeBufferSize) {
638       gint rate = GST_AUDIO_BASE_SINK (asink)->ringbuffer->spec.info.rate;
639       /* Wait for a time proportional to the space needed. In reality, the
640        * directsound sink's position does not update frequently enough, so we
641        * will end up waiting for much longer. Note that Sleep() has millisecond
642        * resolution at best. */
643       sleep_time_ms = gst_util_uint64_scale_int ((length - dwFreeBufferSize),
644           1000, dsoundsink->bytes_per_sample * rate);
645       /* Make sure we don't run in a tight loop unnecessarily */
646       sleep_time_ms = MAX (sleep_time_ms, 10);
647       sleep_until = gst_clock_get_time (dsoundsink->system_clock) +
648           sleep_time_ms * GST_MSECOND;
649 
650       GST_DEBUG_OBJECT (dsoundsink,
651           "length: %u, FreeBufSiz: %ld, sleep_time_ms: %" G_GUINT64_FORMAT
652           ", bps: %i, rate: %i", length, dwFreeBufferSize, sleep_time_ms,
653           dsoundsink->bytes_per_sample, rate);
654 
655       if (G_UNLIKELY (dsoundsink->write_wait_clock_id == NULL ||
656               gst_clock_single_shot_id_reinit (dsoundsink->system_clock,
657                   dsoundsink->write_wait_clock_id, sleep_until) == FALSE)) {
658 
659         if (dsoundsink->write_wait_clock_id != NULL) {
660           gst_clock_id_unref (dsoundsink->write_wait_clock_id);
661         }
662 
663         dsoundsink->write_wait_clock_id =
664             gst_clock_new_single_shot_id (dsoundsink->system_clock,
665             sleep_until);
666       }
667 
668       clock_id = dsoundsink->write_wait_clock_id;
669       dsoundsink->reset_while_sleeping = FALSE;
670 
671       GST_DSOUND_UNLOCK (dsoundsink);
672 
673       /* don't bother with the return value as we'll detect reset separately,
674          as reset could happen between when this returns and we obtain the lock
675          again -- so we can't use UNSCHEDULED here */
676       gst_clock_id_wait (clock_id, NULL);
677 
678       GST_DSOUND_LOCK (dsoundsink);
679 
680       /* if a reset occurs, exit now */
681       if (dsoundsink->reset_while_sleeping == TRUE) {
682         GST_DSOUND_UNLOCK (dsoundsink);
683         return -1;
684       }
685 
686       /* May we send out? */
687       hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
688           &dwCurrentPlayCursor, NULL);
689       hRes2 =
690           IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
691       if (SUCCEEDED (hRes) && SUCCEEDED (hRes2)
692           && (dwStatus & DSBSTATUS_PLAYING))
693         goto calculate_freesize;
694       else {
695         gchar *err1, *err2;
696 
697         dsoundsink->first_buffer_after_reset = FALSE;
698         GST_DSOUND_UNLOCK (dsoundsink);
699 
700         err1 = gst_hres_to_string (hRes);
701         err2 = gst_hres_to_string (hRes2);
702         GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_WRITE,
703             ("IDirectSoundBuffer_GetStatus %s, "
704                 "IDirectSoundBuffer_GetCurrentPosition: %s, dwStatus: %lu",
705                 err2, err1, dwStatus), (NULL));
706         g_free (err1);
707         g_free (err2);
708         return -1;
709       }
710     }
711   }
712 
713   if (dwStatus & DSBSTATUS_BUFFERLOST) {
714     hRes = IDirectSoundBuffer_Restore (dsoundsink->pDSBSecondary);      /*need a loop waiting the buffer is restored?? */
715     dsoundsink->current_circular_offset = 0;
716   }
717 
718   /* Lock a buffer of length @length for writing */
719   hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
720       dsoundsink->current_circular_offset, length, &pLockedBuffer1,
721       &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
722 
723   if (SUCCEEDED (hRes)) {
724     // Write to pointers without reordering.
725     memcpy (pLockedBuffer1, data, dwSizeBuffer1);
726     if (pLockedBuffer2 != NULL)
727       memcpy (pLockedBuffer2, (LPBYTE) data + dwSizeBuffer1, dwSizeBuffer2);
728 
729     hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer1,
730         dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
731 
732     // Update where the buffer will lock (for next time)
733     dsoundsink->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
734     dsoundsink->current_circular_offset %= dsoundsink->buffer_size;     /* Circular buffer */
735   }
736 
737   /* if the buffer was not in playing state yet, call play on the buffer
738      except if this buffer is the fist after a reset (base class call reset and write a buffer when setting the sink to pause) */
739   if (!(dwStatus & DSBSTATUS_PLAYING) &&
740       dsoundsink->first_buffer_after_reset == FALSE) {
741     hRes = IDirectSoundBuffer_Play (dsoundsink->pDSBSecondary, 0, 0,
742         DSBPLAY_LOOPING);
743   }
744 
745   dsoundsink->first_buffer_after_reset = FALSE;
746 
747   GST_DSOUND_UNLOCK (dsoundsink);
748 
749   return length;
750 }
751 
752 static guint
gst_directsound_sink_delay(GstAudioSink * asink)753 gst_directsound_sink_delay (GstAudioSink * asink)
754 {
755   GstDirectSoundSink *dsoundsink;
756   HRESULT hRes;
757   DWORD dwCurrentPlayCursor;
758   DWORD dwBytesInQueue = 0;
759   gint nNbSamplesInQueue = 0;
760   DWORD dwStatus;
761 
762   dsoundsink = GST_DIRECTSOUND_SINK (asink);
763 
764   /* get current buffer status */
765   hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
766 
767   if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) {
768     /*evaluate the number of samples in queue in the circular buffer */
769     hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
770         &dwCurrentPlayCursor, NULL);
771 
772     if (hRes == S_OK) {
773       if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
774         dwBytesInQueue =
775             dsoundsink->current_circular_offset - dwCurrentPlayCursor;
776       else
777         dwBytesInQueue =
778             dsoundsink->current_circular_offset + (dsoundsink->buffer_size -
779             dwCurrentPlayCursor);
780 
781       nNbSamplesInQueue = dwBytesInQueue / dsoundsink->bytes_per_sample;
782     }
783   }
784 
785   return nNbSamplesInQueue;
786 }
787 
788 static void
gst_directsound_sink_reset(GstAudioSink * asink)789 gst_directsound_sink_reset (GstAudioSink * asink)
790 {
791   GstDirectSoundSink *dsoundsink;
792   LPVOID pLockedBuffer = NULL;
793   DWORD dwSizeBuffer = 0;
794 
795   dsoundsink = GST_DIRECTSOUND_SINK (asink);
796 
797   GST_DSOUND_LOCK (dsoundsink);
798 
799   if (dsoundsink->pDSBSecondary) {
800     /*stop playing */
801     HRESULT hRes = IDirectSoundBuffer_Stop (dsoundsink->pDSBSecondary);
802 
803     /*reset position */
804     hRes = IDirectSoundBuffer_SetCurrentPosition (dsoundsink->pDSBSecondary, 0);
805     dsoundsink->current_circular_offset = 0;
806 
807     /*reset the buffer */
808     hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
809         0, dsoundsink->buffer_size,
810         &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
811 
812     if (SUCCEEDED (hRes)) {
813       memset (pLockedBuffer, 0, dwSizeBuffer);
814 
815       hRes =
816           IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer,
817           dwSizeBuffer, NULL, 0);
818     }
819   }
820 
821   dsoundsink->reset_while_sleeping = TRUE;
822   dsoundsink->first_buffer_after_reset = TRUE;
823   if (dsoundsink->write_wait_clock_id != NULL) {
824     gst_clock_id_unschedule (dsoundsink->write_wait_clock_id);
825   }
826 
827   GST_DSOUND_UNLOCK (dsoundsink);
828 }
829 
830 /*
831  * gst_directsound_probe_supported_formats:
832  *
833  * Takes the template caps and returns the subset which is actually
834  * supported by this device.
835  *
836  */
837 
838 static GstCaps *
gst_directsound_probe_supported_formats(GstDirectSoundSink * dsoundsink,const GstCaps * template_caps)839 gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink,
840     const GstCaps * template_caps)
841 {
842   HRESULT hRes;
843   DSBUFFERDESC descSecondary;
844   WAVEFORMATEX wfx;
845   GstCaps *caps;
846   GstCaps *tmp, *tmp2;
847   LPDIRECTSOUNDBUFFER tmpBuffer;
848 
849   caps = gst_caps_copy (template_caps);
850 
851   /*
852    * Check availability of digital output by trying to create an SPDIF buffer
853    */
854 
855 #ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF
856   /* fill the WAVEFORMATEX structure with some standard AC3 over SPDIF params */
857   memset (&wfx, 0, sizeof (wfx));
858   wfx.cbSize = 0;
859   wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
860   wfx.nChannels = 2;
861   wfx.nSamplesPerSec = 48000;
862   wfx.wBitsPerSample = 16;
863   wfx.nBlockAlign = 4;
864   wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
865 
866   // create a secondary directsound buffer
867   memset (&descSecondary, 0, sizeof (DSBUFFERDESC));
868   descSecondary.dwSize = sizeof (DSBUFFERDESC);
869   descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
870   descSecondary.dwBufferBytes = 6144;
871   descSecondary.lpwfxFormat = &wfx;
872 
873   hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
874       &tmpBuffer, NULL);
875   if (FAILED (hRes)) {
876     gchar *error_text = gst_hres_to_string (hRes);
877     GST_INFO_OBJECT (dsoundsink, "AC3 passthrough not supported "
878         "(IDirectSound_CreateSoundBuffer returned: %s)\n", error_text);
879     g_free (error_text);
880     tmp = gst_caps_new_empty_simple ("audio/x-ac3");
881     tmp2 = gst_caps_subtract (caps, tmp);
882     gst_caps_unref (tmp);
883     gst_caps_unref (caps);
884     caps = tmp2;
885     tmp = gst_caps_new_empty_simple ("audio/x-dts");
886     tmp2 = gst_caps_subtract (caps, tmp);
887     gst_caps_unref (tmp);
888     gst_caps_unref (caps);
889     caps = tmp2;
890   } else {
891     GST_INFO_OBJECT (dsoundsink, "AC3 passthrough supported");
892     hRes = IDirectSoundBuffer_Release (tmpBuffer);
893     if (FAILED (hRes)) {
894       gchar *error_text = gst_hres_to_string (hRes);
895       GST_DEBUG_OBJECT (dsoundsink,
896           "(IDirectSoundBuffer_Release returned: %s)\n", error_text);
897       g_free (error_text);
898     }
899   }
900 #else
901   tmp = gst_caps_new_empty_simple ("audio/x-ac3");
902   tmp2 = gst_caps_subtract (caps, tmp);
903   gst_caps_unref (tmp);
904   gst_caps_unref (caps);
905   caps = tmp2;
906   tmp = gst_caps_new_empty_simple ("audio/x-dts");
907   tmp2 = gst_caps_subtract (caps, tmp);
908   gst_caps_unref (tmp);
909   gst_caps_unref (caps);
910   caps = tmp2;
911 #endif
912 
913   return caps;
914 }
915 
916 static GstBuffer *
gst_directsound_sink_payload(GstAudioBaseSink * sink,GstBuffer * buf)917 gst_directsound_sink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
918 {
919   if (gst_directsound_sink_is_spdif_format (&sink->ringbuffer->spec)) {
920     gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec);
921     GstBuffer *out;
922     GstMapInfo infobuf, infoout;
923     gboolean success;
924 
925     if (framesize <= 0)
926       return NULL;
927 
928     out = gst_buffer_new_and_alloc (framesize);
929 
930     if (!gst_buffer_map (buf, &infobuf, GST_MAP_READWRITE)) {
931       gst_buffer_unref (out);
932       return NULL;
933     }
934     if (!gst_buffer_map (out, &infoout, GST_MAP_READWRITE)) {
935       gst_buffer_unmap (buf, &infobuf);
936       gst_buffer_unref (out);
937       return NULL;
938     }
939     success = gst_audio_iec61937_payload (infobuf.data, infobuf.size,
940         infoout.data, infoout.size, &sink->ringbuffer->spec, G_BYTE_ORDER);
941     if (!success) {
942       gst_buffer_unmap (out, &infoout);
943       gst_buffer_unmap (buf, &infobuf);
944       gst_buffer_unref (out);
945       return NULL;
946     }
947 
948     gst_buffer_copy_into (out, buf, GST_BUFFER_COPY_ALL, 0, -1);
949     /* Fix endianness */
950     _swab ((gchar *) infoout.data, (gchar *) infoout.data, infobuf.size);
951     gst_buffer_unmap (out, &infoout);
952     gst_buffer_unmap (buf, &infobuf);
953     return out;
954   } else
955     return gst_buffer_ref (buf);
956 }
957 
958 static void
gst_directsound_sink_set_volume(GstDirectSoundSink * dsoundsink,gdouble dvolume,gboolean store)959 gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink,
960     gdouble dvolume, gboolean store)
961 {
962   glong volume;
963 
964   volume = dvolume * 100;
965   if (store)
966     dsoundsink->volume = volume;
967 
968   if (dsoundsink->pDSBSecondary) {
969     /* DirectSound controls volume using units of 100th of a decibel,
970      * ranging from -10000 to 0. We use a linear scale of 0 - 100
971      * here, so remap.
972      */
973     long dsVolume;
974     if (volume == 0 || dsoundsink->mute)
975       dsVolume = -10000;
976     else
977       dsVolume = 100 * (long) (20 * log10 ((double) volume / 100.));
978     dsVolume = CLAMP (dsVolume, -10000, 0);
979 
980     GST_DEBUG_OBJECT (dsoundsink,
981         "Setting volume on secondary buffer to %d from %d", (int) dsVolume,
982         (int) volume);
983     IDirectSoundBuffer_SetVolume (dsoundsink->pDSBSecondary, dsVolume);
984   }
985 }
986 
987 gdouble
gst_directsound_sink_get_volume(GstDirectSoundSink * dsoundsink)988 gst_directsound_sink_get_volume (GstDirectSoundSink * dsoundsink)
989 {
990   return (gdouble) dsoundsink->volume / 100;
991 }
992 
993 static void
gst_directsound_sink_set_mute(GstDirectSoundSink * dsoundsink,gboolean mute)994 gst_directsound_sink_set_mute (GstDirectSoundSink * dsoundsink, gboolean mute)
995 {
996   if (mute) {
997     gst_directsound_sink_set_volume (dsoundsink, 0, FALSE);
998     dsoundsink->mute = TRUE;
999   } else {
1000     gst_directsound_sink_set_volume (dsoundsink,
1001         gst_directsound_sink_get_volume (dsoundsink), FALSE);
1002     dsoundsink->mute = FALSE;
1003   }
1004 
1005 }
1006 
1007 static gboolean
gst_directsound_sink_get_mute(GstDirectSoundSink * dsoundsink)1008 gst_directsound_sink_get_mute (GstDirectSoundSink * dsoundsink)
1009 {
1010   return dsoundsink->mute;
1011 }
1012 
1013 static const gchar *
gst_directsound_sink_get_device(GstDirectSoundSink * dsoundsink)1014 gst_directsound_sink_get_device (GstDirectSoundSink * dsoundsink)
1015 {
1016   return dsoundsink->device_id;
1017 }
1018 
1019 static void
gst_directsound_sink_set_device(GstDirectSoundSink * dsoundsink,const gchar * device_id)1020 gst_directsound_sink_set_device (GstDirectSoundSink * dsoundsink,
1021     const gchar * device_id)
1022 {
1023   g_free (dsoundsink->device_id);
1024   dsoundsink->device_id = g_strdup (device_id);
1025 }
1026 
1027 /* Converts a HRESULT error to a text string
1028  * LPTSTR is either a */
1029 static gchar *
gst_hres_to_string(HRESULT hRes)1030 gst_hres_to_string (HRESULT hRes)
1031 {
1032   DWORD flags;
1033   gchar *ret_text;
1034   LPTSTR error_text = NULL;
1035 
1036   flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
1037       | FORMAT_MESSAGE_IGNORE_INSERTS;
1038   FormatMessage (flags, NULL, hRes, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
1039       (LPTSTR) & error_text, 0, NULL);
1040 
1041 #ifdef UNICODE
1042   /* If UNICODE is defined, LPTSTR is LPWSTR which is UTF-16 */
1043   ret_text = g_utf16_to_utf8 (error_text, 0, NULL, NULL, NULL);
1044 #else
1045   ret_text = g_strdup (error_text);
1046 #endif
1047 
1048   LocalFree (error_text);
1049   return ret_text;
1050 }
1051