Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2017, Digium Inc.
*
* Mark Michelson <mmmichelson@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/test.h"
#include "asterisk/module.h"
#include "asterisk/sdp.h"
#include "asterisk/stream.h"
#include "asterisk/format.h"
#include "asterisk/format_cache.h"
#include "asterisk/format_cap.h"
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static int validate_o_line(struct ast_test *test, const struct ast_sdp_o_line *o_line,
const char *sdpowner, const char *address_type, const char *address)
{
if (!o_line) {
return -1;
}
if (strcmp(o_line->username, sdpowner)) {
ast_test_status_update(test, "Expected o-line SDP owner %s but got %s\n",
sdpowner, o_line->username);
return -1;
}
if (strcmp(o_line->address_type, address_type)) {
ast_test_status_update(test, "Expected o-line SDP address type %s but got %s\n",
address_type, o_line->address_type);
return -1;
}
if (strcmp(o_line->address, address)) {
ast_test_status_update(test, "Expected o-line SDP address %s but got %s\n",
address, o_line->address);
return -1;
}
ast_test_status_update(test, "SDP o-line is as expected!\n");
return 0;
}
static int validate_c_line(struct ast_test *test, const struct ast_sdp_c_line *c_line,
const char *address_type, const char *address)
{
if (strcmp(c_line->address_type, address_type)) {
ast_test_status_update(test, "Expected c-line SDP address type %s but got %s\n",
address_type, c_line->address_type);
return -1;
}
if (strcmp(c_line->address, address)) {
ast_test_status_update(test, "Expected c-line SDP address %s but got %s\n",
address, c_line->address);
return -1;
}
ast_test_status_update(test, "SDP c-line is as expected!\n");
return 0;
}
static int validate_m_line(struct ast_test *test, const struct ast_sdp_m_line *m_line,
const char *media_type, int num_payloads)
{
if (strcmp(m_line->type, media_type)) {
ast_test_status_update(test, "Expected m-line media type %s but got %s\n",
media_type, m_line->type);
return -1;
}
if (m_line->port == 0) {
ast_test_status_update(test, "Expected %s m-line to not be declined\n",
media_type);
return -1;
}
if (ast_sdp_m_get_payload_count(m_line) != num_payloads) {
ast_test_status_update(test, "Expected %s m-line payload count %d but got %d\n",
media_type, num_payloads, ast_sdp_m_get_payload_count(m_line));
ast_test_status_update(test, "SDP %s m-line is as expected\n", media_type);
return 0;
}
static int validate_m_line_declined(struct ast_test *test,
const struct ast_sdp_m_line *m_line, const char *media_type)
{
if (strcmp(m_line->type, media_type)) {
ast_test_status_update(test, "Expected m-line media type %s but got %s\n",
media_type, m_line->type);
return -1;
}
if (m_line->port != 0) {
ast_test_status_update(test, "Expected %s m-line to be declined but got port %u\n",
media_type, m_line->port);
return -1;
}
ast_test_status_update(test, "SDP %s m-line is as expected\n", media_type);
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
return 0;
}
static int validate_rtpmap(struct ast_test *test, const struct ast_sdp_m_line *m_line,
const char *media_name)
{
struct ast_sdp_a_line *a_line;
int i;
for (i = 0; i < ast_sdp_m_get_a_count(m_line); ++i) {
struct ast_sdp_rtpmap *rtpmap;
int match;
a_line = ast_sdp_m_get_a(m_line, i);
if (strcmp(a_line->name, "rtpmap")) {
continue;
}
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (!rtpmap) {
return -1;
}
match = !strcmp(rtpmap->encoding_name, media_name);
ast_sdp_rtpmap_free(rtpmap);
if (match) {
return 0;
}
}
ast_test_status_update(test, "Could not find rtpmap with encoding name %s\n", media_name);
return -1;
}
static enum ast_test_result_state validate_t38(struct ast_test *test, const struct ast_sdp_m_line *m_line)
{
struct ast_sdp_a_line *a_line;
a_line = ast_sdp_m_find_attribute(m_line, "T38FaxVersion", -1);
ast_test_validate(test, a_line && !strcmp(a_line->value, "0"));
a_line = ast_sdp_m_find_attribute(m_line, "T38FaxMaxBitRate", -1);
ast_test_validate(test, a_line && !strcmp(a_line->value, "14400"));
a_line = ast_sdp_m_find_attribute(m_line, "T38FaxRateManagement", -1);
ast_test_validate(test, a_line && !strcmp(a_line->value, "transferredTCF"));
return AST_TEST_PASS;
}
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
AST_TEST_DEFINE(invalid_rtpmap)
{
/* a=rtpmap: is already assumed. This is the part after that */
static const char *invalids[] = {
"J PCMU/8000",
"0 PCMU:8000",
"0 PCMU/EIGHT-THOUSAND",
"0 PCMU/8000million/2",
"0 PCMU//2",
"0 /8000/2",
"0 PCMU/8000/",
"0 PCMU/8000million",
};
int i;
enum ast_test_result_state res = AST_TEST_PASS;
switch(cmd) {
case TEST_INIT:
info->name = "invalid_rtpmap";
info->category = "/main/sdp/";
info->summary = "Ensure invalid rtpmaps are rejected";
info->description =
"Try to convert several invalid rtpmap attributes. If\n"
"any succeeds, the test fails.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
for (i = 0; i < ARRAY_LEN(invalids); ++i) {
struct ast_sdp_a_line *a_line;
struct ast_sdp_rtpmap *rtpmap;
a_line = ast_sdp_a_alloc("rtpmap", invalids[i]);
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (rtpmap) {
ast_test_status_update(test, "Invalid rtpmap '%s' was accepted as valid\n",
invalids[i]);
res = AST_TEST_FAIL;
}
ast_sdp_a_free(a_line);
ast_sdp_rtpmap_free(rtpmap);
}
return res;
}
AST_TEST_DEFINE(rtpmap)
{
static const char *valids[] = {
"0 PCMU/8000",
"107 opus/48000/2",
};
static int payloads[] = {
0,
107,
};
static const char *encoding_names[] = {
"PCMU",
"opus",
};
static int clock_rates[] = {
8000,
48000,
};
static const char *encoding_parameters[] = {
"",
"2",
};
int i;
enum ast_test_result_state res = AST_TEST_PASS;
switch(cmd) {
case TEST_INIT:
info->name = "rtpmap";
info->category = "/main/sdp/";
info->summary = "Ensure rtpmap attribute values are parsed correctly";
info->description =
"Parse several valid rtpmap attributes. Ensure that the parsed values\n"
"are what we expect";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
for (i = 0; i < ARRAY_LEN(valids); ++i) {
struct ast_sdp_a_line *a_line;
struct ast_sdp_rtpmap *rtpmap;
a_line = ast_sdp_a_alloc("rtpmap", valids[i]);
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (!rtpmap) {
ast_test_status_update(test, "Valid rtpmap '%s' was rejected as invalid\n",
valids[i]);
res = AST_TEST_FAIL;
continue;
}
if (rtpmap->payload != payloads[i]) {
ast_test_status_update(test, "RTPmap payload '%d' does not match expected '%d'\n",
rtpmap->payload, payloads[i]);
res = AST_TEST_FAIL;
}
if (strcmp(rtpmap->encoding_name, encoding_names[i])) {
ast_test_status_update(test, "RTPmap encoding_name '%s' does not match expected '%s'\n",
rtpmap->encoding_name, encoding_names[i]);
res = AST_TEST_FAIL;
}
if (rtpmap->clock_rate != clock_rates[i]) {
ast_test_status_update(test, "RTPmap clock rate '%d' does not match expected '%d'\n",
rtpmap->clock_rate, clock_rates[i]);
res = AST_TEST_FAIL;
}
if (strcmp(rtpmap->encoding_parameters, encoding_parameters[i])) {
ast_test_status_update(test, "RTPmap encoding_parameter '%s' does not match expected '%s'\n",
rtpmap->encoding_parameters, encoding_parameters[i]);
res = AST_TEST_FAIL;
}
ast_sdp_a_free(a_line);
ast_sdp_rtpmap_free(rtpmap);
}
return res;
}
AST_TEST_DEFINE(find_attr)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_m_line *m_line;
struct ast_sdp_a_line *a_line;
int idx;
switch(cmd) {
case TEST_INIT:
info->name = "find_attr";
info->category = "/main/sdp/";
info->summary = "Ensure that finding attributes works as expected";
info->description =
"A SDP m-line is created, and attributes are added.\n"
"We then attempt a series of attribute-finding calls that are expected to work\n"
"followed by a series of attribute-finding calls that are expected fo fail.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
m_line = ast_sdp_m_alloc("audio", 666, 1, "RTP/AVP", NULL);
if (!m_line) {
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_a_alloc("foo", "0 bar");
if (!a_line) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_m_add_a(m_line, a_line);
a_line = ast_sdp_a_alloc("foo", "0 bee");
if (!a_line) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_m_add_a(m_line, a_line);
a_line = ast_sdp_a_alloc("baz", "howdy");
if (!a_line) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_m_add_a(m_line, a_line);
/* These should work */
a_line = ast_sdp_m_find_attribute(m_line, "foo", 0);
if (!a_line || strcmp(a_line->value, "0 bar")) {
ast_test_status_update(test, "Failed to find attribute 'foo' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "foo", -1);
if (!a_line || strcmp(a_line->value, "0 bar")) {
ast_test_status_update(test, "Failed to find attribute 'foo' with unspecified payload\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "baz", -1);
if (!a_line || strcmp(a_line->value, "howdy")) {
ast_test_status_update(test, "Failed to find attribute 'baz' with unspecified payload\n");
res = AST_TEST_FAIL;
}
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
idx = ast_sdp_m_find_a_first(m_line, "foo", 0);
if (idx < 0) {
ast_test_status_update(test, "Failed to find first attribute 'foo' with payload '0'\n");
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_m_get_a(m_line, idx);
if (!a_line || strcmp(a_line->value, "0 bar")) {
ast_test_status_update(test, "Find first attribute 'foo' with payload '0' didn't match\n");
res = AST_TEST_FAIL;
}
idx = ast_sdp_m_find_a_next(m_line, idx, "foo", 0);
if (idx < 0) {
ast_test_status_update(test, "Failed to find next attribute 'foo' with payload '0'\n");
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_m_get_a(m_line, idx);
if (!a_line || strcmp(a_line->value, "0 bee")) {
ast_test_status_update(test, "Find next attribute 'foo' with payload '0' didn't match\n");
res = AST_TEST_FAIL;
}
idx = ast_sdp_m_find_a_next(m_line, idx, "foo", 0);
if (0 <= idx) {
ast_test_status_update(test, "Find next attribute 'foo' with payload '0' found too many\n");
res = AST_TEST_FAIL;
}
idx = ast_sdp_m_find_a_first(m_line, "foo", -1);
if (idx < 0) {
ast_test_status_update(test, "Failed to find first attribute 'foo' with unspecified payload\n");
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_m_get_a(m_line, idx);
if (!a_line || strcmp(a_line->value, "0 bar")) {
ast_test_status_update(test, "Find first attribute 'foo' with unspecified payload didn't match\n");
res = AST_TEST_FAIL;
}
idx = ast_sdp_m_find_a_next(m_line, idx, "foo", -1);
if (idx < 0) {
ast_test_status_update(test, "Failed to find next attribute 'foo' with unspecified payload\n");
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_m_get_a(m_line, idx);
if (!a_line || strcmp(a_line->value, "0 bee")) {
ast_test_status_update(test, "Find next attribute 'foo' with unspecified payload didn't match\n");
res = AST_TEST_FAIL;
}
idx = ast_sdp_m_find_a_next(m_line, idx, "foo", -1);
if (0 <= idx) {
ast_test_status_update(test, "Find next attribute 'foo' with unspecified payload found too many\n");
res = AST_TEST_FAIL;
}
/* These should fail */
a_line = ast_sdp_m_find_attribute(m_line, "foo", 1);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'foo' with payload '1'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "baz", 0);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'baz' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "wibble", 0);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'wibble' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "wibble", -1);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'wibble' with unspecified payload\n");
res = AST_TEST_FAIL;
}
end:
ast_sdp_m_free(m_line);
return res;
}
static struct ast_sdp_options *sdp_options_common(void)
{
struct ast_sdp_options *options;
options = ast_sdp_options_alloc();
if (!options) {
return NULL;
}
ast_sdp_options_set_media_address(options, "127.0.0.1");
ast_sdp_options_set_sdpowner(options, "me");
ast_sdp_options_set_rtp_engine(options, "asterisk");
ast_sdp_options_set_impl(options, AST_SDP_IMPL_PJMEDIA);
return options;
}
struct sdp_format {
enum ast_media_type type;
const char *formats;
};
static int build_sdp_option_formats(struct ast_sdp_options *options, int num_streams, const struct sdp_format *formats)
{
int idx;
for (idx = 0; idx < num_streams; ++idx) {
struct ast_format_cap *caps;
if (ast_strlen_zero(formats[idx].formats)) {
continue;
}
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps
|| ast_format_cap_update_by_allow_disallow(caps, formats[idx].formats, 1) < 0) {
ao2_cleanup(caps);
return -1;
}
ast_sdp_options_set_format_cap_type(options, formats[idx].type, caps);
ao2_cleanup(caps);
}
return 0;
}
/*!
* \brief Common method to build an SDP state for a test.
*
* This uses the passed-in formats to create a stream topology, which is then used to create the SDP
* state.
*
* There is an optional test_options field you can use if your test has specific options you need to
* set. If your test does not require anything special, it can just pass NULL for this parameter. If
* you do pass in test_options, this function steals ownership of those options.
*
* \param num_streams The number of elements in the formats array.
* \param formats Array of media types and formats that will be in the state.
* \param opt_num_streams The number of new stream types allowed to create.
* Not used if test_options provided.
* \param opt_formats Array of new stream media types and formats allowed to create.
* NULL if use a default stream creation.
* Not used if test_options provided.
* \param max_streams 0 if set max to max(3, num_streams) else max(max_streams, num_streams)
* Not used if test_options provided.
* \param test_options Optional SDP options.
*/
static struct ast_sdp_state *build_sdp_state(int num_streams, const struct sdp_format *formats,
int opt_num_streams, const struct sdp_format *opt_formats, unsigned int max_streams,
struct ast_sdp_options *test_options)
{
struct ast_stream_topology *topology = NULL;
struct ast_sdp_state *state = NULL;
struct ast_sdp_options *options;
int i;
static const struct sdp_format sdp_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw" },
{ AST_MEDIA_TYPE_VIDEO, "vp8" },
{ AST_MEDIA_TYPE_IMAGE, "t38" },
};
options = sdp_options_common();
if (!options) {
goto end;
}
/* Determine max_streams to allow */
if (!max_streams) {
max_streams = ARRAY_LEN(sdp_formats);
}
if (max_streams < num_streams) {
max_streams = num_streams;
}
ast_sdp_options_set_max_streams(options, max_streams);
/* Determine new stream formats and types allowed */
if (!opt_formats) {
opt_num_streams = ARRAY_LEN(sdp_formats);
opt_formats = sdp_formats;
}
if (build_sdp_option_formats(options, opt_num_streams, opt_formats)) {
goto end;
}
} else {
options = test_options;
}
topology = ast_stream_topology_alloc();
if (!topology) {
goto end;
}
for (i = 0; i < num_streams; ++i) {
struct ast_stream *stream;
stream = ast_stream_alloc("sure_thing", formats[i].type);
if (!stream) {
goto end;
}
if (!ast_strlen_zero(formats[i].formats)) {
struct ast_format_cap *caps;
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps
|| ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {
ao2_cleanup(caps);
ast_stream_free(stream);
goto end;
}
ast_stream_set_formats(stream, caps);
ao2_cleanup(caps);
} else {
ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);
}
if (ast_stream_topology_append_stream(topology, stream) < 0) {
ast_stream_free(stream);
goto end;
}
}
state = ast_sdp_state_alloc(topology, options);
if (!state) {
goto end;
}
end:
ast_stream_topology_free(topology);
if (!state) {
ast_sdp_options_free(options);
}
return state;
}
AST_TEST_DEFINE(topology_to_sdp)
{
enum ast_test_result_state res = AST_TEST_FAIL;
struct ast_sdp_state *sdp_state = NULL;
const struct ast_sdp *sdp = NULL;
struct ast_sdp_m_line *m_line = NULL;
struct sdp_format formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
switch(cmd) {
case TEST_INIT:
info->name = "topology_to_sdp";
info->category = "/main/sdp/";
info->summary = "Convert a topology into an SDP";
info->description =
"Ensure SDPs get converted to expected stream topology";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state = build_sdp_state(ARRAY_LEN(formats), formats,
ARRAY_LEN(formats), formats, 0, NULL);
if (!sdp_state) {
goto end;
}
sdp = ast_sdp_state_get_local_sdp(sdp_state);
if (!sdp) {
goto end;
}
if (validate_o_line(test, sdp->o_line, "me", "IP4", "127.0.0.1")) {
goto end;
}
if (validate_c_line(test, sdp->c_line, "IP4", "127.0.0.1")) {
goto end;
}
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
ast_test_status_update(test, "Unexpected number of streams in generated SDP: %d\n",
ast_sdp_get_m_count(sdp));
goto end;
}
m_line = ast_sdp_get_m(sdp, 0);
if (validate_m_line(test, m_line, "audio", 4)) {
goto end;
}
if (validate_rtpmap(test, m_line, "PCMU")) {
goto end;
}
if (validate_rtpmap(test, m_line, "PCMA")) {
goto end;
}
if (validate_rtpmap(test, m_line, "G722")) {
goto end;
}
if (validate_rtpmap(test, m_line, "opus")) {
goto end;
}
m_line = ast_sdp_get_m(sdp, 1);
if (validate_m_line(test, m_line, "video", 2)) {
goto end;
}
if (validate_rtpmap(test, m_line, "VP8")) {
goto end;
}
if (validate_rtpmap(test, m_line, "H264")) {
goto end;
}
m_line = ast_sdp_get_m(sdp, 2);
if (validate_m_line(test, m_line, "image", 1)) {
goto end;
}
if (validate_t38(test, m_line) != AST_TEST_PASS) {
goto end;
}
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
res = AST_TEST_PASS;
end:
ast_sdp_state_free(sdp_state);
return res;
}
static int validate_formats(struct ast_test *test, struct ast_stream_topology *topology, int index,
enum ast_media_type type, int format_count, const char **expected_formats)
{
struct ast_stream *stream;
struct ast_format_cap *caps;
struct ast_format *format;
int i;
stream = ast_stream_topology_get_stream(topology, index);
if (ast_stream_get_type(stream) != type) {
ast_test_status_update(test, "Unexpected stream type encountered\n");
return -1;
}
caps = ast_stream_get_formats(stream);
if (ast_format_cap_count(caps) != format_count) {
ast_test_status_update(test, "Unexpected format count '%d'. Expecting '%d'\n",
(int) ast_format_cap_count(caps), format_count);
return -1;
}
for (i = 0; i < ast_format_cap_count(caps); ++i) {
format = ast_format_cap_get_format(caps, i);
if (strcmp(ast_format_get_name(format), expected_formats[i])) {
ast_test_status_update(test, "Unexpected format '%s'at index %d. Expected '%s'\n",
ast_format_get_name(format), i, expected_formats[i]);
return -1;
}
}
return 0;
}
AST_TEST_DEFINE(sdp_to_topology)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_state *sdp_state;
const struct ast_sdp *sdp;
struct ast_stream_topology *topology = NULL;
struct sdp_format sdp_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
static const char *expected_audio_formats[] = {
"ulaw",
"alaw",
"g722",
"opus",
};
static const char *expected_video_formats[] = {
"h264",
"vp8",
};
static const char *expected_image_formats[] = {
"t38",
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_to_topology";
info->category = "/main/sdp/";
info->summary = "Convert an SDP into a topology";
info->description =
"Ensure SDPs get converted to expected stream topology";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state = build_sdp_state(ARRAY_LEN(sdp_formats), sdp_formats,
ARRAY_LEN(sdp_formats), sdp_formats, 0, NULL);
if (!sdp_state) {
res = AST_TEST_FAIL;
goto end;
}
sdp = ast_sdp_state_get_local_sdp(sdp_state);
if (!sdp) {
res = AST_TEST_FAIL;
goto end;
}
topology = ast_get_topology_from_sdp(sdp, 0);
if (!topology) {
res = AST_TEST_FAIL;
goto end;
}
if (ast_stream_topology_get_count(topology) != 3) {
ast_test_status_update(test, "Unexpected topology count '%d'. Expecting 3\n",
ast_stream_topology_get_count(topology));
res = AST_TEST_FAIL;
goto end;
}
if (validate_formats(test, topology, 0, AST_MEDIA_TYPE_AUDIO,
ARRAY_LEN(expected_audio_formats), expected_audio_formats)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_formats(test, topology, 1, AST_MEDIA_TYPE_VIDEO,
ARRAY_LEN(expected_video_formats), expected_video_formats)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_formats(test, topology, 2, AST_MEDIA_TYPE_IMAGE,
ARRAY_LEN(expected_image_formats), expected_image_formats)) {
res = AST_TEST_FAIL;
goto end;
}
end:
ast_sdp_state_free(sdp_state);
ast_stream_topology_free(topology);
return res;
}
static int validate_avi_sdp_streams(struct ast_test *test, const struct ast_sdp *sdp)
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
{
struct ast_sdp_m_line *m_line;
if (!sdp) {
return -1;
}
m_line = ast_sdp_get_m(sdp, 0);
if (validate_m_line(test, m_line, "audio", 1)) {
return -1;
}
if (validate_rtpmap(test, m_line, "PCMU")) {
return -1;
}
/* The other audio formats should *NOT* be present */
if (!validate_rtpmap(test, m_line, "PCMA")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "G722")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "opus")) {
return -1;
}
m_line = ast_sdp_get_m(sdp, 1);
if (validate_m_line(test, m_line, "video", 1)) {
return -1;
}
if (validate_rtpmap(test, m_line, "VP8")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "H264")) {
return -1;
}
m_line = ast_sdp_get_m(sdp, 2);
if (validate_m_line(test, m_line, "image", 1)) {
return -1;
}
static enum ast_test_result_state sdp_negotiation_completed_tests(struct ast_test *test,
int offer_num_streams, const struct sdp_format *offer_formats,
int answer_num_streams, const struct sdp_format *answer_formats,
int allowed_ans_num_streams, const struct sdp_format *allowed_ans_formats,
unsigned int max_streams,
int (*validate_sdp)(struct ast_test *test, const struct ast_sdp *sdp))
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_state *sdp_state_offerer = NULL;
struct ast_sdp_state *sdp_state_answerer = NULL;
const struct ast_sdp *offerer_sdp;
const struct ast_sdp *answerer_sdp;
sdp_state_offerer = build_sdp_state(offer_num_streams, offer_formats,
offer_num_streams, offer_formats, max_streams, NULL);
ast_test_status_update(test, "Building offerer SDP state failed\n");
res = AST_TEST_FAIL;
goto end;
}
sdp_state_answerer = build_sdp_state(answer_num_streams, answer_formats,
allowed_ans_num_streams, allowed_ans_formats, max_streams, NULL);
ast_test_status_update(test, "Building answerer SDP state failed\n");
res = AST_TEST_FAIL;
goto end;
}
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (!offerer_sdp) {
ast_test_status_update(test, "Building offerer offer failed\n");
res = AST_TEST_FAIL;
goto end;
}
if (ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp)) {
ast_test_status_update(test, "Setting answerer offer failed\n");
res = AST_TEST_FAIL;
goto end;
}
answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
if (!answerer_sdp) {
ast_test_status_update(test, "Building answerer answer failed\n");
res = AST_TEST_FAIL;
goto end;
}
if (ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp)) {
ast_test_status_update(test, "Setting offerer answer failed\n");
res = AST_TEST_FAIL;
goto end;
}
/*
* Restart SDP negotiations to build the joint SDP on the offerer
* side. Otherwise we will get the original offer for use in
* case of retransmissions.
*/
if (ast_sdp_state_restart_negotiations(sdp_state_offerer)) {
ast_test_status_update(test, "Restarting negotiations failed\n");
res = AST_TEST_FAIL;
goto end;
}
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (!offerer_sdp) {
ast_test_status_update(test, "Building offerer current sdp failed\n");
res = AST_TEST_FAIL;
goto end;
}
if (validate_sdp(test, offerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_sdp(test, answerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
end:
ast_sdp_state_free(sdp_state_offerer);
ast_sdp_state_free(sdp_state_answerer);
return res;
}
AST_TEST_DEFINE(sdp_negotiation_initial)
static const struct sdp_format offerer_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
{ AST_MEDIA_TYPE_IMAGE, "t38" },
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_negotiation_initial";
info->category = "/main/sdp/";
info->summary = "Simulate an initial negotiation";
info->description =
"Initial negotiation tests creating new streams on the answering side.\n"
"After negotiation both offerer and answerer sides should have the same\n"
"expected stream types and formats.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
return sdp_negotiation_completed_tests(test,
ARRAY_LEN(offerer_formats), offerer_formats,
0, NULL,
0, NULL,
validate_avi_sdp_streams);
}
AST_TEST_DEFINE(sdp_negotiation_type_change)
{
static const struct sdp_format offerer_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
{ AST_MEDIA_TYPE_IMAGE, "t38" },
};
static const struct sdp_format answerer_formats[] = {
{ AST_MEDIA_TYPE_IMAGE, "t38" },
{ AST_MEDIA_TYPE_VIDEO, "vp8" },
{ AST_MEDIA_TYPE_AUDIO, "ulaw" },
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_negotiation_type_change";
info->summary = "Simulate a re-negotiation changing stream types";
"Reinvite negotiation tests changing stream types on the answering side.\n"
"After negotiation both offerer and answerer sides should have the same\n"