Asterisk - The Open Source Telephony Project GIT-master-57e2743
Loading...
Searching...
No Matches
test_res_rtp.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2019, Sangoma, Inc.
5 *
6 * Ben Ford <bford@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \file
21 * \brief RTP/RTCP Unit Tests
22 *
23 * \author Ben Ford <bford@digium.com>
24 *
25 */
26
27/*** MODULEINFO
28 <depend>TEST_FRAMEWORK</depend>
29 <support_level>core</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include "asterisk/module.h"
35#include "asterisk/test.h"
36#include "asterisk/rtp_engine.h"
39#include <assert.h>
40#include <sched.h>
41
43 TEST_TYPE_NONE = 0, /* No special setup required */
44 TEST_TYPE_NACK, /* Enable NACK */
45 TEST_TYPE_REMB, /* Enable REMB */
46 TEST_TYPE_STD_RTCP, /* Let the stack do RTCP */
47};
48
55
56static int test_init_rtp_instances(struct ast_rtp_instance **instance1,
57 struct ast_rtp_instance **instance2, struct ast_sched_context *test_sched,
58 enum test_type type)
59{
60 struct ast_sockaddr addr1;
61 struct ast_sockaddr addr2;
63
64 ast_sockaddr_parse(&addr1, "127.0.0.1", 0);
65 ast_sockaddr_parse(&addr2, "127.0.0.1", 0);
66
67 *instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr1, "instance1");
68 *instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr2, "instance2");
69 if (!instance1 || !instance2) {
70 return -1;
71 }
72
73 ast_rtp_instance_set_channel_id(*instance1, "instance1");
74 ast_rtp_instance_set_channel_id(*instance2, "instance2");
75
76 if (type == TEST_TYPE_STD_RTCP) {
78 }
79
81 AST_RTP_PROPERTY_RTCP, rtcp_type);
83 AST_RTP_PROPERTY_RTCP, rtcp_type);
84
85 if (type == TEST_TYPE_NACK) {
90 } else if (type == TEST_TYPE_REMB) {
93 }
94
95 ast_rtp_instance_get_local_address(*instance1, &addr1);
96 ast_rtp_instance_set_remote_address(*instance2, &addr1);
97
98 ast_rtp_instance_get_local_address(*instance2, &addr2);
99 ast_rtp_instance_set_remote_address(*instance1, &addr2);
100
101 ast_rtp_instance_reset_test_engine(*instance1);
102
103 ast_rtp_instance_activate(*instance1);
104 ast_rtp_instance_activate(*instance2);
105
106 return 0;
107}
108
109static void test_write_frames(struct ast_rtp_instance *instance, int seqno, int num)
110{
111 char data[320] = "";
112 struct ast_frame frame_out = {
114 .subclass.format = ast_format_ulaw,
115 .data.ptr = data,
116 .datalen = 160,
117 };
118 int index;
119
121
122 for (index = 0; index < num; index++) {
123 frame_out.seqno = seqno + index;
124 ast_rtp_instance_write(instance, &frame_out);
125 }
126}
127
128static void test_read_frames(struct ast_rtp_instance *instance, int num)
129{
130 struct ast_frame *frame_in;
131 int index;
132
133 for (index = 0; index < num; index++) {
134 frame_in = ast_rtp_instance_read(instance, 0);
135 if (frame_in) {
136 ast_frfree(frame_in);
137 }
138 }
139}
140
141static void test_write_and_read_frames(struct ast_rtp_instance *instance1,
142 struct ast_rtp_instance *instance2, int seqno, int num)
143{
144 test_write_frames(instance1, seqno, num);
145 test_read_frames(instance2, num);
146}
147
148
149/*
150 * Unfortunately, we can't use usleep() to create
151 * packet spacing because there are signals in use
152 * which cause usleep to immediately return. Instead
153 * we have to spin. :(
154 */
155static void SLEEP_SPINNER(int ms)
156{
157 struct timeval a = ast_tvnow();
158
159 while(1) {
160 sched_yield();
161 if (ast_remaining_ms(a, ms) <= 0) {
162 break;
163 }
164 }
165}
166
167/*
168 * This function is NOT really a reliable implementation.
169 * Its purpose is only to aid in code development in res_rtp_asterisk.
170 */
172 struct ast_rtp_instance *instance2, int howlong, int rtcp_interval)
173{
174 char data[320] = "";
175 int pktinterval = 20;
176
177 struct ast_frame frame_out1 = {
179 .subclass.format = ast_format_ulaw,
180 .seqno = 4556,
181 .data.ptr = data,
182 .datalen = 160,
183 .samples = 1,
184 .len = pktinterval,
185 .ts = 4622295,
186 };
187 struct ast_frame frame_out2 = {
189 .subclass.format = ast_format_ulaw,
190 .seqno = 6554,
191 .data.ptr = data,
192 .datalen = 160,
193 .samples = 1,
194 .len = pktinterval,
195 .ts = 8622295,
196 };
197 struct ast_frame *frame_in1;
198 struct ast_frame *frame_in2;
199 int index;
200 int num;
201 int rtcpnum;
202 int reverse = 1;
203 int send_rtcp = 0;
204
205 num = howlong / pktinterval;
206
207 rtcpnum = rtcp_interval / pktinterval;
208
213
214 for (index = 0; index < num; index++) {
215 struct timeval start = ast_tvnow();
216 time_t ms;
217
218 if (index == 1) {
223 }
224 frame_out1.seqno += index;
225 frame_out1.delivery = start;
226 frame_out1.ts += frame_out1.len;
227 ast_rtp_instance_write(instance1, &frame_out1);
228
229 if (send_rtcp && index && (index % rtcpnum == 0)) {
230 ast_rtp_instance_queue_report(instance1);
231 }
232
233 frame_in2 = ast_rtp_instance_read(instance2, 0);
234 ast_frfree(frame_in2);
235 frame_in2 = ast_rtp_instance_read(instance2, 1);
236 ast_frfree(frame_in2);
237
238 if (reverse) {
239 frame_out2.seqno += index;
240 frame_out2.delivery = ast_tvnow();
241 frame_out2.ts += frame_out2.len;
242 ast_rtp_instance_write(instance2, &frame_out2);
243
244 if (send_rtcp && index && (index % rtcpnum == 0)) {
245 ast_rtp_instance_queue_report(instance2);
246 }
247
248 frame_in1 = ast_rtp_instance_read(instance1, 0);
249 ast_frfree(frame_in1);
250 frame_in1 = ast_rtp_instance_read(instance1, 1);
251 ast_frfree(frame_in1);
252
253 }
254
255 ms = frame_out1.len - ast_tvdiff_ms(ast_tvnow(),start);
256 ms += (index % 2 ? 5 : 12);
257 ms += (index % 3 ? 2 : 30);
258 SLEEP_SPINNER(ms);
259 }
260}
261
262/* Verify failed RTP allocation without a scheduler is safe and releases its resources. */
263AST_TEST_DEFINE(rtp_port_exhaustion)
264{
267 struct ast_sockaddr address;
269
270 switch (cmd) {
271 case TEST_INIT:
272 info->name = "rtp_port_exhaustion";
273 info->category = "/res/res_rtp/";
274 info->summary = "RTP port exhaustion without a scheduler";
275 info->description =
276 "Exhaust a per-instance RTP port range with no scheduler, as used by "
277 "UnicastRTP. Verify allocation fails safely and succeeds after the "
278 "occupied port is released.";
279 return AST_TEST_NOT_RUN;
280 case TEST_EXECUTE:
281 break;
282 }
283
284 ast_sockaddr_parse(&address, "127.0.0.1", 0);
285 occupied = ast_rtp_instance_new("asterisk", NULL, &address, NULL);
286 if (!occupied) {
287 ast_test_status_update(test, "Unable to allocate the initial RTP instance\n");
288 return AST_TEST_FAIL;
289 }
290
292 /* The occupied port is the only even port in this range. */
293 options.port_start = ast_sockaddr_port(&address);
294 options.port_end = options.port_start + 1;
295 instance = ast_rtp_instance_new_with_options("asterisk", NULL, &address,
296 NULL, &options);
297 if (instance) {
298 ast_test_status_update(test, "Allocation succeeded with an exhausted port range\n");
299 return AST_TEST_FAIL;
300 }
301
302 ast_rtp_instance_destroy(occupied);
303 occupied = NULL;
304 instance = ast_rtp_instance_new_with_options("asterisk", NULL, &address,
305 NULL, &options);
306 if (!instance) {
307 ast_test_status_update(test, "Unable to reuse the released RTP port\n");
308 return AST_TEST_FAIL;
309 }
311 if (ast_sockaddr_port(&address) != options.port_start) {
312 ast_test_status_update(test, "RTP instance did not reuse the released port\n");
313 return AST_TEST_FAIL;
314 }
315
316 return AST_TEST_PASS;
317}
318
319AST_TEST_DEFINE(nack_no_packet_loss)
320{
324
325 switch (cmd) {
326 case TEST_INIT:
327 info->name = "nack_no_packet_loss";
328 info->category = "/res/res_rtp/";
329 info->summary = "nack no packet loss unit test";
330 info->description =
331 "Tests sending packets with no packet loss and "
332 "validates that the send buffer stores sent packets "
333 "and the receive buffer is empty";
334 return AST_TEST_NOT_RUN;
335 case TEST_EXECUTE:
336 break;
337 }
338
339 test_sched = ast_sched_context_create();
340
341 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
342 ast_log(LOG_ERROR, "Failed to initialize test!\n");
343 return AST_TEST_FAIL;
344 }
345
346 test_write_and_read_frames(instance1, instance2, 1000, 10);
347
348 ast_test_validate(test, ast_rtp_instance_get_send_buffer_count(instance1) == 10,
349 "Send buffer did not have the expected count of 10");
350
351 ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
352 "Receive buffer did not have the expected count of 0");
353
354 return AST_TEST_PASS;
355}
356
357AST_TEST_DEFINE(nack_nominal)
358{
362
363 switch (cmd) {
364 case TEST_INIT:
365 info->name = "nack_nominal";
366 info->category = "/res/res_rtp/";
367 info->summary = "nack nominal unit test";
368 info->description =
369 "Tests sending packets with some packet loss and "
370 "validates that a NACK request is sent on reaching "
371 "the triggering amount of lost packets";
372 return AST_TEST_NOT_RUN;
373 case TEST_EXECUTE:
374 break;
375 }
376
377 test_sched = ast_sched_context_create();
378
379 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
380 ast_log(LOG_ERROR, "Failed to initialize test!\n");
381 return AST_TEST_FAIL;
382 }
383
384 /* Start normally */
385 test_write_and_read_frames(instance1, instance2, 1000, 10);
386
387 /* Set the number of packets to drop when we send them next */
388 ast_rtp_instance_drop_packets(instance2, 10);
389 test_write_and_read_frames(instance1, instance2, 1010, 10);
390
391 /* Send enough packets to reach the NACK trigger */
392 test_write_and_read_frames(instance1, instance2, 1020, ast_rtp_instance_get_recv_buffer_max(instance2) / 2);
393
394 /* This needs to be read as RTCP */
395 test_read_frames(instance1, 1);
396
397 /* We should have the missing packets to read now */
398 test_read_frames(instance2, 10);
399
400 ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
401 "Receive buffer did not have the expected count of 0");
402
403 return AST_TEST_PASS;
404}
405
406AST_TEST_DEFINE(nack_overflow)
407{
411 int max_packets;
412
413 switch (cmd) {
414 case TEST_INIT:
415 info->name = "nack_overflow";
416 info->category = "/res/res_rtp/";
417 info->summary = "nack overflow unit test";
418 info->description =
419 "Tests that when the buffer hits its capacity, we "
420 "queue all the packets we currently have stored";
421 return AST_TEST_NOT_RUN;
422 case TEST_EXECUTE:
423 break;
424 }
425
426 test_sched = ast_sched_context_create();
427
428 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
429 ast_log(LOG_ERROR, "Failed to initialize test!\n");
430 return AST_TEST_FAIL;
431 }
432
433 /* Start normally */
434 test_write_and_read_frames(instance1, instance2, 1000, 10);
435
436 /* Send enough packets to fill the buffer */
437 max_packets = ast_rtp_instance_get_recv_buffer_max(instance2);
438 test_write_and_read_frames(instance1, instance2, 1020, max_packets);
439
440 ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == max_packets,
441 "Receive buffer did not have the expected count of max buffer size");
442
443 /* Send the packet that will overflow the buffer */
444 test_write_and_read_frames(instance1, instance2, 1020 + max_packets, 1);
445
446 ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
447 "Receive buffer did not have the expected count of 0");
448
449 return AST_TEST_PASS;
450}
451
452AST_TEST_DEFINE(lost_packet_stats_nominal)
453{
457 struct ast_rtp_instance_stats stats = { 0, };
459
460 switch (cmd) {
461 case TEST_INIT:
462 info->name = "lost_packet_stats_nominal";
463 info->category = "/res/res_rtp/";
464 info->summary = "lost packet stats nominal unit test";
465 info->description =
466 "Tests that when some packets are lost, we calculate that "
467 "loss correctly when doing lost packet statistics";
468 return AST_TEST_NOT_RUN;
469 case TEST_EXECUTE:
470 break;
471 }
472
473 test_sched = ast_sched_context_create();
474
475 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
476 ast_log(LOG_ERROR, "Failed to initialize test!\n");
477 return AST_TEST_FAIL;
478 }
479
480 /* Start normally */
481 test_write_and_read_frames(instance1, instance2, 1000, 10);
482
483 /* Send some more packets, but with a gap */
484 test_write_and_read_frames(instance1, instance2, 1015, 5);
485
486 /* Send a RR to calculate lost packet statistics. We should be missing 5 packets */
487 ast_rtp_instance_queue_report(instance1);
488 test_write_frames(instance2, 1000, 1);
489
490 /* Check RTCP stats to see if we got the expected packet loss count */
491 ast_rtp_instance_get_stats(instance2, &stats, stat);
492 ast_test_validate(test, stats.rxploss == 5 && stats.local_minrxploss == 5 &&
493 stats.local_maxrxploss == 5, "Condition of 5 lost packets was not met");
494
495 /* Drop 3 before writing 5 more */
496 test_write_and_read_frames(instance1, instance2, 1023, 5);
497
498 ast_rtp_instance_queue_report(instance1);
499 test_write_frames(instance2, 1001, 1);
500 ast_rtp_instance_get_stats(instance2, &stats, stat);
501
502 /* Should now be missing 8 total packets with a change in min */
503 ast_test_validate(test, stats.rxploss == 8 && stats.local_minrxploss == 3 &&
504 stats.local_maxrxploss == 5);
505
506 /* Write 5 more with no gaps */
507 test_write_and_read_frames(instance1, instance2, 1028, 5);
508
509 ast_rtp_instance_queue_report(instance1);
510 test_write_frames(instance2, 1002, 1);
511 ast_rtp_instance_get_stats(instance2, &stats, stat);
512
513 /* Should still only be missing 8 total packets */
514 ast_test_validate(test, stats.rxploss == 8 && stats.local_minrxploss == 3 &&
515 stats.local_maxrxploss == 5);
516
517 /* Now drop 1, write another 5, drop 8, and then write 5 */
518 test_write_and_read_frames(instance1, instance2, 1034, 5);
519 test_write_and_read_frames(instance1, instance2, 1047, 5);
520
521 ast_rtp_instance_queue_report(instance1);
522 test_write_frames(instance2, 1003, 1);
523 ast_rtp_instance_get_stats(instance2, &stats, stat);
524
525 /* Now it should be missing 17 total packets, with a change in max */
526 ast_test_validate(test, stats.rxploss == 17 && stats.local_minrxploss == 3 &&
527 stats.local_maxrxploss == 9);
528
529 return AST_TEST_PASS;
530}
531
532AST_TEST_DEFINE(remb_nominal)
533{
537 RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
538 /* Use the structure softmix_remb_collector uses to store information for REMB */
539 struct ast_rtp_rtcp_feedback feedback = {
541 .remb.br_exp = 0,
542 .remb.br_mantissa = 1000,
543 };
544 struct ast_frame frame_out = {
546 .subclass.integer = AST_RTP_RTCP_PSFB,
547 .data.ptr = &feedback,
548 .datalen = sizeof(feedback),
549 };
550 struct ast_rtp_rtcp_feedback *received_feedback;
551
552 switch (cmd) {
553 case TEST_INIT:
554 info->name = "remb_nominal";
555 info->category = "/res/res_rtp/";
556 info->summary = "remb nominal unit test";
557 info->description =
558 "Tests sending and receiving a REMB packet";
559 return AST_TEST_NOT_RUN;
560 case TEST_EXECUTE:
561 break;
562 }
563
564 test_sched = ast_sched_context_create();
565
566 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_REMB)) < 0) {
567 ast_log(LOG_ERROR, "Failed to initialize test!\n");
568 return AST_TEST_FAIL;
569 }
570
571 /* The schedid must be 0 or greater, so let's do that now */
572 ast_rtp_instance_set_schedid(instance1, 0);
573
574 ast_rtp_instance_write(instance1, &frame_out);
575
576 /* Verify the high level aspects of the frame */
577 frame_in = ast_rtp_instance_read(instance2, 0);
578 ast_test_validate(test, frame_in != NULL, "Did not receive a REMB frame");
579 ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
580 "REMB frame did not have the expected frametype");
581 ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_PSFB,
582 "REMB frame did not have the expected subclass integer");
583
584 /* Verify the actual REMB information itself */
585 received_feedback = frame_in->data.ptr;
586 ast_test_validate(test, received_feedback->fmt == AST_RTP_RTCP_FMT_REMB,
587 "REMB frame did not have the expected feedback format");
588 ast_test_validate(test, received_feedback->remb.br_exp == feedback.remb.br_exp,
589 "REMB received exponent did not match sent exponent");
590 ast_test_validate(test, received_feedback->remb.br_mantissa == feedback.remb.br_mantissa,
591 "REMB received mantissa did not match sent mantissa");
592
593 return AST_TEST_PASS;
594}
595
596AST_TEST_DEFINE(sr_rr_nominal)
597{
601 RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
602
603 switch (cmd) {
604 case TEST_INIT:
605 info->name = "sr_rr_nominal";
606 info->category = "/res/res_rtp/";
607 info->summary = "SR/RR nominal unit test";
608 info->description =
609 "Tests sending SR/RR and receiving it; includes SDES";
610 return AST_TEST_NOT_RUN;
611 case TEST_EXECUTE:
612 break;
613 }
614
615 test_sched = ast_sched_context_create();
616
617 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
618 ast_log(LOG_ERROR, "Failed to initialize test!\n");
619 return AST_TEST_FAIL;
620 }
621
622 test_write_and_read_frames(instance1, instance2, 1000, 10);
623
624 /*
625 * Set the send_report flag so we send a sender report instead of normal RTP. We
626 * also need to ensure that SDES processed.
627 */
628 ast_rtp_instance_queue_report(instance1);
629 test_write_frames(instance1, 1010, 1);
630
631 frame_in = ast_rtp_instance_read(instance2, 0);
632 ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
633 "Sender report frame did not have the expected frametype");
634 ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_SR,
635 "Sender report frame did not have the expected subclass integer");
636 ast_test_validate(test, ast_rtp_instance_get_sdes_received(instance2) == 1,
637 "SDES was never processed for sender report");
638
639 ast_frfree(frame_in);
640
641 /* Set the send_report flag so we send a receiver report instead of normal RTP */
642 ast_rtp_instance_queue_report(instance1);
643 test_write_frames(instance1, 1010, 1);
644
645 frame_in = ast_rtp_instance_read(instance2, 0);
646 ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
647 "Receiver report frame did not have the expected frametype");
648 ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_RR,
649 "Receiver report frame did not have the expected subclass integer");
650
651 return AST_TEST_PASS;
652}
653
654AST_TEST_DEFINE(fir_nominal)
655{
659 RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
660 struct ast_frame frame_out = {
662 .subclass.integer = AST_CONTROL_VIDUPDATE,
663 };
664
665 switch (cmd) {
666 case TEST_INIT:
667 info->name = "fir_nominal";
668 info->category = "/res/res_rtp/";
669 info->summary = "fir nominal unit test";
670 info->description =
671 "Tests sending and receiving a FIR packet";
672 return AST_TEST_NOT_RUN;
673 case TEST_EXECUTE:
674 break;
675 }
676
677 test_sched = ast_sched_context_create();
678
679 if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
680 ast_log(LOG_ERROR, "Failed to initialize test!\n");
681 return AST_TEST_FAIL;
682 }
683
684 /* Send some packets to learn SSRC */
685 test_write_and_read_frames(instance2, instance1, 1000, 10);
686
687 /* The schedid must be 0 or greater, so let's do that now */
688 ast_rtp_instance_set_schedid(instance1, 0);
689
690 /*
691 * This will not directly write a frame out, but cause Asterisk to see it as a FIR
692 * request, which will then trigger rtp_write_rtcp_fir, which will send out the
693 * appropriate packet.
694 */
695 ast_rtp_instance_write(instance1, &frame_out);
696
697 /*
698 * We only receive one frame, the FIR request. It won't have a subclass integer of
699 * 206 (PSFB) because ast_rtcp_interpret sets it to 18 (AST_CONTROL_VIDUPDATE), so
700 * check for that.
701 */
702 frame_in = ast_rtp_instance_read(instance2, 0);
703 ast_test_validate(test, frame_in != NULL, "Did not receive a FIR frame");
704 ast_test_validate(test, frame_in->frametype == AST_FRAME_CONTROL,
705 "FIR frame did not have the expected frametype");
706 ast_test_validate(test, frame_in->subclass.integer == AST_CONTROL_VIDUPDATE,
707 "FIR frame did not have the expected subclass integer");
708
709 return AST_TEST_PASS;
710}
711
712/*
713 * This test should not normally be run. Its only purpose is to
714 * aid in code development.
715 */
717{
721
722 switch (cmd) {
723 case TEST_INIT:
724 info->name = "mes";
725 info->category = "/res/res_rtp/";
726 info->summary = "Media Experience Score";
727 info->description =
728 "Tests calculation of Media Experience Score (only run by explicit request)";
729 info->explicit_only = 1;
730 return AST_TEST_NOT_RUN;
731 case TEST_EXECUTE:
732 break;
733 }
734
735 test_sched = ast_sched_context_create();
736 ast_sched_start_thread(test_sched);
737
738 if ((test_init_rtp_instances(&instance1, &instance2,
739 test_sched, TEST_TYPE_NONE)) < 0) {
740 ast_log(LOG_ERROR, "Failed to initialize test!\n");
741 return AST_TEST_FAIL;
742 }
743
745 instance1, instance2, 1000, 5000);
746
747 return AST_TEST_PASS;
748}
749
750AST_TEST_DEFINE(payload_merge_abandoned_offer)
751{
756 int offered_initialized = 0;
757 int active_initialized = 0;
758
759 switch (cmd) {
760 case TEST_INIT:
761 info->name = "payload_merge_abandoned_offer";
762 info->category = "/res/res_rtp/";
763 info->summary = "Verify mappings retained after an uncommitted payload merge";
764 info->description =
765 "Characterizes the state left behind when an SDP offer's payloads "
766 "are merged into an active RTP mapping but no later copy commits "
767 "or rolls back that offer.";
768 return AST_TEST_NOT_RUN;
769 case TEST_EXECUTE:
770 break;
771 }
772
774 ast_test_status_update(test, "Unable to initialize RTP codecs\n");
776 goto cleanup;
777 }
778 offered_initialized = 1;
780 ast_test_status_update(test, "Unable to initialize RTP codecs\n");
782 goto cleanup;
783 }
784 active_initialized = 1;
785
786 /* Use dynamic PTs so lookup cannot fall back to the global static table. */
788 &active, NULL, 96, "audio", "PCMU", 0)
790 &offered, NULL, 97, "audio", "PCMA", 0)) {
791 ast_test_status_update(test, "Unable to create dynamic RTP mappings\n");
793 goto cleanup;
794 }
795 ast_rtp_codecs_payloads_xover(&active, &active, NULL);
796 ast_rtp_codecs_payloads_xover(&offered, &offered, NULL);
797
798 ast_rtp_codecs_payloads_merge(&offered, &active, NULL);
799
800 payload = ast_rtp_codecs_get_payload(&active, 96);
801 if (!payload || !payload->asterisk_format
803 ast_test_status_update(test, "Active PCMU payload 96 was not retained by merge\n");
805 }
807
808 payload = ast_rtp_codecs_get_payload(&active, 97);
809 if (!payload || !payload->asterisk_format
811 ast_test_status_update(test, "Offered PCMA payload 97 was not retained after merge\n");
813 }
815
816cleanup:
817 if (active_initialized) {
819 }
820 if (offered_initialized) {
822 }
823 return result;
824}
825
826AST_TEST_DEFINE(payload_merge_preserves_preferences)
827{
830 RAII_VAR(struct ast_format *, preferred, NULL, ao2_cleanup);
832 int offered_initialized = 0;
833 int active_initialized = 0;
834
835 switch (cmd) {
836 case TEST_INIT:
837 info->name = "payload_merge_preserves_preferences";
838 info->category = "/res/res_rtp/";
839 info->summary = "Verify a payload merge preserves active preferences";
840 info->description =
841 "Verifies that merging an SDP offer's provisional payload mappings "
842 "updates framing needed to generate the SDP answer without changing "
843 "the active preferred format or preferred DTMF metadata before the "
844 "offer is committed.";
845 return AST_TEST_NOT_RUN;
846 case TEST_EXECUTE:
847 break;
848 }
849
851 ast_test_status_update(test, "Unable to initialize RTP codecs\n");
853 goto cleanup;
854 }
855 offered_initialized = 1;
857 ast_test_status_update(test, "Unable to initialize RTP codecs\n");
859 goto cleanup;
860 }
861 active_initialized = 1;
862
865 ast_rtp_codecs_set_framing(&active, 20);
867 ast_rtp_codecs_set_preferred_dtmf_format(&offered, 110, 16000);
868 ast_rtp_codecs_set_framing(&offered, 30);
869
870 ast_rtp_codecs_payloads_merge(&offered, &active, NULL);
871
872 preferred = ast_rtp_codecs_get_preferred_format(&active);
873 if (!preferred
875 ast_test_status_update(test, "Payload merge changed the active preferred format\n");
877 }
881 "Payload merge changed the active preferred DTMF metadata\n");
883 }
884 if (ast_rtp_codecs_get_framing(&active) != 30) {
885 ast_test_status_update(test, "Payload merge did not update offered framing\n");
887 }
888
889cleanup:
890 if (active_initialized) {
892 }
893 if (offered_initialized) {
895 }
896 return result;
897}
898
899/* Unregister RTP regression tests when the module unloads. */
900static int unload_module(void)
901{
902 AST_TEST_UNREGISTER(rtp_port_exhaustion);
903 AST_TEST_UNREGISTER(payload_merge_preserves_preferences);
904 AST_TEST_UNREGISTER(payload_merge_abandoned_offer);
906 AST_TEST_UNREGISTER(nack_no_packet_loss);
907 AST_TEST_UNREGISTER(nack_nominal);
908 AST_TEST_UNREGISTER(nack_overflow);
909 AST_TEST_UNREGISTER(lost_packet_stats_nominal);
910 AST_TEST_UNREGISTER(remb_nominal);
911 AST_TEST_UNREGISTER(sr_rr_nominal);
912 AST_TEST_UNREGISTER(fir_nominal);
913 return 0;
914}
915
916/* Register RTP regression tests with the unit test framework. */
917static int load_module(void)
918{
919 AST_TEST_REGISTER(rtp_port_exhaustion);
920 AST_TEST_REGISTER(nack_no_packet_loss);
921 AST_TEST_REGISTER(nack_nominal);
922 AST_TEST_REGISTER(nack_overflow);
923 AST_TEST_REGISTER(lost_packet_stats_nominal);
924 AST_TEST_REGISTER(remb_nominal);
925 AST_TEST_REGISTER(sr_rr_nominal);
926 AST_TEST_REGISTER(fir_nominal);
928 AST_TEST_REGISTER(payload_merge_abandoned_offer);
929 AST_TEST_REGISTER(payload_merge_preserves_preferences);
931}
932
Asterisk main include file. File version handling, generic pbx functions.
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
static PGresult * result
Definition cel_pgsql.c:84
static const char type[]
Data Buffer API.
char * address
Definition f2c.h:59
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition format.c:201
@ AST_FORMAT_CMP_EQUAL
Definition format.h:36
Media Format Cache API.
struct ast_format * ast_format_ulaw
Built-in cached ulaw format.
struct ast_format * ast_format_alaw
Built-in cached alaw format.
@ AST_FRFLAG_HAS_SEQUENCE_NUMBER
@ AST_FRFLAG_HAS_TIMING_INFO
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_VIDUPDATE
#define LOG_ERROR
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition netsock2.h:517
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition netsock2.c:230
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition res_stasis.c:327
#define NULL
Definition resample.c:96
Pluggable RTP Architecture.
#define AST_RTP_RTCP_PSFB
Definition rtp_engine.h:329
void ast_rtp_codecs_payloads_destroy(struct ast_rtp_codecs *codecs)
Destroy the contents of an RTP codecs structure (but not the structure itself)
int ast_rtp_instance_destroy(struct ast_rtp_instance *instance)
Destroy an RTP instance.
Definition rtp_engine.c:468
struct ast_format * ast_rtp_codecs_get_preferred_format(struct ast_rtp_codecs *codecs)
Retrieve rx preferred format.
struct ast_rtp_instance * ast_rtp_instance_new(const char *engine_name, struct ast_sched_context *sched, const struct ast_sockaddr *sa, void *data)
Create a new RTP instance.
Definition rtp_engine.c:497
struct ast_rtp_payload_type * ast_rtp_codecs_get_payload(struct ast_rtp_codecs *codecs, int payload)
Retrieve rx payload mapped information by payload type.
#define AST_RTP_RTCP_SR
Definition rtp_engine.h:323
ast_rtp_instance_rtcp
Definition rtp_engine.h:283
@ AST_RTP_INSTANCE_RTCP_MUX
Definition rtp_engine.h:289
@ AST_RTP_INSTANCE_RTCP_STANDARD
Definition rtp_engine.h:287
ast_rtp_instance_stat
Definition rtp_engine.h:185
@ AST_RTP_INSTANCE_STAT_ALL
Definition rtp_engine.h:187
struct ast_frame * ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp)
Receive a frame over RTP.
Definition rtp_engine.c:629
void ast_rtp_codecs_set_framing(struct ast_rtp_codecs *codecs, unsigned int framing)
Set the framing used for a set of codecs.
#define AST_RTP_RTCP_RR
Definition rtp_engine.h:325
void ast_rtp_instance_set_prop(struct ast_rtp_instance *instance, enum ast_rtp_property property, int value)
Set the value of an RTP instance property.
Definition rtp_engine.c:756
int ast_rtp_codecs_get_preferred_dtmf_format_rate(struct ast_rtp_codecs *codecs)
Retrieve rx preferred dtmf format sample rate.
void ast_rtp_instance_get_local_address(struct ast_rtp_instance *instance, struct ast_sockaddr *address)
Get the local address that we are expecting RTP on.
Definition rtp_engine.c:694
struct ast_rtp_instance * ast_rtp_instance_new_with_options(const char *engine_name, struct ast_sched_context *sched, const struct ast_sockaddr *sa, void *data, const struct ast_rtp_instance_options *options)
Create a new RTP instance with additional options.
Definition rtp_engine.c:505
int ast_rtp_instance_get_stats(struct ast_rtp_instance *instance, struct ast_rtp_instance_stats *stats, enum ast_rtp_instance_stat stat)
Retrieve statistics about an RTP instance.
int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
Send a frame out over RTP.
Definition rtp_engine.c:619
#define ast_rtp_instance_set_remote_address(instance, address)
Set the address of the remote endpoint that we are sending RTP to.
#define AST_RTP_RTCP_FMT_REMB
Definition rtp_engine.h:339
void ast_rtp_codecs_payloads_merge(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
Merge payload information from one RTP instance into another.
int ast_rtp_codecs_payloads_initialize(struct ast_rtp_codecs *codecs)
Initialize an RTP codecs structure.
@ AST_RTP_PROPERTY_RETRANS_RECV
Definition rtp_engine.h:130
@ AST_RTP_PROPERTY_RETRANS_SEND
Definition rtp_engine.h:132
@ AST_RTP_PROPERTY_RTCP
Definition rtp_engine.h:126
@ AST_RTP_PROPERTY_REMB
Definition rtp_engine.h:134
int ast_rtp_instance_activate(struct ast_rtp_instance *instance)
Indicate to the RTP engine that packets are now expected to be sent/received on the RTP instance.
int ast_rtp_codecs_payloads_set_rtpmap_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload, char *mimetype, char *mimesubtype, enum ast_rtp_options options)
Record tx payload type information that was seen in an a=rtpmap: SDP line.
void ast_rtp_instance_set_channel_id(struct ast_rtp_instance *instance, const char *uniqueid)
Set the channel that owns this RTP instance.
Definition rtp_engine.c:604
int ast_rtp_codecs_set_preferred_dtmf_format(struct ast_rtp_codecs *codecs, int pt, int rate)
Set the preferred dtmf format pt and sample rate.
#define AST_RTP_CODECS_NULL_INIT
Definition rtp_engine.h:773
int ast_rtp_codecs_get_preferred_dtmf_format_pt(struct ast_rtp_codecs *codecs)
Retrieve rx preferred dtmf format payload type.
unsigned int ast_rtp_codecs_get_framing(struct ast_rtp_codecs *codecs)
Get the framing used for a set of codecs.
void ast_rtp_codecs_payloads_xover(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance)
Crossover copy the tx payload mapping of src to the rx payload mapping of dest.
int ast_rtp_codecs_set_preferred_format(struct ast_rtp_codecs *codecs, struct ast_format *format)
Set the preferred format.
Scheduler Routines (derived from cheops)
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition sched.c:271
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition sched.c:197
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition sched.c:238
Definition of a media format.
Definition format.c:43
Data structure associated with a single frame of data.
struct timeval delivery
enum ast_frame_type frametype
union ast_frame::@237 data
Options for creating a new RTP instance.
Definition rtp_engine.h:994
An object that represents data received in a feedback report.
Definition rtp_engine.h:388
struct ast_rtp_rtcp_feedback_remb remb
Definition rtp_engine.h:391
Socket address structure.
Definition netsock2.h:97
Definition sched.c:76
Test Framework API.
@ TEST_INIT
Definition test.h:200
@ TEST_EXECUTE
Definition test.h:201
#define AST_TEST_REGISTER(cb)
Definition test.h:127
#define ast_test_status_update(a, b, c...)
Definition test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition test.h:128
#define AST_TEST_DEFINE(hdr)
Definition test.h:126
ast_test_result_state
Definition test.h:193
@ AST_TEST_PASS
Definition test.h:195
@ AST_TEST_FAIL
Definition test.h:196
@ AST_TEST_NOT_RUN
Definition test.h:194
static struct test_options options
static struct test_val a
static void SLEEP_SPINNER(int ms)
static void test_write_and_read_interleaved_frames(struct ast_rtp_instance *instance1, struct ast_rtp_instance *instance2, int howlong, int rtcp_interval)
static int test_init_rtp_instances(struct ast_rtp_instance **instance1, struct ast_rtp_instance **instance2, struct ast_sched_context *test_sched, enum test_type type)
static void test_read_frames(struct ast_rtp_instance *instance, int num)
test_type
@ TEST_TYPE_NACK
@ TEST_TYPE_NONE
@ TEST_TYPE_STD_RTCP
@ TEST_TYPE_REMB
static void ast_sched_context_destroy_wrapper(struct ast_sched_context *sched)
static int load_module(void)
static void test_write_and_read_frames(struct ast_rtp_instance *instance1, struct ast_rtp_instance *instance2, int seqno, int num)
static int unload_module(void)
static void test_write_frames(struct ast_rtp_instance *instance, int seqno, int num)
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition utils.c:2315
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_clear_flag(p, flag)
Definition utils.h:78
#define ast_set_flag(p, flag)
Definition utils.h:71