2009-08-10 09:36:41 +00:00
|
|
|
|
/*
|
|
|
|
|
* GStreamer
|
|
|
|
|
* Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
|
|
|
|
|
* Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
2012-09-14 15:08:49 +00:00
|
|
|
|
* Copyright 2005 S<EFBFBD>bastien Moutte <sebastien@moutte.net>
|
2009-08-10 09:36:41 +00:00
|
|
|
|
* Copyright 2006 Joni Valtanen <joni.valtanen@movial.fi>
|
|
|
|
|
*
|
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
|
|
|
* to deal in the Software without restriction, including without limitation
|
|
|
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
|
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
*
|
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
|
*
|
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
|
|
* DEALINGS IN THE SOFTWARE.
|
|
|
|
|
*
|
|
|
|
|
* Alternatively, the contents of this file may be used under the
|
|
|
|
|
* GNU Lesser General Public License Version 2.1 (the "LGPL"), in
|
|
|
|
|
* which case the following provisions apply instead of the ones
|
|
|
|
|
* mentioned above:
|
|
|
|
|
*
|
|
|
|
|
* 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
|
2012-11-03 20:38:00 +00:00
|
|
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
|
|
|
* Boston, MA 02110-1301, USA.
|
2009-08-10 09:36:41 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
2015-12-18 11:26:16 +00:00
|
|
|
|
TODO: add mixer device init for selection by device-guid
|
2009-08-10 09:36:41 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
/**
|
|
|
|
|
* SECTION:element-directsoundsrc
|
2017-03-08 18:01:13 +00:00
|
|
|
|
* @title: directsoundsrc
|
2012-04-03 09:09:19 +00:00
|
|
|
|
*
|
|
|
|
|
* Reads audio data using the DirectSound API.
|
|
|
|
|
*
|
2017-03-08 18:01:13 +00:00
|
|
|
|
* ## Example pipelines
|
2012-04-03 09:09:19 +00:00
|
|
|
|
* |[
|
2015-12-14 02:09:46 +00:00
|
|
|
|
* gst-launch-1.0 -v directsoundsrc ! audioconvert ! vorbisenc ! oggmux ! filesink location=dsound.ogg
|
2012-04-03 09:09:19 +00:00
|
|
|
|
* ]| Record from DirectSound and encode to Ogg/Vorbis.
|
2017-03-08 18:01:13 +00:00
|
|
|
|
*
|
2012-04-03 09:09:19 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <gst/gst.h>
|
2012-12-12 21:57:21 +00:00
|
|
|
|
#include <gst/audio/audio.h>
|
2012-04-03 09:09:19 +00:00
|
|
|
|
#include <gst/audio/gstaudiobasesrc.h>
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
#include "gstdirectsoundsrc.h"
|
|
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <dsound.h>
|
2015-03-31 09:53:55 +00:00
|
|
|
|
#include <mmsystem.h>
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
#include <stdio.h>
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (directsoundsrc_debug);
|
|
|
|
|
#define GST_CAT_DEFAULT directsoundsrc_debug
|
|
|
|
|
|
|
|
|
|
/* defaults here */
|
|
|
|
|
#define DEFAULT_DEVICE 0
|
2015-03-31 09:53:55 +00:00
|
|
|
|
#define DEFAULT_MUTE FALSE
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
PROP_0,
|
2015-03-31 09:53:55 +00:00
|
|
|
|
PROP_DEVICE_NAME,
|
2015-12-18 11:26:16 +00:00
|
|
|
|
PROP_DEVICE,
|
2015-03-31 09:53:55 +00:00
|
|
|
|
PROP_VOLUME,
|
|
|
|
|
PROP_MUTE
|
2009-07-29 13:35:03 +00:00
|
|
|
|
};
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2012-07-03 10:57:58 +00:00
|
|
|
|
static void gst_directsound_src_finalize (GObject * object);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
static void gst_directsound_src_set_property (GObject * object,
|
|
|
|
|
guint prop_id, const GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
|
|
|
|
static void gst_directsound_src_get_property (GObject * object,
|
|
|
|
|
guint prop_id, GValue * value, GParamSpec * pspec);
|
|
|
|
|
|
|
|
|
|
static gboolean gst_directsound_src_open (GstAudioSrc * asrc);
|
|
|
|
|
static gboolean gst_directsound_src_close (GstAudioSrc * asrc);
|
|
|
|
|
static gboolean gst_directsound_src_prepare (GstAudioSrc * asrc,
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GstAudioRingBufferSpec * spec);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static gboolean gst_directsound_src_unprepare (GstAudioSrc * asrc);
|
|
|
|
|
static void gst_directsound_src_reset (GstAudioSrc * asrc);
|
2012-04-03 09:09:19 +00:00
|
|
|
|
static GstCaps *gst_directsound_src_getcaps (GstBaseSrc * bsrc,
|
|
|
|
|
GstCaps * filter);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
static guint gst_directsound_src_read (GstAudioSrc * asrc,
|
2012-10-17 19:54:28 +00:00
|
|
|
|
gpointer data, guint length, GstClockTime * timestamp);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
static void gst_directsound_src_dispose (GObject * object);
|
|
|
|
|
|
|
|
|
|
static guint gst_directsound_src_delay (GstAudioSrc * asrc);
|
|
|
|
|
|
2015-03-31 09:53:55 +00:00
|
|
|
|
static gboolean gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
MIXERCAPS * mixer_caps);
|
|
|
|
|
static void gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc);
|
|
|
|
|
|
|
|
|
|
static gdouble gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc);
|
|
|
|
|
static void gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
gdouble volume);
|
|
|
|
|
|
|
|
|
|
static gboolean gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc);
|
|
|
|
|
static void gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
gboolean mute);
|
|
|
|
|
|
2015-12-18 11:26:16 +00:00
|
|
|
|
static const gchar *gst_directsound_src_get_device (GstDirectSoundSrc *
|
|
|
|
|
dsoundsrc);
|
|
|
|
|
static void gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
const gchar * device_id);
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static GstStaticPadTemplate directsound_src_src_factory =
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GST_PAD_SRC,
|
|
|
|
|
GST_PAD_ALWAYS,
|
2018-01-22 12:06:31 +00:00
|
|
|
|
GST_STATIC_CAPS (GST_DIRECTSOUND_SRC_CAPS));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
#define gst_directsound_src_parent_class parent_class
|
2015-03-31 09:53:55 +00:00
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSrc, gst_directsound_src,
|
|
|
|
|
GST_TYPE_AUDIO_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL)
|
|
|
|
|
);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_dispose (GObject * object)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static void
|
2012-07-03 10:57:58 +00:00
|
|
|
|
gst_directsound_src_finalize (GObject * object)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (object);
|
|
|
|
|
|
2012-10-25 15:10:40 +00:00
|
|
|
|
g_mutex_clear (&dsoundsrc->dsound_lock);
|
2017-05-09 10:36:10 +00:00
|
|
|
|
gst_object_unref (dsoundsrc->system_clock);
|
|
|
|
|
if (dsoundsrc->read_wait_clock_id != NULL)
|
|
|
|
|
gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
|
2012-07-03 10:57:58 +00:00
|
|
|
|
|
2013-08-22 15:32:17 +00:00
|
|
|
|
g_free (dsoundsrc->device_name);
|
2015-12-18 11:26:16 +00:00
|
|
|
|
|
|
|
|
|
g_free (dsoundsrc->device_id);
|
|
|
|
|
|
2013-08-22 15:32:17 +00:00
|
|
|
|
g_free (dsoundsrc->device_guid);
|
2013-08-22 11:45:59 +00:00
|
|
|
|
|
2012-07-03 10:57:58 +00:00
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_class_init (GstDirectSoundSrcClass * klass)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
|
GstElementClass *gstelement_class;
|
|
|
|
|
GstBaseSrcClass *gstbasesrc_class;
|
|
|
|
|
GstAudioSrcClass *gstaudiosrc_class;
|
|
|
|
|
|
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
|
|
|
gstelement_class = (GstElementClass *) klass;
|
|
|
|
|
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
|
|
|
|
gstaudiosrc_class = (GstAudioSrcClass *) klass;
|
|
|
|
|
|
2014-04-08 11:20:54 +00:00
|
|
|
|
GST_DEBUG_CATEGORY_INIT (directsoundsrc_debug, "directsoundsrc", 0,
|
|
|
|
|
"DirectSound Src");
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG ("initializing directsoundsrc class");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2012-07-03 10:57:58 +00:00
|
|
|
|
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_directsound_src_finalize);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_directsound_src_dispose);
|
|
|
|
|
gobject_class->get_property =
|
2009-07-29 13:35:03 +00:00
|
|
|
|
GST_DEBUG_FUNCPTR (gst_directsound_src_get_property);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
gobject_class->set_property =
|
2009-07-29 13:35:03 +00:00
|
|
|
|
GST_DEBUG_FUNCPTR (gst_directsound_src_set_property);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_directsound_src_getcaps);
|
|
|
|
|
|
|
|
|
|
gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_directsound_src_open);
|
|
|
|
|
gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_directsound_src_close);
|
|
|
|
|
gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_directsound_src_read);
|
|
|
|
|
gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_directsound_src_prepare);
|
|
|
|
|
gstaudiosrc_class->unprepare =
|
2009-07-29 13:35:03 +00:00
|
|
|
|
GST_DEBUG_FUNCPTR (gst_directsound_src_unprepare);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_directsound_src_delay);
|
|
|
|
|
gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_directsound_src_reset);
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
|
|
|
"DirectSound audio source", "Source/Audio",
|
|
|
|
|
"Capture from a soundcard via DirectSound",
|
|
|
|
|
"Joni Valtanen <joni.valtanen@movial.fi>");
|
|
|
|
|
|
2016-03-04 06:50:26 +00:00
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
|
|
|
&directsound_src_src_factory);
|
2013-08-22 11:45:59 +00:00
|
|
|
|
|
|
|
|
|
g_object_class_install_property
|
|
|
|
|
(gobject_class, PROP_DEVICE_NAME,
|
|
|
|
|
g_param_spec_string ("device-name", "Device name",
|
2013-08-22 15:32:17 +00:00
|
|
|
|
"Human-readable name of the sound device", NULL, G_PARAM_READWRITE));
|
2015-03-31 09:53:55 +00:00
|
|
|
|
|
2015-12-18 11:26:16 +00:00
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
|
|
|
PROP_DEVICE,
|
|
|
|
|
g_param_spec_string ("device", "Device",
|
|
|
|
|
"DirectSound playback device as a GUID string (volume and mute will not work!)",
|
|
|
|
|
NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
|
|
2015-03-31 09:53:55 +00:00
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
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));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static GstCaps *
|
2012-04-03 09:09:19 +00:00
|
|
|
|
gst_directsound_src_getcaps (GstBaseSrc * bsrc, GstCaps * filter)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstCaps *caps = NULL;
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (bsrc, "get caps");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2014-08-09 11:22:42 +00:00
|
|
|
|
caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (bsrc));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return caps;
|
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static void
|
2009-07-29 13:35:03 +00:00
|
|
|
|
gst_directsound_src_set_property (GObject * object, guint prop_id,
|
2009-08-10 09:36:41 +00:00
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2013-08-22 11:45:59 +00:00
|
|
|
|
GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG ("set property");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
switch (prop_id) {
|
2013-08-22 11:45:59 +00:00
|
|
|
|
case PROP_DEVICE_NAME:
|
|
|
|
|
if (src->device_name) {
|
|
|
|
|
g_free (src->device_name);
|
|
|
|
|
src->device_name = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (g_value_get_string (value)) {
|
|
|
|
|
src->device_name = g_strdup (g_value_get_string (value));
|
|
|
|
|
}
|
2015-03-31 09:53:55 +00:00
|
|
|
|
break;
|
|
|
|
|
case PROP_VOLUME:
|
|
|
|
|
gst_directsound_src_set_volume (src, g_value_get_double (value));
|
|
|
|
|
break;
|
|
|
|
|
case PROP_MUTE:
|
|
|
|
|
gst_directsound_src_set_mute (src, g_value_get_boolean (value));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
break;
|
2015-12-18 11:26:16 +00:00
|
|
|
|
case PROP_DEVICE:
|
|
|
|
|
gst_directsound_src_set_device (src, g_value_get_string (value));
|
|
|
|
|
break;
|
2009-07-29 13:35:03 +00:00
|
|
|
|
default:
|
2009-08-10 09:36:41 +00:00
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static void
|
2009-07-29 13:35:03 +00:00
|
|
|
|
gst_directsound_src_get_property (GObject * object, guint prop_id,
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GValue * value, GParamSpec * pspec)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *src = GST_DIRECTSOUND_SRC (object);
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG ("get property");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
switch (prop_id) {
|
2013-08-22 11:45:59 +00:00
|
|
|
|
case PROP_DEVICE_NAME:
|
|
|
|
|
g_value_set_string (value, src->device_name);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
break;
|
2015-12-18 11:26:16 +00:00
|
|
|
|
case PROP_DEVICE:
|
|
|
|
|
g_value_set_string (value, gst_directsound_src_get_device (src));
|
|
|
|
|
break;
|
2015-03-31 09:53:55 +00:00
|
|
|
|
case PROP_VOLUME:
|
|
|
|
|
g_value_set_double (value, gst_directsound_src_get_volume (src));
|
|
|
|
|
break;
|
|
|
|
|
case PROP_MUTE:
|
|
|
|
|
g_value_set_boolean (value, gst_directsound_src_get_mute (src));
|
|
|
|
|
break;
|
2009-07-29 13:35:03 +00:00
|
|
|
|
default:
|
2009-08-10 09:36:41 +00:00
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* initialize the new element
|
|
|
|
|
* instantiate pads and add them to element
|
|
|
|
|
* set functions
|
|
|
|
|
* initialize structure
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2012-04-03 09:09:19 +00:00
|
|
|
|
gst_directsound_src_init (GstDirectSoundSrc * src)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (src, "initializing directsoundsrc");
|
2012-10-25 15:10:40 +00:00
|
|
|
|
g_mutex_init (&src->dsound_lock);
|
2017-05-09 10:36:10 +00:00
|
|
|
|
src->system_clock = gst_system_clock_obtain ();
|
|
|
|
|
src->read_wait_clock_id = NULL;
|
|
|
|
|
src->reset_while_sleeping = FALSE;
|
2013-08-22 11:45:59 +00:00
|
|
|
|
src->device_guid = NULL;
|
2015-12-18 11:26:16 +00:00
|
|
|
|
src->device_id = NULL;
|
2013-08-22 11:45:59 +00:00
|
|
|
|
src->device_name = NULL;
|
2015-03-31 09:53:55 +00:00
|
|
|
|
src->mixer = NULL;
|
|
|
|
|
src->control_id_mute = -1;
|
|
|
|
|
src->control_id_volume = -1;
|
|
|
|
|
src->volume = 100;
|
|
|
|
|
src->mute = FALSE;
|
2013-08-22 11:45:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Enumeration callback called by DirectSoundCaptureEnumerate.
|
|
|
|
|
* Gets the GUID of request audio device
|
|
|
|
|
*/
|
|
|
|
|
static BOOL CALLBACK
|
2013-08-22 15:32:17 +00:00
|
|
|
|
gst_directsound_enum_callback (GUID * pGUID, TCHAR * strDesc,
|
|
|
|
|
TCHAR * strDrvName, VOID * pContext)
|
2013-08-22 11:45:59 +00:00
|
|
|
|
{
|
|
|
|
|
GstDirectSoundSrc *dsoundsrc = GST_DIRECTSOUND_SRC (pContext);
|
2016-05-02 08:30:43 +00:00
|
|
|
|
gchar *driver, *description;
|
|
|
|
|
|
|
|
|
|
description = g_locale_to_utf8 (strDesc, -1, NULL, NULL, NULL);
|
|
|
|
|
if (!description) {
|
|
|
|
|
GST_ERROR_OBJECT (dsoundsrc,
|
|
|
|
|
"Failed to convert description from locale encoding to UTF8");
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
driver = g_locale_to_utf8 (strDrvName, -1, NULL, NULL, NULL);
|
2013-08-22 11:45:59 +00:00
|
|
|
|
|
2013-08-22 15:32:17 +00:00
|
|
|
|
if (pGUID && dsoundsrc && dsoundsrc->device_name &&
|
2016-05-02 08:30:43 +00:00
|
|
|
|
!g_strcmp0 (dsoundsrc->device_name, description)) {
|
2013-08-22 15:32:17 +00:00
|
|
|
|
g_free (dsoundsrc->device_guid);
|
|
|
|
|
dsoundsrc->device_guid = (GUID *) g_malloc0 (sizeof (GUID));
|
|
|
|
|
memcpy (dsoundsrc->device_guid, pGUID, sizeof (GUID));
|
|
|
|
|
GST_INFO_OBJECT (dsoundsrc, "found the requested audio device :%s",
|
|
|
|
|
dsoundsrc->device_name);
|
2016-05-02 08:30:43 +00:00
|
|
|
|
g_free (description);
|
|
|
|
|
g_free (driver);
|
2013-08-22 11:45:59 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-22 15:32:17 +00:00
|
|
|
|
GST_INFO_OBJECT (dsoundsrc, "sound device names: %s, %s, requested device:%s",
|
2016-05-02 08:30:43 +00:00
|
|
|
|
description, driver, dsoundsrc->device_name);
|
|
|
|
|
|
|
|
|
|
g_free (description);
|
|
|
|
|
g_free (driver);
|
2013-08-22 11:45:59 +00:00
|
|
|
|
|
|
|
|
|
return TRUE;
|
2009-08-10 09:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-18 11:26:16 +00:00
|
|
|
|
static LPGUID
|
|
|
|
|
string_to_guid (const gchar * str)
|
|
|
|
|
{
|
|
|
|
|
HRESULT ret;
|
|
|
|
|
gunichar2 *wstr;
|
|
|
|
|
LPGUID out;
|
|
|
|
|
|
|
|
|
|
wstr = g_utf8_to_utf16 (str, -1, NULL, NULL, NULL);
|
|
|
|
|
if (!wstr)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
out = g_new (GUID, 1);
|
|
|
|
|
ret = CLSIDFromString ((LPOLESTR) wstr, out);
|
|
|
|
|
g_free (wstr);
|
|
|
|
|
if (ret != NOERROR) {
|
|
|
|
|
g_free (out);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static gboolean
|
|
|
|
|
gst_directsound_src_open (GstAudioSrc * asrc)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
HRESULT hRes; /* Result for windows functions */
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "opening directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
2015-12-18 11:26:16 +00:00
|
|
|
|
if (dsoundsrc->device_id) {
|
|
|
|
|
GST_DEBUG_OBJECT (asrc, "device id set to: %s ", dsoundsrc->device_id);
|
|
|
|
|
dsoundsrc->device_guid = string_to_guid (dsoundsrc->device_id);
|
|
|
|
|
if (dsoundsrc->device_guid == NULL) {
|
|
|
|
|
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
|
|
|
|
|
("gst_directsound_src_open: device set, but guid not found: %s",
|
|
|
|
|
dsoundsrc->device_id), (NULL));
|
|
|
|
|
g_free (dsoundsrc->device_guid);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-08-22 11:45:59 +00:00
|
|
|
|
|
2015-12-18 11:26:16 +00:00
|
|
|
|
hRes = DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)
|
|
|
|
|
gst_directsound_enum_callback, (VOID *) dsoundsrc);
|
|
|
|
|
|
|
|
|
|
if (FAILED (hRes)) {
|
|
|
|
|
goto capture_enumerate;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-08-10 09:36:41 +00:00
|
|
|
|
/* Create capture object */
|
2018-01-22 12:06:31 +00:00
|
|
|
|
hRes =
|
|
|
|
|
DirectSoundCaptureCreate (dsoundsrc->device_guid, &dsoundsrc->pDSC, NULL);
|
2015-12-18 11:26:16 +00:00
|
|
|
|
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
if (FAILED (hRes)) {
|
|
|
|
|
goto capture_object;
|
|
|
|
|
}
|
2015-12-18 11:26:16 +00:00
|
|
|
|
// mixer is only supported when device-id is not set
|
|
|
|
|
if (!dsoundsrc->device_id) {
|
|
|
|
|
gst_directsound_src_mixer_init (dsoundsrc);
|
|
|
|
|
}
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2013-08-22 11:45:59 +00:00
|
|
|
|
capture_enumerate:
|
|
|
|
|
{
|
|
|
|
|
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
|
|
|
|
|
("Unable to enumerate audio capture devices"), (NULL));
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2009-08-10 09:36:41 +00:00
|
|
|
|
capture_object:
|
|
|
|
|
{
|
|
|
|
|
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
|
2009-07-29 13:35:03 +00:00
|
|
|
|
("Unable to create capture object"), (NULL));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-29 13:35:03 +00:00
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
static gboolean
|
|
|
|
|
gst_directsound_src_close (GstAudioSrc * asrc)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "closing directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
|
|
|
|
/* Release capture handler */
|
2012-10-25 15:10:40 +00:00
|
|
|
|
IDirectSoundCapture_Release (dsoundsrc->pDSC);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2015-03-31 09:53:55 +00:00
|
|
|
|
if (dsoundsrc->mixer)
|
|
|
|
|
mixerClose (dsoundsrc->mixer);
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2012-04-03 09:09:19 +00:00
|
|
|
|
gst_directsound_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
WAVEFORMATEX wfx; /* Wave format structure */
|
|
|
|
|
HRESULT hRes; /* Result for windows functions */
|
2012-04-03 09:09:19 +00:00
|
|
|
|
DSCBUFFERDESC descSecondary; /* Capturebuffer description */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "preparing directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
/* Define buffer */
|
|
|
|
|
memset (&wfx, 0, sizeof (WAVEFORMATEX));
|
2012-04-03 09:09:19 +00:00
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
|
wfx.nChannels = GST_AUDIO_INFO_CHANNELS (&spec->info);
|
|
|
|
|
wfx.nSamplesPerSec = GST_AUDIO_INFO_RATE (&spec->info);
|
|
|
|
|
wfx.wBitsPerSample = GST_AUDIO_INFO_BPF (&spec->info) * 8 / wfx.nChannels;
|
|
|
|
|
wfx.nBlockAlign = GST_AUDIO_INFO_BPF (&spec->info);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
2012-04-03 09:09:19 +00:00
|
|
|
|
/* Ignored for WAVE_FORMAT_PCM. */
|
|
|
|
|
wfx.cbSize = 0;
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
if (wfx.wBitsPerSample != 16 && wfx.wBitsPerSample != 8)
|
|
|
|
|
goto dodgy_width;
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc, "latency time: %" G_GUINT64_FORMAT " - buffer time: %"
|
|
|
|
|
G_GUINT64_FORMAT, spec->latency_time, spec->buffer_time);
|
|
|
|
|
|
|
|
|
|
/* Buffer-time should always be >= 2*latency */
|
|
|
|
|
if (spec->buffer_time < spec->latency_time * 2) {
|
|
|
|
|
spec->buffer_time = spec->latency_time * 2;
|
|
|
|
|
GST_WARNING ("buffer-time was less than 2*latency-time, clamping");
|
|
|
|
|
}
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
/* Set the buffer size from our configured buffer time (in microsecs) */
|
|
|
|
|
dsoundsrc->buffer_size =
|
|
|
|
|
gst_util_uint64_scale_int (spec->buffer_time, wfx.nAvgBytesPerSec,
|
|
|
|
|
GST_SECOND / GST_USECOND);
|
|
|
|
|
|
|
|
|
|
GST_INFO_OBJECT (asrc, "Buffer size: %d", dsoundsrc->buffer_size);
|
|
|
|
|
|
|
|
|
|
spec->segsize =
|
|
|
|
|
gst_util_uint64_scale (spec->latency_time, wfx.nAvgBytesPerSec,
|
|
|
|
|
GST_SECOND / GST_USECOND);
|
|
|
|
|
|
|
|
|
|
/* Sanitized segsize */
|
|
|
|
|
if (spec->segsize < GST_AUDIO_INFO_BPF (&spec->info))
|
|
|
|
|
spec->segsize = GST_AUDIO_INFO_BPF (&spec->info);
|
|
|
|
|
else if (spec->segsize % GST_AUDIO_INFO_BPF (&spec->info) != 0)
|
|
|
|
|
spec->segsize =
|
|
|
|
|
((spec->segsize + GST_AUDIO_INFO_BPF (&spec->info) -
|
|
|
|
|
1) / GST_AUDIO_INFO_BPF (&spec->info)) *
|
|
|
|
|
GST_AUDIO_INFO_BPF (&spec->info);
|
|
|
|
|
spec->segtotal = dsoundsrc->buffer_size / spec->segsize;
|
|
|
|
|
/* The device usually takes time = 1-2 segments to start producing buffers */
|
|
|
|
|
spec->seglatency = spec->segtotal + 2;
|
|
|
|
|
|
|
|
|
|
/* Fetch and set the actual latency time that will be used */
|
|
|
|
|
dsoundsrc->latency_time =
|
|
|
|
|
gst_util_uint64_scale (spec->segsize, GST_SECOND / GST_USECOND,
|
|
|
|
|
GST_AUDIO_INFO_BPF (&spec->info) * GST_AUDIO_INFO_RATE (&spec->info));
|
|
|
|
|
|
|
|
|
|
GST_INFO_OBJECT (asrc, "actual latency time: %" G_GUINT64_FORMAT,
|
|
|
|
|
spec->latency_time);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
/* Init secondary buffer desciption */
|
|
|
|
|
memset (&descSecondary, 0, sizeof (DSCBUFFERDESC));
|
|
|
|
|
descSecondary.dwSize = sizeof (DSCBUFFERDESC);
|
|
|
|
|
descSecondary.dwFlags = 0;
|
|
|
|
|
descSecondary.dwReserved = 0;
|
|
|
|
|
|
|
|
|
|
/* This is not primary buffer so have to set size */
|
|
|
|
|
descSecondary.dwBufferBytes = dsoundsrc->buffer_size;
|
|
|
|
|
descSecondary.lpwfxFormat = &wfx;
|
|
|
|
|
|
|
|
|
|
/* Create buffer */
|
|
|
|
|
hRes = IDirectSoundCapture_CreateCaptureBuffer (dsoundsrc->pDSC,
|
|
|
|
|
&descSecondary, &dsoundsrc->pDSBSecondary, NULL);
|
2012-04-03 09:09:19 +00:00
|
|
|
|
if (hRes != DS_OK)
|
2009-08-10 09:36:41 +00:00
|
|
|
|
goto capture_buffer;
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
dsoundsrc->bytes_per_sample = GST_AUDIO_INFO_BPF (&spec->info);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc,
|
2012-04-03 09:09:19 +00:00
|
|
|
|
"bytes/sec: %lu, buffer size: %d, segsize: %d, segtotal: %d",
|
|
|
|
|
wfx.nAvgBytesPerSec, dsoundsrc->buffer_size, spec->segsize,
|
|
|
|
|
spec->segtotal);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
/* Not read anything yet */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
dsoundsrc->current_circular_offset = 0;
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc, "channels: %d, rate: %d, bytes_per_sample: %d"
|
2012-04-03 09:09:19 +00:00
|
|
|
|
" WAVEFORMATEX.nSamplesPerSec: %ld, WAVEFORMATEX.wBitsPerSample: %d,"
|
|
|
|
|
" WAVEFORMATEX.nBlockAlign: %d, WAVEFORMATEX.nAvgBytesPerSec: %ld",
|
|
|
|
|
GST_AUDIO_INFO_CHANNELS (&spec->info), GST_AUDIO_INFO_RATE (&spec->info),
|
|
|
|
|
GST_AUDIO_INFO_BPF (&spec->info), wfx.nSamplesPerSec, wfx.wBitsPerSample,
|
|
|
|
|
wfx.nBlockAlign, wfx.nAvgBytesPerSec);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
capture_buffer:
|
|
|
|
|
{
|
|
|
|
|
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
|
2009-07-29 13:35:03 +00:00
|
|
|
|
("Unable to create capturebuffer"), (NULL));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
dodgy_width:
|
|
|
|
|
{
|
|
|
|
|
GST_ELEMENT_ERROR (dsoundsrc, RESOURCE, OPEN_READ,
|
2012-04-03 09:09:19 +00:00
|
|
|
|
("Unexpected width %d", wfx.wBitsPerSample), (NULL));
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gst_directsound_src_unprepare (GstAudioSrc * asrc)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "unpreparing directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
2015-12-11 10:39:08 +00:00
|
|
|
|
GST_DSOUND_LOCK (dsoundsrc);
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
/* Stop capturing */
|
2012-10-25 15:10:40 +00:00
|
|
|
|
IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
/* Release buffer */
|
2012-10-25 15:10:40 +00:00
|
|
|
|
IDirectSoundCaptureBuffer_Release (dsoundsrc->pDSBSecondary);
|
2015-12-11 10:39:08 +00:00
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
return number of readed bytes */
|
|
|
|
|
static guint
|
2012-10-17 19:54:28 +00:00
|
|
|
|
gst_directsound_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
|
|
|
|
GstClockTime * timestamp)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
2017-05-09 10:36:10 +00:00
|
|
|
|
guint64 sleep_time_ms, sleep_until;
|
|
|
|
|
GstClockID clock_id;
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
HRESULT hRes; /* Result for windows functions */
|
|
|
|
|
DWORD dwCurrentCaptureCursor = 0;
|
|
|
|
|
DWORD dwBufferSize = 0;
|
|
|
|
|
|
|
|
|
|
LPVOID pLockedBuffer1 = NULL;
|
|
|
|
|
LPVOID pLockedBuffer2 = NULL;
|
|
|
|
|
DWORD dwSizeBuffer1 = 0;
|
|
|
|
|
DWORD dwSizeBuffer2 = 0;
|
|
|
|
|
|
|
|
|
|
DWORD dwStatus = 0;
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "reading directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
|
|
|
|
GST_DSOUND_LOCK (dsoundsrc);
|
|
|
|
|
|
|
|
|
|
/* Get current buffer status */
|
|
|
|
|
hRes = IDirectSoundCaptureBuffer_GetStatus (dsoundsrc->pDSBSecondary,
|
|
|
|
|
&dwStatus);
|
|
|
|
|
|
2015-12-11 10:39:08 +00:00
|
|
|
|
if (FAILED (hRes)) {
|
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
/* Starting capturing if not already */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
if (!(dwStatus & DSCBSTATUS_CAPTURING)) {
|
|
|
|
|
hRes = IDirectSoundCaptureBuffer_Start (dsoundsrc->pDSBSecondary,
|
|
|
|
|
DSCBSTART_LOOPING);
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc, "capture started");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
/* Loop till the source has produced bytes equal to or greater than @length.
|
2017-05-09 10:36:10 +00:00
|
|
|
|
*
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
* DirectSound has a notification-based API that uses Windows CreateEvent()
|
|
|
|
|
* + WaitForSingleObject(), but it is completely useless for live streams.
|
|
|
|
|
*
|
|
|
|
|
* 1. You must schedule all events before starting capture
|
|
|
|
|
* 2. The events are all fired exactly once
|
|
|
|
|
* 3. You cannot schedule new events while a capture is running
|
|
|
|
|
* 4. You cannot stop/schedule/start either
|
|
|
|
|
*
|
|
|
|
|
* This means you cannot use the API while doing live looped capture and we
|
|
|
|
|
* must resort to this.
|
|
|
|
|
*
|
|
|
|
|
* However, this is almost as efficient as event-based capture since it's ok
|
|
|
|
|
* to consistently overwait by a fixed amount; the extra bytes will just end
|
|
|
|
|
* up being used in the next call, and the extra latency will be constant. */
|
|
|
|
|
while (TRUE) {
|
2009-08-10 09:36:41 +00:00
|
|
|
|
hRes =
|
|
|
|
|
IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
|
|
|
|
|
&dwCurrentCaptureCursor, NULL);
|
|
|
|
|
|
2015-12-11 10:39:08 +00:00
|
|
|
|
if (FAILED (hRes)) {
|
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
/* calculate the size of the buffer that's been captured while accounting
|
|
|
|
|
* for wrap-arounds */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
|
|
|
|
|
dwBufferSize = dsoundsrc->buffer_size -
|
|
|
|
|
(dsoundsrc->current_circular_offset - dwCurrentCaptureCursor);
|
|
|
|
|
} else {
|
|
|
|
|
dwBufferSize =
|
|
|
|
|
dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
|
|
|
|
|
}
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
if (dwBufferSize >= length) {
|
|
|
|
|
/* Yay, we got all the data we need */
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
GST_DEBUG_OBJECT (asrc, "not enough data, got %lu (want at least %u)",
|
|
|
|
|
dwBufferSize, length);
|
|
|
|
|
/* If we didn't get enough data, sleep for a proportionate time */
|
|
|
|
|
sleep_time_ms = gst_util_uint64_scale (dsoundsrc->latency_time,
|
|
|
|
|
length - dwBufferSize, length * 1000);
|
|
|
|
|
/* Make sure we don't run in a tight loop unnecessarily */
|
|
|
|
|
sleep_time_ms = MAX (sleep_time_ms, 10);
|
2017-05-09 10:36:10 +00:00
|
|
|
|
/* Sleep using gst_clock_id_wait() so that we can be interrupted */
|
|
|
|
|
sleep_until = gst_clock_get_time (dsoundsrc->system_clock) +
|
|
|
|
|
sleep_time_ms * GST_MSECOND;
|
|
|
|
|
/* Setup the clock id wait */
|
|
|
|
|
if (G_UNLIKELY (dsoundsrc->read_wait_clock_id == NULL ||
|
|
|
|
|
gst_clock_single_shot_id_reinit (dsoundsrc->system_clock,
|
|
|
|
|
dsoundsrc->read_wait_clock_id, sleep_until) == FALSE)) {
|
|
|
|
|
if (dsoundsrc->read_wait_clock_id != NULL)
|
|
|
|
|
gst_clock_id_unref (dsoundsrc->read_wait_clock_id);
|
|
|
|
|
dsoundsrc->read_wait_clock_id =
|
|
|
|
|
gst_clock_new_single_shot_id (dsoundsrc->system_clock, sleep_until);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clock_id = dsoundsrc->read_wait_clock_id;
|
|
|
|
|
dsoundsrc->reset_while_sleeping = FALSE;
|
|
|
|
|
|
|
|
|
|
GST_DEBUG_OBJECT (asrc, "waiting %" G_GUINT64_FORMAT "ms for more data",
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
sleep_time_ms);
|
2017-05-09 10:36:10 +00:00
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
|
|
|
|
|
gst_clock_id_wait (clock_id, NULL);
|
|
|
|
|
|
|
|
|
|
GST_DSOUND_LOCK (dsoundsrc);
|
|
|
|
|
|
|
|
|
|
if (dsoundsrc->reset_while_sleeping == TRUE) {
|
|
|
|
|
GST_DEBUG_OBJECT (asrc, "reset while sleeping, cancelled read");
|
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 10:36:10 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "Got enough data: %lu bytes (wanted at least %u)",
|
|
|
|
|
dwBufferSize, length);
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
|
|
|
|
|
/* Lock the buffer and read only the first @length bytes. Keep the rest in
|
|
|
|
|
* the capture buffer for the next read. */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
|
|
|
|
|
dsoundsrc->current_circular_offset,
|
|
|
|
|
length,
|
|
|
|
|
&pLockedBuffer1, &dwSizeBuffer1, &pLockedBuffer2, &dwSizeBuffer2, 0L);
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
/* NOTE: We now assume that dwSizeBuffer1 + dwSizeBuffer2 == length since the
|
|
|
|
|
* API is supposed to guarantee that */
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
/* Copy buffer data to another buffer */
|
|
|
|
|
if (hRes == DS_OK) {
|
|
|
|
|
memcpy (data, pLockedBuffer1, dwSizeBuffer1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ...and if something is in another buffer */
|
|
|
|
|
if (pLockedBuffer2 != NULL) {
|
|
|
|
|
memcpy (((guchar *) data + dwSizeBuffer1), pLockedBuffer2, dwSizeBuffer2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dsoundsrc->current_circular_offset += dwSizeBuffer1 + dwSizeBuffer2;
|
|
|
|
|
dsoundsrc->current_circular_offset %= dsoundsrc->buffer_size;
|
|
|
|
|
|
|
|
|
|
IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
|
|
|
|
|
pLockedBuffer1, dwSizeBuffer1, pLockedBuffer2, dwSizeBuffer2);
|
|
|
|
|
|
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
/* We always read exactly @length data */
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint
|
|
|
|
|
gst_directsound_src_delay (GstAudioSrc * asrc)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
HRESULT hRes;
|
|
|
|
|
DWORD dwCurrentCaptureCursor;
|
|
|
|
|
DWORD dwBytesInQueue = 0;
|
|
|
|
|
gint nNbSamplesInQueue = 0;
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc, "Delay");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
|
|
|
|
/* evaluate the number of samples in queue in the circular buffer */
|
|
|
|
|
hRes =
|
|
|
|
|
IDirectSoundCaptureBuffer_GetCurrentPosition (dsoundsrc->pDSBSecondary,
|
|
|
|
|
&dwCurrentCaptureCursor, NULL);
|
|
|
|
|
/* FIXME: Check is this calculated right */
|
|
|
|
|
if (hRes == S_OK) {
|
|
|
|
|
if (dwCurrentCaptureCursor < dsoundsrc->current_circular_offset) {
|
|
|
|
|
dwBytesInQueue =
|
|
|
|
|
dsoundsrc->buffer_size - (dsoundsrc->current_circular_offset -
|
|
|
|
|
dwCurrentCaptureCursor);
|
|
|
|
|
} else {
|
|
|
|
|
dwBytesInQueue =
|
|
|
|
|
dwCurrentCaptureCursor - dsoundsrc->current_circular_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nNbSamplesInQueue = dwBytesInQueue / dsoundsrc->bytes_per_sample;
|
|
|
|
|
}
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
GST_INFO_OBJECT (asrc, "Delay is %d samples", nNbSamplesInQueue);
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
return nNbSamplesInQueue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_reset (GstAudioSrc * asrc)
|
2009-07-29 13:35:03 +00:00
|
|
|
|
{
|
2009-08-10 09:36:41 +00:00
|
|
|
|
GstDirectSoundSrc *dsoundsrc;
|
|
|
|
|
LPVOID pLockedBuffer = NULL;
|
|
|
|
|
DWORD dwSizeBuffer = 0;
|
|
|
|
|
|
2012-04-03 09:09:19 +00:00
|
|
|
|
GST_DEBUG_OBJECT (asrc, "reset directsoundsrc");
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
dsoundsrc = GST_DIRECTSOUND_SRC (asrc);
|
|
|
|
|
|
|
|
|
|
GST_DSOUND_LOCK (dsoundsrc);
|
|
|
|
|
|
2017-05-09 10:36:10 +00:00
|
|
|
|
dsoundsrc->reset_while_sleeping = TRUE;
|
|
|
|
|
/* Interrupt read sleep if required */
|
|
|
|
|
if (dsoundsrc->read_wait_clock_id != NULL)
|
|
|
|
|
gst_clock_id_unschedule (dsoundsrc->read_wait_clock_id);
|
|
|
|
|
|
2009-08-10 09:36:41 +00:00
|
|
|
|
if (dsoundsrc->pDSBSecondary) {
|
|
|
|
|
/*stop capturing */
|
|
|
|
|
HRESULT hRes = IDirectSoundCaptureBuffer_Stop (dsoundsrc->pDSBSecondary);
|
|
|
|
|
|
|
|
|
|
/*reset position */
|
|
|
|
|
/* hRes = IDirectSoundCaptureBuffer_SetCurrentPosition (dsoundsrc->pDSBSecondary, 0); */
|
|
|
|
|
|
|
|
|
|
/*reset the buffer */
|
|
|
|
|
hRes = IDirectSoundCaptureBuffer_Lock (dsoundsrc->pDSBSecondary,
|
|
|
|
|
dsoundsrc->current_circular_offset, dsoundsrc->buffer_size,
|
2017-07-10 15:35:32 +00:00
|
|
|
|
&pLockedBuffer, &dwSizeBuffer, NULL, NULL, 0L);
|
2009-08-10 09:36:41 +00:00
|
|
|
|
|
|
|
|
|
if (SUCCEEDED (hRes)) {
|
|
|
|
|
memset (pLockedBuffer, 0, dwSizeBuffer);
|
|
|
|
|
|
|
|
|
|
hRes =
|
|
|
|
|
IDirectSoundCaptureBuffer_Unlock (dsoundsrc->pDSBSecondary,
|
|
|
|
|
pLockedBuffer, dwSizeBuffer, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
dsoundsrc->current_circular_offset = 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GST_DSOUND_UNLOCK (dsoundsrc);
|
|
|
|
|
}
|
2015-03-31 09:53:55 +00:00
|
|
|
|
|
|
|
|
|
/* If the PROP_DEVICE_NAME is set, find the mixer related to device;
|
|
|
|
|
* otherwise we get the default input mixer. */
|
|
|
|
|
static gboolean
|
|
|
|
|
gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
MIXERCAPS * mixer_caps)
|
|
|
|
|
{
|
|
|
|
|
MMRESULT mmres;
|
|
|
|
|
guint i, num_mixers;
|
|
|
|
|
|
|
|
|
|
num_mixers = mixerGetNumDevs ();
|
|
|
|
|
for (i = 0; i < num_mixers; i++) {
|
|
|
|
|
mmres = mixerOpen (&dsoundsrc->mixer, i, 0L, 0L,
|
|
|
|
|
MIXER_OBJECTF_MIXER | MIXER_OBJECTF_WAVEIN);
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR)
|
|
|
|
|
continue;
|
|
|
|
|
|
directsoundsrc: Use latency-time and buffer-time settings
Earlier, the plugin was ignoring those settings and blindly setting
buffer-time to 2 seconds and latency-time to 200ms, which forced all
pipelines to have a minimum latency of 200ms + sink latency.
The values of segsize and segtotal were also not derived correctly.
Now we obey these values, and you can get close to the previous
behaviour by setting buffer-time and latency-time manually. Note that
they are set in microseconds.
As a consequence, when we haven't received enough data from the
device, we now sleep for a time proportional to the data remaining.
However, Directsound is a deprecated API so it maintains its own
software ringbuffer which updates at arbitrary intervals. Hence we
might have to wait a full segsize to get the last 10% of data. To
avoid tight loops, we clamp our sleep floor at 10ms.
In my testing, this keeps the wakeups not-too-high (proportional to
the latency-time set on the source). Further improvements should be
made by fixing the WASAPI audio source plugin instead of this.
Directsound is deprecated and as the comments explain, it is
impossible to get low latency, decent quality, or good performance
from it.
Based on a patch by Sebastian Dröge <sebastian@centricular.com>
https://bugzilla.gnome.org/show_bug.cgi?id=781249
2017-05-03 17:20:27 +00:00
|
|
|
|
mmres = mixerGetDevCaps ((UINT_PTR) dsoundsrc->mixer,
|
2015-03-31 09:53:55 +00:00
|
|
|
|
mixer_caps, sizeof (MIXERCAPS));
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR) {
|
|
|
|
|
mixerClose (dsoundsrc->mixer);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get default mixer */
|
|
|
|
|
if (dsoundsrc->device_name == NULL) {
|
|
|
|
|
GST_DEBUG ("Got default input mixer: %s", mixer_caps->szPname);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_strstr_len (dsoundsrc->device_name, -1, mixer_caps->szPname) != NULL) {
|
|
|
|
|
GST_DEBUG ("Got requested input mixer: %s", mixer_caps->szPname);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wrong mixer */
|
|
|
|
|
mixerClose (dsoundsrc->mixer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GST_DEBUG ("Can't find input mixer");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc)
|
|
|
|
|
{
|
|
|
|
|
gint i, k;
|
|
|
|
|
gboolean found_mic;
|
|
|
|
|
MMRESULT mmres;
|
|
|
|
|
MIXERCAPS mixer_caps;
|
|
|
|
|
MIXERLINE mixer_line;
|
|
|
|
|
MIXERLINECONTROLS ml_ctrl;
|
|
|
|
|
PMIXERCONTROL pamixer_ctrls;
|
|
|
|
|
|
|
|
|
|
if (!gst_directsound_src_mixer_find (dsoundsrc, &mixer_caps))
|
|
|
|
|
goto mixer_init_fail;
|
|
|
|
|
|
|
|
|
|
/* Find the MIXERLINE related to MICROPHONE */
|
|
|
|
|
found_mic = FALSE;
|
|
|
|
|
for (i = 0; i < mixer_caps.cDestinations && !found_mic; i++) {
|
|
|
|
|
gint j, num_connections;
|
|
|
|
|
|
|
|
|
|
mixer_line.cbStruct = sizeof (mixer_line);
|
|
|
|
|
mixer_line.dwDestination = i;
|
|
|
|
|
mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
|
|
|
|
|
&mixer_line, MIXER_GETLINEINFOF_DESTINATION);
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR)
|
|
|
|
|
goto mixer_init_fail;
|
|
|
|
|
|
|
|
|
|
num_connections = mixer_line.cConnections;
|
|
|
|
|
for (j = 0; j < num_connections && !found_mic; j++) {
|
|
|
|
|
mixer_line.cbStruct = sizeof (mixer_line);
|
|
|
|
|
mixer_line.dwDestination = i;
|
|
|
|
|
mixer_line.dwSource = j;
|
|
|
|
|
mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer,
|
|
|
|
|
&mixer_line, MIXER_GETLINEINFOF_SOURCE);
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR)
|
|
|
|
|
goto mixer_init_fail;
|
|
|
|
|
|
|
|
|
|
if (mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
|
|
|
|
|
|| mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE)
|
|
|
|
|
found_mic = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found_mic == FALSE) {
|
|
|
|
|
GST_DEBUG ("Can't find mixer line related to input");
|
|
|
|
|
goto mixer_init_fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get control associated with microphone audio line */
|
|
|
|
|
pamixer_ctrls = g_malloc (sizeof (MIXERCONTROL) * mixer_line.cControls);
|
|
|
|
|
ml_ctrl.cbStruct = sizeof (ml_ctrl);
|
|
|
|
|
ml_ctrl.dwLineID = mixer_line.dwLineID;
|
|
|
|
|
ml_ctrl.cControls = mixer_line.cControls;
|
|
|
|
|
ml_ctrl.cbmxctrl = sizeof (MIXERCONTROL);
|
|
|
|
|
ml_ctrl.pamxctrl = pamixer_ctrls;
|
|
|
|
|
mmres = mixerGetLineControls ((HMIXEROBJ) dsoundsrc->mixer,
|
|
|
|
|
&ml_ctrl, MIXER_GETLINECONTROLSF_ALL);
|
|
|
|
|
|
|
|
|
|
/* Find control associated with volume and mute */
|
|
|
|
|
for (k = 0; k < mixer_line.cControls; k++) {
|
|
|
|
|
if (strstr (pamixer_ctrls[k].szName, "Volume") != NULL) {
|
|
|
|
|
dsoundsrc->control_id_volume = pamixer_ctrls[k].dwControlID;
|
|
|
|
|
dsoundsrc->dw_vol_max = pamixer_ctrls[k].Bounds.dwMaximum;
|
|
|
|
|
dsoundsrc->dw_vol_min = pamixer_ctrls[k].Bounds.dwMinimum;
|
|
|
|
|
} else if (strstr (pamixer_ctrls[k].szName, "Mute") != NULL) {
|
|
|
|
|
dsoundsrc->control_id_mute = pamixer_ctrls[k].dwControlID;
|
|
|
|
|
} else {
|
|
|
|
|
GST_DEBUG ("Control not handled: %s", pamixer_ctrls[k].szName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_free (pamixer_ctrls);
|
|
|
|
|
|
|
|
|
|
if (dsoundsrc->control_id_volume < 0 && dsoundsrc->control_id_mute < 0)
|
|
|
|
|
goto mixer_init_fail;
|
|
|
|
|
|
|
|
|
|
/* Save cChannels information to properly changes in volume */
|
|
|
|
|
dsoundsrc->mixerline_cchannels = mixer_line.cChannels;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
mixer_init_fail:
|
|
|
|
|
GST_WARNING ("Failed to get Volume and Mute controls");
|
|
|
|
|
if (dsoundsrc->mixer != NULL) {
|
|
|
|
|
mixerClose (dsoundsrc->mixer);
|
|
|
|
|
dsoundsrc->mixer = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gdouble
|
|
|
|
|
gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc)
|
|
|
|
|
{
|
|
|
|
|
return (gdouble) dsoundsrc->volume / 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc)
|
|
|
|
|
{
|
|
|
|
|
return dsoundsrc->mute;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc, gdouble volume)
|
|
|
|
|
{
|
|
|
|
|
MMRESULT mmres;
|
|
|
|
|
MIXERCONTROLDETAILS details;
|
|
|
|
|
MIXERCONTROLDETAILS_UNSIGNED details_unsigned;
|
|
|
|
|
glong dwvolume;
|
|
|
|
|
|
|
|
|
|
if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_volume < 0) {
|
|
|
|
|
GST_WARNING ("mixer not initialized");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dwvolume = volume * dsoundsrc->dw_vol_max;
|
|
|
|
|
dwvolume = CLAMP (dwvolume, dsoundsrc->dw_vol_min, dsoundsrc->dw_vol_max);
|
|
|
|
|
|
|
|
|
|
GST_DEBUG ("max volume %ld | min volume %ld",
|
|
|
|
|
dsoundsrc->dw_vol_max, dsoundsrc->dw_vol_min);
|
|
|
|
|
GST_DEBUG ("set volume to %f (%ld)", volume, dwvolume);
|
|
|
|
|
|
|
|
|
|
details.cbStruct = sizeof (details);
|
|
|
|
|
details.dwControlID = dsoundsrc->control_id_volume;
|
|
|
|
|
details.cChannels = dsoundsrc->mixerline_cchannels;
|
|
|
|
|
details.cMultipleItems = 0;
|
|
|
|
|
|
|
|
|
|
details_unsigned.dwValue = dwvolume;
|
|
|
|
|
details.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
|
|
|
|
|
details.paDetails = &details_unsigned;
|
|
|
|
|
|
|
|
|
|
mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
|
|
|
|
|
&details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR)
|
|
|
|
|
GST_WARNING ("Failed to set volume");
|
|
|
|
|
else
|
|
|
|
|
dsoundsrc->volume = volume * 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc, gboolean mute)
|
|
|
|
|
{
|
|
|
|
|
MMRESULT mmres;
|
|
|
|
|
MIXERCONTROLDETAILS details;
|
|
|
|
|
MIXERCONTROLDETAILS_BOOLEAN details_boolean;
|
|
|
|
|
|
|
|
|
|
if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_mute < 0) {
|
|
|
|
|
GST_WARNING ("mixer not initialized");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
details.cbStruct = sizeof (details);
|
|
|
|
|
details.dwControlID = dsoundsrc->control_id_mute;
|
|
|
|
|
details.cChannels = dsoundsrc->mixerline_cchannels;
|
|
|
|
|
details.cMultipleItems = 0;
|
|
|
|
|
|
|
|
|
|
details_boolean.fValue = mute;
|
|
|
|
|
details.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN);
|
|
|
|
|
details.paDetails = &details_boolean;
|
|
|
|
|
|
|
|
|
|
mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer,
|
|
|
|
|
&details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE);
|
|
|
|
|
|
|
|
|
|
if (mmres != MMSYSERR_NOERROR)
|
|
|
|
|
GST_WARNING ("Failed to set mute");
|
|
|
|
|
else
|
|
|
|
|
dsoundsrc->mute = mute;
|
|
|
|
|
}
|
2015-12-18 11:26:16 +00:00
|
|
|
|
|
|
|
|
|
static const gchar *
|
|
|
|
|
gst_directsound_src_get_device (GstDirectSoundSrc * dsoundsrc)
|
|
|
|
|
{
|
|
|
|
|
return dsoundsrc->device_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gst_directsound_src_set_device (GstDirectSoundSrc * dsoundsrc,
|
|
|
|
|
const gchar * device_id)
|
|
|
|
|
{
|
|
|
|
|
g_free (dsoundsrc->device_id);
|
|
|
|
|
dsoundsrc->device_id = g_strdup (device_id);
|
|
|
|
|
}
|