/* * Copyright (C) 2008 Ole André Vadla Ravnås * * 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 (); }