blob: 461322e027d6b35a363da67010ce74a21ed1a2bc [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
121/* Osmo FD callbacks */
122
123static int osmo_rtp_fd_cb(struct osmo_fd *fd, unsigned int what)
124{
125 struct osmo_rtp_socket *rs = fd->data;
126 mblk_t *mblk;
127
128 if (what & BSC_FD_READ) {
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 freemsg(mblk);
137 }
138 rs->rx_user_ts += 160;
139 }
140 /* writing is not queued at the moment, so BSC_FD_WRITE
141 * shouldn't occur */
142 return 0;
143}
144
145static int osmo_rtcp_fd_cb(struct osmo_fd *fd, unsigned int what)
146{
147 struct osmo_rtp_socket *rs = fd->data;
148
149 /* We probably don't need this at all, as
150 * rtp_session_recvm_with_ts() will alway also poll the RTCP
151 * file descriptor for new data */
152 return rtp_session_rtcp_recv(rs->sess);
153}
154
155static int osmo_rtp_socket_fdreg(struct osmo_rtp_socket *rs)
156{
157 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
158 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
159 rs->rtp_bfd.when = rs->rtcp_bfd.when = BSC_FD_READ;
160 rs->rtp_bfd.data = rs->rtcp_bfd.data = rs;
161 rs->rtp_bfd.cb = osmo_rtp_fd_cb;
162 rs->rtcp_bfd.cb = osmo_rtcp_fd_cb;
163
164 osmo_fd_register(&rs->rtp_bfd);
165 osmo_fd_register(&rs->rtcp_bfd);
166
167 return 0;
168}
169
170static void create_payload_types()
171{
172 PayloadType *pt;
173
174 /* EFR */
175 pt = payload_type_new();
176 pt->type = PAYLOAD_AUDIO_PACKETIZED;
177 pt->clock_rate = 8000;
178 pt->mime_type = "EFR";
179 pt->normal_bitrate = 12200;
180 pt->channels = 1;
181 payload_type_efr = pt;
182
183 /* HR */
184 pt = payload_type_new();
185 pt->type = PAYLOAD_AUDIO_PACKETIZED;
186 pt->clock_rate = 8000;
187 pt->mime_type = "HR";
188 pt->normal_bitrate = 6750; /* FIXME */
189 pt->channels = 1;
190 payload_type_hr = pt;
191
192 /* create a new RTP profile as clone of AV profile */
193 osmo_pt_profile = rtp_profile_clone(&av_profile);
194
195 /* add the GSM specific payload types. They are all dynamically
196 * assigned, but in the Osmocom GSM system we have allocated
197 * them as follows: */
198 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_EFR, payload_type_efr);
199 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_GSM_HALF, payload_type_hr);
200 rtp_profile_set_payload(osmo_pt_profile, RTP_PT_AMR, &payload_type_amr);
201}
202
203/* public functions */
204
205/*! \brief initialize Osmocom RTP code
206 * \param[in] ctx talloc context
207 */
208void osmo_rtp_init(void *ctx)
209{
210 tall_rtp_ctx = ctx;
211 ortp_set_memory_functions(&osmo_ortp_memfn);
212 ortp_init();
213 ortp_set_log_level_mask(0xffff);
214 ortp_set_log_handler(my_ortp_logfn);
215 create_payload_types();
216}
217
218/*! \brief create a new RTP socket */
219struct osmo_rtp_socket *osmo_rtp_socket_create(void *talloc_ctx)
220{
221 struct osmo_rtp_socket *rs;
222
223 if (!talloc_ctx)
224 talloc_ctx = tall_rtp_ctx;
225
226 rs = talloc_zero(talloc_ctx, struct osmo_rtp_socket);
227 if (!rs)
228 return NULL;
229
230 rs->sess = rtp_session_new(RTP_SESSION_SENDRECV);
231 if (!rs->sess) {
232 talloc_free(rs);
233 return NULL;
234 }
235 rtp_session_set_data(rs->sess, rs);
236 rtp_session_set_profile(rs->sess, osmo_pt_profile);
237
238 rtp_session_signal_connect(rs->sess, "ssrc_changed",
239 (RtpCallback) ortp_sig_cb_ssrc,
240 (unsigned long) rs);
241 rtp_session_signal_connect(rs->sess, "payload_type_changed",
242 (RtpCallback) ortp_sig_cb_pt,
243 (unsigned long) rs);
244 rtp_session_signal_connect(rs->sess, "network_error",
245 (RtpCallback) ortp_sig_cb_net,
246 (unsigned long) rs);
247 rtp_session_signal_connect(rs->sess, "timestamp_jump",
248 (RtpCallback) ortp_sig_cb_ts,
249 (unsigned long) rs);
250
251 return rs;
252}
253
254/*! \brief bind a RTP socket to a local port */
255int osmo_rtp_socket_bind(struct osmo_rtp_socket *rs, const char *ip, int port)
256{
257 int rc;
258
259 rc = rtp_session_set_local_addr(rs->sess, ip, port);
260 if (rc < 0)
261 return rc;
262
263 rs->rtp_bfd.fd = rtp_session_get_rtp_socket(rs->sess);
264 rs->rtcp_bfd.fd = rtp_session_get_rtcp_socket(rs->sess);
265
266 return 0;
267}
268
269/*! \brief connect a RTP socket to a remote port */
270int osmo_rtp_socket_connect(struct osmo_rtp_socket *rs, const char *ip, uint16_t port)
271{
272 int rc;
273
274 rc = rtp_session_set_remote_addr(rs->sess, ip, port);
275 if (rc < 0)
276 return rc;
277
278 return osmo_rtp_socket_fdreg(rs);
279}
280
281/*! \brief Send one RTP frame via a RTP socket */
282int osmo_rtp_send_frame(struct osmo_rtp_socket *rs, const uint8_t *payload,
283 unsigned int payload_len, unsigned int duration)
284{
285 mblk_t *mblk;
286 int rc;
287
288 mblk = rtp_session_create_packet(rs->sess, RTP_FIXED_HEADER_SIZE,
289 payload, payload_len);
290 if (!mblk)
291 return -ENOMEM;
292
293 rc = rtp_session_sendm_with_ts(rs->sess, mblk,
294 rs->tx_timestamp);
295 rs->tx_timestamp += duration;
296 if (rc < 0) {
297 /* no need to free() the mblk, as rtp_session_rtp_send()
298 * unconditionally free()s the mblk even in case of
299 * error */
300 return rc;
301 }
302
303 return rc;
304}
305
306/*! \brief Set the payload type of a RTP socket */
307int osmo_rtp_socket_set_pt(struct osmo_rtp_socket *rs, int payload_type)
308{
309 int rc;
310
311 rc = rtp_session_set_payload_type(rs->sess, payload_type);
312 //rtp_session_set_rtcp_report_interval(rs->sess, 5*1000);
313
314 return rc;
315}
316
317/*! \brief completely close the RTP socket and release all resources */
318int osmo_rtp_socket_free(struct osmo_rtp_socket *rs)
319{
320 if (rs->rtp_bfd.list.next && rs->rtp_bfd.list.next != LLIST_POISON1)
321 osmo_fd_unregister(&rs->rtp_bfd);
322
323 if (rs->rtcp_bfd.list.next && rs->rtcp_bfd.list.next != LLIST_POISON1)
324 osmo_fd_unregister(&rs->rtcp_bfd);
325
326 if (rs->sess) {
327 rtp_session_release_sockets(rs->sess);
328 rtp_session_destroy(rs->sess);
329 rs->sess = NULL;
330 }
331
332 talloc_free(rs);
333
334 return 0;
335}
336
337
338int osmo_rtp_get_bound_ip_port(struct osmo_rtp_socket *rs,
339 uint32_t *ip, int *port)
340{
341 int rc;
342 struct sockaddr_storage ss;
343 struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
344 socklen_t alen = sizeof(ss);
345
346 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
347 if (rc < 0)
348 return rc;
349
350 if (ss.ss_family != AF_INET)
351 return -EIO;
352
353 *ip = ntohl(sin->sin_addr.s_addr);
354 *port = rtp_session_get_local_port(rs->sess);
355
356 return 0;
357}
358
359int osmo_rtp_get_bound_addr(struct osmo_rtp_socket *rs,
360 const char **addr, int *port)
361{
362 int rc;
363 struct sockaddr_storage ss;
364 socklen_t alen = sizeof(ss);
365 static char hostbuf[256];
366
367 memset(hostbuf, 0, sizeof(hostbuf));
368
369 rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&ss, &alen);
370 if (rc < 0)
371 return rc;
372
373 rc = getnameinfo((struct sockaddr *)&ss, alen,
374 hostbuf, sizeof(hostbuf), NULL, 0,
375 NI_NUMERICHOST);
376 if (rc < 0)
377 return rc;
378
379 *port = rtp_session_get_local_port(rs->sess);
380 *addr = hostbuf;
381
382 return 0;
383}