summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-09-04 13:55:33 +0000
committerBenny Prijono <bennylp@teluu.com>2008-09-04 13:55:33 +0000
commitac1206e146b5643263054f0bcccf08261558017e (patch)
tree34a32f48911f816c9c24117f92684a7f043696fa
parent03db406b42ed903c524b0d24b092fc2bffa9855a (diff)
Ticket #610: Added sample to create a media transport adapter, similar to how SRTP media transport works
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2262 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--build.symbian/pjmedia.mmp1
-rw-r--r--pjmedia/build/Makefile3
-rw-r--r--pjmedia/build/pjmedia.dsp8
-rw-r--r--pjmedia/build/pjmedia.vcproj8
-rw-r--r--pjmedia/include/pjmedia.h1
-rw-r--r--pjmedia/include/pjmedia/transport.h7
-rw-r--r--pjmedia/include/pjmedia/transport_adapter_sample.h72
-rw-r--r--pjmedia/src/pjmedia/transport_adapter_sample.c433
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c50
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h1
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c41
11 files changed, 619 insertions, 6 deletions
diff --git a/build.symbian/pjmedia.mmp b/build.symbian/pjmedia.mmp
index 60ec6425..ce2d279f 100644
--- a/build.symbian/pjmedia.mmp
+++ b/build.symbian/pjmedia.mmp
@@ -67,6 +67,7 @@ SOURCE splitcomb.c
SOURCE stereo_port.c
SOURCE stream.c
SOURCE tonegen.c
+SOURCE transport_adapter_sample.c
SOURCE transport_ice.c
SOURCE transport_udp.c
SOURCE transport_srtp.c
diff --git a/pjmedia/build/Makefile b/pjmedia/build/Makefile
index 98e5db06..adf0bcfe 100644
--- a/pjmedia/build/Makefile
+++ b/pjmedia/build/Makefile
@@ -57,7 +57,8 @@ export PJMEDIA_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
resample_port.o rtcp.o rtcp_xr.o rtp.o \
sdp.o sdp_cmp.o sdp_neg.o \
session.o silencedet.o sound_port.o stereo_port.o \
- stream.o tonegen.o transport_ice.o transport_loop.o \
+ stream.o tonegen.o transport_adapter_sample.o \
+ transport_ice.o transport_loop.o \
transport_srtp.o transport_udp.o \
wav_player.o wav_playlist.o wav_writer.o wave.o \
wsola.o $(SOUND_OBJS) $(NULLSOUND_OBJS)
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index 5c8a96b1..5e608f29 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -257,6 +257,10 @@ SOURCE=..\src\pjmedia\tonegen.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\transport_adapter_sample.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\transport_ice.c
# End Source File
# Begin Source File
@@ -441,6 +445,10 @@ SOURCE=..\include\pjmedia\transport.h
# End Source File
# Begin Source File
+SOURCE=..\include\pjmedia\transport_adapter_sample.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\pjmedia\transport_ice.h
# End Source File
# Begin Source File
diff --git a/pjmedia/build/pjmedia.vcproj b/pjmedia/build/pjmedia.vcproj
index 5bc900bc..281ec6b7 100644
--- a/pjmedia/build/pjmedia.vcproj
+++ b/pjmedia/build/pjmedia.vcproj
@@ -970,6 +970,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\src\pjmedia\transport_adapter_sample.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pjmedia\transport_ice.c"
>
</File>
@@ -1245,6 +1249,10 @@
>
</File>
<File
+ RelativePath="..\include\pjmedia\transport_adapter_sample.h"
+ >
+ </File>
+ <File
RelativePath="..\include\pjmedia\transport_ice.h"
>
</File>
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index f6179a94..a992a10c 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -58,6 +58,7 @@
#include <pjmedia/stream.h>
#include <pjmedia/tonegen.h>
#include <pjmedia/transport.h>
+#include <pjmedia/transport_adapter_sample.h>
#include <pjmedia/transport_ice.h>
#include <pjmedia/transport_loop.h>
#include <pjmedia/transport_srtp.h>
diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h
index c9c29098..6c5ffde6 100644
--- a/pjmedia/include/pjmedia/transport.h
+++ b/pjmedia/include/pjmedia/transport.h
@@ -427,7 +427,12 @@ typedef enum pjmedia_transport_type
* stacked with other transport to enable encryption on the underlying
* transport.
*/
- PJMEDIA_TRANSPORT_TYPE_SRTP
+ PJMEDIA_TRANSPORT_TYPE_SRTP,
+
+ /**
+ * Start of user defined transport.
+ */
+ PJMEDIA_TRANSPORT_TYPE_USER
} pjmedia_transport_type;
diff --git a/pjmedia/include/pjmedia/transport_adapter_sample.h b/pjmedia/include/pjmedia/transport_adapter_sample.h
new file mode 100644
index 00000000..803ba412
--- /dev/null
+++ b/pjmedia/include/pjmedia/transport_adapter_sample.h
@@ -0,0 +1,72 @@
+/* $Id:$ */
+/*
+ * 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
+ */
+#ifndef __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__
+#define __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__
+
+
+/**
+ * @file transport_adapter_sample.h
+ * @brief Sample Media Transport Adapter
+ */
+
+#include <pjmedia/transport.h>
+
+
+/**
+ * @defgroup PJMEDIA_TRANSPORT_ADAPTER_SAMPLE Sample Transport Adapter
+ * @ingroup PJMEDIA_TRANSPORT
+ * @brief Example on how to create transport adapter.
+ * @{
+ *
+ * This describes a sample implementation of transport adapter, similar to
+ * the way the SRTP transport adapter works.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Create the transport adapter, specifying the underlying transport to be
+ * used to send and receive RTP/RTCP packets.
+ *
+ * @param endpt The media endpoint.
+ * @param name Optional name to identify this media transport
+ * for logging purposes.
+ * @param transport The underlying media transport to send and receive
+ * RTP/RTCP packets.
+ * @param p_tp Pointer to receive the media transport instance.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
+ const char *name,
+ pjmedia_transport *transport,
+ pjmedia_transport **p_tp);
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+
+#endif /* __PJMEDIA_TRANSPORT_ADAPTER_SAMPLE_H__ */
+
+
diff --git a/pjmedia/src/pjmedia/transport_adapter_sample.c b/pjmedia/src/pjmedia/transport_adapter_sample.c
new file mode 100644
index 00000000..b4d4c9da
--- /dev/null
+++ b/pjmedia/src/pjmedia/transport_adapter_sample.c
@@ -0,0 +1,433 @@
+/* $Id:$ */
+/*
+ * 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_adapter_sample.h>
+#include <pjmedia/endpoint.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+
+
+/* Transport functions prototypes */
+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 *sdp_pool,
+ unsigned options,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index);
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+ pj_pool_t *sdp_pool,
+ pjmedia_sdp_session *local_sdp,
+ 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 *local_sdp,
+ const pjmedia_sdp_session *rem_sdp,
+ 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);
+
+
+/* The transport operations */
+static struct pjmedia_transport_op tp_adapter_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
+};
+
+
+/* The transport adapter instance */
+struct tp_adapter
+{
+ pjmedia_transport base;
+
+ pj_pool_t *pool;
+
+ /* Stream information. */
+ void *stream_user_data;
+ void (*stream_rtp_cb)(void *user_data,
+ void *pkt,
+ pj_ssize_t);
+ void (*stream_rtcp_cb)(void *user_data,
+ void *pkt,
+ pj_ssize_t);
+
+
+ /* Add your own member here.. */
+ pjmedia_transport *slave_tp;
+};
+
+
+/*
+ * Create the adapter.
+ */
+PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
+ const char *name,
+ pjmedia_transport *transport,
+ pjmedia_transport **p_tp)
+{
+ pj_pool_t *pool;
+ struct tp_adapter *adapter;
+
+ if (name == NULL)
+ name = "tpad%p";
+
+ /* Create the pool and initialize the adapter structure */
+ pool = pjmedia_endpt_create_pool(endpt, name, 512, 512);
+ adapter = PJ_POOL_ZALLOC_T(pool, struct tp_adapter);
+ adapter->pool = pool;
+ pj_ansi_strncpy(adapter->base.name, pool->obj_name,
+ sizeof(adapter->base.name));
+ adapter->base.type = PJMEDIA_TRANSPORT_TYPE_USER + 1;
+ adapter->base.op = &tp_adapter_op;
+
+ /* Save the transport as the slave transport */
+ adapter->slave_tp = transport;
+
+ /* Done */
+ *p_tp = &adapter->base;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * get_info() is called to get the transport addresses to be put
+ * in SDP c= line and a=rtcp line.
+ */
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+ pjmedia_transport_info *info)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* Since we don't have our own connection here, we just pass
+ * this function to the slave transport.
+ */
+ return pjmedia_transport_get_info(adapter->slave_tp, info);
+}
+
+
+/* This is our RTP callback, that is called by the slave transport when it
+ * receives RTP packet.
+ */
+static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)user_data;
+
+ pj_assert(adapter->stream_rtp_cb != NULL);
+
+ /* Call stream's callback */
+ adapter->stream_rtp_cb(adapter->stream_user_data, pkt, size);
+}
+
+/* This is our RTCP callback, that is called by the slave transport when it
+ * receives RTCP packet.
+ */
+static void transport_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)user_data;
+
+ pj_assert(adapter->stream_rtcp_cb != NULL);
+
+ /* Call stream's callback */
+ adapter->stream_rtcp_cb(adapter->stream_user_data, pkt, size);
+}
+
+
+/*
+ * attach() is called by stream to register callbacks that we should
+ * call on receipt of RTP and RTCP packets.
+ */
+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 tp_adapter *adapter = (struct tp_adapter*)tp;
+ pj_status_t status;
+
+ /* In this example, we will save the stream information and callbacks
+ * to our structure, and we will register different RTP/RTCP callbacks
+ * instead.
+ */
+ pj_assert(adapter->stream_user_data == NULL);
+ adapter->stream_user_data = user_data;
+ adapter->stream_rtp_cb = rtp_cb;
+ adapter->stream_rtcp_cb = rtcp_cb;
+
+ status = pjmedia_transport_attach(adapter->slave_tp, adapter, rem_addr,
+ rem_rtcp, addr_len, &transport_rtp_cb,
+ &transport_rtcp_cb);
+ if (status != PJ_SUCCESS) {
+ adapter->stream_user_data = NULL;
+ adapter->stream_rtp_cb = NULL;
+ adapter->stream_rtcp_cb = NULL;
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * detach() is called when the media is terminated, and the stream is
+ * to be disconnected from us.
+ */
+static void transport_detach(pjmedia_transport *tp, void *strm)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ PJ_UNUSED_ARG(strm);
+
+ if (adapter->stream_user_data != NULL) {
+ pjmedia_transport_detach(adapter->slave_tp, adapter);
+ adapter->stream_user_data = NULL;
+ adapter->stream_rtp_cb = NULL;
+ adapter->stream_rtcp_cb = NULL;
+ }
+}
+
+
+/*
+ * send_rtp() is called to send RTP packet. The "pkt" and "size" argument
+ * contain both the RTP header and the payload.
+ */
+static pj_status_t transport_send_rtp( pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* You may do some processing to the RTP packet here if you want. */
+
+ /* Send the packet using the slave transport */
+ return pjmedia_transport_send_rtp(adapter->slave_tp, pkt, size);
+}
+
+
+/*
+ * send_rtcp() is called to send RTCP packet. The "pkt" and "size" argument
+ * contain the RTCP packet.
+ */
+static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
+ const void *pkt,
+ pj_size_t size)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* You may do some processing to the RTCP packet here if you want. */
+
+ /* Send the packet using the slave transport */
+ return pjmedia_transport_send_rtcp(adapter->slave_tp, pkt, size);
+}
+
+
+/*
+ * This is another variant of send_rtcp(), with the alternate destination
+ * address in the argument.
+ */
+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 tp_adapter *adapter = (struct tp_adapter*)tp;
+ return pjmedia_transport_send_rtcp2(adapter->slave_tp, addr, addr_len,
+ pkt, size);
+}
+
+/*
+ * The media_create() is called when the transport is about to be used for
+ * a new call.
+ */
+static pj_status_t transport_media_create(pjmedia_transport *tp,
+ pj_pool_t *sdp_pool,
+ unsigned options,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* if "rem_sdp" is not NULL, it means we are UAS. You may do some
+ * inspections on the incoming SDP to verify that the SDP is acceptable
+ * for us. If the SDP is not acceptable, we can reject the SDP by
+ * returning non-PJ_SUCCESS.
+ */
+ if (rem_sdp) {
+ /* Do your stuff.. */
+ }
+
+ /* Once we're done with our initialization, pass the call to the
+ * slave transports to let it do it's own initialization too.
+ */
+ return pjmedia_transport_media_create(adapter->slave_tp, sdp_pool, options,
+ rem_sdp, media_index);
+}
+
+/*
+ * The encode_sdp() is called when we're about to send SDP to remote party,
+ * either as SDP offer or as SDP answer.
+ */
+static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
+ pj_pool_t *sdp_pool,
+ pjmedia_sdp_session *local_sdp,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may
+ * do some more checking on the SDP's once again to make sure that
+ * everything is okay before we send SDP.
+ */
+ if (rem_sdp) {
+ /* Do checking stuffs here.. */
+ }
+
+ /* You may do anything to the local_sdp, e.g. adding new attributes, or
+ * even modifying the SDP if you want.
+ */
+ if (1) {
+ /* Say we add a proprietary attribute here.. */
+ pjmedia_sdp_attr *my_attr;
+
+ my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr);
+ pj_strdup2(sdp_pool, &my_attr->name, "X-adapter");
+ pj_strdup2(sdp_pool, &my_attr->value, "some value");
+
+ pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count,
+ local_sdp->media[media_index]->attr,
+ my_attr);
+ }
+
+ /* And then pass the call to slave transport to let it encode its
+ * information in the SDP. You may choose to call encode_sdp() to slave
+ * first before adding your custom attributes if you want.
+ */
+ return pjmedia_transport_encode_sdp(adapter->slave_tp, sdp_pool, local_sdp,
+ rem_sdp, media_index);
+}
+
+/*
+ * The media_start() is called once both local and remote SDP have been
+ * negotiated successfully, and the media is ready to start. Here we can start
+ * committing our processing.
+ */
+static pj_status_t transport_media_start(pjmedia_transport *tp,
+ pj_pool_t *pool,
+ const pjmedia_sdp_session *local_sdp,
+ const pjmedia_sdp_session *rem_sdp,
+ unsigned media_index)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* Do something.. */
+
+ /* And pass the call to the slave transport */
+ return pjmedia_transport_media_start(adapter->slave_tp, pool, local_sdp,
+ rem_sdp, media_index);
+}
+
+/*
+ * The media_stop() is called when media has been stopped.
+ */
+static pj_status_t transport_media_stop(pjmedia_transport *tp)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* Do something.. */
+
+ /* And pass the call to the slave transport */
+ return pjmedia_transport_media_stop(adapter->slave_tp);
+}
+
+/*
+ * simulate_lost() is called to simulate packet lost
+ */
+static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
+ pjmedia_dir dir,
+ unsigned pct_lost)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+ return pjmedia_transport_simulate_lost(adapter->slave_tp, dir, pct_lost);
+}
+
+/*
+ * destroy() is called when the transport is no longer needed.
+ */
+static pj_status_t transport_destroy (pjmedia_transport *tp)
+{
+ struct tp_adapter *adapter = (struct tp_adapter*)tp;
+
+ /* Close the slave transport */
+ pjmedia_transport_close(adapter->slave_tp);
+
+ /* Self destruct.. */
+ pj_pool_release(adapter->pool);
+
+ return PJ_SUCCESS;
+}
+
+
+
+
+
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index d2c22e1e..b5157ee6 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -23,6 +23,7 @@
#define NO_LIMIT (int)0x7FFFFFFF
//#define STEREO_DEMO
+//#define TRANSPORT_ADAPTER_SAMPLE
/* Ringtones US UK */
#define RINGBACK_FREQ1 440 /* 400 */
@@ -132,6 +133,9 @@ static char some_buf[1024 * 3];
#ifdef STEREO_DEMO
static void stereo_demo();
#endif
+#ifdef TRANSPORT_ADAPTER_SAMPLE
+static pj_status_t transport_adapter_sample(void);
+#endif
pj_status_t app_destroy(void);
static void ringback_start(pjsua_call_id call_id);
@@ -4206,7 +4210,12 @@ pj_status_t app_init(int argc, char *argv[])
}
/* Add RTP transports */
+#ifdef TRANSPORT_ADAPTER_SAMPLE
+ status = transport_adapter_sample();
+
+#else
status = pjsua_media_transports_create(&app_config.rtp_cfg);
+#endif
if (status != PJ_SUCCESS)
goto on_error;
@@ -4391,3 +4400,44 @@ static void stereo_demo()
}
#endif
+#ifdef TRANSPORT_ADAPTER_SAMPLE
+static pj_status_t create_transport_adapter(pjmedia_endpt *med_endpt, int port,
+ pjmedia_transport **p_tp)
+{
+ pjmedia_transport *udp;
+ pj_status_t status;
+
+ /* Create the UDP media transport */
+ status = pjmedia_transport_udp_create(med_endpt, NULL, port, 0, &udp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create the adapter */
+ status = pjmedia_tp_adapter_create(med_endpt, NULL, udp, p_tp);
+ if (status != PJ_SUCCESS) {
+ pjmedia_transport_close(udp);
+ return status;
+ }
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t transport_adapter_sample(void)
+{
+ pjsua_media_transport tp[PJSUA_MAX_CALLS];
+ pj_status_t status;
+ int port = 7000;
+ unsigned i;
+
+ for (i=0; i<app_config.cfg.max_calls; ++i) {
+ status = create_transport_adapter(pjsua_get_pjmedia_endpt(),
+ port + i*10,
+ &tp[i].transport);
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ return pjsua_media_transports_attach(tp, i, PJ_TRUE);
+}
+#endif
+
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 3839fa8f..d52b6d52 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -79,6 +79,7 @@ typedef struct pjsua_call
pjmedia_transport *med_tp; /**< Current media transport. */
pj_status_t med_tp_ready;/**< Media transport status. */
pjmedia_transport *med_orig; /**< Original media transport */
+ pj_bool_t med_tp_auto_del; /**< May delete media transport */
pjsua_med_tp_st med_tp_st; /**< Media transport state */
pj_timer_entry refresh_tm;/**< Timer to send re-INVITE. */
pj_timer_entry hangup_tm; /**< Timer to hangup call. */
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index 02f32a3f..eab29854 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -543,10 +543,10 @@ pj_status_t pjsua_media_subsys_destroy(void)
if (pjsua_var.calls[i].med_tp_st != PJSUA_MED_TP_IDLE) {
pjsua_media_channel_deinit(i);
}
- if (pjsua_var.calls[i].med_tp) {
- (*pjsua_var.calls[i].med_tp->op->destroy)(pjsua_var.calls[i].med_tp);
- pjsua_var.calls[i].med_tp = NULL;
+ if (pjsua_var.calls[i].med_tp && pjsua_var.calls[i].med_tp_auto_del) {
+ pjmedia_transport_close(pjsua_var.calls[i].med_tp);
}
+ pjsua_var.calls[i].med_tp = NULL;
}
/* Destroy media endpoint. */
@@ -841,7 +841,9 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create(
/* Delete existing media transports */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
- if (pjsua_var.calls[i].med_tp != NULL) {
+ if (pjsua_var.calls[i].med_tp != NULL &&
+ pjsua_var.calls[i].med_tp_auto_del)
+ {
pjmedia_transport_close(pjsua_var.calls[i].med_tp);
pjsua_var.calls[i].med_tp = NULL;
}
@@ -850,18 +852,49 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create(
/* Copy config */
pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
+ /* Create the transports */
if (pjsua_var.media_cfg.enable_ice) {
status = create_ice_media_transports();
} else {
status = create_udp_media_transports(&cfg);
}
+ /* Set media transport auto_delete to True */
+ for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+ pjsua_var.calls[i].med_tp_auto_del = PJ_TRUE;
+ }
PJSUA_UNLOCK();
return status;
}
+/*
+ * Attach application's created media transports.
+ */
+PJ_DEF(pj_status_t) pjsua_media_transports_attach(pjsua_media_transport tp[],
+ unsigned count,
+ pj_bool_t auto_delete)
+{
+ unsigned i;
+
+ PJ_ASSERT_RETURN(tp && count==pjsua_var.ua_cfg.max_calls, PJ_EINVAL);
+
+ /* Assign the media transports */
+ for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+ if (pjsua_var.calls[i].med_tp != NULL &&
+ pjsua_var.calls[i].med_tp_auto_del)
+ {
+ pjmedia_transport_close(pjsua_var.calls[i].med_tp);
+ }
+
+ pjsua_var.calls[i].med_tp = tp[i].transport;
+ pjsua_var.calls[i].med_tp_auto_del = auto_delete;
+ }
+
+ return PJ_SUCCESS;
+}
+
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
pjsip_role_e role,