blob: 404268a685d0858e0c550b7a6936d1d2efb0aef2 [file] [log] [blame]
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +02001#include <stdlib.h>
2#include <unistd.h>
3#include <stdio.h>
4#include <string.h>
5#include <err.h>
6
7#include <osmocom/core/talloc.h>
8#include <osmocom/core/application.h>
9
10#include <openbsc/debug.h>
11#include <openbsc/gsm_data.h>
12#include <openbsc/mgcp.h>
13#include <openbsc/mgcp_internal.h>
Holger Hans Peter Freyther91eeeae2014-07-04 20:55:20 +020014#include <openbsc/rtp.h>
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +020015
16#include "bscconfig.h"
17#ifndef BUILD_MGCP_TRANSCODING
18#error "Requires MGCP transcoding enabled (see --enable-mgcp-transcoding)"
19#endif
20
Jacob Erlbeck909fac62014-05-08 14:08:37 +020021#include "openbsc/mgcp_transcode.h"
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +020022
23uint8_t *audio_frame_l16[] = {
24};
25
26struct rtp_packets {
27 float t;
28 int len;
29 char *data;
30};
31
32struct rtp_packets audio_packets_l16[] = {
33 /* RTP: SeqNo=1, TS=160 */
34 {0.020000, 332,
35 "\x80\x0B\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
36 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
37 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
38 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
39 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
40 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
41 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
42 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
43 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
44 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
45 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
46 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
47 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
48 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
49 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
50 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
51 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
52 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
53 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
54 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
55 "\x00\x00\x40\x13\x5A\x9E\x40\x13\x00\x00\xBF\xED\xA5\x62\xBF\xED"
56 },
57};
58
59struct rtp_packets audio_packets_gsm[] = {
60 /* RTP: SeqNo=1, TS=160 */
61 {0.020000, 45,
62 "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
63 "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
64 "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
65 "\xDE"
66 },
67};
68
69struct rtp_packets audio_packets_gsm_invalid_size[] = {
70 /* RTP: SeqNo=1, TS=160 */
71 {0.020000, 41,
72 "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
73 "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
74 "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
75 "\xDE"
76 },
77};
78
79struct rtp_packets audio_packets_gsm_invalid_data[] = {
80 /* RTP: SeqNo=1, TS=160 */
81 {0.020000, 45,
82 "\x80\x03\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
83 "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
84 "\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE\xEE"
85 "\xEE"
86 },
87};
88
89struct rtp_packets audio_packets_gsm_invalid_ptype[] = {
90 /* RTP: SeqNo=1, TS=160 */
91 {0.020000, 45,
92 "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
93 "\xD4\x7C\xE3\xE9\x62\x50\x39\xF0\xF8\xB4\x68\xEA\x6C\x0E\x81\x1B"
94 "\x56\x2A\xD5\xBC\x69\x9C\xD1\xF0\x66\x7A\xEC\x49\x7A\x33\x3D\x0A"
95 "\xDE"
96 },
97};
98
99struct rtp_packets audio_packets_g729[] = {
100 /* RTP: SeqNo=1, TS=160 */
101 {0.020000, 32,
102 "\x80\x12\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
103 "\xAF\xC2\x81\x40\x00\xFA\xCE\xA4\x21\x7C\xC5\xC3\x4F\xA5\x98\xF5"
104 "\xB2\x95\xC4\xAD"
105 },
106};
107
108struct rtp_packets audio_packets_pcma[] = {
109 /* RTP: SeqNo=1, TS=160 */
110 {0.020000, 172,
111 "\x80\x08\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
112 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
113 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
114 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
115 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
116 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
117 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
118 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
119 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
120 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
121 "\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
122 },
123};
124
125
126
127static int audio_name_to_type(const char *name)
128{
129 if (!strcasecmp(name, "gsm"))
130 return 3;
131#ifdef HAVE_BCG729
132 else if (!strcasecmp(name, "g729"))
133 return 18;
134#endif
135 else if (!strcasecmp(name, "pcma"))
136 return 8;
137 else if (!strcasecmp(name, "l16"))
138 return 11;
139 return -1;
140}
141
142int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst);
143
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200144static int given_configured_endpoint(int in_samples, int out_samples,
145 const char *srcfmt, const char *dstfmt,
146 void **out_ctx, struct mgcp_endpoint **out_endp)
147{
148 int rc;
149 struct mgcp_rtp_end *dst_end;
150 struct mgcp_rtp_end *src_end;
151 struct mgcp_config *cfg;
152 struct mgcp_trunk_config *tcfg;
153 struct mgcp_endpoint *endp;
154
155 cfg = mgcp_config_alloc();
156 tcfg = talloc_zero(cfg, struct mgcp_trunk_config);
157 endp = talloc_zero(tcfg, struct mgcp_endpoint);
158
159
160 tcfg->endpoints = endp;
161 tcfg->number_endpoints = 1;
162 tcfg->cfg = cfg;
163 endp->tcfg = tcfg;
164 endp->cfg = cfg;
165 mgcp_free_endp(endp);
166
167 dst_end = &endp->bts_end;
168 dst_end->payload_type = audio_name_to_type(dstfmt);
169
170 src_end = &endp->net_end;
171 src_end->payload_type = audio_name_to_type(srcfmt);
172
173 if (out_samples) {
174 dst_end->frame_duration_den = dst_end->rate;
175 dst_end->frame_duration_num = out_samples;
176 dst_end->frames_per_packet = 1;
177 dst_end->force_output_ptime = 1;
178 }
179
180 rc = mgcp_transcoding_setup(endp, dst_end, src_end);
Holger Hans Peter Freyther6041c8d2014-06-28 13:24:36 +0200181 if (rc < 0) {
182 printf("setup failed: %s", strerror(-rc));
183 abort();
184 }
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200185
186 *out_ctx = cfg;
187 *out_endp = endp;
188 return 0;
189}
190
191
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200192static int transcode_test(const char *srcfmt, const char *dstfmt,
193 uint8_t *src_pkts, size_t src_pkt_size)
194{
195 char buf[4096] = {0x80, 0};
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200196 void *ctx;
197
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200198 struct mgcp_rtp_end *dst_end;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200199 struct mgcp_process_rtp_state *state;
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200200 struct mgcp_endpoint *endp;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200201 int in_size;
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200202 const int in_samples = 160;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200203 int len, cont;
204
205 printf("== Transcoding test ==\n");
206 printf("converting %s -> %s\n", srcfmt, dstfmt);
207
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200208 given_configured_endpoint(in_samples, 0, srcfmt, dstfmt, &ctx, &endp);
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200209
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200210 dst_end = &endp->bts_end;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200211 state = dst_end->rtp_process_data;
212 OSMO_ASSERT(state != NULL);
213
214 in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
215 OSMO_ASSERT(sizeof(buf) >= in_size + 12);
216
217 memcpy(buf, src_pkts, src_pkt_size);
218
219 len = src_pkt_size;
220
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200221 cont = mgcp_transcoding_process_rtp(endp, dst_end,
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200222 buf, &len, sizeof(buf));
Holger Hans Peter Freyther6041c8d2014-06-28 13:24:36 +0200223 if (cont < 0) {
224 printf("processing failed: %s", strerror(-cont));
225 abort();
226 }
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200227
228 if (len < 24) {
229 printf("encoded: %s\n", osmo_hexdump((unsigned char *)buf, len));
230 } else {
231 const char *str = osmo_hexdump((unsigned char *)buf, len);
232 int i = 0;
233 const int prefix = 4;
234 const int cutlen = 48;
235 int nchars = 0;
236
237 printf("encoded:\n");
238 do {
239 nchars = printf("%*s%-.*s", prefix, "", cutlen, str + i);
240 i += nchars - prefix;
241 printf("\n");
242 } while (nchars - prefix >= cutlen);
243 }
Holger Hans Peter Freyther4fb7e642014-06-28 00:10:10 +0200244 printf("counted: %d\n", cont);
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200245 talloc_free(ctx);
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200246 return 0;
247}
248
Holger Hans Peter Freyther91eeeae2014-07-04 20:55:20 +0200249static void test_rtp_seq_state(void)
250{
251 char buf[4096];
252 int len;
253 int cont;
254 void *ctx;
255 struct mgcp_endpoint *endp;
256 struct mgcp_process_rtp_state *state;
257 struct rtp_hdr *hdr;
258 uint32_t ts_no;
259 uint16_t seq_no;
260
261 given_configured_endpoint(160, 0, "pcma", "l16", &ctx, &endp);
262 state = endp->bts_end.rtp_process_data;
263 OSMO_ASSERT(!state->is_running);
264 OSMO_ASSERT(state->next_seq == 0);
265 OSMO_ASSERT(state->next_time == 0);
266
267 /* initialize packet */
268 len = audio_packets_pcma[0].len;
269 memcpy(buf, audio_packets_pcma[0].data, len);
270 cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
271 OSMO_ASSERT(cont >= 0);
272 OSMO_ASSERT(state->is_running);
273 OSMO_ASSERT(state->next_seq == 2);
274 OSMO_ASSERT(state->next_time = 240);
275
276 /* verify that the right timestamp was written */
277 OSMO_ASSERT(len == audio_packets_pcma[0].len);
278 hdr = (struct rtp_hdr *) &buf[0];
279
280 memcpy(&ts_no, &hdr->timestamp, sizeof(ts_no));
281 OSMO_ASSERT(htonl(ts_no) == 160);
282 memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
283 OSMO_ASSERT(htons(seq_no) == 1);
284 /* Check the right sequence number is written */
285 state->next_seq = 1234;
286 len = audio_packets_pcma[0].len;
287 memcpy(buf, audio_packets_pcma[0].data, len);
288 cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, len);
289 OSMO_ASSERT(cont >= 0);
290 OSMO_ASSERT(len == audio_packets_pcma[0].len);
291 hdr = (struct rtp_hdr *) &buf[0];
292
293 memcpy(&seq_no, &hdr->sequence, sizeof(seq_no));
294 OSMO_ASSERT(htons(seq_no) == 1234);
295
296 talloc_free(ctx);
297}
298
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200299static int test_repacking(int in_samples, int out_samples, int no_transcode)
300{
301 char buf[4096] = {0x80, 0};
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200302 int cc;
303 struct mgcp_endpoint *endp;
304 void *ctx;
305
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200306 struct mgcp_process_rtp_state *state;
307 int in_cnt;
308 int out_size;
309 int in_size;
310 uint32_t ts = 0;
311 uint16_t seq = 0;
312 const char *srcfmt = "pcma";
313 const char *dstfmt = no_transcode ? "pcma" : "l16";
314
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200315 printf("== Transcoding test ==\n");
316 printf("converting %s -> %s\n", srcfmt, dstfmt);
317
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200318 given_configured_endpoint(in_samples, out_samples, srcfmt, dstfmt, &ctx, &endp);
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200319
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200320 state = endp->bts_end.rtp_process_data;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200321 OSMO_ASSERT(state != NULL);
322
323 in_size = mgcp_transcoding_get_frame_size(state, in_samples, 0);
324 OSMO_ASSERT(sizeof(buf) >= in_size + 12);
325
326 out_size = mgcp_transcoding_get_frame_size(state, -1, 1);
327 OSMO_ASSERT(sizeof(buf) >= out_size + 12);
328
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200329 buf[1] = endp->net_end.payload_type;
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200330 *(uint16_t*)(buf+2) = htons(1);
331 *(uint32_t*)(buf+4) = htonl(0);
332 *(uint32_t*)(buf+8) = htonl(0xaabbccdd);
333
334 for (in_cnt = 0; in_cnt < 16; in_cnt++) {
335 int cont;
336 int len;
337
338 /* fake PCMA data */
339 printf("generating %d %s input samples\n", in_samples, srcfmt);
340 for (cc = 0; cc < in_samples; cc++)
341 buf[12+cc] = cc;
342
343 *(uint16_t*)(buf+2) = htonl(seq);
344 *(uint32_t*)(buf+4) = htonl(ts);
345
346 seq += 1;
347 ts += in_samples;
348
349 cc += 12; /* include RTP header */
350
351 len = cc;
352
353 do {
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200354 cont = mgcp_transcoding_process_rtp(endp, &endp->bts_end,
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200355 buf, &len, sizeof(buf));
356 if (cont == -EAGAIN) {
357 fprintf(stderr, "Got EAGAIN\n");
358 break;
359 }
360
Holger Hans Peter Freyther6041c8d2014-06-28 13:24:36 +0200361 if (cont < 0) {
362 printf("processing failed: %s", strerror(-cont));
363 abort();
364 }
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200365
366 len -= 12; /* ignore RTP header */
367
Holger Hans Peter Freyther4fb7e642014-06-28 00:10:10 +0200368 printf("got %d %s output frames (%d octets) count=%d\n",
369 len / out_size, dstfmt, len, cont);
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200370
371 len = cont;
372 } while (len > 0);
373 }
Holger Hans Peter Freyther83cbac22014-06-22 21:55:50 +0200374
375 talloc_free(ctx);
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200376 return 0;
377}
378
379int main(int argc, char **argv)
380{
381 osmo_init_logging(&log_info);
382
383 printf("=== Transcoding Good Cases ===\n");
384
385 transcode_test("l16", "l16",
386 (uint8_t *)audio_packets_l16[0].data,
387 audio_packets_l16[0].len);
388 transcode_test("l16", "gsm",
389 (uint8_t *)audio_packets_l16[0].data,
390 audio_packets_l16[0].len);
391 transcode_test("l16", "pcma",
392 (uint8_t *)audio_packets_l16[0].data,
393 audio_packets_l16[0].len);
394 transcode_test("gsm", "l16",
395 (uint8_t *)audio_packets_gsm[0].data,
396 audio_packets_gsm[0].len);
397 transcode_test("gsm", "gsm",
398 (uint8_t *)audio_packets_gsm[0].data,
399 audio_packets_gsm[0].len);
400 transcode_test("gsm", "pcma",
401 (uint8_t *)audio_packets_gsm[0].data,
402 audio_packets_gsm[0].len);
403 transcode_test("pcma", "l16",
404 (uint8_t *)audio_packets_pcma[0].data,
405 audio_packets_pcma[0].len);
406 transcode_test("pcma", "gsm",
407 (uint8_t *)audio_packets_pcma[0].data,
408 audio_packets_pcma[0].len);
409 transcode_test("pcma", "pcma",
410 (uint8_t *)audio_packets_pcma[0].data,
411 audio_packets_pcma[0].len);
412
413 printf("=== Transcoding Bad Cases ===\n");
414
415 printf("Invalid size:\n");
416 transcode_test("gsm", "pcma",
417 (uint8_t *)audio_packets_gsm_invalid_size[0].data,
418 audio_packets_gsm_invalid_size[0].len);
419
420 printf("Invalid data:\n");
421 transcode_test("gsm", "pcma",
422 (uint8_t *)audio_packets_gsm_invalid_data[0].data,
423 audio_packets_gsm_invalid_data[0].len);
424
425 printf("Invalid payload type:\n");
426 transcode_test("gsm", "pcma",
427 (uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
428 audio_packets_gsm_invalid_ptype[0].len);
429
430 printf("=== Repacking ===\n");
431
432 test_repacking(160, 160, 0);
433 test_repacking(160, 160, 1);
434 test_repacking(160, 80, 0);
435 test_repacking(160, 80, 1);
436 test_repacking(160, 320, 0);
437 test_repacking(160, 320, 1);
438 test_repacking(160, 240, 0);
439 test_repacking(160, 240, 1);
440 test_repacking(160, 100, 0);
441 test_repacking(160, 100, 1);
Holger Hans Peter Freyther91eeeae2014-07-04 20:55:20 +0200442 test_rtp_seq_state();
Jacob Erlbeck84a45cb2014-04-08 16:10:04 +0200443
444 return 0;
445}
446