blob: b04da2ef02170e62b62049cad26b51a316665243 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
20#include <pjmedia/transport_loop.h>
21#include <pj/array.h>
22#include <pj/assert.h>
23#include <pj/errno.h>
24#include <pj/ioqueue.h>
25#include <pj/log.h>
26#include <pj/pool.h>
27#include <pj/rand.h>
28#include <pj/string.h>
29
30
31struct user
32{
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. */
36 void*,
37 pj_ssize_t);
38 void (*rtcp_cb)( void*, /**< To report incoming RTCP. */
39 void*,
40 pj_ssize_t);
41};
42
43struct transport_loop
44{
45 pjmedia_transport base; /**< Base transport. */
46
47 pj_pool_t *pool; /**< Memory pool */
48 unsigned user_cnt; /**< Number of attachments */
49 struct user users[4]; /**< Array of users. */
50
51 unsigned tx_drop_pct; /**< Percent of tx pkts to drop. */
52 unsigned rx_drop_pct; /**< Percent of rx pkts to drop. */
53
54};
55
56
57
58/*
59 * These are media transport operations.
60 */
61static pj_status_t transport_get_info (pjmedia_transport *tp,
62 pjmedia_transport_info *info);
63static pj_status_t transport_attach (pjmedia_transport *tp,
64 void *user_data,
65 const pj_sockaddr_t *rem_addr,
66 const pj_sockaddr_t *rem_rtcp,
67 unsigned addr_len,
68 void (*rtp_cb)(void*,
69 void*,
70 pj_ssize_t),
71 void (*rtcp_cb)(void*,
72 void*,
73 pj_ssize_t));
74static void transport_detach (pjmedia_transport *tp,
75 void *strm);
76static pj_status_t transport_send_rtp( pjmedia_transport *tp,
77 const void *pkt,
78 pj_size_t size);
79static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
80 const void *pkt,
81 pj_size_t size);
82static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
83 const pj_sockaddr_t *addr,
84 unsigned addr_len,
85 const void *pkt,
86 pj_size_t size);
87static pj_status_t transport_media_create(pjmedia_transport *tp,
88 pj_pool_t *pool,
89 unsigned options,
90 const pjmedia_sdp_session *sdp_remote,
91 unsigned media_index);
92static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
93 pj_pool_t *pool,
94 pjmedia_sdp_session *sdp_local,
95 const pjmedia_sdp_session *rem_sdp,
96 unsigned media_index);
97static pj_status_t transport_media_start (pjmedia_transport *tp,
98 pj_pool_t *pool,
99 const pjmedia_sdp_session *sdp_local,
100 const pjmedia_sdp_session *sdp_remote,
101 unsigned media_index);
102static pj_status_t transport_media_stop(pjmedia_transport *tp);
103static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
104 pjmedia_dir dir,
105 unsigned pct_lost);
106static pj_status_t transport_destroy (pjmedia_transport *tp);
107
108
109static pjmedia_transport_op transport_udp_op =
110{
111 &transport_get_info,
112 &transport_attach,
113 &transport_detach,
114 &transport_send_rtp,
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,
122 &transport_destroy
123};
124
125
126/**
127 * Create loopback transport.
128 */
129PJ_DEF(pj_status_t) pjmedia_transport_loop_create(pjmedia_endpt *endpt,
130 pjmedia_transport **p_tp)
131{
132 struct transport_loop *tp;
133 pj_pool_t *pool;
134
135 /* Sanity check */
136 PJ_ASSERT_RETURN(endpt && p_tp, PJ_EINVAL);
137
138 /* Create transport structure */
139 pool = pjmedia_endpt_create_pool(endpt, "tploop", 512, 512);
140 if (!pool)
141 return PJ_ENOMEM;
142
143 tp = PJ_POOL_ZALLOC_T(pool, struct transport_loop);
144 tp->pool = pool;
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;
148
149 /* Done */
150 *p_tp = &tp->base;
151 return PJ_SUCCESS;
152}
153
154
155PJ_DEF(pj_status_t) pjmedia_transport_loop_disable_rx( pjmedia_transport *tp,
156 void *user,
157 pj_bool_t disabled)
158{
159 struct transport_loop *loop = (struct transport_loop*) tp;
160 unsigned i;
161
162 for (i=0; i<loop->user_cnt; ++i) {
163 if (loop->users[i].user_data == user) {
164 loop->users[i].rx_disabled = disabled;
165 return PJ_SUCCESS;
166 }
167 }
168 pj_assert(!"Invalid stream user");
169 return PJ_ENOTFOUND;
170}
171
172/**
173 * Close loopback transport.
174 */
175static pj_status_t transport_destroy(pjmedia_transport *tp)
176{
177 struct transport_loop *loop = (struct transport_loop*) tp;
178
179 /* Sanity check */
180 PJ_ASSERT_RETURN(tp, PJ_EINVAL);
181
182 pj_pool_release(loop->pool);
183
184 return PJ_SUCCESS;
185}
186
187
188/* Called to get the transport info */
189static pj_status_t transport_get_info(pjmedia_transport *tp,
190 pjmedia_transport_info *info)
191{
192 PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
193
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);
198
199 return PJ_SUCCESS;
200}
201
202
203/* Called by application to initialize the transport */
204static pj_status_t transport_attach( pjmedia_transport *tp,
205 void *user_data,
206 const pj_sockaddr_t *rem_addr,
207 const pj_sockaddr_t *rem_rtcp,
208 unsigned addr_len,
209 void (*rtp_cb)(void*,
210 void*,
211 pj_ssize_t),
212 void (*rtcp_cb)(void*,
213 void*,
214 pj_ssize_t))
215{
216 struct transport_loop *loop = (struct transport_loop*) tp;
217 unsigned i;
218 const pj_sockaddr *rtcp_addr;
219
220 /* Validate arguments */
221 PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
222
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,
226 PJ_EINVALIDOP);
227 }
228 PJ_ASSERT_RETURN(loop->user_cnt != PJ_ARRAY_SIZE(loop->users),
229 PJ_ETOOMANY);
230
231 PJ_UNUSED_ARG(rem_rtcp);
232 PJ_UNUSED_ARG(rtcp_addr);
233
234 /* "Attach" the application: */
235
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;
240 ++loop->user_cnt;
241
242 return PJ_SUCCESS;
243}
244
245
246/* Called by application when it no longer needs the transport */
247static void transport_detach( pjmedia_transport *tp,
248 void *user_data)
249{
250 struct transport_loop *loop = (struct transport_loop*) tp;
251 unsigned i;
252
253 pj_assert(tp);
254
255 for (i=0; i<loop->user_cnt; ++i) {
256 if (loop->users[i].user_data == user_data)
257 break;
258 }
259
260 /* Remove this user */
261 if (i != loop->user_cnt) {
262 pj_array_erase(loop->users, sizeof(loop->users[0]),
263 loop->user_cnt, i);
264 --loop->user_cnt;
265 }
266}
267
268
269/* Called by application to send RTP packet */
270static pj_status_t transport_send_rtp( pjmedia_transport *tp,
271 const void *pkt,
272 pj_size_t size)
273{
274 struct transport_loop *loop = (struct transport_loop*)tp;
275 unsigned i;
276
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 "
282 "simulation"));
283 return PJ_SUCCESS;
284 }
285 }
286
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 "
292 "simulation"));
293 return PJ_SUCCESS;
294 }
295 }
296
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,
301 size);
302 }
303
304 return PJ_SUCCESS;
305}
306
307/* Called by application to send RTCP packet */
308static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
309 const void *pkt,
310 pj_size_t size)
311{
312 return transport_send_rtcp2(tp, NULL, 0, pkt, size);
313}
314
315
316/* Called by application to send RTCP packet */
317static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
318 const pj_sockaddr_t *addr,
319 unsigned addr_len,
320 const void *pkt,
321 pj_size_t size)
322{
323 struct transport_loop *loop = (struct transport_loop*)tp;
324 unsigned i;
325
326 PJ_UNUSED_ARG(addr_len);
327 PJ_UNUSED_ARG(addr);
328
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,
333 size);
334 }
335
336 return PJ_SUCCESS;
337}
338
339
340static pj_status_t transport_media_create(pjmedia_transport *tp,
341 pj_pool_t *pool,
342 unsigned options,
343 const pjmedia_sdp_session *sdp_remote,
344 unsigned media_index)
345{
346 PJ_UNUSED_ARG(tp);
347 PJ_UNUSED_ARG(pool);
348 PJ_UNUSED_ARG(options);
349 PJ_UNUSED_ARG(sdp_remote);
350 PJ_UNUSED_ARG(media_index);
351 return PJ_SUCCESS;
352}
353
354static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
355 pj_pool_t *pool,
356 pjmedia_sdp_session *sdp_local,
357 const pjmedia_sdp_session *rem_sdp,
358 unsigned media_index)
359{
360 PJ_UNUSED_ARG(tp);
361 PJ_UNUSED_ARG(pool);
362 PJ_UNUSED_ARG(sdp_local);
363 PJ_UNUSED_ARG(rem_sdp);
364 PJ_UNUSED_ARG(media_index);
365 return PJ_SUCCESS;
366}
367
368static pj_status_t transport_media_start(pjmedia_transport *tp,
369 pj_pool_t *pool,
370 const pjmedia_sdp_session *sdp_local,
371 const pjmedia_sdp_session *sdp_remote,
372 unsigned media_index)
373{
374 PJ_UNUSED_ARG(tp);
375 PJ_UNUSED_ARG(pool);
376 PJ_UNUSED_ARG(sdp_local);
377 PJ_UNUSED_ARG(sdp_remote);
378 PJ_UNUSED_ARG(media_index);
379 return PJ_SUCCESS;
380}
381
382static pj_status_t transport_media_stop(pjmedia_transport *tp)
383{
384 PJ_UNUSED_ARG(tp);
385 return PJ_SUCCESS;
386}
387
388static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
389 pjmedia_dir dir,
390 unsigned pct_lost)
391{
392 struct transport_loop *loop = (struct transport_loop*)tp;
393
394 PJ_ASSERT_RETURN(tp && pct_lost <= 100, PJ_EINVAL);
395
396 if (dir & PJMEDIA_DIR_ENCODING)
397 loop->tx_drop_pct = pct_lost;
398
399 if (dir & PJMEDIA_DIR_DECODING)
400 loop->rx_drop_pct = pct_lost;
401
402 return PJ_SUCCESS;
403}
404