blob: 54c5559e95fd45bf09ebdeb02f05aa1346777a8a [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
239 * \param[in] ctx talloc context
240 */
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
251/*! \brief create a new RTP socket */
Harald Welte9b737df2011-09-07 00:59:11 +0200252struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx, unsigned int flags)
Harald Welte41d0d842011-09-03 15:33:24 +0200253{
254 struct osmo_rtp_socket *rs;
255
256 if (!talloc_ctx)
257 talloc_ctx = tall_rtp_ctx;
258
259 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
260 if (!rs)
261 return NULL;
262
Harald Welte9b737df2011-09-07 00:59:11 +0200263 rs->flags = flags;
Harald Welte41d0d842011-09-03 15:33:24 +0200264 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
265 if (!rs->sess) {
266 talloc_free(rs);
267 return NULL;
268 }
269 rtp_session_set_data(rs->sess, rs);
270 rtp_session_set_profile(rs->sess, osmo_pt_profile);
Harald Welte9b737df2011-09-07 00:59:11 +0200271 rtp_session_set_jitter_compensation(rs->sess, 100);
272 //jitter_control_enable_adaptive(rs->sess, 0);
Harald Welte41d0d842011-09-03 15:33:24 +0200273
274 rtp_session_signal_connect(rs->sess, "ssrc_changed",
275 (RtpCallback) ortp_sig_cb_ssrc,
276 (unsigned long) rs);
277 rtp_session_signal_connect(rs->sess, "payload_type_changed",
278 (RtpCallback) ortp_sig_cb_pt,
279 (unsigned long) rs);
280 rtp_session_signal_connect(rs->sess, "network_error",
281 (RtpCallback) ortp_sig_cb_net,
282 (unsigned long) rs);
283 rtp_session_signal_connect(rs->sess, "timestamp_jump",
284 (RtpCallback) ortp_sig_cb_ts,
285 (unsigned long) rs);
286
287 return rs;
288}
289
290/*! \brief bind a RTP socket to a local port */
291int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
292{
293 int rc;
294
295 rc = rtp_session_set_local_addr(rs->sess, ip, port);
296 if (rc < 0)
297 return rc;
298
299 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
300 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
301
302 return 0;
303}
304
305/*! \brief connect a RTP socket to a remote port */
306int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
307{
308 int rc;
309
310 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
311 if (rc < 0)
312 return rc;
313
Harald Welte9b737df2011-09-07 00:59:11 +0200314 if (rs->flags & OSMO_RTP_F_POLL)
315 return rc;
316 else
317 return osmo_rtp_socket_fdreg(rs);
Harald Welte41d0d842011-09-03 15:33:24 +0200318}
319
320/*! \brief Send one RTP frame via a RTP socket */
321int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
322 unsigned int payload_len, unsigned int duration)
323{
324 mblk_t *mblk;
325 int rc;
326
327 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
328 payload, payload_len);
329 if (!mblk)
330 return -ENOMEM;
331
332 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
333 rs->tx_timestamp);
334 rs->tx_timestamp += duration;
335 if (rc < 0) {
336 /* no need to free() the mblk, as rtp_session_rtp_send()
337 * unconditionally free()s the mblk even in case of
338 * error */
339 return rc;
340 }
341
342 return rc;
343}
344
345/*! \brief Set the payload type of a RTP socket */
346int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
347{
348 int rc;
349
350 rc = rtp_session_set_payload_type(rs->sess, payload_type);
351 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
352
353 return rc;
354}
355
356/*! \brief completely close the RTP socket and release all resources */
357int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
358{
359 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
360 osmo_fd_unregister(&rs->rtp_bfd);
361
362 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
363 osmo_fd_unregister(&rs->rtcp_bfd);
364
365 if (rs->sess) {
366 rtp_session_release_sockets(rs->sess);
367 rtp_session_destroy(rs->sess);
368 rs->sess = NULL;
369 }
370
371 talloc_free(rs);
372
373 return 0;
374}
375
376
377int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
378 uint32_t *ip, int *port)
379{
380 int rc;
381 struct sockaddr_storage ss;
382 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
383 socklen_t alen = sizeof(ss);
384
385 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
386 if (rc < 0)
387 return rc;
388
389 if (ss.ss_family != AF_INET)
390 return -EIO;
391
392 *ip = ntohl(sin->sin_addr.s_addr);
393 *port = rtp_session_get_local_port(rs->sess);
394
395 return 0;
396}
397
398int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
399 const char **addr, int *port)
400{
401 int rc;
402 struct sockaddr_storage ss;
403 socklen_t alen = sizeof(ss);
404 static char hostbuf[256];
405
406 memset(hostbuf, 0, sizeof(hostbuf));
407
408 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
409 if (rc < 0)
410 return rc;
411
412 rc = getnameinfo((struct sockaddr *)&ss, alen,
413 hostbuf, sizeof(hostbuf), NULL, 0,
414 NI_NUMERICHOST);
415 if (rc < 0)
416 return rc;
417
418 *port = rtp_session_get_local_port(rs->sess);
419 *addr = hostbuf;
420
421 return 0;
422}