gstreamer/sys/winks/gstksclock.c
Руслан Ижбулатов a31855d618 GstDeviceProvider implementation for WIN Kernel Streaming plugin
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
2015-04-28 20:24:26 -04:00

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 ();
}