1 /* $Id$ */
2 /*
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <pjmedia/clock.h>
21 #include <pjmedia/errno.h>
22 #include <pj/assert.h>
23 #include <pj/lock.h>
24 #include <pj/os.h>
25 #include <pj/pool.h>
26 #include <pj/string.h>
27 #include <pj/compat/high_precision.h>
28 
29 /* API: Init clock source */
pjmedia_clock_src_init(pjmedia_clock_src * clocksrc,pjmedia_type media_type,unsigned clock_rate,unsigned ptime_usec)30 PJ_DEF(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc,
31                                             pjmedia_type media_type,
32                                             unsigned clock_rate,
33                                             unsigned ptime_usec )
34 {
35     PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
36 
37     clocksrc->media_type = media_type;
38     clocksrc->clock_rate = clock_rate;
39     clocksrc->ptime_usec = ptime_usec;
40     pj_set_timestamp32(&clocksrc->timestamp, 0, 0);
41     pj_get_timestamp(&clocksrc->last_update);
42 
43     return PJ_SUCCESS;
44 }
45 
46 /* API: Update clock source */
pjmedia_clock_src_update(pjmedia_clock_src * clocksrc,const pj_timestamp * timestamp)47 PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc,
48                                                const pj_timestamp *timestamp )
49 {
50     PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
51 
52     if (timestamp)
53         pj_memcpy(&clocksrc->timestamp, timestamp, sizeof(pj_timestamp));
54     pj_get_timestamp(&clocksrc->last_update);
55 
56     return PJ_SUCCESS;
57 }
58 
59 /* API: Get clock source's current timestamp */
60 PJ_DEF(pj_status_t)
pjmedia_clock_src_get_current_timestamp(const pjmedia_clock_src * clocksrc,pj_timestamp * timestamp)61 pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc,
62                                          pj_timestamp *timestamp)
63 {
64     pj_timestamp now;
65     unsigned elapsed_ms;
66 
67     PJ_ASSERT_RETURN(clocksrc && timestamp, PJ_EINVAL);
68 
69     pj_get_timestamp(&now);
70     elapsed_ms = pj_elapsed_msec(&clocksrc->last_update, &now);
71     pj_memcpy(timestamp, &clocksrc->timestamp, sizeof(pj_timestamp));
72     pj_add_timestamp32(timestamp, elapsed_ms * clocksrc->clock_rate / 1000);
73 
74     return PJ_SUCCESS;
75 }
76 
77 /* API: Get clock source's time (in ms) */
78 PJ_DEF(pj_uint32_t)
pjmedia_clock_src_get_time_msec(const pjmedia_clock_src * clocksrc)79 pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc )
80 {
81     pj_timestamp ts;
82 
83     pjmedia_clock_src_get_current_timestamp(clocksrc, &ts);
84 
85 #if PJ_HAS_INT64
86     if (ts.u64 > PJ_UINT64(0x3FFFFFFFFFFFFF))
87         return (pj_uint32_t)(ts.u64 / clocksrc->clock_rate * 1000);
88     else
89         return (pj_uint32_t)(ts.u64 * 1000 / clocksrc->clock_rate);
90 #elif PJ_HAS_FLOATING_POINT
91     return (pj_uint32_t)((1.0 * ts.u32.hi * 0xFFFFFFFFUL + ts.u32.lo)
92                          * 1000.0 / clocksrc->clock_rate);
93 #else
94     if (ts.u32.lo > 0x3FFFFFUL)
95         return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi
96                              * 1000UL + ts.u32.lo / clocksrc->clock_rate *
97                              1000UL);
98     else
99         return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi
100                              * 1000UL + ts.u32.lo * 1000UL /
101                              clocksrc->clock_rate);
102 #endif
103 }
104 
105 
106 /*
107  * Implementation of media clock with OS thread.
108  */
109 
110 struct pjmedia_clock
111 {
112     pj_pool_t		    *pool;
113     pj_timestamp	     freq;
114     pj_timestamp	     interval;
115     pj_timestamp	     next_tick;
116     pj_timestamp	     timestamp;
117     unsigned		     timestamp_inc;
118     unsigned		     options;
119     pj_uint64_t		     max_jump;
120     pjmedia_clock_callback  *cb;
121     void		    *user_data;
122     pj_thread_t		    *thread;
123     pj_bool_t		     running;
124     pj_bool_t		     quitting;
125     pj_lock_t		    *lock;
126 };
127 
128 
129 static int clock_thread(void *arg);
130 
131 #define MAX_JUMP_MSEC	500
132 #define USEC_IN_SEC	(pj_uint64_t)1000000
133 
134 /*
135  * Create media clock.
136  */
pjmedia_clock_create(pj_pool_t * pool,unsigned clock_rate,unsigned channel_count,unsigned samples_per_frame,unsigned options,pjmedia_clock_callback * cb,void * user_data,pjmedia_clock ** p_clock)137 PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool,
138 					  unsigned clock_rate,
139 					  unsigned channel_count,
140 					  unsigned samples_per_frame,
141 					  unsigned options,
142 					  pjmedia_clock_callback *cb,
143 					  void *user_data,
144 					  pjmedia_clock **p_clock)
145 {
146     pjmedia_clock_param param;
147 
148     param.usec_interval = (unsigned)(samples_per_frame * USEC_IN_SEC /
149 			             channel_count / clock_rate);
150     param.clock_rate = clock_rate;
151     return pjmedia_clock_create2(pool, &param, options, cb,
152                                  user_data, p_clock);
153 }
154 
pjmedia_clock_create2(pj_pool_t * pool,const pjmedia_clock_param * param,unsigned options,pjmedia_clock_callback * cb,void * user_data,pjmedia_clock ** p_clock)155 PJ_DEF(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
156                                           const pjmedia_clock_param *param,
157 				          unsigned options,
158 				          pjmedia_clock_callback *cb,
159 				          void *user_data,
160 				          pjmedia_clock **p_clock)
161 {
162     pjmedia_clock *clock;
163     pj_status_t status;
164 
165     PJ_ASSERT_RETURN(pool && param->usec_interval && param->clock_rate &&
166                      p_clock, PJ_EINVAL);
167 
168     clock = PJ_POOL_ALLOC_T(pool, pjmedia_clock);
169     clock->pool = pj_pool_create(pool->factory, "clock%p", 512, 512, NULL);
170 
171     status = pj_get_timestamp_freq(&clock->freq);
172     if (status != PJ_SUCCESS)
173 	return status;
174 
175     clock->interval.u64 = param->usec_interval * clock->freq.u64 /
176                           USEC_IN_SEC;
177     clock->next_tick.u64 = 0;
178     clock->timestamp.u64 = 0;
179     clock->max_jump = MAX_JUMP_MSEC * clock->freq.u64 / 1000;
180     clock->timestamp_inc = (unsigned)(param->usec_interval *
181                                       param->clock_rate /
182 				      (unsigned)USEC_IN_SEC);
183     clock->options = options;
184     clock->cb = cb;
185     clock->user_data = user_data;
186     clock->thread = NULL;
187     clock->running = PJ_FALSE;
188     clock->quitting = PJ_FALSE;
189 
190     /* I don't think we need a mutex, so we'll use null. */
191     status = pj_lock_create_null_mutex(pool, "clock", &clock->lock);
192     if (status != PJ_SUCCESS)
193 	return status;
194 
195     *p_clock = clock;
196 
197     return PJ_SUCCESS;
198 }
199 
200 
201 /*
202  * Start the clock.
203  */
pjmedia_clock_start(pjmedia_clock * clock)204 PJ_DEF(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock)
205 {
206     pj_timestamp now;
207     pj_status_t status;
208 
209     PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
210 
211     if (clock->running)
212 	return PJ_SUCCESS;
213 
214     status = pj_get_timestamp(&now);
215     if (status != PJ_SUCCESS)
216 	return status;
217 
218     clock->next_tick.u64 = now.u64 + clock->interval.u64;
219     clock->running = PJ_TRUE;
220     clock->quitting = PJ_FALSE;
221 
222     if ((clock->options & PJMEDIA_CLOCK_NO_ASYNC) == 0 && !clock->thread) {
223 	status = pj_thread_create(clock->pool, "clock", &clock_thread, clock,
224 				  0, 0, &clock->thread);
225 	if (status != PJ_SUCCESS) {
226 	    clock->running = PJ_FALSE;
227 	    return status;
228 	}
229     }
230 
231     return PJ_SUCCESS;
232 }
233 
234 
235 /*
236  * Stop the clock.
237  */
pjmedia_clock_stop(pjmedia_clock * clock)238 PJ_DEF(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock)
239 {
240     PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
241 
242     clock->running = PJ_FALSE;
243     clock->quitting = PJ_TRUE;
244 
245     if (clock->thread) {
246 	if (pj_thread_join(clock->thread) == PJ_SUCCESS) {
247 	    pj_thread_destroy(clock->thread);
248 	    clock->thread = NULL;
249 	    pj_pool_reset(clock->pool);
250 	} else {
251 	    clock->quitting = PJ_FALSE;
252 	}
253     }
254 
255     return PJ_SUCCESS;
256 }
257 
258 
259 /*
260  * Update the clock.
261  */
pjmedia_clock_modify(pjmedia_clock * clock,const pjmedia_clock_param * param)262 PJ_DEF(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock,
263                                          const pjmedia_clock_param *param)
264 {
265     clock->interval.u64 = param->usec_interval * clock->freq.u64 /
266                           USEC_IN_SEC;
267     clock->timestamp_inc = (unsigned)(param->usec_interval *
268                                       param->clock_rate /
269 				      (unsigned)USEC_IN_SEC);
270 
271     return PJ_SUCCESS;
272 }
273 
274 
275 /* Calculate next tick */
clock_calc_next_tick(pjmedia_clock * clock,pj_timestamp * now)276 PJ_INLINE(void) clock_calc_next_tick(pjmedia_clock *clock,
277 				     pj_timestamp *now)
278 {
279     if (clock->next_tick.u64+clock->max_jump < now->u64) {
280 	/* Timestamp has made large jump, adjust next_tick */
281 	clock->next_tick.u64 = now->u64;
282     }
283     clock->next_tick.u64 += clock->interval.u64;
284 
285 }
286 
287 /*
288  * Poll the clock.
289  */
pjmedia_clock_wait(pjmedia_clock * clock,pj_bool_t wait,pj_timestamp * ts)290 PJ_DEF(pj_bool_t) pjmedia_clock_wait( pjmedia_clock *clock,
291 				      pj_bool_t wait,
292 				      pj_timestamp *ts)
293 {
294     pj_timestamp now;
295     pj_status_t status;
296 
297     PJ_ASSERT_RETURN(clock != NULL, PJ_FALSE);
298     PJ_ASSERT_RETURN((clock->options & PJMEDIA_CLOCK_NO_ASYNC) != 0,
299 		     PJ_FALSE);
300     PJ_ASSERT_RETURN(clock->running, PJ_FALSE);
301 
302     status = pj_get_timestamp(&now);
303     if (status != PJ_SUCCESS)
304 	return PJ_FALSE;
305 
306     /* Wait for the next tick to happen */
307     if (now.u64 < clock->next_tick.u64) {
308 	unsigned msec;
309 
310 	if (!wait)
311 	    return PJ_FALSE;
312 
313 	msec = pj_elapsed_msec(&now, &clock->next_tick);
314 	pj_thread_sleep(msec);
315     }
316 
317     /* Call callback, if any */
318     if (clock->cb)
319 	(*clock->cb)(&clock->timestamp, clock->user_data);
320 
321     /* Report timestamp to caller */
322     if (ts)
323 	ts->u64 = clock->timestamp.u64;
324 
325     /* Increment timestamp */
326     clock->timestamp.u64 += clock->timestamp_inc;
327 
328     /* Calculate next tick */
329     clock_calc_next_tick(clock, &now);
330 
331     /* Done */
332     return PJ_TRUE;
333 }
334 
335 
336 /*
337  * Clock thread
338  */
clock_thread(void * arg)339 static int clock_thread(void *arg)
340 {
341     pj_timestamp now;
342     pjmedia_clock *clock = (pjmedia_clock*) arg;
343 
344     /* Set thread priority to maximum unless not wanted. */
345     if ((clock->options & PJMEDIA_CLOCK_NO_HIGHEST_PRIO) == 0) {
346 	int max = pj_thread_get_prio_max(pj_thread_this());
347 	if (max > 0)
348 	    pj_thread_set_prio(pj_thread_this(), max);
349     }
350 
351     /* Get the first tick */
352     pj_get_timestamp(&clock->next_tick);
353     clock->next_tick.u64 += clock->interval.u64;
354 
355 
356     while (!clock->quitting) {
357 
358 	pj_get_timestamp(&now);
359 
360 	/* Wait for the next tick to happen */
361 	if (now.u64 < clock->next_tick.u64) {
362 	    unsigned msec;
363 	    msec = pj_elapsed_msec(&now, &clock->next_tick);
364 	    pj_thread_sleep(msec);
365 	}
366 
367 	/* Skip if not running */
368 	if (!clock->running) {
369 	    /* Calculate next tick */
370 	    clock_calc_next_tick(clock, &now);
371 	    continue;
372 	}
373 
374 	pj_lock_acquire(clock->lock);
375 
376 	/* Call callback, if any */
377 	if (clock->cb)
378 	    (*clock->cb)(&clock->timestamp, clock->user_data);
379 
380 	/* Best effort way to detect if we've been destroyed in the callback */
381 	if (clock->quitting)
382 	    break;
383 
384 	/* Increment timestamp */
385 	clock->timestamp.u64 += clock->timestamp_inc;
386 
387 	/* Calculate next tick */
388 	clock_calc_next_tick(clock, &now);
389 
390 	pj_lock_release(clock->lock);
391     }
392 
393     return 0;
394 }
395 
396 
397 /*
398  * Destroy the clock.
399  */
pjmedia_clock_destroy(pjmedia_clock * clock)400 PJ_DEF(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock)
401 {
402     PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
403 
404     clock->running = PJ_FALSE;
405     clock->quitting = PJ_TRUE;
406 
407     if (clock->thread) {
408 	pj_thread_join(clock->thread);
409 	pj_thread_destroy(clock->thread);
410 	clock->thread = NULL;
411     }
412 
413     if (clock->lock) {
414 	pj_lock_destroy(clock->lock);
415 	clock->lock = NULL;
416     }
417 
418     pj_pool_safe_release(&clock->pool);
419 
420     return PJ_SUCCESS;
421 }
422 
423 
424