gst/gstbin.c: Help the compiler a bit with type registration.
[platform/upstream/gstreamer.git] / gst / gstsystemclock.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2004 Wim Taymans <wim@fluendo.com>
4  *
5  * gstsystemclock.c: Default clock, uses the system clock
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /**
24  * SECTION:gstsystemclock
25  * @short_description: Default clock that uses the current system time
26  * @see_also: #GstClock
27  *
28  * The GStreamer core provides a GstSystemClock based on the system time.
29  * Asynchronous callbacks are scheduled from an internal thread.
30  *
31  * Clock implementors are encouraged to subclass this systemclock as it
32  * implements the async notification.
33  *
34  * Subclasses can however override all of the important methods for sync and
35  * async notifications to implement their own callback methods or blocking
36  * wait operations.
37  *
38  * Last reviewed on 2005-10-28 (0.9.4)
39  */
40
41 #include "gst_private.h"
42 #include "gstinfo.h"
43
44 #include "gstsystemclock.h"
45
46 /* the one instance of the systemclock */
47 static GstClock *_the_system_clock = NULL;
48
49 static void gst_system_clock_class_init (GstSystemClockClass * klass);
50 static void gst_system_clock_init (GstSystemClock * clock);
51 static void gst_system_clock_dispose (GObject * object);
52
53 static GstClockTime gst_system_clock_get_internal_time (GstClock * clock);
54 static guint64 gst_system_clock_get_resolution (GstClock * clock);
55 static GstClockReturn gst_system_clock_id_wait (GstClock * clock,
56     GstClockEntry * entry);
57 static GstClockReturn gst_system_clock_id_wait_unlocked
58     (GstClock * clock, GstClockEntry * entry);
59 static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock,
60     GstClockEntry * entry);
61 static void gst_system_clock_id_unschedule (GstClock * clock,
62     GstClockEntry * entry);
63 static void gst_system_clock_async_thread (GstClock * clock);
64
65 static GStaticMutex _gst_sysclock_mutex = G_STATIC_MUTEX_INIT;
66
67 static GstClockClass *parent_class = NULL;
68
69 /* static guint gst_system_clock_signals[LAST_SIGNAL] = { 0 }; */
70
71 GType
72 gst_system_clock_get_type (void)
73 {
74   static GType clock_type = 0;
75
76   if (G_UNLIKELY (clock_type == 0)) {
77     static const GTypeInfo clock_info = {
78       sizeof (GstSystemClockClass),
79       NULL,
80       NULL,
81       (GClassInitFunc) gst_system_clock_class_init,
82       NULL,
83       NULL,
84       sizeof (GstSystemClock),
85       0,
86       (GInstanceInitFunc) gst_system_clock_init,
87       NULL
88     };
89
90     clock_type = g_type_register_static (GST_TYPE_CLOCK, "GstSystemClock",
91         &clock_info, 0);
92   }
93   return clock_type;
94 }
95
96 static void
97 gst_system_clock_class_init (GstSystemClockClass * klass)
98 {
99   GObjectClass *gobject_class;
100   GstObjectClass *gstobject_class;
101   GstClockClass *gstclock_class;
102
103   gobject_class = (GObjectClass *) klass;
104   gstobject_class = (GstObjectClass *) klass;
105   gstclock_class = (GstClockClass *) klass;
106
107   parent_class = g_type_class_ref (GST_TYPE_CLOCK);
108
109   gobject_class->dispose = gst_system_clock_dispose;
110
111   gstclock_class->get_internal_time = gst_system_clock_get_internal_time;
112   gstclock_class->get_resolution = gst_system_clock_get_resolution;
113   gstclock_class->wait = gst_system_clock_id_wait;
114   gstclock_class->wait_async = gst_system_clock_id_wait_async;
115   gstclock_class->unschedule = gst_system_clock_id_unschedule;
116 }
117
118 static void
119 gst_system_clock_init (GstSystemClock * clock)
120 {
121   GError *error = NULL;
122
123   GST_OBJECT_FLAG_SET (clock,
124       GST_CLOCK_FLAG_CAN_DO_SINGLE_SYNC |
125       GST_CLOCK_FLAG_CAN_DO_SINGLE_ASYNC |
126       GST_CLOCK_FLAG_CAN_DO_PERIODIC_SYNC |
127       GST_CLOCK_FLAG_CAN_DO_PERIODIC_ASYNC);
128
129   GST_OBJECT_LOCK (clock);
130   clock->thread = g_thread_create ((GThreadFunc) gst_system_clock_async_thread,
131       clock, TRUE, &error);
132   if (error)
133     goto no_thread;
134
135   /* wait for it to spin up */
136   GST_CLOCK_WAIT (clock);
137   GST_OBJECT_UNLOCK (clock);
138   return;
139
140 no_thread:
141   {
142     g_warning ("could not create async clock thread: %s", error->message);
143     GST_OBJECT_UNLOCK (clock);
144   }
145 }
146
147 static void
148 gst_system_clock_dispose (GObject * object)
149 {
150   GstClock *clock = (GstClock *) object;
151
152   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
153   GList *entries;
154
155   /* else we have to stop the thread */
156   GST_OBJECT_LOCK (clock);
157   sysclock->stopping = TRUE;
158   /* unschedule all entries */
159   for (entries = clock->entries; entries; entries = g_list_next (entries)) {
160     GstClockEntry *entry = (GstClockEntry *) entries->data;
161
162     GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
163     entry->status = GST_CLOCK_UNSCHEDULED;
164   }
165   g_list_free (clock->entries);
166   clock->entries = NULL;
167   GST_CLOCK_BROADCAST (clock);
168   GST_OBJECT_UNLOCK (clock);
169
170   if (sysclock->thread)
171     g_thread_join (sysclock->thread);
172   sysclock->thread = NULL;
173   GST_CAT_DEBUG (GST_CAT_CLOCK, "joined thread");
174
175   G_OBJECT_CLASS (parent_class)->dispose (object);
176
177   if (_the_system_clock == clock) {
178     _the_system_clock = NULL;
179     GST_CAT_DEBUG (GST_CAT_CLOCK, "disposed system clock");
180   }
181 }
182
183 /**
184  * gst_system_clock_obtain:
185  *
186  * Get a handle to the default system clock. The refcount of the
187  * clock will be increased so you need to unref the clock after
188  * usage.
189  *
190  * Returns: the default clock.
191  *
192  * MT safe.
193  */
194 GstClock *
195 gst_system_clock_obtain (void)
196 {
197   GstClock *clock;
198
199   g_static_mutex_lock (&_gst_sysclock_mutex);
200   clock = _the_system_clock;
201
202   if (clock == NULL) {
203     GST_CAT_DEBUG (GST_CAT_CLOCK, "creating new static system clock");
204     clock = g_object_new (GST_TYPE_SYSTEM_CLOCK,
205         "name", "GstSystemClock", NULL);
206
207     /* we created the global clock; take ownership so
208      * we can hand out instances later */
209     gst_object_ref (clock);
210     gst_object_sink (GST_OBJECT (clock));
211
212     _the_system_clock = clock;
213     g_static_mutex_unlock (&_gst_sysclock_mutex);
214   } else {
215     g_static_mutex_unlock (&_gst_sysclock_mutex);
216     GST_CAT_DEBUG (GST_CAT_CLOCK, "returning static system clock");
217   }
218
219   /* we ref it since we are a clock factory. */
220   gst_object_ref (clock);
221   return clock;
222 }
223
224 /* this thread reads the sorted clock entries from the queue.
225  *
226  * It waits on each of them and fires the callback when the timeout occurs.
227  *
228  * When an entry in the queue was canceled, it is simply skipped.
229  *
230  * When waiting for an entry, it can become canceled, in that case we don't
231  * call the callback but move to the next item in the queue.
232  *
233  * MT safe.
234  */
235 static void
236 gst_system_clock_async_thread (GstClock * clock)
237 {
238   GstSystemClock *sysclock = GST_SYSTEM_CLOCK (clock);
239
240   GST_CAT_DEBUG (GST_CAT_CLOCK, "enter system clock thread");
241   GST_OBJECT_LOCK (clock);
242   /* signal spinup */
243   GST_CLOCK_BROADCAST (clock);
244   /* now enter our (almost) infinite loop */
245   while (!sysclock->stopping) {
246     GstClockEntry *entry;
247     GstClockReturn res;
248
249     /* check if something to be done */
250     while (clock->entries == NULL) {
251       GST_CAT_DEBUG (GST_CAT_CLOCK, "no clock entries, waiting..");
252       /* wait for work to do */
253       GST_CLOCK_WAIT (clock);
254       GST_CAT_DEBUG (GST_CAT_CLOCK, "got signal");
255       /* clock was stopping, exit */
256       if (sysclock->stopping)
257         goto exit;
258     }
259
260     /* pick the next entry */
261     entry = clock->entries->data;
262     /* if it was unscheduled, just move on to the next entry */
263     if (entry->status == GST_CLOCK_UNSCHEDULED) {
264       GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p was unscheduled", entry);
265       goto next_entry;
266     }
267
268     /* now wait for the entry, we already hold the lock */
269     res = gst_system_clock_id_wait_unlocked (clock, (GstClockID) entry);
270
271     switch (res) {
272       case GST_CLOCK_UNSCHEDULED:
273         /* entry was unscheduled, move to the next */
274         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unscheduled", entry);
275         goto next_entry;
276       case GST_CLOCK_OK:
277       case GST_CLOCK_EARLY:
278       {
279         /* entry timed out normally, fire the callback and move to the next
280          * entry */
281         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p unlocked", entry);
282         if (entry->func) {
283           /* unlock before firing the callback */
284           GST_OBJECT_UNLOCK (clock);
285           entry->func (clock, entry->time, (GstClockID) entry,
286               entry->user_data);
287           GST_OBJECT_LOCK (clock);
288         }
289         if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
290           /* adjust time now */
291           entry->time += entry->interval;
292           /* and resort the list now */
293           clock->entries =
294               g_list_sort (clock->entries, gst_clock_id_compare_func);
295           /* and restart */
296           continue;
297         } else {
298           goto next_entry;
299         }
300       }
301       case GST_CLOCK_BUSY:
302         /* somebody unlocked the entry but is was not canceled, This means that
303          * either a new entry was added in front of the queue or some other entry
304          * was canceled. Whatever it is, pick the head entry of the list and
305          * continue waiting. */
306         GST_CAT_DEBUG (GST_CAT_CLOCK, "async entry %p needs restart", entry);
307         continue;
308       default:
309         GST_CAT_DEBUG (GST_CAT_CLOCK,
310             "strange result %d waiting for %p, skipping", res, entry);
311         g_warning ("%s: strange result %d waiting for %p, skipping",
312             GST_OBJECT_NAME (clock), res, entry);
313         goto next_entry;
314     }
315   next_entry:
316     /* we remove the current entry and unref it */
317     clock->entries = g_list_remove (clock->entries, entry);
318     gst_clock_id_unref ((GstClockID) entry);
319   }
320 exit:
321   /* signal exit */
322   GST_CLOCK_BROADCAST (clock);
323   GST_OBJECT_UNLOCK (clock);
324   GST_CAT_DEBUG (GST_CAT_CLOCK, "exit system clock thread");
325 }
326
327 /* MT safe */
328 static GstClockTime
329 gst_system_clock_get_internal_time (GstClock * clock)
330 {
331   GTimeVal timeval;
332
333   g_get_current_time (&timeval);
334
335   return GST_TIMEVAL_TO_TIME (timeval);
336 }
337
338 static guint64
339 gst_system_clock_get_resolution (GstClock * clock)
340 {
341   return 1 * GST_USECOND;
342 }
343
344 /* synchronously wait on the given GstClockEntry.
345  *
346  * We do this by blocking on the global clock GCond variable with
347  * the requested time as a timeout. This allows us to unblock the
348  * entry by signaling the GCond variable.
349  *
350  * Note that signaling the global GCond unlocks all waiting entries. So
351  * we need to check if an unlocked entry has changed when it unlocks.
352  *
353  * Entries that arrive too late are simply not waited on and a
354  * GST_CLOCK_EARLY result is returned.
355  *
356  * should be called with LOCK held.
357  *
358  * MT safe.
359  */
360 static GstClockReturn
361 gst_system_clock_id_wait_unlocked (GstClock * clock, GstClockEntry * entry)
362 {
363   GstClockTime entryt, real, now, target;
364   GstClockTimeDiff diff;
365
366   /* need to call the overridden method */
367   real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock);
368   entryt = GST_CLOCK_ENTRY_TIME (entry);
369
370   now = gst_clock_adjust_unlocked (clock, real);
371   diff = entryt - now;
372   target = gst_system_clock_get_internal_time (clock) + diff;
373
374   GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p"
375       " target %" GST_TIME_FORMAT
376       " entry %" GST_TIME_FORMAT
377       " now %" GST_TIME_FORMAT
378       " real %" GST_TIME_FORMAT
379       " diff %" G_GINT64_FORMAT,
380       entry,
381       GST_TIME_ARGS (target),
382       GST_TIME_ARGS (entryt), GST_TIME_ARGS (now), GST_TIME_ARGS (real), diff);
383
384   if (diff > 0) {
385     GTimeVal tv;
386
387     GST_TIME_TO_TIMEVAL (target, tv);
388
389     while (TRUE) {
390       /* now wait on the entry, it either times out or the cond is signaled. */
391       if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) {
392         /* timeout, this is fine, we can report success now */
393         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked after timeout", entry);
394         entry->status = GST_CLOCK_OK;
395         break;
396       } else {
397         /* the waiting is interrupted because the GCond was signaled. This can
398          * be because this or some other entry was unscheduled. */
399         GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p unlocked with signal", entry);
400         /* if the entry is unscheduled, we can stop waiting for it */
401         if (entry->status == GST_CLOCK_UNSCHEDULED)
402           break;
403       }
404     }
405   } else if (diff == 0) {
406     entry->status = GST_CLOCK_OK;
407   } else {
408     entry->status = GST_CLOCK_EARLY;
409   }
410   return entry->status;
411 }
412
413 static GstClockReturn
414 gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry)
415 {
416   GstClockReturn ret;
417
418   GST_OBJECT_LOCK (clock);
419   ret = gst_system_clock_id_wait_unlocked (clock, entry);
420   GST_OBJECT_UNLOCK (clock);
421
422   return ret;
423 }
424
425 /* Add an entry to the list of pending async waits. The entry is inserted
426  * in sorted order. If we inserted the entry at the head of the list, we
427  * need to signal the thread as it might either be waiting on it or waiting
428  * for a new entry.
429  *
430  * MT safe.
431  */
432 static GstClockReturn
433 gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry)
434 {
435   GST_CAT_DEBUG (GST_CAT_CLOCK, "adding entry %p", entry);
436
437   GST_OBJECT_LOCK (clock);
438   /* need to take a ref */
439   gst_clock_id_ref ((GstClockID) entry);
440   /* insert the entry in sorted order */
441   clock->entries = g_list_insert_sorted (clock->entries, entry,
442       gst_clock_id_compare_func);
443
444   /* only need to send the signal if the entry was added to the
445    * front, else the thread is just waiting for another entry and
446    * will get to this entry automatically. */
447   if (clock->entries->data == entry) {
448     GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
449     GST_CLOCK_BROADCAST (clock);
450   }
451   GST_OBJECT_UNLOCK (clock);
452
453   return GST_CLOCK_OK;
454 }
455
456 /* unschedule an entry. This will set the state of the entry to GST_CLOCK_UNSCHEDULED
457  * and will signal any thread waiting for entries to recheck their entry.
458  * We cannot really decide if the signal is needed or not because the entry
459  * could be waited on in async or sync mode.
460  *
461  * MT safe.
462  */
463 static void
464 gst_system_clock_id_unschedule (GstClock * clock, GstClockEntry * entry)
465 {
466   GST_CAT_DEBUG (GST_CAT_CLOCK, "unscheduling entry %p", entry);
467
468   GST_OBJECT_LOCK (clock);
469   entry->status = GST_CLOCK_UNSCHEDULED;
470   GST_CAT_DEBUG (GST_CAT_CLOCK, "send signal");
471   GST_CLOCK_BROADCAST (clock);
472   GST_OBJECT_UNLOCK (clock);
473 }