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