mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-05 09:00:54 +00:00
a31855d618
gst_ks_device_provider_probe() is a no-braier, just runs ks_enumerate_devices() and reports the results. Monitoring is a bit more tricky. We have to create a dummy message-processing window and register device change notifications for it. As kernel streaming can (and should) be used for audio capture and audio playback, this change also has certain placeholders for such. https://bugzilla.gnome.org/show_bug.cgi?id=747757
355 lines
8.5 KiB
C
355 lines
8.5 KiB
C
/*
|
|
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "gstksclock.h"
|
|
|
|
#include "kshelpers.h"
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
|
|
#define GST_CAT_DEFAULT gst_ks_debug
|
|
|
|
struct _GstKsClockPrivate
|
|
{
|
|
GMutex mutex;
|
|
GCond client_cond;
|
|
GCond worker_cond;
|
|
|
|
HANDLE clock_handle;
|
|
|
|
gboolean open;
|
|
gboolean closing;
|
|
KSSTATE state;
|
|
|
|
GThread *worker_thread;
|
|
gboolean worker_running;
|
|
gboolean worker_initialized;
|
|
|
|
GstClock *master_clock;
|
|
};
|
|
|
|
#define GST_KS_CLOCK_GET_PRIVATE(o) ((o)->priv)
|
|
|
|
#define GST_KS_CLOCK_LOCK() g_mutex_lock (&priv->mutex)
|
|
#define GST_KS_CLOCK_UNLOCK() g_mutex_unlock (&priv->mutex)
|
|
|
|
static void gst_ks_clock_dispose (GObject * object);
|
|
static void gst_ks_clock_finalize (GObject * object);
|
|
|
|
G_DEFINE_TYPE (GstKsClock, gst_ks_clock, G_TYPE_OBJECT);
|
|
|
|
static GstKsClockClass *parent_class = NULL;
|
|
|
|
|
|
static void
|
|
gst_ks_clock_class_init (GstKsClockClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
g_type_class_add_private (klass, sizeof (GstKsClockPrivate));
|
|
|
|
gobject_class->dispose = gst_ks_clock_dispose;
|
|
gobject_class->finalize = gst_ks_clock_finalize;
|
|
}
|
|
|
|
static void
|
|
gst_ks_clock_init (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv;
|
|
|
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GST_TYPE_KS_CLOCK,
|
|
GstKsClockPrivate);
|
|
|
|
priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
g_mutex_init (&priv->mutex);
|
|
g_cond_init (&priv->client_cond);
|
|
g_cond_init (&priv->worker_cond);
|
|
|
|
priv->clock_handle = INVALID_HANDLE_VALUE;
|
|
|
|
priv->open = FALSE;
|
|
priv->closing = FALSE;
|
|
priv->state = KSSTATE_STOP;
|
|
|
|
priv->worker_thread = NULL;
|
|
priv->worker_running = FALSE;
|
|
priv->worker_initialized = FALSE;
|
|
|
|
priv->master_clock = NULL;
|
|
}
|
|
|
|
static void
|
|
gst_ks_clock_dispose (GObject * object)
|
|
{
|
|
g_assert (!GST_KS_CLOCK_GET_PRIVATE (GST_KS_CLOCK (object))->open);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gst_ks_clock_finalize (GObject * object)
|
|
{
|
|
GstKsClock *self = GST_KS_CLOCK (object);
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
g_cond_clear (&priv->worker_cond);
|
|
g_cond_clear (&priv->client_cond);
|
|
g_mutex_clear (&priv->mutex);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void gst_ks_clock_close_unlocked (GstKsClock * self);
|
|
|
|
gboolean
|
|
gst_ks_clock_open (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
GList *devices;
|
|
KsDeviceEntry *device;
|
|
KSSTATE state;
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
|
|
g_assert (!priv->open);
|
|
|
|
priv->state = KSSTATE_STOP;
|
|
|
|
devices = ks_enumerate_devices (&KSCATEGORY_CLOCK, &KSCATEGORY_CAPTURE);
|
|
if (devices == NULL)
|
|
goto error;
|
|
|
|
device = devices->data;
|
|
|
|
priv->clock_handle = CreateFile (device->path, GENERIC_READ | GENERIC_WRITE,
|
|
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
if (!ks_is_valid_handle (priv->clock_handle))
|
|
goto error;
|
|
|
|
state = KSSTATE_STOP;
|
|
if (!ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
|
|
KSPROPERTY_CLOCK_STATE, &state, sizeof (state), NULL))
|
|
goto error;
|
|
|
|
ks_device_list_free (devices);
|
|
priv->open = TRUE;
|
|
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
return TRUE;
|
|
|
|
error:
|
|
ks_device_list_free (devices);
|
|
gst_ks_clock_close_unlocked (self);
|
|
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_ks_clock_set_state_unlocked (GstKsClock * self, KSSTATE state)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
KSSTATE initial_state;
|
|
gint addend;
|
|
|
|
g_assert (priv->open);
|
|
|
|
if (state == priv->state)
|
|
return TRUE;
|
|
|
|
initial_state = priv->state;
|
|
addend = (state > priv->state) ? 1 : -1;
|
|
|
|
GST_DEBUG ("Initiating clock state change from %s to %s",
|
|
ks_state_to_string (priv->state), ks_state_to_string (state));
|
|
|
|
while (priv->state != state) {
|
|
KSSTATE next_state = priv->state + addend;
|
|
|
|
GST_DEBUG ("Changing clock state from %s to %s",
|
|
ks_state_to_string (priv->state), ks_state_to_string (next_state));
|
|
|
|
if (ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
|
|
KSPROPERTY_CLOCK_STATE, &next_state, sizeof (next_state), NULL)) {
|
|
priv->state = next_state;
|
|
|
|
GST_DEBUG ("Changed clock state to %s", ks_state_to_string (priv->state));
|
|
} else {
|
|
GST_WARNING ("Failed to change clock state to %s",
|
|
ks_state_to_string (next_state));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
GST_DEBUG ("Finished clock state change from %s to %s",
|
|
ks_state_to_string (initial_state), ks_state_to_string (state));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gst_ks_clock_close_unlocked (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
if (priv->closing)
|
|
return;
|
|
|
|
priv->closing = TRUE;
|
|
|
|
if (priv->worker_thread != NULL) {
|
|
priv->worker_running = FALSE;
|
|
g_cond_signal (&priv->worker_cond);
|
|
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
g_thread_join (priv->worker_thread);
|
|
priv->worker_thread = NULL;
|
|
GST_KS_CLOCK_LOCK ();
|
|
}
|
|
|
|
if (priv->open)
|
|
gst_ks_clock_set_state_unlocked (self, KSSTATE_STOP);
|
|
|
|
if (ks_is_valid_handle (priv->clock_handle)) {
|
|
CloseHandle (priv->clock_handle);
|
|
priv->clock_handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (priv->master_clock != NULL) {
|
|
gst_object_unref (priv->master_clock);
|
|
priv->master_clock = NULL;
|
|
}
|
|
|
|
priv->open = FALSE;
|
|
priv->closing = FALSE;
|
|
}
|
|
|
|
void
|
|
gst_ks_clock_close (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
gst_ks_clock_close_unlocked (self);
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
}
|
|
|
|
HANDLE
|
|
gst_ks_clock_get_handle (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
HANDLE handle;
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
g_assert (priv->open);
|
|
handle = priv->clock_handle;
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
|
|
return handle;
|
|
}
|
|
|
|
void
|
|
gst_ks_clock_prepare (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
if (priv->state < KSSTATE_PAUSE)
|
|
gst_ks_clock_set_state_unlocked (self, KSSTATE_PAUSE);
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
}
|
|
|
|
static gpointer
|
|
gst_ks_clock_worker_thread_func (gpointer data)
|
|
{
|
|
GstKsClock *self = GST_KS_CLOCK (data);
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
|
|
gst_ks_clock_set_state_unlocked (self, KSSTATE_RUN);
|
|
|
|
while (priv->worker_running) {
|
|
if (priv->master_clock != NULL) {
|
|
GstClockTime now = gst_clock_get_time (priv->master_clock);
|
|
now /= 100;
|
|
|
|
if (ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
|
|
KSPROPERTY_CLOCK_TIME, &now, sizeof (now), NULL)) {
|
|
GST_DEBUG ("clock synchronized");
|
|
gst_object_unref (priv->master_clock);
|
|
priv->master_clock = NULL;
|
|
} else {
|
|
GST_WARNING ("failed to synchronize clock");
|
|
}
|
|
}
|
|
|
|
if (!priv->worker_initialized) {
|
|
priv->worker_initialized = TRUE;
|
|
g_cond_signal (&priv->client_cond);
|
|
}
|
|
|
|
g_cond_wait (&priv->worker_cond, &priv->mutex);
|
|
}
|
|
|
|
priv->worker_initialized = FALSE;
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gst_ks_clock_start (GstKsClock * self)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
|
|
if (priv->worker_thread == NULL) {
|
|
priv->worker_running = TRUE;
|
|
priv->worker_initialized = FALSE;
|
|
|
|
priv->worker_thread =
|
|
g_thread_new ("ks-worker", gst_ks_clock_worker_thread_func, self);
|
|
}
|
|
|
|
while (!priv->worker_initialized)
|
|
g_cond_wait (&priv->client_cond, &priv->mutex);
|
|
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
}
|
|
|
|
void
|
|
gst_ks_clock_provide_master_clock (GstKsClock * self, GstClock * master_clock)
|
|
{
|
|
GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
|
|
|
|
GST_KS_CLOCK_LOCK ();
|
|
|
|
gst_object_ref (master_clock);
|
|
if (priv->master_clock != NULL)
|
|
gst_object_unref (priv->master_clock);
|
|
priv->master_clock = master_clock;
|
|
g_cond_signal (&priv->worker_cond);
|
|
|
|
GST_KS_CLOCK_UNLOCK ();
|
|
}
|