gstdirectsoundsink.c 27.4 KB
Newer Older
1
2
/* GStreamer
* 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
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
*
* gstdirectsoundsink.c:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
22
23
24
25
26
*
*
* The development of this code was made possible due to the involvement
* of Pioneers of the Inevitable, the creators of the Songbird Music player
*
27
28
*/

29
/**
30
 * SECTION:element-directsoundsink
31
32
 *
 * This element lets you output sound using the DirectSound API.
33
 *
34
35
36
37
 * Note that you should almost always use generic audio conversion elements
 * like audioconvert and audioresample in front of an audiosink to make sure
 * your pipeline works under all circumstances (those conversion elements will
 * 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
49
50
 * </refsect2>
 */

51
52
53
54
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

55
56
#include <gst/base/gstbasesink.h>
#include <gst/audio/streamvolume.h>
57
#include "gstdirectsoundsink.h"
58
#include <gst/audio/gstaudioiec61937.h>
59

60
61
#include <math.h>

62
63
64
65
66
67
68
#ifdef __CYGWIN__
#include <unistd.h>
#ifndef _swab
#define _swab swab
#endif
#endif

69
70
#define DEFAULT_MUTE FALSE

71
GST_DEBUG_CATEGORY_STATIC (directsoundsink_debug);
72
73
#define GST_CAT_DEFAULT directsoundsink_debug

74
static void gst_directsound_sink_finalize (GObject * object);
75

76
77
78
79
80
static void gst_directsound_sink_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_directsound_sink_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);

81
82
static GstCaps *gst_directsound_sink_getcaps (GstBaseSink * bsink,
    GstCaps * filter);
83
84
static GstBuffer *gst_directsound_sink_payload (GstAudioBaseSink * sink,
    GstBuffer * buf);
85
static gboolean gst_directsound_sink_prepare (GstAudioSink * asink,
86
    GstAudioRingBufferSpec * spec);
87
88
89
static gboolean gst_directsound_sink_unprepare (GstAudioSink * asink);
static gboolean gst_directsound_sink_open (GstAudioSink * asink);
static gboolean gst_directsound_sink_close (GstAudioSink * asink);
90
91
static gint gst_directsound_sink_write (GstAudioSink * asink,
    gpointer data, guint length);
92
93
static guint gst_directsound_sink_delay (GstAudioSink * asink);
static void gst_directsound_sink_reset (GstAudioSink * asink);
94
95
static GstCaps *gst_directsound_probe_supported_formats (GstDirectSoundSink *
    dsoundsink, const GstCaps * template_caps);
96
97
static gboolean gst_directsound_sink_query (GstBaseSink * pad,
    GstQuery * query);
98

99
100
101
102
103
104
static void gst_directsound_sink_set_volume (GstDirectSoundSink * sink,
    gdouble volume, gboolean store);
static gdouble gst_directsound_sink_get_volume (GstDirectSoundSink * sink);
static void gst_directsound_sink_set_mute (GstDirectSoundSink * sink,
    gboolean mute);
static gboolean gst_directsound_sink_get_mute (GstDirectSoundSink * sink);
105

106
107
static gboolean gst_directsound_sink_is_spdif_format (GstAudioRingBufferSpec *
    spec);
108

109
110
111
112
static GstStaticPadTemplate directsoundsink_sink_factory =
    GST_STATIC_PAD_TEMPLATE ("sink",
    GST_PAD_SINK,
    GST_PAD_ALWAYS,
113
114
115
    GST_STATIC_CAPS ("audio/x-raw, "
        "format = (string) S16LE, "
        "layout = (string) interleaved, "
116
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
117
118
119
        "audio/x-raw, "
        "format = (string) S8, "
        "layout = (string) interleaved, "
120
        "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ];"
121
122
        "audio/x-ac3, framed = (boolean) true;"
        "audio/x-dts, framed = (boolean) true;"));
123

124
125
126
enum
{
  PROP_0,
127
128
  PROP_VOLUME,
  PROP_MUTE
129
130
};

131
132
133
134
#define gst_directsound_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSink, gst_directsound_sink,
    GST_TYPE_AUDIO_SINK, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL)
    );
135
136

static void
137
gst_directsound_sink_finalize (GObject * object)
138
{
139
140
141
142
  GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (object);

  g_mutex_clear (&dsoundsink->dsound_lock);

143
144
145
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

146
static void
147
gst_directsound_sink_class_init (GstDirectSoundSinkClass * klass)
148
{
149
150
151
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
  GstAudioSinkClass *gstaudiosink_class = GST_AUDIO_SINK_CLASS (klass);
152
153
  GstAudioBaseSinkClass *gstaudiobasesink_class =
      GST_AUDIO_BASE_SINK_CLASS (klass);
154
  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
155

156
157
158
  GST_DEBUG_CATEGORY_INIT (directsoundsink_debug, "directsoundsink", 0,
      "DirectSound sink");

159
  parent_class = g_type_class_peek_parent (klass);
160

161
  gobject_class->finalize = gst_directsound_sink_finalize;
162
163
  gobject_class->set_property = gst_directsound_sink_set_property;
  gobject_class->get_property = gst_directsound_sink_get_property;
164

165
166
  gstbasesink_class->get_caps =
      GST_DEBUG_FUNCPTR (gst_directsound_sink_getcaps);
167

168
  gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_directsound_sink_query);
169
170
171
172

  gstaudiobasesink_class->payload =
      GST_DEBUG_FUNCPTR (gst_directsound_sink_payload);

173
174
  gstaudiosink_class->prepare =
      GST_DEBUG_FUNCPTR (gst_directsound_sink_prepare);
175
  gstaudiosink_class->unprepare =
176
177
178
179
180
181
      GST_DEBUG_FUNCPTR (gst_directsound_sink_unprepare);
  gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_directsound_sink_open);
  gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_directsound_sink_close);
  gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_directsound_sink_write);
  gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_sink_delay);
  gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_sink_reset);
182
183
184
185
186
187

  g_object_class_install_property (gobject_class,
      PROP_VOLUME,
      g_param_spec_double ("volume", "Volume",
          "Volume of this stream", 0.0, 1.0, 1.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
188
189
190
191
192
193
194

  g_object_class_install_property (gobject_class,
      PROP_MUTE,
      g_param_spec_boolean ("mute", "Mute",
          "Mute state of this stream", DEFAULT_MUTE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

195
  gst_element_class_set_static_metadata (element_class,
196
197
198
199
200
201
      "Direct Sound Audio Sink", "Sink/Audio",
      "Output to a sound card via Direct Sound",
      "Sebastien Moutte <sebastien@moutte.net>");

  gst_element_class_add_pad_template (element_class,
      gst_static_pad_template_get (&directsoundsink_sink_factory));
202
203
204
}

static void
205
gst_directsound_sink_init (GstDirectSoundSink * dsoundsink)
206
{
207
208
  dsoundsink->volume = 100;
  dsoundsink->mute = FALSE;
209
  dsoundsink->pDS = NULL;
210
  dsoundsink->cached_caps = NULL;
211
212
213
  dsoundsink->pDSBSecondary = NULL;
  dsoundsink->current_circular_offset = 0;
  dsoundsink->buffer_size = DSBSIZE_MIN;
214
  dsoundsink->volume = 100;
215
  g_mutex_init (&dsoundsink->dsound_lock);
216
  dsoundsink->first_buffer_after_reset = FALSE;
217
218
}

219
220
221
222
223
224
225
226
static void
gst_directsound_sink_set_property (GObject * object,
    guint prop_id, const GValue * value, GParamSpec * pspec)
{
  GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);

  switch (prop_id) {
    case PROP_VOLUME:
227
228
229
230
      gst_directsound_sink_set_volume (sink, g_value_get_double (value), TRUE);
      break;
    case PROP_MUTE:
      gst_directsound_sink_set_mute (sink, g_value_get_boolean (value));
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
gst_directsound_sink_get_property (GObject * object,
    guint prop_id, GValue * value, GParamSpec * pspec)
{
  GstDirectSoundSink *sink = GST_DIRECTSOUND_SINK (object);

  switch (prop_id) {
    case PROP_VOLUME:
246
247
248
249
      g_value_set_double (value, gst_directsound_sink_get_volume (sink));
      break;
    case PROP_MUTE:
      g_value_set_boolean (value, gst_directsound_sink_get_mute (sink));
250
251
252
253
254
255
256
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

257
static GstCaps *
258
gst_directsound_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
259
{
260
261
262
263
  GstElementClass *element_class;
  GstPadTemplate *pad_template;
  GstDirectSoundSink *dsoundsink = GST_DIRECTSOUND_SINK (bsink);
  GstCaps *caps;
264
  gchar *caps_string = NULL;
265
266
267
268
269

  if (dsoundsink->pDS == NULL) {
    GST_DEBUG_OBJECT (dsoundsink, "device not open, using template caps");
    return NULL;                /* base class will get template caps for us */
  }
270

271
  if (dsoundsink->cached_caps) {
272
273
274
    caps_string = gst_caps_to_string (dsoundsink->cached_caps);
    GST_DEBUG_OBJECT (dsoundsink, "Returning cached caps: %s", caps_string);
    g_free (caps_string);
275
276
277
278
279
280
    return gst_caps_ref (dsoundsink->cached_caps);
  }

  element_class = GST_ELEMENT_GET_CLASS (dsoundsink);
  pad_template = gst_element_class_get_pad_template (element_class, "sink");
  g_return_val_if_fail (pad_template != NULL, NULL);
281

282
283
284
285
286
287
  caps = gst_directsound_probe_supported_formats (dsoundsink,
      gst_pad_template_get_caps (pad_template));
  if (caps) {
    dsoundsink->cached_caps = gst_caps_ref (caps);
  }

288
289
290
291
292
  if (caps) {
    gchar *caps_string = gst_caps_to_string (caps);
    GST_DEBUG_OBJECT (dsoundsink, "returning caps %s", caps_string);
    g_free (caps_string);
  }
293
294

  return caps;
295
296
}

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
static gboolean
gst_directsound_sink_acceptcaps (GstBaseSink * sink, GstQuery * query)
{
  GstDirectSoundSink *dsink = GST_DIRECTSOUND_SINK (sink);
  GstPad *pad;
  GstCaps *caps;
  GstCaps *pad_caps;
  GstStructure *st;
  gboolean ret = FALSE;
  GstAudioRingBufferSpec spec = { 0 };

  if (G_UNLIKELY (dsink == NULL))
    return FALSE;

  pad = sink->sinkpad;

  gst_query_parse_accept_caps (query, &caps);
  GST_DEBUG_OBJECT (pad, "caps %" GST_PTR_FORMAT, caps);

  pad_caps = gst_pad_query_caps (pad, NULL);
  if (pad_caps) {
318
    gboolean cret = gst_caps_can_intersect (pad_caps, caps);
319
    gst_caps_unref (pad_caps);
320
    if (!cret) {
321
      GST_DEBUG_OBJECT (dsink, "Can't intersect caps, not accepting caps");
322
      goto done;
323
    }
324
325
326
327
  }

  /* If we've not got fixed caps, creating a stream might fail, so let's just
   * return from here with default acceptcaps behaviour */
328
  if (!gst_caps_is_fixed (caps)) {
329
    GST_DEBUG_OBJECT (dsink, "Caps are not fixed, not accepting caps");
330
    goto done;
331
  }
332

333
  spec.latency_time = GST_SECOND;
334
  if (!gst_audio_ring_buffer_parse_caps (&spec, caps)) {
335
    GST_DEBUG_OBJECT (dsink, "Failed to parse caps, not accepting");
336
    goto done;
337
  }
338
339

  /* Make sure input is framed (one frame per buffer) and can be payloaded */
340
  switch (spec.type) {
341
342
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3:
    case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS:
343
344
345
346
347
348
    {
      gboolean framed = FALSE, parsed = FALSE;
      st = gst_caps_get_structure (caps, 0);

      gst_structure_get_boolean (st, "framed", &framed);
      gst_structure_get_boolean (st, "parsed", &parsed);
349
      if ((!framed && !parsed) || gst_audio_iec61937_frame_size (&spec) <= 0) {
350
        GST_DEBUG_OBJECT (dsink, "Wrong AC3/DTS caps, not accepting");
351
        goto done;
352
      }
353
    }
354
355
356
    default:
      break;
  }
357
  ret = TRUE;
358
  GST_DEBUG_OBJECT (dsink, "Accepting caps");
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

done:
  gst_query_set_accept_caps_result (query, ret);
  return TRUE;
}

static gboolean
gst_directsound_sink_query (GstBaseSink * sink, GstQuery * query)
{
  gboolean res = TRUE;

  switch (GST_QUERY_TYPE (query)) {
    case GST_QUERY_ACCEPT_CAPS:
      res = gst_directsound_sink_acceptcaps (sink, query);
      break;
    default:
375
      res = GST_BASE_SINK_CLASS (parent_class)->query (sink, query);
376
377
378
379
380
  }

  return res;
}

381
static gboolean
382
gst_directsound_sink_open (GstAudioSink * asink)
383
{
384
  GstDirectSoundSink *dsoundsink;
385
386
  HRESULT hRes;

387
388
  dsoundsink = GST_DIRECTSOUND_SINK (asink);

389
390
391
  /* create and initialize a DirecSound object */
  if (FAILED (hRes = DirectSoundCreate (NULL, &dsoundsink->pDS, NULL))) {
    GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
392
        ("gst_directsound_sink_open: DirectSoundCreate: %s",
393
394
395
396
            DXGetErrorString9 (hRes)), (NULL));
    return FALSE;
  }

397
  if (FAILED (hRes = IDirectSound_SetCooperativeLevel (dsoundsink->pDS,
398
399
              GetDesktopWindow (), DSSCL_PRIORITY))) {
    GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
400
        ("gst_directsound_sink_open: IDirectSound_SetCooperativeLevel: %s",
401
402
403
404
405
406
407
            DXGetErrorString9 (hRes)), (NULL));
    return FALSE;
  }

  return TRUE;
}

408
static gboolean
409
gst_directsound_sink_is_spdif_format (GstAudioRingBufferSpec * spec)
410
{
411
412
  return spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_AC3 ||
      spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_DTS;
413
414
}

415
static gboolean
416
417
gst_directsound_sink_prepare (GstAudioSink * asink,
    GstAudioRingBufferSpec * spec)
418
{
419
  GstDirectSoundSink *dsoundsink;
420
421
422
423
  HRESULT hRes;
  DSBUFFERDESC descSecondary;
  WAVEFORMATEX wfx;

424
425
  dsoundsink = GST_DIRECTSOUND_SINK (asink);

426
  /*save number of bytes per sample and buffer format */
427
428
  dsoundsink->bytes_per_sample = spec->info.bpf;
  dsoundsink->type = spec->type;
429

430
  /* fill the WAVEFORMATEX structure with spec params */
431
  memset (&wfx, 0, sizeof (wfx));
432
  if (!gst_directsound_sink_is_spdif_format (spec)) {
433
434
    wfx.cbSize = sizeof (wfx);
    wfx.wFormatTag = WAVE_FORMAT_PCM;
435
436
437
438
    wfx.nChannels = spec->info.channels;
    wfx.nSamplesPerSec = spec->info.rate;
    wfx.wBitsPerSample = (spec->info.bpf * 8) / wfx.nChannels;
    wfx.nBlockAlign = spec->info.bpf;
439
440
    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

441
    /* Create directsound buffer with size based on our configured
442
443
444
445
     * buffer_size (which is 200 ms by default) */
    dsoundsink->buffer_size =
        gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->buffer_time,
        GST_MSECOND);
446
    /* Make sure we make those numbers multiple of our sample size in bytes */
447
    dsoundsink->buffer_size += dsoundsink->buffer_size % spec->info.bpf;
448
449
450
451

    spec->segsize =
        gst_util_uint64_scale_int (wfx.nAvgBytesPerSec, spec->latency_time,
        GST_MSECOND);
452
    spec->segsize += spec->segsize % spec->info.bpf;
453
454
    spec->segtotal = dsoundsink->buffer_size / spec->segsize;
  } else {
455
#ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF
456
457
458
    wfx.cbSize = 0;
    wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
    wfx.nChannels = 2;
459
    wfx.nSamplesPerSec = 48000;
460
461
462
463
464
465
    wfx.wBitsPerSample = 16;
    wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
    wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

    spec->segsize = 6144;
    spec->segtotal = 10;
466
467
468
#else
    g_assert_not_reached ();
#endif
469
  }
470
471
472

  // Make the final buffer size be an integer number of segments
  dsoundsink->buffer_size = spec->segsize * spec->segtotal;
473

474
  GST_INFO_OBJECT (dsoundsink,
475
      "GstAudioRingBufferSpec->channels: %d, GstAudioRingBufferSpec->rate: %d, GstAudioRingBufferSpec->bytes_per_sample: %d\n"
476
      "WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d, WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld\n"
477
478
      "Size of dsound circular buffer=>%d\n", spec->info.channels,
      spec->info.rate, spec->info.bpf, wfx.nSamplesPerSec, wfx.wBitsPerSample,
479
480
      wfx.nBlockAlign, wfx.nAvgBytesPerSec, dsoundsink->buffer_size);

481
482
483
  /* create a secondary directsound buffer */
  memset (&descSecondary, 0, sizeof (DSBUFFERDESC));
  descSecondary.dwSize = sizeof (DSBUFFERDESC);
484
  descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
485
  if (!gst_directsound_sink_is_spdif_format (spec))
486
    descSecondary.dwFlags |= DSBCAPS_CTRLVOLUME;
487
488
489
490
491
492
493
494

  descSecondary.dwBufferBytes = dsoundsink->buffer_size;
  descSecondary.lpwfxFormat = (WAVEFORMATEX *) & wfx;

  hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
      &dsoundsink->pDSBSecondary, NULL);
  if (FAILED (hRes)) {
    GST_ELEMENT_ERROR (dsoundsink, RESOURCE, OPEN_READ,
495
        ("gst_directsound_sink_prepare: IDirectSound_CreateSoundBuffer: %s",
496
497
498
499
            DXGetErrorString9 (hRes)), (NULL));
    return FALSE;
  }

500
  gst_directsound_sink_set_volume (dsoundsink, dsoundsink->volume, FALSE);
501

502
503
504
505
  return TRUE;
}

static gboolean
506
gst_directsound_sink_unprepare (GstAudioSink * asink)
507
508
509
510
511
512
{
  GstDirectSoundSink *dsoundsink;

  dsoundsink = GST_DIRECTSOUND_SINK (asink);

  /* release secondary DirectSound buffer */
513
  if (dsoundsink->pDSBSecondary) {
514
    IDirectSoundBuffer_Release (dsoundsink->pDSBSecondary);
515
516
    dsoundsink->pDSBSecondary = NULL;
  }
517
518
519
520
521

  return TRUE;
}

static gboolean
522
gst_directsound_sink_close (GstAudioSink * asink)
523
524
525
526
527
528
529
530
{
  GstDirectSoundSink *dsoundsink = NULL;

  dsoundsink = GST_DIRECTSOUND_SINK (asink);

  /* release DirectSound object */
  g_return_val_if_fail (dsoundsink->pDS != NULL, FALSE);
  IDirectSound_Release (dsoundsink->pDS);
531
532
533
  dsoundsink->pDS = NULL;

  gst_caps_replace (&dsoundsink->cached_caps, NULL);
534
535
536
537

  return TRUE;
}

538
static gint
539
gst_directsound_sink_write (GstAudioSink * asink, gpointer data, guint length)
540
541
542
543
544
545
546
547
548
549
{
  GstDirectSoundSink *dsoundsink;
  DWORD dwStatus;
  HRESULT hRes;
  LPVOID pLockedBuffer1 = NULL, pLockedBuffer2 = NULL;
  DWORD dwSizeBuffer1, dwSizeBuffer2;
  DWORD dwCurrentPlayCursor;

  dsoundsink = GST_DIRECTSOUND_SINK (asink);

550
551
  GST_DSOUND_LOCK (dsoundsink);

552
553
554
555
556
557
  /* get current buffer status */
  hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);

  /* get current play cursor position */
  hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
      &dwCurrentPlayCursor, NULL);
558

559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING)) {
    DWORD dwFreeBufferSize;

  calculate_freesize:
    /* calculate the free size of the circular buffer */
    if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
      dwFreeBufferSize =
          dsoundsink->buffer_size - (dsoundsink->current_circular_offset -
          dwCurrentPlayCursor);
    else
      dwFreeBufferSize =
          dwCurrentPlayCursor - dsoundsink->current_circular_offset;

    if (length >= dwFreeBufferSize) {
      Sleep (100);
      hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
          &dwCurrentPlayCursor, NULL);
576
577
578
579
580
581
582
583
584
585

      hRes =
          IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
      if (SUCCEEDED (hRes) && (dwStatus & DSBSTATUS_PLAYING))
        goto calculate_freesize;
      else {
        dsoundsink->first_buffer_after_reset = FALSE;
        GST_DSOUND_UNLOCK (dsoundsink);
        return 0;
      }
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
    }
  }

  if (dwStatus & DSBSTATUS_BUFFERLOST) {
    hRes = IDirectSoundBuffer_Restore (dsoundsink->pDSBSecondary);      /*need a loop waiting the buffer is restored?? */

    dsoundsink->current_circular_offset = 0;
  }

  hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
      dsoundsink->current_circular_offset, length, &pLockedBuffer1,
      &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);

  if (SUCCEEDED (hRes)) {
    // Write to pointers without reordering.
    memcpy (pLockedBuffer1, data, dwSizeBuffer1);
    if (pLockedBuffer2 != NULL)
      memcpy (pLockedBuffer2, (LPBYTE) data + dwSizeBuffer1, dwSizeBuffer2);

    // Update where the buffer will lock (for next time)
    dsoundsink->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
    dsoundsink->current_circular_offset %= dsoundsink->buffer_size;     /* Circular buffer */

    hRes = IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer1,
        dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
  }

613
614
615
616
  /* if the buffer was not in playing state yet, call play on the buffer 
     except if this buffer is the fist after a reset (base class call reset and write a buffer when setting the sink to pause) */
  if (!(dwStatus & DSBSTATUS_PLAYING) &&
      dsoundsink->first_buffer_after_reset == FALSE) {
617
618
619
620
    hRes = IDirectSoundBuffer_Play (dsoundsink->pDSBSecondary, 0, 0,
        DSBPLAY_LOOPING);
  }

621
622
623
624
  dsoundsink->first_buffer_after_reset = FALSE;

  GST_DSOUND_UNLOCK (dsoundsink);

625
626
627
628
  return length;
}

static guint
629
gst_directsound_sink_delay (GstAudioSink * asink)
630
631
632
633
634
635
{
  GstDirectSoundSink *dsoundsink;
  HRESULT hRes;
  DWORD dwCurrentPlayCursor;
  DWORD dwBytesInQueue = 0;
  gint nNbSamplesInQueue = 0;
636
  DWORD dwStatus;
637
638
639

  dsoundsink = GST_DIRECTSOUND_SINK (asink);

640
641
  /* get current buffer status */
  hRes = IDirectSoundBuffer_GetStatus (dsoundsink->pDSBSecondary, &dwStatus);
642

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
  if (dwStatus & DSBSTATUS_PLAYING) {
    /*evaluate the number of samples in queue in the circular buffer */
    hRes = IDirectSoundBuffer_GetCurrentPosition (dsoundsink->pDSBSecondary,
        &dwCurrentPlayCursor, NULL);

    if (hRes == S_OK) {
      if (dwCurrentPlayCursor < dsoundsink->current_circular_offset)
        dwBytesInQueue =
            dsoundsink->current_circular_offset - dwCurrentPlayCursor;
      else
        dwBytesInQueue =
            dsoundsink->current_circular_offset + (dsoundsink->buffer_size -
            dwCurrentPlayCursor);

      nNbSamplesInQueue = dwBytesInQueue / dsoundsink->bytes_per_sample;
    }
659
660
661
662
663
664
  }

  return nNbSamplesInQueue;
}

static void
665
gst_directsound_sink_reset (GstAudioSink * asink)
666
667
{
  GstDirectSoundSink *dsoundsink;
668
  LPVOID pLockedBuffer = NULL;
669
  DWORD dwSizeBuffer = 0;
670
671
672

  dsoundsink = GST_DIRECTSOUND_SINK (asink);

673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
  GST_DSOUND_LOCK (dsoundsink);

  if (dsoundsink->pDSBSecondary) {
    /*stop playing */
    HRESULT hRes = IDirectSoundBuffer_Stop (dsoundsink->pDSBSecondary);

    /*reset position */
    hRes = IDirectSoundBuffer_SetCurrentPosition (dsoundsink->pDSBSecondary, 0);
    dsoundsink->current_circular_offset = 0;

    /*reset the buffer */
    hRes = IDirectSoundBuffer_Lock (dsoundsink->pDSBSecondary,
        dsoundsink->current_circular_offset, dsoundsink->buffer_size,
        &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);

    if (SUCCEEDED (hRes)) {
      memset (pLockedBuffer, 0, dwSizeBuffer);

      hRes =
          IDirectSoundBuffer_Unlock (dsoundsink->pDSBSecondary, pLockedBuffer,
          dwSizeBuffer, NULL, 0);
    }
  }

  dsoundsink->first_buffer_after_reset = TRUE;

  GST_DSOUND_UNLOCK (dsoundsink);
700
}
701

702
703
704
705
706
707
/*
 * gst_directsound_probe_supported_formats:
 *
 * Takes the template caps and returns the subset which is actually
 * supported by this device.
 *
708
709
710
711
712
713
714
715
716
717
 */

static GstCaps *
gst_directsound_probe_supported_formats (GstDirectSoundSink * dsoundsink,
    const GstCaps * template_caps)
{
  HRESULT hRes;
  DSBUFFERDESC descSecondary;
  WAVEFORMATEX wfx;
  GstCaps *caps;
718
  GstCaps *tmp, *tmp2;
719
  LPDIRECTSOUNDBUFFER tmpBuffer;
720
721
722

  caps = gst_caps_copy (template_caps);

723
724
  /*
   * Check availability of digital output by trying to create an SPDIF buffer
725
726
   */

727
#ifdef WAVE_FORMAT_DOLBY_AC3_SPDIF
728
729
730
731
732
733
734
735
736
737
  /* fill the WAVEFORMATEX structure with some standard AC3 over SPDIF params */
  memset (&wfx, 0, sizeof (wfx));
  wfx.cbSize = 0;
  wfx.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF;
  wfx.nChannels = 2;
  wfx.nSamplesPerSec = 48000;
  wfx.wBitsPerSample = 16;
  wfx.nBlockAlign = 4;
  wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

738
  // create a secondary directsound buffer
739
740
741
742
743
744
745
  memset (&descSecondary, 0, sizeof (DSBUFFERDESC));
  descSecondary.dwSize = sizeof (DSBUFFERDESC);
  descSecondary.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
  descSecondary.dwBufferBytes = 6144;
  descSecondary.lpwfxFormat = &wfx;

  hRes = IDirectSound_CreateSoundBuffer (dsoundsink->pDS, &descSecondary,
746
      &tmpBuffer, NULL);
747
748
749
750
  if (FAILED (hRes)) {
    GST_INFO_OBJECT (dsoundsink, "AC3 passthrough not supported "
        "(IDirectSound_CreateSoundBuffer returned: %s)\n",
        DXGetErrorString9 (hRes));
751
752
753
754
755
756
757
758
759
760
    tmp = gst_caps_new_empty_simple ("audio/x-ac3");
    tmp2 = gst_caps_subtract (caps, tmp);
    gst_caps_unref (tmp);
    gst_caps_unref (caps);
    caps = tmp2;
    tmp = gst_caps_new_empty_simple ("audio/x-dts");
    tmp2 = gst_caps_subtract (caps, tmp);
    gst_caps_unref (tmp);
    gst_caps_unref (caps);
    caps = tmp2;
761
762
  } else {
    GST_INFO_OBJECT (dsoundsink, "AC3 passthrough supported");
763
    hRes = IDirectSoundBuffer_Release (tmpBuffer);
764
765
766
767
768
769
    if (FAILED (hRes)) {
      GST_DEBUG_OBJECT (dsoundsink,
          "(IDirectSoundBuffer_Release returned: %s)\n",
          DXGetErrorString9 (hRes));
    }
  }
770
#else
771
772
773
774
775
776
777
778
779
780
  tmp = gst_caps_new_empty_simple ("audio/x-ac3");
  tmp2 = gst_caps_subtract (caps, tmp);
  gst_caps_unref (tmp);
  gst_caps_unref (caps);
  caps = tmp2;
  tmp = gst_caps_new_empty_simple ("audio/x-dts");
  tmp2 = gst_caps_subtract (caps, tmp);
  gst_caps_unref (tmp);
  gst_caps_unref (caps);
  caps = tmp2;
781
#endif
782
783
784

  return caps;
}
785

786
787
788
static GstBuffer *
gst_directsound_sink_payload (GstAudioBaseSink * sink, GstBuffer * buf)
{
789
790
791
792
793
  if (gst_directsound_sink_is_spdif_format (&sink->ringbuffer->spec)) {
    gint framesize = gst_audio_iec61937_frame_size (&sink->ringbuffer->spec);
    GstBuffer *out;
    GstMapInfo infobuf, infoout;
    gboolean success;
794

795
796
    if (framesize <= 0)
      return NULL;
797

798
    out = gst_buffer_new_and_alloc (framesize);
799

800
801
802
803
804
805
806
807
808
809
810
811
    if (!gst_buffer_map (buf, &infobuf, GST_MAP_READWRITE)) {
      gst_buffer_unref (out);
      return NULL;
    }
    if (!gst_buffer_map (out, &infoout, GST_MAP_READWRITE)) {
      gst_buffer_unmap (buf, &infobuf);
      gst_buffer_unref (out);
      return NULL;
    }
    success = gst_audio_iec61937_payload (infobuf.data, infobuf.size,
        infoout.data, infoout.size, &sink->ringbuffer->spec);
    if (!success) {
812
813
      gst_buffer_unmap (out, &infoout);
      gst_buffer_unmap (buf, &infobuf);
814
815
      gst_buffer_unref (out);
      return NULL;
816
    }
817
818
819
820
821
822
823
824
825

    gst_buffer_copy_into (out, buf, GST_BUFFER_COPY_ALL, 0, -1);
    /* Fix endianness */
    _swab ((gchar *) infoout.data, (gchar *) infoout.data, infobuf.size);
    gst_buffer_unmap (out, &infoout);
    gst_buffer_unmap (buf, &infobuf);
    return out;
  } else
    return gst_buffer_ref (buf);
826
827
}

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
static void
gst_directsound_sink_set_volume (GstDirectSoundSink * dsoundsink,
    gdouble dvolume, gboolean store)
{
  glong volume;

  volume = dvolume * 100;
  if (store)
    dsoundsink->volume = volume;

  if (dsoundsink->pDSBSecondary) {
    /* DirectSound controls volume using units of 100th of a decibel,
     * ranging from -10000 to 0. We use a linear scale of 0 - 100
     * here, so remap.
     */
    long dsVolume;
    if (dsoundsink->volume == 0)
      dsVolume = -10000;
    else
      dsVolume = 100 * (long) (20 * log10 ((double) dsoundsink->volume / 100.));
    dsVolume = CLAMP (dsVolume, -10000, 0);

    GST_DEBUG_OBJECT (dsoundsink,
        "Setting volume on secondary buffer to %d from %d", (int) dsVolume,
        (int) dsoundsink->volume);
    IDirectSoundBuffer_SetVolume (dsoundsink->pDSBSecondary, dsVolume);
  }
}

gdouble
gst_directsound_sink_get_volume (GstDirectSoundSink * dsoundsink)
{
  return (gdouble) dsoundsink->volume / 100;
}

static void
gst_directsound_sink_set_mute (GstDirectSoundSink * dsoundsink, gboolean mute)
{
  if (mute)
    gst_directsound_sink_set_volume (dsoundsink, 0, FALSE);
  else
    gst_directsound_sink_set_volume (dsoundsink, dsoundsink->volume, FALSE);
}

static gboolean
gst_directsound_sink_get_mute (GstDirectSoundSink * dsoundsink)
{
  return FALSE;
}