38 * Last reviewed on 2006-03-08 (0.10.4) |
38 * Last reviewed on 2006-03-08 (0.10.4) |
39 */ |
39 */ |
40 |
40 |
41 #include "gst_private.h" |
41 #include "gst_private.h" |
42 #include "gstinfo.h" |
42 #include "gstinfo.h" |
43 |
|
44 #include "gstsystemclock.h" |
43 #include "gstsystemclock.h" |
|
44 #include "gstenumtypes.h" |
|
45 #include "gstpoll.h" |
|
46 |
|
47 #include <errno.h> |
45 |
48 |
46 #ifdef __SYMBIAN32__ |
49 #ifdef __SYMBIAN32__ |
47 #include <glib_global.h> |
50 #include <glib_global.h> |
48 #endif |
51 #endif |
49 |
52 |
50 /* Define this to get some extra debug about jitter from each clock_wait */ |
53 /* Define this to get some extra debug about jitter from each clock_wait */ |
51 #undef WAIT_DEBUGGING |
54 #undef WAIT_DEBUGGING |
|
55 |
|
56 struct _GstSystemClockPrivate |
|
57 { |
|
58 GstClockType clock_type; |
|
59 GstPoll *timer; |
|
60 gint wakeup_count; /* the number of entries with a pending wakeup */ |
|
61 gboolean async_wakeup; /* if the wakeup was because of a async list change */ |
|
62 }; |
|
63 |
|
64 #define GST_SYSTEM_CLOCK_GET_PRIVATE(obj) \ |
|
65 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_SYSTEM_CLOCK, \ |
|
66 GstSystemClockPrivate)) |
|
67 |
|
68 #ifdef HAVE_POSIX_TIMERS |
|
69 # ifdef HAVE_MONOTONIC_CLOCK |
|
70 # define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_MONOTONIC |
|
71 # else |
|
72 # define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME |
|
73 # endif |
|
74 #else |
|
75 #define DEFAULT_CLOCK_TYPE GST_CLOCK_TYPE_REALTIME |
|
76 #endif |
|
77 |
|
78 enum |
|
79 { |
|
80 PROP_0, |
|
81 PROP_CLOCK_TYPE, |
|
82 /* FILL ME */ |
|
83 }; |
52 |
84 |
53 /* the one instance of the systemclock */ |
85 /* the one instance of the systemclock */ |
54 static GstClock *_the_system_clock = NULL; |
86 static GstClock *_the_system_clock = NULL; |
55 |
87 |
56 static void gst_system_clock_class_init (GstSystemClockClass * klass); |
88 static void gst_system_clock_class_init (GstSystemClockClass * klass); |
57 static void gst_system_clock_init (GstSystemClock * clock); |
89 static void gst_system_clock_init (GstSystemClock * clock); |
58 static void gst_system_clock_dispose (GObject * object); |
90 static void gst_system_clock_dispose (GObject * object); |
|
91 static void gst_system_clock_set_property (GObject * object, guint prop_id, |
|
92 const GValue * value, GParamSpec * pspec); |
|
93 static void gst_system_clock_get_property (GObject * object, guint prop_id, |
|
94 GValue * value, GParamSpec * pspec); |
59 |
95 |
60 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); |
96 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); |
61 static guint64 gst_system_clock_get_resolution (GstClock * clock); |
97 static guint64 gst_system_clock_get_resolution (GstClock * clock); |
62 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock, |
98 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock, |
63 GstClockEntry * entry, GstClockTimeDiff * jitter); |
99 GstClockEntry * entry, GstClockTimeDiff * jitter); |
68 GstClockEntry * entry); |
104 GstClockEntry * entry); |
69 static void gst_system_clock_id_unschedule (GstClock * clock, |
105 static void gst_system_clock_id_unschedule (GstClock * clock, |
70 GstClockEntry * entry); |
106 GstClockEntry * entry); |
71 static void gst_system_clock_async_thread (GstClock * clock); |
107 static void gst_system_clock_async_thread (GstClock * clock); |
72 static gboolean gst_system_clock_start_async (GstSystemClock * clock); |
108 static gboolean gst_system_clock_start_async (GstSystemClock * clock); |
|
109 static void gst_system_clock_add_wakeup (GstSystemClock * sysclock); |
73 |
110 |
74 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT; |
111 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT; |
75 |
112 |
76 static GstClockClass *parent_class = NULL; |
113 static GstClockClass *parent_class = NULL; |
77 |
114 |
78 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */ |
115 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */ |
79 #ifdef __SYMBIAN32__ |
116 |
80 EXPORT_C |
117 G_DEFINE_TYPE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK); |
81 #endif |
|
82 |
|
83 |
|
84 GType |
|
85 gst_system_clock_get_type (void) |
|
86 { |
|
87 static GType clock_type = 0; |
|
88 |
|
89 if (G_UNLIKELY (clock_type == 0)) { |
|
90 static const GTypeInfo clock_info = { |
|
91 sizeof (GstSystemClockClass), |
|
92 NULL, |
|
93 NULL, |
|
94 (GClassInitFunc) gst_system_clock_class_init, |
|
95 NULL, |
|
96 NULL, |
|
97 sizeof (GstSystemClock), |
|
98 0, |
|
99 (GInstanceInitFunc) gst_system_clock_init, |
|
100 NULL |
|
101 }; |
|
102 |
|
103 clock_type = g_type_register_static (GST_TYPE_CLOCK, "GstSystemClock", |
|
104 &clock_info, 0); |
|
105 } |
|
106 return clock_type; |
|
107 } |
|
108 |
118 |
109 static void |
119 static void |
110 gst_system_clock_class_init (GstSystemClockClass * klass) |
120 gst_system_clock_class_init (GstSystemClockClass * klass) |
111 { |
121 { |
112 GObjectClass *gobject_class; |
122 GObjectClass *gobject_class; |
113 GstObjectClass *gstobject_class; |
|
114 GstClockClass *gstclock_class; |
123 GstClockClass *gstclock_class; |
115 |
124 |
116 gobject_class = (GObjectClass *) klass; |
125 gobject_class = (GObjectClass *) klass; |
117 gstobject_class = (GstObjectClass *) klass; |
|
118 gstclock_class = (GstClockClass *) klass; |
126 gstclock_class = (GstClockClass *) klass; |
119 |
127 |
120 parent_class = g_type_class_peek_parent (klass); |
128 parent_class = g_type_class_peek_parent (klass); |
121 |
129 |
|
130 g_type_class_add_private (klass, sizeof (GstSystemClockPrivate)); |
|
131 |
122 gobject_class->dispose = gst_system_clock_dispose; |
132 gobject_class->dispose = gst_system_clock_dispose; |
|
133 gobject_class->set_property = gst_system_clock_set_property; |
|
134 gobject_class->get_property = gst_system_clock_get_property; |
|
135 |
|
136 g_object_class_install_property (gobject_class, PROP_CLOCK_TYPE, |
|
137 g_param_spec_enum ("clock-type", "Clock type", |
|
138 "The type of underlying clock implementation used", |
|
139 GST_TYPE_CLOCK_TYPE, DEFAULT_CLOCK_TYPE, G_PARAM_READWRITE)); |
123 |
140 |
124 gstclock_class->get_internal_time = gst_system_clock_get_internal_time; |
141 gstclock_class->get_internal_time = gst_system_clock_get_internal_time; |
125 gstclock_class->get_resolution = gst_system_clock_get_resolution; |
142 gstclock_class->get_resolution = gst_system_clock_get_resolution; |
126 gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter; |
143 gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter; |
127 gstclock_class->wait_async = gst_system_clock_id_wait_async; |
144 gstclock_class->wait_async = gst_system_clock_id_wait_async; |
164 entry->status = GST_CLOCK_UNSCHEDULED; |
185 entry->status = GST_CLOCK_UNSCHEDULED; |
165 } |
186 } |
166 g_list_free (clock->entries); |
187 g_list_free (clock->entries); |
167 clock->entries = NULL; |
188 clock->entries = NULL; |
168 GST_CLOCK_BROADCAST (clock); |
189 GST_CLOCK_BROADCAST (clock); |
|
190 gst_system_clock_add_wakeup (sysclock); |
169 GST_OBJECT_UNLOCK (clock); |
191 GST_OBJECT_UNLOCK (clock); |
170 |
192 |
171 if (sysclock->thread) |
193 if (sysclock->thread) |
172 g_thread_join (sysclock->thread); |
194 g_thread_join (sysclock->thread); |
173 sysclock->thread = NULL; |
195 sysclock->thread = NULL; |
174 GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread"); |
196 GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread"); |
175 |
197 |
|
198 gst_poll_free (sysclock->priv->timer); |
|
199 |
176 G_OBJECT_CLASS (parent_class)->dispose (object); |
200 G_OBJECT_CLASS (parent_class)->dispose (object); |
177 |
201 |
178 if (_the_system_clock == clock) { |
202 if (_the_system_clock == clock) { |
179 _the_system_clock = NULL; |
203 _the_system_clock = NULL; |
180 GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock"); |
204 GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock"); |
|
205 } |
|
206 } |
|
207 |
|
208 static void |
|
209 gst_system_clock_set_property (GObject * object, guint prop_id, |
|
210 const GValue * value, GParamSpec * pspec) |
|
211 { |
|
212 GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object); |
|
213 |
|
214 switch (prop_id) { |
|
215 case PROP_CLOCK_TYPE: |
|
216 sysclock->priv->clock_type = g_value_get_enum (value); |
|
217 GST_CAT_DEBUG (GST_CAT_CLOCK, "clock-type set to %d", |
|
218 sysclock->priv->clock_type); |
|
219 break; |
|
220 default: |
|
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
222 break; |
|
223 } |
|
224 } |
|
225 |
|
226 static void |
|
227 gst_system_clock_get_property (GObject * object, guint prop_id, GValue * value, |
|
228 GParamSpec * pspec) |
|
229 { |
|
230 GstSystemClock *sysclock = GST_SYSTEM_CLOCK (object); |
|
231 |
|
232 switch (prop_id) { |
|
233 case PROP_CLOCK_TYPE: |
|
234 g_value_set_enum (value, sysclock->priv->clock_type); |
|
235 break; |
|
236 default: |
|
237 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
238 break; |
181 } |
239 } |
182 } |
240 } |
183 |
241 |
184 /** |
242 /** |
185 * gst_system_clock_obtain: |
243 * gst_system_clock_obtain: |
224 /* we ref it since we are a clock factory. */ |
281 /* we ref it since we are a clock factory. */ |
225 gst_object_ref (clock); |
282 gst_object_ref (clock); |
226 return clock; |
283 return clock; |
227 } |
284 } |
228 |
285 |
|
286 static void |
|
287 gst_system_clock_remove_wakeup (GstSystemClock * sysclock) |
|
288 { |
|
289 g_return_if_fail (sysclock->priv->wakeup_count > 0); |
|
290 |
|
291 sysclock->priv->wakeup_count--; |
|
292 if (sysclock->priv->wakeup_count == 0) { |
|
293 /* read the control socket byte when we removed the last wakeup count */ |
|
294 GST_CAT_DEBUG (GST_CAT_CLOCK, "reading control"); |
|
295 while (!gst_poll_read_control (sysclock->priv->timer)) { |
|
296 g_warning ("gstsystemclock: read control failed, trying again\n"); |
|
297 } |
|
298 GST_CLOCK_BROADCAST (sysclock); |
|
299 } |
|
300 GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d", |
|
301 sysclock->priv->wakeup_count); |
|
302 } |
|
303 |
|
304 static void |
|
305 gst_system_clock_add_wakeup (GstSystemClock * sysclock) |
|
306 { |
|
307 /* only write the control socket for the first wakeup */ |
|
308 if (sysclock->priv->wakeup_count == 0) { |
|
309 GST_CAT_DEBUG (GST_CAT_CLOCK, "writing control"); |
|
310 while (!gst_poll_write_control (sysclock->priv->timer)) { |
|
311 g_warning |
|
312 ("gstsystemclock: write control failed in wakeup_async, trying again : %d:%s\n", |
|
313 errno, g_strerror (errno)); |
|
314 } |
|
315 } |
|
316 sysclock->priv->wakeup_count++; |
|
317 GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup count %d", |
|
318 sysclock->priv->wakeup_count); |
|
319 } |
|
320 |
|
321 static void |
|
322 gst_system_clock_wait_wakeup (GstSystemClock * sysclock) |
|
323 { |
|
324 while (sysclock->priv->wakeup_count > 0) { |
|
325 GST_CLOCK_WAIT (sysclock); |
|
326 } |
|
327 } |
|
328 |
229 /* this thread reads the sorted clock entries from the queue. |
329 /* this thread reads the sorted clock entries from the queue. |
230 * |
330 * |
231 * It waits on each of them and fires the callback when the timeout occurs. |
331 * It waits on each of them and fires the callback when the timeout occurs. |
232 * |
332 * |
233 * When an entry in the queue was canceled before we wait for it, it is |
333 * When an entry in the queue was canceled before we wait for it, it is |
287 case GST_CLOCK_OK: |
395 case GST_CLOCK_OK: |
288 case GST_CLOCK_EARLY: |
396 case GST_CLOCK_EARLY: |
289 { |
397 { |
290 /* entry timed out normally, fire the callback and move to the next |
398 /* entry timed out normally, fire the callback and move to the next |
291 * entry */ |
399 * entry */ |
292 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry); |
400 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p timed out", entry); |
293 if (entry->func) { |
401 if (entry->func) { |
294 /* unlock before firing the callback */ |
402 /* unlock before firing the callback */ |
295 GST_OBJECT_UNLOCK (clock); |
403 GST_OBJECT_UNLOCK (clock); |
296 entry->func (clock, entry->time, (GstClockID) entry, |
404 entry->func (clock, entry->time, (GstClockID) entry, |
297 entry->user_data); |
405 entry->user_data); |
298 GST_OBJECT_LOCK (clock); |
406 GST_OBJECT_LOCK (clock); |
299 } |
407 } |
300 if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { |
408 if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { |
|
409 GST_CAT_DEBUG (GST_CAT_CLOCK, "updating periodic entry %p", entry); |
301 /* adjust time now */ |
410 /* adjust time now */ |
302 entry->time = requested + entry->interval; |
411 entry->time = requested + entry->interval; |
303 /* and resort the list now */ |
412 /* and resort the list now */ |
304 clock->entries = |
413 clock->entries = |
305 g_list_sort (clock->entries, gst_clock_id_compare_func); |
414 g_list_sort (clock->entries, gst_clock_id_compare_func); |
306 /* and restart */ |
415 /* and restart */ |
307 continue; |
416 continue; |
308 } else { |
417 } else { |
|
418 GST_CAT_DEBUG (GST_CAT_CLOCK, "moving to next entry"); |
309 goto next_entry; |
419 goto next_entry; |
310 } |
420 } |
311 } |
421 } |
312 case GST_CLOCK_BUSY: |
422 case GST_CLOCK_BUSY: |
313 /* somebody unlocked the entry but is was not canceled, This means that |
423 /* somebody unlocked the entry but is was not canceled, This means that |
314 * either a new entry was added in front of the queue or some other entry |
424 * either a new entry was added in front of the queue or some other entry |
315 * was canceled. Whatever it is, pick the head entry of the list and |
425 * was canceled. Whatever it is, pick the head entry of the list and |
316 * continue waiting. */ |
426 * continue waiting. */ |
317 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry); |
427 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry); |
|
428 |
|
429 /* we set the entry back to the OK state. This is needed so that the |
|
430 * _unschedule() code can see if an entry is currently being waited |
|
431 * on (when its state is BUSY). */ |
|
432 entry->status = GST_CLOCK_OK; |
318 continue; |
433 continue; |
319 default: |
434 default: |
320 GST_CAT_DEBUG (GST_CAT_CLOCK, |
435 GST_CAT_DEBUG (GST_CAT_CLOCK, |
321 "strange result %d waiting for %p, skipping", res, entry); |
436 "strange result %d waiting for %p, skipping", res, entry); |
322 g_warning ("%s: strange result %d waiting for %p, skipping", |
437 g_warning ("%s: strange result %d waiting for %p, skipping", |
333 GST_CLOCK_BROADCAST (clock); |
448 GST_CLOCK_BROADCAST (clock); |
334 GST_OBJECT_UNLOCK (clock); |
449 GST_OBJECT_UNLOCK (clock); |
335 GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread"); |
450 GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread"); |
336 } |
451 } |
337 |
452 |
|
453 #ifdef HAVE_POSIX_TIMERS |
|
454 static inline clockid_t |
|
455 clock_type_to_posix_id (GstClockType clock_type) |
|
456 { |
|
457 #ifdef HAVE_MONOTONIC_CLOCK |
|
458 if (clock_type == GST_CLOCK_TYPE_MONOTONIC) |
|
459 return CLOCK_MONOTONIC; |
|
460 else |
|
461 #endif |
|
462 return CLOCK_REALTIME; |
|
463 } |
|
464 #endif |
|
465 |
338 /* MT safe */ |
466 /* MT safe */ |
339 static GstClockTime |
467 static GstClockTime |
340 gst_system_clock_get_internal_time (GstClock * clock) |
468 gst_system_clock_get_internal_time (GstClock * clock) |
341 { |
469 { |
|
470 #ifdef HAVE_POSIX_TIMERS |
|
471 GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock); |
|
472 clockid_t ptype; |
|
473 struct timespec ts; |
|
474 |
|
475 ptype = clock_type_to_posix_id (sysclock->priv->clock_type); |
|
476 |
|
477 if (G_UNLIKELY (clock_gettime (ptype, &ts))) |
|
478 return GST_CLOCK_TIME_NONE; |
|
479 |
|
480 return GST_TIMESPEC_TO_TIME (ts); |
|
481 #else |
342 GTimeVal timeval; |
482 GTimeVal timeval; |
343 |
483 |
344 g_get_current_time (&timeval); |
484 g_get_current_time (&timeval); |
345 |
485 |
346 return GST_TIMEVAL_TO_TIME (timeval); |
486 return GST_TIMEVAL_TO_TIME (timeval); |
|
487 #endif |
347 } |
488 } |
348 |
489 |
349 static guint64 |
490 static guint64 |
350 gst_system_clock_get_resolution (GstClock * clock) |
491 gst_system_clock_get_resolution (GstClock * clock) |
351 { |
492 { |
|
493 #ifdef HAVE_POSIX_TIMERS |
|
494 GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock); |
|
495 clockid_t ptype; |
|
496 struct timespec ts; |
|
497 |
|
498 ptype = clock_type_to_posix_id (sysclock->priv->clock_type); |
|
499 |
|
500 if (G_UNLIKELY (clock_getres (ptype, &ts))) |
|
501 return GST_CLOCK_TIME_NONE; |
|
502 |
|
503 return GST_TIMESPEC_TO_TIME (ts); |
|
504 #else |
352 return 1 * GST_USECOND; |
505 return 1 * GST_USECOND; |
|
506 #endif |
353 } |
507 } |
354 |
508 |
355 /* synchronously wait on the given GstClockEntry. |
509 /* synchronously wait on the given GstClockEntry. |
356 * |
510 * |
357 * We do this by blocking on the global clock GCond variable with |
511 * We do this by blocking on the global clock GCond variable with |
370 */ |
524 */ |
371 static GstClockReturn |
525 static GstClockReturn |
372 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock, |
526 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock, |
373 GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart) |
527 GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart) |
374 { |
528 { |
375 GstClockTime entryt, real, now, target; |
529 GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock); |
|
530 GstClockTime entryt, real, now; |
376 GstClockTimeDiff diff; |
531 GstClockTimeDiff diff; |
377 |
532 |
378 /* need to call the overridden method because we want to sync against the time |
533 /* need to call the overridden method because we want to sync against the time |
379 * of the clock, whatever the subclass uses as a clock. */ |
534 * of the clock, whatever the subclass uses as a clock. */ |
380 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
535 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
|
536 now = gst_clock_adjust_unlocked (clock, real); |
|
537 |
|
538 /* get the time of the entry */ |
381 entryt = GST_CLOCK_ENTRY_TIME (entry); |
539 entryt = GST_CLOCK_ENTRY_TIME (entry); |
382 |
540 |
383 now = gst_clock_adjust_unlocked (clock, real); |
|
384 if (jitter) { |
541 if (jitter) { |
385 *jitter = GST_CLOCK_DIFF (entryt, now); |
542 *jitter = GST_CLOCK_DIFF (entryt, now); |
386 } |
543 } |
387 /* the diff of the entry with the clock is the amount of time we have to |
544 /* the diff of the entry with the clock is the amount of time we have to |
388 * wait */ |
545 * wait */ |
389 diff = entryt - now; |
546 diff = entryt - now; |
390 /* Our GCond implementation expects an absolute time against the system clock |
|
391 * as a timeout value. We use our internal time to get the system time and add |
|
392 * the expected timeout to it, this gives us the absolute time of the |
|
393 * timeout. */ |
|
394 target = gst_system_clock_get_internal_time (clock) + diff; |
|
395 |
547 |
396 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" |
548 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" |
397 " target %" GST_TIME_FORMAT |
549 " time %" GST_TIME_FORMAT |
398 " entry %" GST_TIME_FORMAT |
|
399 " now %" GST_TIME_FORMAT |
550 " now %" GST_TIME_FORMAT |
400 " real %" GST_TIME_FORMAT |
551 " real %" GST_TIME_FORMAT |
401 " diff (entry-now) %" G_GINT64_FORMAT, |
552 " diff (time-now) %" G_GINT64_FORMAT, |
402 entry, |
553 entry, |
403 GST_TIME_ARGS (target), |
|
404 GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff); |
554 GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff); |
405 |
555 |
406 if (diff > 0) { |
556 if (diff > 0) { |
407 GTimeVal tv; |
|
408 |
|
409 #ifdef WAIT_DEBUGGING |
557 #ifdef WAIT_DEBUGGING |
410 GstClockTime result, final; |
558 GstClockTime final; |
411 #endif |
559 #endif |
412 |
560 |
413 GST_TIME_TO_TIMEVAL (target, tv); |
561 while (entry->status != GST_CLOCK_UNSCHEDULED) { |
414 |
562 gint pollret; |
415 while (TRUE) { |
563 |
416 /* now wait on the entry, it either times out or the cond is signaled. */ |
564 /* mark the entry as busy */ |
417 if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) { |
565 entry->status = GST_CLOCK_BUSY; |
418 /* timeout, this is fine, we can report success now */ |
566 GST_OBJECT_UNLOCK (clock); |
419 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry); |
567 |
420 entry->status = GST_CLOCK_OK; |
568 /* now wait on the entry, it either times out or the fd is written. */ |
|
569 pollret = gst_poll_wait (sysclock->priv->timer, diff); |
|
570 |
|
571 /* another thread can read the fd before we get the lock */ |
|
572 GST_OBJECT_LOCK (clock); |
|
573 if (entry->status == GST_CLOCK_UNSCHEDULED) { |
|
574 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked", entry); |
|
575 gst_system_clock_remove_wakeup (sysclock); |
|
576 } else { |
|
577 if (pollret != 0) { |
|
578 /* some other id got unlocked */ |
|
579 if (!restart) { |
|
580 /* this can happen if the entry got unlocked because of an async |
|
581 * entry was added to the head of the async queue. */ |
|
582 GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup waiting for entry %p", entry); |
|
583 break; |
|
584 } |
|
585 |
|
586 /* mark ourselves as EARLY, we release the lock and we could be |
|
587 * unscheduled ourselves but we don't want the unscheduling thread |
|
588 * to write on the control socket (it does that when an entry has a |
|
589 * BUSY status). */ |
|
590 entry->status = GST_CLOCK_EARLY; |
|
591 |
|
592 /* wait till all the entries got woken up */ |
|
593 gst_system_clock_wait_wakeup (sysclock); |
|
594 |
|
595 /* we released the lock in the wait, recheck our status, we don't need |
|
596 * to remove the wakeup count because we marked the entry as EARLY |
|
597 * before releasing the object lock. */ |
|
598 if (entry->status == GST_CLOCK_UNSCHEDULED) { |
|
599 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p got unscheduled", entry); |
|
600 break; |
|
601 } |
|
602 |
|
603 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p needs to be restarted", |
|
604 entry); |
|
605 } else { |
|
606 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", |
|
607 entry); |
|
608 } |
|
609 |
|
610 /* reschedule if gst_poll_wait returned early or we have to reschedule after |
|
611 * an unlock*/ |
|
612 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
|
613 now = gst_clock_adjust_unlocked (clock, real); |
|
614 diff = entryt - now; |
|
615 |
|
616 if (diff <= 0) { |
|
617 /* timeout, this is fine, we can report success now */ |
|
618 entry->status = GST_CLOCK_OK; |
|
619 |
|
620 GST_CAT_DEBUG (GST_CAT_CLOCK, |
|
621 "entry %p finished, diff %" G_GINT64_FORMAT, entry, diff); |
421 |
622 |
422 #ifdef WAIT_DEBUGGING |
623 #ifdef WAIT_DEBUGGING |
423 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
624 final = gst_system_clock_get_internal_time (clock); |
424 result = gst_clock_adjust_unlocked (clock, real); |
625 GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT |
425 final = gst_system_clock_get_internal_time (clock); |
626 " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT |
426 GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT |
627 " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now, |
427 " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT |
628 now - entryt, |
428 " %g target-offset %" G_GINT64_FORMAT " %g", entryt, result, |
629 (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND, |
429 result - entryt, |
630 (final - target), |
430 (double) (GstClockTimeDiff) (result - entryt) / GST_SECOND, |
631 ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND); |
431 (final - target), |
632 #endif |
432 ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND); |
|
433 #endif |
|
434 break; |
|
435 } else { |
|
436 /* the waiting is interrupted because the GCond was signaled. This can |
|
437 * be because this or some other entry was unscheduled. */ |
|
438 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry); |
|
439 /* if the entry is unscheduled, we can stop waiting for it, else we |
|
440 * continue our while loop. */ |
|
441 if (entry->status == GST_CLOCK_UNSCHEDULED) |
|
442 break; |
633 break; |
443 /* else restart if we must */ |
634 } else { |
444 if (!restart) |
635 GST_CAT_DEBUG (GST_CAT_CLOCK, |
445 break; |
636 "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff); |
446 |
637 } |
447 /* this can happen if the entry got unlocked because of an async entry |
|
448 * was added to the head of the async queue. */ |
|
449 GST_CAT_DEBUG (GST_CAT_CLOCK, "continue waiting for entry %p", entry); |
|
450 } |
638 } |
451 } |
639 } |
452 } else if (diff == 0) { |
640 } else if (diff == 0) { |
453 entry->status = GST_CLOCK_OK; |
641 entry->status = GST_CLOCK_OK; |
454 } else { |
642 } else { |
462 GstClockTimeDiff * jitter) |
650 GstClockTimeDiff * jitter) |
463 { |
651 { |
464 GstClockReturn ret; |
652 GstClockReturn ret; |
465 |
653 |
466 GST_OBJECT_LOCK (clock); |
654 GST_OBJECT_LOCK (clock); |
|
655 if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED)) |
|
656 goto was_unscheduled; |
|
657 |
467 ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE); |
658 ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE); |
468 GST_OBJECT_UNLOCK (clock); |
659 GST_OBJECT_UNLOCK (clock); |
469 |
660 |
470 return ret; |
661 return ret; |
|
662 |
|
663 /* ERRORS */ |
|
664 was_unscheduled: |
|
665 { |
|
666 GST_OBJECT_UNLOCK (clock); |
|
667 return GST_CLOCK_UNSCHEDULED; |
|
668 } |
471 } |
669 } |
472 |
670 |
473 /* Start the async clock thread. Must be called with the object lock |
671 /* Start the async clock thread. Must be called with the object lock |
474 * held */ |
672 * held */ |
475 static gboolean |
673 static gboolean |
476 gst_system_clock_start_async (GstSystemClock * clock) |
674 gst_system_clock_start_async (GstSystemClock * clock) |
477 { |
675 { |
478 GError *error = NULL; |
676 GError *error = NULL; |
479 |
677 |
480 if (clock->thread != NULL) |
678 if (G_LIKELY (clock->thread != NULL)) |
481 return TRUE; /* Thread already running. Nothing to do */ |
679 return TRUE; /* Thread already running. Nothing to do */ |
482 |
680 |
483 clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread, |
681 clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread, |
484 clock, TRUE, &error); |
682 clock, TRUE, &error); |
485 if (error) |
683 if (G_UNLIKELY (error)) |
486 goto no_thread; |
684 goto no_thread; |
487 |
685 |
488 /* wait for it to spin up */ |
686 /* wait for it to spin up */ |
489 GST_CLOCK_WAIT (clock); |
687 GST_CLOCK_WAIT (clock); |
490 |
688 |
524 |
735 |
525 /* only need to send the signal if the entry was added to the |
736 /* only need to send the signal if the entry was added to the |
526 * front, else the thread is just waiting for another entry and |
737 * front, else the thread is just waiting for another entry and |
527 * will get to this entry automatically. */ |
738 * will get to this entry automatically. */ |
528 if (clock->entries->data == entry) { |
739 if (clock->entries->data == entry) { |
529 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head, sending signal"); |
740 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head"); |
530 /* this will wake up _all_ entries waiting for the clock because we have |
741 if (head == NULL) { |
531 * only one cond for all entries (makes allocation faster). Entries that |
742 /* the list was empty before, signal the cond so that the async thread can |
532 * have not timed out will have their status set to BUSY and should continue |
743 * start taking a look at the queue */ |
533 * to wait. In the case of the async ones, the new head entry should be |
744 GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal"); |
534 * taken and waited for. */ |
745 GST_CLOCK_BROADCAST (clock); |
535 GST_CLOCK_BROADCAST (clock); |
746 } else { |
|
747 if (head->status == GST_CLOCK_BUSY) { |
|
748 /* the async thread was waiting for an entry, unlock the wait so that it |
|
749 * looks at the new head entry instead, we only need to do this once */ |
|
750 if (!sysclock->priv->async_wakeup) { |
|
751 GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup async thread"); |
|
752 sysclock->priv->async_wakeup = TRUE; |
|
753 gst_system_clock_add_wakeup (sysclock); |
|
754 } |
|
755 } |
|
756 } |
536 } |
757 } |
537 GST_OBJECT_UNLOCK (clock); |
758 GST_OBJECT_UNLOCK (clock); |
538 |
759 |
539 return GST_CLOCK_OK; |
760 return GST_CLOCK_OK; |
540 |
761 |
|
762 /* ERRORS */ |
541 thread_error: |
763 thread_error: |
542 /* Could not start the async clock thread */ |
764 { |
543 return GST_CLOCK_ERROR; |
765 /* Could not start the async clock thread */ |
|
766 GST_OBJECT_UNLOCK (clock); |
|
767 return GST_CLOCK_ERROR; |
|
768 } |
|
769 was_unscheduled: |
|
770 { |
|
771 GST_OBJECT_UNLOCK (clock); |
|
772 return GST_CLOCK_UNSCHEDULED; |
|
773 } |
544 } |
774 } |
545 |
775 |
546 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED |
776 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED |
547 * and will signal any thread waiting for entries to recheck their entry. |
777 * and will signal any thread waiting for entries to recheck their entry. |
548 * We cannot really decide if the signal is needed or not because the entry |
778 * We cannot really decide if the signal is needed or not because the entry |