summaryrefslogtreecommitdiff
path: root/pjmedia
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
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')
-rw-r--r--pjmedia/build/pjmedia.dsp23
-rw-r--r--pjmedia/include/pjmedia.h2
-rw-r--r--pjmedia/include/pjmedia/clock.h143
-rw-r--r--pjmedia/include/pjmedia/master_port.h105
-rw-r--r--pjmedia/src/pjmedia/clock_thread.c247
-rw-r--r--pjmedia/src/pjmedia/master_port.c194
6 files changed, 707 insertions, 7 deletions
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index 519678c0..97a5fce5 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -87,6 +87,10 @@ LIB32=link.exe -lib
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
+SOURCE=..\src\pjmedia\clock_thread.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\codec.c
# End Source File
# Begin Source File
@@ -123,6 +127,10 @@ SOURCE=..\src\pjmedia\jbuf.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\master_port.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\null_port.c
# End Source File
# Begin Source File
@@ -132,13 +140,6 @@ SOURCE=..\src\pjmedia\nullsound.c
# Begin Source File
SOURCE=..\src\pjmedia\pasound.c
-
-!IF "$(CFG)" == "pjmedia - Win32 Release"
-
-!ELSEIF "$(CFG)" == "pjmedia - Win32 Debug"
-
-!ENDIF
-
# End Source File
# Begin Source File
@@ -198,6 +199,10 @@ SOURCE=..\src\pjmedia\wave.c
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
+SOURCE=..\include\pjmedia\clock.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjmedia\codec.h
# End Source File
# Begin Source File
@@ -234,6 +239,10 @@ SOURCE=..\include\pjmedia\jbuf.h
# End Source File
# Begin Source File
+SOURCE=..\include\pjmedia\master_port.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjmedia\null_port.h
# End Source File
# Begin Source File
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 34264b61..f65b2da9 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -25,6 +25,7 @@
*/
#include <pjmedia/types.h>
+#include <pjmedia/clock.h>
#include <pjmedia/codec.h>
#include <pjmedia/conference.h>
#include <pjmedia/endpoint.h>
@@ -32,6 +33,7 @@
#include <pjmedia/file_port.h>
#include <pjmedia/g711.h>
#include <pjmedia/jbuf.h>
+#include <pjmedia/master_port.h>
#include <pjmedia/null_port.h>
#include <pjmedia/port.h>
#include <pjmedia/resample.h>
diff --git a/pjmedia/include/pjmedia/clock.h b/pjmedia/include/pjmedia/clock.h
new file mode 100644
index 00000000..fa15ee6f
--- /dev/null
+++ b/pjmedia/include/pjmedia/clock.h
@@ -0,0 +1,143 @@
+/* $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
+ */
+#ifndef __PJMEDIA_CLOCK_H__
+#define __PJMEDIA_CLOCK_H__
+
+/**
+ * @file clock.h
+ * @brief Media clock.
+ */
+#include <pjmedia/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration for media clock.
+ */
+typedef struct pjmedia_clock pjmedia_clock;
+
+
+enum pjmedia_clock_options
+{
+ PJMEDIA_CLOCK_NO_ASYNC = 1,
+};
+
+/**
+ * Type of media clock callback.
+ *
+ * @param ts Current timestamp, in samples.
+ * @param user_data Application data that is passed when
+ * the clock was created.
+ */
+typedef void pjmedia_clock_callback(const pj_timestamp *ts,
+ void *user_data);
+
+
+
+/**
+ * Create media clock.
+ *
+ * @param pool Pool to allocate memory.
+ * @param clock_rate Number of samples per second.
+ * @param samples_per_frame Number of samples per frame. This argument
+ * along with clock_rate, specifies the interval
+ * of each clock run (or clock ticks).
+ * @param options By default, the callback will be called
+ * asynchronously (depending on the clock
+ * implementation backend, a thread may be
+ * created). If PJMEDIA_CLOCK_NO_ASYNC is set,
+ * application must poll the clock with
+ * #pjmedia_clock_wait() to let the clock runs.
+ * @param cb Callback to be called for each clock tick.
+ * @param user_data User data, which will be passed to the callback.
+ * @param p_clock Pointer to receive the clock instance.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error
+ * code.
+ */
+PJ_DECL(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);
+
+/**
+ * Start the clock. For clock created with asynchronous flag set to TRUE,
+ * this may start a worker thread for the clock (depending on the
+ * backend clock implementation being used).
+ *
+ * @param clock The media clock.
+ *
+ * @return PJ_SUCCES on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_start(pjmedia_clock *clock);
+
+
+/**
+ * Stop the clock.
+ *
+ * @param clock The media clock.
+ *
+ * @return PJ_SUCCES on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_stop(pjmedia_clock *clock);
+
+
+
+/**
+ * Poll the media clock, and execute the callback when the clock tick has
+ * elapsed. This operation is only valid if the clock is created with async
+ * flag set to FALSE.
+ *
+ * @param clock The media clock.
+ * @param wait If non-zero, then the function will block until
+ * a clock tick elapsed and callback has been called.
+ * @param ts Optional argument to receive the current
+ * timestamp.
+ *
+ * @return Non-zero if clock tick has elapsed, or FALSE if
+ * the function returns before a clock tick has
+ * elapsed.
+ */
+PJ_DECL(pj_bool_t) pjmedia_clock_wait(pjmedia_clock *clock,
+ pj_bool_t wait,
+ pj_timestamp *ts);
+
+
+/**
+ * Destroy the clock.
+ *
+ * @param clock The media clock.
+ *
+ * @return PJ_SUCCES on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_clock_destroy(pjmedia_clock *clock);
+
+
+
+PJ_END_DECL
+
+
+
+#endif /* __PJMEDIA_CLOCK_H__ */
+
diff --git a/pjmedia/include/pjmedia/master_port.h b/pjmedia/include/pjmedia/master_port.h
new file mode 100644
index 00000000..44b7db78
--- /dev/null
+++ b/pjmedia/include/pjmedia/master_port.h
@@ -0,0 +1,105 @@
+/* $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
+ */
+#ifndef __PJMEDIA_MASTER_PORT_H__
+#define __PJMEDIA_MASTER_PORT_H__
+
+
+/**
+ * @file master_port.h
+ * @brief Master port.
+ */
+#include <pjmedia/port.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration for master port.
+ * A master port has two media ports connected to it, i.e. downstream and
+ * upstream ports. The media stream flowing to the downstream port is called
+ * encoding or send direction, and media stream flowing to the upstream port
+ * is called decoding or receive direction.
+ *
+ * A master port has a "clock" that periodically passes the media frame from
+ * downstream to upstream ports, and vice versa. In each run, it retrieves
+ * media frame from one side with #pjmedia_port_get_frame(), and passes the
+ * media frame to the other side with #pjmedia_port_put_frame(). In each run,
+ * this process is done for twice, i.e. one for each direction.
+ */
+typedef struct pjmedia_master_port pjmedia_master_port;
+
+
+/**
+ * Create a master port.
+ *
+ * @param pool Pool to allocate master port from.
+ * @param u_port Upstream port.
+ * @param d_port Downstream port.
+ * @param options Options flags.
+ * @param p_m Pointer to receive the master port instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(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);
+
+
+
+/**
+ * Start the media flow.
+ *
+ * @param m The master port.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_master_port_start(pjmedia_master_port *m);
+
+
+
+/**
+ * Stop the media flow.
+ *
+ * @param m The master port.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_master_port_stop(pjmedia_master_port *m);
+
+
+/**
+ * Destroy the master port, and optionally destroy the upstream and
+ * downstream ports.
+ *
+ * @param m The master port.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_master_port_destroy(pjmedia_master_port *m);
+
+
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_MASTER_PORT_H__ */
+
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;
+}
+
+