3 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
4 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5 * Copyright 2005 S�bastien Moutte <sebastien@moutte.net>
6 * Copyright 2006 Joni Valtanen <joni.valtanen@movial.fi>
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * Alternatively, the contents of this file may be used under the
27 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28 * which case the following provisions apply instead of the ones
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the
43 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
44 * Boston, MA 02110-1301, USA.
48 TODO: add mixer device init for selection by device-guid
52 * SECTION:element-directsoundsrc
53 * @title: directsoundsrc
55 * Reads audio data using the DirectSound API.
57 * ## Example pipelines
59 * gst-launch-1.0 -v directsoundsrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=dsound.ogg
60 * ]| Record from DirectSound and encode to Ogg/Vorbis.
69 #include <gst/audio/audio.h>
70 #include <gst/audio/gstaudiobasesrc.h>
72 #include "gstdirectsoundsrc.h"
79 GST_DEBUG_CATEGORY_STATIC (directsoundsrc_debug);
80 #define GST_CAT_DEFAULT directsoundsrc_debug
83 #define DEFAULT_DEVICE 0
84 #define DEFAULT_MUTE FALSE
96 static void gst_directsound_src_finalize (GObject * object);
98 static void gst_directsound_src_set_property (GObject * object,
99 guint prop_id, const GValue * value, GParamSpec * pspec);
101 static void gst_directsound_src_get_property (GObject * object,
102 guint prop_id, GValue * value, GParamSpec * pspec);
104 static gboolean gst_directsound_src_open (GstAudioSrc * asrc);
105 static gboolean gst_directsound_src_close (GstAudioSrc * asrc);
106 static gboolean gst_directsound_src_prepare (GstAudioSrc * asrc,
107 GstAudioRingBufferSpec * spec);
108 static gboolean gst_directsound_src_unprepare (GstAudioSrc * asrc);
109 static void gst_directsound_src_reset (GstAudioSrc * asrc);
110 static GstCaps *gst_directsound_src_getcaps (GstBaseSrc * bsrc,
113 static guint gst_directsound_src_read (GstAudioSrc * asrc,
114 gpointer data, guint length, GstClockTime * timestamp);
116 static void gst_directsound_src_dispose (GObject * object);
118 static guint gst_directsound_src_delay (GstAudioSrc * asrc);
120 static gboolean gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
121 MIXERCAPS * mixer_caps);
122 static void gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc);
124 static gdouble gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc);
125 static void gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc,
128 static gboolean gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc);
129 static void gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc,
132 static const gchar *gst_directsound_src_get_device (GstDirectSoundSrc *
134 static void gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
135 const gchar * device_id);
137 static GstStaticPadTemplate directsound_src_src_factory =
138 GST_STATIC_PAD_TEMPLATE ("src",
141 GST_STATIC_CAPS (GST_DIRECTSOUND_SRC_CAPS));
143 #define gst_directsound_src_parent_class parent_class
144 G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSrc, gst_directsound_src,
145 GST_TYPE_AUDIO_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL)
149 gst_directsound_src_dispose (GObject * object)
151 G_OBJECT_CLASS (parent_class)->dispose (object);
155 gst_directsound_src_finalize (GObject * object)
157 GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (object);
159 g_mutex_clear (&dsoundsrc->dsound_lock);
160 gst_object_unref (dsoundsrc->system_clock);
161 if (dsoundsrc->read_wait_clock_id != NULL)
162 gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
164 g_free (dsoundsrc->device_name);
166 g_free (dsoundsrc->device_id);
168 g_free (dsoundsrc->device_guid);
170 G_OBJECT_CLASS (parent_class)->finalize (object);
174 gst_directsound_src_class_init (GstDirectSoundSrcClass * klass)
176 GObjectClass *gobject_class;
177 GstElementClass *gstelement_class;
178 GstBaseSrcClass *gstbasesrc_class;
179 GstAudioSrcClass *gstaudiosrc_class;
181 gobject_class = (GObjectClass *) klass;
182 gstelement_class = (GstElementClass *) klass;
183 gstbasesrc_class = (GstBaseSrcClass *) klass;
184 gstaudiosrc_class = (GstAudioSrcClass *) klass;
186 GST_DEBUG_CATEGORY_INIT (directsoundsrc_debug, "directsoundsrc", 0,
189 GST_DEBUG ("initializing directsoundsrc class");
191 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directsound_src_finalize);
192 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_directsound_src_dispose);
193 gobject_class->get_property =
194 GST_DEBUG_FUNCPTR (gst_directsound_src_get_property);
195 gobject_class->set_property =
196 GST_DEBUG_FUNCPTR (gst_directsound_src_set_property);
198 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_directsound_src_getcaps);
200 gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_directsound_src_open);
201 gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_directsound_src_close);
202 gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_directsound_src_read);
203 gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_directsound_src_prepare);
204 gstaudiosrc_class->unprepare =
205 GST_DEBUG_FUNCPTR (gst_directsound_src_unprepare);
206 gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_src_delay);
207 gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_src_reset);
209 gst_element_class_set_static_metadata (gstelement_class,
210 "DirectSound audio source", "Source/Audio",
211 "Capture from a soundcard via DirectSound",
212 "Joni Valtanen <joni.valtanen@movial.fi>");
214 gst_element_class_add_static_pad_template (gstelement_class,
215 &directsound_src_src_factory);
217 g_object_class_install_property
218 (gobject_class, PROP_DEVICE_NAME,
219 g_param_spec_string ("device-name", "Device name",
220 "Human-readable name of the sound device", NULL, G_PARAM_READWRITE));
222 g_object_class_install_property (gobject_class,
224 g_param_spec_string ("device", "Device",
225 "DirectSound playback device as a GUID string (volume and mute will not work!)",
226 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228 g_object_class_install_property
229 (gobject_class, PROP_VOLUME,
230 g_param_spec_double ("volume", "Volume",
231 "Volume of this stream", 0.0, 1.0, 1.0,
232 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234 g_object_class_install_property
235 (gobject_class, PROP_MUTE,
236 g_param_spec_boolean ("mute", "Mute",
237 "Mute state of this stream", DEFAULT_MUTE,
238 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242 gst_directsound_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
244 GstCaps *caps = NULL;
245 GST_DEBUG_OBJECT (bsrc, "get caps");
247 caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
252 gst_directsound_src_set_property (GObject * object, guint prop_id,
253 const GValue * value, GParamSpec * pspec)
255 GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
256 GST_DEBUG ("set property");
259 case PROP_DEVICE_NAME:
260 if (src->device_name) {
261 g_free (src->device_name);
262 src->device_name = NULL;
264 if (g_value_get_string (value)) {
265 src->device_name = g_strdup (g_value_get_string (value));
269 gst_directsound_src_set_volume (src, g_value_get_double (value));
272 gst_directsound_src_set_mute (src, g_value_get_boolean (value));
275 gst_directsound_src_set_device (src, g_value_get_string (value));
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
284 gst_directsound_src_get_property (GObject * object, guint prop_id,
285 GValue * value, GParamSpec * pspec)
287 GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
289 GST_DEBUG ("get property");
292 case PROP_DEVICE_NAME:
293 g_value_set_string (value, src->device_name);
296 g_value_set_string (value, gst_directsound_src_get_device (src));
299 g_value_set_double (value, gst_directsound_src_get_volume (src));
302 g_value_set_boolean (value, gst_directsound_src_get_mute (src));
305 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
311 /* initialize the new element
312 * instantiate pads and add them to element
314 * initialize structure
317 gst_directsound_src_init (GstDirectSoundSrc * src)
319 GST_DEBUG_OBJECT (src, "initializing directsoundsrc");
320 g_mutex_init (&src->dsound_lock);
321 src->system_clock = gst_system_clock_obtain ();
322 src->read_wait_clock_id = NULL;
323 src->reset_while_sleeping = FALSE;
324 src->device_guid = NULL;
325 src->device_id = NULL;
326 src->device_name = NULL;
328 src->control_id_mute = -1;
329 src->control_id_volume = -1;
335 /* Enumeration callback called by DirectSoundCaptureEnumerate.
336 * Gets the GUID of request audio device
339 gst_directsound_enum_callback (GUID * pGUID, TCHAR * strDesc,
340 TCHAR * strDrvName, VOID * pContext)
342 GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (pContext);
343 gchar *driver, *description;
345 description = g_locale_to_utf8 (strDesc, -1, NULL, NULL, NULL);
347 GST_ERROR_OBJECT (dsoundsrc,
348 "Failed to convert description from locale encoding to UTF8");
352 driver = g_locale_to_utf8 (strDrvName, -1, NULL, NULL, NULL);
354 if (pGUID && dsoundsrc && dsoundsrc->device_name &&
355 !g_strcmp0 (dsoundsrc->device_name, description)) {
356 g_free (dsoundsrc->device_guid);
357 dsoundsrc->device_guid = (GUID *) g_malloc0 (sizeof (GUID));
358 memcpy (dsoundsrc->device_guid, pGUID, sizeof (GUID));
359 GST_INFO_OBJECT (dsoundsrc, "found the requested audio device :%s",
360 dsoundsrc->device_name);
361 g_free (description);
366 GST_INFO_OBJECT (dsoundsrc, "sound device names: %s, %s, requested device:%s",
367 description, driver, dsoundsrc->device_name);
369 g_free (description);
376 string_to_guid (const gchar * str)
382 wstr = g_utf8_to_utf16 (str, -1, NULL, NULL, NULL);
386 out = g_new (GUID, 1);
387 ret = CLSIDFromString ((LPOLESTR) wstr, out);
389 if (ret != NOERROR) {
398 gst_directsound_src_open (GstAudioSrc * asrc)
400 GstDirectSoundSrc *dsoundsrc;
401 HRESULT hRes; /* Result for windows functions */
403 GST_DEBUG_OBJECT (asrc, "opening directsoundsrc");
405 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
407 if (dsoundsrc->device_id) {
408 GST_DEBUG_OBJECT (asrc, "device id set to: %s ", dsoundsrc->device_id);
409 dsoundsrc->device_guid = string_to_guid (dsoundsrc->device_id);
410 if (dsoundsrc->device_guid == NULL) {
411 GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
412 ("gst_directsound_src_open: device set, but guid not found: %s",
413 dsoundsrc->device_id), (NULL));
414 g_free (dsoundsrc->device_guid);
419 hRes = DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)
420 gst_directsound_enum_callback, (VOID *) dsoundsrc);
423 goto capture_enumerate;
426 /* Create capture object */
428 DirectSoundCaptureCreate (dsoundsrc->device_guid, &dsoundsrc->pDSC, NULL);
434 // mixer is only supported when device-id is not set
435 if (!dsoundsrc->device_id) {
436 gst_directsound_src_mixer_init (dsoundsrc);
443 GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
444 ("Unable to enumerate audio capture devices"), (NULL));
449 GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
450 ("Unable to create capture object"), (NULL));
456 gst_directsound_src_close (GstAudioSrc * asrc)
458 GstDirectSoundSrc *dsoundsrc;
460 GST_DEBUG_OBJECT (asrc, "closing directsoundsrc");
462 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
464 /* Release capture handler */
465 IDirectSoundCapture_Release (dsoundsrc->pDSC);
467 if (dsoundsrc->mixer)
468 mixerClose (dsoundsrc->mixer);
474 gst_directsound_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
476 GstDirectSoundSrc *dsoundsrc;
477 WAVEFORMATEX wfx; /* Wave format structure */
478 HRESULT hRes; /* Result for windows functions */
479 DSCBUFFERDESC descSecondary; /* Capturebuffer description */
481 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
483 GST_DEBUG_OBJECT (asrc, "preparing directsoundsrc");
486 memset (&wfx, 0, sizeof (WAVEFORMATEX));
487 wfx.wFormatTag = WAVE_FORMAT_PCM;
488 wfx.nChannels = GST_AUDIO_INFO_CHANNELS (&spec->info);
489 wfx.nSamplesPerSec = GST_AUDIO_INFO_RATE (&spec->info);
490 wfx.wBitsPerSample = GST_AUDIO_INFO_BPF (&spec->info) * 8 / wfx.nChannels;
491 wfx.nBlockAlign = GST_AUDIO_INFO_BPF (&spec->info);
492 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
493 /* Ignored for WAVE_FORMAT_PCM. */
496 if (wfx.wBitsPerSample != 16 && wfx.wBitsPerSample != 8)
499 GST_INFO_OBJECT (asrc, "latency time: %" G_GUINT64_FORMAT " - buffer time: %"
500 G_GUINT64_FORMAT, spec->latency_time, spec->buffer_time);
502 /* Buffer-time should always be >= 2*latency */
503 if (spec->buffer_time < spec->latency_time * 2) {
504 spec->buffer_time = spec->latency_time * 2;
505 GST_WARNING ("buffer-time was less than 2*latency-time, clamping");
508 /* Set the buffer size from our configured buffer time (in microsecs) */
509 dsoundsrc->buffer_size =
510 gst_util_uint64_scale_int (spec->buffer_time, wfx.nAvgBytesPerSec,
511 GST_SECOND / GST_USECOND);
513 GST_INFO_OBJECT (asrc, "Buffer size: %d", dsoundsrc->buffer_size);
516 gst_util_uint64_scale (spec->latency_time, wfx.nAvgBytesPerSec,
517 GST_SECOND / GST_USECOND);
519 /* Sanitized segsize */
520 if (spec->segsize < GST_AUDIO_INFO_BPF (&spec->info))
521 spec->segsize = GST_AUDIO_INFO_BPF (&spec->info);
522 else if (spec->segsize % GST_AUDIO_INFO_BPF (&spec->info) != 0)
524 ((spec->segsize + GST_AUDIO_INFO_BPF (&spec->info) -
525 1) / GST_AUDIO_INFO_BPF (&spec->info)) *
526 GST_AUDIO_INFO_BPF (&spec->info);
527 spec->segtotal = dsoundsrc->buffer_size / spec->segsize;
528 /* The device usually takes time = 1-2 segments to start producing buffers */
529 spec->seglatency = spec->segtotal + 2;
531 /* Fetch and set the actual latency time that will be used */
532 dsoundsrc->latency_time =
533 gst_util_uint64_scale (spec->segsize, GST_SECOND / GST_USECOND,
534 GST_AUDIO_INFO_BPF (&spec->info) * GST_AUDIO_INFO_RATE (&spec->info));
536 GST_INFO_OBJECT (asrc, "actual latency time: %" G_GUINT64_FORMAT,
539 /* Init secondary buffer description */
540 memset (&descSecondary, 0, sizeof (DSCBUFFERDESC));
541 descSecondary.dwSize = sizeof (DSCBUFFERDESC);
542 descSecondary.dwFlags = 0;
543 descSecondary.dwReserved = 0;
545 /* This is not primary buffer so have to set size */
546 descSecondary.dwBufferBytes = dsoundsrc->buffer_size;
547 descSecondary.lpwfxFormat = &wfx;
550 hRes = IDirectSoundCapture_CreateCaptureBuffer (dsoundsrc->pDSC,
551 &descSecondary, &dsoundsrc->pDSBSecondary, NULL);
555 dsoundsrc->bytes_per_sample = GST_AUDIO_INFO_BPF (&spec->info);
557 GST_INFO_OBJECT (asrc,
558 "bytes/sec: %lu, buffer size: %d, segsize: %d, segtotal: %d",
559 wfx.nAvgBytesPerSec, dsoundsrc->buffer_size, spec->segsize,
562 /* Not read anything yet */
563 dsoundsrc->current_circular_offset = 0;
565 GST_INFO_OBJECT (asrc, "channels: %d, rate: %d, bytes_per_sample: %d"
566 " WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d,"
567 " WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld",
568 GST_AUDIO_INFO_CHANNELS (&spec->info), GST_AUDIO_INFO_RATE (&spec->info),
569 GST_AUDIO_INFO_BPF (&spec->info), wfx.nSamplesPerSec, wfx.wBitsPerSample,
570 wfx.nBlockAlign, wfx.nAvgBytesPerSec);
576 GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
577 ("Unable to create capturebuffer"), (NULL));
582 GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
583 ("Unexpected width %d", wfx.wBitsPerSample), (NULL));
589 gst_directsound_src_unprepare (GstAudioSrc * asrc)
591 GstDirectSoundSrc *dsoundsrc;
593 GST_DEBUG_OBJECT (asrc, "unpreparing directsoundsrc");
595 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
597 GST_DSOUND_LOCK (dsoundsrc);
600 IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
603 IDirectSoundCaptureBuffer_Release (dsoundsrc->pDSBSecondary);
604 GST_DSOUND_UNLOCK (dsoundsrc);
609 return number of readed bytes */
611 gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length,
612 GstClockTime * timestamp)
614 GstDirectSoundSrc *dsoundsrc;
615 guint64 sleep_time_ms, sleep_until;
618 HRESULT hRes; /* Result for windows functions */
619 DWORD dwCurrentCaptureCursor = 0;
620 DWORD dwBufferSize = 0;
622 LPVOID pLockedBuffer1 = NULL;
623 LPVOID pLockedBuffer2 = NULL;
624 DWORD dwSizeBuffer1 = 0;
625 DWORD dwSizeBuffer2 = 0;
629 GST_DEBUG_OBJECT (asrc, "reading directsoundsrc");
631 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
633 GST_DSOUND_LOCK (dsoundsrc);
635 /* Get current buffer status */
636 hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary,
640 GST_DSOUND_UNLOCK (dsoundsrc);
644 /* Starting capturing if not already */
645 if (!(dwStatus & DSCBSTATUS_CAPTURING)) {
646 hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary,
648 GST_INFO_OBJECT (asrc, "capture started");
651 /* Loop till the source has produced bytes equal to or greater than @length.
653 * DirectSound has a notification-based API that uses Windows CreateEvent()
654 * + WaitForSingleObject(), but it is completely useless for live streams.
656 * 1. You must schedule all events before starting capture
657 * 2. The events are all fired exactly once
658 * 3. You cannot schedule new events while a capture is running
659 * 4. You cannot stop/schedule/start either
661 * This means you cannot use the API while doing live looped capture and we
662 * must resort to this.
664 * However, this is almost as efficient as event-based capture since it's ok
665 * to consistently overwait by a fixed amount; the extra bytes will just end
666 * up being used in the next call, and the extra latency will be constant. */
669 IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
670 &dwCurrentCaptureCursor, NULL);
673 GST_DSOUND_UNLOCK (dsoundsrc);
677 /* calculate the size of the buffer that's been captured while accounting
678 * for wrap-arounds */
679 if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
680 dwBufferSize = dsoundsrc->buffer_size -
681 (dsoundsrc->current_circular_offset - dwCurrentCaptureCursor);
684 dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
687 if (dwBufferSize >= length) {
688 /* Yay, we got all the data we need */
691 GST_DEBUG_OBJECT (asrc, "not enough data, got %lu (want at least %u)",
692 dwBufferSize, length);
693 /* If we didn't get enough data, sleep for a proportionate time */
694 sleep_time_ms = gst_util_uint64_scale (dsoundsrc->latency_time,
695 length - dwBufferSize, length * 1000);
696 /* Make sure we don't run in a tight loop unnecessarily */
697 sleep_time_ms = MAX (sleep_time_ms, 10);
698 /* Sleep using gst_clock_id_wait() so that we can be interrupted */
699 sleep_until = gst_clock_get_time (dsoundsrc->system_clock) +
700 sleep_time_ms * GST_MSECOND;
701 /* Setup the clock id wait */
702 if (G_UNLIKELY (dsoundsrc->read_wait_clock_id == NULL ||
703 gst_clock_single_shot_id_reinit (dsoundsrc->system_clock,
704 dsoundsrc->read_wait_clock_id, sleep_until) == FALSE)) {
705 if (dsoundsrc->read_wait_clock_id != NULL)
706 gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
707 dsoundsrc->read_wait_clock_id =
708 gst_clock_new_single_shot_id (dsoundsrc->system_clock, sleep_until);
711 clock_id = dsoundsrc->read_wait_clock_id;
712 dsoundsrc->reset_while_sleeping = FALSE;
714 GST_DEBUG_OBJECT (asrc, "waiting %" G_GUINT64_FORMAT "ms for more data",
716 GST_DSOUND_UNLOCK (dsoundsrc);
718 gst_clock_id_wait (clock_id, NULL);
720 GST_DSOUND_LOCK (dsoundsrc);
722 if (dsoundsrc->reset_while_sleeping == TRUE) {
723 GST_DEBUG_OBJECT (asrc, "reset while sleeping, cancelled read");
724 GST_DSOUND_UNLOCK (dsoundsrc);
730 GST_DEBUG_OBJECT (asrc, "Got enough data: %lu bytes (wanted at least %u)",
731 dwBufferSize, length);
733 /* Lock the buffer and read only the first @length bytes. Keep the rest in
734 * the capture buffer for the next read. */
735 hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
736 dsoundsrc->current_circular_offset,
738 &pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
740 /* NOTE: We now assume that dwSizeBuffer1 + dwSizeBuffer2 == length since the
741 * API is supposed to guarantee that */
743 /* Copy buffer data to another buffer */
745 memcpy (data, pLockedBuffer1, dwSizeBuffer1);
748 /* ...and if something is in another buffer */
749 if (pLockedBuffer2 != NULL) {
750 memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2);
753 dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
754 dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size;
756 IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
757 pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
759 GST_DSOUND_UNLOCK (dsoundsrc);
761 /* We always read exactly @length data */
766 gst_directsound_src_delay (GstAudioSrc * asrc)
768 GstDirectSoundSrc *dsoundsrc;
770 DWORD dwCurrentCaptureCursor;
771 DWORD dwBytesInQueue = 0;
772 gint nNbSamplesInQueue = 0;
774 GST_INFO_OBJECT (asrc, "Delay");
776 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
778 /* evaluate the number of samples in queue in the circular buffer */
780 IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
781 &dwCurrentCaptureCursor, NULL);
782 /* FIXME: Check is this calculated right */
784 if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
786 dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset -
787 dwCurrentCaptureCursor);
790 dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
793 nNbSamplesInQueue = dwBytesInQueue / dsoundsrc->bytes_per_sample;
796 GST_INFO_OBJECT (asrc, "Delay is %d samples", nNbSamplesInQueue);
798 return nNbSamplesInQueue;
802 gst_directsound_src_reset (GstAudioSrc * asrc)
804 GstDirectSoundSrc *dsoundsrc;
805 LPVOID pLockedBuffer = NULL;
806 DWORD dwSizeBuffer = 0;
808 GST_DEBUG_OBJECT (asrc, "reset directsoundsrc");
810 dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
812 GST_DSOUND_LOCK (dsoundsrc);
814 dsoundsrc->reset_while_sleeping = TRUE;
815 /* Interrupt read sleep if required */
816 if (dsoundsrc->read_wait_clock_id != NULL)
817 gst_clock_id_unschedule (dsoundsrc->read_wait_clock_id);
819 if (dsoundsrc->pDSBSecondary) {
821 HRESULT hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
824 /* hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */
826 /*reset the buffer */
827 hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
828 dsoundsrc->current_circular_offset, dsoundsrc->buffer_size,
829 &pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
831 if (SUCCEEDED (hRes)) {
832 memset (pLockedBuffer, 0, dwSizeBuffer);
835 IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
836 pLockedBuffer, dwSizeBuffer, NULL, 0);
838 dsoundsrc->current_circular_offset = 0;
842 GST_DSOUND_UNLOCK (dsoundsrc);
845 /* If the PROP_DEVICE_NAME is set, find the mixer related to device;
846 * otherwise we get the default input mixer. */
848 gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
849 MIXERCAPS * mixer_caps)
854 num_mixers = mixerGetNumDevs ();
855 for (i = 0; i < num_mixers; i++) {
856 mmres = mixerOpen (&dsoundsrc->mixer, i, 0L, 0L,
857 MIXER_OBJECTF_MIXER | MIXER_OBJECTF_WAVEIN);
859 if (mmres != MMSYSERR_NOERROR)
862 mmres = mixerGetDevCaps ((UINT_PTR) dsoundsrc->mixer,
863 mixer_caps, sizeof (MIXERCAPS));
865 if (mmres != MMSYSERR_NOERROR) {
866 mixerClose (dsoundsrc->mixer);
870 /* Get default mixer */
871 if (dsoundsrc->device_name == NULL) {
872 GST_DEBUG ("Got default input mixer: %s", mixer_caps->szPname);
876 if (g_strstr_len (dsoundsrc->device_name, -1, mixer_caps->szPname) != NULL) {
877 GST_DEBUG ("Got requested input mixer: %s", mixer_caps->szPname);
882 mixerClose (dsoundsrc->mixer);
885 GST_DEBUG ("Can't find input mixer");
890 gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc)
895 MIXERCAPS mixer_caps;
896 MIXERLINE mixer_line;
897 MIXERLINECONTROLS ml_ctrl;
898 PMIXERCONTROL pamixer_ctrls;
900 if (!gst_directsound_src_mixer_find (dsoundsrc, &mixer_caps))
901 goto mixer_init_fail;
903 /* Find the MIXERLINE related to MICROPHONE */
905 for (i = 0; i < mixer_caps.cDestinations && !found_mic; i++) {
906 gint j, num_connections;
908 mixer_line.cbStruct = sizeof (mixer_line);
909 mixer_line.dwDestination = i;
910 mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
911 &mixer_line, MIXER_GETLINEINFOF_DESTINATION);
913 if (mmres != MMSYSERR_NOERROR)
914 goto mixer_init_fail;
916 num_connections = mixer_line.cConnections;
917 for (j = 0; j < num_connections && !found_mic; j++) {
918 mixer_line.cbStruct = sizeof (mixer_line);
919 mixer_line.dwDestination = i;
920 mixer_line.dwSource = j;
921 mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
922 &mixer_line, MIXER_GETLINEINFOF_SOURCE);
924 if (mmres != MMSYSERR_NOERROR)
925 goto mixer_init_fail;
927 if (mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
928 || mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE)
933 if (found_mic == FALSE) {
934 GST_DEBUG ("Can't find mixer line related to input");
935 goto mixer_init_fail;
938 /* Get control associated with microphone audio line */
939 pamixer_ctrls = g_malloc (sizeof (MIXERCONTROL) * mixer_line.cControls);
940 ml_ctrl.cbStruct = sizeof (ml_ctrl);
941 ml_ctrl.dwLineID = mixer_line.dwLineID;
942 ml_ctrl.cControls = mixer_line.cControls;
943 ml_ctrl.cbmxctrl = sizeof (MIXERCONTROL);
944 ml_ctrl.pamxctrl = pamixer_ctrls;
945 mmres = mixerGetLineControls ((HMIXEROBJ) dsoundsrc->mixer,
946 &ml_ctrl, MIXER_GETLINECONTROLSF_ALL);
948 /* Find control associated with volume and mute */
949 for (k = 0; k < mixer_line.cControls; k++) {
950 if (strstr (pamixer_ctrls[k].szName, "Volume") != NULL) {
951 dsoundsrc->control_id_volume = pamixer_ctrls[k].dwControlID;
952 dsoundsrc->dw_vol_max = pamixer_ctrls[k].Bounds.dwMaximum;
953 dsoundsrc->dw_vol_min = pamixer_ctrls[k].Bounds.dwMinimum;
954 } else if (strstr (pamixer_ctrls[k].szName, "Mute") != NULL) {
955 dsoundsrc->control_id_mute = pamixer_ctrls[k].dwControlID;
957 GST_DEBUG ("Control not handled: %s", pamixer_ctrls[k].szName);
960 g_free (pamixer_ctrls);
962 if (dsoundsrc->control_id_volume < 0 && dsoundsrc->control_id_mute < 0)
963 goto mixer_init_fail;
965 /* Save cChannels information to properly changes in volume */
966 dsoundsrc->mixerline_cchannels = mixer_line.cChannels;
970 GST_WARNING ("Failed to get Volume and Mute controls");
971 if (dsoundsrc->mixer != NULL) {
972 mixerClose (dsoundsrc->mixer);
973 dsoundsrc->mixer = NULL;
978 gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc)
980 return (gdouble) dsoundsrc->volume / 100;
984 gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc)
986 return dsoundsrc->mute;
990 gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc, gdouble volume)
993 MIXERCONTROLDETAILS details;
994 MIXERCONTROLDETAILS_UNSIGNED details_unsigned;
997 if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_volume < 0) {
998 GST_WARNING ("mixer not initialized");
1002 dwvolume = volume * dsoundsrc->dw_vol_max;
1003 dwvolume = CLAMP (dwvolume, dsoundsrc->dw_vol_min, dsoundsrc->dw_vol_max);
1005 GST_DEBUG ("max volume %ld | min volume %ld",
1006 dsoundsrc->dw_vol_max, dsoundsrc->dw_vol_min);
1007 GST_DEBUG ("set volume to %f (%ld)", volume, dwvolume);
1009 details.cbStruct = sizeof (details);
1010 details.dwControlID = dsoundsrc->control_id_volume;
1011 details.cChannels = dsoundsrc->mixerline_cchannels;
1012 details.cMultipleItems = 0;
1014 details_unsigned.dwValue = dwvolume;
1015 details.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
1016 details.paDetails = &details_unsigned;
1018 mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
1019 &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
1021 if (mmres != MMSYSERR_NOERROR)
1022 GST_WARNING ("Failed to set volume");
1024 dsoundsrc->volume = volume * 100;
1028 gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc, gboolean mute)
1031 MIXERCONTROLDETAILS details;
1032 MIXERCONTROLDETAILS_BOOLEAN details_boolean;
1034 if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_mute < 0) {
1035 GST_WARNING ("mixer not initialized");
1039 details.cbStruct = sizeof (details);
1040 details.dwControlID = dsoundsrc->control_id_mute;
1041 details.cChannels = dsoundsrc->mixerline_cchannels;
1042 details.cMultipleItems = 0;
1044 details_boolean.fValue = mute;
1045 details.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);
1046 details.paDetails = &details_boolean;
1048 mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
1049 &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
1051 if (mmres != MMSYSERR_NOERROR)
1052 GST_WARNING ("Failed to set mute");
1054 dsoundsrc->mute = mute;
1057 static const gchar *
1058 gst_directsound_src_get_device (GstDirectSoundSrc * dsoundsrc)
1060 return dsoundsrc->device_id;
1064 gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
1065 const gchar * device_id)
1067 g_free (dsoundsrc->device_id);
1068 dsoundsrc->device_id = g_strdup (device_id);