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 * @title: directsoundsink
32 *
33 * This element lets you output sound using the DirectSound API.
34 *
35 * Note that you should almost always use generic audio conversion elements
36 * like audioconvert and audioresample in front of an audiosink to make sure
37 * your pipeline works under all circumstances (those conversion elements will
38 * act in passthrough-mode if no conversion is necessary).
39 *
40 * ## Example pipelines
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 *
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