summaryrefslogtreecommitdiff
path: root/pjmedia/src/pjmedia/transport_loop.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia/src/pjmedia/transport_loop.c')
-rw-r--r--pjmedia/src/pjmedia/transport_loop.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/pjmedia/src/pjmedia/transport_loop.c b/pjmedia/src/pjmedia/transport_loop.c
new file mode 100644
index 0000000..c8a2c34
--- /dev/null
+++ b/pjmedia/src/pjmedia/transport_loop.c
@@ -0,0 +1,404 @@
+/* $Id: transport_loop.c 3553 2011-05-05 06:14:19Z nanang $ */
+/*
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 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/transport_loop.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+
+
+struct user
+{
+ pj_bool_t rx_disabled; /**< Doesn't want to receive pkt? */
+ void *user_data; /**< Only valid when attached */
+ void (*rtp_cb)( void*, /**< To report incoming RTP. */
+ void*,
+ pj_ssize_t);
+ void (*rtcp_cb)( void*, /**< To report incoming RTCP. */
+ void*,
+ pj_ssize_t);
+};
+
+struct transport_loop
+{
+ pjmedia_transport base; /**< Base transport. */
+
+ pj_pool_t *pool; /**< Memory pool */
+ unsigned user_cnt; /**< Number of attachments */
+ struct user users[4]; /**< Array of users. */
+
+ unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
+ unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
+
+};
+
+
+
+/*
+ * These are media transport operations.
+ */
+static pj_status_t transport_get_info (pjmedia_transport *tp,
+ pjmedia_transport_info *info);
+static pj_status_t transport_attach (pjmedia_transport *tp,
+ void *user_data,
+ const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
+ unsigned addr_len,
+ void (*rtp_cb)(void*,
+ void*,
+ pj_ssize_t),
+ void (*rtcp_cb)(void*,
+ void*,
+ pj_ssize_t));
+static void transport_detach (pjmedia_transport *tp,
+ void *strm);
+static pj_status_t transport_send_rtp( pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size);
+static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size);
+static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ const void *pkt,
+ pj_size_t size);
+static pj_status_t transport_media_create(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ unsigned options,
+ const pjmedia_sdp_session *sdp_remote,
+ unsigned media_index);
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ pjmedia_sdp_session *sdp_local,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index);
+static pj_status_t transport_media_start (pjmedia_transport *tp,
+ pj_pool_t *pool,
+ const pjmedia_sdp_session *sdp_local,
+ const pjmedia_sdp_session *sdp_remote,
+ unsigned media_index);
+static pj_status_t transport_media_stop(pjmedia_transport *tp);
+static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
+ pjmedia_dir dir,
+ unsigned pct_lost);
+static pj_status_t transport_destroy (pjmedia_transport *tp);
+
+
+static pjmedia_transport_op transport_udp_op =
+{
+ &transport_get_info,
+ &transport_attach,
+ &transport_detach,
+ &transport_send_rtp,
+ &transport_send_rtcp,
+ &transport_send_rtcp2,
+ &transport_media_create,
+ &transport_encode_sdp,
+ &transport_media_start,
+ &transport_media_stop,
+ &transport_simulate_lost,
+ &transport_destroy
+};
+
+
+/**
+ * Create loopback transport.
+ */
+PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt,
+ pjmedia_transport **p_tp)
+{
+ struct transport_loop *tp;
+ pj_pool_t *pool;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL);
+
+ /* Create transport structure */
+ pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop);
+ tp->pool = pool;
+ pj_ansi_strncpy(tp->base.name, tp->pool->obj_name, PJ_MAX_OBJ_NAME-1);
+ tp->base.op = &transport_udp_op;
+ tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
+
+ /* Done */
+ *p_tp = &tp->base;
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_transport_loop_disable_rx( pjmedia_transport *tp,
+ void *user,
+ pj_bool_t disabled)
+{
+ struct transport_loop *loop = (struct transport_loop*) tp;
+ unsigned i;
+
+ for (i=0; i<loop->user_cnt; ++i) {
+ if (loop->users[i].user_data == user) {
+ loop->users[i].rx_disabled = disabled;
+ return PJ_SUCCESS;
+ }
+ }
+ pj_assert(!"Invalid stream user");
+ return PJ_ENOTFOUND;
+}
+
+/**
+ * Close loopback transport.
+ */
+static pj_status_t transport_destroy(pjmedia_transport *tp)
+{
+ struct transport_loop *loop = (struct transport_loop*) tp;
+
+ /* Sanity check */
+ PJ_ASSERT_RETURN(tp, PJ_EINVAL);
+
+ pj_pool_release(loop->pool);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Called to get the transport info */
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+ pjmedia_transport_info *info)
+{
+ PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
+
+ info->sock_info.rtp_sock = 1;
+ pj_sockaddr_in_init(&info->sock_info.rtp_addr_name.ipv4, 0, 0);
+ info->sock_info.rtcp_sock = 2;
+ pj_sockaddr_in_init(&info->sock_info.rtcp_addr_name.ipv4, 0, 0);
+
+ return PJ_SUCCESS;
+}
+
+
+/* Called by application to initialize the transport */
+static pj_status_t transport_attach( pjmedia_transport *tp,
+ void *user_data,
+ const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
+ unsigned addr_len,
+ void (*rtp_cb)(void*,
+ void*,
+ pj_ssize_t),
+ void (*rtcp_cb)(void*,
+ void*,
+ pj_ssize_t))
+{
+ struct transport_loop *loop = (struct transport_loop*) tp;
+ unsigned i;
+ const pj_sockaddr *rtcp_addr;
+
+ /* Validate arguments */
+ PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
+
+ /* Must not be "attached" to same user */
+ for (i=0; i<loop->user_cnt; ++i) {
+ PJ_ASSERT_RETURN(loop->users[i].user_data != user_data,
+ PJ_EINVALIDOP);
+ }
+ PJ_ASSERT_RETURN(loop->user_cnt != PJ_ARRAY_SIZE(loop->users),
+ PJ_ETOOMANY);
+
+ PJ_UNUSED_ARG(rem_rtcp);
+ PJ_UNUSED_ARG(rtcp_addr);
+
+ /* "Attach" the application: */
+
+ /* Save the new user */
+ loop->users[loop->user_cnt].rtp_cb = rtp_cb;
+ loop->users[loop->user_cnt].rtcp_cb = rtcp_cb;
+ loop->users[loop->user_cnt].user_data = user_data;
+ ++loop->user_cnt;
+
+ return PJ_SUCCESS;
+}
+
+
+/* Called by application when it no longer needs the transport */
+static void transport_detach( pjmedia_transport *tp,
+ void *user_data)
+{
+ struct transport_loop *loop = (struct transport_loop*) tp;
+ unsigned i;
+
+ pj_assert(tp);
+
+ for (i=0; i<loop->user_cnt; ++i) {
+ if (loop->users[i].user_data == user_data)
+ break;
+ }
+
+ /* Remove this user */
+ if (i != loop->user_cnt) {
+ pj_array_erase(loop->users, sizeof(loop->users[0]),
+ loop->user_cnt, i);
+ --loop->user_cnt;
+ }
+}
+
+
+/* Called by application to send RTP packet */
+static pj_status_t transport_send_rtp( pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size)
+{
+ struct transport_loop *loop = (struct transport_loop*)tp;
+ unsigned i;
+
+ /* Simulate packet lost on TX direction */
+ if (loop->tx_drop_pct) {
+ if ((pj_rand() % 100) <= (int)loop->tx_drop_pct) {
+ PJ_LOG(5,(loop->base.name,
+ "TX RTP packet dropped because of pkt lost "
+ "simulation"));
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Simulate packet lost on RX direction */
+ if (loop->rx_drop_pct) {
+ if ((pj_rand() % 100) <= (int)loop->rx_drop_pct) {
+ PJ_LOG(5,(loop->base.name,
+ "RX RTP packet dropped because of pkt lost "
+ "simulation"));
+ return PJ_SUCCESS;
+ }
+ }
+
+ /* Distribute to users */
+ for (i=0; i<loop->user_cnt; ++i) {
+ if (!loop->users[i].rx_disabled && loop->users[i].rtp_cb)
+ (*loop->users[i].rtp_cb)(loop->users[i].user_data, (void*)pkt,
+ size);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Called by application to send RTCP packet */
+static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size)
+{
+ return transport_send_rtcp2(tp, NULL, 0, pkt, size);
+}
+
+
+/* Called by application to send RTCP packet */
+static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
+ const pj_sockaddr_t *addr,
+ unsigned addr_len,
+ const void *pkt,
+ pj_size_t size)
+{
+ struct transport_loop *loop = (struct transport_loop*)tp;
+ unsigned i;
+
+ PJ_UNUSED_ARG(addr_len);
+ PJ_UNUSED_ARG(addr);
+
+ /* Distribute to users */
+ for (i=0; i<loop->user_cnt; ++i) {
+ if (!loop->users[i].rx_disabled && loop->users[i].rtcp_cb)
+ (*loop->users[i].rtcp_cb)(loop->users[i].user_data, (void*)pkt,
+ size);
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t transport_media_create(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ unsigned options,
+ const pjmedia_sdp_session *sdp_remote,
+ unsigned media_index)
+{
+ PJ_UNUSED_ARG(tp);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(options);
+ PJ_UNUSED_ARG(sdp_remote);
+ PJ_UNUSED_ARG(media_index);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ pjmedia_sdp_session *sdp_local,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index)
+{
+ PJ_UNUSED_ARG(tp);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(sdp_local);
+ PJ_UNUSED_ARG(rem_sdp);
+ PJ_UNUSED_ARG(media_index);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t transport_media_start(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ const pjmedia_sdp_session *sdp_local,
+ const pjmedia_sdp_session *sdp_remote,
+ unsigned media_index)
+{
+ PJ_UNUSED_ARG(tp);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(sdp_local);
+ PJ_UNUSED_ARG(sdp_remote);
+ PJ_UNUSED_ARG(media_index);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t transport_media_stop(pjmedia_transport *tp)
+{
+ PJ_UNUSED_ARG(tp);
+ return PJ_SUCCESS;
+}
+
+static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
+ pjmedia_dir dir,
+ unsigned pct_lost)
+{
+ struct transport_loop *loop = (struct transport_loop*)tp;
+
+ PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL);
+
+ if (dir & PJMEDIA_DIR_ENCODING)
+ loop->tx_drop_pct = pct_lost;
+
+ if (dir & PJMEDIA_DIR_DECODING)
+ loop->rx_drop_pct = pct_lost;
+
+ return PJ_SUCCESS;
+}
+