Asterisk - The Open Source Telephony Project  21.4.1
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"
37 #include "asterisk/data_buffer.h"
38 #include "asterisk/format_cache.h"
39 #include <assert.h>
40 #include <sched.h>
41 
42 enum test_type {
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 
49 static void ast_sched_context_destroy_wrapper(struct ast_sched_context *sched)
50 {
51  if (sched) {
53  }
54 }
55 
56 static 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 
80  ast_rtp_instance_set_prop(*instance1,
81  AST_RTP_PROPERTY_RTCP, rtcp_type);
82  ast_rtp_instance_set_prop(*instance2,
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 
109 static 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 
120  ast_set_flag(&frame_out, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
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 
128 static 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 
141 static 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  */
155 static 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  */
171 static void test_write_and_read_interleaved_frames(struct ast_rtp_instance *instance1,
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 
209  ast_set_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
210  ast_set_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
211  ast_set_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
212  ast_set_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
213 
214  for (index = 0; index < num; index++) {
215  struct timeval start = ast_tvnow();
216  time_t ms;
217 
218  if (index == 1) {
219  ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
220  ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
221  ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
222  ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
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 AST_TEST_DEFINE(nack_no_packet_loss)
263 {
264  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
265  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
266  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
267 
268  switch (cmd) {
269  case TEST_INIT:
270  info->name = "nack_no_packet_loss";
271  info->category = "/res/res_rtp/";
272  info->summary = "nack no packet loss unit test";
273  info->description =
274  "Tests sending packets with no packet loss and "
275  "validates that the send buffer stores sent packets "
276  "and the receive buffer is empty";
277  return AST_TEST_NOT_RUN;
278  case TEST_EXECUTE:
279  break;
280  }
281 
282  test_sched = ast_sched_context_create();
283 
284  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
285  ast_log(LOG_ERROR, "Failed to initialize test!\n");
286  return AST_TEST_FAIL;
287  }
288 
289  test_write_and_read_frames(instance1, instance2, 1000, 10);
290 
291  ast_test_validate(test, ast_rtp_instance_get_send_buffer_count(instance1) == 10,
292  "Send buffer did not have the expected count of 10");
293 
294  ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
295  "Receive buffer did not have the expected count of 0");
296 
297  return AST_TEST_PASS;
298 }
299 
300 AST_TEST_DEFINE(nack_nominal)
301 {
302  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
303  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
304  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
305 
306  switch (cmd) {
307  case TEST_INIT:
308  info->name = "nack_nominal";
309  info->category = "/res/res_rtp/";
310  info->summary = "nack nominal unit test";
311  info->description =
312  "Tests sending packets with some packet loss and "
313  "validates that a NACK request is sent on reaching "
314  "the triggering amount of lost packets";
315  return AST_TEST_NOT_RUN;
316  case TEST_EXECUTE:
317  break;
318  }
319 
320  test_sched = ast_sched_context_create();
321 
322  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
323  ast_log(LOG_ERROR, "Failed to initialize test!\n");
324  return AST_TEST_FAIL;
325  }
326 
327  /* Start normally */
328  test_write_and_read_frames(instance1, instance2, 1000, 10);
329 
330  /* Set the number of packets to drop when we send them next */
331  ast_rtp_instance_drop_packets(instance2, 10);
332  test_write_and_read_frames(instance1, instance2, 1010, 10);
333 
334  /* Send enough packets to reach the NACK trigger */
335  test_write_and_read_frames(instance1, instance2, 1020, ast_rtp_instance_get_recv_buffer_max(instance2) / 2);
336 
337  /* This needs to be read as RTCP */
338  test_read_frames(instance1, 1);
339 
340  /* We should have the missing packets to read now */
341  test_read_frames(instance2, 10);
342 
343  ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
344  "Receive buffer did not have the expected count of 0");
345 
346  return AST_TEST_PASS;
347 }
348 
349 AST_TEST_DEFINE(nack_overflow)
350 {
351  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
352  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
353  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
354  int max_packets;
355 
356  switch (cmd) {
357  case TEST_INIT:
358  info->name = "nack_overflow";
359  info->category = "/res/res_rtp/";
360  info->summary = "nack overflow unit test";
361  info->description =
362  "Tests that when the buffer hits its capacity, we "
363  "queue all the packets we currently have stored";
364  return AST_TEST_NOT_RUN;
365  case TEST_EXECUTE:
366  break;
367  }
368 
369  test_sched = ast_sched_context_create();
370 
371  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NACK)) < 0) {
372  ast_log(LOG_ERROR, "Failed to initialize test!\n");
373  return AST_TEST_FAIL;
374  }
375 
376  /* Start normally */
377  test_write_and_read_frames(instance1, instance2, 1000, 10);
378 
379  /* Send enough packets to fill the buffer */
380  max_packets = ast_rtp_instance_get_recv_buffer_max(instance2);
381  test_write_and_read_frames(instance1, instance2, 1020, max_packets);
382 
383  ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == max_packets,
384  "Receive buffer did not have the expected count of max buffer size");
385 
386  /* Send the packet that will overflow the buffer */
387  test_write_and_read_frames(instance1, instance2, 1020 + max_packets, 1);
388 
389  ast_test_validate(test, ast_rtp_instance_get_recv_buffer_count(instance2) == 0,
390  "Receive buffer did not have the expected count of 0");
391 
392  return AST_TEST_PASS;
393 }
394 
395 AST_TEST_DEFINE(lost_packet_stats_nominal)
396 {
397  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
398  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
399  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
400  struct ast_rtp_instance_stats stats = { 0, };
402 
403  switch (cmd) {
404  case TEST_INIT:
405  info->name = "lost_packet_stats_nominal";
406  info->category = "/res/res_rtp/";
407  info->summary = "lost packet stats nominal unit test";
408  info->description =
409  "Tests that when some packets are lost, we calculate that "
410  "loss correctly when doing lost packet statistics";
411  return AST_TEST_NOT_RUN;
412  case TEST_EXECUTE:
413  break;
414  }
415 
416  test_sched = ast_sched_context_create();
417 
418  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
419  ast_log(LOG_ERROR, "Failed to initialize test!\n");
420  return AST_TEST_FAIL;
421  }
422 
423  /* Start normally */
424  test_write_and_read_frames(instance1, instance2, 1000, 10);
425 
426  /* Send some more packets, but with a gap */
427  test_write_and_read_frames(instance1, instance2, 1015, 5);
428 
429  /* Send a RR to calculate lost packet statistics. We should be missing 5 packets */
430  ast_rtp_instance_queue_report(instance1);
431  test_write_frames(instance2, 1000, 1);
432 
433  /* Check RTCP stats to see if we got the expected packet loss count */
434  ast_rtp_instance_get_stats(instance2, &stats, stat);
435  ast_test_validate(test, stats.rxploss == 5 && stats.local_minrxploss == 5 &&
436  stats.local_maxrxploss == 5, "Condition of 5 lost packets was not met");
437 
438  /* Drop 3 before writing 5 more */
439  test_write_and_read_frames(instance1, instance2, 1023, 5);
440 
441  ast_rtp_instance_queue_report(instance1);
442  test_write_frames(instance2, 1001, 1);
443  ast_rtp_instance_get_stats(instance2, &stats, stat);
444 
445  /* Should now be missing 8 total packets with a change in min */
446  ast_test_validate(test, stats.rxploss == 8 && stats.local_minrxploss == 3 &&
447  stats.local_maxrxploss == 5);
448 
449  /* Write 5 more with no gaps */
450  test_write_and_read_frames(instance1, instance2, 1028, 5);
451 
452  ast_rtp_instance_queue_report(instance1);
453  test_write_frames(instance2, 1002, 1);
454  ast_rtp_instance_get_stats(instance2, &stats, stat);
455 
456  /* Should still only be missing 8 total packets */
457  ast_test_validate(test, stats.rxploss == 8 && stats.local_minrxploss == 3 &&
458  stats.local_maxrxploss == 5);
459 
460  /* Now drop 1, write another 5, drop 8, and then write 5 */
461  test_write_and_read_frames(instance1, instance2, 1034, 5);
462  test_write_and_read_frames(instance1, instance2, 1047, 5);
463 
464  ast_rtp_instance_queue_report(instance1);
465  test_write_frames(instance2, 1003, 1);
466  ast_rtp_instance_get_stats(instance2, &stats, stat);
467 
468  /* Now it should be missing 17 total packets, with a change in max */
469  ast_test_validate(test, stats.rxploss == 17 && stats.local_minrxploss == 3 &&
470  stats.local_maxrxploss == 9);
471 
472  return AST_TEST_PASS;
473 }
474 
475 AST_TEST_DEFINE(remb_nominal)
476 {
477  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
478  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
479  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
480  RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
481  /* Use the structure softmix_remb_collector uses to store information for REMB */
482  struct ast_rtp_rtcp_feedback feedback = {
484  .remb.br_exp = 0,
485  .remb.br_mantissa = 1000,
486  };
487  struct ast_frame frame_out = {
489  .subclass.integer = AST_RTP_RTCP_PSFB,
490  .data.ptr = &feedback,
491  .datalen = sizeof(feedback),
492  };
493  struct ast_rtp_rtcp_feedback *received_feedback;
494 
495  switch (cmd) {
496  case TEST_INIT:
497  info->name = "remb_nominal";
498  info->category = "/res/res_rtp/";
499  info->summary = "remb nominal unit test";
500  info->description =
501  "Tests sending and receiving a REMB packet";
502  return AST_TEST_NOT_RUN;
503  case TEST_EXECUTE:
504  break;
505  }
506 
507  test_sched = ast_sched_context_create();
508 
509  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_REMB)) < 0) {
510  ast_log(LOG_ERROR, "Failed to initialize test!\n");
511  return AST_TEST_FAIL;
512  }
513 
514  /* The schedid must be 0 or greater, so let's do that now */
515  ast_rtp_instance_set_schedid(instance1, 0);
516 
517  ast_rtp_instance_write(instance1, &frame_out);
518 
519  /* Verify the high level aspects of the frame */
520  frame_in = ast_rtp_instance_read(instance2, 0);
521  ast_test_validate(test, frame_in != NULL, "Did not receive a REMB frame");
522  ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
523  "REMB frame did not have the expected frametype");
524  ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_PSFB,
525  "REMB frame did not have the expected subclass integer");
526 
527  /* Verify the actual REMB information itself */
528  received_feedback = frame_in->data.ptr;
529  ast_test_validate(test, received_feedback->fmt == AST_RTP_RTCP_FMT_REMB,
530  "REMB frame did not have the expected feedback format");
531  ast_test_validate(test, received_feedback->remb.br_exp == feedback.remb.br_exp,
532  "REMB received exponent did not match sent exponent");
533  ast_test_validate(test, received_feedback->remb.br_mantissa == feedback.remb.br_mantissa,
534  "REMB received mantissa did not match sent mantissa");
535 
536  return AST_TEST_PASS;
537 }
538 
539 AST_TEST_DEFINE(sr_rr_nominal)
540 {
541  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
542  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
543  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
544  RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
545 
546  switch (cmd) {
547  case TEST_INIT:
548  info->name = "sr_rr_nominal";
549  info->category = "/res/res_rtp/";
550  info->summary = "SR/RR nominal unit test";
551  info->description =
552  "Tests sending SR/RR and receiving it; includes SDES";
553  return AST_TEST_NOT_RUN;
554  case TEST_EXECUTE:
555  break;
556  }
557 
558  test_sched = ast_sched_context_create();
559 
560  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
561  ast_log(LOG_ERROR, "Failed to initialize test!\n");
562  return AST_TEST_FAIL;
563  }
564 
565  test_write_and_read_frames(instance1, instance2, 1000, 10);
566 
567  /*
568  * Set the send_report flag so we send a sender report instead of normal RTP. We
569  * also need to ensure that SDES processed.
570  */
571  ast_rtp_instance_queue_report(instance1);
572  test_write_frames(instance1, 1010, 1);
573 
574  frame_in = ast_rtp_instance_read(instance2, 0);
575  ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
576  "Sender report frame did not have the expected frametype");
577  ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_SR,
578  "Sender report frame did not have the expected subclass integer");
579  ast_test_validate(test, ast_rtp_instance_get_sdes_received(instance2) == 1,
580  "SDES was never processed for sender report");
581 
582  ast_frfree(frame_in);
583 
584  /* Set the send_report flag so we send a receiver report instead of normal RTP */
585  ast_rtp_instance_queue_report(instance1);
586  test_write_frames(instance1, 1010, 1);
587 
588  frame_in = ast_rtp_instance_read(instance2, 0);
589  ast_test_validate(test, frame_in->frametype == AST_FRAME_RTCP,
590  "Receiver report frame did not have the expected frametype");
591  ast_test_validate(test, frame_in->subclass.integer == AST_RTP_RTCP_RR,
592  "Receiver report frame did not have the expected subclass integer");
593 
594  return AST_TEST_PASS;
595 }
596 
597 AST_TEST_DEFINE(fir_nominal)
598 {
599  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
600  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
601  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
602  RAII_VAR(struct ast_frame *, frame_in, NULL, ast_frfree);
603  struct ast_frame frame_out = {
605  .subclass.integer = AST_CONTROL_VIDUPDATE,
606  };
607 
608  switch (cmd) {
609  case TEST_INIT:
610  info->name = "fir_nominal";
611  info->category = "/res/res_rtp/";
612  info->summary = "fir nominal unit test";
613  info->description =
614  "Tests sending and receiving a FIR packet";
615  return AST_TEST_NOT_RUN;
616  case TEST_EXECUTE:
617  break;
618  }
619 
620  test_sched = ast_sched_context_create();
621 
622  if ((test_init_rtp_instances(&instance1, &instance2, test_sched, TEST_TYPE_NONE)) < 0) {
623  ast_log(LOG_ERROR, "Failed to initialize test!\n");
624  return AST_TEST_FAIL;
625  }
626 
627  /* Send some packets to learn SSRC */
628  test_write_and_read_frames(instance2, instance1, 1000, 10);
629 
630  /* The schedid must be 0 or greater, so let's do that now */
631  ast_rtp_instance_set_schedid(instance1, 0);
632 
633  /*
634  * This will not directly write a frame out, but cause Asterisk to see it as a FIR
635  * request, which will then trigger rtp_write_rtcp_fir, which will send out the
636  * appropriate packet.
637  */
638  ast_rtp_instance_write(instance1, &frame_out);
639 
640  /*
641  * We only receive one frame, the FIR request. It won't have a subclass integer of
642  * 206 (PSFB) because ast_rtcp_interpret sets it to 18 (AST_CONTROL_VIDUPDATE), so
643  * check for that.
644  */
645  frame_in = ast_rtp_instance_read(instance2, 0);
646  ast_test_validate(test, frame_in != NULL, "Did not receive a FIR frame");
647  ast_test_validate(test, frame_in->frametype == AST_FRAME_CONTROL,
648  "FIR frame did not have the expected frametype");
649  ast_test_validate(test, frame_in->subclass.integer == AST_CONTROL_VIDUPDATE,
650  "FIR frame did not have the expected subclass integer");
651 
652  return AST_TEST_PASS;
653 }
654 
655 /*
656  * This test should not normally be run. Its only purpose is to
657  * aid in code development.
658  */
659 AST_TEST_DEFINE(mes)
660 {
661  RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
662  RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
663  RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
664 
665  switch (cmd) {
666  case TEST_INIT:
667  info->name = "mes";
668  info->category = "/res/res_rtp/";
669  info->summary = "Media Experience Score";
670  info->description =
671  "Tests calculation of Media Experience Score (only run by explicit request)";
672  info->explicit_only = 1;
673  return AST_TEST_NOT_RUN;
674  case TEST_EXECUTE:
675  break;
676  }
677 
678  test_sched = ast_sched_context_create();
679  ast_sched_start_thread(test_sched);
680 
681  if ((test_init_rtp_instances(&instance1, &instance2,
682  test_sched, TEST_TYPE_NONE)) < 0) {
683  ast_log(LOG_ERROR, "Failed to initialize test!\n");
684  return AST_TEST_FAIL;
685  }
686 
687  test_write_and_read_interleaved_frames(
688  instance1, instance2, 1000, 5000);
689 
690  return AST_TEST_PASS;
691 }
692 
693 static int unload_module(void)
694 {
695  AST_TEST_UNREGISTER(mes);
696  AST_TEST_UNREGISTER(nack_no_packet_loss);
697  AST_TEST_UNREGISTER(nack_nominal);
698  AST_TEST_UNREGISTER(nack_overflow);
699  AST_TEST_UNREGISTER(lost_packet_stats_nominal);
700  AST_TEST_UNREGISTER(remb_nominal);
701  AST_TEST_UNREGISTER(sr_rr_nominal);
702  AST_TEST_UNREGISTER(fir_nominal);
703  return 0;
704 }
705 
706 static int load_module(void)
707 {
708  AST_TEST_REGISTER(nack_no_packet_loss);
709  AST_TEST_REGISTER(nack_nominal);
710  AST_TEST_REGISTER(nack_overflow);
711  AST_TEST_REGISTER(lost_packet_stats_nominal);
712  AST_TEST_REGISTER(remb_nominal);
713  AST_TEST_REGISTER(sr_rr_nominal);
714  AST_TEST_REGISTER(fir_nominal);
715  AST_TEST_REGISTER(mes);
717 }
718 
719 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "RTP/RTCP test module");
int ast_sched_start_thread(struct ast_sched_context *con)
Start a thread for processing scheduler entries.
Definition: sched.c:197
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...
Definition: rtp_engine.c:2795
Asterisk main include file. File version handling, generic pbx functions.
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:575
Data Buffer API.
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.
Definition: rtp_engine.c:2570
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
struct ast_format * ast_format_ulaw
Built-in cached ulaw format.
Definition: format_cache.c:86
int ast_rtp_instance_write(struct ast_rtp_instance *instance, struct ast_frame *frame)
Send a frame out over RTP.
Definition: rtp_engine.c:590
An object that represents data received in a feedback report.
Definition: rtp_engine.h:388
Test Framework API.
Definition: sched.c:76
unsigned int rxploss
Definition: rtp_engine.h:424
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
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
unsigned int fmt
Definition: rtp_engine.h:389
struct ast_rtp_rtcp_feedback_remb remb
Definition: rtp_engine.h:391
Socket address structure.
Definition: netsock2.h:97
struct ast_frame_subclass subclass
ast_rtp_instance_rtcp
Definition: rtp_engine.h:283
ast_rtp_instance_stat
Definition: rtp_engine.h:185
Scheduler Routines (derived from cheops)
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
#define ast_rtp_instance_set_remote_address(instance, address)
Set the address of the remote endpoint that we are sending RTP to.
Definition: rtp_engine.h:1133
union ast_frame::@224 data
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:665
int ast_rtp_instance_destroy(struct ast_rtp_instance *instance)
Destroy an RTP instance.
Definition: rtp_engine.c:458
struct timeval delivery
#define AST_RTP_RTCP_RR
Definition: rtp_engine.h:325
#define AST_RTP_RTCP_SR
Definition: rtp_engine.h:323
Data structure associated with a single frame of data.
#define AST_RTP_RTCP_PSFB
Definition: rtp_engine.h:329
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
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:727
enum ast_frame_type frametype
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:487
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Pluggable RTP Architecture.
Asterisk module definitions.
#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:941
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
struct ast_frame * ast_rtp_instance_read(struct ast_rtp_instance *instance, int rtcp)
Receive a frame over RTP.
Definition: rtp_engine.c:600
Media Format Cache API.
#define AST_RTP_RTCP_FMT_REMB
Definition: rtp_engine.h:339