1
/* $Id: transport_loop.c 2881 2009-08-15 09:53:58Z bennylp $ */
3
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify this program, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, Teluu Inc. (http://www.teluu.com)
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
31
#include <pjmedia/transport_loop.h>
33
#include <pj/assert.h>
35
#include <pj/ioqueue.h>
39
#include <pj/string.h>
44
pj_bool_t rx_disabled; /**< Doesn't want to receive pkt? */
45
void *user_data; /**< Only valid when attached */
46
void (*rtp_cb)( void*, /**< To report incoming RTP. */
49
void (*rtcp_cb)( void*, /**< To report incoming RTCP. */
56
pjmedia_transport base; /**< Base transport. */
58
pj_pool_t *pool; /**< Memory pool */
59
unsigned user_cnt; /**< Number of attachments */
60
struct user users[4]; /**< Array of users. */
62
unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
63
unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
70
* These are media transport operations.
72
static pj_status_t transport_get_info (pjmedia_transport *tp,
73
pjmedia_transport_info *info);
74
static pj_status_t transport_attach (pjmedia_transport *tp,
76
const pj_sockaddr_t *rem_addr,
77
const pj_sockaddr_t *rem_rtcp,
82
void (*rtcp_cb)(void*,
85
static void transport_detach (pjmedia_transport *tp,
87
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
90
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
93
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
94
const pj_sockaddr_t *addr,
98
static pj_status_t transport_media_create(pjmedia_transport *tp,
101
const pjmedia_sdp_session *sdp_remote,
102
unsigned media_index);
103
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
105
pjmedia_sdp_session *sdp_local,
106
const pjmedia_sdp_session *rem_sdp,
107
unsigned media_index);
108
static pj_status_t transport_media_start (pjmedia_transport *tp,
110
const pjmedia_sdp_session *sdp_local,
111
const pjmedia_sdp_session *sdp_remote,
112
unsigned media_index);
113
static pj_status_t transport_media_stop(pjmedia_transport *tp);
114
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
117
static pj_status_t transport_destroy (pjmedia_transport *tp);
120
static pjmedia_transport_op transport_udp_op =
126
&transport_send_rtcp,
127
&transport_send_rtcp2,
128
&transport_media_create,
129
&transport_encode_sdp,
130
&transport_media_start,
131
&transport_media_stop,
132
&transport_simulate_lost,
138
* Create loopback transport.
140
PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt,
141
pjmedia_transport **p_tp)
143
struct transport_loop *tp;
147
PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL);
149
/* Create transport structure */
150
pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512);
154
tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop);
156
pj_ansi_strncpy(tp->base.name, tp->pool->obj_name, PJ_MAX_OBJ_NAME-1);
157
tp->base.op = &transport_udp_op;
158
tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
166
PJ_DEF(pj_status_t) pjmedia_transport_loop_disable_rx( pjmedia_transport *tp,
170
struct transport_loop *loop = (struct transport_loop*) tp;
173
for (i=0; i<loop->user_cnt; ++i) {
174
if (loop->users[i].user_data == user) {
175
loop->users[i].rx_disabled = disabled;
179
pj_assert(!"Invalid stream user");
184
* Close loopback transport.
186
static pj_status_t transport_destroy(pjmedia_transport *tp)
188
struct transport_loop *loop = (struct transport_loop*) tp;
191
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
193
pj_pool_release(loop->pool);
199
/* Called to get the transport info */
200
static pj_status_t transport_get_info(pjmedia_transport *tp,
201
pjmedia_transport_info *info)
203
PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
205
info->sock_info.rtp_sock = 1;
206
pj_sockaddr_in_init(&info->sock_info.rtp_addr_name.ipv4, 0, 0);
207
info->sock_info.rtcp_sock = 2;
208
pj_sockaddr_in_init(&info->sock_info.rtcp_addr_name.ipv4, 0, 0);
214
/* Called by application to initialize the transport */
215
static pj_status_t transport_attach( pjmedia_transport *tp,
217
const pj_sockaddr_t *rem_addr,
218
const pj_sockaddr_t *rem_rtcp,
220
void (*rtp_cb)(void*,
223
void (*rtcp_cb)(void*,
227
struct transport_loop *loop = (struct transport_loop*) tp;
229
const pj_sockaddr *rtcp_addr;
231
/* Validate arguments */
232
PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
234
/* Must not be "attached" to same user */
235
for (i=0; i<loop->user_cnt; ++i) {
236
PJ_ASSERT_RETURN(loop->users[i].user_data != user_data,
239
PJ_ASSERT_RETURN(loop->user_cnt != PJ_ARRAY_SIZE(loop->users),
242
PJ_UNUSED_ARG(rem_rtcp);
243
PJ_UNUSED_ARG(rtcp_addr);
245
/* "Attach" the application: */
247
/* Save the new user */
248
loop->users[loop->user_cnt].rtp_cb = rtp_cb;
249
loop->users[loop->user_cnt].rtcp_cb = rtcp_cb;
250
loop->users[loop->user_cnt].user_data = user_data;
257
/* Called by application when it no longer needs the transport */
258
static void transport_detach( pjmedia_transport *tp,
261
struct transport_loop *loop = (struct transport_loop*) tp;
266
for (i=0; i<loop->user_cnt; ++i) {
267
if (loop->users[i].user_data == user_data)
271
/* Remove this user */
272
if (i != loop->user_cnt) {
273
pj_array_erase(loop->users, sizeof(loop->users[0]),
280
/* Called by application to send RTP packet */
281
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
285
struct transport_loop *loop = (struct transport_loop*)tp;
288
/* Simulate packet lost on TX direction */
289
if (loop->tx_drop_pct) {
290
if ((pj_rand() % 100) <= (int)loop->tx_drop_pct) {
291
PJ_LOG(5,(loop->base.name,
292
"TX RTP packet dropped because of pkt lost "
298
/* Simulate packet lost on RX direction */
299
if (loop->rx_drop_pct) {
300
if ((pj_rand() % 100) <= (int)loop->rx_drop_pct) {
301
PJ_LOG(5,(loop->base.name,
302
"RX RTP packet dropped because of pkt lost "
308
/* Distribute to users */
309
for (i=0; i<loop->user_cnt; ++i) {
310
if (!loop->users[i].rx_disabled && loop->users[i].rtp_cb)
311
(*loop->users[i].rtp_cb)(loop->users[i].user_data, (void*)pkt,
318
/* Called by application to send RTCP packet */
319
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
323
return transport_send_rtcp2(tp, NULL, 0, pkt, size);
327
/* Called by application to send RTCP packet */
328
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
329
const pj_sockaddr_t *addr,
334
struct transport_loop *loop = (struct transport_loop*)tp;
337
PJ_UNUSED_ARG(addr_len);
340
/* Distribute to users */
341
for (i=0; i<loop->user_cnt; ++i) {
342
if (!loop->users[i].rx_disabled && loop->users[i].rtcp_cb)
343
(*loop->users[i].rtcp_cb)(loop->users[i].user_data, (void*)pkt,
351
static pj_status_t transport_media_create(pjmedia_transport *tp,
354
const pjmedia_sdp_session *sdp_remote,
355
unsigned media_index)
359
PJ_UNUSED_ARG(options);
360
PJ_UNUSED_ARG(sdp_remote);
361
PJ_UNUSED_ARG(media_index);
365
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
367
pjmedia_sdp_session *sdp_local,
368
const pjmedia_sdp_session *rem_sdp,
369
unsigned media_index)
373
PJ_UNUSED_ARG(sdp_local);
374
PJ_UNUSED_ARG(rem_sdp);
375
PJ_UNUSED_ARG(media_index);
379
static pj_status_t transport_media_start(pjmedia_transport *tp,
381
const pjmedia_sdp_session *sdp_local,
382
const pjmedia_sdp_session *sdp_remote,
383
unsigned media_index)
387
PJ_UNUSED_ARG(sdp_local);
388
PJ_UNUSED_ARG(sdp_remote);
389
PJ_UNUSED_ARG(media_index);
393
static pj_status_t transport_media_stop(pjmedia_transport *tp)
399
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
403
struct transport_loop *loop = (struct transport_loop*)tp;
405
PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL);
407
if (dir & PJMEDIA_DIR_ENCODING)
408
loop->tx_drop_pct = pct_lost;
410
if (dir & PJMEDIA_DIR_DECODING)
411
loop->rx_drop_pct = pct_lost;