blob: cac2508920eeb4ce92f002bd4d8641deedd4b191 [file] [log] [blame]
Harald Welte41d0d842011-09-03 15:33:24 +02001/* (C) 2011 by Harald Welte <laforge@gnumonks.org>
2 * (C) 2011 by On-Waves e.h.f
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21/*! \file osmo_ortp.c
22 * \brief Integration of libortp into osmocom framework (select, logging)
23 */
24
25#include <stdint.h>
26#include <netdb.h>
27
28#include <osmocom/core/logging.h>
29#include <osmocom/core/talloc.h>
30#include <osmocom/core/utils.h>
31#include <osmocom/core/select.h>
32#include <osmocom/trau/osmo_ortp.h>
33
34#include <ortp/ortp.h>
35
36
37static PayloadType *payload_type_efr;
38static PayloadType *payload_type_hr;
39static RtpProfile *osmo_pt_profile;
40
41static void *tall_rtp_ctx;
42
43/* malloc integration */
44
45static void *osmo_ortp_malloc(size_t sz)
46{
47 return talloc_size(tall_rtp_ctx, sz);
48}
49
50static void *osmo_ortp_realloc(void *ptr, size_t sz)
51{
52 return talloc_realloc_size(tall_rtp_ctx, ptr, sz);
53}
54
55static void osmo_ortp_free(void *ptr)
56{
57 talloc_free(ptr);
58}
59
60static OrtpMemoryFunctions osmo_ortp_memfn = {
61 .malloc_fun = osmo_ortp_malloc,
62 .realloc_fun = osmo_ortp_realloc,
63 .free_fun = osmo_ortp_free
64};
65
66/* logging */
67
68struct level_map {
69 OrtpLogLevel ortp;
70 int osmo_level;
71};
72static const struct level_map level_map[] = {
73 { ORTP_DEBUG, LOGL_DEBUG },
74 { ORTP_MESSAGE, LOGL_INFO },
75 { ORTP_WARNING, LOGL_NOTICE },
76 { ORTP_ERROR, LOGL_ERROR },
77 { ORTP_FATAL, LOGL_FATAL },
78};
79static int ortp_to_osmo_lvl(OrtpLogLevel lev)
80{
81 int i;
82
83 for (i = 0; i < ARRAY_SIZE(level_map); i++) {
84 if (level_map[i].ortp == lev)
85 return level_map[i].osmo_level;
86 }
87 /* default */
88 return LOGL_ERROR;
89}
90
91static void my_ortp_logfn(OrtpLogLevel lev, const char *fmt,
92 va_list args)
93{
94 osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0,
95 0, fmt, args);
96}
97
98/* ORTP signal callbacks */
99
100static void ortp_sig_cb_ssrc(RtpSession *rs, void *data)
101{
102 fprintf(stderr, "ssrc_changed\n");
103}
104
105static void ortp_sig_cb_pt(RtpSession *rs, void *data)
106{
107 fprintf(stderr, "payload_type_changed\n");
108}
109
110static void ortp_sig_cb_net(RtpSession *rs, void *data)
111{
112 fprintf(stderr, "network_error\n");
113}
114
115static void ortp_sig_cb_ts(RtpSession *rs, void *data)
116{
117 fprintf(stderr, "timestamp_jump\n");
118}
119
120
Harald Welte9b737df2011-09-07 00:59:11 +0200121/*! \brief poll the socket for incoming data
122 * \param[in] rs the socket to be polled
123 * \returns number of packets received + handed to the rx_cb
124 */
125int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs)
126{
127 mblk_t *mblk;
128
129 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
130 if (mblk) {
131 rtp_get_payload(mblk, &mblk->b_rptr);
132 /* hand into receiver */
133 if (rs->rx_cb)
134 rs->rx_cb(rs, mblk->b_rptr,
135 mblk->b_wptr - mblk->b_rptr);
136 //rs->rx_user_ts += 160;
137 freemsg(mblk);
138 return 1;
139 } else {
140 LOGP(DLMIB, LOGL_INFO, "osmo_rtp_poll(%u): ERROR!\n",
141 rs->rx_user_ts);
142 return 0;
143 }
144}
145
Harald Welte41d0d842011-09-03 15:33:24 +0200146/* Osmo FD callbacks */
147
148static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
149{
150 struct osmo_rtp_socket *rs = fd->data;
151 mblk_t *mblk;
152
153 if (what & BSC_FD_READ) {
Harald Welte9b737df2011-09-07 00:59:11 +0200154 /* in polling mode, we don't want to be called here */
155 if (rs->flags & OSMO_RTP_F_POLL) {
156 fd->when &= ~BSC_FD_READ;
157 return 0;
158 }
Harald Welte41d0d842011-09-03 15:33:24 +0200159 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
160 if (mblk) {
161 rtp_get_payload(mblk, &mblk->b_rptr);
162 /* hand into receiver */
163 if (rs->rx_cb)
164 rs->rx_cb(rs, mblk->b_rptr,
165 mblk->b_wptr - mblk->b_rptr);
166 freemsg(mblk);
Harald Welte9b737df2011-09-07 00:59:11 +0200167 } else
168 LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n",
169 rs->rx_user_ts);
Harald Welte41d0d842011-09-03 15:33:24 +0200170 rs->rx_user_ts += 160;
171 }
172 /* writing is not queued at the moment, so BSC_FD_WRITE
173 * shouldn't occur */
174 return 0;
175}
176
177static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
178{
179 struct osmo_rtp_socket *rs = fd->data;
180
181 /* We probably don't need this at all, as
182 * rtp_session_recvm_with_ts() will alway also poll the RTCP
183 * file descriptor for new data */
184 return rtp_session_rtcp_recv(rs->sess);
185}
186
187static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
188{
189 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
190 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
191 rs->rtp_bfd.when = rs->rtcp_bfd.when = BSC_FD_READ;
Harald Welte9b737df2011-09-07 00:59:11 +0200192 rs->rtp_bfd.when = rs->rtcp_bfd.when = 0;
Harald Welte41d0d842011-09-03 15:33:24 +0200193 rs->rtp_bfd.data = rs->rtcp_bfd.data = rs;
194 rs->rtp_bfd.cb = osmo_rtp_fd_cb;
195 rs->rtcp_bfd.cb = osmo_rtcp_fd_cb;
196
197 osmo_fd_register(&rs->rtp_bfd);
198 osmo_fd_register(&rs->rtcp_bfd);
199
200 return 0;
201}
202
203static void create_payload_types()
204{
205 PayloadType *pt;
206
207 /* EFR */
208 pt = payload_type_new();
209 pt->type = PAYLOAD_AUDIO_PACKETIZED;
210 pt->clock_rate = 8000;
211 pt->mime_type = "EFR";
212 pt->normal_bitrate = 12200;
213 pt->channels = 1;
214 payload_type_efr = pt;
215
216 /* HR */
217 pt = payload_type_new();
218 pt->type = PAYLOAD_AUDIO_PACKETIZED;
219 pt->clock_rate = 8000;
220 pt->mime_type = "HR";
221 pt->normal_bitrate = 6750; /* FIXME */
222 pt->channels = 1;
223 payload_type_hr = pt;
224
225 /* create a new RTP profile as clone of AV profile */
226 osmo_pt_profile = rtp_profile_clone(&av_profile);
227
228 /* add the GSM specific payload types. They are all dynamically
229 * assigned, but in the Osmocom GSM system we have allocated
230 * them as follows: */
231 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
232 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
233 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
234}
235
236/* public functions */
237
238/*! \brief initialize Osmocom RTP code
Harald Weltefcb1fe82011-09-07 11:51:52 +0200239 * \param[in] ctx default talloc context for library-internal allocations
Harald Welte41d0d842011-09-03 15:33:24 +0200240 */
241void osmo_rtp_init(void *ctx)
242{
243 tall_rtp_ctx = ctx;
244 ortp_set_memory_functions(&osmo_ortp_memfn);
245 ortp_init();
246 ortp_set_log_level_mask(0xffff);
247 ortp_set_log_handler(my_ortp_logfn);
248 create_payload_types();
249}
250
Harald Weltefcb1fe82011-09-07 11:51:52 +0200251/*! \brief Create a new RTP socket
252 * \param[in] talloc_cxt talloc context for this allocation. NULL for
253 * dafault context
254 * \param[in] flags Flags like OSMO_RTP_F_POLL
255 * \returns pointer to library-allocated \a struct osmo_rtp_socket
256 */
Harald Welte9b737df2011-09-07 00:59:11 +0200257struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
Harald Welte41d0d842011-09-03 15:33:24 +0200258{
259 struct osmo_rtp_socket *rs;
260
261 if (!talloc_ctx)
262 talloc_ctx = tall_rtp_ctx;
263
264 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
265 if (!rs)
266 return NULL;
267
Harald Welte9b737df2011-09-07 00:59:11 +0200268 rs->flags = flags;
Harald Welte41d0d842011-09-03 15:33:24 +0200269 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
270 if (!rs->sess) {
271 talloc_free(rs);
272 return NULL;
273 }
274 rtp_session_set_data(rs->sess, rs);
275 rtp_session_set_profile(rs->sess, osmo_pt_profile);
Harald Welte9b737df2011-09-07 00:59:11 +0200276 rtp_session_set_jitter_compensation(rs->sess, 100);
277 //jitter_control_enable_adaptive(rs->sess, 0);
Harald Welte41d0d842011-09-03 15:33:24 +0200278
279 rtp_session_signal_connect(rs->sess, "ssrc_changed",
280 (RtpCallback) ortp_sig_cb_ssrc,
281 (unsigned long) rs);
282 rtp_session_signal_connect(rs->sess, "payload_type_changed",
283 (RtpCallback) ortp_sig_cb_pt,
284 (unsigned long) rs);
285 rtp_session_signal_connect(rs->sess, "network_error",
286 (RtpCallback) ortp_sig_cb_net,
287 (unsigned long) rs);
288 rtp_session_signal_connect(rs->sess, "timestamp_jump",
289 (RtpCallback) ortp_sig_cb_ts,
290 (unsigned long) rs);
291
292 return rs;
293}
294
Harald Weltefcb1fe82011-09-07 11:51:52 +0200295/*! \brief bind a RTP socket to a local port
296 * \param[in] rs OsmoRTP socket
297 * \param[in] ip hostname/ip as string
298 * \param[in] port UDP port number, -1 for random selection
299 * \returns 0 on success, <0 on error
300 */
Harald Welte41d0d842011-09-03 15:33:24 +0200301int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
302{
303 int rc;
304
305 rc = rtp_session_set_local_addr(rs->sess, ip, port);
306 if (rc < 0)
307 return rc;
308
309 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
310 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
311
312 return 0;
313}
314
Harald Weltefcb1fe82011-09-07 11:51:52 +0200315/*! \brief connect a OsmoRTP socket to a remote port
316 * \param[in] rs OsmoRTP socket
317 * \param[in] ip String representation of remote hostname or IP address
318 * \param[in] port UDP port number to connect to
319 *
320 * If the OsmoRTP socket is not in POLL mode, this function will also
321 * cause the RTP and RTCP file descriptors to be registred with the
322 * libosmocore select() loop integration.
323 *
324 * \returns 0 on success, <0 in case of error
325 */
Harald Welte41d0d842011-09-03 15:33:24 +0200326int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
327{
328 int rc;
329
330 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
331 if (rc < 0)
332 return rc;
333
Harald Welte9b737df2011-09-07 00:59:11 +0200334 if (rs->flags & OSMO_RTP_F_POLL)
335 return rc;
336 else
337 return osmo_rtp_socket_fdreg(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200338}
339
Harald Weltefcb1fe82011-09-07 11:51:52 +0200340/*! \brief Send one RTP frame via a RTP socket
341 * \param[in] rs OsmoRTP socket
342 * \param[in] payload pointer to buffer with RTP payload data
343 * \param[in] payload_len length of \a payload in bytes
344 * \param[in] duration duration in number of RTP clock ticks
345 * \returns 0 on success, <0 in case of error.
346 */
Harald Welte41d0d842011-09-03 15:33:24 +0200347int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
348 unsigned int payload_len, unsigned int duration)
349{
350 mblk_t *mblk;
351 int rc;
352
353 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
354 payload, payload_len);
355 if (!mblk)
356 return -ENOMEM;
357
358 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
359 rs->tx_timestamp);
360 rs->tx_timestamp += duration;
361 if (rc < 0) {
362 /* no need to free() the mblk, as rtp_session_rtp_send()
363 * unconditionally free()s the mblk even in case of
364 * error */
365 return rc;
366 }
367
368 return rc;
369}
370
Harald Weltefcb1fe82011-09-07 11:51:52 +0200371/*! \brief Set the payload type of a RTP socket
372 * \param[in] rs OsmoRTP socket
373 * \param[in] payload_type RTP payload type
374 * \returns 0 on success, < 0 otherwise
375 */
Harald Welte41d0d842011-09-03 15:33:24 +0200376int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
377{
378 int rc;
379
380 rc = rtp_session_set_payload_type(rs->sess, payload_type);
381 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
382
383 return rc;
384}
385
Harald Weltefcb1fe82011-09-07 11:51:52 +0200386/*! \brief completely close the RTP socket and release all resources
387 * \param[in] rs OsmoRTP socket to be released
388 * \returns 0 on success
389 */
Harald Welte41d0d842011-09-03 15:33:24 +0200390int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
391{
392 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
393 osmo_fd_unregister(&rs->rtp_bfd);
394
395 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
396 osmo_fd_unregister(&rs->rtcp_bfd);
397
398 if (rs->sess) {
399 rtp_session_release_sockets(rs->sess);
400 rtp_session_destroy(rs->sess);
401 rs->sess = NULL;
402 }
403
404 talloc_free(rs);
405
406 return 0;
407}
408
Harald Weltefcb1fe82011-09-07 11:51:52 +0200409/*! \brief obtain the locally bound IPv4 address and UDP port
410 * \param[in] rs OsmoRTP socket
411 * \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address
412 * \oaram[out] port Pointer to caller-allocated int for UDP port number
413 * \returns 0 on success, <0 on error, -EIO in case of IPv6 socket
414 */
Harald Welte41d0d842011-09-03 15:33:24 +0200415int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
416 uint32_t *ip, int *port)
417{
418 int rc;
419 struct sockaddr_storage ss;
420 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
421 socklen_t alen = sizeof(ss);
422
423 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
424 if (rc < 0)
425 return rc;
426
427 if (ss.ss_family != AF_INET)
428 return -EIO;
429
430 *ip = ntohl(sin->sin_addr.s_addr);
431 *port = rtp_session_get_local_port(rs->sess);
432
433 return 0;
434}
435
Harald Weltefcb1fe82011-09-07 11:51:52 +0200436/*! \brief obtain the locally bound address and port
437 * \param[in] rs OsmoRTP socket
438 * \param[out] addr caller-allocated char ** to which the string pointer for
439 * the address is stored
440 * \param[out] port caller-allocated int * to which the port number is
441 * stored
442 * \returns 0 on success, <0 in case of error
443 */
Harald Welte41d0d842011-09-03 15:33:24 +0200444int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
445 const char **addr, int *port)
446{
447 int rc;
448 struct sockaddr_storage ss;
449 socklen_t alen = sizeof(ss);
450 static char hostbuf[256];
451
452 memset(hostbuf, 0, sizeof(hostbuf));
453
454 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
455 if (rc < 0)
456 return rc;
457
458 rc = getnameinfo((struct sockaddr *)&ss, alen,
459 hostbuf, sizeof(hostbuf), NULL, 0,
460 NI_NUMERICHOST);
461 if (rc < 0)
462 return rc;
463
464 *port = rtp_session_get_local_port(rs->sess);
465 *addr = hostbuf;
466
467 return 0;
468}