1
/* $Id: transport_loop.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 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
#include <pjmedia/transport_loop.h>
22
#include <pj/assert.h>
24
#include <pj/ioqueue.h>
28
#include <pj/string.h>
33
pj_bool_t rx_disabled; /**< Doesn't want to receive pkt? */
34
void *user_data; /**< Only valid when attached */
35
void (*rtp_cb)( void*, /**< To report incoming RTP. */
38
void (*rtcp_cb)( void*, /**< To report incoming RTCP. */
45
pjmedia_transport base; /**< Base transport. */
47
pj_pool_t *pool; /**< Memory pool */
48
unsigned user_cnt; /**< Number of attachments */
49
struct user users[4]; /**< Array of users. */
51
unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
52
unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
59
* These are media transport operations.
61
static pj_status_t transport_get_info (pjmedia_transport *tp,
62
pjmedia_transport_info *info);
63
static pj_status_t transport_attach (pjmedia_transport *tp,
65
const pj_sockaddr_t *rem_addr,
66
const pj_sockaddr_t *rem_rtcp,
71
void (*rtcp_cb)(void*,
74
static void transport_detach (pjmedia_transport *tp,
76
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
79
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
82
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
83
const pj_sockaddr_t *addr,
87
static pj_status_t transport_media_create(pjmedia_transport *tp,
90
const pjmedia_sdp_session *sdp_remote,
91
unsigned media_index);
92
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
94
pjmedia_sdp_session *sdp_local,
95
const pjmedia_sdp_session *rem_sdp,
96
unsigned media_index);
97
static pj_status_t transport_media_start (pjmedia_transport *tp,
99
const pjmedia_sdp_session *sdp_local,
100
const pjmedia_sdp_session *sdp_remote,
101
unsigned media_index);
102
static pj_status_t transport_media_stop(pjmedia_transport *tp);
103
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
106
static pj_status_t transport_destroy (pjmedia_transport *tp);
109
static pjmedia_transport_op transport_udp_op =
115
&transport_send_rtcp,
116
&transport_send_rtcp2,
117
&transport_media_create,
118
&transport_encode_sdp,
119
&transport_media_start,
120
&transport_media_stop,
121
&transport_simulate_lost,
127
* Create loopback transport.
129
PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt,
130
pjmedia_transport **p_tp)
132
struct transport_loop *tp;
136
PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL);
138
/* Create transport structure */
139
pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512);
143
tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop);
145
pj_ansi_strncpy(tp->base.name, tp->pool->obj_name, PJ_MAX_OBJ_NAME-1);
146
tp->base.op = &transport_udp_op;
147
tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
155
PJ_DEF(pj_status_t) pjmedia_transport_loop_disable_rx( pjmedia_transport *tp,
159
struct transport_loop *loop = (struct transport_loop*) tp;
162
for (i=0; i<loop->user_cnt; ++i) {
163
if (loop->users[i].user_data == user) {
164
loop->users[i].rx_disabled = disabled;
168
pj_assert(!"Invalid stream user");
173
* Close loopback transport.
175
static pj_status_t transport_destroy(pjmedia_transport *tp)
177
struct transport_loop *loop = (struct transport_loop*) tp;
180
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
182
pj_pool_release(loop->pool);
188
/* Called to get the transport info */
189
static pj_status_t transport_get_info(pjmedia_transport *tp,
190
pjmedia_transport_info *info)
192
PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
194
info->sock_info.rtp_sock = 1;
195
pj_sockaddr_in_init(&info->sock_info.rtp_addr_name.ipv4, 0, 0);
196
info->sock_info.rtcp_sock = 2;
197
pj_sockaddr_in_init(&info->sock_info.rtcp_addr_name.ipv4, 0, 0);
203
/* Called by application to initialize the transport */
204
static pj_status_t transport_attach( pjmedia_transport *tp,
206
const pj_sockaddr_t *rem_addr,
207
const pj_sockaddr_t *rem_rtcp,
209
void (*rtp_cb)(void*,
212
void (*rtcp_cb)(void*,
216
struct transport_loop *loop = (struct transport_loop*) tp;
218
const pj_sockaddr *rtcp_addr;
220
/* Validate arguments */
221
PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
223
/* Must not be "attached" to same user */
224
for (i=0; i<loop->user_cnt; ++i) {
225
PJ_ASSERT_RETURN(loop->users[i].user_data != user_data,
228
PJ_ASSERT_RETURN(loop->user_cnt != PJ_ARRAY_SIZE(loop->users),
231
PJ_UNUSED_ARG(rem_rtcp);
232
PJ_UNUSED_ARG(rtcp_addr);
234
/* "Attach" the application: */
236
/* Save the new user */
237
loop->users[loop->user_cnt].rtp_cb = rtp_cb;
238
loop->users[loop->user_cnt].rtcp_cb = rtcp_cb;
239
loop->users[loop->user_cnt].user_data = user_data;
246
/* Called by application when it no longer needs the transport */
247
static void transport_detach( pjmedia_transport *tp,
250
struct transport_loop *loop = (struct transport_loop*) tp;
255
for (i=0; i<loop->user_cnt; ++i) {
256
if (loop->users[i].user_data == user_data)
260
/* Remove this user */
261
if (i != loop->user_cnt) {
262
pj_array_erase(loop->users, sizeof(loop->users[0]),
269
/* Called by application to send RTP packet */
270
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
274
struct transport_loop *loop = (struct transport_loop*)tp;
277
/* Simulate packet lost on TX direction */
278
if (loop->tx_drop_pct) {
279
if ((pj_rand() % 100) <= (int)loop->tx_drop_pct) {
280
PJ_LOG(5,(loop->base.name,
281
"TX RTP packet dropped because of pkt lost "
287
/* Simulate packet lost on RX direction */
288
if (loop->rx_drop_pct) {
289
if ((pj_rand() % 100) <= (int)loop->rx_drop_pct) {
290
PJ_LOG(5,(loop->base.name,
291
"RX RTP packet dropped because of pkt lost "
297
/* Distribute to users */
298
for (i=0; i<loop->user_cnt; ++i) {
299
if (!loop->users[i].rx_disabled && loop->users[i].rtp_cb)
300
(*loop->users[i].rtp_cb)(loop->users[i].user_data, (void*)pkt,
307
/* Called by application to send RTCP packet */
308
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
312
return transport_send_rtcp2(tp, NULL, 0, pkt, size);
316
/* Called by application to send RTCP packet */
317
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
318
const pj_sockaddr_t *addr,
323
struct transport_loop *loop = (struct transport_loop*)tp;
326
PJ_UNUSED_ARG(addr_len);
329
/* Distribute to users */
330
for (i=0; i<loop->user_cnt; ++i) {
331
if (!loop->users[i].rx_disabled && loop->users[i].rtcp_cb)
332
(*loop->users[i].rtcp_cb)(loop->users[i].user_data, (void*)pkt,
340
static pj_status_t transport_media_create(pjmedia_transport *tp,
343
const pjmedia_sdp_session *sdp_remote,
344
unsigned media_index)
348
PJ_UNUSED_ARG(options);
349
PJ_UNUSED_ARG(sdp_remote);
350
PJ_UNUSED_ARG(media_index);
354
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
356
pjmedia_sdp_session *sdp_local,
357
const pjmedia_sdp_session *rem_sdp,
358
unsigned media_index)
362
PJ_UNUSED_ARG(sdp_local);
363
PJ_UNUSED_ARG(rem_sdp);
364
PJ_UNUSED_ARG(media_index);
368
static pj_status_t transport_media_start(pjmedia_transport *tp,
370
const pjmedia_sdp_session *sdp_local,
371
const pjmedia_sdp_session *sdp_remote,
372
unsigned media_index)
376
PJ_UNUSED_ARG(sdp_local);
377
PJ_UNUSED_ARG(sdp_remote);
378
PJ_UNUSED_ARG(media_index);
382
static pj_status_t transport_media_stop(pjmedia_transport *tp)
388
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
392
struct transport_loop *loop = (struct transport_loop*)tp;
394
PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL);
396
if (dir & PJMEDIA_DIR_ENCODING)
397
loop->tx_drop_pct = pct_lost;
399
if (dir & PJMEDIA_DIR_DECODING)
400
loop->rx_drop_pct = pct_lost;