1 /*
2  * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "gstksclock.h"
21 
22 #include "kshelpers.h"
23 
24 GST_DEBUG_CATEGORY_EXTERN (gst_ks_debug);
25 #define GST_CAT_DEFAULT gst_ks_debug
26 
27 struct _GstKsClockPrivate
28 {
29   GMutex mutex;
30   GCond client_cond;
31   GCond worker_cond;
32 
33   HANDLE clock_handle;
34 
35   gboolean open;
36   gboolean closing;
37   KSSTATE state;
38 
39   GThread *worker_thread;
40   gboolean worker_running;
41   gboolean worker_initialized;
42 
43   GstClock *master_clock;
44 };
45 
46 #define GST_KS_CLOCK_GET_PRIVATE(o) ((o)->priv)
47 
48 #define GST_KS_CLOCK_LOCK() g_mutex_lock (&priv->mutex)
49 #define GST_KS_CLOCK_UNLOCK() g_mutex_unlock (&priv->mutex)
50 
51 static void gst_ks_clock_dispose (GObject * object);
52 static void gst_ks_clock_finalize (GObject * object);
53 
54 G_DEFINE_TYPE_WITH_PRIVATE (GstKsClock, gst_ks_clock, G_TYPE_OBJECT);
55 
56 static GstKsClockClass *parent_class = NULL;
57 
58 
59 static void
gst_ks_clock_class_init(GstKsClockClass * klass)60 gst_ks_clock_class_init (GstKsClockClass * klass)
61 {
62   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
63 
64   parent_class = g_type_class_peek_parent (klass);
65 
66   gobject_class->dispose = gst_ks_clock_dispose;
67   gobject_class->finalize = gst_ks_clock_finalize;
68 }
69 
70 static void
gst_ks_clock_init(GstKsClock * self)71 gst_ks_clock_init (GstKsClock * self)
72 {
73   GstKsClockPrivate *priv;
74 
75   self->priv = gst_ks_clock_get_instance_private (self);
76 
77   priv = GST_KS_CLOCK_GET_PRIVATE (self);
78 
79   g_mutex_init (&priv->mutex);
80   g_cond_init (&priv->client_cond);
81   g_cond_init (&priv->worker_cond);
82 
83   priv->clock_handle = INVALID_HANDLE_VALUE;
84 
85   priv->open = FALSE;
86   priv->closing = FALSE;
87   priv->state = KSSTATE_STOP;
88 
89   priv->worker_thread = NULL;
90   priv->worker_running = FALSE;
91   priv->worker_initialized = FALSE;
92 
93   priv->master_clock = NULL;
94 }
95 
96 static void
gst_ks_clock_dispose(GObject * object)97 gst_ks_clock_dispose (GObject * object)
98 {
99   g_assert (!GST_KS_CLOCK_GET_PRIVATE (GST_KS_CLOCK (object))->open);
100 
101   G_OBJECT_CLASS (parent_class)->dispose (object);
102 }
103 
104 static void
gst_ks_clock_finalize(GObject * object)105 gst_ks_clock_finalize (GObject * object)
106 {
107   GstKsClock *self = GST_KS_CLOCK (object);
108   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
109 
110   g_cond_clear (&priv->worker_cond);
111   g_cond_clear (&priv->client_cond);
112   g_mutex_clear (&priv->mutex);
113 
114   G_OBJECT_CLASS (parent_class)->finalize (object);
115 }
116 
117 static void gst_ks_clock_close_unlocked (GstKsClock * self);
118 
119 gboolean
gst_ks_clock_open(GstKsClock * self)120 gst_ks_clock_open (GstKsClock * self)
121 {
122   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
123   GList *devices;
124   KsDeviceEntry *device;
125   KSSTATE state;
126 
127   GST_KS_CLOCK_LOCK ();
128 
129   g_assert (!priv->open);
130 
131   priv->state = KSSTATE_STOP;
132 
133   devices = ks_enumerate_devices (&KSCATEGORY_CLOCK, &KSCATEGORY_CAPTURE);
134   if (devices == NULL)
135     goto error;
136 
137   device = devices->data;
138 
139   priv->clock_handle = CreateFile (device->path, GENERIC_READ | GENERIC_WRITE,
140       0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
141       NULL);
142   if (!ks_is_valid_handle (priv->clock_handle))
143     goto error;
144 
145   state = KSSTATE_STOP;
146   if (!ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
147           KSPROPERTY_CLOCK_STATE, &state, sizeof (state), NULL))
148     goto error;
149 
150   ks_device_list_free (devices);
151   priv->open = TRUE;
152 
153   GST_KS_CLOCK_UNLOCK ();
154   return TRUE;
155 
156 error:
157   ks_device_list_free (devices);
158   gst_ks_clock_close_unlocked (self);
159 
160   GST_KS_CLOCK_UNLOCK ();
161   return FALSE;
162 }
163 
164 static gboolean
gst_ks_clock_set_state_unlocked(GstKsClock * self,KSSTATE state)165 gst_ks_clock_set_state_unlocked (GstKsClock * self, KSSTATE state)
166 {
167   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
168   KSSTATE initial_state;
169   gint addend;
170 
171   g_assert (priv->open);
172 
173   if (state == priv->state)
174     return TRUE;
175 
176   initial_state = priv->state;
177   addend = (state > priv->state) ? 1 : -1;
178 
179   GST_DEBUG ("Initiating clock state change from %s to %s",
180       ks_state_to_string (priv->state), ks_state_to_string (state));
181 
182   while (priv->state != state) {
183     KSSTATE next_state = priv->state + addend;
184 
185     GST_DEBUG ("Changing clock state from %s to %s",
186         ks_state_to_string (priv->state), ks_state_to_string (next_state));
187 
188     if (ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
189             KSPROPERTY_CLOCK_STATE, &next_state, sizeof (next_state), NULL)) {
190       priv->state = next_state;
191 
192       GST_DEBUG ("Changed clock state to %s", ks_state_to_string (priv->state));
193     } else {
194       GST_WARNING ("Failed to change clock state to %s",
195           ks_state_to_string (next_state));
196       return FALSE;
197     }
198   }
199 
200   GST_DEBUG ("Finished clock state change from %s to %s",
201       ks_state_to_string (initial_state), ks_state_to_string (state));
202 
203   return TRUE;
204 }
205 
206 static void
gst_ks_clock_close_unlocked(GstKsClock * self)207 gst_ks_clock_close_unlocked (GstKsClock * self)
208 {
209   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
210 
211   if (priv->closing)
212     return;
213 
214   priv->closing = TRUE;
215 
216   if (priv->worker_thread != NULL) {
217     priv->worker_running = FALSE;
218     g_cond_signal (&priv->worker_cond);
219 
220     GST_KS_CLOCK_UNLOCK ();
221     g_thread_join (priv->worker_thread);
222     priv->worker_thread = NULL;
223     GST_KS_CLOCK_LOCK ();
224   }
225 
226   if (priv->open)
227     gst_ks_clock_set_state_unlocked (self, KSSTATE_STOP);
228 
229   if (ks_is_valid_handle (priv->clock_handle)) {
230     CloseHandle (priv->clock_handle);
231     priv->clock_handle = INVALID_HANDLE_VALUE;
232   }
233 
234   if (priv->master_clock != NULL) {
235     gst_object_unref (priv->master_clock);
236     priv->master_clock = NULL;
237   }
238 
239   priv->open = FALSE;
240   priv->closing = FALSE;
241 }
242 
243 void
gst_ks_clock_close(GstKsClock * self)244 gst_ks_clock_close (GstKsClock * self)
245 {
246   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
247 
248   GST_KS_CLOCK_LOCK ();
249   gst_ks_clock_close_unlocked (self);
250   GST_KS_CLOCK_UNLOCK ();
251 }
252 
253 HANDLE
gst_ks_clock_get_handle(GstKsClock * self)254 gst_ks_clock_get_handle (GstKsClock * self)
255 {
256   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
257   HANDLE handle;
258 
259   GST_KS_CLOCK_LOCK ();
260   g_assert (priv->open);
261   handle = priv->clock_handle;
262   GST_KS_CLOCK_UNLOCK ();
263 
264   return handle;
265 }
266 
267 void
gst_ks_clock_prepare(GstKsClock * self)268 gst_ks_clock_prepare (GstKsClock * self)
269 {
270   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
271 
272   GST_KS_CLOCK_LOCK ();
273   if (priv->state < KSSTATE_PAUSE)
274     gst_ks_clock_set_state_unlocked (self, KSSTATE_PAUSE);
275   GST_KS_CLOCK_UNLOCK ();
276 }
277 
278 static gpointer
gst_ks_clock_worker_thread_func(gpointer data)279 gst_ks_clock_worker_thread_func (gpointer data)
280 {
281   GstKsClock *self = GST_KS_CLOCK (data);
282   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
283 
284   GST_KS_CLOCK_LOCK ();
285 
286   gst_ks_clock_set_state_unlocked (self, KSSTATE_RUN);
287 
288   while (priv->worker_running) {
289     if (priv->master_clock != NULL) {
290       GstClockTime now = gst_clock_get_time (priv->master_clock);
291       now /= 100;
292 
293       if (ks_object_set_property (priv->clock_handle, KSPROPSETID_Clock,
294               KSPROPERTY_CLOCK_TIME, &now, sizeof (now), NULL)) {
295         GST_DEBUG ("clock synchronized");
296         gst_object_unref (priv->master_clock);
297         priv->master_clock = NULL;
298       } else {
299         GST_WARNING ("failed to synchronize clock");
300       }
301     }
302 
303     if (!priv->worker_initialized) {
304       priv->worker_initialized = TRUE;
305       g_cond_signal (&priv->client_cond);
306     }
307 
308     g_cond_wait (&priv->worker_cond, &priv->mutex);
309   }
310 
311   priv->worker_initialized = FALSE;
312   GST_KS_CLOCK_UNLOCK ();
313 
314   return NULL;
315 }
316 
317 void
gst_ks_clock_start(GstKsClock * self)318 gst_ks_clock_start (GstKsClock * self)
319 {
320   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
321 
322   GST_KS_CLOCK_LOCK ();
323 
324   if (priv->worker_thread == NULL) {
325     priv->worker_running = TRUE;
326     priv->worker_initialized = FALSE;
327 
328     priv->worker_thread =
329         g_thread_new ("ks-worker", gst_ks_clock_worker_thread_func, self);
330   }
331 
332   while (!priv->worker_initialized)
333     g_cond_wait (&priv->client_cond, &priv->mutex);
334 
335   GST_KS_CLOCK_UNLOCK ();
336 }
337 
338 void
gst_ks_clock_provide_master_clock(GstKsClock * self,GstClock * master_clock)339 gst_ks_clock_provide_master_clock (GstKsClock * self, GstClock * master_clock)
340 {
341   GstKsClockPrivate *priv = GST_KS_CLOCK_GET_PRIVATE (self);
342 
343   GST_KS_CLOCK_LOCK ();
344 
345   gst_object_ref (master_clock);
346   if (priv->master_clock != NULL)
347     gst_object_unref (priv->master_clock);
348   priv->master_clock = master_clock;
349   g_cond_signal (&priv->worker_cond);
350 
351   GST_KS_CLOCK_UNLOCK ();
352 }
353