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