#!/usr/bin/perl use strict; use warnings; use NGCP::Rtpengine::Test; use NGCP::Rtpclient::SRTP; use NGCP::Rtpengine::AutoTest; use Test::More; use Test2::Tools::Compare qw(); use NGCP::Rtpclient::ICE; 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 --log-level-internals=7)) or die; my $extended_tests = $ENV{RTPENGINE_EXTENDED_TESTS}; # 100 ms sine wave my $wav_file = "\x52\x49\x46\x46\x64\x06\x00\x00\x57\x41\x56\x45\x66\x6d\x74\x20\x10\x00\x00\x00\x01\x00\x01\x00\x40\x1f\x00\x00\x80\x3e\x00\x00\x02\x00\x10\x00\x64\x61\x74\x61\x40\x06\x00\x00\x00\x00\xb0\x22\x45\x41\x25\x58\x95\x64\x24\x65\xbd\x59\xb6\x43\xb4\x25\x35\x03\x5e\xe0\x3b\xc1\x8c\xa9\x0f\x9c\x6a\x9a\xc2\xa4\xe7\xb9\x55\xd7\x92\xf9\x92\x1c\x30\x3c\xb2\x54\x2e\x63\xf3\x65\xa7\x5c\x68\x48\x9b\x2b\xa1\x09\x8a\xe6\x71\xc6\x28\xad\xab\x9d\xcc\x99\x06\xa2\x5c\xb5\x81\xd1\x2d\xf3\x53\x16\xe1\x36\xe8\x50\x64\x61\x59\x66\x36\x5f\xcf\x4c\x56\x31\x04\x10\xd0\xec\xe0\xcb\x19\xb1\xa9\x9f\x98\x99\xa8\x9f\x1a\xb1\xdf\xcb\xd1\xec\x04\x10\x54\x31\xd2\x4c\x33\x5f\x5c\x66\x61\x61\xeb\x50\xde\x36\x56\x16\x2b\xf3\x83\xd1\x59\xb5\x08\xa2\xcb\x99\xac\x9d\x28\xad\x70\xc6\x8a\xe6\xa3\x09\x98\x2b\x6a\x48\xa6\x5c\xf4\x65\x2d\x63\xb3\x54\x2e\x3c\x93\x1c\x93\xf9\x53\xd7\xe9\xb9\xc1\xa4\x69\x9a\x11\x9c\x8b\xa9\x3b\xc1\x5e\xe0\x36\x03\xb2\x25\xba\x43\xb7\x59\x2a\x65\x90\x64\x29\x58\x42\x41\xb2\x22\xff\xff\x50\xdd\xbb\xbe\xdb\xa7\x6b\x9b\xdd\x9a\x42\xa6\x4b\xbc\x4b\xda\xca\xfc\xa5\x1f\xc2\x3e\x77\x56\xed\x63\x9a\x65\x3b\x5b\x1b\x46\xa9\x28\x70\x06\x6c\xe3\xd2\xc3\x4d\xab\xd1\x9c\x10\x9a\x56\xa3\x99\xb7\x67\xd4\x5b\xf6\x79\x19\x8e\x39\xd7\x52\x58\x62\x30\x66\xfd\x5d\xa2\x4a\x81\x2e\xd1\x0c\xae\xe9\x1f\xc9\x17\xaf\x9e\x9e\xa4\x99\xce\xa0\x2c\xb3\xaf\xce\xf8\xef\x33\x13\x1e\x34\xe8\x4e\x57\x60\x68\x66\x57\x60\xe9\x4e\x1c\x34\x35\x13\xf6\xef\xb0\xce\x2d\xb3\xcc\xa0\xa6\x99\x9c\x9e\x17\xaf\x22\xc9\xa9\xe9\xd6\x0c\x7c\x2e\xa7\x4a\xf8\x5d\x36\x66\x52\x62\xdb\x52\x8c\x39\x79\x19\x5c\xf6\x67\xd4\x97\xb7\x59\xa3\x0e\x9a\xd1\x9c\x4e\xab\xd0\xc3\x6e\xe3\x6e\x06\xac\x28\x18\x46\x3d\x5b\x98\x65\xef\x63\x76\x56\xc3\x3e\xa4\x1f\xc9\xfc\x4e\xda\x49\xbc\x43\xa6\xdd\x9a\x69\x9b\xdd\xa7\xbb\xbe\x4f\xdd\x01\x00\xaf\x22\x47\x41\x23\x58\x96\x64\x24\x65\xbb\x59\xba\x43\xb0\x25\x39\x03\x59\xe0\x40\xc1\x87\xa9\x15\x9c\x65\x9a\xc4\xa4\xe7\xb9\x56\xd7\x90\xf9\x94\x1c\x2e\x3c\xb3\x54\x2f\x63\xf1\x65\xa8\x5c\x68\x48\x9a\x2b\xa2\x09\x8a\xe6\x71\xc6\x27\xad\xac\x9d\xcb\x99\x08\xa2\x59\xb5\x84\xd1\x2a\xf3\x56\x16\xe0\x36\xe7\x50\x65\x61\x59\x66\x35\x5f\xd1\x4c\x54\x31\x04\x10\xd2\xec\xdd\xcb\x1c\xb1\xa5\x9f\x9b\x99\xa8\x9f\x18\xb1\xe2\xcb\xcd\xec\x07\x10\x54\x31\xd1\x4c\x33\x5f\x5d\x66\x60\x61\xec\x50\xdd\x36\x57\x16\x29\xf3\x86\xd1\x57\xb5\x09\xa2\xcb\x99\xab\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9a\x2b\x69\x48\xa7\x5c\xf2\x65\x2e\x63\xb2\x54\x31\x3c\x91\x1c\x93\xf9\x53\xd7\xe9\xb9\xc1\xa4\x6a\x9a\x10\x9c\x8a\xa9\x3f\xc1\x59\xe0\x3a\x03\xb0\x25\xb8\x43\xbd\x59\x24\x65\x95\x64\x24\x58\x46\x41\xaf\x22\x02\x00\x4e\xdd\xbb\xbe\xdd\xa7\x68\x9b\xdf\x9a\x42\xa6\x48\xbc\x50\xda\xc6\xfc\xa7\x1f\xc2\x3e\x75\x56\xef\x63\x99\x65\x3c\x5b\x1a\x46\xaa\x28\x6e\x06\x6e\xe3\xd1\xc3\x4e\xab\xd1\x9c\x0e\x9a\x57\xa3\x9a\xb7\x64\xd4\x60\xf6\x75\x19\x90\x39\xd7\x52\x55\x62\x34\x66\xf9\x5d\xa8\x4a\x7a\x2e\xd8\x0c\xa7\xe9\x23\xc9\x16\xaf\x9d\x9e\xa6\x99\xcb\xa0\x2f\xb3\xad\xce\xfa\xef\x30\x13\x21\x34\xe6\x4e\x59\x60\x66\x66\x5a\x60\xe4\x4e\x23\x34\x2e\x13\xfc\xef\xab\xce\x30\xb3\xcb\xa0\xa5\x99\x9f\x9e\x14\xaf\x24\xc9\xa7\xe9\xd8\x0c\x7b\x2e\xa8\x4a\xf7\x5d\x36\x66\x53\x62\xda\x52\x8d\x39\x78\x19\x5d\xf6\x67\xd4\x97\xb7\x59\xa3\x0d\x9a\xd2\x9c\x4e\xab\xd1\xc3\x6d\xe3\x6f\x06\xaa\x28\x19\x46\x3f\x5b\x95\x65\xf2\x63\x74\x56\xc2\x3e\xa8\x1f\xc4\xfc\x52\xda\x45\xbc\x46\xa6\xdc\x9a\x6a\x9b\xdc\xa7\xba\xbe\x51\xdd\xff\xff\xb1\x22\x45\x41\x24\x58\x97\x64\x22\x65\xbd\x59\xb7\x43\xb3\x25\x37\x03\x5b\xe0\x3e\xc1\x89\xa9\x11\x9c\x6a\x9a\xc0\xa4\xeb\xb9\x51\xd7\x94\xf9\x91\x1c\x31\x3c\xb1\x54\x2f\x63\xf3\x65\xa5\x5c\x6c\x48\x95\x2b\xa7\x09\x86\xe6\x73\xc6\x28\xad\xa9\x9d\xcf\x99\x04\xa2\x5b\xb5\x84\xd1\x29\xf3\x57\x16\xde\x36\xe9\x50\x65\x61\x57\x66\x38\x5f\xcd\x4c\x57\x31\x04\x10\xd0\xec\xe1\xcb\x17\xb1\xaa\x9f\x97\x99\xaa\x9f\x18\xb1\xe1\xcb\xce\xec\x07\x10\x53\x31\xd0\x4c\x38\x5f\x55\x66\x68\x61\xe6\x50\xe0\x36\x56\x16\x2b\xf3\x81\xd1\x5d\xb5\x04\xa2\xce\x99\xaa\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9b\x2b\x67\x48\xa9\x5c\xf1\x65\x2e\x63\xb4\x54\x2e\x3c\x93\x1c\x92\xf9\x54\xd7\xe8\xb9\xc2\xa4\x69\x9a\x10\x9c\x8c\xa9\x3c\xc1\x5c\xe0\x37\x03\xb2\x25\xb8\x43\xbc\x59\x24\x65\x95\x64\x26\x58\x43\x41\xb2\x22\xff\xff\x50\xdd\xba\xbe\xde\xa7\x68\x9b\xdd\x9a\x45\xa6\x45\xbc\x52\xda\xc5\xfc\xa8\x1f\xbf\x3e\x79\x56\xec\x63\x9b\x65\x3b\x5b\x1a\x46\xaa\x28\x6f\x06\x6e\xe3\xd0\xc3\x4f\xab\xd0\x9c\x0f\x9a\x58\xa3\x97\xb7\x68\xd4\x5c\xf6\x78\x19\x8f\x39\xd6\x52\x57\x62\x32\x66\xfb\x5d\xa6\x4a\x7b\x2e\xd8\x0c\xa6\xe9\x25\xc9\x15\xaf\x9c\x9e\xa9\x99\xc7\xa0\x33\xb3\xa9\xce\xfd\xef\x2f\x13\x21\x34\xe6\x4e\x58\x60\x67\x66\x59\x60\xe5\x4e\x23\x34\x2c\x13\x00\xf0\xa6\xce\x35\xb3\xc7\xa0\xa8\x99\x9d\x9e\x15\xaf\x24\xc9\xa8\xe9\xd5\x0c\x7e\x2e\xa5\x4a\xfa\x5d\x35\x66\x52\x62\xdb\x52\x8d\x39\x77\x19\x5e\xf6\x66\xd4\x98\xb7\x59\xa3\x0c\x9a\xd3\x9c\x4d\xab\xd1\xc3\x6e\xe3\x6e\x06\xaa\x28\x1b\x46\x3b\x5b\x9a\x65\xed\x63\x76\x56\xc4\x3e\xa3\x1f\xcb\xfc\x4b\xda\x4a\xbc\x43\xa6\xdd\x9a\x6a\x9b\xdc\xa7\xba\xbe\x51\xdd\xff\xff\xb1\x22\x44\x41\x25\x58\x96\x64\x23\x65\xbd\x59\xb6\x43\xb4\x25\x36\x03\x5c\xe0\x3d\xc1\x8a\xa9\x12\x9c\x67\x9a\xc4\xa4\xe6\xb9\x55\xd7\x93\xf9\x91\x1c\x31\x3c\xb0\x54\x31\x63\xef\x65\xab\x5c\x66\x48\x9a\x2b\xa4\x09\x87\xe6\x73\xc6\x26\xad\xad\x9d\xcb\x99\x07\xa2\x5b\xb5\x81\xd1\x2c\xf3\x56\x16\xde\x36\xeb\x50\x62\x61\x59\x66\x38\x5f\xcc\x4c\x59\x31\x01\x10\xd3\xec\xdd\xcb\x1b\xb1\xa8\x9f\x98\x99\xa9\x9f\x18\xb1\xe0\xcb\xd1\xec\x03\x10\x57\x31\xce\x4c\x37\x5f\x58\x66\x63\x61\xec\x50\xdb\x36\x5a\x16\x27\xf3\x85\xd1\x5a\xb5\x05\xa2\xce\x99\xaa\x9d\x29\xad\x70\xc6\x8a\xe6\xa2\x09\x9a\x2b\x69\x48\xa6\x5c\xf4\x65\x2e\x63\xb1\x54\x32\x3c\x8e\x1c\x96\xf9\x52\xd7\xea\xb9\xc1\xa4\x67\x9a\x13\x9c\x8a\xa9\x3c\xc1\x5e\xe0\x33\x03\xb7\x25\xb4\x43\xbf\x59\x21\x65\x99\x64\x21\x58\x48\x41\xad\x22\x03\x00\x4f\xdd\xbb\xbe\xdb\xa7\x6a\x9b\xdd\x9a\x43\xa6\x4b\xbc\x4a\xda\xcb\xfc\xa4\x1f\xc3\x3e\x76\x56\xef\x63\x96\x65\x40\x5b\x17\x46\xac\x28\x6e\x06\x6d\xe3\xd2\xc3\x4d\xab\xd2\x9c\x0d\x9a\x59\xa3\x97\xb7\x68\xd4\x5c\xf6\x77\x19\x8f\x39\xd8\x52\x55\x62\x33\x66\xfb\x5d\xa4\x4a\x7f\x2e\xd4\x0c\xab\xe9\x20\xc9\x17\xaf\x9d\x9e\xa7\x99\xc9\xa0\x32\xb3\xa9\xce\xfd\xef\x2f\x13\x20\x34\xe8\x4e\x56\x60\x6a\x66\x55\x60\xe9\x4e\x1f\x34\x31\x13\xfa\xef\xad\xce\x2e\xb3\xcc\xa0\xa7\x99\x9b\x9e\x18\xaf\x20\xc9\xac\xe9\xd2\x0c\x81\x2e\xa1\x4a\xff\x5d\x30\x66\x56\x62\xd7\x52\x90\x39\x77\x19\x5d\xf6\x67\xd4\x96\xb7\x5a\xa3\x0e\x9a\xd0\x9c\x50\xab\xcf\xc3\x6e\xe3\x6f\x06\xaa\x28\x1a\x46\x3d\x5b\x98\x65\xee\x63\x77\x56\xc1\x3e\xa7\x1f\xc8\xfc\x4c\xda\x4b\xbc\x41\xa6\xdf\x9a\x68\x9b\xdd\xa7\xba\xbe\x51\xdd"; is length($wav_file), 1644, 'embedded binary wav file'; my $pcma_1 = "\xd5\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\x55\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c"; my $pcma_2 = "\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\xd5\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\xd5\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09"; my $pcma_3 = "\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\x55\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0"; my $pcma_4 = "\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\x55\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34\x55\xb4\xa5\xa3\xac\xac\xa3\xa5\xb7\xfc\x0a\x3a\x20\x2d\x2c\x23\x24\x31\x6c\x89\xbb\xa0\xad\xac\xa2\xa7\xb0\x96\x0c\x39\x21\x2d\x2c\x22\x27\x32\x1c\x83\xbe\xa1"; my $pcma_5 = "\xad\xac\xa2\xa6\xbd\x9a\x06\x3f\x26\x2d\x2c\x2d\x26\x3f\x06\x9a\xbd\xa6\xa2\xac\xad\xa1\xbe\x83\x1c\x32\x27\x22\x2c\x2d\x21\x39\x0c\x96\xb0\xa7\xa2\xac\xad\xa0\xbb\x89\x6c\x31\x24\x23\x2c\x2d\x20\x3a\x0a\xfc\xb7\xa5\xa3\xac\xac\xa3\xa5\xb4\xd5\x34\x25\x23\x2c\x2c\x23\x25\x37\x7c\x8a\xba\xa0\xad\xac\xa3\xa4\xb1\xec\x09\x3b\x20\x2d\x2c\x22\x27\x30\x16\x8c\xb9\xa1\xad\xac\xa2\xa7\xb2\x9c\x03\x3e\x21\x2d\x2c\x22\x26\x3d\x1a\x86\xbf\xa6\xad\xac\xad\xa6\xbf\x86\x1a\x3d\x26\x22\x2c\x2d\x21\x3e\x03\x9c\xb2\xa7\xa2\xac\xad\xa1\xb9\x8c\x16\x30\x27\x22\x2c\x2d\x20\x3b\x09\xec\xb1\xa4\xa3\xac\xad\xa0\xba\x8a\x7c\x37\x25\x23\x2c\x2c\x23\x25\x34"; 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, $t_a, $t_b, $t_c, $t_d, $sock_cx, $sock_dx, $port_c, $port_d, $port_cx, $port_dx, $srtp_ctx_a, $srtp_ctx_b, $srtp_ctx_a_rev, $srtp_ctx_b_rev, $ufrag_a, $ufrag_b, @ret1, @ret2, @ret3, @ret4, $srtp_key_a, $srtp_key_b, $ts, $seq, $has_recv, $tmp_blob); sub stun_req { my ($controlling, $pref, $comp, $my_ufrag, $other_ufrag, $other_pwd) = @_; my $tid = NGCP::Rtpclient::ICE::random_string(12); my @attrs; unshift(@attrs, NGCP::Rtpclient::ICE::attr(0x8022, 'perltester')); unshift(@attrs, NGCP::Rtpclient::ICE::attr($controlling ? 0x802a : 0x8029, NGCP::Rtpclient::ICE::random_string(8))); unshift(@attrs, NGCP::Rtpclient::ICE::attr(0x0024, pack('N', NGCP::Rtpclient::ICE::calc_priority('prflx', $pref, $comp)))); unshift(@attrs, NGCP::Rtpclient::ICE::attr(0x0006, "$other_ufrag:$my_ufrag")); # nominate NGCP::Rtpclient::ICE::integrity(\@attrs, 1, $tid, $other_pwd); NGCP::Rtpclient::ICE::fingerprint(\@attrs, 1, $tid); my $packet = join('', @attrs); $packet = pack('nnNa12', 1, length($packet), 0x2112A442, $tid) . $packet; return ($packet, $tid); } sub stun_succ { my ($port, $tid, $my_pwd) = @_; my $sw = NGCP::Rtpclient::ICE::attr(0x8022, 'perltester'); my $xor_addr = NGCP::Rtpclient::ICE::attr(0x0020, pack('nna4', 1, $port ^ 0x2112, pack('CCCC', 203,0,113,1) ^ "\x21\x12\xa4\x42")); my $attrs = [$sw, $xor_addr]; NGCP::Rtpclient::ICE::integrity($attrs, 257, $tid, $my_pwd); NGCP::Rtpclient::ICE::fingerprint($attrs, 257, $tid); my $pack = join('', @{$attrs}); my $packet = pack('nnNa12', 257, length($pack), 0x2112A442, $tid) . $pack; #print(unpack('H*', $packet)."\n"); return $packet; }; ($sock_a, $sock_ax, $sock_b, $sock_bx, $sock_c, $sock_cx, $sock_d, $sock_dx) = new_call([qw(198.51.100.35 3090)], [qw(198.51.100.35 3091)], [qw(198.51.100.35 3092)], [qw(198.51.100.35 3093)], [qw(198.51.100.35 3094)], [qw(198.51.100.35 3095)], [qw(198.51.100.35 3096)], [qw(198.51.100.35 3097)], ); ($port_a, $port_ax) = offer('connect between different transcoders', { codec => { transcode => ['PCMU'] } }, < { transcode => ['G723'] } }, < $t_a, 'to-tag' => $t_c }); snd($sock_c, $port_d, rtp(9, 3002, 5320, 0x7532346, "\x00" x 160)); Time::HiRes::usleep(20000); # resample delay snd($sock_c, $port_d, rtp(9, 3003, 5480, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(8, 3002, 5320, 0x7532346, "\xd5\x55\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\x55\x55\x55\x55\xd5\x55\xd5\x55\xd5\x54\xd5\x55\xd5\xd5\x55\xd5\xd5\x55\xd5\x55\x55\xd5\x55\xd5\x55\xd5\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\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\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\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\x55\x55\x55\x55\xd5\x55\xd5\x54\xd5\x54\xd5\x55\xd5\xd5\x55\x55\x55\x55\xd5\x55\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\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\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55\x55")); snd($sock_a, $port_b, rtp(8, 1002, 3320, 0x5432345, "\x00" x 160)); Time::HiRes::usleep(20000); # resample delay snd($sock_a, $port_b, rtp(8, 1003, 3480, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(9, 1002, 3320, 0x5432345, "\x34\x8a\x20\x85\x21\x84\x04\x8a\x0e\x91\xd2\xd3\xd5\xd6\xd6\xd7\xd8\xd8\xd7\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd8\xd9\xd9\xd9\xd9\xda\xda\xda\xda\xdb\xdb\xdb\xdc\xd9\xda\xda\xda\xda\xda\xdb\xdc\xd9\xd9\xda\xda\xd9\xd9\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xdb\xda\xda\xda\xda\xd9\xd9\xd9\xd9\xd9\xd8\xd8\xd8\xd8\xd7\xdd\xd8\xd8\xd8\xd7\xdd\xd6\xdc\xd8\xd8\xd8\xd5\xdb\xda\xda\xdb\xdc\xd7\xda\xd9\xd8\xd7\xde\xdb\xda\xd9\xd8\xd8\xd5\xdb\xdd\xd8\xd8\xd8\xd7\xdc\xd5\xdb\xdf\xda\xda\xd7\xdf\xd8\xd5\xdd\xda\xdb\xd8\xd8\xd9\xd6\xdf\xdb\xdc\xd5\xdb\xde\xd5\xda\xdd\xda\xd9\xd8\xd9\xd8\xd6\xff\xd8\xd7\xfe\xd5\xda\xd8\xda\xdf\xd8\xda\xda\xda\xd9\xd9\xdc\xd8")); new_call; offer('i= etc lines', {}, < 'a' }, < 'b', codec => { strip => ['all'], offer => ['PCMA', 'opus', 'telephone-event' ] } }, < 'a' }, < ['original sendrecv'] }, < ['original sendrecv'] }, <opus+DTMF', { }, <opus+DTMF', { flags => [qw,allow-asymmetric-codecs,] }, <opus+DTMF', { codec => { accept => ['all'], transcode => [qw,G722 PCMA,] } }, <opus+DTMF', { }, < $t_a, 'to-tag' => $t_c }); snd($sock_c, $port_d, rtp(0, 3001, 5160, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 3001, 5160, 0x7532346, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(0, 1002, 3320, 0x5432345, "\x00" x 160)); ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.35 3008)], [qw(198.51.100.35 3009)], [qw(198.51.100.35 3010)], [qw(198.51.100.35 3011)], ); ($port_a, $port_ax) = offer('cross call connect', { }, < $t_c, 'to-tag' => $t_a, 'to-call-id' => $cid1 }); snd($sock_c, $port_d, rtp(0, 3001, 5160, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 3001, 5160, 0x7532346, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(0, 1002, 3320, 0x5432345, "\x00" x 160)); rtpe_req('delete', 'delete'); ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.35 3024)], [qw(198.51.100.35 3025)], [qw(198.51.100.35 3026)], [qw(198.51.100.35 3027)], ); ($port_a, $port_ax) = offer('cross call connect with proper delete', { }, < $t_c, 'to-tag' => $t_a, 'to-call-id' => $cid1 }); snd($sock_c, $port_d, rtp(0, 3001, 5160, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 3001, 5160, 0x7532346, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(0, 1002, 3320, 0x5432345, "\x00" x 160)); rtpe_req('delete', 'delete'); rtpe_req('delete', 'delete', { 'call-id' => $cid1 } ); ($sock_a, $sock_ax, $sock_b, $sock_bx, $sock_c, $sock_cx, $sock_d, $sock_dx) = new_call([qw(198.51.100.35 3032)], [qw(198.51.100.35 3033)], [qw(198.51.100.35 3034)], [qw(198.51.100.35 3035)], [qw(198.51.100.35 3036)], [qw(198.51.100.35 3037)], [qw(198.51.100.35 3038)], [qw(198.51.100.35 3039)], ); ($port_a, $port_ax) = offer('connect with mismatched media types', { }, < $t_a, 'to-tag' => $t_c }); snd($sock_c, $port_d, rtp(0, 3001, 5160, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 3001, 5160, 0x7532346, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(0, 1002, 3320, 0x5432345, "\x00" x 160)); offer('connect with mismatched media types', { 'from-tag' => $t_a, 'to-tag' => $t_c }, < $t_a, 'to-tag' => $t_c }, < $t_a, 'to-tag' => $t_c }); snd($sock_c, $port_d, rtp(0, 3001, 5160, 0x7532346, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(8, 3001, 5160, 0x7532346, "\x2a" x 160)); snd($sock_a, $port_b, rtp(8, 1002, 3320, 0x5432345, "\x00" x 160)); rcv($sock_c, $port_d, rtpm(0, 1002, 3320, 0x5432345, "\x29" x 160)); new_call; offer('unsolicited to-tag', { }, < 'ok', 'last redis update' => '0', 'SSRC' => {}, 'last signal' => qr//, 'tags' => { ft() => { 'tag' => ft(), 'subscriptions' => [ { 'type' => 'offer/answer', 'tag' => tt() } ], 'medias' => [ { 'index' => '1', 'streams' => [ { 'last user packet' => qr//, 'egress SSRCs' => [], 'advertised endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3000' }, 'ingress SSRCs' => [], 'flags' => [ 'RTP', 'filled' ], 'stats_out' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'last kernel packet' => '0', 'stats' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'family' => 'IPv4', 'local address' => '203.0.113.1', 'local port' => qr/^\d*$/, 'endpoint' => { 'port' => '3000', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'last packet' => qr// }, { 'egress SSRCs' => [], 'advertised endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3001' }, 'ingress SSRCs' => [], 'flags' => [ 'RTCP', 'filled' ], 'last user packet' => qr//, 'stats_out' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'last kernel packet' => '0', 'stats' => { 'errors' => '0', 'packets' => '0', 'bytes' => '0' }, 'local address' => '203.0.113.1', 'family' => 'IPv4', 'endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3001' }, 'last packet' => qr//, 'local port' => qr/^\d*$/ } ], 'protocol' => 'RTP/AVP', 'type' => 'audio', 'flags' => [ 'initialized', 'send', 'recv' ] } ], 'VSC' => [], 'subscribers' => [ { 'tag' => tt(), 'type' => 'offer/answer' } ], 'created' => qr// }, tt() => { 'tag' => tt(), 'medias' => [ { 'streams' => [ { 'stats_out' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'egress SSRCs' => [], 'ingress SSRCs' => [], 'advertised endpoint' => { 'port' => '4000', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'flags' => [ 'RTP', 'filled' ], 'last user packet' => qr//, 'endpoint' => { 'port' => '4000', 'address' => '198.51.100.1', 'family' => 'IPv4' }, 'last packet' => qr//, 'local port' => qr/^\d*$/, 'stats' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'last kernel packet' => '0', 'family' => 'IPv4', 'local address' => '203.0.113.1' }, { 'family' => 'IPv4', 'local address' => '203.0.113.1', 'last kernel packet' => '0', 'stats' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'last packet' => qr//, 'endpoint' => { 'port' => '4001', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'local port' => qr/^\d*$/, 'flags' => [ 'RTCP', 'filled' ], 'ingress SSRCs' => [], 'advertised endpoint' => { 'port' => '4001', 'address' => '198.51.100.1', 'family' => 'IPv4' }, 'egress SSRCs' => [], 'last user packet' => qr//, 'stats_out' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' } } ], 'protocol' => 'RTP/AVP', 'index' => '1', 'flags' => [ 'initialized', 'send', 'recv', 'ICE controlling' ], 'type' => 'audio' } ], 'created' => qr//, 'tag-aliases' => [ $old_tt, ], 'VSC' => [], 'subscriptions' => [ { 'type' => 'offer/answer', 'tag' => ft() } ], 'subscribers' => [ { 'tag' => ft(), 'type' => 'offer/answer' } ] } }, 'totals' => { 'RTCP' => { 'errors' => '0', 'packets' => '0', 'bytes' => '0' }, 'RTP' => { 'bytes' => '0', 'errors' => '0', 'packets' => '0' } }, 'created_us' => qr//, 'created' => qr// }, "query result matches"); new_call; offer('unsolicited to-tag w/ via-branch', { 'via-branch' => 'foobar' }, < 'foobar' }, < 'foobar' }, < 'ok', 'last redis update' => '0', 'SSRC' => {}, 'last signal' => qr//, 'tags' => { ft() => { 'tag' => ft(), 'subscriptions' => [ { 'type' => 'offer/answer', 'tag' => tt() } ], 'medias' => [ { 'index' => '1', 'streams' => [ { 'last user packet' => qr//, 'egress SSRCs' => [], 'advertised endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3000' }, 'ingress SSRCs' => [], 'flags' => [ 'RTP', 'filled' ], 'stats_out' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'last kernel packet' => '0', 'stats' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'family' => 'IPv4', 'local address' => '203.0.113.1', 'local port' => qr/^\d*$/, 'endpoint' => { 'port' => '3000', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'last packet' => qr// }, { 'egress SSRCs' => [], 'advertised endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3001' }, 'ingress SSRCs' => [], 'flags' => [ 'RTCP', 'filled' ], 'last user packet' => qr//, 'stats_out' => { 'packets' => '0', 'errors' => '0', 'bytes' => '0' }, 'last kernel packet' => '0', 'stats' => { 'errors' => '0', 'packets' => '0', 'bytes' => '0' }, 'local address' => '203.0.113.1', 'family' => 'IPv4', 'endpoint' => { 'address' => '198.51.100.1', 'family' => 'IPv4', 'port' => '3001' }, 'last packet' => qr//, 'local port' => qr/^\d*$/ } ], 'protocol' => 'RTP/AVP', 'type' => 'audio', 'flags' => [ 'initialized', 'send', 'recv' ] } ], 'VSC' => [], 'subscribers' => [ { 'tag' => tt(), 'type' => 'offer/answer' } ], 'created' => qr// }, tt() => { 'tag' => tt(), 'medias' => [ { 'streams' => [ { 'stats_out' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'egress SSRCs' => [], 'ingress SSRCs' => [], 'advertised endpoint' => { 'port' => '4000', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'flags' => [ 'RTP', 'filled' ], 'last user packet' => qr//, 'endpoint' => { 'port' => '4000', 'address' => '198.51.100.1', 'family' => 'IPv4' }, 'last packet' => qr//, 'local port' => qr/^\d*$/, 'stats' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'last kernel packet' => '0', 'family' => 'IPv4', 'local address' => '203.0.113.1' }, { 'family' => 'IPv4', 'local address' => '203.0.113.1', 'last kernel packet' => '0', 'stats' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' }, 'last packet' => qr//, 'endpoint' => { 'port' => '4001', 'family' => 'IPv4', 'address' => '198.51.100.1' }, 'local port' => qr/^\d*$/, 'flags' => [ 'RTCP', 'filled' ], 'ingress SSRCs' => [], 'advertised endpoint' => { 'port' => '4001', 'address' => '198.51.100.1', 'family' => 'IPv4' }, 'egress SSRCs' => [], 'last user packet' => qr//, 'stats_out' => { 'bytes' => '0', 'packets' => '0', 'errors' => '0' } } ], 'protocol' => 'RTP/AVP', 'index' => '1', 'flags' => [ 'initialized', 'send', 'recv', 'ICE controlling' ], 'type' => 'audio' } ], 'created' => qr//, 'tag-aliases' => [ $old_tt, ], 'VSC' => [], 'subscriptions' => [ { 'type' => 'offer/answer', 'tag' => ft() } ], 'subscribers' => [ { 'tag' => ft(), 'type' => 'offer/answer' } ] } }, 'totals' => { 'RTCP' => { 'errors' => '0', 'packets' => '0', 'bytes' => '0' }, 'RTP' => { 'bytes' => '0', 'errors' => '0', 'packets' => '0' } }, 'created_us' => qr//, 'created' => qr// }, "query result matches"); if ($ENV{RTPENGINE_MOS_TESTS}) { ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3000)], [qw(198.51.100.23 3001)], [qw(198.51.100.23 3002)], [qw(198.51.100.23 3003)]); ($port_a, $port_ax) = offer('MOS basic', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 43, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 0, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 43, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 0, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', $processing_us, 'metric matches'; ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3004)], [qw(198.51.100.23 3005)], [qw(198.51.100.23 3006)], [qw(198.51.100.23 3007)]); ($port_a, $port_ax) = offer('MOS degraded', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 35, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 36, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 15, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 3, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', 250000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 130000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 130000 + $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 35, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 36, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 15, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 3, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', 250000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 120000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', 120000 + $processing_us, 'metric matches'; ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3008)], [qw(198.51.100.23 3009)], [qw(198.51.100.23 3010)], [qw(198.51.100.23 3011)]); ($port_a, $port_ax) = offer('MOS very degraded', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 29, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 20, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 5, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', 400000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 200000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 200000 + $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 29, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 20, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 5, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', 400000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 200000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', 200000 + $processing_us, 'metric matches'; ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3016)], [qw(198.51.100.23 3017)], [qw(198.51.100.23 3018)], [qw(198.51.100.23 3019)]); ($port_a, $port_ax) = offer('Opus MOS basic', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 43, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 0, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 43, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 0, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 0, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', $processing_us, 'metric matches'; ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3020)], [qw(198.51.100.23 3021)], [qw(198.51.100.23 3022)], [qw(198.51.100.23 3023)]); ($port_a, $port_ax) = offer('Opus MOS degraded', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 36, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 15, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 3, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', 250000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 130000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 130000 + $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 36, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 15, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 3, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', 250000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 120000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', 120000 + $processing_us, 'metric matches'; ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call([qw(198.51.100.23 3024)], [qw(198.51.100.23 3025)], [qw(198.51.100.23 3026)], [qw(198.51.100.23 3027)]); ($port_a, $port_ax) = offer('Opus MOS very degraded', { }, <{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 29, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 20, 'metric matches'; is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 5, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '<', 400000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 200000, 'metric matches'; cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 200000 + $processing_us, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 29, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 20, 'metric matches'; is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 5, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '<', 400000 + $processing_us * 2, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '>=', 200000, 'metric matches'; cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time leg'}, '<', 200000 + $processing_us, 'metric matches'; } new_call; offer('a=mid mixup', { 'address family' => 'IP6' }, < { } }, < { } }, < ['reuse-codecs'], codec => { } }, < ['reuse-codecs'], codec => { } }, < { } }, < { } }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; # receive mode 3 ($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(96 | 0x80, -1, -1, -1, "\xf0\x1c\xfc\xce\x67\x32\x83\x15\x62\xbe\x89\x42\xed\xdd\x82\xd4\xb5\x63\x35\x34\x44\x44\x76\x86\xb5\x76\x4c\xd4\x54\x44\x8c\xcd\xce\xc5\x74\x47\x2f\xe8")); ($sock_a, $sock_b) = new_call([qw(198.51.100.45 6064)], [qw(198.51.100.45 6066)]); ($port_a) = offer('AMR play media bit rate highest mode', { codec => { } }, < { } }, < ft(), blob => $wav_file, codec => { set => [qw,AMR/8000/1/12200 AMR-WB/16000/1/23850,] }, }); is $resp->{duration}, 100, 'media duration'; # receive mode 8 ($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(96 | 0x80, -1, -1, -1, "\xf0\x44\xfa\xce\x55\xb2\x8f\x39\x12\xbe\x89\xf0\x00\x44\x6d\xdd\xa2\xf0\x03\x18\x61\x10\x19\xd0\x18\x0a\x0c\x21\xba\x06\xc2\x24\x00\x24\xcc\x10\x8c\xfb\x43\xcf\x01\x39\x5b\x65\x04\x01\x73\x14\x1c\xbc\xd1\x9c\x70\xe5\x6e\x16\x16\x17\xd4\x71\xff\xc0")); ($sock_a, $sock_b) = new_call([qw(198.51.100.45 6068)], [qw(198.51.100.45 6066)]); ($port_a) = offer('AMR play media bit rate lower mode', { codec => { } }, < { } }, < ft(), blob => $wav_file, codec => { set => [qw,AMR/8000/1/12200 AMR-WB/16000/1/23850,] }, }); is $resp->{duration}, 100, 'media duration'; # receive mode 7 ($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(96 | 0x80, -1, -1, -1, "\xf0\x3c\xfa\xce\x55\xb2\x8f\x39\x12\xbe\x89\x44\x6d\xdd\xa2\xf0\x03\x18\x61\x10\x19\xd0\x18\x0a\x0c\x21\xba\x06\xc2\x24\x00\x24\xcc\x10\x8c\xfb\x43\xcf\x01\x39\x5b\x65\x04\x01\x73\x14\x1c\xbc\xd1\x9c\x70\xe5\x6e\x16\x16\x17\xd4\x71\xff\xc0")); } new_call; offer('mismatched t-e control', { }, < ['allow asymmetric codecs'] }, < {transcode => ['telephone-event'] } }, < {transcode => ['telephone-event'] } }, < ['allow asymmetric codecs'] }, < {accept => ['telephone-event'] } }, < {accept => ['telephone-event'] } }, < ['allow asymmetric codecs'] }, < { strip => ['all'], except => [qw/opus G722 PCMA telephone-event/], transcode => ['telephone-event'], } }, < ['allow asymmetric codecs'] }, < [qw(codec-strip-all codec-transcode-PCMA codec-transcode-telephone-event)], ICE => 'remove' }, < [qw(codec-strip-all codec-transcode-PCMA codec-transcode-telephone-event)], ICE => 'remove' }, < [qw(codec-strip-all codec-transcode-PCMA codec-transcode-telephone-event)], ICE => 'remove' }, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }}, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }}, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }, flags => ['no codec renegotiation'] }, < ['no codec renegotiation'] }, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }, flags => ['no codec renegotiation'] }, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }, flags => ['static codecs'] }, < ['static codecs'] }, < { transcode => ['opus/48000/2///useinbandfec--1;stereo--0;sprop-stereo--0'], }, flags => ['static codecs'] }, < 20, 'ptime-reverse' => 20, ICE => 'force', flags => [qw( generate-mid generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-opus codec-strip-G729 codec-strip-G729a codec-strip-speex codec-strip-G723 codec-strip-GSM codec-strip-iLBC codec-mask-G722 codec-mask-PCMA codec-mask-PCMU transcode-opus codec-offer-telephone-event)], replace => ['origin'], 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['require']}, < 'remove', flags => [qw(port-latching always-transcode SDES-off no-rtcp-attribute strip-extmap)], 'transport-protocol' => 'RTP/AVP', replace => ['origin'], 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw( generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 codec-offer-telephone-event always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw( generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 codec-offer-telephone-event always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'force', flags => [qw( generate-mid generate-rtcp port-latching SDES-off codec-strip-opus codec-strip-G729 codec-strip-G729a codec-strip-speex codec-strip-G723 codec-strip-GSM codec-strip-iLBC codec-mask-G722 codec-mask-PCMA codec-mask-PCMU transcode-opus)], replace => ['origin'], 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['require']}, < 'remove', flags => [qw(port-latching always-transcode SDES-off no-rtcp-attribute strip-extmap)], 'transport-protocol' => 'RTP/AVP', replace => ['origin'], 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw( generate-rtcp port-latching SDES-off codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw( generate-rtcp port-latching SDES-off codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'force', flags => [qw(no-codec-renegotiation generate-mid generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-opus codec-strip-G729 codec-strip-G729a codec-strip-speex codec-strip-G723 codec-strip-GSM codec-strip-iLBC codec-mask-G722 codec-mask-PCMA codec-mask-PCMU transcode-opus codec-offer-telephone-event)], replace => ['origin'], 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['require']}, < 'remove', flags => [qw(port-latching always-transcode SDES-off no-rtcp-attribute strip-extmap)], 'transport-protocol' => 'RTP/AVP', replace => ['origin'], 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw(no-codec-renegotiation generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 codec-offer-telephone-event always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw(no-codec-renegotiation generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-G722 codec-strip-PCMU codec-strip-PCMA codec-strip-CN codec-strip-red codec-mask-opus transcode-PCMU transcode-PCMA transcode-G722 codec-offer-telephone-event always-transcode no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'force', flags => [qw(no-codec-renegotiation generate-mid generate-rtcp port-latching SDES-off codec-strip-telephone-event codec-strip-opus codec-strip-G729 codec-strip-G729a codec-strip-speex codec-strip-G723 codec-strip-GSM codec-strip-iLBC codec-mask-G722 codec-mask-PCMA codec-mask-PCMU transcode-opus codec-offer-telephone-event)], replace => ['origin'], 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['require']}, < 'remove', flags => [qw(port-latching SDES-off no-rtcp-attribute strip-extmap)], 'transport-protocol' => 'RTP/AVP', replace => ['origin'], 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw(no-codec-renegotiation generate-rtcp port-latching SDES-off transcode-PCMU transcode-PCMA transcode-G722 codec-mask-opus no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < 20, 'ptime-reverse' => 20, ICE => 'remove', flags => [qw(no-codec-renegotiation generate-rtcp port-latching SDES-off transcode-PCMU transcode-PCMA transcode-G722 codec-mask-opus no-rtcp-attribute strip-extmap)], replace => ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux']}, < ['allow asymmetric codecs']}, < [qw/SIPREC all/]}, < [qw(inject-DTMF)] }, < [qw(inject-DTMF)] }, < ft(), code => '0', volume => 10, duration => 100 }); snd($sock_a, $port_b, rtp(8, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101 | 0x80, 1003, 3480, 0x1234, "\x00\x0a\x00\xa0")); snd($sock_a, $port_b, rtp(8, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1004, 3480, 0x1234, "\x00\x0a\x01\x40")); snd($sock_a, $port_b, rtp(8, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1005, 3480, 0x1234, "\x00\x0a\x01\xe0")); snd($sock_a, $port_b, rtp(8, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1006, 3480, 0x1234, "\x00\x0a\x02\x80")); snd($sock_a, $port_b, rtp(8, 1007, 4120, 0x1234, "\x00" x 160)); # end event rcv($sock_b, $port_a, rtpm(101, 1007, 3480, 0x1234, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1008, 3480, 0x1234, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1009, 3480, 0x1234, "\x00\x8a\x03\x20")); snd($sock_a, $port_b, rtp(8, 1008, 4280, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1010, 4280, 0x1234, "\x00" x 160)); # inject DTMF to SRTP with passthrough ($sock_a, $sock_b) = new_call([qw(198.51.100.50 3056)], [qw(198.51.100.50 3058)]); ($port_a) = offer('inject passthrough', { flags => [qw(inject-DTMF)], DTLS => 'off' }, < [qw(inject-DTMF)] }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'QjnnaukLn7iwASAs0YLzPUplJkjOhTZK2dvOwo6c', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Opr7g+J9VgQnRkCFNmL/0LP/dcF1Exu43qwiE0So', }; srtp_snd($sock_a, $port_b, rtp(8, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(8, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1001, 3160, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(8, 1001, 3160, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1002, 3320, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(8, 1002, 3320, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_b, $port_a, rtp(8, 5000, 7000, 0x5432, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(8, 5000, 7000, 0x5432, "\x00" x 160), $srtp_ctx_b); srtp_snd($sock_b, $port_a, rtp(8, 5001, 7160, 0x5432, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(8, 5001, 7160, 0x5432, "\x00" x 160), $srtp_ctx_b); srtp_snd($sock_b, $port_a, rtp(8, 5002, 7320, 0x5432, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(8, 5002, 7320, 0x5432, "\x00" x 160), $srtp_ctx_b); $resp = rtpe_req('play DTMF', 'inject DTMF towards B', { 'from-tag' => ft(), code => '0', volume => 10, duration => 100 }); srtp_snd($sock_a, $port_b, rtp(8, 1003, 3480, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101 | 0x80, 1003, 3480, 0x1234, "\x00\x0a\x00\xa0"), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1004, 3640, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101, 1004, 3480, 0x1234, "\x00\x0a\x01\x40"), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1005, 3800, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101, 1005, 3480, 0x1234, "\x00\x0a\x01\xe0"), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1006, 3960, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101, 1006, 3480, 0x1234, "\x00\x0a\x02\x80"), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1007, 4120, 0x1234, "\x00" x 160), $srtp_ctx_a); # end event srtp_rcv($sock_b, $port_a, rtpm(101, 1007, 3480, 0x1234, "\x00\x8a\x03\x20"), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101, 1008, 3480, 0x1234, "\x00\x8a\x03\x20"), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(101, 1009, 3480, 0x1234, "\x00\x8a\x03\x20"), $srtp_ctx_a); srtp_snd($sock_a, $port_b, rtp(8, 1008, 4280, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(8, 1010, 4280, 0x1234, "\x00" x 160), $srtp_ctx_a); # inject DTMF with passthrough and blocking ($sock_a, $sock_b) = new_call([qw(198.51.100.50 3002)], [qw(198.51.100.50 3004)]); ($port_a) = offer('inject passthrough', { flags => [qw(inject-DTMF)] }, < [qw(inject-DTMF)] }, < ft() }); snd($sock_a, $port_b, rtp(8, 1000, 3000, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1000, 3000, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(8, 1001, 3160, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1001, 3160, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(8, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1002, 3320, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(101 | 0x80, 1003, 3480, 0x1234, "\x02\x14\x00\xa0")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1004, 3480, 0x1234, "\x02\x14\x01\x40")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1005, 3480, 0x1234, "\x02\x14\x01\xe0")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1006, 3480, 0x1234, "\x02\x14\x02\x80")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1007, 3480, 0x1234, "\x02\x14\x03\x20")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1008, 3480, 0x1234, "\x02\x94\x03\xc0")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1009, 3480, 0x1234, "\x02\x94\x03\xc0")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1010, 3480, 0x1234, "\x02\x94\x03\xc0")); rcv_no($sock_b); snd($sock_b, $port_a, rtp(8, 5000, 7000, 0x5432, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(8, 5000, 7000, 0x5432, "\x00" x 160)); snd($sock_b, $port_a, rtp(8, 5001, 7160, 0x5432, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(8, 5001, 7160, 0x5432, "\x00" x 160)); snd($sock_b, $port_a, rtp(8, 5002, 7320, 0x5432, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(8, 5002, 7320, 0x5432, "\x00" x 160)); snd($sock_a, $port_b, rtp(8, 1011, 4440, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1011, 4440, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(8, 1012, 4600, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1012, 4600, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(8, 1013, 4760, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1013, 4760, 0x1234, "\x00" x 160)); $resp = rtpe_req('play DTMF', 'inject DTMF towards B', { 'from-tag' => ft(), code => '0', volume => 10, duration => 100 }); snd($sock_a, $port_b, rtp(8, 1014, 4920, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101 | 0x80, 1014, 4920, 0x1234, "\x00\x0a\x00\xa0")); snd($sock_a, $port_b, rtp(8, 1015, 5080, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1015, 4920, 0x1234, "\x00\x0a\x01\x40")); snd($sock_a, $port_b, rtp(8, 1016, 5240, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1016, 4920, 0x1234, "\x00\x0a\x01\xe0")); snd($sock_a, $port_b, rtp(8, 1017, 5400, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1017, 4920, 0x1234, "\x00\x0a\x02\x80")); snd($sock_a, $port_b, rtp(8, 1018, 5560, 0x1234, "\x00" x 160)); # end event rcv($sock_b, $port_a, rtpm(101, 1018, 4920, 0x1234, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1019, 4920, 0x1234, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1020, 4920, 0x1234, "\x00\x8a\x03\x20")); snd($sock_a, $port_b, rtp(8, 1019, 5720, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1021, 5720, 0x1234, "\x00" x 160)); snd($sock_a, $port_b, rtp(101 | 0x80, 1021, 6040, 0x1234, "\x03\x26\x00\xa0")); rcv_no($sock_b); $resp = rtpe_req('play DTMF', 'inject DTMF towards B over received DTMF', { 'from-tag' => ft(), code => '1', volume => 12, duration => 100 }); snd($sock_a, $port_b, rtp(101, 1022, 6040, 0x1234, "\x03\x26\x01\x40")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1023, 6040, 0x1234, "\x03\x26\x01\xe0")); rcv_no($sock_b); snd($sock_a, $port_b, rtp(101, 1024, 6040, 0x1234, "\x03\x26\x02\x80")); rcv($sock_b, $port_a, rtpm(101 | 0x80, 1026, 6520, 0x1234, "\x01\x0c\x00\xa0")); snd($sock_a, $port_b, rtp(101, 1025, 6040, 0x1234, "\x03\x26\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1027, 6520, 0x1234, "\x01\x0c\x01\x40")); # send end event snd($sock_a, $port_b, rtp(101, 1026, 6040, 0x1234, "\x03\xa6\x03\xc0")); snd($sock_a, $port_b, rtp(101, 1027, 6040, 0x1234, "\x03\xa6\x03\xc0")); snd($sock_a, $port_b, rtp(101, 1028, 6040, 0x1234, "\x03\xa6\x03\xc0")); rcv($sock_b, $port_a, rtpm(101, 1028, 6520, 0x1234, "\x01\x0c\x01\xe0")); rcv_no($sock_b); # send audio, receive end event snd($sock_a, $port_b, rtp(8, 1029, 7000, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1029, 6520, 0x1234, "\x01\x0c\x02\x80")); snd($sock_a, $port_b, rtp(8, 1030, 7160, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(101, 1030, 6520, 0x1234, "\x01\x8c\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1031, 6520, 0x1234, "\x01\x8c\x03\x20")); rcv($sock_b, $port_a, rtpm(101, 1032, 6520, 0x1234, "\x01\x8c\x03\x20")); snd($sock_a, $port_b, rtp(8, 1031, 7320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1033, 7320, 0x1234, "\x00" x 160)); if ($extended_tests) { ($sock_a, $sock_b) = new_call([qw(198.51.100.43 6060)], [qw(198.51.100.43 6062)]); ($port_a) = offer('opus fmtp options, full offer list', { codec => { transcode => ['opus/48000/2///maxaveragebitrate--40000;maxplaybackrate--32000;sprop-stereo--0;stereo--0;cbr--0;useinbandfec--0;usedtx--0;sprop-maxcapturerate--16000', 'PCMU'], mask => ['all'] } }, < { transcode => ['PCMA'], mask => ['opus'] } }, < { transcode => ['opus'] } }, < { transcode => ['opus/48000/2///useinbandfec=1;stereo=1;sprop-stereo=1'] } }, < { transcode => ['opus/48000/2///useinbandfec=1;stereo=0;sprop-stereo=0'] } }, < { transcode => ['opus/48000/2///stereo=1;sprop-stereo=0'] } }, < { transcode => ['opus/48000/2///stereo=0;sprop-stereo=1'] } }, < { transcode => ['PCMA'], mask => ['opus'] } }, < [ ] }, < [ ] }, < [ 'OSRTP-accept' ], 'transport-protocol' => 'RTP/AVP' }, < [ 'OSRTP-accept' ] }, < 'nonew' }, < [ 'OSRTP-accept' ] }, < [ 'OSRTP-accept' ], 'transport-protocol' => 'RTP/AVP' }, < [ 'OSRTP-accept' ] }, < 'nonew' }, < [ 'OSRTP-accept' ] }, < [ 'OSRTP-offer-legacy' ], 'transport-protocol' => 'RTP/SAVP' }, < [ 'OSRTP-accept-legacy' ] }, < [ 'OSRTP-offer-legacy' ], 'transport-protocol' => 'RTP/SAVP' }, < [ 'OSRTP-accept-legacy' ] }, < { transcode => ['AMR/8000/1///octet-align=1;mode-change-capability=2'] } }, < { transcode => ['AMR'] } }, < { transcode => ['AMR'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-change-capability=2'] } }, < { transcode => ['AMR/8000/1///octet-align=1'] } }, < { transcode => ['AMR/8000/1///octet-align=0'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-set=1,2,3'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-set=1,2,3'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-set=1,2,3'] } }, < { transcode => ['AMR/8000/1///octet-align=1'] } }, < { transcode => ['AMR/8000/1///octet-align=0'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-set=1,2,3,4,5'] } }, < { transcode => ['AMR/8000/1///octet-align=1;mode-set=1,2,3'] } }, < { accept => ['AMR-WB'] } }, < { accept => ['AMR-WB'] } }, < { accept => ['AMR-WB'] } }, < { accept => ['AMR-WB/16000/1///octet-align=0'] } }, < { accept => ['AMR-WB/16000/1///octet-align=1'] } }, < { accept => ['AMR-WB/16000/1///octet-align=1'] } }, < { accept => ['AMR-WB/16000/1///octet-align=1'] } }, < { accept => ['AMR-WB/16000/1///octet-align=1;mode-set=2,3,4'] } }, < { accept => ['AMR-WB/16000/1///octet-align=1;mode-set=3,4,5,6'] } }, < { accept => ['AMR-WB/16000/1///mode-set=3,4,5,6'] } }, < 'remove', 'ICE-lite' => 'backward', 'rtcp-mux' => ['demux'] }, < 'force' }, < 'remove', 'ICE-lite' => 'backward', 'rtcp-mux' => ['demux'] }, < 'force' }, < 'remove', 'ICE-lite' => 'backward', 'rtcp-mux' => ['demux'] }, < ['zero address'], ICE => 'force' }, < 'forward', ICE => 'force', 'to-tag' => tt(), 'rtcp-mux' => ['offer'] }, < { transcode => ['opus/48000/2'] } }, < { transcode => ['opus/48000/2////application=audio'] } }, < { transcode => ['opus/48000/2/16000'] } }, < { transcode => ['opus/48000/2////compression_level=2'] } }, < { transcode => ['opus/48000/2/16000///compression_level=2'] } }, < { transcode => ['PCMA'] } }, < { transcode => ['PCMA'], set => ['opus/48000/2////application=audio'] } }, < { transcode => ['PCMA'], set => ['opus/48000/2/16000'] } }, < { transcode => ['PCMA'], set => ['opus/48000/2////compression_level=2'] } }, < { transcode => ['PCMA'], set => ['opus/48000/2/16000///compression_level=2'] } }, < { transcode => ['PCMA', 'PCMU'] } }, < 'remove' }, < 'force' }, < 'RTP/AVP' }, < 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_a, }; # consume DTLS rcv($sock_a, -1, qr/^\x16\xfe\xff\x00\x00\x00\x00\x00\x00\x00/); snd($sock_b, $port_a, rtp(0, 65534, 4000, 0x6543, "\x00" x 160)); srtp_rcv($sock_a, $port_b, rtpm(0, 65534, 4000, -1, "\x00" x 160), $srtp_ctx_a); is($srtp_ctx_a->{roc}, 0, "initial zero ROC"); snd($sock_b, $port_a, rtp(0, 65535, 4160, 0x6543, "\x00" x 160)); srtp_rcv($sock_a, $port_b, rtpm(0, 65535, 4160, -1, "\x00" x 160), $srtp_ctx_a); is($srtp_ctx_a->{roc}, 0, "initial zero ROC"); snd($sock_b, $port_a, rtp(0, 0, 4320, 0x6543, "\x00" x 160)); srtp_rcv($sock_a, $port_b, rtpm(0, 0, 4320, -1, "\x00" x 160), $srtp_ctx_a); is($srtp_ctx_a->{roc}, 1, "ROC increase"); ($port_ax) = offer('ROC reset after re-invite', { 'transport-protocol' => 'RTP/AVP' }, < 'off' }, <{roc}, 1, "ROC unchanged"); new_call; offer('ICE restart', { ICE => 'remove' }, < { transcode => [qw(PCMA G722 PCMU)] } }, < { transcode => [qw(PCMA G722 PCMU)] } }, < { strip => ['all'] } }, < { strip => ['all'] } }, < 'foo' }, < 'bar' }, < 'bar' }, < $ft1 }); # reverse re-invite to new from-tag ft3 new_ft; my $ft3 = ft(); reverse_tags; # tt is the new tag ft3 now ($port_b) = offer('re-invite re-tag w/ via-branch', { 'via-branch' => 'blah', 'to-tag' => tt() }, < 'baz', 'to-tag' => $ft2 }, < { transcode => ['opus','PCMA'] } }, < 'fw' }, < 'bk' }, < 'off' }, < 'off' }, < 'off' }, < 'off' }, < ['static'] }, < 'force', }, < 'force', }, < 'force', }, < 'force', 'rtcp-mux' => ['require'] }, < 'force', 'rtcp-mux' => ['require'] }, < 'force', flags => ['no port latching']}, < 'force', flags => ['no port latching']}, < 'force', flags => ['no port latching']}, < ['zero address'] }, < ['zero address'] }, < ['zero address'] }, < ['zero address'] }, < 'IP6' }, < 'IP6' }, < 'IP6' }, < 'IP6' }, < 'IP4' }, < 'IP4' }, < 'IP4' }, < 'IP4' }, < 'remove' }, < 'remove' }, < { accept => ['PCMU'] } }, < { consume => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['PCMA'] } }, < { mask => ['PCMU'] } }, < { mask => ['telephone-event'] } }, < { mask => ['telephone-event'] } }, < { strip => ['all'], consume => ['CN'], offer => ['PCMA', 'PCMU', 'telephone-event'], } }, < ['origin'], codec => { transcode => ['opus/48000/1', 'opus/48000/2'] } }, < ['origin'], codec => { transcode => ['opus/48000', 'opus/48000/2'] } }, < ['origin'], codec => { transcode => ['opus', 'opus/48000/2'] } }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], flags => ['always transcode'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { strip => ['all'], consume => ['CN'], offer => ['PCMA','PCMU','telephone-event'], } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'], codec => { transcode => ['G722'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['CN'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMU', 'CN'] } }, < 'remove', replace => ['origin'] }, < { mask => ['all'], transcode => ['G722', 'opus/48000/1//test=1', 'speex', 'PCMA', 'telephone-event'], } }, < { transcode => ['AMR','PCMA'] } }, < 'remove', replace => ['origin'], codec => { mask => ['all'], transcode => [ 'PCMA', 'AMR', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'telephone-event', ], } }, < 'remove', replace => ['origin'], flags => [] }, < 'remove', replace => ['origin'], codec => { mask => ['all'], transcode => [ 'PCMA', 'AMR', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'telephone-event', ], } }, < 'remove', replace => ['origin'], flags => ['reorder-codecs'] }, < { mask => ['all'], transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], } }, < ['single-codec'], }, < { mask => ['all'], transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], } }, < { strip => ['all'], offer => ['PCMA'], } }, < { mask => ['all'], transcode => ['G722', 'AMR-WB/16000/1///mode-set--0,1,2;mode-change-period--2;mode-change-capability--2/dtx--1', 'AMR', 'PCMA', 'telephone-event'], } }, < { strip => ['all'], offer => ['PCMA', 'telephone-event'], } }, < 'remove', replace => ['origin'], flags => [qw(inject-DTMF)] }, < 'remove', replace => ['origin'], flags => [qw(inject-DTMF)] }, < AMR-WB default', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['AMR-WB'] } }, < AMR-WB default', { ICE => 'remove', replace => ['origin'] }, < AMR-WB force bitrate', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['AMR-WB/16000/1/23850'] } }, < AMR-WB force bitrate', { ICE => 'remove', replace => ['origin'] }, < AMR-WB answer mode-set', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['AMR-WB/16000/1/23850'] } }, < AMR-WB answer mode-set', { ICE => 'remove', replace => ['origin'] }, < AMR-WB offer mode-set', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['AMR-WB/16000/1/23850//mode-set=0,1,2,3,4,5'] } }, < AMR-WB offer mode-set', { ICE => 'remove', replace => ['origin'] }, < AMR-WB offer mode-set, restrict answer', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['AMR-WB/16000/1/23850//mode-set=0,1,2,3,4,5'] } }, < AMR-WB offer mode-set, restrict answer', { ICE => 'remove', replace => ['origin'] }, < PCM default', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] } }, < PCM default', { ICE => 'remove', replace => ['origin'] }, < PCM force bitrate', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < PCM force bitrate', { ICE => 'remove', replace => ['origin'] }, < PCM offer mode-set', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < PCM offer mode-set', { ICE => 'remove', replace => ['origin'] }, < PCM CMR', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < PCM CMR', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/6600'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1////CMR-interval=200'] } }, < 'remove', replace => ['origin'] }, < PCM CMR w/ mode-change-interval', { ICE => 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850///mode-change-interval=200'] } }, < PCM CMR w/ mode-change-interval', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'], 'set' => ['AMR-WB/16000/1/23850///mode-change-interval=200'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { 'transcode' => ['AMR-WB/16000/1/23850//octet-align=1/mode-change-interval=200'] } }, < 'remove', replace => ['origin'] }, < 'remove', flags => ['inject-DTMF'], codec => {transcode => ['G722']}, }, < 'remove', flags => ['inject-DTMF'], }, < 'remove', flags => [], codec => {transcode => ['G722']}, }, < 'remove', flags => [], }, < { mask => ['all'], transcode => ['G722', 'opus/48000/1', 'PCMA', 'telephone-event'] }, }, < 'remove', flags => [], codec => {transcode => ['G722']}, }, < 'remove', flags => [], }, < 'remove', flags => [], codec => {transcode => ['G722']}, }, < 'remove', flags => [], }, < 'remove', flags => [], codec => {transcode => ['G722']}, }, < 'remove', flags => ['single codec'], }, < 'remove', flags => [], }, < 'remove', flags => [], }, < 'remove', flags => [], }, < 'remove', flags => ['single codec'], }, < 'remove', flags => [], }, < 'remove', flags => ['single codec'], }, < 'remove', flags => [], codec => {transcode => ['opus']}, }, < 'remove', flags => ['single codec'], }, < 'remove', flags => [], codec => {mask => ['all'], transcode => ['opus/48000/1', 'PCMA', 'telephone-event']}, }, < 'remove', flags => ['single codec'], }, < 'remove', flags => [], codec => {transcode => ['PCMA']}, }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', SDES => ['off'], }, < [], }, < [], }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', SDES => ['off'], 'DTLS-fingerprint' => 'SHA-1', }, < [], }, < [], }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', SDES => ['off'], 'DTLS-fingerprint' => 'sha-256', }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', }, < ft() }); offer('GH 1086', { ICE => 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', DTLS => 'off', }, < 'remove', flags => [], DTLS => 'off', }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', DTLS => 'off', }, < 'remove', flags => [], DTLS => 'off', 'transport-protocol' => 'accept', }, < 'remove', flags => [], 'transport-protocol' => 'RTP/SAVP', DTLS => 'off', }, < 'remove', flags => [], DTLS => 'off', 'transport-protocol' => 'RTP/AVPF', }, < 'remove', flags => ["codec-mask-all", "codec-strip-telephone-event", "codec-transcode-PCMU", "codec-transcode-G722", "codec-transcode-t38", "codec-offer-telephone-event", "port-latching"], 'to-tag' => tt(), }, < 'remove', flags => ["trust-address", "symmetric-codecs"], }, < 'remove', flags => ["codec-mask-all", "codec-strip-telephone-event", "codec-transcode-PCMU", "codec-transcode-G722", "codec-transcode-t38", "codec-offer-telephone-event", "port-latching"], 'to-tag' => tt(), }, < 'remove', 'transport-protocol' => 'RTP/AVP', }, < 'remove', }, < 'remove', 'transport-protocol' => 'RTP/AVP', 'DTLS-reverse' => 'passive', }, < 'remove', }, < 'remove', 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], }, < 'remove', SDES => 'off', 'via-branch' => 'foo.0', 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['offer'], }, < 'remove', SDES => 'off', 'via-branch' => 'foo.1', 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], }, < ft(), 'via-branch' => 'foo.1' }); answer('rtcp-mux branched w delete-delay', { ICE => 'remove', SDES => 'off', 'via-branch' => 'foo.0', 'transport-protocol' => 'RTP/AVPF', 'rtcp-mux' => ['demux'], }, < 'remove', SDES => 'off', 'via-branch' => 'foo.0', 'transport-protocol' => 'RTP/SAVPF', 'rtcp-mux' => ['offer'], }, < 'remove', SDES => 'off', 'via-branch' => 'foo.1', 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], }, < ft(), 'via-branch' => 'foo.1', 'delete-delay' => 0, }); answer('rtcp-mux branched delete-delay=0', { ICE => 'remove', SDES => 'off', 'via-branch' => 'foo.0', 'transport-protocol' => 'RTP/AVPF', 'rtcp-mux' => ['demux'], }, < "RTP/SAVP", "ICE" => "remove", "rtcp-mux" => [ "demux" ], DTLS => 'off', "replace" => [ "origin" ], "via-branch" => "z9hG4bK0ae8.cc3c994fa8d0c0f1f2536bba541306fb.0", }, < "remove", "rtcp-mux" => [ "demux" ], DTLS => 'off', "replace" => [ "origin" ], "via-branch" => "z9hG4bK0ae8.cc3c994fa8d0c0f1f2536bba541306fb.0", }, < "RTP/SAVP", "ICE" => "remove", "rtcp-mux" => [ "demux" ], DTLS => 'off', "replace" => [ "origin" ], "via-branch" => "z9hG4bK0ae8.cc3c994fa8d0c0f1f2536bba541306fb.0", 'to-tag' => tt(), }, < "remove", "rtcp-mux" => [ "demux" ], DTLS => 'off', "replace" => [ "origin" ], "via-branch" => "z9hG4bK0ae8.cc3c994fa8d0c0f1f2536bba541306fb.0", }, < "RTP/SAVP", "ICE" => "remove", "rtcp-mux" => [ "demux" ], DTLS => 'off', "replace" => [ "origin" ], "via-branch" => "z9hG4bK0ae8.cc3c994fa8d0c0f1f2536bba541306fb.0", 'to-tag' => tt(), }, < 'remove', replace => ['origin'], DTLS => 'off', 'transport-protocol' => 'RTP/SAVP', flags => ['inject-DTMF'], 'rtcp-mux' => ['demux'] }, < 'remove', replace => ['origin'], DTLS => 'off', ICE => 'remove', flags => ['inject-DTMF'], 'rtcp-mux' => ['demux'] }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'QjnnaukLn7iwASAs0YLzPUplJkjOhTZK2dvOwo6c', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'cdDuBSOp/rX/7ikmU1Tnuu337gXUUMFAhkARhB/j', }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x6543, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, -1, "\x00" x 160), $srtp_ctx_b); # RTCP ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call( [qw(198.51.100.1 7430)], [qw(198.51.100.1 7431)], [qw(198.51.100.3 7432)], [qw(198.51.100.3 7433)], ); ($port_a, $port_ax) = offer('demux unannounced RTCP', { }, < 'remove', }, < 'remove', }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; ($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); # SR LEN SSRC NTP1 NTP2 RTP PACKETS OCTETS SSRC LOST SEQ JITTER LAST SR DLSR CNAME @ret1 = rcv($sock_ax, $port_bx, qr/^\x81\xc8\x00\x0c(.{4})(.{4})(.{4})(.{4})\x00\x00\x00\x01\x00\x00\x00\xac\x00\x00\x12\x34\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xca\x00\x05(.{4})\x01\x0c([0-9a-f]{12})\x00\x00$/s); is $ret1[0], $ssrc, 'SSRC matches'; is $ret1[3], $ts, 'TS matches'; is $ret1[4], $ssrc, 'SSRC matches'; rtpe_req('delete', "delete", { 'from-tag' => ft() }); ($sock_a, $sock_ax, $sock_b, $sock_bx) = new_call( [qw(198.51.100.1 7400)], [qw(198.51.100.1 7401)], [qw(198.51.100.3 7402)], [qw(198.51.100.3 7403)], ); ($port_a, $port_ax) = offer('RTCP player w/ previous SR', { ICE => 'remove', }, < 'remove', }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; ($seq, $ts, $ssrc) = rcv($sock_a, $port_b, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); # SR LEN SSRC NTP1 NTP2 RTP PACKETS OCTETS SSRC LOST SEQ JITTER LAST SR DLSR CNAME @ret1 = rcv($sock_ax, $port_bx, qr/^\x81\xc8\x00\x0c(.{4})(.{4})(.{4})(.{4})\x00\x00\x00\x01\x00\x00\x00\xac\x00\x00\x12\x34\x00\x00\x00\x00\x00\x00\x03\xe8\x00\x00\x00\x00\x56\x78\x9a\xbc(.{4})\x81\xca\x00\x05(.{4})\x01\x0c([0-9a-f]{12})\x00\x00$/s); is $ret1[0], $ssrc, 'SSRC matches'; is $ret1[3], $ts, 'TS matches'; cmp_ok $ret1[4], '<', 6553, 'DSLR ok'; is $ret1[5], $ssrc, 'SSRC matches'; rtpe_req('delete', "delete", { 'from-tag' => ft() }); # SRTP control - accept diff suite from offer ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3328)], [qw(198.51.100.3 3330)]); ($port_a, undef, undef, undef, undef, undef, undef, undef, $srtp_key_a) = offer('reg SRTP offer, accept, diff suite', { ICE => 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_32}, key => 'Kl3GFJ5Gqz5x07xYkoyHODkVkSpiplZnXsQIw+Q7', }; $srtp_ctx_a_rev = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_32}, key => $srtp_key_a, }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; $srtp_ctx_b_rev = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_b, }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a_rev); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b_rev); # OSRTP ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3380)], [qw(198.51.100.3 3382)]); ($port_a) = offer('OSRTP offer, reject, reinvite', { DTLS => 'off' }, < 'off' }, < 'off' }, < 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Kl3GFJ5Gqz5x07xYkoyHODkVkSpiplZnXsQIw+Q7', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3320)], [qw(198.51.100.3 3322)]); ($port_a, undef, undef, undef, undef, undef, undef, undef, $srtp_key_a) = offer('OSRTP offer, accept, diff suite', { ICE => 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_32}, key => 'Kl3GFJ5Gqz5x07xYkoyHODkVkSpiplZnXsQIw+Q7', }; $srtp_ctx_a_rev = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_32}, key => $srtp_key_a, }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; $srtp_ctx_b_rev = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_b, }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a_rev); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b_rev); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3324)], [qw(198.51.100.3 3326)]); ($port_a) = offer('OSRTP offer, reject', { ICE => 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off' }, < 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['accept'] }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_a, }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_b); rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160)); snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160)); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_a); reverse_tags(); offer('OSRTP offer, reject w/ accept flag, reverse reinvite', { ICE => 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['offer'] }, < 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['accept'] }, < 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['offer'] }, < 'remove', replace => ['origin'], DTLS => 'off', }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Kl3GFJ5Gqz5x07xYkoyHODkVkSpiplZnXsQIw+Q7', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_a, }; snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160)); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_b); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_a); rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3356)], [qw(198.51.100.3 3358)]); ($port_a, undef, undef, undef, undef, undef, undef, undef, $srtp_key_a) = offer('non-OSRTP offer with offer flag and protocol, accept', { ICE => 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['offer'], 'transport protocol' => 'RTP/AVP'}, < 'remove', replace => ['origin'], DTLS => 'off', }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Kl3GFJ5Gqz5x07xYkoyHODkVkSpiplZnXsQIw+Q7', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => $srtp_key_a, }; snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160)); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_b); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_a); rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3340)], [qw(198.51.100.3 3342)]); ($port_a, undef, $srtp_key_a) = offer('non-OSRTP offer with offer flag, reject', { ICE => 'remove', replace => ['origin'], DTLS => 'off', OSRTP => ['offer'] }, < 'remove', replace => ['origin'], DTLS => 'off', }, < 'remove', }, < 'remove', 'transport-protocol' => 'RTP/SAVP', SDES => ['lifetime'], DTLS => 'off' }, < 'remove', codec => { mask => ['full'], transcode => ['PCMA','telephone-event'] } }, < 'remove', }, < 'remove', codec => { mask => ['all'], transcode => ['PCMA','telephone-event'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', }, < 'remove', codec => { transcode => ['G722'] } }, < 'remove', flags => ['symmetric codecs'] }, < [ 'decode' ], ICE => 'remove', }, < ft() }); new_call(); offer('T.38 forward re-invite', { ICE => 'remove', }, < 'remove', }, < 'remove', 'T.38' => [ 'force' ], }, < 'remove', }, < ft() }); new_call(); offer('T.38 reverse re-invite', { ICE => 'remove', }, < 'remove', }, < 'remove', 'T.38' => [ 'decode' ], }, < 'remove' }, < ft() }); new_call(); offer('T.38 forward re-invite w/ unsupported codec', { ICE => 'remove', }, < 'remove', }, < 'remove', 'T.38' => [ 'force' ], }, < 'remove', }, < ft() }); new_call(); offer('T.38 reverse re-invite w/ unsupported codec', { ICE => 'remove', }, < 'remove', }, < 'remove', 'T.38' => [ 'decode' ], }, < 'remove' }, < ft() }); new_call; offer('T.38 FEC invite', { ICE => 'remove', 'T.38' => [ 'force', 'FEC' ], }, < ft() }); # github issue 850 new_call; @ret1 = offer('gh 850', { ICE => 'force-relay', flags => [qw(SDES-off)], 'transport-protocol' => 'UDP/TLS/RTP/SAVPF', 'rtcp-mux' => [qw(accept offer)], 'via-branch' => 'z9hG4bK9463.af303705.113', }, < 'force-relay', flags => [qw(SDES-off)], 'transport-protocol' => 'UDP/TLS/RTP/SAVPF', 'rtcp-mux' => [qw(accept offer)], 'via-branch' => 'z9hG4bK9463.af303705.113', }, < 'force-relay', flags => [qw(SDES-off)], 'transport-protocol' => 'UDP/TLS/RTP/SAVPF', 'rtcp-mux' => [qw(accept offer)], 'via-branch' => 'z9hG4bK9463.af303705.113', }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], flags => ['pad crypto'], DTLS => 'off' }, < 'remove', replace => ['origin'], flags => ['pad crypto'], DTLS => 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'IDdiM2QzOWYzMjA2YzkwZWIxY2NmOWVhOTc4MjE1', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 7310)], [qw(198.51.100.3 7312)]); ($port_a) = offer('gh829', { ICE => 'remove', replace => ['origin'], flags => ['pad crypto'], DTLS => 'off' }, < 'remove', replace => ['origin'], flags => ['pad crypto'], DTLS => 'off' }, < $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'IDdiM2QzOWYzMjA2YzkwZWIxY2NmOWVhOTc4MjE1', }; $srtp_ctx_b = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'Qk0TvVeyfqfjFd/YebnyyklqSEhJntpVKV1KAhHa', }; srtp_snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160), $srtp_ctx_a); srtp_snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); srtp_rcv($sock_a, $port_b, rtpm(0, 2000, 4000, 0x3456, "\x00" x 160), $srtp_ctx_b); # DTMF injection # # no transcoding, RFC payload type present ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6010)], [qw(198.51.100.3 6012)]); ($port_a) = offer('no transcoding, RFC payload type present', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\x20")); snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1009, 4120, $ssrc, "\x00" x 160)); snd($sock_b, $port_a, rtp(0, 4000, 8000, 0x6543, "\x00" x 160)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 8160, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); $resp = rtpe_req('play DTMF', 'inject DTMF towards A', { 'from-tag' => tt(), code => '*', volume => 10, duration => 100 }); snd($sock_b, $port_a, rtp(0, 4002, 8320, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(96 | 0x80, 4002, 8320, $ssrc, "\x0a\x0a\x00\xa0")); snd($sock_b, $port_a, rtp(0, 4003, 8480, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(96, 4003, 8320, $ssrc, "\x0a\x0a\x01\x40")); snd($sock_b, $port_a, rtp(0, 4004, 8640, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(96, 4004, 8320, $ssrc, "\x0a\x0a\x01\xe0")); snd($sock_b, $port_a, rtp(0, 4005, 8800, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(96, 4005, 8320, $ssrc, "\x0a\x0a\x02\x80")); snd($sock_b, $port_a, rtp(0, 4006, 8960, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(96, 4006, 8320, $ssrc, "\x0a\x8a\x03\x20")); rcv($sock_a, $port_b, rtpm(96, 4007, 8320, $ssrc, "\x0a\x8a\x03\x20")); rcv($sock_a, $port_b, rtpm(96, 4008, 8320, $ssrc, "\x0a\x8a\x03\x20")); snd($sock_b, $port_a, rtp(0, 4007, 9120, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4009, 9120, $ssrc, "\x00" x 160)); # transcoding, RFC payload type present on both sides ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6110)], [qw(198.51.100.3 6112)]); ($port_a) = offer('transcoding, RFC payload type present on both sides', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'], codec => { transcode => ['PCMA'] }}, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\x20")); snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1009, 4120, $ssrc, "\x2a" x 160)); snd($sock_b, $port_a, rtp(8, 4000, 8000, 0x6543, "\x2a" x 160)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); snd($sock_b, $port_a, rtp(8, 4001, 8160, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); $resp = rtpe_req('play DTMF', 'inject DTMF towards A', { 'from-tag' => tt(), code => '#', volume => -10, duration => 100 }); snd($sock_b, $port_a, rtp(8, 4002, 8320, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(96 | 0x80, 4002, 8320, $ssrc, "\x0b\x0a\x00\xa0")); snd($sock_b, $port_a, rtp(8, 4003, 8480, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(96, 4003, 8320, $ssrc, "\x0b\x0a\x01\x40")); snd($sock_b, $port_a, rtp(8, 4004, 8640, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(96, 4004, 8320, $ssrc, "\x0b\x0a\x01\xe0")); snd($sock_b, $port_a, rtp(8, 4005, 8800, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(96, 4005, 8320, $ssrc, "\x0b\x0a\x02\x80")); snd($sock_b, $port_a, rtp(8, 4006, 8960, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(96, 4006, 8320, $ssrc, "\x0b\x8a\x03\x20")); rcv($sock_a, $port_b, rtpm(96, 4007, 8320, $ssrc, "\x0b\x8a\x03\x20")); rcv($sock_a, $port_b, rtpm(96, 4008, 8320, $ssrc, "\x0b\x8a\x03\x20")); snd($sock_b, $port_a, rtp(8, 4007, 9120, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4009, 9120, $ssrc, "\x00" x 160)); # no transcoding, no RFC payload type present ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6014)], [qw(198.51.100.3 6016)]); ($port_a) = offer('no transcoding, no RFC payload type present', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 120, pause => 110 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1002, 3320, $ssrc, "\xff\x93\x94\xbc\x2e\x56\xbf\x2b\x13\x1b\xa7\x8e\x98\x47\x25\x41\xe2\x24\x16\x2b\x99\x8e\x9f\x28\x1e\x3d\x5b\x23\x1c\xdf\x92\x8f\xb6\x1c\x1c\x40\x5d\x26\x25\xaa\x8f\x95\x3b\x15\x1d\x5e\xde\x2c\x38\x9d\x8f\x9e\x1f\x11\x20\xc0\xc1\x37\xdd\x99\x92\xb7\x15\x10\x2c\xac\xb5\x49\xb8\x97\x99\x37\x0f\x13\x58\xa0\xae\x67\xae\x99\xa4\x1f\x0d\x1a\xae\x9b\xad\x7b\xad\x9d\xbf\x16\x0e\x27\x9d\x98\xb0\x55\xb1\xa6\x3a\x11\x11\x63\x95\x98\xbf\x3e\xbb\xb4\x26\x10\x1a\xa9\x90\x9a\x4e\x30\xce\xd4\x1e\x12\x29\x99\x8e\xa1\x2d\x29\x6d\x4b\x1c\x18\xef\x91\x8f\xb6\x1f\x24\x57\x3e\x1d\x20\xa9\x8e\x95\x3e\x19\x23\x67\x3e\x21\x31\x9c\x8e\x9e\x22\x14\x26\xcd\x4a")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3480, $ssrc, "\x2a\xdf\x96\x90\xb5\x17\x13\x2f\xb6\xf5\x36\xb1\x93\x96\x39\x10\x15\x55\xaa\xc8\x4c\xa7\x95\xa0\x1f\x0e\x1b\xb4\xa1\xbd\xed\xa4\x99\xbb\x15\x0e\x27\xa0\x9d\xbd\xda\xa4\x9f\x39\x10\x11\x58\x98\x9c\xc8\xf9\xa9\xac\x23\x0e\x19\xab\x92\x9e\x59\x4c\xb0\xca\x1b\x10\x27\x9a\x90\xa5\x35\x3a\xbe\x43\x18\x15\x6c\x92\x91\xb7\x26\x30\xd6\x32\x18\x1d\xa9\x8e\x96\x44\x1d\x2d\xfc\x2e\x1b\x2d\x9a\x8d\x9e\x25\x19\x2d\xe7\x2f\x20\xea\x94\x8f\xb3\x19\x17\x36\xc8\x36\x2c\xae\x90\x95\x3b\x12\x18\x55\xb7\x43\x3e\xa1\x91\x9e\x1f\x0f\x1d\xba\xac\x64\xe8\x9d\x95\xb7\x15\x0e\x29\xa6\xa6\xda\xc3\x9d\x9b\x39\x0f\x11\x51\x9c\xa2\xd8\xbe\x9f\xa7\x21\x0e\x18\xad")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1004, 3640, $ssrc, "\x96\xa3\x68\xc4\xa5\xc2\x19\x0e\x26\x9c\x93\xa9\x3f\xdb\xae\x3e\x14\x12\x5b\x93\x93\xb9\x2e\x51\xbe\x2c\x14\x1b\xa9\x8f\x97\x4c\x25\x3f\xde\x25\x16\x2a\x9a\x8e\x9e\x29\x1e\x3b\x5e\x24\x1b\x7b\x92\x8f\xb2\x1c\x1c\x3e\x61\x27\x25\xac\x8f\x94\x3e\x15\x1c\x59\xdb\x2d\x37\x9e\x8f\x9d\x20\x11\x1f\xc2\xbf\x38\xea\x99\x92\xb4\x16\x10\x2b\xad\xb4\x49\xba\x98\x98\x3a\x0f\x12\x4e\xa1\xad\x68\xaf\x99\xa3\x20\x0d\x19\xb0\x9b\xac\x7b\xae\x9d\xbc\x17\x0e\x25\x9e\x98\xaf\x55\xb2\xa6\x3d\x12\x11\x52\x96\x97\xbd\x3e\xbc\xb3\x28\x10\x19\xab\x90\x9a\x54\x2f\xd0\xcf\x1f\x12\x27\x9a\x8e\xa0\x2e\x28\x66\x4e\x1d\x18\x62\x92\x8f\xb2\x20\x23\x53\x3f\x1d\x1f")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1005, 3800, $ssrc, "\xab\x8e\x94\x44\x19\x22\x61\x40\x21\x2f\x9c\x8e\x9d\x23\x14\x25\xce\x4d\x2a\xf7\x96\x8f\xb1\x18\x13\x2e\xb7\xe8\x36\xb3\x94\x96\x3c\x10\x15\x4d\xaa\xc5\x4b\xa8\x95\x9f\x20\x0e\x1a\xb6\xa0\xbc\xf5\xa4\x99\xb8\x16\x0e\x26\xa1\x9d\xbb\xdd\xa5\x9f\x3c\x10\x10\x4c\x99\x9b\xc5\x78\xaa\xac\x24\x0f\x18\xac\x93\x9d\x5f\x4a\xb1\xc7\x1c\x0f\x25\x9b\x90\xa3\x36\x39\xbf\x47\x18\x14\x56\x92\x90\xb4\x27\x2f\xd7\x34\x18\x1c\xab\x8e\x95\x4b\x1d\x2c\xfe\x2f\x1b\x2c\x9b\x8d\x9d\x27\x19\x2c\xe7\x30\x20\x6d\x94\x8f\xaf\x1a\x17\x34\xc8\x37\x2b\xaf\x91\x94\x3f\x12\x18\x4e\xb6\x45\x3d\xa3\x91\x9e\x20\x0f\x1c\xbc\xab\x6c\xf5\x9e\x95\xb3\x16\x0e\x27\xa7\xa5")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1006, 3960, $ssrc, "\xd6\xc6\x9d\x9b\x3d\x0f\x11\x49\x9c\xa1\xd4\xbf\x9f\xa6\x22\x0e\x18\xaf\x96\xa2\x6e\xc6\xa5\xbe\x19\x0e\x24\x9d\x93\xa8\x40\xe1\xae\x42\x15\x12\x4e\x94\x93\xb7\x2e\x4e\xbe\x2d\x14\x1a\xab\x8f\x97\x52\x25\x3e\xdc\x26\x16\x28\x9b\x8e\x9e\x2b\x1e\x3a\x61\x25\x1b\x5d\x93\x8f\xaf\x1d\x1c\x3d\x67\x27\x24\xad\x8f\x93\x45\x15\x1c\x53\xd7\x2d\x35\x9f\x8f\x9c\x22\x11\x1f\xc5\xbe\x38\x7a\x9a\x91\xb0\x17\x10\x29\xad\xb3\x4a\xbc\x98\x98\x3e\x10\x12\x48\xa1\xad\x6a\xb1\x9a\xa1\x21\x0e\x18\xb3\x9b\xab\x7d\xaf\x9d\xb9\x18\x0e\x23\x9f\x97\xae\x55\xb4\xa5\x40\x12\x10\x49\x96\x97\xbb\x3d\xbd\xb2\x29\x10\x18\xac\x90\x99\x5d\x2f\xd4\xcd\x1f\x12\x25\x9b")); snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1007, 4120, $ssrc, "\x8e\x9f\x2f\x28\x5f\x51\x1d\x17\x52\x92\x8f\xaf\x20\x22\x50\x42\x1e\x1f\xad\x8e\x93\x4b\x19\x21\x5d\x42\x22\x2e\x9d\x8e\x9c\x25\x14\x24\xd0\x4f\x2a\x68\x97\x8f\xae\x18\x12\x2c\xb7\xdf\x36\xb6\x94\x95\x41\x11\x14\x48\xaa\xc3\x4a\xaa\x95\x9e\x21\x0e\x19\xb8\xa0\xba\xfe\xa5\x99\xb4\x17\x0e\x24\xa2\x9c\xba\xe0\xa6\x9e\x40\x10\x10\x45\x99\x9b\xc2\x6d\xaa\xab\x26\x0f\x17\xae\x93\x9c\x6a\x48\xb2\xc3\x1c\x0f\x23\x9c\x90\xa2\x37\x38\xbf\x4b\x19\x14\x4b\x93\x90\xb1\x27\x2e\xd8\x36\x19\x1c\xad\x8e\x94\x52\x1d\x2b\x7d\x30\x1b\x2a\x9c\x8d\x9c\x28\x19\x2b\xe7\x31\x20\x5a\x95\x8f\xad\x1a\x16\x32\xc8\x39\x2b\xb2\x91\x94\x46\x13\x17\x4a\xb6\x48\x3c")); # pause snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1008, 4280, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1009, 4440, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1010, 4600, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1011, 4760, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1012, 4920, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1013, 5080, $ssrc, "\xff" x 80 . "\x00" x 80)); snd($sock_b, $port_a, rtp(0, 4000, 8000, 0x6543, "\x00" x 160)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 8160, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); $resp = rtpe_req('play DTMF', 'inject DTMF towards A', { 'from-tag' => tt(), code => '4', volume => 3, duration => 150, pause => 100 }); snd($sock_b, $port_a, rtp(0, 4002, 8320, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 8320, $ssrc, "\xff\x90\x8a\x93\xd9\x1b\x18\x27\x65\xe5\x33\x29\x4c\x9e\x8f\x91\xb8\x15\x09\x0d\x32\x98\x8e\x96\xbb\x2c\x2b\x4c\xd8\x34\x1c\x18\x2e\x9d\x8c\x8c\xa5\x1a\x0b\x0d\x27\xa3\x97\x9e\xbd\x4f\xc4\xaa\xb2\x2c\x12\x0e\x1e\xa1\x8b\x8a\x9c\x25\x0e\x10\x25\xb7\xa7\xb7\x5e\xcb\xa2\x98\x9f\x30\x0f\x0a\x16\xae\x8d\x8a\x98\x3a\x18\x19\x2c\xdd\xfd\x30\x2b\xce\x99\x8e\x95\x4c\x0f\x09\x10\xdf\x93\x8e\x9a\xec\x28\x2c\x56\xee\x2d\x1a\x1a\x48\x97\x8b\x8e\xba\x14\x0a\x0f\x39\x9d\x96\xa1\xcd\x4e\xbe\xab\xbe\x23\x10\x10\x2b\x99\x8a\x8c\xa7\x1b\x0d\x12\x2f\xad\xa7\xbc\x5e\xbd\x9f\x99\xa8\x23\x0d\x0b\x1d\x9f\x8b\x8c\x9f\x29\x16\x1b\x34\xcd\x60\x2f\x2f\xb6\x96")); snd($sock_b, $port_a, rtp(0, 4003, 8480, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4003, 8480, $ssrc, "\x8e\x9b\x2b\x0c\x09\x17\xae\x8f\x8e\x9e\x3f\x25\x2e\x65\x5c\x28\x1a\x1e\xc2\x92\x8a\x92\x44\x0f\x0a\x14\xd6\x99\x97\xa6\x7c\x4e\xba\xad\xe5\x1d\x0f\x13\x49\x92\x89\x8e\xbe\x15\x0d\x16\x43\xa8\xa7\xc1\x66\xb5\x9d\x9a\xb6\x1b\x0c\x0d\x2b\x98\x8a\x8d\xab\x1f\x15\x1d\x3f\xc7\x52\x2e\x39\xaa\x93\x8f\xa3\x1e\x0b\x0b\x1e\x9f\x8d\x8f\xa7\x30\x23\x31\x7c\x4a\x24\x1a\x24\xac\x8e\x8b\x99\x28\x0c\x0a\x1a\xb0\x96\x98\xac\x4f\x53\xb7\xaf\x44\x19\x0f\x18\xba\x8e\x89\x93\x3f\x10\x0d\x1a\xd5\xa3\xa8\xca\xf9\xae\x9c\x9d\xec\x16\x0b\x10\x4e\x91\x89\x90\xc6\x1a\x14\x20\x55\xc3\x4a\x2f\x49\xa2\x91\x92\xb2\x17\x09\x0c\x2d\x99\x8d\x92\xb3\x29\x23\x36\xf2")); snd($sock_b, $port_a, rtp(0, 4004, 8640, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4004, 8640, $ssrc, "\x3e\x20\x1b\x2d\xa0\x8d\x8c\xa1\x1c\x0a\x0c\x22\xa3\x94\x9a\xb5\x44\x5c\xb5\xb6\x32\x16\x0f\x1e\xa6\x8c\x8a\x99\x28\x0e\x0e\x20\xb7\xa1\xab\xd4\xdb\xaa\x9c\xa1\x38\x11\x0b\x15\xb5\x8d\x8a\x96\x3f\x16\x15\x26\xdd\xc2\x43\x31\xdf\x9d\x90\x96\x6d\x11\x09\x0f\x5a\x93\x8c\x97\xd2\x23\x23\x3b\xf6\x37\x1f\x1d\x40\x9a\x8c\x8e\xb2\x15\x09\x0e\x31\x9c\x93\x9c\xc2\x3e\x74\xb4\xbf\x29\x14\x11\x29\x9b\x8a\x8b\xa3\x1c\x0d\x0f\x2a\xab\x9f\xad\xe0\xcc\xa6\x9c\xa9\x28\x0e\x0c\x1c\xa2\x8b\x8b\x9c\x2a\x14\x17\x2c\xc6\xc4\x3e\x36\xbd\x99\x90\x9b\x30\x0d\x09\x15\xb3\x8f\x8d\x9b\x42\x1f\x25\x42\x70\x30\x1e\x1f\xcf\x95\x8b\x92\x58\x0f\x09\x12\x6f\x98\x93")); snd($sock_b, $port_a, rtp(0, 4005, 8800, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 8800, $ssrc, "\x9f\xe5\x3b\xe2\xb5\xd9\x21\x12\x14\x3e\x95\x89\x8d\xb6\x16\x0c\x13\x3a\xa4\x9f\xb1\xf1\xc0\xa3\x9d\xb4\x1e\x0d\x0d\x27\x99\x8a\x8c\xa7\x1f\x12\x19\x37\xbc\xc8\x3c\x3c\xaf\x97\x91\xa2\x21\x0b\x0a\x1c\xa2\x8d\x8e\xa2\x2f\x1e\x28\x4c\x5d\x2c\x1e\x25\xb0\x90\x8c\x98\x2c\x0c\x0a\x18\xb4\x94\x94\xa6\x4d\x3a\xd4\xb8\x4f\x1d\x11\x18\xc5\x8f\x89\x91\x4d\x10\x0c\x17\xec\x9f\xa0\xb8\xff\xba\xa1\x9f\xd3\x19\x0c\x0f\x3f\x92\x89\x8f\xbb\x19\x11\x1c\x48\xb8\xce\x3b\x4a\xa8\x95\x93\xaf\x19\x0a\x0c\x29\x99\x8c\x8f\xad\x27\x1d\x2b\x59\x4f\x29\x1e\x2d\xa5\x8e\x8d\x9f\x1e\x0b\x0b\x1e\xa4\x91\x96\xad\x3e\x3b\xcc\xbc\x3a\x1a\x12\x1e\xaa\x8d\x8a\x98\x2b")); snd($sock_b, $port_a, rtp(0, 4006, 8960, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4006, 8960, $ssrc, "\x0e\x0c\x1d\xb8\x9d\xa2\xbe\xf9\xb4\xa0\xa3\x3f\x14\x0c\x14\xbd\x8e\x89\x93\x49\x15\x12\x1f\xe7\xb5\xd9\x3c\x7c\xa1\x93\x97\xd5\x13\x09\x0e\x45\x93\x8b\x93\xc4\x20\x1d\x2e\x6b\x46\x26\x1f\x3d\x9d\x8d\x8e\xae\x17\x09\x0d\x2c\x9c\x90\x98\xba\x36\x3d\xc7\xc4\x2e\x17\x13\x27\x9e\x8b\x8b\x9f\x1e\x0c\x0e\x25\xaa\x9c\xa5\xc8\xe8\xae\xa0\xaa\x2d\x10\x0c\x1b\xa6\x8c\x8a\x9a\x2c\x12\x13\x27\xc3\xb3\xed\x3e\xc8\x9d\x93\x9b\x38\x0f\x09\x13\xba\x8f\x8b\x98\x4a\x1d\x1e\x34\xf9\x3e\x24\x23\xea\x98\x8c\x92\xdf\x10\x09\x0f\x4d\x97\x90\x9c\xd2\x31\x3f\xc5\xd6\x28\x16\x16\x39\x97\x8a\x8d\xaf\x17\x0b\x10\x32\xa2\x9b\xa8\xd6\xd9\xac\xa1\xb3\x22\x0e\x0e")); snd($sock_b, $port_a, rtp(0, 4007, 9120, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4007, 9120, $ssrc, "\x24\x9b\x8a\x8b\xa2\x1f\x10\x15\x2f\xb8\xb4\x68\x43\xb8\x9a\x94\xa1\x25\x0c\x0a\x1a\xa5\x8d\x8c\x9e\x30\x1b\x1f\x3c\xee\x38\x23\x28\xb8\x93\x8d\x97\x31\x0d\x09\x15\xb9\x93\x90\xa0\x4f\x2f\x46\xc4\x5e\x21\x15\x19\xd7\x91\x89\x90\x7b\x10\x0b\x14\x5b\x9d\x9c\xad\xed\xcd\xa9\xa3\xca\x1c\x0d\x10\x38\x94\x89\x8e\xb3\x19\x0f\x18\x3e\xb0\xb5\x59\x4d\xae\x98\x95\xad\x1c\x0b\x0c\x25\x9b\x8b\x8e\xa9\x26\x1a\x22\x46\xf5\x33\x23\x2e\xaa\x90\x8d\x9e\x21\x0b\x0a\x1c\xa6\x90\x92\xa8\x3b\x2e\x4d\xc7\x43\x1e\x15\x1e\xaf\x8e\x8a\x96\x2e\x0e\x0b\x1a\xbb\x9b\x9d\xb2\x68\xc5\xa8\xa7\x4c\x17\x0d\x14\xcb\x8f\x89\x91\x5e\x14\x0f\x1c\x6e\xad\xb8\x52\x68\xa8")); snd($sock_b, $port_a, rtp(0, 4008, 9280, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4008, 9280, $ssrc, "\x97\x98\xc7\x16\x0a\x0e\x3a\x94\x8a\x90\xbb\x1e\x1a\x27\x56\x6f\x2f\x25\x3b\xa0\x8e\x8f\xaa\x19\x09\x0c\x28\x9c\x8f\x95\xb2\x31\x2e\x59\xcc\x37\x1b\x16\x26\xa1\x8c\x8b\x9d\x1f\x0c\x0c\x20\xab\x99\x9e\xbb\x5d\xbe\xa7\xac\x32\x13\x0d\x1a\xab\x8c\x89\x97\x2e\x10\x10\x21\xc3\xab\xbc\x4f\xd4\xa2\x96\x9c\x3f\x10\x0a\x12\xc4\x8f\x8a\x95\x57\x1b\x1a\x2b\xfd\x5d\x2d\x27\x62\x9b\x8e\x92\xc9\x12\x09\x0e\x3f\x97\x8e\x98\xc6\x2c\x2f\x6b\xd9\x2e\x1a\x18\x34\x9a\x8b\x8d\xab\x18\x0a\x0e\x2d\xa1\x98\xa1\xc7\x5b\xb9\xa7\xb4\x27\x10\x0e\x22\x9d\x8a\x8b\x9f\x20\x0e\x12\x2a\xb4\xaa\xc0\x50\xc0\x9e\x97\xa1\x2a\x0e\x0a\x19\xa8\x8c\x8b\x9b\x31\x18\x1b\x31")); snd($sock_b, $port_a, rtp(0, 4009, 9440, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4009, 9440, $ssrc, "\xda\x50\x2c\x2b\xc0\x97\x8e\x97\x39\x0e\x09\x13\xbf\x92\x8e\x9c\x57\x29\x31\xef\x72\x28\x19\x1b\x6d\x94\x8a\x8f\xce\x11\x0a\x11\x48\x9c\x98\xa5\xdc\x5e\xb5\xa9\xc6\x1f\x0f\x10\x31\x96\x89\x8d\xad\x19\x0e\x15\x37\xac\xaa\xc8\x57\xb7\x9c\x98\xac\x1e\x0c\x0c\x21\x9c\x8b\x8d\xa4\x25\x17\x1d\x3b\xcf\x48\x2b\x30\xae\x93\x8e" . "\xff" x 80)); # pause snd($sock_b, $port_a, rtp(0, 4010, 9600, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4010, 9600, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(0, 4011, 9760, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4011, 9760, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(0, 4012, 9920, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4012, 9920, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(0, 4013, 10080, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4013, 10080, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(0, 4014, 10240, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x00" x 80)); # transcoding, no RFC payload type present ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6018)], [qw(198.51.100.3 6020)]); ($port_a) = offer('transcoding, no RFC payload type present', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'], codec => { transcode => ['PCMA'] } }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 120 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1002, 3320, $ssrc, "\xd5\xb9\xbe\x97\x05\x70\xea\x01\x3e\x31\x82\xa5\xb2\x63\x0f\x69\xc1\x0f\x3d\x06\xb3\xa4\x8a\x03\x35\x14\x75\x0e\x36\xcc\xb8\xa5\x9d\x36\x36\x68\x49\x0d\x0c\x81\xa5\xbf\x16\x3f\x37\x4f\xcf\x07\x13\xb4\xa5\xb4\x0a\x3b\x0b\xeb\xe9\x12\xc9\xb3\xb8\x92\x3c\x3a\x07\x87\x9c\x61\x93\xb2\xb3\x12\x25\x39\x76\x8b\x85\x5a\x85\xb3\x8e\x35\x24\x30\x85\xb1\x87\x57\x84\xb7\xeb\x3c\x24\x0d\xb4\xb2\x9b\x70\x98\x8c\x11\x3b\x38\x41\xbf\xb2\xeb\x15\x96\x9f\x0d\x3a\x30\x83\xba\xb1\x7b\x1b\xfa\xf2\x34\x39\x03\xb0\xa5\x88\x04\x03\x5f\x67\x37\x32\xdd\xb8\xba\x9d\x35\x0e\x71\x15\x37\x0a\x80\xa4\xbf\x15\x33\x09\x45\x15\x0b\x18\xb6\xa4\xb4\x08\x3f\x0d\xe5\x66")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1003, 3480, $ssrc, "\x00\xcd\xbc\xba\x9c\x3d\x39\x1a\x9d\xd1\x1d\x98\xbe\xbd\x10\x3a\x3f\x73\x80\xe0\x64\x82\xbf\x8b\x35\x24\x31\x9f\x8b\x94\xdf\x8e\xb3\x96\x3c\x24\x02\x8b\xb7\x94\xf4\x8f\xb5\x10\x3a\x3b\x76\xb2\xb6\xe0\xd6\x80\x87\x09\x25\x33\x81\xb9\xb4\x74\x64\x9b\xe6\x31\x3a\x0d\xb1\xba\x8f\x1c\x11\x95\x6f\x32\x3f\x5e\xb8\xbb\x92\x0d\x1a\xf0\x19\x32\x37\x83\xa4\xbc\x6d\x37\x07\xd4\x04\x31\x07\xb1\xa4\xb4\x0c\x33\x04\xc5\x05\x0b\xd8\xbe\xa5\x9e\x30\x3d\x1d\xe0\x1d\x06\x84\xbb\xbf\x16\x38\x33\x73\x92\x6f\x15\x88\xbb\xb5\x35\x25\x37\x91\x86\x46\xda\xb7\xbf\x92\x3c\x25\x03\x8d\x8c\xf4\xef\xb7\xb6\x10\x25\x3b\x7f\xb6\x89\xf6\x95\xb5\x82\x0b\x24\x33\x84")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1004, 3640, $ssrc, "\xbd\x8e\x5a\xec\x8c\xee\x33\x24\x0c\xb6\xbe\x80\x6b\xf5\x85\x6a\x3f\x39\x4a\xbe\xbe\x90\x05\x7f\x95\x06\x3e\x31\x80\xa5\xbd\x64\x0f\x6b\xcc\x0c\x3d\x00\xb0\xa4\xb5\x00\x34\x16\x4e\x0e\x36\x57\xb9\xa5\x99\x36\x36\x6a\x43\x0d\x0f\x86\xa5\xbe\x15\x3f\x36\x77\xf5\x07\x12\xb4\xa5\xb4\x0b\x3b\x0a\xee\xeb\x13\xd8\xb0\xb8\x9f\x3c\x3a\x01\x87\x9f\x66\x91\xb2\xb3\x11\x25\x39\x7a\x8b\x84\x5b\x9a\xb0\x89\x0a\x24\x33\x9b\xb1\x87\x54\x85\xb7\x97\x3d\x24\x0c\xb4\xb2\x9a\x73\x99\x8c\x14\x38\x3b\x7c\xbc\xbd\x94\x15\x97\x9e\x02\x3a\x33\x81\xba\xb0\x73\x1a\xfe\xf9\x35\x39\x02\xb1\xa4\x8a\x05\x03\x44\x7a\x37\x32\x40\xb8\xa5\x99\x0a\x0e\x72\x6b\x34\x35")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1005, 3800, $ssrc, "\x81\xa4\xbe\x6c\x33\x08\x43\x68\x08\x1a\xb7\xa4\xb7\x0e\x3f\x0c\xfb\x65\x00\xd1\xbd\xba\x98\x32\x39\x04\x92\xdb\x1d\x9e\xbe\xbc\x17\x3a\x3f\x65\x80\xed\x67\x83\xbf\xb5\x0a\x24\x30\x9d\x8b\x97\xd0\x8f\xb3\x93\x3c\x24\x0c\x88\xb7\x96\xc9\x8c\xb5\x17\x3a\x3a\x64\xb3\xb6\xed\x56\x80\x86\x0f\x25\x32\x87\xb9\xb7\x4d\x66\x98\xe3\x36\x3a\x0c\xb1\xba\x8e\x1d\x10\xea\x63\x33\x3f\x70\xb9\xbb\x9f\x0d\x05\xf1\x1f\x33\x36\x81\xa4\xbf\x67\x34\x06\xd5\x05\x31\x06\xb6\xa4\xb7\x0d\x33\x07\xc5\x1a\x0a\x5f\xbe\xa5\x9a\x30\x3d\x1f\xe0\x12\x06\x9a\xbb\xbf\x6b\x39\x32\x7b\x9d\x62\x14\x89\xbb\xb4\x0b\x25\x36\x97\x86\x5e\xd1\xb4\xbf\x9e\x3c\x24\x0d\x82\x8c")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1006, 3960, $ssrc, "\xf0\xe2\xb7\xb1\x14\x3a\x3b\x61\xb6\x88\xf3\xeb\xb5\x8d\x09\x24\x32\x85\xbd\x89\x5c\xe2\x8c\x95\x30\x24\x0e\xb7\xb9\x83\x68\xc3\x85\x6e\x3f\x38\x7a\xbe\xb9\x92\x05\x7a\x95\x07\x3e\x30\x86\xa5\xbd\x7c\x0f\x15\xcb\x0d\x3d\x03\xb1\xa4\xb4\x01\x34\x11\x40\x0f\x36\x48\xb9\xa5\x85\x37\x36\x14\x45\x02\x0f\x84\xa5\xbe\x6d\x3c\x36\x7d\xf1\x04\x1c\xb5\xa5\xb7\x09\x3b\x35\xed\xea\x13\x57\xb0\xb8\x9b\x3d\x3a\x00\x84\x9e\x66\x97\xb2\xb2\x15\x3a\x38\x60\x8b\x87\x58\x98\xb0\x88\x08\x24\x32\x9e\xb1\x86\x54\x9a\xb7\x90\x32\x24\x0e\xb5\xb2\x84\x73\x9f\x8c\x68\x38\x3b\x61\xbc\xbd\x96\x14\x94\x99\x03\x3b\x32\x87\xba\xb3\x48\x1a\xf2\xe5\x0a\x39\x0c\xb1")); snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1007, 4120, $ssrc, "\xa4\xb5\x1a\x02\x4c\x7f\x37\x32\x7c\xb9\xa5\x9a\x0a\x09\x7e\x6e\x34\x35\x87\xa5\xbe\x67\x33\x0b\x48\x6e\x08\x05\xb7\xa4\xb6\x0f\x3f\x0e\xfe\x79\x00\x5a\xbd\xa5\x85\x32\x39\x07\x92\xcd\x1d\x9d\xbe\xbc\x69\x3b\x3e\x60\x80\xef\x66\x80\xbf\xb5\x08\x24\x30\x90\x8b\x91\xd5\x8c\xb3\x9f\x3d\x24\x0e\x89\xb7\x91\xc2\x8c\xb5\x68\x3b\x3a\x6d\xb3\xb1\xee\x5c\x81\x81\x0c\x25\x3d\x85\xb9\xb7\x58\x60\x99\xef\x37\x3a\x0e\xb6\xba\x89\x12\x13\xeb\x67\x33\x3e\x67\xb9\xba\x98\x02\x05\xf7\x1d\x33\x36\x87\xa4\xbe\x7c\x34\x01\x54\x1a\x31\x01\xb6\xa4\xb6\x03\x33\x06\xda\x18\x0a\x75\xbf\xa5\x84\x31\x3d\x19\xe0\x10\x01\x99\xbb\xbe\x62\x39\x3d\x66\x9d\x60\x17")); # pause snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1008, 4280, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1009, 4440, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1010, 4600, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1011, 4760, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1012, 4920, $ssrc, "\xd5" x 160)); snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(8, 1013, 5080, $ssrc, "\x2a" x 160)); snd($sock_b, $port_a, rtp(8, 4000, 8000, 0x6543, "\x2a" x 160)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 8000, -1, "\x00" x 160)); snd($sock_b, $port_a, rtp(8, 4001, 8160, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4001, 8160, $ssrc, "\x00" x 160)); $resp = rtpe_req('play DTMF', 'inject DTMF towards A', { 'from-tag' => tt(), code => '4', volume => 3, duration => 150 }); snd($sock_b, $port_a, rtp(8, 4002, 8320, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 8320, $ssrc, "\xff\x90\x8a\x93\xd9\x1b\x18\x27\x65\xe5\x33\x29\x4c\x9e\x8f\x91\xb8\x15\x09\x0d\x32\x98\x8e\x96\xbb\x2c\x2b\x4c\xd8\x34\x1c\x18\x2e\x9d\x8c\x8c\xa5\x1a\x0b\x0d\x27\xa3\x97\x9e\xbd\x4f\xc4\xaa\xb2\x2c\x12\x0e\x1e\xa1\x8b\x8a\x9c\x25\x0e\x10\x25\xb7\xa7\xb7\x5e\xcb\xa2\x98\x9f\x30\x0f\x0a\x16\xae\x8d\x8a\x98\x3a\x18\x19\x2c\xdd\xfd\x30\x2b\xce\x99\x8e\x95\x4c\x0f\x09\x10\xdf\x93\x8e\x9a\xec\x28\x2c\x56\xee\x2d\x1a\x1a\x48\x97\x8b\x8e\xba\x14\x0a\x0f\x39\x9d\x96\xa1\xcd\x4e\xbe\xab\xbe\x23\x10\x10\x2b\x99\x8a\x8c\xa7\x1b\x0d\x12\x2f\xad\xa7\xbc\x5e\xbd\x9f\x99\xa8\x23\x0d\x0b\x1d\x9f\x8b\x8c\x9f\x29\x16\x1b\x34\xcd\x60\x2f\x2f\xb6\x96")); snd($sock_b, $port_a, rtp(8, 4003, 8480, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4003, 8480, $ssrc, "\x8e\x9b\x2b\x0c\x09\x17\xae\x8f\x8e\x9e\x3f\x25\x2e\x65\x5c\x28\x1a\x1e\xc2\x92\x8a\x92\x44\x0f\x0a\x14\xd6\x99\x97\xa6\x7c\x4e\xba\xad\xe5\x1d\x0f\x13\x49\x92\x89\x8e\xbe\x15\x0d\x16\x43\xa8\xa7\xc1\x66\xb5\x9d\x9a\xb6\x1b\x0c\x0d\x2b\x98\x8a\x8d\xab\x1f\x15\x1d\x3f\xc7\x52\x2e\x39\xaa\x93\x8f\xa3\x1e\x0b\x0b\x1e\x9f\x8d\x8f\xa7\x30\x23\x31\x7c\x4a\x24\x1a\x24\xac\x8e\x8b\x99\x28\x0c\x0a\x1a\xb0\x96\x98\xac\x4f\x53\xb7\xaf\x44\x19\x0f\x18\xba\x8e\x89\x93\x3f\x10\x0d\x1a\xd5\xa3\xa8\xca\xf9\xae\x9c\x9d\xec\x16\x0b\x10\x4e\x91\x89\x90\xc6\x1a\x14\x20\x55\xc3\x4a\x2f\x49\xa2\x91\x92\xb2\x17\x09\x0c\x2d\x99\x8d\x92\xb3\x29\x23\x36\xf2")); snd($sock_b, $port_a, rtp(8, 4004, 8640, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4004, 8640, $ssrc, "\x3e\x20\x1b\x2d\xa0\x8d\x8c\xa1\x1c\x0a\x0c\x22\xa3\x94\x9a\xb5\x44\x5c\xb5\xb6\x32\x16\x0f\x1e\xa6\x8c\x8a\x99\x28\x0e\x0e\x20\xb7\xa1\xab\xd4\xdb\xaa\x9c\xa1\x38\x11\x0b\x15\xb5\x8d\x8a\x96\x3f\x16\x15\x26\xdd\xc2\x43\x31\xdf\x9d\x90\x96\x6d\x11\x09\x0f\x5a\x93\x8c\x97\xd2\x23\x23\x3b\xf6\x37\x1f\x1d\x40\x9a\x8c\x8e\xb2\x15\x09\x0e\x31\x9c\x93\x9c\xc2\x3e\x74\xb4\xbf\x29\x14\x11\x29\x9b\x8a\x8b\xa3\x1c\x0d\x0f\x2a\xab\x9f\xad\xe0\xcc\xa6\x9c\xa9\x28\x0e\x0c\x1c\xa2\x8b\x8b\x9c\x2a\x14\x17\x2c\xc6\xc4\x3e\x36\xbd\x99\x90\x9b\x30\x0d\x09\x15\xb3\x8f\x8d\x9b\x42\x1f\x25\x42\x70\x30\x1e\x1f\xcf\x95\x8b\x92\x58\x0f\x09\x12\x6f\x98\x93")); snd($sock_b, $port_a, rtp(8, 4005, 8800, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 8800, $ssrc, "\x9f\xe5\x3b\xe2\xb5\xd9\x21\x12\x14\x3e\x95\x89\x8d\xb6\x16\x0c\x13\x3a\xa4\x9f\xb1\xf1\xc0\xa3\x9d\xb4\x1e\x0d\x0d\x27\x99\x8a\x8c\xa7\x1f\x12\x19\x37\xbc\xc8\x3c\x3c\xaf\x97\x91\xa2\x21\x0b\x0a\x1c\xa2\x8d\x8e\xa2\x2f\x1e\x28\x4c\x5d\x2c\x1e\x25\xb0\x90\x8c\x98\x2c\x0c\x0a\x18\xb4\x94\x94\xa6\x4d\x3a\xd4\xb8\x4f\x1d\x11\x18\xc5\x8f\x89\x91\x4d\x10\x0c\x17\xec\x9f\xa0\xb8\xff\xba\xa1\x9f\xd3\x19\x0c\x0f\x3f\x92\x89\x8f\xbb\x19\x11\x1c\x48\xb8\xce\x3b\x4a\xa8\x95\x93\xaf\x19\x0a\x0c\x29\x99\x8c\x8f\xad\x27\x1d\x2b\x59\x4f\x29\x1e\x2d\xa5\x8e\x8d\x9f\x1e\x0b\x0b\x1e\xa4\x91\x96\xad\x3e\x3b\xcc\xbc\x3a\x1a\x12\x1e\xaa\x8d\x8a\x98\x2b")); snd($sock_b, $port_a, rtp(8, 4006, 8960, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4006, 8960, $ssrc, "\x0e\x0c\x1d\xb8\x9d\xa2\xbe\xf9\xb4\xa0\xa3\x3f\x14\x0c\x14\xbd\x8e\x89\x93\x49\x15\x12\x1f\xe7\xb5\xd9\x3c\x7c\xa1\x93\x97\xd5\x13\x09\x0e\x45\x93\x8b\x93\xc4\x20\x1d\x2e\x6b\x46\x26\x1f\x3d\x9d\x8d\x8e\xae\x17\x09\x0d\x2c\x9c\x90\x98\xba\x36\x3d\xc7\xc4\x2e\x17\x13\x27\x9e\x8b\x8b\x9f\x1e\x0c\x0e\x25\xaa\x9c\xa5\xc8\xe8\xae\xa0\xaa\x2d\x10\x0c\x1b\xa6\x8c\x8a\x9a\x2c\x12\x13\x27\xc3\xb3\xed\x3e\xc8\x9d\x93\x9b\x38\x0f\x09\x13\xba\x8f\x8b\x98\x4a\x1d\x1e\x34\xf9\x3e\x24\x23\xea\x98\x8c\x92\xdf\x10\x09\x0f\x4d\x97\x90\x9c\xd2\x31\x3f\xc5\xd6\x28\x16\x16\x39\x97\x8a\x8d\xaf\x17\x0b\x10\x32\xa2\x9b\xa8\xd6\xd9\xac\xa1\xb3\x22\x0e\x0e")); snd($sock_b, $port_a, rtp(8, 4007, 9120, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4007, 9120, $ssrc, "\x24\x9b\x8a\x8b\xa2\x1f\x10\x15\x2f\xb8\xb4\x68\x43\xb8\x9a\x94\xa1\x25\x0c\x0a\x1a\xa5\x8d\x8c\x9e\x30\x1b\x1f\x3c\xee\x38\x23\x28\xb8\x93\x8d\x97\x31\x0d\x09\x15\xb9\x93\x90\xa0\x4f\x2f\x46\xc4\x5e\x21\x15\x19\xd7\x91\x89\x90\x7b\x10\x0b\x14\x5b\x9d\x9c\xad\xed\xcd\xa9\xa3\xca\x1c\x0d\x10\x38\x94\x89\x8e\xb3\x19\x0f\x18\x3e\xb0\xb5\x59\x4d\xae\x98\x95\xad\x1c\x0b\x0c\x25\x9b\x8b\x8e\xa9\x26\x1a\x22\x46\xf5\x33\x23\x2e\xaa\x90\x8d\x9e\x21\x0b\x0a\x1c\xa6\x90\x92\xa8\x3b\x2e\x4d\xc7\x43\x1e\x15\x1e\xaf\x8e\x8a\x96\x2e\x0e\x0b\x1a\xbb\x9b\x9d\xb2\x68\xc5\xa8\xa7\x4c\x17\x0d\x14\xcb\x8f\x89\x91\x5e\x14\x0f\x1c\x6e\xad\xb8\x52\x68\xa8")); snd($sock_b, $port_a, rtp(8, 4008, 9280, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4008, 9280, $ssrc, "\x97\x98\xc7\x16\x0a\x0e\x3a\x94\x8a\x90\xbb\x1e\x1a\x27\x56\x6f\x2f\x25\x3b\xa0\x8e\x8f\xaa\x19\x09\x0c\x28\x9c\x8f\x95\xb2\x31\x2e\x59\xcc\x37\x1b\x16\x26\xa1\x8c\x8b\x9d\x1f\x0c\x0c\x20\xab\x99\x9e\xbb\x5d\xbe\xa7\xac\x32\x13\x0d\x1a\xab\x8c\x89\x97\x2e\x10\x10\x21\xc3\xab\xbc\x4f\xd4\xa2\x96\x9c\x3f\x10\x0a\x12\xc4\x8f\x8a\x95\x57\x1b\x1a\x2b\xfd\x5d\x2d\x27\x62\x9b\x8e\x92\xc9\x12\x09\x0e\x3f\x97\x8e\x98\xc6\x2c\x2f\x6b\xd9\x2e\x1a\x18\x34\x9a\x8b\x8d\xab\x18\x0a\x0e\x2d\xa1\x98\xa1\xc7\x5b\xb9\xa7\xb4\x27\x10\x0e\x22\x9d\x8a\x8b\x9f\x20\x0e\x12\x2a\xb4\xaa\xc0\x50\xc0\x9e\x97\xa1\x2a\x0e\x0a\x19\xa8\x8c\x8b\x9b\x31\x18\x1b\x31")); snd($sock_b, $port_a, rtp(8, 4009, 9440, 0x6543, "\x2a" x 160)); rcv($sock_a, $port_b, rtpm(0, 4009, 9440, $ssrc, "\xda\x50\x2c\x2b\xc0\x97\x8e\x97\x39\x0e\x09\x13\xbf\x92\x8e\x9c\x57\x29\x31\xef\x72\x28\x19\x1b\x6d\x94\x8a\x8f\xce\x11\x0a\x11\x48\x9c\x98\xa5\xdc\x5e\xb5\xa9\xc6\x1f\x0f\x10\x31\x96\x89\x8d\xad\x19\x0e\x15\x37\xac\xaa\xc8\x57\xb7\x9c\x98\xac\x1e\x0c\x0c\x21\x9c\x8b\x8d\xa4\x25\x17\x1d\x3b\xcf\x48\x2b\x30\xae\x93\x8e" . "\xff" x 80)); # pause snd($sock_b, $port_a, rtp(8, 4010, 9600, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4010, 9600, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(8, 4011, 9760, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4011, 9760, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(8, 4012, 9920, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4012, 9920, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(8, 4013, 10080, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4013, 10080, $ssrc, "\xff" x 160)); snd($sock_b, $port_a, rtp(8, 4014, 10240, 0x6543, "\x00" x 160)); rcv($sock_a, $port_b, rtpm(0, 4014, 10240, $ssrc, "\xff" x 80 . "\x29" x 80)); # multiple consecutive DTMF events ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6024)], [qw(198.51.100.3 6026)]); ($port_a) = offer('multiple consecutive DTMF events', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => 'C', volume => 5, duration => 100 }); $resp = rtpe_req('play DTMF', 'inject DTMF towards B', { 'from-tag' => ft(), code => '4', volume => 5, duration => 100 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1002, 3320, $ssrc, "\xff\x93\x94\xbc\x2e\x56\xbf\x2b\x13\x1b\xa7\x8e\x98\x47\x25\x41\xe2\x24\x16\x2b\x99\x8e\x9f\x28\x1e\x3d\x5b\x23\x1c\xdf\x92\x8f\xb6\x1c\x1c\x40\x5d\x26\x25\xaa\x8f\x95\x3b\x15\x1d\x5e\xde\x2c\x38\x9d\x8f\x9e\x1f\x11\x20\xc0\xc1\x37\xdd\x99\x92\xb7\x15\x10\x2c\xac\xb5\x49\xb8\x97\x99\x37\x0f\x13\x58\xa0\xae\x67\xae\x99\xa4\x1f\x0d\x1a\xae\x9b\xad\x7b\xad\x9d\xbf\x16\x0e\x27\x9d\x98\xb0\x55\xb1\xa6\x3a\x11\x11\x63\x95\x98\xbf\x3e\xbb\xb4\x26\x10\x1a\xa9\x90\x9a\x4e\x30\xce\xd4\x1e\x12\x29\x99\x8e\xa1\x2d\x29\x6d\x4b\x1c\x18\xef\x91\x8f\xb6\x1f\x24\x57\x3e\x1d\x20\xa9\x8e\x95\x3e\x19\x23\x67\x3e\x21\x31\x9c\x8e\x9e\x22\x14\x26\xcd\x4a")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3480, $ssrc, "\x2a\xdf\x96\x90\xb5\x17\x13\x2f\xb6\xf5\x36\xb1\x93\x96\x39\x10\x15\x55\xaa\xc8\x4c\xa7\x95\xa0\x1f\x0e\x1b\xb4\xa1\xbd\xed\xa4\x99\xbb\x15\x0e\x27\xa0\x9d\xbd\xda\xa4\x9f\x39\x10\x11\x58\x98\x9c\xc8\xf9\xa9\xac\x23\x0e\x19\xab\x92\x9e\x59\x4c\xb0\xca\x1b\x10\x27\x9a\x90\xa5\x35\x3a\xbe\x43\x18\x15\x6c\x92\x91\xb7\x26\x30\xd6\x32\x18\x1d\xa9\x8e\x96\x44\x1d\x2d\xfc\x2e\x1b\x2d\x9a\x8d\x9e\x25\x19\x2d\xe7\x2f\x20\xea\x94\x8f\xb3\x19\x17\x36\xc8\x36\x2c\xae\x90\x95\x3b\x12\x18\x55\xb7\x43\x3e\xa1\x91\x9e\x1f\x0f\x1d\xba\xac\x64\xe8\x9d\x95\xb7\x15\x0e\x29\xa6\xa6\xda\xc3\x9d\x9b\x39\x0f\x11\x51\x9c\xa2\xd8\xbe\x9f\xa7\x21\x0e\x18\xad")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1004, 3640, $ssrc, "\x96\xa3\x68\xc4\xa5\xc2\x19\x0e\x26\x9c\x93\xa9\x3f\xdb\xae\x3e\x14\x12\x5b\x93\x93\xb9\x2e\x51\xbe\x2c\x14\x1b\xa9\x8f\x97\x4c\x25\x3f\xde\x25\x16\x2a\x9a\x8e\x9e\x29\x1e\x3b\x5e\x24\x1b\x7b\x92\x8f\xb2\x1c\x1c\x3e\x61\x27\x25\xac\x8f\x94\x3e\x15\x1c\x59\xdb\x2d\x37\x9e\x8f\x9d\x20\x11\x1f\xc2\xbf\x38\xea\x99\x92\xb4\x16\x10\x2b\xad\xb4\x49\xba\x98\x98\x3a\x0f\x12\x4e\xa1\xad\x68\xaf\x99\xa3\x20\x0d\x19\xb0\x9b\xac\x7b\xae\x9d\xbc\x17\x0e\x25\x9e\x98\xaf\x55\xb2\xa6\x3d\x12\x11\x52\x96\x97\xbd\x3e\xbc\xb3\x28\x10\x19\xab\x90\x9a\x54\x2f\xd0\xcf\x1f\x12\x27\x9a\x8e\xa0\x2e\x28\x66\x4e\x1d\x18\x62\x92\x8f\xb2\x20\x23\x53\x3f\x1d\x1f")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1005, 3800, $ssrc, "\xab\x8e\x94\x44\x19\x22\x61\x40\x21\x2f\x9c\x8e\x9d\x23\x14\x25\xce\x4d\x2a\xf7\x96\x8f\xb1\x18\x13\x2e\xb7\xe8\x36\xb3\x94\x96\x3c\x10\x15\x4d\xaa\xc5\x4b\xa8\x95\x9f\x20\x0e\x1a\xb6\xa0\xbc\xf5\xa4\x99\xb8\x16\x0e\x26\xa1\x9d\xbb\xdd\xa5\x9f\x3c\x10\x10\x4c\x99\x9b\xc5\x78\xaa\xac\x24\x0f\x18\xac\x93\x9d\x5f\x4a\xb1\xc7\x1c\x0f\x25\x9b\x90\xa3\x36\x39\xbf\x47\x18\x14\x56\x92\x90\xb4\x27\x2f\xd7\x34\x18\x1c\xab\x8e\x95\x4b\x1d\x2c\xfe\x2f\x1b\x2c\x9b\x8d\x9d\x27\x19\x2c\xe7\x30\x20\x6d\x94\x8f\xaf\x1a\x17\x34\xc8\x37\x2b\xaf\x91\x94\x3f\x12\x18\x4e\xb6\x45\x3d\xa3\x91\x9e\x20\x0f\x1c\xbc\xab\x6c\xf5\x9e\x95\xb3\x16\x0e\x27\xa7\xa5")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1006, 3960, $ssrc, "\xd6\xc6\x9d\x9b\x3d\x0f\x11\x49\x9c\xa1\xd4\xbf\x9f\xa6\x22\x0e\x18\xaf\x96\xa2\x6e\xc6\xa5\xbe\x19\x0e\x24\x9d\x93\xa8\x40\xe1\xae\x42\x15\x12\x4e\x94\x93\xb7\x2e\x4e\xbe\x2d\x14\x1a\xab\x8f\x97\x52\x25\x3e\xdc\x26\x16\x28\x9b\x8e\x9e\x2b\x1e\x3a\x61\x25\x1b\x5d\x93\x8f\xaf\x1d\x1c\x3d\x67\x27\x24\xad\x8f\x93\x45\x15\x1c\x53\xd7\x2d\x35\x9f\x8f\x9c\x22\x11\x1f\xc5\xbe\x38\x7a\x9a\x91\xb0\x17\x10\x29\xad\xb3\x4a\xbc\x98\x98\x3e\x10\x12\x48\xa1\xad\x6a\xb1\x9a\xa1\x21\x0e\x18\xb3\x9b\xab\x7d\xaf\x9d\xb9\x18\x0e\x23\x9f\x97\xae\x55\xb4\xa5\x40\x12\x10\x49\x96\x97\xbb\x3d\xbd\xb2\x29\x10\x18\xac\x90\x99\x5d\x2f\xd4\xcd\x1f\x12\x25\x9b")); # pause snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1007, 4120, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1008, 4280, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1009, 4440, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1010, 4600, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1011, 4760, $ssrc, "\xff" x 160)); # next event snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1012, 4920, $ssrc, "\xff\x96\x8e\x99\xdd\x1f\x1d\x2c\x69\xe9\x39\x2d\x50\xa3\x95\x97\xbd\x1a\x0e\x12\x38\x9d\x93\x9b\xbf\x30\x2f\x4f\xdc\x39\x20\x1d\x33\xa2\x90\x91\xaa\x1f\x0f\x12\x2c\xa9\x9c\xa3\xc2\x55\xc9\xaf\xb8\x30\x18\x14\x24\xa7\x8f\x8e\xa0\x2a\x14\x16\x2a\xbc\xac\xbc\x61\xcf\xa8\x9d\xa6\x36\x15\x0f\x1b\xb4\x92\x8f\x9d\x3e\x1d\x1e\x31\xe0\xfe\x36\x30\xd3\x9e\x94\x9b\x50\x15\x0d\x17\xe3\x99\x93\x9e\xee\x2c\x30\x5b\xf0\x32\x1f\x1f\x4c\x9c\x8f\x94\xbe\x19\x0e\x15\x3d\xa2\x9b\xa7\xd2\x52\xc3\xaf\xc4\x29\x16\x16\x2f\x9e\x8e\x90\xac\x20\x13\x18\x34\xb2\xac\xc0\x61\xc2\xa5\x9d\xad\x29\x12\x10\x23\xa5\x8f\x90\xa5\x2d\x1b\x1f\x39\xd1\x65\x34\x36\xbb\x9b")); snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1013, 5080, $ssrc, "\x94\x9f\x2f\x11\x0e\x1c\xb3\x95\x94\xa4\x45\x2a\x33\x69\x60\x2d\x1e\x23\xc7\x98\x8f\x98\x49\x15\x0e\x1a\xda\x9d\x9c\xab\x7d\x53\xbe\xb1\xe8\x22\x15\x19\x4d\x98\x8d\x94\xc3\x1b\x12\x1b\x48\xac\xac\xc7\x69\xba\xa2\x9f\xbb\x1f\x10\x12\x2f\x9c\x8e\x93\xb0\x25\x1a\x22\x44\xcb\x57\x34\x3d\xae\x99\x96\xa9\x23\x0f\x0f\x24\xa6\x93\x96\xac\x36\x29\x37\x7c\x4e\x29\x1e\x29\xb0\x94\x8f\x9e\x2d\x11\x0f\x1f\xb6\x9b\x9d\xb0\x55\x58\xbc\xb5\x49\x1e\x15\x1d\xbe\x94\x8e\x99\x45\x17\x12\x1f\xd9\xa9\xad\xce\xfa\xb3\xa0\xa2\xef\x1b\x0f\x16\x52\x97\x8e\x96\xcb\x1e\x1a\x26\x59\xc8\x4e\x35\x4d\xa8\x97\x98\xb8\x1c\x0e\x11\x31\x9d\x91\x98\xb9\x2d\x29\x3b\xf5")); snd($sock_a, $port_b, rtp(0, 1014, 5240, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1014, 5240, $ssrc, "\x43\x27\x1f\x32\xa6\x92\x91\xa7\x21\x0f\x10\x28\xa9\x99\x9e\xba\x49\x60\xba\xbb\x38\x1b\x16\x23\xab\x90\x8e\x9e\x2d\x14\x13\x26\xbc\xa7\xaf\xd8\xde\xae\xa0\xa7\x3d\x17\x0f\x1a\xba\x93\x8e\x9b\x44\x1b\x1b\x2b\xe0\xc8\x48\x37\xe4\xa2\x96\x9b\x6f\x17\x0e\x15\x5d\x99\x91\x9c\xd7\x29\x29\x3f\xf8\x3c\x24\x21\x46\x9e\x90\x94\xb8\x1a\x0e\x14\x37\xa1\x99\xa1\xc8\x43\x76\xba\xc5\x2d\x19\x17\x2d\xa0\x8f\x8f\xa8\x21\x11\x16\x2e\xaf\xa6\xb2\xe5\xcf\xab\xa0\xad\x2d\x14\x10\x20\xa8\x90\x8f\xa1\x2e\x19\x1c\x31\xcb\xc9\x44\x3b\xc2\x9e\x96\x9f\x36\x13\x0e\x1a\xb8\x95\x92\xa0\x48\x26\x2a\x48\x73\x36\x23\x25\xd4\x9a\x90\x98\x5c\x15\x0e\x18\x72\x9c\x99")); snd($sock_a, $port_b, rtp(0, 1015, 5400, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1015, 5400, $ssrc, "\xa6\xe8\x3f\xe7\xba\xdd\x27\x18\x1a\x43\x9a\x8e\x93\xbb\x1b\x10\x19\x3e\xaa\xa5\xb7\xf4\xc6\xa9\xa2\xba\x23\x12\x12\x2c\x9e\x8e\x91\xac\x25\x18\x1e\x3c\xc1\xcd\x41\x40\xb5\x9c\x97\xa8\x27\x10\x0f\x21\xa8\x92\x93\xa8\x35\x24\x2c\x50\x61\x30\x23\x2b\xb7\x97\x90\x9d\x31\x11\x0e\x1c\xb9\x9a\x9a\xab\x52\x3f\xd9\xbc\x54\x22\x18\x1d\xca\x96\x8e\x97\x52\x17\x10\x1c\xef\xa5\xa6\xbc\xff\xbe\xa7\xa5\xd8\x1d\x10\x16\x45\x98\x8e\x95\xbf\x1e\x17\x20\x4d\xbc\xd2\x3f\x4e\xad\x9a\x99\xb4\x1e\x0e\x10\x2d\x9e\x90\x96\xb2\x2c\x22\x2f\x5c\x54\x2d\x24\x32\xaa\x94\x91\xa5\x24\x0f\x0f\x24\xaa\x98\x9b\xb2\x43\x3f\xcf\xc0\x3e\x1e\x18\x23\xaf\x92\x8e\x9c\x2f")); snd($sock_a, $port_b, rtp(0, 1016, 5560, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1016, 5560, $ssrc, "\x13\x11\x21\xbd\xa2\xa8\xc3\xfa\xb9\xa6\xa9\x45\x19\x10\x1a\xc2\x94\x8e\x99\x4e\x1a\x18\x26\xeb\xba\xdd\x40\x7d\xa7\x99\x9c\xda\x19\x0e\x14\x4a\x99\x90\x99\xc9\x26\x23\x34\x6d\x4b\x2b\x25\x41\xa1\x92\x94\xb3\x1c\x0e\x12\x30\xa0\x96\x9d\xbe\x3b\x41\xcc\xc9\x34\x1c\x19\x2c\xa3\x8f\x8f\xa5\x23\x10\x13\x2a\xaf\xa0\xaa\xcd\xeb\xb4\xa6\xae\x31\x16\x11\x1f\xab\x90\x8e\x9e\x30\x18\x19\x2c\xc8\xb9\xf0\x43\xcc\xa2\x99\x9f\x3c\x14\x0e\x19\xbe\x95\x90\x9d\x4e\x22\x24\x3a\xfa\x43\x2a\x28\xec\x9d\x91\x98\xe4\x16\x0d\x16\x51\x9c\x96\xa0\xd7\x37\x45\xca\xda\x2c\x1b\x1b\x3d\x9c\x8e\x92\xb4\x1c\x0f\x16\x38\xa8\xa0\xad\xda\xdd\xb0\xa7\xb9\x28\x14\x13")); # pause snd($sock_a, $port_b, rtp(0, 1017, 5720, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1017, 5720, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1018, 5880, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1018, 5880, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1019, 6040, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1019, 6040, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1020, 6200, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1020, 6200, $ssrc, "\xff" x 160)); snd($sock_a, $port_b, rtp(0, 1021, 6360, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1021, 6360, $ssrc, "\xff" x 160)); # resume snd($sock_a, $port_b, rtp(0, 1022, 6520, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1022, 6520, $ssrc, "\x00" x 160)); # RFC payload type present ($sock_a, $sock_b) = new_call([qw(198.51.100.1 6210)], [qw(198.51.100.3 6212)]); ($port_a) = offer('multi- no transcoding, RFC payload type present', { ICE => 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < 'remove', replace => ['origin'], flags => ['inject DTMF'] }, < ft(), code => '0', volume => 10, duration => 100 }); $resp = rtpe_req('play DTMF', 'inject DTMF towards B', { 'from-tag' => ft(), code => '1', volume => 6, duration => 100 }); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3320, $ssrc, "\x00\x0a\x00\xa0")); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1003, 3320, $ssrc, "\x00\x0a\x01\x40")); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1004, 3320, $ssrc, "\x00\x0a\x01\xe0")); snd($sock_a, $port_b, rtp(0, 1005, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1005, 3320, $ssrc, "\x00\x0a\x02\x80")); snd($sock_a, $port_b, rtp(0, 1006, 3960, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1006, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1007, 3320, $ssrc, "\x00\x8a\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1008, 3320, $ssrc, "\x00\x8a\x03\x20")); snd($sock_a, $port_b, rtp(0, 1007, 4120, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1009, 4120, $ssrc, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1008, 4280, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1010, 4280, $ssrc, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1009, 4440, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1011, 4440, $ssrc, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1010, 4600, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1012, 4600, $ssrc, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1011, 4760, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1013, 4760, $ssrc, "\x00" x 160)); snd($sock_a, $port_b, rtp(0, 1012, 4920, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96 | 0x80, 1014, 4920, $ssrc, "\x01\x06\x00\xa0")); snd($sock_a, $port_b, rtp(0, 1013, 5080, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1015, 4920, $ssrc, "\x01\x06\x01\x40")); snd($sock_a, $port_b, rtp(0, 1014, 5240, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1016, 4920, $ssrc, "\x01\x06\x01\xe0")); snd($sock_a, $port_b, rtp(0, 1015, 5400, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1017, 4920, $ssrc, "\x01\x06\x02\x80")); snd($sock_a, $port_b, rtp(0, 1016, 5560, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(96, 1018, 4920, $ssrc, "\x01\x86\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1019, 4920, $ssrc, "\x01\x86\x03\x20")); rcv($sock_b, $port_a, rtpm(96, 1020, 4920, $ssrc, "\x01\x86\x03\x20")); snd($sock_a, $port_b, rtp(0, 1017, 5720, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1021, 5720, $ssrc, "\x00" x 160)); # extmap stripping new_call; offer('strip extmap control', { }, < ['strip extmap'] }, < 'remove' }, < 'remove' }, < 'default' }, < 'default' }, < 'optional' }, < 'remove' }, < 'remove' }, < 'optional' }, < '3.4.5.6', flags => ['full-rtcp-attribute'], ICE => 'optional', }, < 'optional' }, < 'optional' }, < 'force', flags => ['trickle ICE'] }, < 'force' }, < 'optional' }, < 'force' }, < 'optional' }, < 'force' }, < 'force' }, < 'default' }, < 'default' }, < 'default' }, < 'force' }, < 'force' }, < 'remove' }, < 'remove', DTLS => 'off' }, < 'remove' }, < 'remove', DTLS => 'off', 'transport protocol' => 'RTP/AVP' }, < 'remove' }, < 'remove', DTLS => 'off', 'transport protocol' => 'RTP/SAVP' }, < 'remove' }, < 'remove', replace => [qw(origin)], flags => [qw(codec-mask-opus codec-mask-G722 codec-strip-G7221)] }, < 'remove', replace => [qw(origin)] }, < 'remove', replace => [qw(origin)], flags => [qw(codec-mask-opus codec-mask-G722 codec-strip-G7221 always-transcode)] }, < 'remove', replace => [qw(origin)] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] }}, < ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA'] }}, < ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['pcma'] }}, < ['origin'] }, < 'remove', replace => ['origin'] }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2020)], [qw(198.51.100.3 2022)]); offer('media playback, side A', { ICE => 'remove', replace => ['origin'] }, < ['origin'] }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2100)], [qw(198.51.100.3 2102)]); offer('media playback, side A, repeat', { ICE => 'remove', replace => ['origin'] }, < ['origin'] }, < ft(), blob => $wav_file, 'repeat-times' => 2 }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); rcv($sock_a, -1, rtpm(8, $seq + 5, $ts + 160 * 5, $ssrc, $pcma_1)); rcv($sock_a, -1, rtpm(8, $seq + 6, $ts + 160 * 6, $ssrc, $pcma_2)); rcv($sock_a, -1, rtpm(8, $seq + 7, $ts + 160 * 7, $ssrc, $pcma_3)); rcv($sock_a, -1, rtpm(8, $seq + 8, $ts + 160 * 8, $ssrc, $pcma_4)); rcv($sock_a, -1, rtpm(8, $seq + 9, $ts + 160 * 9, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2030)], [qw(198.51.100.3 2032)]); offer('media playback, side B', { ICE => 'remove', replace => ['origin'] }, < ['origin'] }, < tt(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); $resp = rtpe_req('play media', 'restart media playback', { 'from-tag' => tt(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; $ts += 160 * 5; my $old_ts = $ts; (undef, undef, $ts) = rcv($sock_b, -1, rtpm(8 | 0x80, $seq + 5, -1, $ssrc, $pcma_1)); print("ts $ts old $old_ts\n"); SKIP: { skip 'random timestamp too close to margin', 2 if $old_ts < 500 or $old_ts > 4294966795; cmp_ok($ts, '<', $old_ts + 500, 'ts within < range'); cmp_ok($ts, '>', $old_ts - 500, 'ts within > range'); } rcv($sock_b, -1, rtpm(8, $seq + 6, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_b, -1, rtpm(8, $seq + 7, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_b, -1, rtpm(8, $seq + 8, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_b, -1, rtpm(8, $seq + 9, $ts + 160 * 4, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.9 2020)], [qw(198.51.100.9 2022)]); offer('media playback, side A, select by label', { ICE => 'remove', replace => ['origin'], label => 'foobar' }, < ['origin'], label => 'blah' }, < 'foobar', blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.9 2030)], [qw(198.51.100.9 2032)]); offer('media playback, side B, select by label', { ICE => 'remove', replace => ['origin'], label => 'quux' }, < ['origin'], label => 'meh' }, < 'meh', blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 2050)], [qw(198.51.100.3 2052)]); offer('media playback, SRTP', { ICE => 'remove', replace => ['origin'], DTLS => 'off' }, < ['origin'] }, < ft(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; my $srtp_ctx = { cs => $NGCP::Rtpclient::SRTP::crypto_suites{AES_CM_128_HMAC_SHA1_80}, key => 'DVM+BTeYX2UI1LaA9bgXrcBEDBxoItA9/39fSoRF', }; (undef, undef, $seq, $ts, $ssrc) = srtp_rcv($sock_a, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1), $srtp_ctx); srtp_rcv($sock_a, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2), $srtp_ctx); srtp_rcv($sock_a, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3), $srtp_ctx); srtp_rcv($sock_a, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4), $srtp_ctx); srtp_rcv($sock_a, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5), $srtp_ctx); # ptime tests ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3000)], [qw(198.51.100.3 3002)]); ($port_a) = offer('default ptime in/out', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], ptime => 30 }, < 'remove', replace => ['origin'] }, <B: 5x 20 ms packets -> 3x 30 ms snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160)); Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1002, 3320, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1003, 3480, 0x1234, "\x00" x 160)); Time::HiRes::usleep(1000); snd($sock_a, $port_b, rtp(0, 1004, 3640, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: 60 ms packet -> 2x 30 ms # also perform TS and seq reset snd($sock_a, $port_b, rtp(0, 8000, 500000, 0x1234, "\x00" x 480)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1004, 3960, $ssrc, "\x00" x 240)); # B->A: 2x 60 ms packet -> 6x 20 ms snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 480)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4001, 5160, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 5320, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 5480, 0x4567, "\x88" x 480)); rcv($sock_a, $port_b, rtpm(0, 4003, 5480, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4004, 5640, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 5800, $ssrc, "\x88" x 160)); # B->A: 4x 10 ms packet -> 2x 20 ms snd($sock_b, $port_a, rtp(0, 4002, 5960, 0x4567, "\x88" x 80)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4003, 6040, 0x4567, "\x88" x 80)); rcv($sock_a, $port_b, rtpm(0, 4006, 5960, $ssrc, "\x88" x 160)); # out of order packet input snd($sock_b, $port_a, rtp(0, 4005, 6200, 0x4567, "\x88" x 80)); Time::HiRes::usleep(10000); snd($sock_b, $port_a, rtp(0, 4004, 6120, 0x4567, "\x88" x 80)); rcv($sock_a, $port_b, rtpm(0, 4007, 6120, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4006, 6280, 0x4567, "\x88" x 80)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4007, 6360, 0x4567, "\x88" x 80)); rcv($sock_a, $port_b, rtpm(0, 4008, 6280, $ssrc, "\x88" x 160)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3008)], [qw(198.51.100.3 3010)]); ($port_a) = offer('default ptime in, no change, ptime=30 response', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, <B: 20 ms unchanged snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 160)); # A->B: 30 ms unchanged snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1001, 3160, $ssrc, "\x00" x 240)); # B->A: 20 ms unchanged snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 160)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 160)); # B->A: 30 ms unchanged snd($sock_b, $port_a, rtp(0, 4001, 5160, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5160, $ssrc, "\x88" x 240)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3012)], [qw(198.51.100.3 3014)]); ($port_a) = offer('ptime=50 in, change to 30, default response', { ICE => 'remove', replace => ['origin'], ptime => 30 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 2x 50 ms (plus 20 ms left) snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 400)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5400, $ssrc, "\x88" x 400)); # B->A: add another 30 ms for another full 50 ms snd($sock_b, $port_a, rtp(0, 4004, 5960, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4002, 5800, $ssrc, "\x88" x 400)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3016)], [qw(198.51.100.3 3018)]); ($port_a) = offer('ptime=50 in, change to 30, reverse to 50, response 30', { ICE => 'remove', replace => ['origin'], ptime => 30, 'ptime-reverse' => 50 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 2x 50 ms (plus 20 ms left) snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 400)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5400, $ssrc, "\x88" x 400)); # B->A: add another 30 ms for another full 50 ms snd($sock_b, $port_a, rtp(0, 4004, 5960, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4002, 5800, $ssrc, "\x88" x 400)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3012)], [qw(198.51.100.3 3014)]); ($port_a) = offer('ptime=50 in, change to 30, response 30', { ICE => 'remove', replace => ['origin'], ptime => 30 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 2x 50 ms (plus 20 ms left) snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 400)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5400, $ssrc, "\x88" x 400)); # B->A: add another 30 ms for another full 50 ms snd($sock_b, $port_a, rtp(0, 4004, 5960, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4002, 5800, $ssrc, "\x88" x 400)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3016)], [qw(198.51.100.3 3018)]); ($port_a) = offer('ptime=50 in, change to 30, reverse to 50, default response', { ICE => 'remove', replace => ['origin'], ptime => 30, 'ptime-reverse' => 50 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 2x 50 ms (plus 20 ms left) snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 400)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); Time::HiRes::usleep(1000); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5400, $ssrc, "\x88" x 400)); # B->A: add another 30 ms for another full 50 ms snd($sock_b, $port_a, rtp(0, 4004, 5960, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4002, 5800, $ssrc, "\x88" x 400)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3016)], [qw(198.51.100.3 3018)]); ($port_a) = offer('ptime=50 in, change to 30, reverse to 20, default response', { ICE => 'remove', replace => ['origin'], ptime => 30, 'ptime-reverse' => 20 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 6x 20 ms snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5160, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 5320, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4003, 5480, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4004, 5640, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 5800, $ssrc, "\x88" x 160)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3016)], [qw(198.51.100.3 3018)]); ($port_a) = offer('ptime=50 in, change to 30, reverse to 20, response 40', { ICE => 'remove', replace => ['origin'], ptime => 30, 'ptime-reverse' => 20 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 6x 20 ms snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5160, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 5320, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4003, 5480, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4004, 5640, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 5800, $ssrc, "\x88" x 160)); ($sock_a, $sock_b) = new_call([qw(198.51.100.1 3016)], [qw(198.51.100.3 3018)]); ($port_a) = offer('ptime=30 in, no change, reverse to 20, response 40', { ICE => 'remove', replace => ['origin'], 'ptime-reverse' => 20 }, < 'remove', replace => ['origin'] }, <B: 2x 50 ms -> 3x 30 ms (plus 10 ms left) snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 400)); ($ssrc) = rcv($sock_b, $port_a, rtpm(0, 1000, 3000, -1, "\x00" x 240)); snd($sock_a, $port_b, rtp(0, 1001, 3400, 0x1234, "\x00" x 400)); rcv($sock_b, $port_a, rtpm(0, 1001, 3240, $ssrc, "\x00" x 240)); rcv($sock_b, $port_a, rtpm(0, 1002, 3480, $ssrc, "\x00" x 240)); # A->B: add another 20 ms for another full 30 ms snd($sock_a, $port_b, rtp(0, 1002, 3800, 0x1234, "\x00" x 160)); rcv($sock_b, $port_a, rtpm(0, 1003, 3720, $ssrc, "\x00" x 240)); # B->A: 4x 30 ms -> 6x 20 ms snd($sock_b, $port_a, rtp(0, 4000, 5000, 0x4567, "\x88" x 240)); ($ssrc) = rcv($sock_a, $port_b, rtpm(0, 4000, 5000, -1, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4001, 5240, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4001, 5160, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4002, 5320, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4002, 5480, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4003, 5480, $ssrc, "\x88" x 160)); snd($sock_b, $port_a, rtp(0, 4003, 5720, 0x4567, "\x88" x 240)); rcv($sock_a, $port_b, rtpm(0, 4004, 5640, $ssrc, "\x88" x 160)); rcv($sock_a, $port_b, rtpm(0, 4005, 5800, $ssrc, "\x88" x 160)); # gh #730 ($sock_a, $sock_b) = new_call([qw(198.51.100.1 7300)], [qw(198.51.100.3 7302)]); ($port_a) = offer('gh 730', { ICE => 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'] }, < 'remove', replace => ['origin'], flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin'], flags => [ "loop-protect", "asymmetric" ] }, < tt(), ICE => 'remove', replace => ['origin'], flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin'], flags => [ "loop-protect", "asymmetric" ] }, < 'remove', replace => ['origin'], codec => { transcode => ['telephone-event'] }}, < ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA', 'telephone-event'] }}, < ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['telephone-event'] } }, < 'remove', replace => ['origin'], codec => { transcode => ['opus'] } }, < 'remove', replace => ['origin'], codec => { strip => ['PCMA'] } }, < 'remove', replace => ['origin'], codec => { transcode => ['opus'], mask => ['PCMA'] } }, < 'remove', replace => ['origin'], codec => { strip => [qw/PCMU RED CN G729/]} }, < 'remove', replace => ['origin'], flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], codec => { transcode => ['PCMA', 'telephone-event/8000'] }, flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], flags => ['strict-source'], ptime => 20, 'ptime-reverse' => 60, 'rtcp-mux' => ['demux'], }, < 'remove', 'via-branch' => 'x123', 'rtcp-mux' => ['demux'] }, < 'remove', 'via-branch' => 'x456', 'rtcp-mux' => ['offer'] }, < 'remove', 'via-branch' => 'x123' }, < 'remove', 'via-branch' => 'x123', 'rtcp-mux' => ['demux'] }, < 'remove', 'via-branch' => 'x456', 'rtcp-mux' => ['offer'] }, < 'remove', 'via-branch' => 'x456' }, < 'remove', replace => ['origin'], 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx', flags => ['strict-source', 'record-call'], 'transport-protocol' => 'RTP/AVP' }, < ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < ft() }); # new to-tag new_tt(); offer('media playback after delete', { ICE => 'remove', replace => ['origin'], 'transport-protocol' => 'transparent', flags => ['strict-source', 'record-call'], 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < ['origin'], 'transport-protocol' => 'RTP/AVP', 'rtcp-mux' => ['demux'], 'via-branch' => 'xxxx' }, < tt(), 'to-tag' => tt(), blob => $wav_file }); is $resp->{duration}, 100, 'media duration'; (undef, undef, $seq, $ts, $ssrc) = rcv($sock_b, -1, rtpm(8 | 0x80, -1, -1, -1, $pcma_1)); rcv($sock_b, -1, rtpm(8, $seq + 1, $ts + 160 * 1, $ssrc, $pcma_2)); rcv($sock_b, -1, rtpm(8, $seq + 2, $ts + 160 * 2, $ssrc, $pcma_3)); rcv($sock_b, -1, rtpm(8, $seq + 3, $ts + 160 * 3, $ssrc, $pcma_4)); rcv($sock_b, -1, rtpm(8, $seq + 4, $ts + 160 * 4, $ssrc, $pcma_5)); # GH 1042 $resp = rtpe_req('statistics', 'statistics'); new_call; offer('GH #1461', { }, < 'off' }, < 'off' }, < 'remove', "transport-protocol" => "RTP/AVP"}, < { strip => ['PCMA'] } }, < 'RTP/AVP' }, < 'RTP/AVP' }, < 'RTP/AVP' }, < ['WebRTC'] }, < ft(), SDP => "v=0\no=- 1545997027 1 IN IP4 198.51.101.40\ns=tester\nt=0 0\nm=audio 3000 RTP/AVP 0 8\nc=IN IP4 198.51.100.1\na=foobar\n" } ); like($resp->{sdp}, qr/\r\na=foobar\r\na=sendrecv\r\na=rtcp:\d+\r\n$/s, 'SDP matches'); new_call; $resp = rtpe_req('offer', 'non-terminated SDP', { 'from-tag' => ft(), SDP => "v=0\r\no=- 1545997027 1 IN IP4 198.51.101.40\r\ns=tester\r\nt=0 0\r\nm=audio 3000 RTP/AVP 0 8\r\nc=IN IP4 198.51.100.1\r\na=foobar" } ); like($resp->{sdp}, qr/\r\na=foobar\r\na=sendrecv\r\na=rtcp:\d+\r\n$/s, 'SDP matches'); new_call; $resp = rtpe_req('offer', 'blank line in SDP', { 'from-tag' => ft(), SDP => "v=0\r\no=- 1545997027 1 IN IP4 198.51.101.40\r\ns=tester\r\nt=0 0\r\nm=audio 3000 RTP/AVP 0 8\r\nc=IN IP4 198.51.100.1\r\na=foobar\r\n\r\na=quux\r\n" } ); like($resp->{sdp}, qr/\r\na=foobar\r\na=sendrecv\r\na=rtcp:\d+\r\n$/s, 'SDP matches'); new_call; offer('allow-no-codec-media control', { codec => { strip => ['all'], except => ['PCMA'] }, }, < { strip => ['all'], except => ['PCMA'] }, flags => ['allow no codec media'], }, < { transcode => ['PCMU'] } }, < { transcode => ['PCMU'], strip => ['PCMA'] } }, < { transcode => ['PCMU'], ignore => ['PCMA'] } }, < { transcode => ['PCMU'], ignore => ['PCMA'] } }, <