blob: b6818883c1e399d830c9686ea2f77e17a9928b30 [file] [log] [blame]
Harald Welte14277e32021-04-29 21:38:25 +02001/* (C) 2011-2021 by Harald Welte <laforge@gnumonks.org>
Harald Welte41d0d842011-09-03 15:33:24 +02002 * (C) 2011 by On-Waves e.h.f
3 * All Rights Reserved
4 *
Harald Welte323d39d2017-11-13 01:09:21 +09005 * SPDX-License-Identifier: GPL-2.0+
6 *
Harald Welte41d0d842011-09-03 15:33:24 +02007 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
Harald Welte41d0d842011-09-03 15:33:24 +020017 */
18
19/*! \file osmo_ortp.c
20 * \brief Integration of libortp into osmocom framework (select, logging)
21 */
22
23#include <stdint.h>
Max73b9bc72016-05-18 15:52:37 +020024#include <stdbool.h>
Harald Welte65a50892011-09-08 14:42:58 +020025#include <inttypes.h>
Harald Welte41d0d842011-09-03 15:33:24 +020026#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>
Harald Welte14277e32021-04-29 21:38:25 +020032#include <osmocom/core/socket.h>
Harald Welte41d0d842011-09-03 15:33:24 +020033#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
Harald Welte2bfc01d2013-10-06 12:23:35 +020040#include "config.h"
Harald Welte41d0d842011-09-03 15:33:24 +020041
42static PayloadType *payload_type_efr;
43static PayloadType *payload_type_hr;
44static RtpProfile *osmo_pt_profile;
45
46static void *tall_rtp_ctx;
47
48/* malloc integration */
49
50static void *osmo_ortp_malloc(size_t sz)
51{
52 return talloc_size(tall_rtp_ctx, sz);
53}
54
55static void *osmo_ortp_realloc(void *ptr, size_t sz)
56{
57 return talloc_realloc_size(tall_rtp_ctx, ptr, sz);
58}
59
60static void osmo_ortp_free(void *ptr)
61{
62 talloc_free(ptr);
63}
64
65static OrtpMemoryFunctions osmo_ortp_memfn = {
66 .malloc_fun = osmo_ortp_malloc,
67 .realloc_fun = osmo_ortp_realloc,
68 .free_fun = osmo_ortp_free
69};
70
71/* logging */
72
73struct level_map {
74 OrtpLogLevel ortp;
75 int osmo_level;
76};
77static const struct level_map level_map[] = {
78 { ORTP_DEBUG, LOGL_DEBUG },
79 { ORTP_MESSAGE, LOGL_INFO },
80 { ORTP_WARNING, LOGL_NOTICE },
81 { ORTP_ERROR, LOGL_ERROR },
82 { ORTP_FATAL, LOGL_FATAL },
83};
84static int ortp_to_osmo_lvl(OrtpLogLevel lev)
85{
86 int i;
87
88 for (i = 0; i < ARRAY_SIZE(level_map); i++) {
89 if (level_map[i].ortp == lev)
90 return level_map[i].osmo_level;
91 }
92 /* default */
93 return LOGL_ERROR;
94}
95
Pau Espin Pedrolc42bf192017-03-24 17:26:49 +010096static void my_ortp_logfn(
97#if HAVE_ORTP_LOG_DOMAIN
98 const char *domain,
99#endif
100 OrtpLogLevel lev, const char *fmt, va_list args)
Harald Welte41d0d842011-09-03 15:33:24 +0200101{
Pau Espin Pedrolde5758d2018-11-12 17:00:22 +0100102 /* Some strings coming from ortp are not endline terminated and mangle
103 * the output. Make sure all strings are endl terminated before
104 * printing.
105 */
106 int needs_endl;
107 const char *domain_str;
108 char *str;
109 size_t fmt_len = strlen(fmt);
110#if HAVE_ORTP_LOG_DOMAIN
111 /* domain can be NULL, found experimentally */
112 domain_str = domain ? : "";
113#else
114 domain_str = "";
115#endif
116 size_t domain_len = strlen(domain_str);
117
118 if (fmt_len == 0)
119 return;
120
121 needs_endl = fmt[fmt_len - 1] != '\n' ? 1 : 0;
122
123 str = osmo_ortp_malloc(domain_len + 2 /*": "*/ + fmt_len + needs_endl + 1);
124 sprintf(str, "%s%s%s%s", domain_str, domain_len ? ": " : "", fmt, needs_endl ? "\n" : "");
125
Harald Welte41d0d842011-09-03 15:33:24 +0200126 osmo_vlogp(DLMIB, ortp_to_osmo_lvl(lev), __FILE__, 0,
Pau Espin Pedrolde5758d2018-11-12 17:00:22 +0100127 0, str, args);
128
129 osmo_ortp_free(str);
130
Harald Welte41d0d842011-09-03 15:33:24 +0200131}
132
133/* ORTP signal callbacks */
134
135static void ortp_sig_cb_ssrc(RtpSession *rs, void *data)
136{
Harald Weltee7a3f432013-02-19 13:35:22 +0100137 int port = rtp_session_get_local_port(rs);
Harald Weltee7a3f432013-02-19 13:35:22 +0100138 uint32_t ssrc = rtp_session_get_recv_ssrc(rs);
Harald Weltee7a3f432013-02-19 13:35:22 +0100139
140 LOGP(DLMIB, LOGL_INFO,
Pau Espin Pedrol24e02172021-07-20 14:29:37 +0200141 "osmo-ortp(%d): ssrc_changed to 0x%08x, resyncing\n", port, ssrc);
142 rtp_session_resync(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200143}
144
145static void ortp_sig_cb_pt(RtpSession *rs, void *data)
146{
Harald Weltee7a3f432013-02-19 13:35:22 +0100147 int port = rtp_session_get_local_port(rs);
148 int pt = rtp_session_get_recv_payload_type(rs);
149
150 LOGP(DLMIB, LOGL_NOTICE,
151 "osmo-ortp(%d): payload_type_changed to 0x%02x\n", port, pt);
Harald Welte41d0d842011-09-03 15:33:24 +0200152}
153
154static void ortp_sig_cb_net(RtpSession *rs, void *data)
155{
Harald Weltee7a3f432013-02-19 13:35:22 +0100156 int port = rtp_session_get_local_port(rs);
157
158 LOGP(DLMIB, LOGL_ERROR,
Max7c840be2016-12-05 16:13:53 +0100159 "osmo-ortp(%d): network_error %s\n", port, (char *)data);
Harald Welte41d0d842011-09-03 15:33:24 +0200160}
161
162static void ortp_sig_cb_ts(RtpSession *rs, void *data)
163{
Harald Weltee7a3f432013-02-19 13:35:22 +0100164 int port = rtp_session_get_local_port(rs);
165 uint32_t ts = rtp_session_get_current_recv_ts(rs);
166
167 LOGP(DLMIB, LOGL_NOTICE,
Yves Godin2c32f0a2016-10-06 15:55:06 +0200168 "osmo-ortp(%d): timestamp_jump, new TS %d, resyncing\n", port, ts);
169 rtp_session_resync(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200170}
171
Maxf6906002016-10-24 14:10:10 +0200172static inline bool recv_with_cb(struct osmo_rtp_socket *rs)
173{
Harald Welte7895e042016-10-28 10:40:24 +0200174 uint8_t *payload;
Maxf6906002016-10-24 14:10:10 +0200175 mblk_t *mblk = rtp_session_recvm_with_ts(rs->sess, rs->rx_user_ts);
176 if (!mblk)
177 return false;
178
Harald Welte7895e042016-10-28 10:40:24 +0200179 int plen = rtp_get_payload(mblk, &payload);
Maxf6906002016-10-24 14:10:10 +0200180 /* hand into receiver */
181 if (rs->rx_cb && plen > 0)
Harald Welte7895e042016-10-28 10:40:24 +0200182 rs->rx_cb(rs, payload, plen, rtp_get_seqnumber(mblk),
Maxf6906002016-10-24 14:10:10 +0200183 rtp_get_timestamp(mblk), rtp_get_markbit(mblk));
184 freemsg(mblk);
185 if (plen > 0)
186 return true;
187 return false;
188}
Harald Welte41d0d842011-09-03 15:33:24 +0200189
Harald Welte9b737df2011-09-07 00:59:11 +0200190/*! \brief poll the socket for incoming data
191 * \param[in] rs the socket to be polled
192 * \returns number of packets received + handed to the rx_cb
193 */
194int osmo_rtp_socket_poll(struct osmo_rtp_socket *rs)
195{
Maxe54d7bc2016-04-29 12:39:33 +0200196 if (rs->flags & OSMO_RTP_F_DISABLED)
197 return 0;
Harald Welte9b737df2011-09-07 00:59:11 +0200198
Maxf6906002016-10-24 14:10:10 +0200199 if (recv_with_cb(rs))
Harald Welte9b737df2011-09-07 00:59:11 +0200200 return 1;
Maxf6906002016-10-24 14:10:10 +0200201
Harald Welte8c724c02021-02-06 15:25:39 +0100202 /* this happens every time we miss an incoming RTP frame, which is quite common
203 * when a voice channel is first activated, or also in case of packet loss.
204 * See also https://osmocom.org/issues/4464 */
205 LOGP(DLMIB, LOGL_DEBUG, "osmo_rtp_socket_poll(%u): No message received\n", rs->rx_user_ts);
Maxf6906002016-10-24 14:10:10 +0200206 return 0;
Harald Welte9b737df2011-09-07 00:59:11 +0200207}
208
Harald Welte41d0d842011-09-03 15:33:24 +0200209/* Osmo FD callbacks */
210
211static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
212{
213 struct osmo_rtp_socket *rs = fd->data;
Harald Welte41d0d842011-09-03 15:33:24 +0200214
Harald Weltede3959e2020-10-18 22:28:50 +0200215 if (what & OSMO_FD_READ) {
Harald Welte9b737df2011-09-07 00:59:11 +0200216 /* in polling mode, we don't want to be called here */
217 if (rs->flags & OSMO_RTP_F_POLL) {
Harald Welte949b8a22020-10-18 23:01:53 +0200218 osmo_fd_read_disable(fd);
Harald Welte9b737df2011-09-07 00:59:11 +0200219 return 0;
220 }
Maxf6906002016-10-24 14:10:10 +0200221 if (!recv_with_cb(rs))
Harald Welte9b737df2011-09-07 00:59:11 +0200222 LOGP(DLMIB, LOGL_INFO, "recvm_with_ts(%u): ERROR!\n",
223 rs->rx_user_ts);
Harald Welte41d0d842011-09-03 15:33:24 +0200224 rs->rx_user_ts += 160;
225 }
Harald Weltede3959e2020-10-18 22:28:50 +0200226 /* writing is not queued at the moment, so OSMO_FD_WRITE
Harald Welte41d0d842011-09-03 15:33:24 +0200227 * shouldn't occur */
228 return 0;
229}
230
Pau Espin Pedrolb0c3a4a2017-06-21 07:25:18 +0200231/* Internal API coming from rtpsession_priv.h, used in osmo_rtcp_fd_cb */
232#pragma message ("Using internal ortp API: rtp_session_rtcp_rec")
233int rtp_session_rtcp_recv(RtpSession * session);
234
Harald Welte41d0d842011-09-03 15:33:24 +0200235static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
236{
237 struct osmo_rtp_socket *rs = fd->data;
238
239 /* We probably don't need this at all, as
240 * rtp_session_recvm_with_ts() will alway also poll the RTCP
241 * file descriptor for new data */
242 return rtp_session_rtcp_recv(rs->sess);
243}
244
245static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
246{
Harald Welte34260c82016-11-26 09:27:04 +0100247 int rc;
248
Harald Welte6e831b72020-10-18 22:59:58 +0200249 osmo_fd_setup(&rs->rtp_bfd, rtp_session_get_rtp_socket(rs->sess), OSMO_FD_READ, osmo_rtp_fd_cb, rs, 0);
250 osmo_fd_setup(&rs->rtcp_bfd, rtp_session_get_rtcp_socket(rs->sess), OSMO_FD_READ, osmo_rtcp_fd_cb, rs, 0);
Harald Welte41d0d842011-09-03 15:33:24 +0200251
Harald Welte34260c82016-11-26 09:27:04 +0100252 rc = osmo_fd_register(&rs->rtp_bfd);
253 if (rc < 0)
254 return rc;
255
256 rc = osmo_fd_register(&rs->rtcp_bfd);
257 if (rc < 0) {
258 osmo_fd_unregister(&rs->rtp_bfd);
259 return rc;
260 }
Harald Welte41d0d842011-09-03 15:33:24 +0200261
262 return 0;
263}
264
Harald Welte35c109f2022-11-03 11:17:40 +0100265static void create_payload_types(void)
Harald Welte41d0d842011-09-03 15:33:24 +0200266{
267 PayloadType *pt;
268
269 /* EFR */
270 pt = payload_type_new();
271 pt->type = PAYLOAD_AUDIO_PACKETIZED;
272 pt->clock_rate = 8000;
273 pt->mime_type = "EFR";
274 pt->normal_bitrate = 12200;
275 pt->channels = 1;
276 payload_type_efr = pt;
277
278 /* HR */
279 pt = payload_type_new();
280 pt->type = PAYLOAD_AUDIO_PACKETIZED;
281 pt->clock_rate = 8000;
282 pt->mime_type = "HR";
283 pt->normal_bitrate = 6750; /* FIXME */
284 pt->channels = 1;
285 payload_type_hr = pt;
286
287 /* create a new RTP profile as clone of AV profile */
288 osmo_pt_profile = rtp_profile_clone(&av_profile);
289
290 /* add the GSM specific payload types. They are all dynamically
291 * assigned, but in the Osmocom GSM system we have allocated
292 * them as follows: */
293 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
294 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
295 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
296}
297
298/* public functions */
299
300/*! \brief initialize Osmocom RTP code
Harald Weltefcb1fe82011-09-07 11:51:52 +0200301 * \param[in] ctx default talloc context for library-internal allocations
Harald Welte41d0d842011-09-03 15:33:24 +0200302 */
303void osmo_rtp_init(void *ctx)
304{
305 tall_rtp_ctx = ctx;
306 ortp_set_memory_functions(&osmo_ortp_memfn);
307 ortp_init();
Pau Espin Pedrolc42bf192017-03-24 17:26:49 +0100308 ortp_set_log_level_mask(
309#if HAVE_ORTP_LOG_DOMAIN
310 ORTP_LOG_DOMAIN,
311#endif
312 0xffff);
313
Harald Welte41d0d842011-09-03 15:33:24 +0200314 ortp_set_log_handler(my_ortp_logfn);
315 create_payload_types();
316}
317
Maxbf42e0a2016-12-15 19:57:57 +0100318/*! \brief Set Osmocom RTP socket parameters
319 * \param[in] rs OsmoRTP socket
320 * \param[in] param defined which parameter to set
321 OSMO_RTP_P_JITBUF - enables regular jitter buffering
322 OSMO_RTP_P_JIT_ADAP - enables adaptive jitter buffering
323 * \param[in] val Size of jitter buffer (in ms), 0 means disable buffering
324 * \returns negative value on error, 0 or 1 otherwise
325 (depending on whether given jitter buffering is enabled)
326 */
Harald Welte65a50892011-09-08 14:42:58 +0200327int osmo_rtp_socket_set_param(struct osmo_rtp_socket *rs,
328 enum osmo_rtp_param param, int val)
329{
Harald Welte65a50892011-09-08 14:42:58 +0200330 switch (param) {
Maxbf42e0a2016-12-15 19:57:57 +0100331 case OSMO_RTP_P_JIT_ADAP:
332 rtp_session_enable_adaptive_jitter_compensation(rs->sess,
333 (bool)val);
334 /* fall-through on-purpose - we have to set val anyway */
Harald Welte65a50892011-09-08 14:42:58 +0200335 case OSMO_RTP_P_JITBUF:
Andreas Eversberg41bc6152013-02-14 11:03:26 +0100336 rtp_session_enable_jitter_buffer(rs->sess,
337 (val) ? TRUE : FALSE);
338 if (val)
339 rtp_session_set_jitter_compensation(rs->sess, val);
Harald Welte65a50892011-09-08 14:42:58 +0200340 break;
Harald Welte65a50892011-09-08 14:42:58 +0200341 default:
342 return -EINVAL;
343 }
Maxbf42e0a2016-12-15 19:57:57 +0100344 if (param == OSMO_RTP_P_JIT_ADAP)
345 return rtp_session_adaptive_jitter_compensation_enabled(rs->sess);
346 return rtp_session_jitter_buffer_enabled(rs->sess);
Harald Welte65a50892011-09-08 14:42:58 +0200347}
348
Harald Weltefcb1fe82011-09-07 11:51:52 +0200349/*! \brief Create a new RTP socket
350 * \param[in] talloc_cxt talloc context for this allocation. NULL for
351 * dafault context
352 * \param[in] flags Flags like OSMO_RTP_F_POLL
353 * \returns pointer to library-allocated \a struct osmo_rtp_socket
354 */
Harald Welte9b737df2011-09-07 00:59:11 +0200355struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
Harald Welte41d0d842011-09-03 15:33:24 +0200356{
357 struct osmo_rtp_socket *rs;
358
359 if (!talloc_ctx)
360 talloc_ctx = tall_rtp_ctx;
361
362 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
363 if (!rs)
364 return NULL;
365
Maxe54d7bc2016-04-29 12:39:33 +0200366 rs->flags = OSMO_RTP_F_DISABLED | flags;
Harald Welte41d0d842011-09-03 15:33:24 +0200367 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
368 if (!rs->sess) {
369 talloc_free(rs);
370 return NULL;
371 }
372 rtp_session_set_data(rs->sess, rs);
373 rtp_session_set_profile(rs->sess, osmo_pt_profile);
Harald Welte9b737df2011-09-07 00:59:11 +0200374 rtp_session_set_jitter_compensation(rs->sess, 100);
Harald Welte41d0d842011-09-03 15:33:24 +0200375
Harald Welte54c919e2020-03-08 21:50:01 +0100376 /* ortp >= 0.24.0 doesn't differentiate between SO_REUSEADDR and
377 * SO_REUSEPORT, and has both enabled by default. The latter means that
378 * we can end up with non-unique port bindings as we will not fail to
379 * bind the same port twice */
380 rtp_session_set_reuseaddr(rs->sess, false);
381
Harald Welte41d0d842011-09-03 15:33:24 +0200382 rtp_session_signal_connect(rs->sess, "ssrc_changed",
383 (RtpCallback) ortp_sig_cb_ssrc,
Pau Espin Pedrol05df2d62017-06-21 07:37:45 +0200384 RTP_SIGNAL_PTR_CAST(rs));
385
Harald Welte41d0d842011-09-03 15:33:24 +0200386 rtp_session_signal_connect(rs->sess, "payload_type_changed",
387 (RtpCallback) ortp_sig_cb_pt,
Pau Espin Pedrol05df2d62017-06-21 07:37:45 +0200388 RTP_SIGNAL_PTR_CAST(rs));
389
Harald Welte41d0d842011-09-03 15:33:24 +0200390 rtp_session_signal_connect(rs->sess, "network_error",
391 (RtpCallback) ortp_sig_cb_net,
Pau Espin Pedrol05df2d62017-06-21 07:37:45 +0200392 RTP_SIGNAL_PTR_CAST(rs));
393
Harald Welte41d0d842011-09-03 15:33:24 +0200394 rtp_session_signal_connect(rs->sess, "timestamp_jump",
395 (RtpCallback) ortp_sig_cb_ts,
Pau Espin Pedrol05df2d62017-06-21 07:37:45 +0200396 RTP_SIGNAL_PTR_CAST(rs));
Harald Welte41d0d842011-09-03 15:33:24 +0200397
Holger Hans Peter Freytherbf6f1f42014-06-24 10:57:35 +0200398 /* initialize according to the RFC */
399 rtp_session_set_seq_number(rs->sess, random());
Holger Hans Peter Freytherfb6e1e92014-06-24 11:02:01 +0200400 rs->tx_timestamp = random();
Pau Espin Pedrolb0c3a4a2017-06-21 07:25:18 +0200401
Philipp Maierc81b68f2018-05-30 11:00:21 +0200402 /* Make sure ssrc changes are detected immediately */
403 rtp_session_set_ssrc_changed_threshold(rs->sess, 0);
Holger Hans Peter Freytherbf6f1f42014-06-24 10:57:35 +0200404
Harald Welte41d0d842011-09-03 15:33:24 +0200405 return rs;
406}
407
Harald Weltefcb1fe82011-09-07 11:51:52 +0200408/*! \brief bind a RTP socket to a local port
409 * \param[in] rs OsmoRTP socket
410 * \param[in] ip hostname/ip as string
411 * \param[in] port UDP port number, -1 for random selection
412 * \returns 0 on success, <0 on error
413 */
Harald Welte41d0d842011-09-03 15:33:24 +0200414int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
415{
Max15d9b792016-04-28 12:05:27 +0200416 int rc, rtcp = (-1 != port) ? port + 1 : -1;
Max80f7c042016-04-29 12:39:34 +0200417 rc = rtp_session_set_local_addr(rs->sess, ip, port, rtcp);
Max15d9b792016-04-28 12:05:27 +0200418
Harald Welte41d0d842011-09-03 15:33:24 +0200419 if (rc < 0)
420 return rc;
421
422 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
423 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
424
425 return 0;
426}
427
Harald Weltefcb1fe82011-09-07 11:51:52 +0200428/*! \brief connect a OsmoRTP socket to a remote port
429 * \param[in] rs OsmoRTP socket
430 * \param[in] ip String representation of remote hostname or IP address
431 * \param[in] port UDP port number to connect to
432 *
433 * If the OsmoRTP socket is not in POLL mode, this function will also
434 * cause the RTP and RTCP file descriptors to be registred with the
435 * libosmocore select() loop integration.
436 *
437 * \returns 0 on success, <0 in case of error
438 */
Harald Welte41d0d842011-09-03 15:33:24 +0200439int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
440{
441 int rc;
Maxe54d7bc2016-04-29 12:39:33 +0200442 if (!port) {
443 LOGP(DLMIB, LOGL_INFO, "osmo_rtp_socket_connect() refused to "
444 "set remote %s:%u\n", ip, port);
445 return 0;
446 }
Max8c119f72016-04-29 12:39:35 +0200447
Neels Hofmeyra0ff9422016-10-06 15:43:38 +0200448 /* We don't want the connected mode enabled during
449 * rtp_session_set_remote_addr(), because that will already setup a
450 * connection and updating the remote address will no longer have an
451 * effect. Contrary to what one may expect, this must be 0 at first,
452 * and we're setting to 1 further down to establish a connection once
453 * the first RTP packet is received (OS#1661). */
454 rtp_session_set_connected_mode(rs->sess, 0);
455
456 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
457 if (rc < 0)
458 return rc;
459
Harald Welted426d452013-02-09 11:01:19 +0100460 /* enable the use of connect() so later getsockname() will
461 * actually return the IP address that was chosen for the local
462 * sid of the connection */
463 rtp_session_set_connected_mode(rs->sess, 1);
Maxe54d7bc2016-04-29 12:39:33 +0200464 rs->flags &= ~OSMO_RTP_F_DISABLED;
Harald Welted426d452013-02-09 11:01:19 +0100465
Harald Welte9b737df2011-09-07 00:59:11 +0200466 if (rs->flags & OSMO_RTP_F_POLL)
467 return rc;
468 else
469 return osmo_rtp_socket_fdreg(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200470}
471
Sylvain Munautbab7ae72019-03-11 15:36:13 +0100472/*! \brief Automatically associates a RTP socket with the first incoming UDP packet
473 * \param[in] rs OsmoRTP socket
474 *
475 * The bound RTP socket will wait for incoming RTP packets and as soon as it
476 * sees one, will 'connect' to it, so all replies will go to that sources and
477 * incoming messages from other sources will be discarded. This obviously only
478 * works once.
479 *
480 * \returns 0 on success, <0 in case of error.
481 */
482int osmo_rtp_socket_autoconnect(struct osmo_rtp_socket *rs)
483{
484 rtp_session_set_symmetric_rtp(rs->sess, 1);
485 rtp_session_set_connected_mode(rs->sess, 1);
486 rs->flags &= ~OSMO_RTP_F_DISABLED;
487
488 if (rs->flags & OSMO_RTP_F_POLL)
489 return 0;
490 else
491 return osmo_rtp_socket_fdreg(rs);
492}
493
Pau Espin Pedrol524923a2017-06-28 15:31:46 +0200494/*! \brief Increment timestamp on a RTP socket without sending any packet
495 * \param[in] rs OsmoRTP socket
496 * \param[in] duration duration in number of RTP clock ticks
497 *
498 * Useful to keep the RTP internal clock up to date if an RTP frame should be
499 * send at a given time but no audio content is available. When next packet is
500 * sent, the receiver will see a different increase on the sequence number and
501 * the timestamp, and it should then take it as a synchronization point. For
502 * that same reason, it is advisable to enable the marker bit on the next RTP
503 * packet to be sent after calling this function.
504 *
505 * \returns 0 on success, <0 in case of error.
506 */
507int osmo_rtp_skipped_frame(struct osmo_rtp_socket *rs, unsigned int duration)
508{
509 if (rs->flags & OSMO_RTP_F_DISABLED)
510 return 0;
511
512 rs->tx_timestamp += duration;
513 return 0;
514}
515
Harald Weltefcb1fe82011-09-07 11:51:52 +0200516/*! \brief Send one RTP frame via a RTP socket
517 * \param[in] rs OsmoRTP socket
518 * \param[in] payload pointer to buffer with RTP payload data
519 * \param[in] payload_len length of \a payload in bytes
520 * \param[in] duration duration in number of RTP clock ticks
521 * \returns 0 on success, <0 in case of error.
522 */
Harald Welte41d0d842011-09-03 15:33:24 +0200523int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
524 unsigned int payload_len, unsigned int duration)
525{
Max73b9bc72016-05-18 15:52:37 +0200526 return osmo_rtp_send_frame_ext(rs, payload, payload_len, duration,
527 false);
528}
529
530/*! \brief Send one RTP frame via a RTP socket
531 * \param[in] rs OsmoRTP socket
532 * \param[in] payload pointer to buffer with RTP payload data
533 * \param[in] payload_len length of \a payload in bytes
534 * \param[in] duration duration in number of RTP clock ticks
535 * \param[in] marker the status of Marker bit in RTP header
536 * \returns 0 on success, <0 in case of error.
537 */
538int osmo_rtp_send_frame_ext(struct osmo_rtp_socket *rs, const uint8_t *payload,
539 unsigned int payload_len, unsigned int duration,
540 bool marker)
541{
Harald Welte41d0d842011-09-03 15:33:24 +0200542 mblk_t *mblk;
543 int rc;
544
Maxe54d7bc2016-04-29 12:39:33 +0200545 if (rs->flags & OSMO_RTP_F_DISABLED)
546 return 0;
547
Harald Welte41d0d842011-09-03 15:33:24 +0200548 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
549 payload, payload_len);
550 if (!mblk)
551 return -ENOMEM;
552
Max73b9bc72016-05-18 15:52:37 +0200553 rtp_set_markbit(mblk, marker);
Harald Welte41d0d842011-09-03 15:33:24 +0200554 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
555 rs->tx_timestamp);
Pau Espin Pedrol9e992c22017-06-29 18:04:18 +0200556 rs->tx_timestamp += duration;
Harald Welte41d0d842011-09-03 15:33:24 +0200557 if (rc < 0) {
558 /* no need to free() the mblk, as rtp_session_rtp_send()
559 * unconditionally free()s the mblk even in case of
560 * error */
561 return rc;
562 }
563
564 return rc;
565}
566
Harald Weltefcb1fe82011-09-07 11:51:52 +0200567/*! \brief Set the payload type of a RTP socket
568 * \param[in] rs OsmoRTP socket
569 * \param[in] payload_type RTP payload type
570 * \returns 0 on success, < 0 otherwise
571 */
Harald Welte41d0d842011-09-03 15:33:24 +0200572int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
573{
574 int rc;
575
576 rc = rtp_session_set_payload_type(rs->sess, payload_type);
577 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
578
579 return rc;
580}
581
Oliver Smith3c514822020-03-06 14:31:47 +0100582/*! \brief Set the DSCP (Differentiated Services Code Point) for outgoing RTP packets
583 * \param[in] rs OsmoRTP socket
584 * \param[in] dscp DSCP value
585 * \returns 0 on success, < 0 otherwise
586 */
587int osmo_rtp_socket_set_dscp(struct osmo_rtp_socket *rs, int dscp)
588{
589 return rtp_session_set_dscp(rs->sess, dscp);
590}
591
Harald Welte14277e32021-04-29 21:38:25 +0200592/*! \brief Set the socket priority for outgoing RTP packets
593 * \param[in] rs OsmoRTP socket
594 * \param[in] prio socket priority
595 * \returns 0 on success, < 0 otherwise
596 */
597int osmo_rtp_socket_set_priority(struct osmo_rtp_socket *rs, uint8_t prio)
598{
599 int rc;
600
601 rc = osmo_sock_set_priority(rs->rtp_bfd.fd, prio);
602 if (rc < 0)
603 return rc;
604 return osmo_sock_set_priority(rs->rtcp_bfd.fd, prio);
605}
606
Harald Weltefcb1fe82011-09-07 11:51:52 +0200607/*! \brief completely close the RTP socket and release all resources
608 * \param[in] rs OsmoRTP socket to be released
609 * \returns 0 on success
610 */
Harald Welte41d0d842011-09-03 15:33:24 +0200611int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
612{
613 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
614 osmo_fd_unregister(&rs->rtp_bfd);
615
616 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
617 osmo_fd_unregister(&rs->rtcp_bfd);
618
619 if (rs->sess) {
620 rtp_session_release_sockets(rs->sess);
621 rtp_session_destroy(rs->sess);
622 rs->sess = NULL;
623 }
624
625 talloc_free(rs);
626
627 return 0;
628}
629
Harald Weltefcb1fe82011-09-07 11:51:52 +0200630/*! \brief obtain the locally bound IPv4 address and UDP port
631 * \param[in] rs OsmoRTP socket
632 * \param[out] ip Pointer to caller-allocated uint32_t for IPv4 address
633 * \oaram[out] port Pointer to caller-allocated int for UDP port number
634 * \returns 0 on success, <0 on error, -EIO in case of IPv6 socket
635 */
Harald Welte41d0d842011-09-03 15:33:24 +0200636int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
637 uint32_t *ip, int *port)
638{
639 int rc;
640 struct sockaddr_storage ss;
641 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
642 socklen_t alen = sizeof(ss);
643
644 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
645 if (rc < 0)
646 return rc;
647
648 if (ss.ss_family != AF_INET)
649 return -EIO;
650
651 *ip = ntohl(sin->sin_addr.s_addr);
652 *port = rtp_session_get_local_port(rs->sess);
653
654 return 0;
655}
656
Harald Weltefcb1fe82011-09-07 11:51:52 +0200657/*! \brief obtain the locally bound address and port
658 * \param[in] rs OsmoRTP socket
659 * \param[out] addr caller-allocated char ** to which the string pointer for
660 * the address is stored
661 * \param[out] port caller-allocated int * to which the port number is
662 * stored
663 * \returns 0 on success, <0 in case of error
664 */
Harald Welte41d0d842011-09-03 15:33:24 +0200665int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
666 const char **addr, int *port)
667{
668 int rc;
669 struct sockaddr_storage ss;
670 socklen_t alen = sizeof(ss);
671 static char hostbuf[256];
672
673 memset(hostbuf, 0, sizeof(hostbuf));
674
675 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
676 if (rc < 0)
677 return rc;
678
679 rc = getnameinfo((struct sockaddr *)&ss, alen,
680 hostbuf, sizeof(hostbuf), NULL, 0,
681 NI_NUMERICHOST);
682 if (rc < 0)
683 return rc;
684
685 *port = rtp_session_get_local_port(rs->sess);
686 *addr = hostbuf;
687
688 return 0;
689}
Harald Welte65a50892011-09-08 14:42:58 +0200690
691
692void osmo_rtp_socket_log_stats(struct osmo_rtp_socket *rs,
693 int subsys, int level,
694 const char *pfx)
695{
696 const rtp_stats_t *stats;
697
698 stats = rtp_session_get_stats(rs->sess);
699 if (!stats)
700 return;
701
702 LOGP(subsys, level, "%sRTP Tx(%"PRIu64" pkts, %"PRIu64" bytes) "
703 "Rx(%"PRIu64" pkts, %"PRIu64" bytes, %"PRIu64" late, "
704 "%"PRIu64" loss, %"PRIu64" qmax)\n",
705 pfx, stats->packet_sent, stats->sent,
706 stats->packet_recv, stats->hw_recv, stats->outoftime,
707 stats->cum_packet_loss, stats->discarded);
708}
Holger Hans Peter Freytherfe019082015-09-21 10:52:52 +0200709
710void osmo_rtp_socket_stats(struct osmo_rtp_socket *rs,
711 uint32_t *sent_packets, uint32_t *sent_octets,
712 uint32_t *recv_packets, uint32_t *recv_octets,
713 uint32_t *recv_lost, uint32_t *last_jitter)
714{
715 const rtp_stats_t *stats;
Holger Hans Peter Freytherfe019082015-09-21 10:52:52 +0200716
717 *sent_packets = *sent_octets = *recv_packets = *recv_octets = 0;
718 *recv_lost = *last_jitter = 0;
719
720 stats = rtp_session_get_stats(rs->sess);
721 if (stats) {
722 /* truncate from 64bit to 32bit here */
723 *sent_packets = stats->packet_sent;
724 *sent_octets = stats->sent;
725 *recv_packets = stats->packet_recv;
726 *recv_octets = stats->recv;
727 *recv_lost = stats->cum_packet_loss;
728 }
729
Holger Hans Peter Freyther71bc9e22015-09-21 12:18:37 +0200730 const jitter_stats_t *jitter;
731
Holger Hans Peter Freytherfe019082015-09-21 10:52:52 +0200732 jitter = rtp_session_get_jitter_stats(rs->sess);
733 if (jitter)
734 *last_jitter = jitter->jitter;
735}
Harald Welted1dd22c2017-12-03 10:00:16 +0100736
737void osmo_rtp_set_source_desc(struct osmo_rtp_socket *rs, const char *cname,
738 const char *name, const char *email, const char *phone,
739 const char *loc, const char *tool, const char *note)
740{
741 rtp_session_set_source_description(rs->sess, cname, name, email, phone, loc, tool, note);
742}