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 |
43 #include "gstsystemclock.h" |
44 #include "gstsystemclock.h" |
44 #include "gstenumtypes.h" |
|
45 #include "gstpoll.h" |
|
46 |
|
47 #include <errno.h> |
|
48 |
45 |
49 #ifdef __SYMBIAN32__ |
46 #ifdef __SYMBIAN32__ |
50 #include <glib_global.h> |
47 #include <glib_global.h> |
51 #endif |
48 #endif |
52 |
49 |
53 /* Define this to get some extra debug about jitter from each clock_wait */ |
50 /* Define this to get some extra debug about jitter from each clock_wait */ |
54 #undef WAIT_DEBUGGING |
51 #undef WAIT_DEBUGGING |
55 |
52 |
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 }; |
|
84 |
|
85 /* the one instance of the systemclock */ |
53 /* the one instance of the systemclock */ |
86 static GstClock *_the_system_clock = NULL; |
54 static GstClock *_the_system_clock = NULL; |
87 |
55 |
88 static void gst_system_clock_class_init (GstSystemClockClass * klass); |
56 static void gst_system_clock_class_init (GstSystemClockClass * klass); |
89 static void gst_system_clock_init (GstSystemClock * clock); |
57 static void gst_system_clock_init (GstSystemClock * clock); |
90 static void gst_system_clock_dispose (GObject * object); |
58 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); |
|
95 |
59 |
96 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); |
60 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); |
97 static guint64 gst_system_clock_get_resolution (GstClock * clock); |
61 static guint64 gst_system_clock_get_resolution (GstClock * clock); |
98 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock, |
62 static GstClockReturn gst_system_clock_id_wait_jitter (GstClock * clock, |
99 GstClockEntry * entry, GstClockTimeDiff * jitter); |
63 GstClockEntry * entry, GstClockTimeDiff * jitter); |
104 GstClockEntry * entry); |
68 GstClockEntry * entry); |
105 static void gst_system_clock_id_unschedule (GstClock * clock, |
69 static void gst_system_clock_id_unschedule (GstClock * clock, |
106 GstClockEntry * entry); |
70 GstClockEntry * entry); |
107 static void gst_system_clock_async_thread (GstClock * clock); |
71 static void gst_system_clock_async_thread (GstClock * clock); |
108 static gboolean gst_system_clock_start_async (GstSystemClock * clock); |
72 static gboolean gst_system_clock_start_async (GstSystemClock * clock); |
109 static void gst_system_clock_add_wakeup (GstSystemClock * sysclock); |
|
110 |
73 |
111 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT; |
74 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT; |
112 |
75 |
113 static GstClockClass *parent_class = NULL; |
76 static GstClockClass *parent_class = NULL; |
114 |
77 |
115 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */ |
78 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */ |
116 |
79 #ifdef __SYMBIAN32__ |
117 G_DEFINE_TYPE (GstSystemClock, gst_system_clock, GST_TYPE_CLOCK); |
80 EXPORT_C |
|
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 } |
118 |
108 |
119 static void |
109 static void |
120 gst_system_clock_class_init (GstSystemClockClass * klass) |
110 gst_system_clock_class_init (GstSystemClockClass * klass) |
121 { |
111 { |
122 GObjectClass *gobject_class; |
112 GObjectClass *gobject_class; |
|
113 GstObjectClass *gstobject_class; |
123 GstClockClass *gstclock_class; |
114 GstClockClass *gstclock_class; |
124 |
115 |
125 gobject_class = (GObjectClass *) klass; |
116 gobject_class = (GObjectClass *) klass; |
|
117 gstobject_class = (GstObjectClass *) klass; |
126 gstclock_class = (GstClockClass *) klass; |
118 gstclock_class = (GstClockClass *) klass; |
127 |
119 |
128 parent_class = g_type_class_peek_parent (klass); |
120 parent_class = g_type_class_peek_parent (klass); |
129 |
121 |
130 g_type_class_add_private (klass, sizeof (GstSystemClockPrivate)); |
|
131 |
|
132 gobject_class->dispose = gst_system_clock_dispose; |
122 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)); |
|
140 |
123 |
141 gstclock_class->get_internal_time = gst_system_clock_get_internal_time; |
124 gstclock_class->get_internal_time = gst_system_clock_get_internal_time; |
142 gstclock_class->get_resolution = gst_system_clock_get_resolution; |
125 gstclock_class->get_resolution = gst_system_clock_get_resolution; |
143 gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter; |
126 gstclock_class->wait_jitter = gst_system_clock_id_wait_jitter; |
144 gstclock_class->wait_async = gst_system_clock_id_wait_async; |
127 gstclock_class->wait_async = gst_system_clock_id_wait_async; |
185 entry->status = GST_CLOCK_UNSCHEDULED; |
164 entry->status = GST_CLOCK_UNSCHEDULED; |
186 } |
165 } |
187 g_list_free (clock->entries); |
166 g_list_free (clock->entries); |
188 clock->entries = NULL; |
167 clock->entries = NULL; |
189 GST_CLOCK_BROADCAST (clock); |
168 GST_CLOCK_BROADCAST (clock); |
190 gst_system_clock_add_wakeup (sysclock); |
|
191 GST_OBJECT_UNLOCK (clock); |
169 GST_OBJECT_UNLOCK (clock); |
192 |
170 |
193 if (sysclock->thread) |
171 if (sysclock->thread) |
194 g_thread_join (sysclock->thread); |
172 g_thread_join (sysclock->thread); |
195 sysclock->thread = NULL; |
173 sysclock->thread = NULL; |
196 GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread"); |
174 GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread"); |
197 |
175 |
198 gst_poll_free (sysclock->priv->timer); |
|
199 |
|
200 G_OBJECT_CLASS (parent_class)->dispose (object); |
176 G_OBJECT_CLASS (parent_class)->dispose (object); |
201 |
177 |
202 if (_the_system_clock == clock) { |
178 if (_the_system_clock == clock) { |
203 _the_system_clock = NULL; |
179 _the_system_clock = NULL; |
204 GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock"); |
180 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; |
|
239 } |
181 } |
240 } |
182 } |
241 |
183 |
242 /** |
184 /** |
243 * gst_system_clock_obtain: |
185 * gst_system_clock_obtain: |
281 /* we ref it since we are a clock factory. */ |
224 /* we ref it since we are a clock factory. */ |
282 gst_object_ref (clock); |
225 gst_object_ref (clock); |
283 return clock; |
226 return clock; |
284 } |
227 } |
285 |
228 |
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 |
|
329 /* this thread reads the sorted clock entries from the queue. |
229 /* this thread reads the sorted clock entries from the queue. |
330 * |
230 * |
331 * It waits on each of them and fires the callback when the timeout occurs. |
231 * It waits on each of them and fires the callback when the timeout occurs. |
332 * |
232 * |
333 * When an entry in the queue was canceled before we wait for it, it is |
233 * When an entry in the queue was canceled before we wait for it, it is |
395 case GST_CLOCK_OK: |
287 case GST_CLOCK_OK: |
396 case GST_CLOCK_EARLY: |
288 case GST_CLOCK_EARLY: |
397 { |
289 { |
398 /* entry timed out normally, fire the callback and move to the next |
290 /* entry timed out normally, fire the callback and move to the next |
399 * entry */ |
291 * entry */ |
400 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p timed out", entry); |
292 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry); |
401 if (entry->func) { |
293 if (entry->func) { |
402 /* unlock before firing the callback */ |
294 /* unlock before firing the callback */ |
403 GST_OBJECT_UNLOCK (clock); |
295 GST_OBJECT_UNLOCK (clock); |
404 entry->func (clock, entry->time, (GstClockID) entry, |
296 entry->func (clock, entry->time, (GstClockID) entry, |
405 entry->user_data); |
297 entry->user_data); |
406 GST_OBJECT_LOCK (clock); |
298 GST_OBJECT_LOCK (clock); |
407 } |
299 } |
408 if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { |
300 if (entry->type == GST_CLOCK_ENTRY_PERIODIC) { |
409 GST_CAT_DEBUG (GST_CAT_CLOCK, "updating periodic entry %p", entry); |
|
410 /* adjust time now */ |
301 /* adjust time now */ |
411 entry->time = requested + entry->interval; |
302 entry->time = requested + entry->interval; |
412 /* and resort the list now */ |
303 /* and resort the list now */ |
413 clock->entries = |
304 clock->entries = |
414 g_list_sort (clock->entries, gst_clock_id_compare_func); |
305 g_list_sort (clock->entries, gst_clock_id_compare_func); |
415 /* and restart */ |
306 /* and restart */ |
416 continue; |
307 continue; |
417 } else { |
308 } else { |
418 GST_CAT_DEBUG (GST_CAT_CLOCK, "moving to next entry"); |
|
419 goto next_entry; |
309 goto next_entry; |
420 } |
310 } |
421 } |
311 } |
422 case GST_CLOCK_BUSY: |
312 case GST_CLOCK_BUSY: |
423 /* somebody unlocked the entry but is was not canceled, This means that |
313 /* somebody unlocked the entry but is was not canceled, This means that |
424 * either a new entry was added in front of the queue or some other entry |
314 * either a new entry was added in front of the queue or some other entry |
425 * was canceled. Whatever it is, pick the head entry of the list and |
315 * was canceled. Whatever it is, pick the head entry of the list and |
426 * continue waiting. */ |
316 * continue waiting. */ |
427 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry); |
317 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; |
|
433 continue; |
318 continue; |
434 default: |
319 default: |
435 GST_CAT_DEBUG (GST_CAT_CLOCK, |
320 GST_CAT_DEBUG (GST_CAT_CLOCK, |
436 "strange result %d waiting for %p, skipping", res, entry); |
321 "strange result %d waiting for %p, skipping", res, entry); |
437 g_warning ("%s: strange result %d waiting for %p, skipping", |
322 g_warning ("%s: strange result %d waiting for %p, skipping", |
448 GST_CLOCK_BROADCAST (clock); |
333 GST_CLOCK_BROADCAST (clock); |
449 GST_OBJECT_UNLOCK (clock); |
334 GST_OBJECT_UNLOCK (clock); |
450 GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread"); |
335 GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread"); |
451 } |
336 } |
452 |
337 |
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 |
|
466 /* MT safe */ |
338 /* MT safe */ |
467 static GstClockTime |
339 static GstClockTime |
468 gst_system_clock_get_internal_time (GstClock * clock) |
340 gst_system_clock_get_internal_time (GstClock * clock) |
469 { |
341 { |
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 |
|
482 GTimeVal timeval; |
342 GTimeVal timeval; |
483 |
343 |
484 g_get_current_time (&timeval); |
344 g_get_current_time (&timeval); |
485 |
345 |
486 return GST_TIMEVAL_TO_TIME (timeval); |
346 return GST_TIMEVAL_TO_TIME (timeval); |
487 #endif |
|
488 } |
347 } |
489 |
348 |
490 static guint64 |
349 static guint64 |
491 gst_system_clock_get_resolution (GstClock * clock) |
350 gst_system_clock_get_resolution (GstClock * clock) |
492 { |
351 { |
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 |
|
505 return 1 * GST_USECOND; |
352 return 1 * GST_USECOND; |
506 #endif |
|
507 } |
353 } |
508 |
354 |
509 /* synchronously wait on the given GstClockEntry. |
355 /* synchronously wait on the given GstClockEntry. |
510 * |
356 * |
511 * We do this by blocking on the global clock GCond variable with |
357 * We do this by blocking on the global clock GCond variable with |
524 */ |
370 */ |
525 static GstClockReturn |
371 static GstClockReturn |
526 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock, |
372 gst_system_clock_id_wait_jitter_unlocked (GstClock * clock, |
527 GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart) |
373 GstClockEntry * entry, GstClockTimeDiff * jitter, gboolean restart) |
528 { |
374 { |
529 GstSystemClock *sysclock = GST_SYSTEM_CLOCK_CAST (clock); |
375 GstClockTime entryt, real, now, target; |
530 GstClockTime entryt, real, now; |
|
531 GstClockTimeDiff diff; |
376 GstClockTimeDiff diff; |
532 |
377 |
533 /* need to call the overridden method because we want to sync against the time |
378 /* need to call the overridden method because we want to sync against the time |
534 * of the clock, whatever the subclass uses as a clock. */ |
379 * of the clock, whatever the subclass uses as a clock. */ |
535 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
380 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
|
381 entryt = GST_CLOCK_ENTRY_TIME (entry); |
|
382 |
536 now = gst_clock_adjust_unlocked (clock, real); |
383 now = gst_clock_adjust_unlocked (clock, real); |
537 |
|
538 /* get the time of the entry */ |
|
539 entryt = GST_CLOCK_ENTRY_TIME (entry); |
|
540 |
|
541 if (jitter) { |
384 if (jitter) { |
542 *jitter = GST_CLOCK_DIFF (entryt, now); |
385 *jitter = GST_CLOCK_DIFF (entryt, now); |
543 } |
386 } |
544 /* the diff of the entry with the clock is the amount of time we have to |
387 /* the diff of the entry with the clock is the amount of time we have to |
545 * wait */ |
388 * wait */ |
546 diff = entryt - now; |
389 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; |
547 |
395 |
548 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" |
396 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" |
549 " time %" GST_TIME_FORMAT |
397 " target %" GST_TIME_FORMAT |
|
398 " entry %" GST_TIME_FORMAT |
550 " now %" GST_TIME_FORMAT |
399 " now %" GST_TIME_FORMAT |
551 " real %" GST_TIME_FORMAT |
400 " real %" GST_TIME_FORMAT |
552 " diff (time-now) %" G_GINT64_FORMAT, |
401 " diff (entry-now) %" G_GINT64_FORMAT, |
553 entry, |
402 entry, |
|
403 GST_TIME_ARGS (target), |
554 GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff); |
404 GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff); |
555 |
405 |
556 if (diff > 0) { |
406 if (diff > 0) { |
|
407 GTimeVal tv; |
|
408 |
557 #ifdef WAIT_DEBUGGING |
409 #ifdef WAIT_DEBUGGING |
558 GstClockTime final; |
410 GstClockTime result, final; |
559 #endif |
411 #endif |
560 |
412 |
561 while (entry->status != GST_CLOCK_UNSCHEDULED) { |
413 GST_TIME_TO_TIMEVAL (target, tv); |
562 gint pollret; |
414 |
563 |
415 while (TRUE) { |
564 /* mark the entry as busy */ |
416 /* now wait on the entry, it either times out or the cond is signaled. */ |
565 entry->status = GST_CLOCK_BUSY; |
417 if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) { |
566 GST_OBJECT_UNLOCK (clock); |
418 /* timeout, this is fine, we can report success now */ |
567 |
419 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry); |
568 /* now wait on the entry, it either times out or the fd is written. */ |
420 entry->status = GST_CLOCK_OK; |
569 pollret = gst_poll_wait (sysclock->priv->timer, diff); |
421 |
570 |
422 #ifdef WAIT_DEBUGGING |
571 /* another thread can read the fd before we get the lock */ |
423 real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); |
572 GST_OBJECT_LOCK (clock); |
424 result = gst_clock_adjust_unlocked (clock, real); |
573 if (entry->status == GST_CLOCK_UNSCHEDULED) { |
425 final = gst_system_clock_get_internal_time (clock); |
574 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked", entry); |
426 GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT |
575 gst_system_clock_remove_wakeup (sysclock); |
427 " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT |
|
428 " %g target-offset %" G_GINT64_FORMAT " %g", entryt, result, |
|
429 result - entryt, |
|
430 (double) (GstClockTimeDiff) (result - entryt) / GST_SECOND, |
|
431 (final - target), |
|
432 ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND); |
|
433 #endif |
|
434 break; |
576 } else { |
435 } else { |
577 if (pollret != 0) { |
436 /* the waiting is interrupted because the GCond was signaled. This can |
578 /* some other id got unlocked */ |
437 * be because this or some other entry was unscheduled. */ |
579 if (!restart) { |
438 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry); |
580 /* this can happen if the entry got unlocked because of an async |
439 /* if the entry is unscheduled, we can stop waiting for it, else we |
581 * entry was added to the head of the async queue. */ |
440 * continue our while loop. */ |
582 GST_CAT_DEBUG (GST_CAT_CLOCK, "wakeup waiting for entry %p", entry); |
441 if (entry->status == GST_CLOCK_UNSCHEDULED) |
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); |
|
622 |
|
623 #ifdef WAIT_DEBUGGING |
|
624 final = gst_system_clock_get_internal_time (clock); |
|
625 GST_CAT_DEBUG (GST_CAT_CLOCK, "Waited for %" G_GINT64_FORMAT |
|
626 " got %" G_GINT64_FORMAT " diff %" G_GINT64_FORMAT |
|
627 " %g target-offset %" G_GINT64_FORMAT " %g", entryt, now, |
|
628 now - entryt, |
|
629 (double) (GstClockTimeDiff) (now - entryt) / GST_SECOND, |
|
630 (final - target), |
|
631 ((double) (GstClockTimeDiff) (final - target)) / GST_SECOND); |
|
632 #endif |
|
633 break; |
442 break; |
634 } else { |
443 /* else restart if we must */ |
635 GST_CAT_DEBUG (GST_CAT_CLOCK, |
444 if (!restart) |
636 "entry %p restart, diff %" G_GINT64_FORMAT, entry, diff); |
445 break; |
637 } |
446 |
|
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); |
638 } |
450 } |
639 } |
451 } |
640 } else if (diff == 0) { |
452 } else if (diff == 0) { |
641 entry->status = GST_CLOCK_OK; |
453 entry->status = GST_CLOCK_OK; |
642 } else { |
454 } else { |
650 GstClockTimeDiff * jitter) |
462 GstClockTimeDiff * jitter) |
651 { |
463 { |
652 GstClockReturn ret; |
464 GstClockReturn ret; |
653 |
465 |
654 GST_OBJECT_LOCK (clock); |
466 GST_OBJECT_LOCK (clock); |
655 if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED)) |
|
656 goto was_unscheduled; |
|
657 |
|
658 ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE); |
467 ret = gst_system_clock_id_wait_jitter_unlocked (clock, entry, jitter, TRUE); |
659 GST_OBJECT_UNLOCK (clock); |
468 GST_OBJECT_UNLOCK (clock); |
660 |
469 |
661 return ret; |
470 return ret; |
662 |
|
663 /* ERRORS */ |
|
664 was_unscheduled: |
|
665 { |
|
666 GST_OBJECT_UNLOCK (clock); |
|
667 return GST_CLOCK_UNSCHEDULED; |
|
668 } |
|
669 } |
471 } |
670 |
472 |
671 /* Start the async clock thread. Must be called with the object lock |
473 /* Start the async clock thread. Must be called with the object lock |
672 * held */ |
474 * held */ |
673 static gboolean |
475 static gboolean |
674 gst_system_clock_start_async (GstSystemClock * clock) |
476 gst_system_clock_start_async (GstSystemClock * clock) |
675 { |
477 { |
676 GError *error = NULL; |
478 GError *error = NULL; |
677 |
479 |
678 if (G_LIKELY (clock->thread != NULL)) |
480 if (clock->thread != NULL) |
679 return TRUE; /* Thread already running. Nothing to do */ |
481 return TRUE; /* Thread already running. Nothing to do */ |
680 |
482 |
681 clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread, |
483 clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread, |
682 clock, TRUE, &error); |
484 clock, TRUE, &error); |
683 if (G_UNLIKELY (error)) |
485 if (error) |
684 goto no_thread; |
486 goto no_thread; |
685 |
487 |
686 /* wait for it to spin up */ |
488 /* wait for it to spin up */ |
687 GST_CLOCK_WAIT (clock); |
489 GST_CLOCK_WAIT (clock); |
688 |
490 |
735 |
524 |
736 /* only need to send the signal if the entry was added to the |
525 /* only need to send the signal if the entry was added to the |
737 * front, else the thread is just waiting for another entry and |
526 * front, else the thread is just waiting for another entry and |
738 * will get to this entry automatically. */ |
527 * will get to this entry automatically. */ |
739 if (clock->entries->data == entry) { |
528 if (clock->entries->data == entry) { |
740 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head"); |
529 GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry added to head, sending signal"); |
741 if (head == NULL) { |
530 /* this will wake up _all_ entries waiting for the clock because we have |
742 /* the list was empty before, signal the cond so that the async thread can |
531 * only one cond for all entries (makes allocation faster). Entries that |
743 * start taking a look at the queue */ |
532 * have not timed out will have their status set to BUSY and should continue |
744 GST_CAT_DEBUG (GST_CAT_CLOCK, "first entry, sending signal"); |
533 * to wait. In the case of the async ones, the new head entry should be |
745 GST_CLOCK_BROADCAST (clock); |
534 * taken and waited for. */ |
746 } else { |
535 GST_CLOCK_BROADCAST (clock); |
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 } |
|
757 } |
536 } |
758 GST_OBJECT_UNLOCK (clock); |
537 GST_OBJECT_UNLOCK (clock); |
759 |
538 |
760 return GST_CLOCK_OK; |
539 return GST_CLOCK_OK; |
761 |
540 |
762 /* ERRORS */ |
|
763 thread_error: |
541 thread_error: |
764 { |
542 /* Could not start the async clock thread */ |
765 /* Could not start the async clock thread */ |
543 return GST_CLOCK_ERROR; |
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 } |
|
774 } |
544 } |
775 |
545 |
776 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED |
546 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED |
777 * and will signal any thread waiting for entries to recheck their entry. |
547 * and will signal any thread waiting for entries to recheck their entry. |
778 * We cannot really decide if the signal is needed or not because the entry |
548 * We cannot really decide if the signal is needed or not because the entry |