1
/* $Id: clock_thread.c 4160 2012-06-07 04:10:22Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20
#include <pjmedia/clock.h>
21
#include <pjmedia/errno.h>
22
#include <pj/assert.h>
26
#include <pj/string.h>
27
#include <pj/compat/high_precision.h>
29
/* API: Init clock source */
30
PJ_DEF(pj_status_t) pjmedia_clock_src_init( pjmedia_clock_src *clocksrc,
31
pjmedia_type media_type,
35
PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
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);
46
/* API: Update clock source */
47
PJ_DECL(pj_status_t) pjmedia_clock_src_update( pjmedia_clock_src *clocksrc,
48
const pj_timestamp *timestamp )
50
PJ_ASSERT_RETURN(clocksrc, PJ_EINVAL);
53
pj_memcpy(&clocksrc->timestamp, timestamp, sizeof(pj_timestamp));
54
pj_get_timestamp(&clocksrc->last_update);
59
/* API: Get clock source's current timestamp */
61
pjmedia_clock_src_get_current_timestamp( const pjmedia_clock_src *clocksrc,
62
pj_timestamp *timestamp)
67
PJ_ASSERT_RETURN(clocksrc && timestamp, PJ_EINVAL);
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);
77
/* API: Get clock source's time (in ms) */
79
pjmedia_clock_src_get_time_msec( const pjmedia_clock_src *clocksrc )
83
pjmedia_clock_src_get_current_timestamp(clocksrc, &ts);
86
if (ts.u64 > PJ_UINT64(0x3FFFFFFFFFFFFF))
87
return (pj_uint32_t)(ts.u64 / clocksrc->clock_rate * 1000);
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);
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 *
99
return (pj_uint32_t)(0xFFFFFFFFUL / clocksrc->clock_rate * ts.u32.hi
100
* 1000UL + ts.u32.lo * 1000UL /
101
clocksrc->clock_rate);
107
* Implementation of media clock with OS thread.
114
pj_timestamp interval;
115
pj_timestamp next_tick;
116
pj_timestamp timestamp;
117
unsigned timestamp_inc;
119
pj_uint64_t max_jump;
120
pjmedia_clock_callback *cb;
129
static int clock_thread(void *arg);
131
#define MAX_JUMP_MSEC 500
132
#define USEC_IN_SEC (pj_uint64_t)1000000
135
* Create media clock.
137
PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool,
139
unsigned channel_count,
140
unsigned samples_per_frame,
142
pjmedia_clock_callback *cb,
144
pjmedia_clock **p_clock)
146
pjmedia_clock_param param;
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, ¶m, options, cb,
155
PJ_DEF(pj_status_t) pjmedia_clock_create2(pj_pool_t *pool,
156
const pjmedia_clock_param *param,
158
pjmedia_clock_callback *cb,
160
pjmedia_clock **p_clock)
162
pjmedia_clock *clock;
165
PJ_ASSERT_RETURN(pool && param->usec_interval && param->clock_rate &&
168
clock = PJ_POOL_ALLOC_T(pool, pjmedia_clock);
169
clock->pool = pj_pool_create(pool->factory, "clock%p", 512, 512, NULL);
171
status = pj_get_timestamp_freq(&clock->freq);
172
if (status != PJ_SUCCESS)
175
clock->interval.u64 = param->usec_interval * clock->freq.u64 /
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 *
183
clock->options = options;
185
clock->user_data = user_data;
186
clock->thread = NULL;
187
clock->running = PJ_FALSE;
188
clock->quitting = PJ_FALSE;
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)
204
PJ_DEF(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock)
209
PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
214
status = pj_get_timestamp(&now);
215
if (status != PJ_SUCCESS)
218
clock->next_tick.u64 = now.u64 + clock->interval.u64;
219
clock->running = PJ_TRUE;
220
clock->quitting = PJ_FALSE;
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;
238
PJ_DEF(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock)
240
PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
242
clock->running = PJ_FALSE;
243
clock->quitting = PJ_TRUE;
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);
251
clock->quitting = PJ_FALSE;
262
PJ_DEF(pj_status_t) pjmedia_clock_modify(pjmedia_clock *clock,
263
const pjmedia_clock_param *param)
265
clock->interval.u64 = param->usec_interval * clock->freq.u64 /
267
clock->timestamp_inc = (unsigned)(param->usec_interval *
275
/* Calculate next tick */
276
PJ_INLINE(void) clock_calc_next_tick(pjmedia_clock *clock,
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;
283
clock->next_tick.u64 += clock->interval.u64;
290
PJ_DEF(pj_bool_t) pjmedia_clock_wait( pjmedia_clock *clock,
297
PJ_ASSERT_RETURN(clock != NULL, PJ_FALSE);
298
PJ_ASSERT_RETURN((clock->options & PJMEDIA_CLOCK_NO_ASYNC) != 0,
300
PJ_ASSERT_RETURN(clock->running, PJ_FALSE);
302
status = pj_get_timestamp(&now);
303
if (status != PJ_SUCCESS)
306
/* Wait for the next tick to happen */
307
if (now.u64 < clock->next_tick.u64) {
313
msec = pj_elapsed_msec(&now, &clock->next_tick);
314
pj_thread_sleep(msec);
317
/* Call callback, if any */
319
(*clock->cb)(&clock->timestamp, clock->user_data);
321
/* Report timestamp to caller */
323
ts->u64 = clock->timestamp.u64;
325
/* Increment timestamp */
326
clock->timestamp.u64 += clock->timestamp_inc;
328
/* Calculate next tick */
329
clock_calc_next_tick(clock, &now);
339
static int clock_thread(void *arg)
342
pjmedia_clock *clock = (pjmedia_clock*) arg;
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());
348
pj_thread_set_prio(pj_thread_this(), max);
351
/* Get the first tick */
352
pj_get_timestamp(&clock->next_tick);
353
clock->next_tick.u64 += clock->interval.u64;
356
while (!clock->quitting) {
358
pj_get_timestamp(&now);
360
/* Wait for the next tick to happen */
361
if (now.u64 < clock->next_tick.u64) {
363
msec = pj_elapsed_msec(&now, &clock->next_tick);
364
pj_thread_sleep(msec);
367
/* Skip if not running */
368
if (!clock->running) {
369
/* Calculate next tick */
370
clock_calc_next_tick(clock, &now);
374
pj_lock_acquire(clock->lock);
376
/* Call callback, if any */
378
(*clock->cb)(&clock->timestamp, clock->user_data);
380
/* Best effort way to detect if we've been destroyed in the callback */
384
/* Increment timestamp */
385
clock->timestamp.u64 += clock->timestamp_inc;
387
/* Calculate next tick */
388
clock_calc_next_tick(clock, &now);
390
pj_lock_release(clock->lock);
400
PJ_DEF(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock)
402
PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
404
clock->running = PJ_FALSE;
405
clock->quitting = PJ_TRUE;
408
pj_thread_join(clock->thread);
409
pj_thread_destroy(clock->thread);
410
clock->thread = NULL;
414
pj_lock_destroy(clock->lock);
419
pj_pool_t *pool = clock->pool;
421
pj_pool_release(pool);