summaryrefslogtreecommitdiff
path: root/pjmedia/src
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-04-20 11:10:09 +0000
committerBenny Prijono <bennylp@teluu.com>2006-04-20 11:10:09 +0000
commit9bb036e2aea4f32a394b14b85d9b83bdd081b254 (patch)
treec8ae3627bece811cc2d49832acda5475a8b5dfee /pjmedia/src
parentf57a202e450a6477dffdfd85765c31a6e8ca9270 (diff)
Added implementation of media clock and master port (to control frame timing of clock-less ports such as file reader/player)
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@404 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia/clock_thread.c247
-rw-r--r--pjmedia/src/pjmedia/master_port.c194
2 files changed, 441 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/clock_thread.c b/pjmedia/src/pjmedia/clock_thread.c
new file mode 100644
index 00000000..dad7447a
--- /dev/null
+++ b/pjmedia/src/pjmedia/clock_thread.c
@@ -0,0 +1,247 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/clock.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+
+
+/*
+ * Implementation of media clock with OS thread.
+ */
+
+
+struct pjmedia_clock
+{
+ pj_timestamp freq;
+ pj_timestamp interval;
+ pj_timestamp next_tick;
+ pj_timestamp timestamp;
+ unsigned samples_per_frame;
+ unsigned options;
+ pjmedia_clock_callback *cb;
+ void *user_data;
+ pj_thread_t *thread;
+ pj_bool_t running;
+ pj_bool_t quitting;
+};
+
+
+static int clock_thread(void *arg);
+
+
+/*
+ * Create media clock.
+ */
+PJ_DEF(pj_status_t) pjmedia_clock_create( pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned samples_per_frame,
+ unsigned options,
+ pjmedia_clock_callback *cb,
+ void *user_data,
+ pjmedia_clock **p_clock)
+{
+ pjmedia_clock *clock;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && clock_rate && samples_per_frame && p_clock,
+ PJ_EINVAL);
+
+ clock = pj_pool_alloc(pool, sizeof(pjmedia_clock));
+
+
+ status = pj_get_timestamp_freq(&clock->freq);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ clock->interval.u64 = samples_per_frame * clock->freq.u64 / clock_rate;
+ clock->next_tick.u64 = 0;
+ clock->timestamp.u64 = 0;
+ clock->samples_per_frame = samples_per_frame;
+ clock->options = options;
+ clock->cb = cb;
+ clock->user_data = user_data;
+ clock->thread = NULL;
+ clock->running = PJ_FALSE;
+ clock->quitting = PJ_FALSE;
+
+ status = pj_thread_create(pool, "clock", &clock_thread, clock,
+ 0, 0, &clock->thread);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ *p_clock = clock;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Start the clock.
+ */
+PJ_DEF(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock)
+{
+ pj_timestamp now;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
+
+ status = pj_get_timestamp(&now);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ clock->next_tick.u64 = now.u64 + clock->interval.u64;
+ clock->running = PJ_TRUE;
+
+ return status;
+}
+
+
+/*
+ * Stop the clock.
+ */
+PJ_DEF(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock)
+{
+ PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
+
+ clock->running = PJ_FALSE;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Poll the clock.
+ */
+PJ_DEF(pj_bool_t) pjmedia_clock_wait( pjmedia_clock *clock,
+ pj_bool_t wait,
+ pj_timestamp *ts)
+{
+ pj_timestamp now;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(clock != NULL, PJ_FALSE);
+ PJ_ASSERT_RETURN((clock->options & PJMEDIA_CLOCK_NO_ASYNC) != 0,
+ PJ_FALSE);
+ PJ_ASSERT_RETURN(clock->running, PJ_FALSE);
+
+ status = pj_get_timestamp(&now);
+ if (status != PJ_SUCCESS)
+ return PJ_FALSE;
+
+ /* Wait for the next tick to happen */
+ if (now.u64 < clock->next_tick.u64) {
+ unsigned msec;
+
+ if (!wait)
+ return PJ_FALSE;
+
+ msec = pj_elapsed_msec(&now, &clock->next_tick);
+ pj_thread_sleep(msec);
+ }
+
+ /* Call callback, if any */
+ if (clock->cb)
+ (*clock->cb)(&clock->timestamp, clock->user_data);
+
+ /* Report timestamp to caller */
+ if (ts)
+ ts->u64 = clock->timestamp.u64;
+
+ /* Increment timestamp */
+ clock->timestamp.u64 += clock->samples_per_frame;
+
+ /* Calculate next tick */
+ clock->next_tick.u64 += clock->interval.u64;
+
+ /* Done */
+ return PJ_TRUE;
+}
+
+
+/*
+ * Clock thread
+ */
+static int clock_thread(void *arg)
+{
+ pj_timestamp now;
+ pjmedia_clock *clock = arg;
+
+ /* Get the first tick */
+ pj_get_timestamp(&clock->next_tick);
+ clock->next_tick.u64 += clock->interval.u64;
+
+
+ while (!clock->quitting) {
+
+ pj_get_timestamp(&now);
+
+ /* Wait for the next tick to happen */
+ if (now.u64 < clock->next_tick.u64) {
+ unsigned msec;
+ msec = pj_elapsed_msec(&now, &clock->next_tick);
+ pj_thread_sleep(msec);
+ }
+
+ /* Skip if not running */
+ if (!clock->running)
+ continue;
+
+ /* Call callback, if any */
+ if (clock->cb)
+ (*clock->cb)(&clock->timestamp, clock->user_data);
+
+ /* Increment timestamp */
+ clock->timestamp.u64 += clock->samples_per_frame;
+
+ /* Calculate next tick */
+ clock->next_tick.u64 += clock->interval.u64;
+
+
+ }
+
+ return 0;
+}
+
+
+/*
+ * Destroy the clock.
+ */
+PJ_DEF(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock)
+{
+ PJ_ASSERT_RETURN(clock != NULL, PJ_EINVAL);
+
+ clock->running = PJ_FALSE;
+ clock->quitting = PJ_TRUE;
+
+ if (clock->thread) {
+ pj_thread_join(clock->thread);
+ pj_thread_destroy(clock->thread);
+ clock->thread = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+
+
+
diff --git a/pjmedia/src/pjmedia/master_port.c b/pjmedia/src/pjmedia/master_port.c
new file mode 100644
index 00000000..0e67b846
--- /dev/null
+++ b/pjmedia/src/pjmedia/master_port.c
@@ -0,0 +1,194 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/master_port.h>
+#include <pjmedia/clock.h>
+#include <pjmedia/errno.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+struct pjmedia_master_port
+{
+ unsigned options;
+ pjmedia_clock *clock;
+ pjmedia_port *u_port;
+ pjmedia_port *d_port;
+ unsigned buff_size;
+ void *buff;
+};
+
+
+static void clock_callback(const pj_timestamp *ts, void *user_data);
+
+
+/*
+ * Create a master port.
+ *
+ */
+PJ_DEF(pj_status_t) pjmedia_master_port_create( pj_pool_t *pool,
+ pjmedia_port *u_port,
+ pjmedia_port *d_port,
+ unsigned options,
+ pjmedia_master_port **p_m)
+{
+ pjmedia_master_port *m;
+ unsigned clock_rate;
+ unsigned samples_per_frame;
+ unsigned bytes_per_frame;
+ pj_status_t status;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(pool && u_port && d_port && p_m, PJ_EINVAL);
+
+
+ /* Both ports MUST have the equal ptime */
+ PJ_ASSERT_RETURN(u_port->info.sample_rate/u_port->info.samples_per_frame==
+ d_port->info.sample_rate/d_port->info.samples_per_frame,
+ PJMEDIA_ENCSAMPLESPFRAME);
+
+
+ /* Get clock_rate and samples_per_frame from one of the port. */
+ clock_rate = u_port->info.sample_rate;
+ samples_per_frame = u_port->info.samples_per_frame;
+
+
+ /* Get the bytes_per_frame value, to determine the size of the
+ * buffer. We take the larger size of the two ports.
+ */
+ bytes_per_frame = u_port->info.bytes_per_frame;
+ if (d_port->info.bytes_per_frame > bytes_per_frame)
+ bytes_per_frame = d_port->info.bytes_per_frame;
+
+
+ /* Create the master port instance */
+ m = pj_pool_zalloc(pool, sizeof(pjmedia_master_port));
+ m->options = options;
+ m->u_port = u_port;
+ m->d_port = d_port;
+
+
+ /* Create buffer */
+ m->buff_size = bytes_per_frame;
+ m->buff = pj_pool_alloc(pool, m->buff_size);
+ if (!m->buff)
+ return PJ_ENOMEM;
+
+
+ /* Create media clock */
+ status = pjmedia_clock_create(pool, clock_rate, samples_per_frame, 0,
+ &clock_callback, m, &m->clock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ /* Done */
+ *p_m = m;
+
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Start the media flow.
+ */
+PJ_DEF(pj_status_t) pjmedia_master_port_start(pjmedia_master_port *m)
+{
+ PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL);
+ PJ_ASSERT_RETURN(m->u_port && m->d_port, PJ_EINVALIDOP);
+
+ return pjmedia_clock_start(m->clock);
+}
+
+
+
+/*
+ * Stop the media flow.
+ */
+PJ_DEF(pj_status_t) pjmedia_master_port_stop(pjmedia_master_port *m)
+{
+ PJ_ASSERT_RETURN(m && m->clock, PJ_EINVAL);
+
+ return pjmedia_clock_stop(m->clock);
+}
+
+
+/*
+ * Callback to be called for each clock ticks.
+ */
+static void clock_callback(const pj_timestamp *ts, void *user_data)
+{
+ pjmedia_master_port *m = user_data;
+ pjmedia_frame frame;
+ pj_status_t status;
+
+
+ /* Get frame from upstream port and pass it to downstream port */
+ pj_memset(&frame, 0, sizeof(frame));
+ frame.buf = m->buff;
+ frame.size = m->buff_size;
+ frame.timestamp.u64 = ts->u64;
+
+ status = pjmedia_port_get_frame(m->u_port, &frame);
+ if (status != PJ_SUCCESS)
+ frame.type = PJMEDIA_FRAME_TYPE_NONE;
+
+ status = pjmedia_port_put_frame(m->d_port, &frame);
+
+ /* Get frame from downstream port and pass it to upstream port */
+ pj_memset(&frame, 0, sizeof(frame));
+ frame.buf = m->buff;
+ frame.size = m->buff_size;
+ frame.timestamp.u64 = ts->u64;
+
+ status = pjmedia_port_get_frame(m->d_port, &frame);
+ if (status != PJ_SUCCESS)
+ frame.type = PJMEDIA_FRAME_TYPE_NONE;
+
+ status = pjmedia_port_put_frame(m->u_port, &frame);
+}
+
+
+/*
+ * Destroy the master port, and optionally destroy the u_port and
+ * d_port ports.
+ */
+PJ_DEF(pj_status_t) pjmedia_master_port_destroy(pjmedia_master_port *m)
+{
+ PJ_ASSERT_RETURN(m, PJ_EINVAL);
+
+ if (m->clock) {
+ pjmedia_clock_destroy(m->clock);
+ m->clock = NULL;
+ }
+
+ if (m->u_port) {
+ pjmedia_port_destroy(m->u_port);
+ m->u_port = NULL;
+ }
+
+ if (m->d_port) {
+ pjmedia_port_destroy(m->d_port);
+ m->d_port = NULL;
+ }
+
+ return PJ_SUCCESS;
+}
+
+