TT#136957 DTMF delay support

Change-Id: I62dce272a1acc0a9a3b6b8c37df68a18357dbee4
pull/1430/head
Richard Fuchs 4 years ago
parent 1ef4eeba80
commit 3582b623a9

@ -1587,6 +1587,14 @@ Optionally included keys are:
setting this value to zero. The delay buffer setting is honoured in all
messages that set up codec handlers, such as `block DTMF`.
* `DTMF-delay`
Time in milliseconds to delay DTMF events (both RFC event packets and
DTMF tones) for. With this option enabled (set to non-zero), DTMF
events are initially replaced by silence and then subsequently
reproduced after the given delay. DTMF blocking modes are honoured at
the time when the DTMF events are reproduced.
An example of a complete `offer` request dictionary could be (SDP body abbreviated):
{ "command": "offer", "call-id": "cfBXzDSZqhYNcXM", "from-tag": "mS9rSAn0Cr",

@ -1378,6 +1378,12 @@ static void call_ng_main_flags(struct sdp_ng_flags *out, str *key, bencode_item_
case CSH_LOOKUP("end trigger digits"):
out->trigger_end_digits = bencode_get_integer_str(value, out->trigger_end_digits);
break;
case CSH_LOOKUP("DTMF-delay"):
case CSH_LOOKUP("DTMF delay"):
case CSH_LOOKUP("dtmf-delay"):
case CSH_LOOKUP("dtmf delay"):
out->dtmf_delay = bencode_get_integer_str(value, out->dtmf_delay);
break;
#ifdef WITH_TRANSCODING
case CSH_LOOKUP("T38"):
case CSH_LOOKUP("T.38"):
@ -2440,6 +2446,7 @@ static void call_monologue_set_block_mode(struct call_monologue *ml, struct sdp_
ml->dtmf_trigger_match = 0;
ml->dtmf_trigger_digits = flags->trigger_end_digits;
ml->block_dtmf_trigger_end_ms = flags->trigger_end_ms;
ml->dtmf_delay = flags->dtmf_delay;
codec_update_all_handlers(ml);
}

@ -1014,6 +1014,9 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
bool do_pcm_dtmf_blocking = is_pcm_dtmf_block_mode(dtmf_block_mode);
bool do_dtmf_blocking = is_dtmf_replace_mode(dtmf_block_mode);
if (receiver->monologue->dtmf_delay) // received DTMF must be replaced by silence initially, therefore:
do_pcm_dtmf_blocking = true;
bool do_dtmf_detect = false;
if (receiver->monologue->dtmf_trigger.len)
do_dtmf_detect = true;
@ -1115,6 +1118,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
sink_pt->clock_rate, "telephone-event");
struct rtp_payload_type *sink_cn_pt = __supp_payload_type(supplemental_sinks,
sink_pt->clock_rate, "CN");
struct rtp_payload_type *real_sink_dtmf_pt = NULL; // for DTMF delay
// XXX synthesise missing supp codecs according to codec tracker XXX needed?
@ -1142,6 +1146,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// special mode for DTMF blocking
if (do_pcm_dtmf_blocking) {
real_sink_dtmf_pt = sink_dtmf_pt; // remember for DTMF delay
sink_dtmf_pt = NULL; // always transcode DTMF to PCM
// enable DSP if we expect DTMF to be carried as PCM
@ -1256,6 +1261,11 @@ transcode:;
__make_transcoder(handler, sink_pt, output_transcoders,
sink_dtmf_pt ? sink_dtmf_pt->payload_type : -1,
pcm_dtmf_detect, sink_cn_pt ? sink_cn_pt->payload_type : -1);
// for DTMF delay: we pretend that there is no output DTMF payload type (sink_dtmf_pt == NULL)
// so that DTMF is converted to audio (so it can be replaced with silence). we still want
// to output DTMF event packets when we can though, so we need to remember the DTMF payload
// type here.
handler->real_dtmf_payload_type = real_sink_dtmf_pt ? real_sink_dtmf_pt->payload_type : -1;
__check_dtmf_injector(receiver, sink, handler, output_transcoders);
next:
@ -2206,6 +2216,7 @@ void codec_add_dtmf_event(struct codec_ssrc_handler *ch, int code, int level, ui
ts + ch->first_ts);
// add to queue if we're doing PCM -> DTMF event conversion
// this does not capture events when doing DTMF delay (dtmf_payload_type == -1)
if (ch->handler && ch->handler->dtmf_payload_type != -1) {
struct dtmf_event *ev = g_slice_alloc(sizeof(*ev));
*ev = new_ev;
@ -2514,23 +2525,74 @@ static void delay_frame_manipulate(struct delay_frame *dframe) {
struct call_monologue *ml = media->monologue;
enum block_dtmf_mode mode = dtmf_get_block_mode(dframe->mp.call, ml);
if (mode == BLOCK_DTMF_OFF)
if (mode == BLOCK_DTMF_OFF && media->monologue->dtmf_delay == 0)
return;
mutex_lock(&media->dtmf_lock);
struct dtmf_event *dtmf_recv = is_in_dtmf_event(&media->dtmf_recv, dframe->ts, frame->sample_rate,
media->buffer_delay, media->buffer_delay);
struct dtmf_event *dtmf_send = is_in_dtmf_event(&media->dtmf_send, dframe->ts, frame->sample_rate,
media->buffer_delay, media->buffer_delay);
0, 0);
mutex_unlock(&media->dtmf_lock);
if (!dtmf_send) {
if (!dtmf_recv)
return;
mode = BLOCK_DTMF_SILENCE;
if (mode == BLOCK_DTMF_OFF) {
if (!dtmf_send) {
mode = BLOCK_DTMF_SILENCE;
if (dframe->ch->handler->real_dtmf_payload_type != -1) {
// add end event to queue
if (dframe->ch->dtmf_event.code) {
struct dtmf_event *ev = g_slice_alloc0(sizeof(*ev));
uint64_t ts = dframe->ch->encoder ? dframe->ch->encoder->next_pts
: dframe->ts;
*ev = (struct dtmf_event) { .code = 0, .volume = 0, .ts = ts };
g_queue_push_tail(&dframe->ch->dtmf_events, ev);
}
}
if (!dtmf_recv)
return;
}
else
mode = dtmf_send->block_dtmf;
}
else if (!dtmf_recv)
return;
// XXX this should be used for DTMF injection instead of a separate codec handler
switch (mode) {
case BLOCK_DTMF_OFF:
// DTMF delay mode: play original DTMF
if (dframe->ch->handler->real_dtmf_payload_type != -1) {
// add event to handler queue so the packet can be translated
// to DTMF event packet.
memset(frame->extended_data[0], 0, frame->linesize[0]);
// XXX quite some redundant operations here: first the incoming
// DTMF event is decoded to audio, which is then later (maybe) replaced
// by silence. when the delayed DTMF is reproduced, the frame samples
// are first filled with silence, and then replaced
// by the DTMF event packet in packet_encoded_rtp().
if (dframe->ch->dtmf_event.code != dtmf_send->code) {
// XXX this should be switched to proper state tracking instead
// of using start/stop events
struct dtmf_event *ev = g_slice_alloc0(sizeof(*ev));
uint64_t ts = dframe->ch->encoder ? dframe->ch->encoder->next_pts
: dframe->ts;
*ev = (struct dtmf_event) { .code = dtmf_send->code,
.volume = -1 * dtmf_send->volume,
.ts = ts };
g_queue_push_tail(&dframe->ch->dtmf_events, ev);
}
}
else {
// fill with DTMF PCM
frame_fill_dtmf_samples(frame->format, frame->extended_data[0], dframe->ts,
frame->nb_samples, dtmf_code_from_char(dtmf_send->code),
dtmf_send->volume, frame->sample_rate,
frame->channels);
}
break;
case BLOCK_DTMF_SILENCE:
memset(frame->extended_data[0], 0, frame->linesize[0]);
break;
@ -3335,6 +3397,8 @@ static int packet_encoded_rtp(encoder_t *enc, void *u1, void *u2) {
unsigned int repeats = 0;
int payload_type = -1;
int dtmf_pt = ch->handler->dtmf_payload_type;
if (dtmf_pt == -1)
dtmf_pt = ch->handler->real_dtmf_payload_type;
int is_dtmf = 0;
if (dtmf_pt != -1)
@ -3439,6 +3503,12 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
if (!new_ch->first_ts)
new_ch->first_ts = ch->first_ts;
if (decoder->def->supplemental) {
// supp codecs return bogus timestamps. Adjust the frame's TS to be in
// line with the primary decoder
frame->pts -= new_ch->first_ts;
}
ch = new_ch;
}
@ -3474,7 +3544,7 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
if (mp->media_out)
ch->encoder->codec_options.amr.cmr = mp->media_out->u.amr.cmr;
uint32_t ts = mp->rtp ? ntohl(mp->rtp->timestamp) : frame->pts;
uint32_t ts = frame->pts + ch->first_ts;
__buffer_delay_frame(h->input_handler ? h->input_handler->delay_buffer : h->delay_buffer,
ch, input_func, frame, mp, ts);
frame = NULL; // consumed
@ -3698,12 +3768,6 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet *
packet->rtp = *mp->rtp;
packet->handler = h;
if (h->source_pt.codec_def->dtmf && h->dest_pt.codec_def->dtmf) {
// DTMF scaler
packet->func = packet_dtmf;
packet->dup_func = packet_dtmf_dup;
}
int ret = __handler_func_sequencer(mp, packet);
return ret;

@ -68,7 +68,7 @@ static void dtmf_bencode_and_notify(struct call_media *media, unsigned int event
bencode_dictionary_add_string(data, "source_ip", sockaddr_print_buf(&fsin->address));
bencode_dictionary_add_integer(data, "timestamp", rtpe_now.tv_sec);
bencode_dictionary_add_integer(data, "event", event);
bencode_dictionary_add_integer(data, "duration", ((long long) ntohs(duration) * (1000000LL / clockrate)) / 1000LL);
bencode_dictionary_add_integer(data, "duration", ((long long) duration * (1000000LL / clockrate)) / 1000LL);
bencode_dictionary_add_integer(data, "volume", volume);
bencode_collapse_str(notify, &encoded_data);
@ -113,7 +113,7 @@ static GString *dtmf_json_print(struct call_media *media, unsigned int event, un
(unsigned long) rtpe_now.tv_sec,
sockaddr_print_buf(&fsin->address),
(unsigned int) event,
(ntohs(duration) * (1000000 / clockrate)) / 1000,
(duration * (1000000 / clockrate)) / 1000,
(unsigned int) volume);
return buf;
@ -137,7 +137,8 @@ static void dtmf_end_event(struct call_media *media, unsigned int event, unsigne
g_queue_push_tail(&media->dtmf_recv, ev);
ev = g_slice_alloc0(sizeof(*ev));
*ev = (struct dtmf_event) { .code = 0, .ts = ts, .volume = 0 };
*ev = (struct dtmf_event) { .code = 0, .ts = ts + media->monologue->dtmf_delay * clockrate / 1000,
.volume = 0, .block_dtmf = media->monologue->block_dtmf };
g_queue_push_tail(&media->dtmf_send, ev);
if (!dtmf_do_logging())
@ -284,7 +285,9 @@ static void dtmf_code_event(struct call_media *media, char event, uint64_t ts, i
g_queue_push_tail(&media->dtmf_recv, ev);
ev = g_slice_alloc0(sizeof(*ev));
*ev = (struct dtmf_event) { .code = event, .ts = ts, .volume = volume,
*ev = (struct dtmf_event) { .code = event, .ts = ts + media->monologue->dtmf_delay * clockrate / 1000,
.volume = volume,
.block_dtmf = media->monologue->block_dtmf,
.rand_code = '0' + (ssl_random() % 10) };
g_queue_push_tail(&media->dtmf_send, ev);
@ -292,7 +295,7 @@ static void dtmf_code_event(struct call_media *media, char event, uint64_t ts, i
}
struct dtmf_event *is_in_dtmf_event(GQueue *events, uint32_t ts, int clockrate, unsigned int head,
struct dtmf_event *is_in_dtmf_event(GQueue *events, uint32_t this_ts, int clockrate, unsigned int head,
unsigned int trail)
{
if (!clockrate)
@ -300,8 +303,8 @@ struct dtmf_event *is_in_dtmf_event(GQueue *events, uint32_t ts, int clockrate,
uint32_t cutoff = clockrate * 10;
uint32_t neg = ~(clockrate * 100);
uint32_t start_ts = ts + head * clockrate / 1000;
uint32_t end_ts = ts - trail * clockrate / 1000;
uint32_t start_ts = this_ts + head * clockrate / 1000;
uint32_t end_ts = this_ts - trail * clockrate / 1000;
// go backwards through our list of DTMF events
for (GList *l = events->tail; l; l = l->prev) {
@ -345,16 +348,18 @@ int dtmf_event_packet(struct media_packet *mp, str *payload, int clockrate, uint
return -1;
}
dtmf = (void *) payload->s;
uint16_t duration = ntohs(dtmf->duration);
ilog(LOG_DEBUG, "DTMF event packet: event %u, volume %u, end %u, duration %u",
dtmf->event, dtmf->volume, dtmf->end, ntohs(dtmf->duration));
dtmf->event, dtmf->volume, dtmf->end, duration);
if (!dtmf->end) {
dtmf_code_event(mp->media, dtmf_code_to_char(dtmf->event), ts, clockrate, dtmf->volume);
return 0;
}
dtmf_end_event(mp->media, dtmf->event, dtmf->volume, dtmf->duration, &mp->fsin, clockrate, true, ts);
dtmf_end_event(mp->media, dtmf->event, dtmf->volume, duration,
&mp->fsin, clockrate, true, ts + duration - 1);
return 1;
}
@ -383,7 +388,7 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e
// we don't have a real fsin so just use the stream address
struct packet_stream *ps = media->streams.head->data;
unsigned int duration = cur_event.ts - new_event->ts;
unsigned int duration = new_event->ts - cur_event.ts;
LOCK(&media->dtmf_lock);
@ -399,7 +404,8 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e
new_event->code, new_event->volume, duration);
int code = dtmf_code_from_char(new_event->code); // for validation
if (code != -1)
dtmf_code_event(media, (char) new_event->code, ts, clockrate, new_event->volume);
dtmf_code_event(media, (char) new_event->code, ts, clockrate,
dtmf_volume_from_dsp(new_event->volume));
}
}

@ -477,6 +477,7 @@ struct call_monologue {
int dtmf_trigger_digits;
enum block_dtmf_mode block_dtmf_trigger_end;
unsigned int block_dtmf_trigger_end_ms;
unsigned int dtmf_delay;
unsigned int block_media:1;
unsigned int silence_media:1;

@ -87,6 +87,7 @@ struct sdp_ng_flags {
enum block_dtmf_mode block_dtmf_mode_trigger_end;
int trigger_end_digits;
int trigger_end_ms;
int dtmf_delay;
unsigned int asymmetric:1,
protocol_accept:1,
no_redis_update:1,

@ -36,6 +36,7 @@ struct codec_handler {
struct rtp_payload_type source_pt; // source_pt.payload_type = hashtable index
struct rtp_payload_type dest_pt;
int dtmf_payload_type;
int real_dtmf_payload_type;
int cn_payload_type;
codec_handler_func *func;
unsigned int passthrough:1;

@ -7,6 +7,7 @@
#include <stdbool.h>
#include "str.h"
#include "socket.h"
#include "call.h"
struct media_packet;
@ -20,6 +21,7 @@ struct dtmf_event {
int volume;
uint64_t ts;
int rand_code; // state for random replace mode
enum block_dtmf_mode block_dtmf; // block mode at the time of the event
};
void dtmf_init(void);

@ -2597,6 +2597,7 @@ static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
AVFrame *frame = dtmf_frame_int16_t_mono(frame_ts, num_samples, dtmf->event, dtmf->volume,
dec->in_format.clockrate);
frame->pts += dec->u.dtmf.start_ts;
g_queue_push_tail(out, frame);
dec->u.dtmf.duration = duration;

@ -24,6 +24,541 @@ my ($sock_a, $sock_b, $sock_c, $sock_d, $port_a, $port_b, $ssrc, $ssrc_b, $resp,
($sock_a, $sock_b) = new_call([qw(198.51.100.1 4000)], [qw(198.51.100.1 5000)]);
($port_a) = offer('DTMF delay - RFC', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 4000 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=sendrecv
a=rtpmap:101 telephone-event/8000
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 101
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('DTMF delay - RFC', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 5000 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=sendrecv
a=rtpmap:101 telephone-event/8000
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 101
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
a=rtcp:PORT
SDP
$seq = 0;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# control
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rtpe_req('block DTMF', 'DTMF block',
{ 'from-tag' => ft(), 'trigger' => '##', 'trigger-end' => '#', 'DTMF-security-trigger' => 'silence',
'delay-buffer' => 1, 'DTMF-delay' => 60 });
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\xe0"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
$rseq = $seq;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x02\x80"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x00\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x03\x20"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x01\x40"));
Time::HiRes::usleep(18000); $seq++;
# end
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x03\xc0"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x01\xe0"));
Time::HiRes::usleep(18000); $seq++;
# back to PCM
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x02\x80"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x03\x20"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x03\xc0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x04\x60"));
rcv($sock_b, $port_a, rtpm(101, 1001 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x04\x60"));
rcv($sock_b, $port_a, rtpm(101, 1002 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x04\x60"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
($sock_a, $sock_b) = new_call([qw(198.51.100.1 4004)], [qw(198.51.100.1 5004)]);
($port_a) = offer('DTMF delay - PCM', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 4004 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('DTMF delay - PCM', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 5004 RTP/AVP 0
c=IN IP4 198.51.100.1
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=sendrecv
a=rtcp:PORT
SDP
$seq = 0;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# control
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rtpe_req('block DTMF', 'DTMF block',
{ 'from-tag' => ft(), 'trigger' => '##', 'trigger-end' => '#', 'DTMF-security-trigger' => 'silence',
'delay-buffer' => 1, 'DTMF-delay' => 60 });
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb0\xac\xbc\x4c\x39\x3f\x63\xee\x55\x4a\xf6\xba\xaf\xbc\x45\x2c\x2d\x4b\xba\xaf\xbb\x6e\x48\x53\xf3\x5f\x3f\x3a\x52\xba\xac\xb3\x5e\x2f\x2d\x3e\xc8\xb8\xc0\xe8\x6b\xd7\xcc\x66\x39\x30\x3f\xbf\xac\xae\xd2\x37\x2f\x3c\xe1\xc6\xd2\x77\xdd\xbf\xbb\xdc\x38\x2c\x35\xd1\xae\xad\xc2\x43\x37\x40\x6e\xe7\x58\x4e\xdd\xb8\xb1\xc3\x3d\x2b\x2f\x5e\xb5\xaf\xbe\x59\x44\x51\xfb\x5b\x3f\x3d\x6b\xb6\xac\xb8\x4a\x2d\x2d\x47\xbf\xb6\xc1\xfa\x63\xda\xd1\x57\x37\x32\x49\xba\xab\xb0\xfe\x33\x2f\x40\xd2\xc2\xd1\x7e\xda\xbf\xbe\x73\x35\x2d\x3a\xc4\xac\xae\xcd\x3d\x36\x43\xf6\xdf\x5c\x55\xd2\xb7\xb4\xce\x37\x2b\x32\xdf\xb1\xaf\xc3\x4d\x41\x50\x7e\x59\x40"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb0\xac\xbc\x4c\x39\x3f\x63\xee\x55\x4a\xf6\xba\xaf\xbc\x45\x2c\x2d\x4b\xba\xaf\xbb\x6e\x48\x53\xf3\x5f\x3f\x3a\x52\xba\xac\xb3\x5e\x2f\x2d\x3e\xc8\xb8\xc0\xe8\x6b\xd7\xcc\x66\x39\x30\x3f\xbf\xac\xae\xd2\x37\x2f\x3c\xe1\xc6\xd2\x77\xdd\xbf\xbb\xdc\x38\x2c\x35\xd1\xae\xad\xc2\x43\x37\x40\x6e\xe7\x58\x4e\xdd\xb8\xb1\xc3\x3d\x2b\x2f\x5e\xb5\xaf\xbe\x59\x44\x51\xfb\x5b\x3f\x3d\x6b\xb6\xac\xb8\x4a\x2d\x2d\x47\xbf\xb6\xc1\xfa\x63\xda\xd1\x57\x37\x32\x49\xba\xab\xb0\xfe\x33\x2f\x40\xd2\xc2\xd1\x7e\xda\xbf\xbe\x73\x35\x2d\x3a\xc4\xac\xae\xcd\x3d\x36\x43\xf6\xdf\x5c\x55\xd2\xb7\xb4\xce\x37\x2b\x32\xdf\xb1\xaf\xc3\x4d\x41\x50\x7e\x59\x40"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x40\xe0\xb3\xad\xbd\x3f\x2c\x2f\x54\xbb\xb5\xc4\x6b\x5d\xde\xd9\x4e\x37\x35\x58\xb5\xab\xb4\x52\x2f\x2f\x47\xca\xbf\xd0\xfe\xd8\xc1\xc3\x57\x32\x2e\x40\xbc\xab\xb0\xe0\x39\x35\x46\xe3\xdb\x61\x5d\xcc\xb7\xb7\xe8\x33\x2b\x37\xcb\xae\xb0\xcb\x46\x3f\x50\x7e\x58\x41\x46\xcf\xb1\xae\xc6\x39\x2b\x31\x7d\xb7\xb5\xc8\x5d\x58\xe5\xe1\x4a\x37\x38\xf2\xb1\xab\xba\x44\x2e\x30\x4f\xc3\xbe\xd1\x7d\xd8\xc3\xc9\x4b\x30\x2f\x4c\xb6\xab\xb3\x61\x35\x35\x4b\xd8\xd6\x68\x68\xc8\xb7\xba\x5d\x30\x2c\x3c\xbf\xad\xb1\xd8\x40\x3e\x52\xfb\x58\x44\x4c\xc8\xb0\xb0\xd6\x34\x2b\x35\xd5\xb3\xb5\xcd\x54\x54\xec\xef\x47\x37\x3c\xd3\xaf\xac\xc0\x3c\x2d\x33\x63\xbe"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x40\xe0\xb3\xad\xbd\x3f\x2c\x2f\x54\xbb\xb5\xc4\x6b\x5d\xde\xd9\x4e\x37\x35\x58\xb5\xab\xb4\x52\x2f\x2f\x47\xca\xbf\xd0\xfe\xd8\xc1\xc3\x57\x32\x2e\x40\xbc\xab\xb0\xe0\x39\x35\x46\xe3\xdb\x61\x5d\xcc\xb7\xb7\xe8\x33\x2b\x37\xcb\xae\xb0\xcb\x46\x3f\x50\x7e\x58\x41\x46\xcf\xb1\xae\xc6\x39\x2b\x31\x7d\xb7\xb5\xc8\x5d\x58\xe5\xe1\x4a\x37\x38\xf2\xb1\xab\xba\x44\x2e\x30\x4f\xc3\xbe\xd1\x7d\xd8\xc3\xc9\x4b\x30\x2f\x4c\xb6\xab\xb3\x61\x35\x35\x4b\xd8\xd6\x68\x68\xc8\xb7\xba\x5d\x30\x2c\x3c\xbf\xad\xb1\xd8\x40\x3e\x52\xfb\x58\x44\x4c\xc8\xb0\xb0\xd6\x34\x2b\x35\xd5\xb3\xb5\xcd\x54\x54\xec\xef\x47\x37\x3c\xd3\xaf\xac\xc0\x3c\x2d\x33\x63\xbe"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xbd\xd3\x77\xd9\xc5\xd0\x44\x30\x32\x65\xb2\xab\xb8\x4c\x32\x35\x50\xcf\xd2\x70\x7a\xc6\xb8\xbe\x4c\x2e\x2d\x45\xb9\xac\xb4\xfd\x3c\x3d\x55\xf2\x5a\x47\x56\xc1\xb0\xb4\x71\x30\x2b\x3a\xc7\xb0\xb6\xd7\x4d\x50\xf6\x78\x45\x38\x41\xc7\xae\xae\xcc\x37\x2c\x36\xe5\xbb\xbd\xd7\x6d\xdb\xc9\xdd\x3f\x30\x36\xdc\xae\xab\xbd\x41\x2f\x37\x5d\xcb\xcf\x7b\xef\xc4\xb9\xc6\x42\x2d\x2e\x55\xb4\xac\xb8\x58\x39\x3d\x59\xea\x5c\x4a\x66\xbd\xb0\xb8\x50\x2e\x2c\x40\xbd\xaf\xb8\xe8\x48\x4e\x7d\x6b\x43\x3a\x4a\xbf\xad\xaf\xe4\x32\x2c\x3a\xcf\xb8\xbd\xdc\x66\xde\xcc\xf5\x3c\x30\x3b\xca\xad\xac\xc6\x3b\x2e\x39\x7c\xc6\xcd\xfa\xe7\xc3\xbb\xce\x3c\x2d\x31\xf2"));
# replaced by silence
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xc0\xe9\xfe\xcc\xc6\x66\x35\x2d\x3b\xbe\xa9\xac\xce\x36\x2e\x3b\xe4\xc8\xda\x68\xd7\xbb\xb7\xd6\x35\x29\x31\xd2\xac\xab\xbf\x43\x37\x41\x6e\xfb\x4d\x48\xdb\xb4\xad\xbf\x3b\x29\x2c\x5a\xb3\xae\xbd\x5b\x46\x59\xec\x54\x3b\x38\x63\xb3\xaa\xb4\x4a\x2b\x2c\x44\xbf\xb6\xc2\xf8\x75\xce\xcb\x54\x33\x2e\x44\xb8\xa9\xae\xf0\x31\x2e\x3e\xd4\xc4\xd8\x70\xd2\xbb\xba\x7c\x31\x2a\x37\xc3\xab\xac\xca\x3c\x35\x43\xfa\xef\x4f\x4e\xce\xb2\xaf\xca\x35\x28\x2f\xe3\xaf\xae\xc1\x4e\x43\x57\xf6\x50\x3b\x3c\xe1\xaf\xaa\xba\x3e\x2a\x2d\x50\xba\xb4\xc4\x6e\x6b\xd1\xd1\x4b\x32\x30\x52\xb2\xa8\xb1\x53\x2e\x2e\x45\xca\xc1\xd6\x7a\xcf\xbc\xbe\x56\x2f\x2b\x3d\xba"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xa9\xae\xdb\x38\x34\x46\xe7\xe7\x54\x56\xc8\xb2\xb2\xdf\x30\x28\x33\xca\xad\xae\xc9\x47\x40\x55\xfe\x4f\x3c\x40\xce\xae\xac\xc1\x37\x29\x2f\x70\xb6\xb4\xc8\x5f\x62\xd6\xdb\x46\x32\x34\xfd\xae\xa9\xb6\x44\x2c\x2f\x4d\xc3\xbf\xd6\xfe\xce\xbd\xc3\x49\x2d\x2c\x48\xb4\xa9\xb0\x65\x34\x34\x4a\xdb\xdf\x59\x60\xc3\xb2\xb6\x5e\x2d\x29\x39\xbe\xac\xaf\xd5\x40\x3e\x56\x7d\x4e\x3d\x47\xc4\xad\xad\xd0\x31\x28\x32\xd6\xb2\xb4\xcd\x57\x5c\xdb\xe9\x41\x32\x38\xd2\xad\xaa\xbd\x3b\x2b\x30\x5e\xbe\xbe\xd7\xfe\xce\xbe\xcb\x40\x2d\x2e\x5d\xaf\xa9\xb5\x4c\x30\x34\x4f\xd2\xda\x5e\x74\xc0\xb3\xbb\x4a\x2c\x2a\x41\xb8\xab\xb2\xf2\x3c\x3d\x58\xfe\x4e\x3f\x4f"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
($sock_a, $sock_b) = new_call([qw(198.51.100.1 4008)], [qw(198.51.100.1 5008)]);
($port_a) = offer('DTMF trigger', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 4008 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=sendrecv
a=rtpmap:101 telephone-event/8000
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 101
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('DTMF trigger', { }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 5008 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=sendrecv
a=rtpmap:101 telephone-event/8000
----------------------------------
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 101
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
a=rtcp:PORT
SDP
$seq = 0;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# control
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0"));
Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rtpe_req('block DTMF', 'DTMF block',
{ 'from-tag' => ft(), 'trigger' => '##', 'trigger-end' => '#', 'DTMF-security-trigger' => 'tone',
'delay-buffer' => 1, 'DTMF-delay' => 100 });
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\xe0"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x02\x80"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x03\x20"));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
$rseq = $seq;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x03\xc0"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x00\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x04\x60"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x01\x40"));
Time::HiRes::usleep(18000); $seq++;
# end
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x05\x00"));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x01\xe0"));
Time::HiRes::usleep(18000); $seq++;
# back to PCM
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x02\x80"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x03\x20"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x03\xc0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x04\x60"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x0a\x05\x00"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(101, 1000 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x05\xa0"));
rcv($sock_b, $port_a, rtpm(101, 1001 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x05\xa0"));
rcv($sock_b, $port_a, rtpm(101, 1002 + $seq, 3000 + 160 * $rseq, 0x1234, "\x05\x8a\x05\xa0"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# trigger
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x8a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x0a\x00\xa0"));
# trigger matched, now tone
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x8a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x01\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x01\x8a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x02\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x02\x8a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# end trigger
$sseq = $seq;
snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x0a\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x0b\x8a\x01\x40"));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1002 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
($sock_a, $sock_b) = new_call([qw(198.51.100.1 2000)], [qw(198.51.100.1 3000)]);
($port_a) = offer('PCM DTMF block', { }, <<SDP);
@ -737,6 +1272,9 @@ Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
@ -909,6 +1447,18 @@ $rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35\xff\xb5\xa7\x9f\x9c\x9b\x9c\x9f\xa7\xb5\xff\x35\x27\x1f\x1c\x1b\x1c\x1f\x27\x35"));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# finally back to silence
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160));
@ -1067,6 +1617,9 @@ Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c"));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x2a" x 160));
Time::HiRes::usleep(18000); $seq++;
@ -1242,6 +1795,18 @@ $rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c"));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c\xd5\x9c\x82\xb5\xb6\xb1\xb6\xb5\x82\x9c\xd5\x1c\x02\x35\x36\x31\x36\x35\x02\x1c"));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# finally back to silence
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160));
@ -1397,6 +1962,12 @@ Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xff" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
@ -1577,6 +2148,18 @@ $rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff" x 160));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff" x 160));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# finally back to silence
rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160));
@ -1735,6 +2318,9 @@ Time::HiRes::usleep(18000); $seq++;
# gap
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\xd5" x 160));
Time::HiRes::usleep(18000); $seq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x2a" x 160));
Time::HiRes::usleep(18000); $seq++;
@ -1916,6 +2502,18 @@ $rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5" x 160));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5" x 160));
$rseq++;
snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160));
Time::HiRes::usleep(18000); $seq++;
# finally back to silence
rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160));

Loading…
Cancel
Save