blob: 7faca417ee8a8feda18264f6914da94359676b80 [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>
Harald Welte65a50892011-09-08 14:42:58 +020026#include <inttypes.h>
Harald Welte41d0d842011-09-03 15:33:24 +020027#include <netdb.h>
28
29#include <osmocom/core/logging.h>
30#include <osmocom/core/talloc.h>
31#include <osmocom/core/utils.h>
32#include <osmocom/core/select.h>
33#include <osmocom/trau/osmo_ortp.h>
34
35#include <ortp/ortp.h>
Harald Welte65a50892011-09-08 14:42:58 +020036#include <ortp/rtp.h>
Harald Welte0b5ffc12011-10-22 15:58:02 +020037#include <ortp/port.h>
Harald Weltee7a3f432013-02-19 13:35:22 +010038#include <ortp/rtpsession.h>
Harald Welte41d0d842011-09-03 15:33:24 +020039
40
41static PayloadType *payload_type_efr;
42static PayloadType *payload_type_hr;
43static RtpProfile *osmo_pt_profile;
44
45static void *tall_rtp_ctx;
46
47/* malloc integration */
48
49static void *osmo_ortp_malloc(size_t sz)
50{
51 return talloc_size(tall_rtp_ctx, sz);
52}
53
54static void *osmo_ortp_realloc(void *ptr, size_t sz)
55{
56 return talloc_realloc_size(tall_rtp_ctx, ptr, sz);
57}
58
59static void osmo_ortp_free(void *ptr)
60{
61 talloc_free(ptr);
62}
63
64static OrtpMemoryFunctions osmo_ortp_memfn = {
65 .malloc_fun = osmo_ortp_malloc,
66 .realloc_fun = osmo_ortp_realloc,
67 .free_fun = osmo_ortp_free
68};
69
70/* logging */
71
72struct level_map {
73 OrtpLogLevel ortp;
74 int osmo_level;
75};
76static const struct level_map level_map[] = {
77 { ORTP_DEBUG, LOGL_DEBUG },
78 { ORTP_MESSAGE, LOGL_INFO },
79 { ORTP_WARNING, LOGL_NOTICE },
80 { ORTP_ERROR, LOGL_ERROR },
81 { ORTP_FATAL, LOGL_FATAL },
82};
83static int ortp_to_osmo_lvl(OrtpLogLevel lev)
84{
85 int i;
86
87 for (i = 0; i < ARRAY_SIZE(level_map); i++) {
88 if (level_map[i].ortp == lev)
89 return level_map[i].osmo_level;
90 }
91 /* default */
92 return LOGL_ERROR;
93}
94
95static void my_ortp_logfn(OrtpLogLevel lev, const char *fmt,
96 va_list args)
97{
98 osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0,
99 0, fmt, args);
100}
101
102/* ORTP signal callbacks */
103
104static void ortp_sig_cb_ssrc(RtpSession *rs, void *data)
105{
Harald Weltee7a3f432013-02-19 13:35:22 +0100106 int port = rtp_session_get_local_port(rs);
107#if 0 /* post 0.20.0 ORTP has this function */
108 uint32_t ssrc = rtp_session_get_recv_ssrc(rs);
109#else
110 uint32_t ssrc = rs->rcv.ssrc;
111#endif
112
113 LOGP(DLMIB, LOGL_INFO,
114 "osmo-ortp(%d): ssrc_changed to 0x%08x\n", port, ssrc);
Harald Welte41d0d842011-09-03 15:33:24 +0200115}
116
117static void ortp_sig_cb_pt(RtpSession *rs, void *data)
118{
Harald Weltee7a3f432013-02-19 13:35:22 +0100119 int port = rtp_session_get_local_port(rs);
120 int pt = rtp_session_get_recv_payload_type(rs);
121
122 LOGP(DLMIB, LOGL_NOTICE,
123 "osmo-ortp(%d): payload_type_changed to 0x%02x\n", port, pt);
Harald Welte41d0d842011-09-03 15:33:24 +0200124}
125
126static void ortp_sig_cb_net(RtpSession *rs, void *data)
127{
Harald Weltee7a3f432013-02-19 13:35:22 +0100128 int port = rtp_session_get_local_port(rs);
129
130 LOGP(DLMIB, LOGL_ERROR,
131 "osmo-ortp(%d): network_error\n", port);
Harald Welte41d0d842011-09-03 15:33:24 +0200132}
133
134static void ortp_sig_cb_ts(RtpSession *rs, void *data)
135{
Harald Weltee7a3f432013-02-19 13:35:22 +0100136 int port = rtp_session_get_local_port(rs);
137 uint32_t ts = rtp_session_get_current_recv_ts(rs);
138
139 LOGP(DLMIB, LOGL_NOTICE,
140 "osmo-ortp(%d): timestamp_jump, new TS %d\n", port, ts);
Harald Welte41d0d842011-09-03 15:33:24 +0200141}
142
143
Harald Welte9b737df2011-09-07 00:59:11 +0200144/*! \brief poll the socket for incoming data
145 * \param[in] rs the socket to be polled
146 * \returns number of packets received + handed to the rx_cb
147 */
148int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs)
149{
150 mblk_t *mblk;
151
152 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
153 if (mblk) {
154 rtp_get_payload(mblk, &mblk->b_rptr);
155 /* hand into receiver */
156 if (rs->rx_cb)
157 rs->rx_cb(rs, mblk->b_rptr,
158 mblk->b_wptr - mblk->b_rptr);
159 //rs->rx_user_ts += 160;
160 freemsg(mblk);
161 return 1;
162 } else {
163 LOGP(DLMIB, LOGL_INFO, "osmo_rtp_poll(%u): ERROR!\n",
164 rs->rx_user_ts);
165 return 0;
166 }
167}
168
Harald Welte41d0d842011-09-03 15:33:24 +0200169/* Osmo FD callbacks */
170
171static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
172{
173 struct osmo_rtp_socket *rs = fd->data;
174 mblk_t *mblk;
175
176 if (what & BSC_FD_READ) {
Harald Welte9b737df2011-09-07 00:59:11 +0200177 /* in polling mode, we don't want to be called here */
178 if (rs->flags & OSMO_RTP_F_POLL) {
179 fd->when &= ~BSC_FD_READ;
180 return 0;
181 }
Harald Welte41d0d842011-09-03 15:33:24 +0200182 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
183 if (mblk) {
184 rtp_get_payload(mblk, &mblk->b_rptr);
185 /* hand into receiver */
186 if (rs->rx_cb)
187 rs->rx_cb(rs, mblk->b_rptr,
188 mblk->b_wptr - mblk->b_rptr);
189 freemsg(mblk);
Harald Welte9b737df2011-09-07 00:59:11 +0200190 } else
191 LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n",
192 rs->rx_user_ts);
Harald Welte41d0d842011-09-03 15:33:24 +0200193 rs->rx_user_ts += 160;
194 }
195 /* writing is not queued at the moment, so BSC_FD_WRITE
196 * shouldn't occur */
197 return 0;
198}
199
200static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
201{
202 struct osmo_rtp_socket *rs = fd->data;
203
204 /* We probably don't need this at all, as
205 * rtp_session_recvm_with_ts() will alway also poll the RTCP
206 * file descriptor for new data */
207 return rtp_session_rtcp_recv(rs->sess);
208}
209
210static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
211{
212 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
213 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
214 rs->rtp_bfd.when = rs->rtcp_bfd.when = BSC_FD_READ;
Harald Welte9b737df2011-09-07 00:59:11 +0200215 rs->rtp_bfd.when = rs->rtcp_bfd.when = 0;
Harald Welte41d0d842011-09-03 15:33:24 +0200216 rs->rtp_bfd.data = rs->rtcp_bfd.data = rs;
217 rs->rtp_bfd.cb = osmo_rtp_fd_cb;
218 rs->rtcp_bfd.cb = osmo_rtcp_fd_cb;
219
220 osmo_fd_register(&rs->rtp_bfd);
221 osmo_fd_register(&rs->rtcp_bfd);
222
223 return 0;
224}
225
226static void create_payload_types()
227{
228 PayloadType *pt;
229
230 /* EFR */
231 pt = payload_type_new();
232 pt->type = PAYLOAD_AUDIO_PACKETIZED;
233 pt->clock_rate = 8000;
234 pt->mime_type = "EFR";
235 pt->normal_bitrate = 12200;
236 pt->channels = 1;
237 payload_type_efr = pt;
238
239 /* HR */
240 pt = payload_type_new();
241 pt->type = PAYLOAD_AUDIO_PACKETIZED;
242 pt->clock_rate = 8000;
243 pt->mime_type = "HR";
244 pt->normal_bitrate = 6750; /* FIXME */
245 pt->channels = 1;
246 payload_type_hr = pt;
247
248 /* create a new RTP profile as clone of AV profile */
249 osmo_pt_profile = rtp_profile_clone(&av_profile);
250
251 /* add the GSM specific payload types. They are all dynamically
252 * assigned, but in the Osmocom GSM system we have allocated
253 * them as follows: */
254 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
255 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
256 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
257}
258
259/* public functions */
260
261/*! \brief initialize Osmocom RTP code
Harald Weltefcb1fe82011-09-07 11:51:52 +0200262 * \param[in] ctx default talloc context for library-internal allocations
Harald Welte41d0d842011-09-03 15:33:24 +0200263 */
264void osmo_rtp_init(void *ctx)
265{
266 tall_rtp_ctx = ctx;
267 ortp_set_memory_functions(&osmo_ortp_memfn);
268 ortp_init();
269 ortp_set_log_level_mask(0xffff);
270 ortp_set_log_handler(my_ortp_logfn);
271 create_payload_types();
272}
273
Harald Welte65a50892011-09-08 14:42:58 +0200274int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs,
275 enum osmo_rtp_param param, int val)
276{
277 int rc = 0;
278
279 switch (param) {
280 case OSMO_RTP_P_JITBUF:
Andreas Eversberg41bc6152013-02-14 11:03:26 +0100281 rtp_session_enable_jitter_buffer(rs->sess,
282 (val) ? TRUE : FALSE);
283 if (val)
284 rtp_session_set_jitter_compensation(rs->sess, val);
Harald Welte65a50892011-09-08 14:42:58 +0200285 break;
286#if 0
287 case OSMO_RTP_P_JIT_ADAP:
288 rc = jitter_control_enable_adaptive(rs->sess, val);
289 break;
290#endif
291 default:
292 return -EINVAL;
293 }
294
295 return rc;
296}
297
Harald Weltefcb1fe82011-09-07 11:51:52 +0200298/*! \brief Create a new RTP socket
299 * \param[in] talloc_cxt talloc context for this allocation. NULL for
300 * dafault context
301 * \param[in] flags Flags like OSMO_RTP_F_POLL
302 * \returns pointer to library-allocated \a struct osmo_rtp_socket
303 */
Harald Welte9b737df2011-09-07 00:59:11 +0200304struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
Harald Welte41d0d842011-09-03 15:33:24 +0200305{
306 struct osmo_rtp_socket *rs;
307
308 if (!talloc_ctx)
309 talloc_ctx = tall_rtp_ctx;
310
311 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
312 if (!rs)
313 return NULL;
314
Harald Welte9b737df2011-09-07 00:59:11 +0200315 rs->flags = flags;
Harald Welte41d0d842011-09-03 15:33:24 +0200316 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
317 if (!rs->sess) {
318 talloc_free(rs);
319 return NULL;
320 }
321 rtp_session_set_data(rs->sess, rs);
322 rtp_session_set_profile(rs->sess, osmo_pt_profile);
Harald Welte9b737df2011-09-07 00:59:11 +0200323 rtp_session_set_jitter_compensation(rs->sess, 100);
324 //jitter_control_enable_adaptive(rs->sess, 0);
Harald Welte41d0d842011-09-03 15:33:24 +0200325
326 rtp_session_signal_connect(rs->sess, "ssrc_changed",
327 (RtpCallback) ortp_sig_cb_ssrc,
328 (unsigned long) rs);
329 rtp_session_signal_connect(rs->sess, "payload_type_changed",
330 (RtpCallback) ortp_sig_cb_pt,
331 (unsigned long) rs);
332 rtp_session_signal_connect(rs->sess, "network_error",
333 (RtpCallback) ortp_sig_cb_net,
334 (unsigned long) rs);
335 rtp_session_signal_connect(rs->sess, "timestamp_jump",
336 (RtpCallback) ortp_sig_cb_ts,
337 (unsigned long) rs);
338
339 return rs;
340}
341
Harald Weltefcb1fe82011-09-07 11:51:52 +0200342/*! \brief bind a RTP socket to a local port
343 * \param[in] rs OsmoRTP socket
344 * \param[in] ip hostname/ip as string
345 * \param[in] port UDP port number, -1 for random selection
346 * \returns 0 on success, <0 on error
347 */
Harald Welte41d0d842011-09-03 15:33:24 +0200348int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
349{
350 int rc;
351
352 rc = rtp_session_set_local_addr(rs->sess, ip, port);
353 if (rc < 0)
354 return rc;
355
356 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
357 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
358
359 return 0;
360}
361
Harald Weltefcb1fe82011-09-07 11:51:52 +0200362/*! \brief connect a OsmoRTP socket to a remote port
363 * \param[in] rs OsmoRTP socket
364 * \param[in] ip String representation of remote hostname or IP address
365 * \param[in] port UDP port number to connect to
366 *
367 * If the OsmoRTP socket is not in POLL mode, this function will also
368 * cause the RTP and RTCP file descriptors to be registred with the
369 * libosmocore select() loop integration.
370 *
371 * \returns 0 on success, <0 in case of error
372 */
Harald Welte41d0d842011-09-03 15:33:24 +0200373int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
374{
375 int rc;
376
Harald Welted426d452013-02-09 11:01:19 +0100377 /* enable the use of connect() so later getsockname() will
378 * actually return the IP address that was chosen for the local
379 * sid of the connection */
380 rtp_session_set_connected_mode(rs->sess, 1);
381
Harald Welte41d0d842011-09-03 15:33:24 +0200382 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
383 if (rc < 0)
384 return rc;
385
Harald Welte9b737df2011-09-07 00:59:11 +0200386 if (rs->flags & OSMO_RTP_F_POLL)
387 return rc;
388 else
389 return osmo_rtp_socket_fdreg(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200390}
391
Harald Weltefcb1fe82011-09-07 11:51:52 +0200392/*! \brief Send one RTP frame via a RTP socket
393 * \param[in] rs OsmoRTP socket
394 * \param[in] payload pointer to buffer with RTP payload data
395 * \param[in] payload_len length of \a payload in bytes
396 * \param[in] duration duration in number of RTP clock ticks
397 * \returns 0 on success, <0 in case of error.
398 */
Harald Welte41d0d842011-09-03 15:33:24 +0200399int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
400 unsigned int payload_len, unsigned int duration)
401{
402 mblk_t *mblk;
403 int rc;
404
405 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
406 payload, payload_len);
407 if (!mblk)
408 return -ENOMEM;
409
410 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
411 rs->tx_timestamp);
412 rs->tx_timestamp += duration;
413 if (rc < 0) {
414 /* no need to free() the mblk, as rtp_session_rtp_send()
415 * unconditionally free()s the mblk even in case of
416 * error */
417 return rc;
418 }
419
420 return rc;
421}
422
Harald Weltefcb1fe82011-09-07 11:51:52 +0200423/*! \brief Set the payload type of a RTP socket
424 * \param[in] rs OsmoRTP socket
425 * \param[in] payload_type RTP payload type
426 * \returns 0 on success, < 0 otherwise
427 */
Harald Welte41d0d842011-09-03 15:33:24 +0200428int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
429{
430 int rc;
431
432 rc = rtp_session_set_payload_type(rs->sess, payload_type);
433 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
434
435 return rc;
436}
437
Harald Weltefcb1fe82011-09-07 11:51:52 +0200438/*! \brief completely close the RTP socket and release all resources
439 * \param[in] rs OsmoRTP socket to be released
440 * \returns 0 on success
441 */
Harald Welte41d0d842011-09-03 15:33:24 +0200442int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
443{
444 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
445 osmo_fd_unregister(&rs->rtp_bfd);
446
447 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
448 osmo_fd_unregister(&rs->rtcp_bfd);
449
450 if (rs->sess) {
451 rtp_session_release_sockets(rs->sess);
452 rtp_session_destroy(rs->sess);
453 rs->sess = NULL;
454 }
455
456 talloc_free(rs);
457
458 return 0;
459}
460
Harald Weltefcb1fe82011-09-07 11:51:52 +0200461/*! \brief obtain the locally bound IPv4 address and UDP port
462 * \param[in] rs OsmoRTP socket
463 * \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address
464 * \oaram[out] port Pointer to caller-allocated int for UDP port number
465 * \returns 0 on success, <0 on error, -EIO in case of IPv6 socket
466 */
Harald Welte41d0d842011-09-03 15:33:24 +0200467int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
468 uint32_t *ip, int *port)
469{
470 int rc;
471 struct sockaddr_storage ss;
472 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
473 socklen_t alen = sizeof(ss);
474
475 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
476 if (rc < 0)
477 return rc;
478
479 if (ss.ss_family != AF_INET)
480 return -EIO;
481
482 *ip = ntohl(sin->sin_addr.s_addr);
483 *port = rtp_session_get_local_port(rs->sess);
484
485 return 0;
486}
487
Harald Weltefcb1fe82011-09-07 11:51:52 +0200488/*! \brief obtain the locally bound address and port
489 * \param[in] rs OsmoRTP socket
490 * \param[out] addr caller-allocated char ** to which the string pointer for
491 * the address is stored
492 * \param[out] port caller-allocated int * to which the port number is
493 * stored
494 * \returns 0 on success, <0 in case of error
495 */
Harald Welte41d0d842011-09-03 15:33:24 +0200496int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
497 const char **addr, int *port)
498{
499 int rc;
500 struct sockaddr_storage ss;
501 socklen_t alen = sizeof(ss);
502 static char hostbuf[256];
503
504 memset(hostbuf, 0, sizeof(hostbuf));
505
506 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
507 if (rc < 0)
508 return rc;
509
510 rc = getnameinfo((struct sockaddr *)&ss, alen,
511 hostbuf, sizeof(hostbuf), NULL, 0,
512 NI_NUMERICHOST);
513 if (rc < 0)
514 return rc;
515
516 *port = rtp_session_get_local_port(rs->sess);
517 *addr = hostbuf;
518
519 return 0;
520}
Harald Welte65a50892011-09-08 14:42:58 +0200521
522
523void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs,
524 int subsys, int level,
525 const char *pfx)
526{
527 const rtp_stats_t *stats;
528
529 stats = rtp_session_get_stats(rs->sess);
530 if (!stats)
531 return;
532
533 LOGP(subsys, level, "%sRTP Tx(%"PRIu64" pkts, %"PRIu64" bytes) "
534 "Rx(%"PRIu64" pkts, %"PRIu64" bytes, %"PRIu64" late, "
535 "%"PRIu64" loss, %"PRIu64" qmax)\n",
536 pfx, stats->packet_sent, stats->sent,
537 stats->packet_recv, stats->hw_recv, stats->outoftime,
538 stats->cum_packet_loss, stats->discarded);
539}