blob: a076bdab6e1378ae33a4ecddc35bf19cf61dd924 [file] [log] [blame]
/* $Id$ */
/*
* 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_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_bool_t del_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,
pj_bool_t del_base,
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)
(PJMEDIA_TRANSPORT_TYPE_USER + 1);
adapter->base.op = &tp_adapter_op;
/* Save the transport as the slave transport */
adapter->slave_tp = transport;
adapter->del_base = del_base;
/* 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 */
if (adapter->del_base) {
pjmedia_transport_close(adapter->slave_tp);
}
/* Self destruct.. */
pj_pool_release(adapter->pool);
return PJ_SUCCESS;
}