diff --git a/README.md b/README.md index 2a7fe8cde..5037de97b 100644 --- a/README.md +++ b/README.md @@ -1526,7 +1526,8 @@ Optionally included keys are: supported modes are: `silence` which replaces DTMF events with silence audio; `tone` which replaces DTMF events with a single sine wave tone; `random` which replaces DTMF events with random other DTMF events (both - in-band DTMF audio tones and RFC event packets). + in-band DTMF audio tones and RFC event packets); `zero` which is + similar to `random` except that a zero event is always used. * `delay-buffer` diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index b02bfd4ab..b69d634d0 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1338,6 +1338,9 @@ static void call_ng_main_flags(struct sdp_ng_flags *out, str *key, bencode_item_ case CSH_LOOKUP("random"): out->block_dtmf_mode = BLOCK_DTMF_RANDOM; break; + case CSH_LOOKUP("zero"): + out->block_dtmf_mode = BLOCK_DTMF_ZERO; + break; default: ilog(LOG_WARN, "Unknown 'DTMF-security' flag encountered: '" STR_FORMAT "'", STR_FMT(&s)); @@ -2370,7 +2373,7 @@ const char *call_block_dtmf_ng(bencode_item_t *input, bencode_item_t *output) { call->block_dtmf = mode; } - if (is_pcm_dtmf_block_mode(mode) || flags.delay_buffer >= 0) { + if (is_dtmf_replace_mode(mode) || flags.delay_buffer >= 0) { if (monologue) { if (flags.delay_buffer >= 0) { for (GList *l = monologue->medias.head; l; l = l->next) { @@ -2413,7 +2416,7 @@ const char *call_unblock_dtmf_ng(bencode_item_t *input, bencode_item_t *output) STR_FMT_M(&monologue->tag)); enum block_dtmf_mode prev_mode = monologue->block_dtmf; monologue->block_dtmf = BLOCK_DTMF_OFF; - if (is_pcm_dtmf_block_mode(prev_mode) || flags.delay_buffer >= 0) { + if (is_dtmf_replace_mode(prev_mode) || flags.delay_buffer >= 0) { if (flags.delay_buffer >= 0) { for (GList *l = monologue->medias.head; l; l = l->next) { struct call_media *media = l->data; @@ -2428,7 +2431,7 @@ const char *call_unblock_dtmf_ng(bencode_item_t *input, bencode_item_t *output) ilog(LOG_INFO, "Unblocking DTMF (entire call)"); enum block_dtmf_mode prev_mode = call->block_dtmf; call->block_dtmf = BLOCK_DTMF_OFF; - if (flags.all || is_pcm_dtmf_block_mode(prev_mode) || flags.delay_buffer >= 0) { + if (flags.all || is_dtmf_replace_mode(prev_mode) || flags.delay_buffer >= 0) { for (GList *l = call->monologues.head; l; l = l->next) { monologue = l->data; enum block_dtmf_mode prev_ml_mode = BLOCK_DTMF_OFF; @@ -2443,7 +2446,7 @@ const char *call_unblock_dtmf_ng(bencode_item_t *input, bencode_item_t *output) } } monologue->detect_dtmf = flags.detect_dtmf; - if (is_pcm_dtmf_block_mode(prev_ml_mode) || is_pcm_dtmf_block_mode(prev_mode) + if (is_dtmf_replace_mode(prev_ml_mode) || is_dtmf_replace_mode(prev_mode) || flags.delay_buffer >= 0) codec_update_all_handlers(monologue); } diff --git a/daemon/codec.c b/daemon/codec.c index 17545c6be..e7f75f297 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -35,8 +35,8 @@ struct mqtt_timer { typedef void (*raw_input_func_t)(struct media_packet *mp, unsigned int); -static void __buffer_delay_raw(struct delay_buffer *dbuf, - raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t); +static void __buffer_delay_raw(struct delay_buffer *dbuf, struct codec_handler *handler, + raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate); static codec_handler_func handler_func_passthrough; @@ -120,6 +120,11 @@ struct dtx_packet { typedef int (*encoder_input_func_t)(encoder_t *enc, AVFrame *frame, int (*callback)(encoder_t *, void *u1, void *u2), void *u1, void *u2); +typedef int (*packet_input_func_t)(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, + unsigned long ts_delay, + int payload_type, + struct media_packet *mp); struct delay_buffer { struct codec_timer ct; @@ -132,12 +137,17 @@ struct delay_buffer { struct delay_frame { AVFrame *frame; struct media_packet mp; + struct transcode_packet *packet; + unsigned long ts_delay; + int payload_type; unsigned int clockrate; uint32_t ts; encoder_input_func_t encoder_func; raw_input_func_t raw_func; + packet_input_func_t packet_func; struct codec_handler *handler; struct codec_ssrc_handler *ch; + struct codec_ssrc_handler *input_ch; }; struct silence_event { @@ -247,6 +257,13 @@ static void __delay_buffer_setup(struct delay_buffer **dbufp, struct codec_handler *h, struct call *call, unsigned int delay); static void __delay_buffer_shutdown(struct delay_buffer *dbuf, bool); static void delay_buffer_stop(struct delay_buffer **pcmbp); +static int __buffer_delay_packet(struct delay_buffer *dbuf, + struct codec_ssrc_handler *ch, + struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, + unsigned long ts_delay, + int payload_type, + packet_input_func_t packet_func, struct media_packet *mp, unsigned int clockrate); static struct codec_handler codec_handler_stub_ssrc = { @@ -987,7 +1004,12 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, enum block_dtmf_mode dtmf_block_mode = dtmf_get_block_mode(NULL, receiver->monologue); bool do_pcm_dtmf_blocking = is_pcm_dtmf_block_mode(dtmf_block_mode); + bool do_dtmf_blocking = is_dtmf_replace_mode(dtmf_block_mode); + // do we have to force everything through the transcoding engine even if codecs match? + bool force_transcoding = do_pcm_dtmf_blocking || do_dtmf_blocking; + if (sink->monologue->inject_dtmf) + force_transcoding = true; for (GList *l = receiver->codecs.codec_prefs.head; l; ) { struct rtp_payload_type *pt = l->data; @@ -1114,6 +1136,12 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, if (!recv_dtmf_pt) pcm_dtmf_detect = true; } + else if (do_dtmf_blocking) { + // we only need the DSP if there's no DTMF payload present, as otherwise + // we expect DTMF event packets + if (!recv_dtmf_pt) + pcm_dtmf_detect = true; + } if (pcm_dtmf_detect) { if (sink_dtmf_pt) @@ -1132,58 +1160,58 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, // we can now decide whether we can do passthrough, or transcode - // different codecs? + // different codecs? this will only be true for non-supplemental codecs // XXX needs more intelligent fmtp matching if (rtp_payload_type_cmp_nf(pt, sink_pt)) goto transcode; - // different ptime? - if (sink_pt->ptime && pt->ptime && sink_pt->ptime != pt->ptime) { - if (MEDIA_ISSET(sink, PTIME_OVERRIDE) || MEDIA_ISSET(receiver, PTIME_OVERRIDE)) { + // supplemental codecs are always matched up. we want them as passthrough if + // possible. skip checks that are only applicable for real codecs + if (!pt->codec_def->supplemental) { + // different ptime? + if (sink_pt->ptime && pt->ptime && sink_pt->ptime != pt->ptime) { + if (MEDIA_ISSET(sink, PTIME_OVERRIDE) || MEDIA_ISSET(receiver, PTIME_OVERRIDE)) { + ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " + "enabling transcoding", + sink_pt->ptime, pt->ptime); + goto transcode; + } ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " - "enabling transcoding", + "but no override requested", sink_pt->ptime, pt->ptime); - goto transcode; } - ilogs(codec, LOG_DEBUG, "Mismatched ptime between source and sink (%i <> %i), " - "but no override requested", - sink_pt->ptime, pt->ptime); - } - if (sink->monologue->inject_dtmf) { - // we have a matching output codec, but we were told that we might - // want to inject DTMF, so we must still go through our transcoding - // engine, despite input and output codecs being the same. - goto transcode; - } + if (force_transcoding) + goto transcode; - // compare supplemental codecs - // DTMF - if (pcm_dtmf_detect) - goto transcode; - if (recv_dtmf_pt && (recv_dtmf_pt->for_transcoding || do_pcm_dtmf_blocking) && !sink_dtmf_pt) { - ilogs(codec, LOG_DEBUG, "Transcoding DTMF events to PCM from " STR_FORMAT - " to " STR_FORMAT, - STR_FMT(&pt->encoding_with_params), - STR_FMT(&sink_pt->encoding_with_params)); - goto transcode; - } - // CN - if (!recv_cn_pt && sink_cn_pt && sink_cn_pt->for_transcoding) { - ilogs(codec, LOG_DEBUG, "Enabling CN silence detection from " STR_FORMAT - " to " STR_FORMAT - "/" STR_FORMAT, - STR_FMT(&pt->encoding_with_params), - STR_FMT(&sink_pt->encoding_with_params), - STR_FMT(&sink_cn_pt->encoding_with_params)); - goto transcode; - } - if (recv_cn_pt && recv_cn_pt->for_transcoding && !sink_cn_pt) { - ilogs(codec, LOG_DEBUG, "Transcoding CN packets to PCM from " STR_FORMAT - " to " STR_FORMAT, - STR_FMT(&pt->encoding_with_params), - STR_FMT(&sink_pt->encoding_with_params)); - goto transcode; + // compare supplemental codecs + // DTMF + if (pcm_dtmf_detect) + goto transcode; + if (recv_dtmf_pt && (recv_dtmf_pt->for_transcoding || do_pcm_dtmf_blocking) && !sink_dtmf_pt) { + ilogs(codec, LOG_DEBUG, "Transcoding DTMF events to PCM from " STR_FORMAT + " to " STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params)); + goto transcode; + } + // CN + if (!recv_cn_pt && sink_cn_pt && sink_cn_pt->for_transcoding) { + ilogs(codec, LOG_DEBUG, "Enabling CN silence detection from " STR_FORMAT + " to " STR_FORMAT + "/" STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params), + STR_FMT(&sink_cn_pt->encoding_with_params)); + goto transcode; + } + if (recv_cn_pt && recv_cn_pt->for_transcoding && !sink_cn_pt) { + ilogs(codec, LOG_DEBUG, "Transcoding CN packets to PCM from " STR_FORMAT + " to " STR_FORMAT, + STR_FMT(&pt->encoding_with_params), + STR_FMT(&sink_pt->encoding_with_params)); + goto transcode; + } } // everything matches - we can do passthrough @@ -1434,7 +1462,7 @@ static int handler_func_passthrough(struct codec_handler *h, struct media_packet codec_calc_lost(mp->ssrc_in, ntohs(mp->rtp->seq_num)); } - __buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate, ts); + __buffer_delay_raw(h->delay_buffer, h, codec_add_raw_packet, mp, h->source_pt.clock_rate); return 0; } @@ -1529,11 +1557,13 @@ static int __handler_func_sequencer(struct media_packet *mp, struct transcode_pa int seq_ret = packet_sequencer_insert(&ssrc_in_p->sequencer, &packet->p); if (seq_ret < 0) { // dupe + int func_ret = 0; if (packet->dup_func) - packet->dup_func(ch, input_ch, packet, mp); + func_ret = packet->dup_func(ch, input_ch, packet, mp); else ilogs(transcoding, LOG_DEBUG, "Ignoring duplicate RTP packet"); - __transcode_packet_free(packet); + if (func_ret != 1) + __transcode_packet_free(packet); ssrc_in_p->duplicates++; goto out; } @@ -1752,17 +1782,15 @@ static struct codec_ssrc_handler *__output_ssrc_handler(struct codec_ssrc_handle return new_ch; } -// forwards DTMF input to DTMF output, plus rescaling duration -static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, +static int codec_add_dtmf_packet(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, struct transcode_packet *packet, + unsigned long ts_delay, + int payload_type, struct media_packet *mp) { - int payload_type = -1; // take from handler's output config - unsigned long ts_delay = 0; - + struct codec_handler *h = ch->handler; struct codec_ssrc_handler *output_ch = NULL; - // this is actually a DTMF -> PCM handler // grab our underlying PCM transcoder output_ch = __output_ssrc_handler(input_ch, mp); if (G_UNLIKELY(!output_ch->encoder)) @@ -1794,13 +1822,13 @@ static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct codec_ssrc_hand ilogs(transcoding, LOG_DEBUG, "Scaling DTMF packet timestamp and duration: TS %lu -> %lu " "(%u -> %u)", packet->ts, packet_ts, - ch->handler->source_pt.clock_rate, ch->handler->dest_pt.clock_rate); + h->source_pt.clock_rate, h->dest_pt.clock_rate); packet->ts = packet_ts; if (packet->payload->len >= sizeof(struct telephone_event_payload)) { struct telephone_event_payload *dtmf = (void *) packet->payload->s; unsigned int duration = av_rescale(ntohs(dtmf->duration), - ch->handler->dest_pt.clock_rate, ch->handler->source_pt.clock_rate); + h->dest_pt.clock_rate, h->source_pt.clock_rate); dtmf->duration = htons(duration); // we can't directly use the RTP TS to schedule the send, as we have to adjust it @@ -1814,24 +1842,41 @@ static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct codec_ssrc_hand output_ch->encoder->packet_pts += (duration - ch->last_dtmf_event_ts) * output_ch->encoder->def->clockrate_mult; ch->last_dtmf_event_ts = duration; } - payload_type = ch->handler->dtmf_payload_type; + payload_type = h->dtmf_payload_type; skip: if (output_ch) obj_put(&output_ch->h); - char *buf = malloc(packet->payload->len + sizeof(struct rtp_header) + RTP_BUFFER_TAIL_ROOM); memcpy(buf + sizeof(struct rtp_header), packet->payload->s, packet->payload->len); if (packet->bypass_seq) // inject original seq - __output_rtp(mp, ch, packet->handler ? : ch->handler, buf, packet->payload->len, packet->ts, + __output_rtp(mp, ch, packet->handler ? : h, buf, packet->payload->len, packet->ts, packet->marker, packet->p.seq, -1, payload_type, ts_delay); else // use our own sequencing - __output_rtp(mp, ch, packet->handler ? : ch->handler, buf, packet->payload->len, packet->ts, + __output_rtp(mp, ch, packet->handler ? : h, buf, packet->payload->len, packet->ts, packet->marker, -1, 0, payload_type, ts_delay); + mp->ssrc_out->parent->seq_diff++; return 0; } +// forwards DTMF input to DTMF output, plus rescaling duration +// returns 1 if packet has been consumed +static int packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, + struct media_packet *mp) +{ + int payload_type = -1; // take from handler's output config + unsigned long ts_delay = 0; + struct codec_handler *h = ch->handler; + struct codec_handler *input_h = input_ch->handler; + + int ret = __buffer_delay_packet(input_h->delay_buffer, ch, input_ch, packet, ts_delay, payload_type, + codec_add_dtmf_packet, mp, h->source_pt.clock_rate); + mp->ssrc_out->parent->seq_diff--; + return ret; +} + // returns the codec handler for the primary payload type - mostly determined by guessing static struct codec_handler *__input_handler(struct codec_handler *h, struct media_packet *mp) { if (!mp->ssrc_in) @@ -1892,7 +1937,7 @@ static int packet_dtmf(struct codec_ssrc_handler *ch, struct codec_ssrc_handler if (__buffer_dtx(input_ch->dtx_buffer, ch, input_ch, packet, mp, packet_dtmf_fwd)) ret = 1; // consumed else - packet_dtmf_fwd(ch, input_ch, packet, mp); + ret = packet_dtmf_fwd(ch, input_ch, packet, mp); } return ret; @@ -1903,11 +1948,13 @@ static int packet_dtmf_dup(struct codec_ssrc_handler *ch, struct codec_ssrc_hand { enum block_dtmf_mode block_dtmf = dtmf_get_block_mode(mp->call, mp->media->monologue); + int ret = 0; + if (block_dtmf == BLOCK_DTMF_DROP) { } else // pass through - packet_dtmf_fwd(ch, input_ch, packet, mp); - return 0; + ret = packet_dtmf_fwd(ch, input_ch, packet, mp); + return ret; } static int __handler_func_supplemental(struct codec_handler *h, struct media_packet *mp, @@ -2106,7 +2153,7 @@ static int handler_func_passthrough_ssrc(struct codec_handler *h, struct media_p // keep track of other stats here? - __buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate, ts); + __buffer_delay_raw(h->delay_buffer, h, codec_add_raw_packet, mp, h->source_pt.clock_rate); return 0; } @@ -2239,6 +2286,7 @@ static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_ha dframe->encoder_func = input_func; dframe->ts = ts; dframe->ch = obj_get(&ch->h); + dframe->handler = ch->handler; media_packet_copy(&dframe->mp, mp); LOCK(&dbuf->lock); @@ -2248,8 +2296,8 @@ static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_ha } -static void __buffer_delay_raw(struct delay_buffer *dbuf, - raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t ts) +static void __buffer_delay_raw(struct delay_buffer *dbuf, struct codec_handler *handler, + raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate) { if (__buffer_delay_do_direct(dbuf)) { // direct passthrough @@ -2260,6 +2308,7 @@ static void __buffer_delay_raw(struct delay_buffer *dbuf, struct delay_frame *dframe = g_slice_alloc0(sizeof(*dframe)); dframe->raw_func = input_func; dframe->clockrate = clockrate; + dframe->handler = handler; media_packet_copy(&dframe->mp, mp); // also copy packet payload @@ -2273,6 +2322,41 @@ static void __buffer_delay_raw(struct delay_buffer *dbuf, __delay_buffer_schedule(dbuf); } +// returns 1 if packet has been consumed +static int __buffer_delay_packet(struct delay_buffer *dbuf, + struct codec_ssrc_handler *ch, + struct codec_ssrc_handler *input_ch, + struct transcode_packet *packet, + unsigned long ts_delay, + int payload_type, + packet_input_func_t packet_func, struct media_packet *mp, unsigned int clockrate) +{ + if (__buffer_delay_do_direct(dbuf)) { + // direct passthrough + packet_func(ch, input_ch, packet, ts_delay, payload_type, mp); + return 0; + } + + struct delay_frame *dframe = g_slice_alloc0(sizeof(*dframe)); + dframe->packet_func = packet_func; + dframe->clockrate = clockrate; + dframe->ch = ch ? obj_get(&ch->h) : NULL; + dframe->input_ch = input_ch ? obj_get(&input_ch->h) : NULL; + dframe->ts_delay = ts_delay; + dframe->payload_type = payload_type; + dframe->packet = packet; + dframe->ts = packet->ts; + dframe->handler = ch->handler; + media_packet_copy(&dframe->mp, mp); + + LOCK(&dbuf->lock); + g_queue_insert_sorted(&dbuf->frames, dframe, delay_frame_cmp, NULL); + + __delay_buffer_schedule(dbuf); + + return 1; +} + // consumes `packet` if buffered (returns 1) static int __buffer_dtx(struct dtx_buffer *dtxb, struct codec_ssrc_handler *decoder_handler, struct codec_ssrc_handler *input_handler, @@ -2328,6 +2412,10 @@ static void delay_frame_free(struct delay_frame *dframe) { media_packet_release(&dframe->mp); if (dframe->ch) obj_put(&dframe->ch->h); + if (dframe->input_ch) + obj_put(&dframe->input_ch->h); + if (dframe->packet) + __transcode_packet_free(dframe->packet); g_slice_free1(sizeof(*dframe), dframe); } static void delay_frame_send(struct delay_frame *dframe) { @@ -2390,6 +2478,17 @@ static void delay_frame_manipulate(struct delay_frame *dframe) { frame_fill_tone_samples(frame->format, frame->extended_data[0], dframe->ts, frame->nb_samples, 400, 10, frame->sample_rate, frame->channels); break; + case BLOCK_DTMF_ZERO: + // if we have DTMF output, use silence, otherwise use a DTMF zero + if (dframe->ch->handler->dtmf_payload_type != -1) + memset(frame->extended_data[0], 0, frame->linesize[0]); + else + frame_fill_dtmf_samples(frame->format, frame->extended_data[0], + dframe->ts, + frame->nb_samples, 0, + 10, frame->sample_rate, + frame->channels); + break; case BLOCK_DTMF_RANDOM: if (!media->dtmf_event_state) media->dtmf_event_state = '0' + (ssl_random() % 10); @@ -2403,6 +2502,40 @@ static void delay_frame_manipulate(struct delay_frame *dframe) { } } } +static void delay_packet_manipulate(struct delay_frame *dframe) { + struct call_media *media = dframe->mp.media; + if (!media) + return; + if (!dframe->handler) + return; + + struct media_packet *mp = &dframe->mp; + + if (is_in_dtmf_event(media, dframe->ts, dframe->clockrate, media->buffer_delay, media->buffer_delay)) { + // is this a DTMF event packet? + if (!dframe->handler->source_pt.codec_def || !dframe->handler->source_pt.codec_def->dtmf) + return; + + enum block_dtmf_mode mode = dtmf_get_block_mode(dframe->mp.call, media->monologue); + + // this can be a "raw" or "packet" - get the appropriate payload + str *payload = &mp->raw; + if (dframe->packet) + payload = dframe->packet->payload; + + struct telephone_event_payload *dtmf = (void *) payload->s; + if (payload->len < sizeof(*dtmf)) + return; + + switch (mode) { + case BLOCK_DTMF_ZERO: + dtmf->event = 0; + break; + default: + break; + } + } +} static void __delay_frame_process(struct delay_buffer *dbuf, struct delay_frame *dframe) { if (dframe->mp.rtp) { // adjust output seq num. pushing packets into the delay buffer looks as if they @@ -2420,8 +2553,15 @@ static void __delay_frame_process(struct delay_buffer *dbuf, struct delay_frame dframe->encoder_func(csh->encoder, dframe->frame, csh->handler->packet_encoded, csh, &dframe->mp); } - else if (dframe->raw_func) + else if (dframe->raw_func) { + delay_packet_manipulate(dframe); dframe->raw_func(&dframe->mp, dframe->clockrate); + } + else if (dframe->packet_func && dframe->packet) { + delay_packet_manipulate(dframe); + dframe->packet_func(csh, dframe->input_ch, dframe->packet, dframe->ts_delay, + dframe->payload_type, &dframe->mp); + } else ilog(LOG_ERR | LOG_FLAG_LIMIT, "Delay buffer bug"); } @@ -3349,8 +3489,8 @@ out: #else // dummy/stub -static void __buffer_delay_raw(struct delay_buffer *dbuf, - raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t ts) +static void __buffer_delay_raw(struct delay_buffer *dbuf, struct codec_handler *handler, + raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate) { input_func(mp, clockrate); } diff --git a/daemon/dtmf.c b/daemon/dtmf.c index c396dda7e..4b9ef7ef1 100644 --- a/daemon/dtmf.c +++ b/daemon/dtmf.c @@ -503,3 +503,9 @@ bool is_pcm_dtmf_block_mode(enum block_dtmf_mode mode) { return true; return false; } + +bool is_dtmf_replace_mode(enum block_dtmf_mode mode) { + if (mode >= BLOCK_DTMF___REPLACE_START && mode <= BLOCK_DTMF___REPLACE_END) + return true; + return false; +} diff --git a/include/call.h b/include/call.h index c0916d3be..21e421630 100644 --- a/include/call.h +++ b/include/call.h @@ -203,11 +203,16 @@ enum block_dtmf_mode { BLOCK_DTMF_OFF = 0, BLOCK_DTMF_DROP = 1, + BLOCK_DTMF___REPLACE_START = 2, BLOCK_DTMF___PCM_REPLACE_START = 2, + // block modes that replace any DTMF with PCM BLOCK_DTMF_SILENCE = 2, BLOCK_DTMF_TONE = 3, BLOCK_DTMF_RANDOM = 4, BLOCK_DTMF___PCM_REPLACE_END = 4, + // block modes that replace DTMF events with other DTMF events if possible + BLOCK_DTMF_ZERO = 5, + BLOCK_DTMF___REPLACE_END = 5, }; diff --git a/include/dtmf.h b/include/dtmf.h index b487f67f7..f4d61c83e 100644 --- a/include/dtmf.h +++ b/include/dtmf.h @@ -33,6 +33,7 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e struct call_media *media, int clockrate, uint64_t ts); enum block_dtmf_mode dtmf_get_block_mode(struct call *call, struct call_monologue *ml); bool is_pcm_dtmf_block_mode(enum block_dtmf_mode mode); +bool is_dtmf_replace_mode(enum block_dtmf_mode mode); bool is_in_dtmf_event(struct call_media *, uint32_t ts, int clockrate, unsigned int head, unsigned int trail); #endif diff --git a/t/auto-daemon-tests-delay-buffer.pl b/t/auto-daemon-tests-delay-buffer.pl index 948241cef..316bee258 100755 --- a/t/auto-daemon-tests-delay-buffer.pl +++ b/t/auto-daemon-tests-delay-buffer.pl @@ -11,7 +11,7 @@ use POSIX; autotest_start(qw(--config-file=none -t -1 -i 203.0.113.1 - -n 2223 -c 12345 -f -L 7 -E -u 2222 --silence-detect=1)) + -n 2223 -c 12345 -f -L 7 -E -u 2222)) or die; @@ -1285,5 +1285,1279 @@ Time::HiRes::usleep(18000); $seq++; +($sock_a, $sock_b) = new_call([qw(198.51.100.1 2008)], [qw(198.51.100.1 3008)]); + +($port_a) = offer('zero DTMF block w event PT', { }, < ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 1 }); + +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")); +# replaced by zero +rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x00\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, "\x00\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, "\x00\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++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w event PT', + { 'from-tag' => ft() }); + +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, "\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, "\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, "\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, "\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++; + +# enable blocking + +rtpe_req('block DTMF', 'zero DTMF block w event PT', + { 'from-tag' => ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 100 }); + +# pre-send 100 ms worth of audio + +$rseq = $seq; + +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# start receiving + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +# send interleaved with received old PCM + +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, "\x00" 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, "\x00" x 160)); +$rseq++; + +# send some non-silence to make sure we drop it + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +# start DTMF + +$sseq = $seq; + +snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0")); +Time::HiRes::usleep(18000); $seq++; + +# buffered PCM now replaced by silence + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff" x 160)); +$rseq++; + +snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40")); +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(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0")); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\xff" x 160)); +$rseq++; + +# input some non-silence to make sure it's also dropped + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# still silence + +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, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# now the adjusted DTMF is getting through + +$sseq = $rseq; + +rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $rseq, 3000 + 160 * $sseq, 0x1234, "\x00\x0a\x00\xa0")); +$rseq++; + +# continue non-silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(101, 1000 + $rseq, 3000 + 160 * $sseq, 0x1234, "\x00\x0a\x01\x40")); +$rseq++; + +# switch back to silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# old DTMF end event now coming through + +rcv($sock_b, $port_a, rtpm(101, 1000 + $rseq, 3000 + 160 * $sseq, 0x1234, "\x00\x8a\x01\xe0")); +$rseq++; + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# audio still replaced by silence + +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++; + +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)); +$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, "\x00" 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, "\x00" 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, "\x00" x 160)); +$rseq++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w event PT', + { 'from-tag' => ft(), 'delay-buffer' => 0 }); + +# buffer flushing + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +# sync forwarding + +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 2010)], [qw(198.51.100.1 3010)]); + +($port_a) = offer('zero DTMF block w transcoding w event PT', { codec => { transcode => ['PCMA'] } }, < ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 1 }); + +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++; +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++; + +$sseq = $seq; + +snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0")); +# replaced by zero +rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, $ssrc, "\x00\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, $ssrc, "\x00\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, $ssrc, "\x00\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(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x2a" 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++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w transcoding w event PT', + { 'from-tag' => ft() }); + +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; + +# enable blocking + +rtpe_req('block DTMF', 'zero DTMF block w transcoding w event PT', + { 'from-tag' => ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 100 }); + +# pre-send 100 ms worth of audio + +$rseq = $seq; + +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# start receiving + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +# send interleaved with received old PCM + +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, "\x2a" 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, "\x2a" x 160)); +$rseq++; + +# send some non-silence to make sure we drop it + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x11" x 160)); +Time::HiRes::usleep(18000); $seq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +# start DTMF + +$sseq = $seq; + +snd($sock_a, $port_b, rtp(101 | 0x80, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x00\xa0")); +Time::HiRes::usleep(18000); $seq++; + +# buffered PCM now replaced by silence + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5" x 160)); +$rseq++; + +snd($sock_a, $port_b, rtp(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x0a\x01\x40")); +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(101, 1000 + $seq, 3000 + 160 * $sseq, 0x1234, "\x05\x8a\x01\xe0")); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\xd5" x 160)); +$rseq++; + +# input some non-silence to make sure it's also dropped + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# still silence + +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, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# now the adjusted DTMF is getting through + +$sseq = $rseq; + +rcv($sock_b, $port_a, rtpm(101 | 0x80, 1000 + $rseq, 3000 + 160 * $sseq, $ssrc, "\x00\x0a\x00\xa0")); +$rseq++; + +# continue non-silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x22" x 160)); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(101, 1000 + $rseq, 3000 + 160 * $sseq, $ssrc, "\x00\x0a\x01\x40")); +$rseq++; + +# switch back to silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# old DTMF end event now coming through + +rcv($sock_b, $port_a, rtpm(101, 1000 + $rseq, 3000 + 160 * $sseq, $ssrc, "\x00\x8a\x01\xe0")); +$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++; + +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)); +$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, "\x2a" 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, "\x2a" 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, "\x2a" x 160)); +$rseq++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w transcoding w event PT', + { 'from-tag' => ft(), 'delay-buffer' => 0 }); + +# buffer flushing + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +# sync forwarding + +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++; + + + + +($sock_a, $sock_b) = new_call([qw(198.51.100.1 2016)], [qw(198.51.100.1 3016)]); + +($port_a) = offer('zero DTMF block', { }, < ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 1 }); + +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 DTMF zero +rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xb8\x2b\x1c\x22\xcc\x9f\x9d\xb6\x2f\x24\x2f\xe2\xbd\xcd\x6b\xcd\xb2\xb1\x60\x27\x1f\x32\xad\x9b\xa0\x62\x20\x1e\x34\xb5\xa8\xb3\x7b\x4b\xf0\xd4\x47\x2d\x2c\x71\xa6\x9e\xad\x2f\x1c\x1f\x60\xa4\x9f\xb3\x3c\x2d\x39\x70\xea\x49\x47\xc4\xa9\xa8\xd5\x25\x1c\x2a\xb3\x9c\x9f\xcd\x27\x21\x33\xc3\xb2\xbf\xf3\xd8\xbb\xbd\x44\x26\x24\x48\xa6\x9c\xa8\x38\x1d\x1f\x44\xab\xa4\xb4\x4d\x3b\x4e\xf2\x49\x32\x36\xc7\xa4\xa1\xbe\x27\x1b\x25\xbe\x9e\x9f\xbe\x2e\x28\x37\xda\xc9\x73\x5d\xc0\xad\xaf\x4b\x23\x1e\x38\xa9\x9b\xa3\x49\x20\x20\x3c\xb4\xac\xbb\x6a\x67\xca\xce\x3d\x29\x2b\xd6\xa2\x9d\xb2\x2b\x1b\x22\xd9\xa3\xa2\xbb\x3b\x31\x42\xfc\x55\x3c\x44\xbb")); +Time::HiRes::usleep(18000); $seq++; + +# gap + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +# still DTMF zero +rcv($sock_b, $port_a, rtpm(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\xa5\xa7\x70\x22\x1c\x2e\xad\x9c\xa1\xf3\x27\x25\x3b\xc2\xba\xd0\x7d\xc5\xb3\xbc\x3b\x22\x24\x5a\xa2\x9b\xab\x31\x1d\x21\x56\xab\xa8\xbc\x4d\x47\xf6\xec\x3d\x2d\x35\xbc\xa0\xa0\xc9\x23\x1b\x29\xb7\x9e\xa2\xcb\x2e\x2c\x3f\xdb\xe4\x4c\x5b\xb8\xa9\xaf\x3e\x1f\x1e\x3f\xa5\x9b\xa7\x3d\x20\x24\x46\xb5\xb0\xc6\x6e\xd3\xbd\xce\x35\x25\x2b\xc4\x9f\x9d\xb9\x28\x1c\x26\xc7\xa4\xa6\xc4\x3c\x39\x53\x77\x42\x34\x44\xb3\xa2\xa7\x4d\x1f\x1c\x33\xaa\x9c\xa5\x54\x28\x29\x43\xc5\xc6\x79\xf1\xba\xad\xbc\x33\x1f\x24\xdf\x9f\x9c\xaf\x2d\x1d\x25\xef\xac\xac\xc6\x51\x62\xce\xfd\x34\x29\x36\xb4\x9e\xa1\xe4\x20\x1b\x2d\xb1\x9f\xa6\xe1\x30\x31\x4b\xe6\x52\x3f")); +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++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block', + { 'from-tag' => ft() }); + +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, "\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, "\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, "\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, "\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++; + +# enable blocking + +rtpe_req('block DTMF', 'zero DTMF block', + { 'from-tag' => ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 100 }); + +# pre-send 100 ms worth of audio + +$rseq = $seq; + +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# start receiving + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +# start DTMF, interleaved with receiving older audio + +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")); +Time::HiRes::usleep(18000); $seq++; + +# still buffered silence + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +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")); +Time::HiRes::usleep(18000); $seq++; + +# now replaced by DTMF zero + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x74\xb7\xbc\xdf\xe5\xbd\xb6\xea\x2c\x21\x2f\xb2\x9c\x9f\xd7\x22\x1d\x2d\xb7\xa5\xac\xe0\x3e\x4a\xed\x57\x35\x2f\x55\xaa\x9f\xab\x36\x1c\x1e\x48\xa5\x9d\xad\x3e\x29\x2f\x5b\xcc\xf5\x50\xd1\xaf\xaa\xc9\x2a\x1d\x28\xbb\x9d\x9d\xbe\x28\x1e\x2d\xc7\xad\xb5\xdc\x64\xcc\xc3\x51\x2b\x26\x3f\xaa\x9c\xa5\x40\x1e\x1d\x3a\xab\xa0\xad\x51\x33\x3c\x66\x5f\x3d\x3a\xd6\xa9\xa3\xb9\x2b\x1c\x22\xcc\x9f\x9d\xb6\x2f\x24\x2f\xe0\xbc\xca\x71\xcf\xb4\xb2\x60\x27\x1f\x32\xad\x9c\xa0\x60\x20\x1e\x34\xb4\xa8\xb2\x7a\x48\x72\xd7\x49\x2e\x2d\x74\xa6\x9e\xae\x2f\x1c\x1f\x61\xa3\x9f\xb2\x3b\x2c\x37\x6f\xe0\x4c\x49\xc5\xaa\xa9\xd6\x26\x1d\x2b\xb3\x9c\x9f\xcd\x27")); +$rseq++; + +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")); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x20\x32\xc2\xb1\xbe\xf0\xdc\xbd\xbe\x45\x27\x25\x48\xa6\x9c\xa8\x38\x1d\x1f\x44\xaa\xa3\xb4\x4c\x3a\x4b\xf8\x4b\x34\x37\xc7\xa5\xa1\xbe\x27\x1b\x25\xbe\x9e\x9f\xbe\x2e\x27\x36\xd9\xc6\xf1\x5f\xc2\xad\xb0\x4c\x23\x1f\x38\xa9\x9b\xa3\x48\x20\x20\x3c\xb3\xab\xba\x68\x5d\xce\xcf\x3d\x29\x2c\xd5\xa2\x9d\xb2\x2b\x1b\x22\xd8\xa3\xa1\xba\x3a\x30\x40\xfa\x5a\x3d\x45\xbc\xa6\xa8\x6e\x22\x1c\x2e\xad\x9c\xa1\xf6\x26\x24\x3a\xc1\xb8\xcd\x7e\xc7\xb4\xbc\x3b\x23\x24\x5b\xa2\x9b\xab\x31\x1d\x21\x56\xaa\xa7\xbb\x4b\x44\x70\xee\x3e\x2d\x36\xbc\xa1\xa1\xca\x23\x1b\x29\xb7\x9e\xa2\xcb\x2e\x2b\x3e\xd9\xdc\x4f\x5d\xb9\xa9\xaf\x3e\x1f\x1f\x3f\xa5\x9b\xa7")); +$rseq++; + +# back to silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# still tone + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x3d\x20\x23\x46\xb4\xaf\xc4\x6b\xd9\xbf\xcf\x36\x25\x2b\xc4\x9f\x9d\xb9\x27\x1b\x26\xc6\xa3\xa5\xc3\x3b\x38\x4f\x7a\x45\x36\x46\xb4\xa2\xa8\x4d\x1f\x1c\x33\xaa\x9c\xa5\x53\x27\x28\x42\xc3\xc2\xf0\xf3\xbb\xae\xbd\x34\x20\x24\xde\x9f\x9c\xaf\x2d\x1d\x25\xee\xab\xab\xc5\x4f\x5a\xd2\xfe\x35\x2a\x37\xb4\x9e\xa1\xe6\x20\x1b\x2d\xb1\x9f\xa6\xe1\x2f\x2f\x4a\xe0\x58\x41\x65\xb1\xa6\xb0\x37\x1e\x1f\x4d\xa2\x9c\xab\x37\x21\x27\x55\xb6\xb8\xd4\xf6\xc3\xb8\xd5\x2f\x22\x2c\xba\x9d\x9e\xc2\x25\x1c\x2a\xbd\xa5\xaa\xcf\x3e\x42\x71\x62\x39\x2f\x49\xae\x9f\xa9\x3e\x1d\x1d\x3b\xa7\x9d\xa9\x47\x29\x2c\x4e\xc9\xd9\x56\xde\xb2\xab\xbf\x2d\x1e\x25\xc7\x9e")); +$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, "\x9c\xb6\x2a\x1e\x29\xd2\xad\xb0\xd1\x5b\xd8\xc5\x61\x2e\x27\x39\xae\x9d\xa2\x54\x1f\x1c\x32\xae\xa0\xaa\x66\x32\x38\x5a\x73\x42\x3a\xf1\xac\xa3\xb3\x2f\x1c\x20\xff\xa0\x9c\xaf\x33\x23\x2c\x70\xba\xc2\xf3\xda\xb8\xb2\xe6\x2a\x20\x2e\xb2\x9c\x9f\xd4\x22\x1d\x2e\xb9\xa7\xae\xe1\x45\x58\xdb\x50\x30\x2d\x52\xa9\x9e\xaa\x36\x1c\x1e\x47\xa5\x9e\xad\x3f\x2b\x32\x5e\xd6\x59\x49\xce\xac\xa9\xc7\x29\x1d\x27\xbb\x9d\x9d\xbe\x29\x1f\x2e\xc9\xaf\xb9\xe2\xf1\xc2\xbe\x4f\x2a\x25\x3d\xaa\x9c\xa5\x42\x1e\x1d\x3a\xac\xa2\xaf\x55\x38\x43\x76\x54\x38\x36\xd5\xa7\xa1\xb7\x2b\x1c\x22\xcd\x9f\x9e\xb7\x30\x26\x31\xe5\xc1\xd9\x60\xca\xaf\xaf\x61\x26\x1f\x31")); +$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, "\xad\x9b\xa0\x65\x21\x1e\x34\xb7\xaa\xb5\x7d\x52\xda\xcf\x45\x2b\x2b\x6d\xa5\x9d\xad\x2f\x1c\x1f\x5e\xa4\xa0\xb4\x3d\x2e\x3b\x72\x70\x42\x43\xc3\xa8\xa7\xd3\x25\x1c\x2a\xb3\x9c\x9f\xcd\x28\x22\x34\xc6\xb6\xc5\xfb\xcf\xb8\xbb\x43\x25\x23\x46\xa6\x9b\xa7\x39\x1d\x1f\x44\xab\xa5\xb6\x4f\x3f\x59\xeb\x44\x2f\x34\xc6\xa3\xa0\xbd\x27\x1b\x25\xbf\x9f\x9f\xbe\x2f\x29\x39\xdd\xcf\x5b\x59\xbe\xab\xae\x4b\x22\x1e\x37\xa9\x9b\xa3\x4a\x21\x21\x3c\xb6\xad\xbe\x6e\xf0\xc4\xcc\x3b\x27\x2a\xd6\xa1\x9d\xb1\x2b\x1b\x22\xdb\xa4\xa3\xbb\x3c\x34\x47\xfe\x4d\x38\x41\xba\xa4\xa6\x76\x21\x1c\x2d\xae\x9c\xa2\xef\x28\x26\x3c\xc5\xbd\xdb\x7a\xc0\xb0\xba\x3a\x21")); +$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, "\x23\x58\xa2\x9b\xab\x32\x1d\x22\x55\xac\xa9\xbe\x4f\x4d\xdd\xe8\x3a\x2b\x33\xbc\xa0\xa0\xc8\x23\x1b\x29\xb8\x9f\xa3\xcc\x30\x2d\x42\xdf\x71\x46\x59\xb6\xa7\xae\x3e\x1f\x1e\x3e\xa5\x9b\xa7\x3e\x21\x25\x47\xb7\xb4\xcb\x77\xcb\xba\xcc\x33\x23\x2a\xc4\x9f\x9d\xb8\x28\x1c\x26\xc8\xa5\xa7\xc5\x3e\x3d\x5c\x70\x3e\x31\x42\xb2\xa0\xa7\x4d\x1f\x1c\x33\xab\x9d\xa6\x56\x29\x2a\x45\xc9\xcd\x60\xf0\xb7\xac\xbb\x32\x1f\x23\xe1\x9f\x9c\xaf\x2e\x1e\x26\xf4\xad\xae\xc9\x57\xf3\xc9\xfa\x32\x28\x34\xb4\x9e\xa0\xe0\x20\x1c\x2d\xb2\x9f\xa7\xe0\x32\x34\x4f\xf1\x4a\x3c\x60\xaf\xa4\xaf\x36\x1d\x1f\x4c\xa3\x9c\xab\x39\x23\x29\x56\xba\xbd\xdf\xe7\xbc\xb3\xd1")); +$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, "\x2d\x20\x2b\xba\x9d\x9e\xc1\x25\x1c\x2a\xbf\xa6\xab\xd1\x43\x4c\xe4\x5c\x35\x2d\x47\xad\x9e\xa8\x3e\x1d\x1d\x3b\xa8\x9d\xaa\x49\x2a\x2f\x51\xd0\x74\x4c\xdb\xaf\xa9\xbe\x2d\x1d\x25\xc8\x9e\x9c\xb6\x2b\x1f\x2a\xd6\xaf\xb5\xd7\x6c\xca\xbe\x5f\x2c\x25\x37\xad\x9d\xa2\x57\x1f\x1c\x32\xaf\xa1\xac\x6a\x36\x3d\x64\x5f\x3c\x36\xf4\xaa\xa1\xb1\x2f\x1c\x20\x74\xa0\x9d\xaf\x34\x25\x2d\x6f\xbe\xcc\x69\xd2\xb3\xaf\xe1\x29\x1f\x2d\xb2\x9c\x9f\xd2\x23\x1e\x2e\xbb\xa9\xb0\xe3\x4c\xf7\xd0\x4d\x2e\x2b\x4f\xa8\x9d\xaa\x36\x1c\x1e\x46\xa6\x9f\xae\x42\x2d\x37\x62\xe9\x4a\x43\xcc\xaa\xa7\xc4\x28\x1c\x27\xbc\x9d\x9e\xbe\x2a\x21\x2f\xcc\xb3\xbe\xec\xdb\xbb")); +$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, "\xba\x4e\x28\x23\x3c\xa9\x9c\xa4\x43\x1e\x1e\x3a\xad\xa4\xb0\x59\x3c\x4c\xee\x4c\x33\x33\xd4\xa6\xa0\xb6\x2b\x1b\x22\xcf\x9f\x9e\xb7\x32\x28\x34\xea\xc9\x7a\x58\xc5\xad\xad\x63\x25\x1e\x30\xad\x9b\xa0\x6b\x22\x1f\x35\xb9\xac\xb9\xfe\x65\xcb\xca\x41\x29\x29\x69\xa4\x9d\xac\x2f\x1c\x1f\x5c\xa5\xa1\xb5\x3f\x31\x3f\x77\x58\x3c\x3f\xc0\xa6\xa5\xd0\x24\x1c\x2a\xb4\x9d\x9f\xcc\x29\x24\x36\xc9\xba\xce\x77\xc8\xb3\xb8\x41\x24\x22\x44\xa5\x9b\xa7\x3a\x1e\x20\x44\xad\xa8\xb8\x54\x47\xfd\xe1\x3f\x2d\x31\xc5\xa2\x9f\xbc\x27\x1b\x25\xc0\x9f\xa0\xbf\x31\x2b\x3c\xe3\xe2\x4c\x52\xbb\xa9\xac\x4b\x21\x1e\x36\xa9\x9b\xa4\x4c\x22\x22\x3d\xb8\xb0\xc3\x75")); +$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, "\xd6\xbd\xc8\x39\x25\x29\xd8\xa0\x9c\xb0\x2b\x1c\x23\xde\xa6\xa5\xbd\x3f\x39\x4f\x7d\x45\x34\x3e\xb9\xa2\xa5\xff\x21\x1c\x2d\xae\x9d\xa3\xeb\x29\x28\x3e\xca\xc5\xff\x75\xbc\xad\xb8\x39\x20\x22\x55\xa2\x9b\xab\x33\x1e\x23\x55\xad\xac\xc0\x56\x5f\xce\xe2\x37\x29\x31\xbb\x9f\x9f\xc7\x23\x1b\x29\xb9\x9f\xa4\xcc\x33\x30\x47\xea\x55\x3e\x54\xb4\xa5\xad\x3e\x1e\x1e\x3d\xa6\x9b\xa7\x3f\x23\x26\x48\xba\xb9\xd3\xf6\xc2\xb5\xc9\x31\x22\x29\xc5\x9e\x9d\xb7\x28\x1c\x27\xca\xa6\xa9\xc7\x42\x44\x7d\x6b\x3a\x2e\x3f\xb0\x9f\xa5\x4e\x1f\x1c\x32\xab\x9d\xa7\x5a\x2a\x2c\x49\xce\xde\x50\xee\xb3\xaa\xb9\x31\x1e\x22\xe6\x9f\x9c\xaf\x2e\x1e\x27\xfc\xaf\xb0")); +$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, "\xcd\x61\xd4\xc0\xf5\x2f\x26\x32\xb3\x9d\xa0\xdd\x21\x1c\x2d\xb4\xa1\xa9\xdf\x36\x39\x58\x6f\x40\x38\x5d\xad\xa2\xae\x36\x1d\x1e\x4b\xa3\x9c\xab\x3a\x24\x2b\x57\xbd\xc4\xfc\xdd\xb8\xaf\xce\x2c\x1f\x2a\xba\x9d\x9d\xc0\x26\x1d\x2b\xc1\xa8\xad\xd4\x49\x5d\xd5\x58\x31\x2b\x44\xac\x9e\xa7\x3f\x1d\x1d\x3a\xa9\x9e\xab\x4b\x2c\x32\x56\xdc\x54\x45\xd9\xad\xa7\xbc\x2c\x1d\x24\xc9\x9e\x9d\xb6\x2c\x20\x2c\xd9\xb2\xb9\xde\xec\xc0\xbb\x5f\x2b\x23\x36\xad\x9c\xa1\x59\x1f\x1d\x32\xb0\xa3\xad\x6e\x3a\x44\x7b\x56\x37\x33\xf9\xa9\xa0\xb0\x2f\x1c\x1f\x6c\xa1\x9d\xb0\x36\x27\x2f\x6e\xc5\xdd\x5a\xcd\xaf\xad\xdd\x28\x1e\x2c\xb2\x9c\x9e\xd0\x24\x1e\x2f\xbd")); +$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, "\xab\xb4\xe6\x58\xd5\xca\x4a\x2c\x29\x4d\xa8\x9d\xa9\x37\x1c\x1e\x45\xa7\xa0\xaf\x45\x2f\x3b\x68\x6a\x40\x3e\xca\xa9\xa5\xc2\x28\x1c\x26\xbc\x9d\x9e\xbe\x2b\x22\x30\xcf\xb8\xc5\xfc\xcf\xb6\xb7\x4d\x26\x21\x3b\xa9\x9b\xa4\x45\x1e\x1e\x3a\xae\xa6\xb3\x5c\x42\x5d\xe1\x47\x2f\x30\xd4\xa5\x9f\xb5\x2b\x1b\x22\xd1\xa0\x9f\xb8\x34\x2a\x37\xee\xd5\x56\x50\xc1\xab\xab\x66\x24\x1d\x2f\xad\x9b\xa0\x71\x23\x20\x37\xbb\xae\xbd\xfd\xe9\xc0\xc5\x3f\x27\x28\x64\xa3\x9c\xac\x2f\x1c\x20\x5a\xa7\xa3\xb7\x42\x36\x47\x7e\x4c\x37\x3c\xbf\xa4\xa4\xce\x24\x1b\x29\xb5\x9d\xa0\xcc\x2a\x26\x38\xcc\xbf\xde\x6c\xc1\xaf\xb5\x40\x22\x20\x42\xa5\x9b\xa7\x3b\x1e\x20")); +$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, "\x44\xae\xaa\xbb\x59\x51\xd8\xdb\x3c\x2a\x2f\xc5\xa1\x9e\xbb\x27\x1b\x25\xc2\xa0\xa2\xc0\x34\x2e\x3f\xea\x68\x42\x4e\xb9\xa7\xab\x4c\x20\x1d\x35\xa9\x9b\xa4\x4d\x23\x24\x3e\xbb\xb5\xca\x7d\xca\xb8\xc4\x37\x23\x27\xd9\xa0\x9c\xb0\x2c\x1c\x23\xe2\xa7\xa7\xbe\x43\x3e\x5f\x7d\x3e\x2f\x3c\xb7\xa1\xa4\xf5\x20\x1b\x2d\xaf\x9d\xa3\xe7\x2b\x2a\x40\xce\xd0\x5a\x6f\xb8\xab\xb5\x38\x1f\x20\x52\xa2\x9b\xab\x34\x1e\x24\x54\xaf\xae\xc5\x5e\xe8\xc5\xde\x34\x27\x2f\xbb\x9e\x9f\xc5\x24\x1b\x29\xba\xa1\xa6\xcd\x36\x35\x4d\xfa\x49\x39\x4f\xb1\xa3\xab\x3e\x1e\x1d\x3c\xa6\x9c\xa8\x41\x24\x28\x4a\xbd\xbe\xe1\xeb\xbc\xb1\xc6\x30\x20\x28\xc5\x9e\x9d\xb7\x29")); +$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)); +$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, "\x00" x 160)); +$rseq++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block', + { 'from-tag' => ft(), 'delay-buffer' => 0 }); + +# buffer flushing + +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(0, 1000 + $rseq, 3000 + 160 * $rseq, 0x1234, "\x00" x 160)); +$rseq++; + +# sync forwarding + +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 2018)], [qw(198.51.100.1 3018)]); + +($port_a) = offer('zero DTMF block w transcoding', { codec => { transcode => ['PCMA'] } }, < ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 1 }); + +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++; +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++; + +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(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\xd5\x9b\x87\x97\x64\x10\x6b\x41\xdc\x73\x66\xd1\x91\x9a\x97\x6d\x07\x04\x67\x91\x9a\x96\x5c\x60\x7d\xd3\x4d\x6b\x11\x7c\x91\x87\x9e\x4f\x1a\x04\x15\xe0\x93\xe8\xda\x59\xf1\xe4\x44\x10\x1b\x6b\xeb\x87\x85\xfc\x12\x1a\x17\xc3\xe2\xfc\x51\xc9\xeb\x96\xcb\x13\x07\x1c\xff\x85\x84\xee\x6f\x12\x68\x5c\xc5\x76\x7b\xc9\x93\x98\xef\x14\x06\x1a\x4f\x9c\x9a\x95\x77\x6c\x7f\xd7\x75\x6b\x14\x59\x9d\x87\x93\x66\x04\x04\x63\xeb\x9d\xe9\xd7\x41\xf4\xff\x71\x12\x19\x61\x91\x86\x9b\xd5\x1e\x1a\x68\xfc\xee\xff\x55\xf4\xeb\x95\x53\x1c\x04\x11\xec\x87\x85\xe5\x14\x1d\x6f\xd1\xcd\x4b\x73\xfc\x92\x9f\xfb\x12\x06\x19\xcd\x98\x9a\xef\x65\x69\x7e\x55\x77\x68")); +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(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x68\xc2\x9e\x84\x94\x6b\x07\x1a\x72\x96\x9c\xec\x59\x49\xcf\xf7\x7b\x12\x1c\x76\x9c\x86\x9f\x7c\x1a\x1a\x63\xe6\xeb\xfe\xd5\xf6\xe9\xef\x71\x19\x05\x68\x97\x86\x9b\xc2\x10\x1c\x62\xc1\xf5\x43\x49\xe4\x92\x92\xda\x1e\x06\x12\xe7\x85\x9b\xe7\x62\x6b\x7e\x55\x76\x69\x62\xf9\x98\x85\xe2\x10\x06\x18\x54\x92\x9c\xe0\x49\x76\xc7\xc3\x66\x12\x13\xd3\x98\x86\x91\x6c\x05\x1b\x79\xef\x95\xff\x54\xf6\xef\xe1\x67\x1b\x1a\x64\x9d\x86\x9e\x43\x1c\x1c\x67\xf6\xf0\x5a\x5a\xe0\x92\x91\x49\x1b\x07\x17\xeb\x84\x98\xf6\x68\x15\x7c\xd7\x76\x6c\x64\xe0\x9b\x9b\xf0\x1f\x06\x1c\xf3\x9e\x9c\xe5\x72\x72\xde\xdd\x63\x12\x17\xfd\x9a\x87\xe8\x17\x04\x1e\x41\x95")); +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 DTMF zero +rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x93\x06\x36\x09\xe5\xb5\xb4\x9d\x1a\x0f\x1a\xc0\x94\xfa\x59\xe5\x99\x98\x42\x0d\x35\x19\x87\xb6\x8b\x40\x0b\x34\x1f\x9c\x83\x9e\x54\x67\xd2\xf2\x63\x07\x07\x53\x8c\xb4\x84\x05\x36\x0a\x42\x8e\xb5\x9e\x17\x07\x10\x52\xd8\x61\x63\xed\x80\x83\xf0\x0c\x37\x01\x9e\xb6\xb5\xe5\x0d\x0b\x1e\xef\x9e\xeb\xd0\xf6\x96\x94\x6c\x0d\x0f\x60\x8c\xb6\x82\x13\x37\x35\x6c\x81\x8f\x9f\x65\x16\x7b\xd3\x61\x19\x1d\xe3\x8f\x8b\x95\x02\x31\x0c\x95\xb5\xb5\x95\x05\x02\x12\xf4\xe1\x53\x48\xe8\x87\x9a\x64\x09\x35\x13\x83\xb1\x8e\x61\x0a\x0a\x17\x9f\x86\x96\x58\x45\xe6\xfb\x14\x03\x06\xf0\x88\xb7\x99\x01\x36\x09\xf7\x8e\x88\x96\x16\x18\x6e\xd4\x73\x17\x6c\x96")); +Time::HiRes::usleep(18000); $seq++; + +# gap + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +# still tone +rcv($sock_b, $port_a, rtpm(8, 1000 + $seq, 3000 + 160 * $seq, $ssrc, "\x8c\x82\x52\x08\x36\x04\x84\xb6\x88\xd0\x0d\x0f\x16\xee\x91\xfe\x54\xed\x9e\x97\x16\x09\x0e\x74\x89\xb6\x81\x18\x37\x08\x70\x81\x82\x97\x65\x63\xd1\xde\x14\x07\x1c\x97\x8b\x8b\xe6\x0e\x31\x03\x92\xb5\x89\xe4\x05\x06\x6a\xf5\xc7\x64\x4a\x93\x83\x85\x6a\x0a\x35\x6a\x8c\xb1\x8d\x14\x0b\x0e\x62\x9c\x9b\xe3\x5c\xfd\x94\xfb\x1c\x0f\x06\xec\xb5\xb7\x90\x02\x36\x0c\xe3\x8e\x8c\xec\x17\x10\x7d\x51\x6e\x1f\x6c\x9e\x88\x82\x65\x35\x37\x1e\x81\xb7\x8c\x72\x02\x03\x6f\xed\xe2\x57\xd3\x91\x84\x97\x1e\x0a\x0e\xcc\x8a\xb6\x85\x04\x34\x0c\xdd\x86\x87\xe2\x7f\x40\xfb\xd4\x1f\x00\x1d\x9f\xb4\x8b\xc6\x0b\x36\x07\x98\xb5\x8d\xc3\x1b\x18\x67\xc4\x7c\x6a")); +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++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w transcoding', + { 'from-tag' => ft() }); + +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; +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++; + +# enable blocking + +rtpe_req('block DTMF', 'zero DTMF block w transcoding', + { 'from-tag' => ft(), 'DTMF-security' => 'zero', 'delay-buffer' => 100 }); + +# pre-send 100 ms worth of audio + +$rseq = $seq; + +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(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)); +Time::HiRes::usleep(18000); $seq++; +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# start receiving + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +# start DTMF, interleaved with receiving older audio + +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")); +Time::HiRes::usleep(18000); $seq++; + +# still buffered silence + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +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")); +Time::HiRes::usleep(18000); $seq++; + +# now replaced by DTMF zero + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x50\x92\x97\xcd\xc7\x94\x9d\xd8\x06\x08\x05\x99\xb7\xb5\xf1\x08\x37\x04\x92\x8f\x87\xc2\x6a\x66\xdf\x71\x1c\x05\x70\x81\xb5\x86\x1d\x37\x34\x60\x8f\xb4\x87\x15\x03\x1a\x75\xe4\xd0\x7e\xff\x85\x81\xe1\x00\x34\x03\x96\xb7\xb4\xea\x02\x35\x07\xe3\x84\x9c\xc8\x47\xe4\xef\x7f\x06\x0d\x6a\x81\xb7\x8c\x69\x34\x37\x11\x86\x8b\x84\x7f\x1e\x17\x45\x4d\x14\x11\xf0\x83\x89\x90\x06\x36\x09\xe4\xb5\xb4\x9d\x05\x0e\x05\xc3\x97\xe6\x52\xf8\x9f\x99\x42\x02\x0a\x19\x87\xb6\x8b\x42\x0b\x34\x1f\x9c\x82\x99\x57\x61\x53\xf1\x61\x04\x07\x50\x8d\xb4\x84\x05\x36\x0a\x43\x8e\xb5\x99\x16\x06\x12\x5d\xc2\x65\x61\xed\x81\x83\xf1\x0c\x37\x01\x9e\xb6\xb5\xfa\x0d")); +$rseq++; + +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")); +Time::HiRes::usleep(18000); $seq++; + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x0b\x19\xee\x98\x95\xd2\xc8\x94\x95\x6d\x02\x0f\x60\x8d\xb6\x82\x13\x37\x35\x6c\x81\x8e\x9f\x64\x11\x67\xd6\x67\x1f\x12\xe3\x8f\x88\x95\x02\x31\x0c\x95\xb4\xb5\x95\x04\x02\x1d\xf7\xe2\xd2\x4c\xee\x84\x9a\x64\x0e\x35\x13\x83\xb1\x8e\x60\x0a\x0a\x17\x9e\x86\x91\x5b\x49\xfa\xf9\x15\x00\x06\xf0\x89\xb4\x99\x01\x36\x08\xf6\x89\x88\x91\x11\x1a\x68\xd7\x75\x14\x6d\x97\x8c\x82\x5d\x08\x36\x04\x84\xb6\x88\xd1\x0d\x0f\x11\xe9\x93\xe5\x55\xe3\x9f\x94\x16\x0e\x0f\x75\x89\xb6\x86\x18\x37\x08\x71\x81\x82\x96\x64\x6c\x52\xdc\x15\x04\x1d\x97\x8b\x8b\xe6\x0e\x31\x03\x92\xb4\x88\xe7\x04\x01\x15\xf7\xca\x78\x49\x90\x80\x9a\x6a\x0a\x35\x6a\x8c\xb1\x8d")); +$rseq++; + +# back to silence + +snd($sock_a, $port_b, rtp(0, 1000 + $seq, 3000 + 160 * $seq, 0x1234, "\x00" x 160)); +Time::HiRes::usleep(18000); $seq++; + +# still tone + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x14\x0a\x0e\x62\x9f\x9a\xec\x59\xf7\xea\xf9\x1d\x0c\x06\xec\xb5\xb4\x90\x02\x36\x0c\xe2\x8e\x8c\xef\x16\x13\x78\x57\x6d\x1d\x62\x9f\x89\x82\x65\x35\x37\x1e\x81\xb6\x8c\x7d\x0d\x03\x6f\xef\xee\xd2\xd3\x96\x85\x94\x1f\x0a\x0f\xcf\x8a\xb6\x85\x04\x37\x0c\xdc\x81\x86\xed\x78\x74\xfc\xd5\x1c\x00\x12\x9c\xb5\x88\xc4\x0b\x36\x07\x98\xb5\x8c\xc0\x1a\x1a\x66\xc3\x77\x69\x44\x98\x8c\x9b\x12\x34\x35\x7a\x89\xb6\x81\x12\x0b\x02\x73\x9d\x93\xf2\xd1\xef\x93\xf3\x05\x09\x07\x91\xb4\xb4\xee\x0f\x36\x00\x94\x8f\x80\xf8\x15\x6e\x52\x40\x10\x1a\x61\x84\x8a\x83\x15\x34\x37\x16\x82\xb7\x80\x63\x03\x07\x7b\xe6\xf7\x70\xce\x99\x81\xeb\x04\x34\x0c\xe3\xb4")); +$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, "\xb7\x9d\x01\x34\x00\xfc\x87\x9b\xff\x75\xf7\xed\x43\x05\x0d\x10\x84\xb7\x89\x72\x35\x36\x19\x84\x8a\x81\x44\x19\x13\x74\x53\x6e\x11\xd2\x86\x89\x9e\x1a\x37\x0a\xd5\x8a\xb7\x9a\x1e\x09\x06\x52\x91\xee\xd0\xf4\x93\x99\xc4\x01\x0a\x04\x99\xb6\xb5\xf2\x09\x37\x04\x90\x8d\x85\xc3\x6d\x76\xf5\x7f\x1b\x07\x7c\x80\xb4\x81\x1d\x37\x34\x63\x8c\xb4\x84\x6b\x01\x19\x4e\xf0\x77\x61\xfb\x87\x83\xe3\x00\x37\x02\x96\xb7\xb4\x95\x03\x0a\x04\xe6\x9a\x90\xc0\xd3\xee\x95\x78\x00\x0f\x14\x80\xb6\x8f\x6e\x34\x34\x11\x87\x89\x85\x73\x13\x6f\x51\x72\x13\x1d\xf3\x82\x8b\x92\x01\x36\x09\xfa\xb5\xb4\x92\x1b\x0c\x18\xc4\xe9\xf7\x43\xe6\x9a\x9a\x43\x0c\x35\x18")); +$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, "\x87\xb6\x8b\x44\x08\x35\x1f\x92\x80\x9c\x55\x7c\xf4\xf8\x6d\x06\x01\x5c\x8c\xb7\x84\x05\x36\x0a\x4f\x8f\x8a\x9f\x14\x05\x16\x53\x52\x6e\x6f\xef\x83\x8d\xfd\x0c\x36\x01\x9e\xb7\xb5\xe5\x02\x09\x1f\xe2\x9d\xed\xd7\xf8\x93\x96\x6f\x0c\x0e\x62\x8c\xb6\x82\x10\x34\x35\x6c\x86\x8c\x9d\x79\x6a\x77\xd9\x6c\x1a\x1f\xe2\x8e\x8a\x94\x0d\x31\x0c\xea\xb5\x8a\xea\x1a\x00\x10\xc9\xf9\x4a\x77\x95\x81\x84\x67\x08\x34\x12\x83\xb1\x8e\x66\x0b\x0b\x17\x9d\x84\x95\x5c\xd2\xec\xe4\x16\x02\x01\xf1\x88\xb7\x98\x06\x36\x09\xf5\x8f\x8e\x96\x17\x1f\x63\xd5\x65\x13\x69\x91\x8f\x8d\x51\x08\x36\x04\x84\xb6\x88\xdd\x02\x0c\x17\xed\x94\xf5\x57\xe8\x9a\x91\x11\x08")); +$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, "\x09\x76\x88\xb6\x81\x19\x34\x09\x73\x86\x80\x95\x79\x7a\xce\xdb\x11\x06\x1e\x97\x8a\x8a\xe0\x0e\x31\x03\x93\xb5\x8e\xe4\x1a\x04\x6e\xcc\x52\x62\x77\x9d\x82\x84\x15\x35\x34\x15\x8c\xb1\x82\x15\x08\x0f\x63\x92\x9f\xe7\x56\xe7\x91\xe4\x1f\x0e\x01\xec\xb5\xb7\x93\x02\x36\x0d\xe0\x8f\x82\xed\x15\x14\x4b\x52\x15\x18\x6e\x99\x8b\x8d\x7a\x35\x36\x1e\x81\xb7\x8c\x71\x03\x01\x6d\xe1\xe5\x42\xd2\x92\x86\x96\x19\x35\x0e\xc3\x8a\xb6\x85\x04\x34\x0d\xd0\x87\x84\xe1\x71\xd3\xe1\xd7\x19\x02\x1f\x9f\xb4\x8b\xc2\x0b\x36\x07\x99\x8a\x82\xc2\x19\x1f\x78\xd3\x66\x17\x42\x85\x8e\x85\x1d\x34\x35\x64\x89\xb6\x86\x10\x09\x03\x70\x91\x94\xcd\xc5\x97\x9e\xff")); +$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, "\x04\x0b\x06\x91\xb7\xb4\xe9\x0c\x37\x01\xea\x8d\x86\xff\x6f\x64\xc7\x4b\x1c\x04\x63\x87\xb5\x82\x6a\x34\x37\x16\x83\xb4\x80\x61\x01\x05\x7f\xfe\x50\x64\xca\x9a\x83\x95\x07\x34\x0f\xe0\xb4\xb7\x9d\x06\x35\x01\xf0\x85\x9c\xf1\x5f\xe6\xea\x4d\x07\x0f\x12\x84\xb7\x88\x71\x35\x37\x19\x85\x88\x86\x58\x1d\x14\x47\x4c\x17\x12\xd0\x81\x88\x98\x05\x36\x0a\x50\x8b\xb7\x9a\x1c\x0f\x04\x5d\xea\xe4\x58\xfc\x9e\x9a\xc3\x03\x35\x07\x99\xb6\xb5\xfc\x0e\x34\x05\x96\x83\x9b\xc1\x64\xd6\xfe\x65\x04\x01\x78\x83\xb4\x80\x1d\x37\x34\x62\x8d\xb5\x85\x6e\x04\x12\x40\xdb\x66\x6f\xe4\x81\x8d\xec\x03\x37\x0d\x97\xb7\xb4\x95\x00\x0b\x05\xe4\x9e\x95\xde\xca\x96")); +$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, "\x91\x7a\x02\x09\x17\x80\xb6\x8f\x6f\x34\x34\x11\x84\x8f\x9b\x77\x17\x65\xdd\x64\x1e\x1e\xf3\x8c\x8a\x9d\x01\x36\x08\xf8\x8a\xb4\x92\x19\x02\x1f\xd8\xe1\x57\x76\xed\x87\x84\x41\x0f\x34\x1b\x87\xb6\x8b\x59\x09\x35\x1d\x90\x87\x90\xd5\x44\xe7\xe6\x69\x00\x00\x5b\x8f\xb7\x87\x1a\x36\x0a\x4a\x8c\x88\x9c\x6a\x18\x6b\x56\x76\x17\x6a\xe8\x8d\x8c\xfe\x0f\x36\x00\x9f\xb7\x8a\xe4\x00\x0e\x1d\xe1\x91\xfa\x51\xe0\x9e\x93\x69\x0e\x08\x6c\x8c\xb1\x82\x11\x34\x0a\x6c\x87\x82\x93\x72\x63\xd4\xc3\x6a\x07\x18\xed\x88\xb5\x97\x0d\x31\x0c\xe8\xb5\x8b\xea\x18\x06\x17\xc1\xc0\x64\x7c\x96\x83\x87\x67\x0b\x34\x1d\x83\xb1\x8e\x64\x08\x09\x14\x90\x9b\xef\x51")); +$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, "\xf0\x94\xe0\x10\x0c\x03\xf6\x8b\xb7\x9b\x06\x36\x09\xcf\x8c\x8f\x94\x6a\x10\x79\x55\x6d\x1f\x15\x90\x89\x8f\xd5\x0b\x36\x04\x85\xb7\x89\xd9\x00\x02\x15\xe6\xed\xd5\x50\x97\x84\x93\x10\x0a\x08\x73\x88\xb1\x81\x1e\x34\x0e\x73\x84\x86\xe8\x71\x4d\xfa\xc1\x12\x03\x18\x96\xb5\xb5\xe3\x0e\x31\x03\x90\x8a\x8f\xe4\x1e\x1b\x63\xd8\x73\x15\x72\x9f\x8c\x87\x15\x35\x34\x14\x8c\xb6\x82\x6b\x09\x0d\x60\x91\x90\xfd\xd1\xee\x9c\xe1\x18\x08\x03\xed\xb5\xb7\x93\x03\x36\x0d\xe6\x8d\x83\xe3\x6e\x6c\x54\x59\x11\x04\x6b\x98\x8a\x8c\x7b\x35\x36\x19\x86\xb7\x8d\x74\x01\x07\x61\xfb\xcf\x7e\xdd\x9e\x80\x90\x18\x34\x09\xc4\x8a\xb6\x85\x05\x35\x02\xd4\x85\x9b")); +$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, "\xe5\x43\xf2\xe8\xd1\x1a\x0c\x19\x9e\xb4\x8a\xc9\x0b\x36\x07\x9f\x8b\x83\xcd\x1d\x10\x76\x5d\x68\x13\x49\x84\x88\x84\x1d\x37\x35\x67\x8e\xb6\x86\x11\x0f\x01\x71\x94\xec\xd4\xc9\x93\x9a\xfb\x07\x35\x01\x91\xb7\xb4\xeb\x0c\x37\x01\xe9\x83\x84\xf2\x61\x49\xf3\x77\x18\x06\x6c\x86\xb4\x8d\x6a\x34\x37\x11\x83\xb4\x81\x67\x07\x19\x70\xcb\x72\x6d\xf7\x84\x8d\x97\x07\x37\x0e\xe1\xb4\xb7\x9d\x07\x0a\x06\xf7\x99\x91\xce\xde\xeb\x96\x4d\x01\x09\x1d\x84\xb6\x88\x77\x35\x37\x19\x9b\x89\x87\x5c\x11\x6c\x54\x70\x12\x1e\xd7\x83\x8a\x9a\x05\x36\x0a\x5f\x88\xb4\x9b\x1d\x0d\x1a\x5c\xed\xc9\x74\xe5\x9a\x87\xce\x02\x34\x07\x99\xb6\xb5\xfe\x0f\x34\x1a\x94")); +$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, "\x81\x9f\xc4\x76\xf3\xe6\x66\x06\x03\x65\x82\xb7\x80\x12\x37\x34\x6d\x82\x8a\x9a\x6d\x1a\x16\x5b\x58\x6b\x15\xe6\x83\x8f\xee\x02\x36\x0d\x97\xb4\xb4\x95\x01\x09\x1b\xf8\x93\xe2\xd4\xf9\x9d\x92\x65\x0d\x08\x16\x83\xb6\x8e\x6d\x35\x34\x11\x85\x8d\x9e\x48\x6e\x49\xc3\x63\x05\x1a\xf2\x8f\xb5\x9c\x01\x31\x08\xff\x8b\xb5\x93\x1f\x01\x12\xdc\xf3\x70\x79\xe9\x81\x86\x44\x0e\x34\x1a\x87\xb6\x8b\x52\x0e\x0b\x12\x96\x85\x94\xd4\xdb\xe8\xed\x6a\x02\x02\x46\x8e\xb6\x86\x1a\x36\x0a\x74\x8d\x89\x92\x6e\x1d\x63\x55\x64\x12\x17\xea\x8f\x8e\xfb\x0e\x36\x00\x9c\xb7\x8a\xe4\x01\x0c\x13\xe5\xeb\xce\x5f\xe9\x85\x9c\x68\x09\x0b\x6e\x8c\xb1\x8d\x16\x34\x0b")); +$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, "\x6c\x85\x80\x96\x74\x7c\xf6\xca\x17\x01\x05\xed\x8b\xb5\x96\x0d\x31\x0c\xee\x8a\x88\xeb\x1f\x04\x6b\xd8\x5a\x6e\x7a\x90\x8d\x81\x64\x0a\x37\x1c\x80\xb6\x8e\x7a\x0e\x0e\x15\x96\x9c\xe6\x55\xe6\x93\xec\x12\x0e\x02\xf4\x8a\xb6\x9a\x06\x36\x0e\xc0\x82\x8d\x95\x6f\x15\x4c\x54\x15\x1a\x17\x92\x8b\x8e\xd0\x0b\x36\x07\x85\xb7\x8e\xc5\x01\x01\x68\xfb\xf9\x74\x5d\x93\x81\x9c\x13\x35\x0b\x7c\x88\xb1\x81\x1f\x35\x0f\x72\x9a\x84\xed\x4e\xda\xed\xce\x1f\x0d\x1a\x96\xb5\xb5\xed\x0e\x36\x03\x91\x8b\x8c\xe5\x1d\x1c\x65\xd7\x61\x10\x79\x98\x8e\x86\x15\x34\x34\x17\x8d\xb6\x82\x69\x0f\x03\x66\x94\x95\xc3\xd9\x97\x98\xe2\x1a\x0a\x02\xed\xb4\xb7\x92\x03")); +$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)); +$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, "\x2a" x 160)); +$rseq++; + +# disable blocking + +rtpe_req('unblock DTMF', 'zero DTMF block w transcoding', + { 'from-tag' => ft(), 'delay-buffer' => 0 }); + +# buffer flushing + +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; +rcv($sock_b, $port_a, rtpm(8, 1000 + $rseq, 3000 + 160 * $rseq, $ssrc, "\x2a" x 160)); +$rseq++; + +# sync forwarding + +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++; + + + + done_testing(); -#NGCP::Rtpengine::AutoTest::terminate('f00'); +#done_testing;NGCP::Rtpengine::AutoTest::terminate('f00');exit;