1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * audioclock.c: Clock for use by audio plugins
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., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 /**
24  * SECTION:gstaudioclock
25  * @title: GstAudioClock
26  * @short_description: Helper object for implementing audio clocks
27  * @see_also: #GstAudioBaseSink, #GstSystemClock
28  *
29  * #GstAudioClock makes it easy for elements to implement a #GstClock, they
30  * simply need to provide a function that returns the current clock time.
31  *
32  * This object is internally used to implement the clock in #GstAudioBaseSink.
33  */
34 
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38 
39 #include "gstaudioclock.h"
40 
41 GST_DEBUG_CATEGORY_STATIC (gst_audio_clock_debug);
42 #define GST_CAT_DEFAULT gst_audio_clock_debug
43 
44 static void gst_audio_clock_dispose (GObject * object);
45 
46 static GstClockTime gst_audio_clock_get_internal_time (GstClock * clock);
47 
48 #define parent_class gst_audio_clock_parent_class
49 G_DEFINE_TYPE (GstAudioClock, gst_audio_clock, GST_TYPE_SYSTEM_CLOCK);
50 
51 static void
gst_audio_clock_class_init(GstAudioClockClass * klass)52 gst_audio_clock_class_init (GstAudioClockClass * klass)
53 {
54   GstClockClass *gstclock_class;
55   GObjectClass *gobject_class;
56 
57   gobject_class = (GObjectClass *) klass;
58   gstclock_class = (GstClockClass *) klass;
59 
60   gobject_class->dispose = gst_audio_clock_dispose;
61   gstclock_class->get_internal_time = gst_audio_clock_get_internal_time;
62 
63   GST_DEBUG_CATEGORY_INIT (gst_audio_clock_debug, "audioclock", 0,
64       "audioclock");
65 }
66 
67 static void
gst_audio_clock_init(GstAudioClock * clock)68 gst_audio_clock_init (GstAudioClock * clock)
69 {
70   GST_DEBUG_OBJECT (clock, "init");
71   clock->last_time = 0;
72   clock->time_offset = 0;
73   GST_OBJECT_FLAG_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER);
74 }
75 
76 static void
gst_audio_clock_dispose(GObject * object)77 gst_audio_clock_dispose (GObject * object)
78 {
79   GstAudioClock *clock = GST_AUDIO_CLOCK (object);
80 
81   if (clock->destroy_notify && clock->user_data)
82     clock->destroy_notify (clock->user_data);
83   clock->destroy_notify = NULL;
84   clock->user_data = NULL;
85 
86   G_OBJECT_CLASS (parent_class)->dispose (object);
87 }
88 
89 /**
90  * gst_audio_clock_new:
91  * @name: the name of the clock
92  * @func: a function
93  * @user_data: user data
94  * @destroy_notify: #GDestroyNotify for @user_data
95  *
96  * Create a new #GstAudioClock instance. Whenever the clock time should be
97  * calculated it will call @func with @user_data. When @func returns
98  * #GST_CLOCK_TIME_NONE, the clock will return the last reported time.
99  *
100  * Returns: (transfer full): a new #GstAudioClock casted to a #GstClock.
101  */
102 GstClock *
gst_audio_clock_new(const gchar * name,GstAudioClockGetTimeFunc func,gpointer user_data,GDestroyNotify destroy_notify)103 gst_audio_clock_new (const gchar * name, GstAudioClockGetTimeFunc func,
104     gpointer user_data, GDestroyNotify destroy_notify)
105 {
106   GstAudioClock *aclock =
107       GST_AUDIO_CLOCK (g_object_new (GST_TYPE_AUDIO_CLOCK, "name", name,
108           "clock-type", GST_CLOCK_TYPE_OTHER, NULL));
109 
110   aclock->func = func;
111   aclock->user_data = user_data;
112   aclock->destroy_notify = destroy_notify;
113 
114   gst_object_ref_sink (aclock);
115 
116   return (GstClock *) aclock;
117 }
118 
119 /**
120  * gst_audio_clock_reset:
121  * @clock: a #GstAudioClock
122  * @time: a #GstClockTime
123  *
124  * Inform @clock that future calls to #GstAudioClockGetTimeFunc will return values
125  * starting from @time. The clock will update an internal offset to make sure that
126  * future calls to internal_time will return an increasing result as required by
127  * the #GstClock object.
128  */
129 void
gst_audio_clock_reset(GstAudioClock * clock,GstClockTime time)130 gst_audio_clock_reset (GstAudioClock * clock, GstClockTime time)
131 {
132   GstClockTimeDiff time_offset;
133 
134   if (clock->last_time >= time)
135     time_offset = clock->last_time - time;
136   else
137     time_offset = -(time - clock->last_time);
138 
139   clock->time_offset = time_offset;
140 
141   GST_DEBUG_OBJECT (clock,
142       "reset clock to %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT
143       ", offset %" GST_STIME_FORMAT, GST_TIME_ARGS (time),
144       GST_TIME_ARGS (clock->last_time), GST_STIME_ARGS (time_offset));
145 }
146 
147 static GstClockTime
gst_audio_clock_func_invalid(GstClock * clock,gpointer user_data)148 gst_audio_clock_func_invalid (GstClock * clock, gpointer user_data)
149 {
150   return GST_CLOCK_TIME_NONE;
151 }
152 
153 static GstClockTime
gst_audio_clock_get_internal_time(GstClock * clock)154 gst_audio_clock_get_internal_time (GstClock * clock)
155 {
156   GstAudioClock *aclock;
157   GstClockTime result;
158 
159   aclock = GST_AUDIO_CLOCK_CAST (clock);
160 
161   result = aclock->func (clock, aclock->user_data);
162   if (result == GST_CLOCK_TIME_NONE) {
163     result = aclock->last_time;
164   } else {
165     result += aclock->time_offset;
166     /* clock must be increasing */
167     if (aclock->last_time < result)
168       aclock->last_time = result;
169     else
170       result = aclock->last_time;
171   }
172 
173   GST_DEBUG_OBJECT (clock,
174       "result %" GST_TIME_FORMAT ", last_time %" GST_TIME_FORMAT,
175       GST_TIME_ARGS (result), GST_TIME_ARGS (aclock->last_time));
176 
177   return result;
178 }
179 
180 /**
181  * gst_audio_clock_get_time:
182  * @clock: a #GstAudioClock
183  *
184  * Report the time as returned by the #GstAudioClockGetTimeFunc without applying
185  * any offsets.
186  *
187  * Returns: the time as reported by the time function of the audio clock
188  */
189 GstClockTime
gst_audio_clock_get_time(GstAudioClock * clock)190 gst_audio_clock_get_time (GstAudioClock * clock)
191 {
192   GstClockTime result;
193 
194   result = clock->func (GST_CLOCK_CAST (clock), clock->user_data);
195   if (result == GST_CLOCK_TIME_NONE) {
196     GST_DEBUG_OBJECT (clock, "no time, reuse last");
197     result = clock->last_time - clock->time_offset;
198   }
199 
200   GST_DEBUG_OBJECT (clock,
201       "result %" GST_TIME_FORMAT ", last_time %" GST_TIME_FORMAT,
202       GST_TIME_ARGS (result), GST_TIME_ARGS (clock->last_time));
203 
204   return result;
205 }
206 
207 /**
208  * gst_audio_clock_adjust:
209  * @clock: a #GstAudioClock
210  * @time: a #GstClockTime
211  *
212  * Adjust @time with the internal offset of the audio clock.
213  *
214  * Returns: @time adjusted with the internal offset.
215  */
216 GstClockTime
gst_audio_clock_adjust(GstAudioClock * clock,GstClockTime time)217 gst_audio_clock_adjust (GstAudioClock * clock, GstClockTime time)
218 {
219   GstClockTime result;
220 
221   result = time + clock->time_offset;
222 
223   return result;
224 }
225 
226 /**
227  * gst_audio_clock_invalidate:
228  * @clock: a #GstAudioClock
229  *
230  * Invalidate the clock function. Call this function when the provided
231  * #GstAudioClockGetTimeFunc cannot be called anymore, for example, when the
232  * user_data becomes invalid.
233  *
234  * After calling this function, @clock will return the last returned time for
235  * the rest of its lifetime.
236  */
237 void
gst_audio_clock_invalidate(GstAudioClock * clock)238 gst_audio_clock_invalidate (GstAudioClock * clock)
239 {
240   clock->func = gst_audio_clock_func_invalid;
241 }
242