From ac1206e146b5643263054f0bcccf08261558017e Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 4 Sep 2008 13:55:33 +0000 Subject: 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 --- build.symbian/pjmedia.mmp | 1 + pjmedia/build/Makefile | 3 +- pjmedia/build/pjmedia.dsp | 8 + pjmedia/build/pjmedia.vcproj | 8 + pjmedia/include/pjmedia.h | 1 + pjmedia/include/pjmedia/transport.h | 7 +- pjmedia/include/pjmedia/transport_adapter_sample.h | 72 ++++ pjmedia/src/pjmedia/transport_adapter_sample.c | 433 +++++++++++++++++++++ pjsip-apps/src/pjsua/pjsua_app.c | 50 +++ pjsip/include/pjsua-lib/pjsua_internal.h | 1 + pjsip/src/pjsua-lib/pjsua_media.c | 41 +- 11 files changed, 619 insertions(+), 6 deletions(-) create mode 100644 pjmedia/include/pjmedia/transport_adapter_sample.h create mode 100644 pjmedia/src/pjmedia/transport_adapter_sample.c 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 @@ -969,6 +969,10 @@ /> + + @@ -1244,6 +1248,10 @@ RelativePath="..\include\pjmedia\transport.h" > + + 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 #include #include +#include #include #include #include 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 + * + * 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 + + +/** + * @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 + * + * 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 +#include +#include +#include + + +/* 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; iop->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