blob: a076bdab6e1378ae33a4ecddc35bf19cf61dd924 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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_adapter_sample.h>
21#include <pjmedia/endpoint.h>
22#include <pj/assert.h>
23#include <pj/pool.h>
24
25
26/* Transport functions prototypes */
27static pj_status_t transport_get_info (pjmedia_transport *tp,
28 pjmedia_transport_info *info);
29static pj_status_t transport_attach (pjmedia_transport *tp,
30 void *user_data,
31 const pj_sockaddr_t *rem_addr,
32 const pj_sockaddr_t *rem_rtcp,
33 unsigned addr_len,
34 void (*rtp_cb)(void*,
35 void*,
36 pj_ssize_t),
37 void (*rtcp_cb)(void*,
38 void*,
39 pj_ssize_t));
40static void transport_detach (pjmedia_transport *tp,
41 void *strm);
42static pj_status_t transport_send_rtp( pjmedia_transport *tp,
43 const void *pkt,
44 pj_size_t size);
45static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
46 const void *pkt,
47 pj_size_t size);
48static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
49 const pj_sockaddr_t *addr,
50 unsigned addr_len,
51 const void *pkt,
52 pj_size_t size);
53static pj_status_t transport_media_create(pjmedia_transport *tp,
54 pj_pool_t *sdp_pool,
55 unsigned options,
56 const pjmedia_sdp_session *rem_sdp,
57 unsigned media_index);
58static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
59 pj_pool_t *sdp_pool,
60 pjmedia_sdp_session *local_sdp,
61 const pjmedia_sdp_session *rem_sdp,
62 unsigned media_index);
63static pj_status_t transport_media_start (pjmedia_transport *tp,
64 pj_pool_t *pool,
65 const pjmedia_sdp_session *local_sdp,
66 const pjmedia_sdp_session *rem_sdp,
67 unsigned media_index);
68static pj_status_t transport_media_stop(pjmedia_transport *tp);
69static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
70 pjmedia_dir dir,
71 unsigned pct_lost);
72static pj_status_t transport_destroy (pjmedia_transport *tp);
73
74
75/* The transport operations */
76static struct pjmedia_transport_op tp_adapter_op =
77{
78 &transport_get_info,
79 &transport_attach,
80 &transport_detach,
81 &transport_send_rtp,
82 &transport_send_rtcp,
83 &transport_send_rtcp2,
84 &transport_media_create,
85 &transport_encode_sdp,
86 &transport_media_start,
87 &transport_media_stop,
88 &transport_simulate_lost,
89 &transport_destroy
90};
91
92
93/* The transport adapter instance */
94struct tp_adapter
95{
96 pjmedia_transport base;
97 pj_bool_t del_base;
98
99 pj_pool_t *pool;
100
101 /* Stream information. */
102 void *stream_user_data;
103 void (*stream_rtp_cb)(void *user_data,
104 void *pkt,
105 pj_ssize_t);
106 void (*stream_rtcp_cb)(void *user_data,
107 void *pkt,
108 pj_ssize_t);
109
110
111 /* Add your own member here.. */
112 pjmedia_transport *slave_tp;
113};
114
115
116/*
117 * Create the adapter.
118 */
119PJ_DEF(pj_status_t) pjmedia_tp_adapter_create( pjmedia_endpt *endpt,
120 const char *name,
121 pjmedia_transport *transport,
122 pj_bool_t del_base,
123 pjmedia_transport **p_tp)
124{
125 pj_pool_t *pool;
126 struct tp_adapter *adapter;
127
128 if (name == NULL)
129 name = "tpad%p";
130
131 /* Create the pool and initialize the adapter structure */
132 pool = pjmedia_endpt_create_pool(endpt, name, 512, 512);
133 adapter = PJ_POOL_ZALLOC_T(pool, struct tp_adapter);
134 adapter->pool = pool;
135 pj_ansi_strncpy(adapter->base.name, pool->obj_name,
136 sizeof(adapter->base.name));
137 adapter->base.type = (pjmedia_transport_type)
138 (PJMEDIA_TRANSPORT_TYPE_USER + 1);
139 adapter->base.op = &tp_adapter_op;
140
141 /* Save the transport as the slave transport */
142 adapter->slave_tp = transport;
143 adapter->del_base = del_base;
144
145 /* Done */
146 *p_tp = &adapter->base;
147 return PJ_SUCCESS;
148}
149
150
151/*
152 * get_info() is called to get the transport addresses to be put
153 * in SDP c= line and a=rtcp line.
154 */
155static pj_status_t transport_get_info(pjmedia_transport *tp,
156 pjmedia_transport_info *info)
157{
158 struct tp_adapter *adapter = (struct tp_adapter*)tp;
159
160 /* Since we don't have our own connection here, we just pass
161 * this function to the slave transport.
162 */
163 return pjmedia_transport_get_info(adapter->slave_tp, info);
164}
165
166
167/* This is our RTP callback, that is called by the slave transport when it
168 * receives RTP packet.
169 */
170static void transport_rtp_cb(void *user_data, void *pkt, pj_ssize_t size)
171{
172 struct tp_adapter *adapter = (struct tp_adapter*)user_data;
173
174 pj_assert(adapter->stream_rtp_cb != NULL);
175
176 /* Call stream's callback */
177 adapter->stream_rtp_cb(adapter->stream_user_data, pkt, size);
178}
179
180/* This is our RTCP callback, that is called by the slave transport when it
181 * receives RTCP packet.
182 */
183static void transport_rtcp_cb(void *user_data, void *pkt, pj_ssize_t size)
184{
185 struct tp_adapter *adapter = (struct tp_adapter*)user_data;
186
187 pj_assert(adapter->stream_rtcp_cb != NULL);
188
189 /* Call stream's callback */
190 adapter->stream_rtcp_cb(adapter->stream_user_data, pkt, size);
191}
192
193
194/*
195 * attach() is called by stream to register callbacks that we should
196 * call on receipt of RTP and RTCP packets.
197 */
198static pj_status_t transport_attach(pjmedia_transport *tp,
199 void *user_data,
200 const pj_sockaddr_t *rem_addr,
201 const pj_sockaddr_t *rem_rtcp,
202 unsigned addr_len,
203 void (*rtp_cb)(void*,
204 void*,
205 pj_ssize_t),
206 void (*rtcp_cb)(void*,
207 void*,
208 pj_ssize_t))
209{
210 struct tp_adapter *adapter = (struct tp_adapter*)tp;
211 pj_status_t status;
212
213 /* In this example, we will save the stream information and callbacks
214 * to our structure, and we will register different RTP/RTCP callbacks
215 * instead.
216 */
217 pj_assert(adapter->stream_user_data == NULL);
218 adapter->stream_user_data = user_data;
219 adapter->stream_rtp_cb = rtp_cb;
220 adapter->stream_rtcp_cb = rtcp_cb;
221
222 status = pjmedia_transport_attach(adapter->slave_tp, adapter, rem_addr,
223 rem_rtcp, addr_len, &transport_rtp_cb,
224 &transport_rtcp_cb);
225 if (status != PJ_SUCCESS) {
226 adapter->stream_user_data = NULL;
227 adapter->stream_rtp_cb = NULL;
228 adapter->stream_rtcp_cb = NULL;
229 return status;
230 }
231
232 return PJ_SUCCESS;
233}
234
235/*
236 * detach() is called when the media is terminated, and the stream is
237 * to be disconnected from us.
238 */
239static void transport_detach(pjmedia_transport *tp, void *strm)
240{
241 struct tp_adapter *adapter = (struct tp_adapter*)tp;
242
243 PJ_UNUSED_ARG(strm);
244
245 if (adapter->stream_user_data != NULL) {
246 pjmedia_transport_detach(adapter->slave_tp, adapter);
247 adapter->stream_user_data = NULL;
248 adapter->stream_rtp_cb = NULL;
249 adapter->stream_rtcp_cb = NULL;
250 }
251}
252
253
254/*
255 * send_rtp() is called to send RTP packet. The "pkt" and "size" argument
256 * contain both the RTP header and the payload.
257 */
258static pj_status_t transport_send_rtp( pjmedia_transport *tp,
259 const void *pkt,
260 pj_size_t size)
261{
262 struct tp_adapter *adapter = (struct tp_adapter*)tp;
263
264 /* You may do some processing to the RTP packet here if you want. */
265
266 /* Send the packet using the slave transport */
267 return pjmedia_transport_send_rtp(adapter->slave_tp, pkt, size);
268}
269
270
271/*
272 * send_rtcp() is called to send RTCP packet. The "pkt" and "size" argument
273 * contain the RTCP packet.
274 */
275static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
276 const void *pkt,
277 pj_size_t size)
278{
279 struct tp_adapter *adapter = (struct tp_adapter*)tp;
280
281 /* You may do some processing to the RTCP packet here if you want. */
282
283 /* Send the packet using the slave transport */
284 return pjmedia_transport_send_rtcp(adapter->slave_tp, pkt, size);
285}
286
287
288/*
289 * This is another variant of send_rtcp(), with the alternate destination
290 * address in the argument.
291 */
292static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
293 const pj_sockaddr_t *addr,
294 unsigned addr_len,
295 const void *pkt,
296 pj_size_t size)
297{
298 struct tp_adapter *adapter = (struct tp_adapter*)tp;
299 return pjmedia_transport_send_rtcp2(adapter->slave_tp, addr, addr_len,
300 pkt, size);
301}
302
303/*
304 * The media_create() is called when the transport is about to be used for
305 * a new call.
306 */
307static pj_status_t transport_media_create(pjmedia_transport *tp,
308 pj_pool_t *sdp_pool,
309 unsigned options,
310 const pjmedia_sdp_session *rem_sdp,
311 unsigned media_index)
312{
313 struct tp_adapter *adapter = (struct tp_adapter*)tp;
314
315 /* if "rem_sdp" is not NULL, it means we are UAS. You may do some
316 * inspections on the incoming SDP to verify that the SDP is acceptable
317 * for us. If the SDP is not acceptable, we can reject the SDP by
318 * returning non-PJ_SUCCESS.
319 */
320 if (rem_sdp) {
321 /* Do your stuff.. */
322 }
323
324 /* Once we're done with our initialization, pass the call to the
325 * slave transports to let it do it's own initialization too.
326 */
327 return pjmedia_transport_media_create(adapter->slave_tp, sdp_pool, options,
328 rem_sdp, media_index);
329}
330
331/*
332 * The encode_sdp() is called when we're about to send SDP to remote party,
333 * either as SDP offer or as SDP answer.
334 */
335static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
336 pj_pool_t *sdp_pool,
337 pjmedia_sdp_session *local_sdp,
338 const pjmedia_sdp_session *rem_sdp,
339 unsigned media_index)
340{
341 struct tp_adapter *adapter = (struct tp_adapter*)tp;
342
343 /* If "rem_sdp" is not NULL, it means we're encoding SDP answer. You may
344 * do some more checking on the SDP's once again to make sure that
345 * everything is okay before we send SDP.
346 */
347 if (rem_sdp) {
348 /* Do checking stuffs here.. */
349 }
350
351 /* You may do anything to the local_sdp, e.g. adding new attributes, or
352 * even modifying the SDP if you want.
353 */
354 if (1) {
355 /* Say we add a proprietary attribute here.. */
356 pjmedia_sdp_attr *my_attr;
357
358 my_attr = PJ_POOL_ALLOC_T(sdp_pool, pjmedia_sdp_attr);
359 pj_strdup2(sdp_pool, &my_attr->name, "X-adapter");
360 pj_strdup2(sdp_pool, &my_attr->value, "some value");
361
362 pjmedia_sdp_attr_add(&local_sdp->media[media_index]->attr_count,
363 local_sdp->media[media_index]->attr,
364 my_attr);
365 }
366
367 /* And then pass the call to slave transport to let it encode its
368 * information in the SDP. You may choose to call encode_sdp() to slave
369 * first before adding your custom attributes if you want.
370 */
371 return pjmedia_transport_encode_sdp(adapter->slave_tp, sdp_pool, local_sdp,
372 rem_sdp, media_index);
373}
374
375/*
376 * The media_start() is called once both local and remote SDP have been
377 * negotiated successfully, and the media is ready to start. Here we can start
378 * committing our processing.
379 */
380static pj_status_t transport_media_start(pjmedia_transport *tp,
381 pj_pool_t *pool,
382 const pjmedia_sdp_session *local_sdp,
383 const pjmedia_sdp_session *rem_sdp,
384 unsigned media_index)
385{
386 struct tp_adapter *adapter = (struct tp_adapter*)tp;
387
388 /* Do something.. */
389
390 /* And pass the call to the slave transport */
391 return pjmedia_transport_media_start(adapter->slave_tp, pool, local_sdp,
392 rem_sdp, media_index);
393}
394
395/*
396 * The media_stop() is called when media has been stopped.
397 */
398static pj_status_t transport_media_stop(pjmedia_transport *tp)
399{
400 struct tp_adapter *adapter = (struct tp_adapter*)tp;
401
402 /* Do something.. */
403
404 /* And pass the call to the slave transport */
405 return pjmedia_transport_media_stop(adapter->slave_tp);
406}
407
408/*
409 * simulate_lost() is called to simulate packet lost
410 */
411static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
412 pjmedia_dir dir,
413 unsigned pct_lost)
414{
415 struct tp_adapter *adapter = (struct tp_adapter*)tp;
416 return pjmedia_transport_simulate_lost(adapter->slave_tp, dir, pct_lost);
417}
418
419/*
420 * destroy() is called when the transport is no longer needed.
421 */
422static pj_status_t transport_destroy (pjmedia_transport *tp)
423{
424 struct tp_adapter *adapter = (struct tp_adapter*)tp;
425
426 /* Close the slave transport */
427 if (adapter->del_base) {
428 pjmedia_transport_close(adapter->slave_tp);
429 }
430
431 /* Self destruct.. */
432 pj_pool_release(adapter->pool);
433
434 return PJ_SUCCESS;
435}
436
437
438
439
440