#!/usr/bin/perl use strict; use warnings; use NGCP::Rtpengine::Test; use NGCP::Rtpclient::SRTP; use NGCP::Rtpengine::AutoTest; use Test::More; use POSIX; autotest_start(qw(--config-file=none -t -1 -i 203.0.113.1 -i 2001:db8:4321::1 -n 2223 -c 12345 -f -L 7 -E -u 2222 --silence-detect=1 --dtx-delay=10)) or die; my $amr_tests = $ENV{RTPENGINE_EXTENDED_TESTS}; #my $amr_tests = 0; my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $ssrc, $ssrc_b, $resp, $sock_ax, $sock_bx, $port_ax, $port_bx, $srtp_ctx_a, $srtp_ctx_b, $srtp_ctx_a_rev, $srtp_ctx_b_rev, @ret1, @ret2, @ret3, @ret4, $srtp_key_a, $srtp_key_b, $ts, $seq); if ($amr_tests) { ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5210)], [qw(198.51.100.10 5212)]); ($port_a) = offer('packet loss', { codec => { transcode => ['L16/16000/1'], } }, < { transcode => ['L16/16000/1'], } }, < { transcode => ['L16/16000/1'], } }, < { transcode => ['L16/16000/1'], } }, < ['origin'], codec => { transcode => ['AMR-WB'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(96, $seq + 2, 4640, $ssrc, "\xf0\x1c\xe0\xd2\x00\xb3\xbc\xed\x24\xe1\xd1\x79\x49\x06\xa4\xa0\x3c\x1f\x42\xe1\x06\xbb\x5c\x60\x68\x99\xc5\x18\x55\x81\x0d\x41\x03\xc1\x00\xcf\xd4\x20")); rcv($sock_b, $port_a, rtpm(96, $seq + 3, 4960, $ssrc, "\xf0\x1c\x61\x06\x40\xbb\xba\xec\x31\xe2\x59\xed\xd8\x6f\x37\x5c\xff\x31\x88\x8d\xf3\x3a\x5c\x90\x6d\x40\x82\x12\x88\x86\xf8\xb8\x4e\x88\x12\x20\xd9\xe8")); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4640, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(96, $seq + 4, 5280, $ssrc, "\xf0\x1c\x60\x06\x00\xbb\xb6\xee\xe3\xe1\x71\xc3\x1a\x12\x37\x62\x61\x0b\x5a\x58\xf1\xe1\xc8\x70\x4b\xe9\x88\xe0\x00\x8b\xc8\x0c\x81\x42\x65\x88\xe4\xf0")); # interject unknown payload type snd($sock_a, $port_b, rtp(9, 2003, 4800, 0x5678, "\x40" x 160)); # no passthrough, get more DTX rcv($sock_b, $port_a, rtpm(96, $seq + 5, 5600, $ssrc, "\xf0\x1c\x40\x40\x30\xff\xf3\xfe\x44\xf1\x2f\x69\xa0\x9b\x22\xb8\x49\x98\x45\xc5\xd9\x11\x30\x20\x1c\x50\x00\xe5\x00\x01\x23\x90\x03\xeb\xa0\x00\x07\x30")); snd($sock_a, $port_b, rtp(9, 2004, 4960, 0x5678, "\x40" x 160)); # back to normal media snd($sock_a, $port_b, rtp(0, 2003, 4960, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(96, $seq + 6, 5920, $ssrc, "\xf0\x1c\x60\x40\x30\xee\xe3\xb8\xa7\x31\x33\x86\x17\x42\x07\x48\x3d\xd5\x5d\xd8\x0a\xd2\x88\x20\x0e\x40\x80\x4a\x38\x44\x73\x64\x80\x7e\x6c\xc4\x1b\x78")); # reinvite to PCMA ($port_a) = offer('G.711/AMR-WB codec change reverse', { replace => ['origin'], codec => { transcode => ['AMR-WB'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(96, $seq + 9, 6880, 0x5678, "\xf0\x1c\xe2\xd0\x10\xc0\xd9\x63\x84\xb1\x73\xa1\x9a\x8a\x97\x36\x46\x16\x24\x03\x54\x22\x14\x52\x03\xe1\x0a\x40\x10\x45\x54\x15\x23\x72\xcc\xea\x16\x00")); rcv($sock_b, $port_a, rtpm(96, $seq + 10, 7200, 0x5678, "\xf0\x1c\x50\x54\x30\x77\xf3\xdd\x1d\xd3\x57\x6b\xce\xc7\xcd\x7a\x17\x71\x05\x36\x04\x42\x70\x72\x73\x25\x0c\x14\x26\x2a\x05\x72\x03\x90\xf2\x5c\x48\xd8")); # inject old PCM snd($sock_a, $port_b, rtp(0, 2004, 5120, 0x5678, "\x40" x 160)); snd($sock_a, $port_b, rtp(0, 2006, 5440, 0x5678, "\x40" x 160)); # packet dropped rcv_no($sock_b); # reinvite back to PCMU ($port_a) = offer('G.711/AMR-WB codec change reverse', { replace => ['origin'], codec => { transcode => ['AMR-WB'], } }, < ['origin'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5098)], [qw(198.51.100.10 5100)]); ($port_a) = offer('G.711/AMR-WB codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 2, 4320, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 3, 4480, $ssrc, "\xd5" x 160)); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4640, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, 4640, $ssrc, "\x68" x 160)); # interject unknown payload type snd($sock_a, $port_b, rtp(9, 2003, 4800, 0x5678, "\x40" x 160)); # no passthrough, get more DTX rcv($sock_b, $port_a, rtpm(8, $seq + 5, 4800, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(9, 2004, 4960, 0x5678, "\x40" x 160)); # back to normal media snd($sock_a, $port_b, rtp(0, 2003, 4960, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 6, 4960, $ssrc, "\x68" x 160)); # reinvite to AMR-WB ($port_a) = offer('G.711/AMR-WB codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 9, 5440, 0x5678, "\x54\x54\x54\x54\x54\x54\x54\x54\x54\x55\x54\x54\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\xd5\xd5\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5")); rcv($sock_b, $port_a, rtpm(8, $seq + 10, 5600, 0x5678, "\xd5\xd5\xd5\xd5\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\xd5\x55\xd5\x55\x55\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\x55\x55\x55\x55\x55")); # inject old PCM snd($sock_a, $port_b, rtp(0, 2006, 5760, 0x5678, "\x40" x 160)); # packet dropped rcv_no($sock_b); # reinvite back to PCM ($port_a) = offer('G.711/AMR-WB codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5068)], [qw(198.51.100.10 5070)]); ($port_a) = offer('G.711/AMR codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 2, 4320, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 3, 4480, $ssrc, "\xd5" x 160)); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4640, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, 4640, $ssrc, "\x68" x 160)); # interject unknown payload type snd($sock_a, $port_b, rtp(9, 2003, 4800, 0x5678, "\x40" x 160)); # no passthrough, get more DTX rcv($sock_b, $port_a, rtpm(8, $seq + 5, 4800, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(9, 2004, 4960, 0x5678, "\x40" x 160)); # back to normal media snd($sock_a, $port_b, rtp(0, 2003, 4960, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 6, 4960, $ssrc, "\x68" x 160)); # reinvite to AMR ($port_a) = offer('G.711/AMR codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 9, 5440, 0x5678, "\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5")); rcv($sock_b, $port_a, rtpm(8, $seq + 10, 5600, 0x5678, "\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5")); # inject old PCM snd($sock_a, $port_b, rtp(0, 2006, 5600, 0x5678, "\x40" x 160)); # packet dropped rcv_no($sock_b); # reinvite back to PCM ($port_a) = offer('G.711/AMR codec change with timing', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 4024)], [qw(198.51.100.10 4026)]); ($port_a) = offer('AMR SID', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < 'remove', replace => ['origin'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 4026)], [qw(198.51.100.10 4028)]); ($port_a) = offer('AMR SID TS gap', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < 'remove', replace => ['origin'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 4034)], [qw(198.51.100.10 4036)]); ($port_a) = offer('AMR w DTMF', { codec => { mask => ['all'], transcode => [ 'G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2', 'AMR', 'PCMA', 'telephone-event' ], } }, < ['single codec'] }, < ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 4042)], [qw(198.51.100.10 4044)]); ($port_a) = offer('AMR w reverse DTMF', { codec => { mask => ['all'], transcode => [ 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'PCMA', 'telephone-event' ], } }, < [] }, < ft() }); } ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5000)], [qw(198.51.100.10 5002)]); ($port_a) = offer('G.711 DTX', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 2, 4320, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 3, 4480, $ssrc, "\xd5" x 160)); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4640, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, 4640, $ssrc, "\x68" x 160)); rtpe_req('delete', 'G.711 DTX', { 'from-tag' => ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5032)], [qw(198.51.100.10 5034)]); ($port_a) = offer('G.711 DTX with seq skips', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 2, $ts + 320, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 3, $ts + 480, $ssrc, "\xd5" x 160)); # start audio again snd($sock_a, $port_b, rtp(0, 4002, $ts + 640, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, $ts + 640, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4003, $ts + 800, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 5, $ts + 800, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4004, $ts + 960, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 6, $ts + 960, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4005, $ts + 1120, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 7, $ts + 1120, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4006, $ts + 1280, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 8, $ts + 1280, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4007, $ts + 1440, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 9, $ts + 1440, $ssrc, "\x68" x 160)); # lost packet 4008 (skip seq), trigger DTX rcv($sock_b, $port_a, rtpm(8, $seq + 10, $ts + 1600, $ssrc, "\xd5" x 160)); # resume snd($sock_a, $port_b, rtp(0, 4009, $ts + 1760, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 11, $ts + 1760, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 4010, $ts + 1920, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 12, $ts + 1920, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4011, $ts + 2080, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 13, $ts + 2080, $ssrc, "\x68" x 160)); snd($sock_a, $port_b, rtp(0, 4012, $ts + 2240, 0x5678, "\x40" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 14, $ts + 2240, $ssrc, "\x68" x 160)); rtpe_req('delete', 'G.711 DTX with seq skips', { 'from-tag' => ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5004)], [qw(198.51.100.10 5006)]); ($port_a) = offer('G.711 DTX ptime=30', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 2, 4480, $ssrc, "\xd5" x 240)); rcv($sock_b, $port_a, rtpm(8, $seq + 3, 4720, $ssrc, "\xd5" x 240)); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4960, 0x5678, "\x40" x 240)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, 4960, $ssrc, "\x68" x 240)); rtpe_req('delete', 'G.711 DTX ptime=30', { 'from-tag' => ft() }); ($sock_a, $sock_b) = new_call([qw(198.51.100.10 5008)], [qw(198.51.100.10 5010)]); ($port_a) = offer('G.711 DTX ptime change', { replace => ['origin'], codec => { transcode => ['PCMA'], } }, < ['origin'] }, < silence rcv($sock_b, $port_a, rtpm(8, $seq + 3, 4480, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 4, 4640, $ssrc, "\xd5" x 160)); rcv($sock_b, $port_a, rtpm(8, $seq + 5, 4800, $ssrc, "\xd5" x 160)); # start audio again snd($sock_a, $port_b, rtp(0, 2002, 4960, 0x5678, "\x40" x 240)); rcv($sock_b, $port_a, rtpm(8, $seq + 6, 4960, $ssrc, "\x68" x 160)); rtpe_req('delete', 'G.711 DTX ptime change', { 'from-tag' => ft() }); #done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit; done_testing();