blob: ed5c5192c6bccbcc5ababbf1f67b480216467d52 [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 Welte41d0d842011-09-03 15:33:24 +020038
39
40static PayloadType *payload_type_efr;
41static PayloadType *payload_type_hr;
42static RtpProfile *osmo_pt_profile;
43
44static void *tall_rtp_ctx;
45
46/* malloc integration */
47
48static void *osmo_ortp_malloc(size_t sz)
49{
50 return talloc_size(tall_rtp_ctx, sz);
51}
52
53static void *osmo_ortp_realloc(void *ptr, size_t sz)
54{
55 return talloc_realloc_size(tall_rtp_ctx, ptr, sz);
56}
57
58static void osmo_ortp_free(void *ptr)
59{
60 talloc_free(ptr);
61}
62
63static OrtpMemoryFunctions osmo_ortp_memfn = {
64 .malloc_fun = osmo_ortp_malloc,
65 .realloc_fun = osmo_ortp_realloc,
66 .free_fun = osmo_ortp_free
67};
68
69/* logging */
70
71struct level_map {
72 OrtpLogLevel ortp;
73 int osmo_level;
74};
75static const struct level_map level_map[] = {
76 { ORTP_DEBUG, LOGL_DEBUG },
77 { ORTP_MESSAGE, LOGL_INFO },
78 { ORTP_WARNING, LOGL_NOTICE },
79 { ORTP_ERROR, LOGL_ERROR },
80 { ORTP_FATAL, LOGL_FATAL },
81};
82static int ortp_to_osmo_lvl(OrtpLogLevel lev)
83{
84 int i;
85
86 for (i = 0; i < ARRAY_SIZE(level_map); i++) {
87 if (level_map[i].ortp == lev)
88 return level_map[i].osmo_level;
89 }
90 /* default */
91 return LOGL_ERROR;
92}
93
94static void my_ortp_logfn(OrtpLogLevel lev, const char *fmt,
95 va_list args)
96{
97 osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0,
98 0, fmt, args);
99}
100
101/* ORTP signal callbacks */
102
103static void ortp_sig_cb_ssrc(RtpSession *rs, void *data)
104{
Holger Hans Peter Freytherdf78e922012-01-27 09:58:56 +0100105 fprintf(stderr, "osmo-ortp: ssrc_changed\n");
Harald Welte41d0d842011-09-03 15:33:24 +0200106}
107
108static void ortp_sig_cb_pt(RtpSession *rs, void *data)
109{
Holger Hans Peter Freytherdf78e922012-01-27 09:58:56 +0100110 fprintf(stderr, "osmo-ortp: payload_type_changed\n");
Harald Welte41d0d842011-09-03 15:33:24 +0200111}
112
113static void ortp_sig_cb_net(RtpSession *rs, void *data)
114{
Holger Hans Peter Freytherdf78e922012-01-27 09:58:56 +0100115 fprintf(stderr, "osmo-ortp: network_error\n");
Harald Welte41d0d842011-09-03 15:33:24 +0200116}
117
118static void ortp_sig_cb_ts(RtpSession *rs, void *data)
119{
Holger Hans Peter Freytherdf78e922012-01-27 09:58:56 +0100120 fprintf(stderr, "osmo-rtp: timestamp_jump\n");
Harald Welte41d0d842011-09-03 15:33:24 +0200121}
122
123
Harald Welte9b737df2011-09-07 00:59:11 +0200124/*! \brief poll the socket for incoming data
125 * \param[in] rs the socket to be polled
126 * \returns number of packets received + handed to the rx_cb
127 */
128int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs)
129{
130 mblk_t *mblk;
131
132 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
133 if (mblk) {
134 rtp_get_payload(mblk, &mblk->b_rptr);
135 /* hand into receiver */
136 if (rs->rx_cb)
137 rs->rx_cb(rs, mblk->b_rptr,
138 mblk->b_wptr - mblk->b_rptr);
139 //rs->rx_user_ts += 160;
140 freemsg(mblk);
141 return 1;
142 } else {
143 LOGP(DLMIB, LOGL_INFO, "osmo_rtp_poll(%u): ERROR!\n",
144 rs->rx_user_ts);
145 return 0;
146 }
147}
148
Harald Welte41d0d842011-09-03 15:33:24 +0200149/* Osmo FD callbacks */
150
151static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
152{
153 struct osmo_rtp_socket *rs = fd->data;
154 mblk_t *mblk;
155
156 if (what & BSC_FD_READ) {
Harald Welte9b737df2011-09-07 00:59:11 +0200157 /* in polling mode, we don't want to be called here */
158 if (rs->flags & OSMO_RTP_F_POLL) {
159 fd->when &= ~BSC_FD_READ;
160 return 0;
161 }
Harald Welte41d0d842011-09-03 15:33:24 +0200162 mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
163 if (mblk) {
164 rtp_get_payload(mblk, &mblk->b_rptr);
165 /* hand into receiver */
166 if (rs->rx_cb)
167 rs->rx_cb(rs, mblk->b_rptr,
168 mblk->b_wptr - mblk->b_rptr);
169 freemsg(mblk);
Harald Welte9b737df2011-09-07 00:59:11 +0200170 } else
171 LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n",
172 rs->rx_user_ts);
Harald Welte41d0d842011-09-03 15:33:24 +0200173 rs->rx_user_ts += 160;
174 }
175 /* writing is not queued at the moment, so BSC_FD_WRITE
176 * shouldn't occur */
177 return 0;
178}
179
180static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
181{
182 struct osmo_rtp_socket *rs = fd->data;
183
184 /* We probably don't need this at all, as
185 * rtp_session_recvm_with_ts() will alway also poll the RTCP
186 * file descriptor for new data */
187 return rtp_session_rtcp_recv(rs->sess);
188}
189
190static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
191{
192 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
193 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
194 rs->rtp_bfd.when = rs->rtcp_bfd.when = BSC_FD_READ;
Harald Welte9b737df2011-09-07 00:59:11 +0200195 rs->rtp_bfd.when = rs->rtcp_bfd.when = 0;
Harald Welte41d0d842011-09-03 15:33:24 +0200196 rs->rtp_bfd.data = rs->rtcp_bfd.data = rs;
197 rs->rtp_bfd.cb = osmo_rtp_fd_cb;
198 rs->rtcp_bfd.cb = osmo_rtcp_fd_cb;
199
200 osmo_fd_register(&rs->rtp_bfd);
201 osmo_fd_register(&rs->rtcp_bfd);
202
203 return 0;
204}
205
206static void create_payload_types()
207{
208 PayloadType *pt;
209
210 /* EFR */
211 pt = payload_type_new();
212 pt->type = PAYLOAD_AUDIO_PACKETIZED;
213 pt->clock_rate = 8000;
214 pt->mime_type = "EFR";
215 pt->normal_bitrate = 12200;
216 pt->channels = 1;
217 payload_type_efr = pt;
218
219 /* HR */
220 pt = payload_type_new();
221 pt->type = PAYLOAD_AUDIO_PACKETIZED;
222 pt->clock_rate = 8000;
223 pt->mime_type = "HR";
224 pt->normal_bitrate = 6750; /* FIXME */
225 pt->channels = 1;
226 payload_type_hr = pt;
227
228 /* create a new RTP profile as clone of AV profile */
229 osmo_pt_profile = rtp_profile_clone(&av_profile);
230
231 /* add the GSM specific payload types. They are all dynamically
232 * assigned, but in the Osmocom GSM system we have allocated
233 * them as follows: */
234 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
235 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
236 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
237}
238
239/* public functions */
240
241/*! \brief initialize Osmocom RTP code
Harald Weltefcb1fe82011-09-07 11:51:52 +0200242 * \param[in] ctx default talloc context for library-internal allocations
Harald Welte41d0d842011-09-03 15:33:24 +0200243 */
244void osmo_rtp_init(void *ctx)
245{
246 tall_rtp_ctx = ctx;
247 ortp_set_memory_functions(&osmo_ortp_memfn);
248 ortp_init();
249 ortp_set_log_level_mask(0xffff);
250 ortp_set_log_handler(my_ortp_logfn);
251 create_payload_types();
252}
253
Harald Welte65a50892011-09-08 14:42:58 +0200254int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs,
255 enum osmo_rtp_param param, int val)
256{
257 int rc = 0;
258
259 switch (param) {
260 case OSMO_RTP_P_JITBUF:
Andreas Eversberg41bc6152013-02-14 11:03:26 +0100261 rtp_session_enable_jitter_buffer(rs->sess,
262 (val) ? TRUE : FALSE);
263 if (val)
264 rtp_session_set_jitter_compensation(rs->sess, val);
Harald Welte65a50892011-09-08 14:42:58 +0200265 break;
266#if 0
267 case OSMO_RTP_P_JIT_ADAP:
268 rc = jitter_control_enable_adaptive(rs->sess, val);
269 break;
270#endif
271 default:
272 return -EINVAL;
273 }
274
275 return rc;
276}
277
Harald Weltefcb1fe82011-09-07 11:51:52 +0200278/*! \brief Create a new RTP socket
279 * \param[in] talloc_cxt talloc context for this allocation. NULL for
280 * dafault context
281 * \param[in] flags Flags like OSMO_RTP_F_POLL
282 * \returns pointer to library-allocated \a struct osmo_rtp_socket
283 */
Harald Welte9b737df2011-09-07 00:59:11 +0200284struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
Harald Welte41d0d842011-09-03 15:33:24 +0200285{
286 struct osmo_rtp_socket *rs;
287
288 if (!talloc_ctx)
289 talloc_ctx = tall_rtp_ctx;
290
291 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
292 if (!rs)
293 return NULL;
294
Harald Welte9b737df2011-09-07 00:59:11 +0200295 rs->flags = flags;
Harald Welte41d0d842011-09-03 15:33:24 +0200296 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
297 if (!rs->sess) {
298 talloc_free(rs);
299 return NULL;
300 }
301 rtp_session_set_data(rs->sess, rs);
302 rtp_session_set_profile(rs->sess, osmo_pt_profile);
Harald Welte9b737df2011-09-07 00:59:11 +0200303 rtp_session_set_jitter_compensation(rs->sess, 100);
304 //jitter_control_enable_adaptive(rs->sess, 0);
Harald Welte41d0d842011-09-03 15:33:24 +0200305
306 rtp_session_signal_connect(rs->sess, "ssrc_changed",
307 (RtpCallback) ortp_sig_cb_ssrc,
308 (unsigned long) rs);
309 rtp_session_signal_connect(rs->sess, "payload_type_changed",
310 (RtpCallback) ortp_sig_cb_pt,
311 (unsigned long) rs);
312 rtp_session_signal_connect(rs->sess, "network_error",
313 (RtpCallback) ortp_sig_cb_net,
314 (unsigned long) rs);
315 rtp_session_signal_connect(rs->sess, "timestamp_jump",
316 (RtpCallback) ortp_sig_cb_ts,
317 (unsigned long) rs);
318
319 return rs;
320}
321
Harald Weltefcb1fe82011-09-07 11:51:52 +0200322/*! \brief bind a RTP socket to a local port
323 * \param[in] rs OsmoRTP socket
324 * \param[in] ip hostname/ip as string
325 * \param[in] port UDP port number, -1 for random selection
326 * \returns 0 on success, <0 on error
327 */
Harald Welte41d0d842011-09-03 15:33:24 +0200328int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
329{
330 int rc;
331
332 rc = rtp_session_set_local_addr(rs->sess, ip, port);
333 if (rc < 0)
334 return rc;
335
336 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
337 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
338
339 return 0;
340}
341
Harald Weltefcb1fe82011-09-07 11:51:52 +0200342/*! \brief connect a OsmoRTP socket to a remote port
343 * \param[in] rs OsmoRTP socket
344 * \param[in] ip String representation of remote hostname or IP address
345 * \param[in] port UDP port number to connect to
346 *
347 * If the OsmoRTP socket is not in POLL mode, this function will also
348 * cause the RTP and RTCP file descriptors to be registred with the
349 * libosmocore select() loop integration.
350 *
351 * \returns 0 on success, <0 in case of error
352 */
Harald Welte41d0d842011-09-03 15:33:24 +0200353int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
354{
355 int rc;
356
Harald Welted426d452013-02-09 11:01:19 +0100357 /* enable the use of connect() so later getsockname() will
358 * actually return the IP address that was chosen for the local
359 * sid of the connection */
360 rtp_session_set_connected_mode(rs->sess, 1);
361
Harald Welte41d0d842011-09-03 15:33:24 +0200362 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
363 if (rc < 0)
364 return rc;
365
Harald Welte9b737df2011-09-07 00:59:11 +0200366 if (rs->flags & OSMO_RTP_F_POLL)
367 return rc;
368 else
369 return osmo_rtp_socket_fdreg(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200370}
371
Harald Weltefcb1fe82011-09-07 11:51:52 +0200372/*! \brief Send one RTP frame via a RTP socket
373 * \param[in] rs OsmoRTP socket
374 * \param[in] payload pointer to buffer with RTP payload data
375 * \param[in] payload_len length of \a payload in bytes
376 * \param[in] duration duration in number of RTP clock ticks
377 * \returns 0 on success, <0 in case of error.
378 */
Harald Welte41d0d842011-09-03 15:33:24 +0200379int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
380 unsigned int payload_len, unsigned int duration)
381{
382 mblk_t *mblk;
383 int rc;
384
385 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
386 payload, payload_len);
387 if (!mblk)
388 return -ENOMEM;
389
390 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
391 rs->tx_timestamp);
392 rs->tx_timestamp += duration;
393 if (rc < 0) {
394 /* no need to free() the mblk, as rtp_session_rtp_send()
395 * unconditionally free()s the mblk even in case of
396 * error */
397 return rc;
398 }
399
400 return rc;
401}
402
Harald Weltefcb1fe82011-09-07 11:51:52 +0200403/*! \brief Set the payload type of a RTP socket
404 * \param[in] rs OsmoRTP socket
405 * \param[in] payload_type RTP payload type
406 * \returns 0 on success, < 0 otherwise
407 */
Harald Welte41d0d842011-09-03 15:33:24 +0200408int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
409{
410 int rc;
411
412 rc = rtp_session_set_payload_type(rs->sess, payload_type);
413 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
414
415 return rc;
416}
417
Harald Weltefcb1fe82011-09-07 11:51:52 +0200418/*! \brief completely close the RTP socket and release all resources
419 * \param[in] rs OsmoRTP socket to be released
420 * \returns 0 on success
421 */
Harald Welte41d0d842011-09-03 15:33:24 +0200422int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
423{
424 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
425 osmo_fd_unregister(&rs->rtp_bfd);
426
427 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
428 osmo_fd_unregister(&rs->rtcp_bfd);
429
430 if (rs->sess) {
431 rtp_session_release_sockets(rs->sess);
432 rtp_session_destroy(rs->sess);
433 rs->sess = NULL;
434 }
435
436 talloc_free(rs);
437
438 return 0;
439}
440
Harald Weltefcb1fe82011-09-07 11:51:52 +0200441/*! \brief obtain the locally bound IPv4 address and UDP port
442 * \param[in] rs OsmoRTP socket
443 * \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address
444 * \oaram[out] port Pointer to caller-allocated int for UDP port number
445 * \returns 0 on success, <0 on error, -EIO in case of IPv6 socket
446 */
Harald Welte41d0d842011-09-03 15:33:24 +0200447int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
448 uint32_t *ip, int *port)
449{
450 int rc;
451 struct sockaddr_storage ss;
452 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
453 socklen_t alen = sizeof(ss);
454
455 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
456 if (rc < 0)
457 return rc;
458
459 if (ss.ss_family != AF_INET)
460 return -EIO;
461
462 *ip = ntohl(sin->sin_addr.s_addr);
463 *port = rtp_session_get_local_port(rs->sess);
464
465 return 0;
466}
467
Harald Weltefcb1fe82011-09-07 11:51:52 +0200468/*! \brief obtain the locally bound address and port
469 * \param[in] rs OsmoRTP socket
470 * \param[out] addr caller-allocated char ** to which the string pointer for
471 * the address is stored
472 * \param[out] port caller-allocated int * to which the port number is
473 * stored
474 * \returns 0 on success, <0 in case of error
475 */
Harald Welte41d0d842011-09-03 15:33:24 +0200476int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
477 const char **addr, int *port)
478{
479 int rc;
480 struct sockaddr_storage ss;
481 socklen_t alen = sizeof(ss);
482 static char hostbuf[256];
483
484 memset(hostbuf, 0, sizeof(hostbuf));
485
486 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
487 if (rc < 0)
488 return rc;
489
490 rc = getnameinfo((struct sockaddr *)&ss, alen,
491 hostbuf, sizeof(hostbuf), NULL, 0,
492 NI_NUMERICHOST);
493 if (rc < 0)
494 return rc;
495
496 *port = rtp_session_get_local_port(rs->sess);
497 *addr = hostbuf;
498
499 return 0;
500}
Harald Welte65a50892011-09-08 14:42:58 +0200501
502
503void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs,
504 int subsys, int level,
505 const char *pfx)
506{
507 const rtp_stats_t *stats;
508
509 stats = rtp_session_get_stats(rs->sess);
510 if (!stats)
511 return;
512
513 LOGP(subsys, level, "%sRTP Tx(%"PRIu64" pkts, %"PRIu64" bytes) "
514 "Rx(%"PRIu64" pkts, %"PRIu64" bytes, %"PRIu64" late, "
515 "%"PRIu64" loss, %"PRIu64" qmax)\n",
516 pfx, stats->packet_sent, stats->sent,
517 stats->packet_recv, stats->hw_recv, stats->outoftime,
518 stats->cum_packet_loss, stats->discarded);
519}