Browse Source

TT#58660 PCM to RFC DTMF transcoding

Change-Id: Iea6a11c0caad1f5e7dcca966101e2969d3516b6f
changes/07/29907/16
Richard Fuchs 7 years ago
parent
commit
910c03fa62
13 changed files with 1494 additions and 67 deletions
  1. +2
    -0
      daemon/Makefile
  2. +1
    -1
      daemon/call.c
  3. +356
    -57
      daemon/codec.c
  4. +69
    -0
      daemon/dtmf.c
  5. +1
    -0
      debian/control
  6. +1
    -1
      include/call.h
  7. +3
    -1
      include/codec.h
  8. +10
    -0
      include/dtmf.h
  9. +18
    -1
      lib/codeclib.c
  10. +1
    -0
      t/.gitignore
  11. +6
    -2
      t/Makefile
  12. +377
    -4
      t/auto-daemon-tests.pl
  13. +649
    -0
      t/test-dtmf-detect.c

+ 2
- 0
daemon/Makefile View File

@ -75,6 +75,7 @@ CFLAGS+= $(shell pkg-config --cflags libavformat)
CFLAGS+= $(shell pkg-config --cflags libavutil)
CFLAGS+= $(shell pkg-config --cflags libswresample)
CFLAGS+= $(shell pkg-config --cflags libavfilter)
CFLAGS+= $(shell pkg-config --cflags spandsp)
CFLAGS+= -DWITH_TRANSCODING
ifeq ($(have_bcg729),yes)
CFLAGS+= -DHAVE_BCG729
@ -115,6 +116,7 @@ LDLIBS+= $(shell pkg-config --libs libavformat)
LDLIBS+= $(shell pkg-config --libs libavutil)
LDLIBS+= $(shell pkg-config --libs libswresample)
LDLIBS+= $(shell pkg-config --libs libavfilter)
LDLIBS+= $(shell pkg-config --libs spandsp)
ifeq ($(have_bcg729),yes)
LDLIBS+= $(bcg729_lib)
endif


+ 1
- 1
daemon/call.c View File

@ -1772,7 +1772,7 @@ static void __update_media_id(struct call_media *media, struct call_media *other
/* called with call->master_lock held in W */
int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
const struct sdp_ng_flags *flags)
struct sdp_ng_flags *flags)
{
struct stream_params *sp;
GList *media_iter, *ml_media, *other_ml_media;


+ 356
- 57
daemon/codec.c View File

@ -3,6 +3,10 @@
#include <assert.h>
#include <inttypes.h>
#include <sys/types.h>
#include <spandsp/telephony.h>
#include <spandsp/super_tone_rx.h>
#include <spandsp/logging.h>
#include <spandsp/dtmf.h>
#include "call.h"
#include "log.h"
#include "rtplib.h"
@ -11,6 +15,8 @@
#include "rtcp.h"
#include "call_interfaces.h"
#include "dtmf.h"
#include "resample.h"
#include "dtmflib.h"
@ -65,6 +71,15 @@ struct codec_ssrc_handler {
struct timeval first_send;
unsigned long first_send_ts;
GString *sample_buffer;
// DTMF DSP stuff
dtmf_rx_state_t *dtmf_dsp;
resample_t dtmf_resampler;
format_t dtmf_format;
uint64_t dtmf_ts, last_dtmf_event_ts;
GQueue dtmf_events;
struct dtmf_event dtmf_event;
int rtp_mark:1;
};
struct transcode_packet {
@ -75,7 +90,7 @@ struct transcode_packet {
int marker:1,
ignore_seq:1;
int (*func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *);
void (*dup_func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *);
int (*dup_func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *);
struct rtp_header rtp;
};
@ -107,7 +122,9 @@ static void __handler_shutdown(struct codec_handler *handler) {
handler->ssrc_handler = NULL;
handler->kernelize = 0;
handler->transcoder = 0;
handler->dtmf_scaler = 0;
handler->output_handler = handler; // reset to default
handler->dtmf_payload_type = -1;
}
static void __codec_handler_free(void *pp) {
@ -123,6 +140,7 @@ static struct codec_handler *__handler_new(struct rtp_payload_type *pt) {
struct codec_handler *handler = g_slice_alloc0(sizeof(*handler));
handler->source_pt = *pt;
handler->output_handler = handler; // default
handler->dtmf_payload_type = -1;
return handler;
}
@ -154,7 +172,7 @@ static void __make_passthrough_ssrc(struct codec_handler *handler) {
}
static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_type *dest,
GHashTable *output_transcoders)
GHashTable *output_transcoders, int dtmf_payload_type)
{
assert(handler->source_pt.codec_def != NULL);
assert(dest->codec_def != NULL);
@ -180,13 +198,24 @@ reset:
handler->dest_pt = *dest;
handler->func = handler_func_transcode;
handler->transcoder = 1;
if (dtmf_payload_type != -1)
handler->dtmf_payload_type = dtmf_payload_type;
// is this DTMF to DTMF?
if (dtmf_payload_type != -1 && handler->source_pt.codec_def->dtmf) {
ilog(LOG_DEBUG, "Created DTMF transcode context for " STR_FORMAT " -> PT %i",
STR_FMT(&handler->source_pt.encoding_with_params),
dtmf_payload_type);
handler->dtmf_scaler = 1;
}
else
ilog(LOG_DEBUG, "Created transcode context for " STR_FORMAT " -> " STR_FORMAT
" with DTMF output %i",
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT(&dest->encoding_with_params), dtmf_payload_type);
handler->ssrc_hash = create_ssrc_hash_full(__ssrc_handler_transcode_new, handler);
ilog(LOG_DEBUG, "Created transcode context for " STR_FORMAT " -> " STR_FORMAT "",
STR_FMT(&handler->source_pt.encoding_with_params),
STR_FMT(&dest->encoding_with_params));
check_output:;
// check if we have multiple decoders transcoding to the same output PT
struct codec_handler *output_handler = g_hash_table_lookup(output_transcoders,
@ -242,6 +271,31 @@ static void __make_passthrough_gsl(struct codec_handler *handler, GSList **handl
*handlers = g_slist_prepend(*handlers, handler);
}
// only called from codec_handlers_update()
static void __dtmf_dsp_shutdown(struct call_media *sink, int payload_type) {
if (!sink->codec_handlers)
return;
GList *list = g_hash_table_get_values(sink->codec_handlers);
for (GList *l = list; l; l = l->next) {
struct codec_handler *handler = l->data;
if (!handler->transcoder)
continue;
if (handler->dtmf_payload_type != payload_type)
continue;
if (handler->dtmf_scaler)
continue;
ilog(LOG_DEBUG, "Shutting down DTMF DSP for '" STR_FORMAT "' -> %i (not needed)",
STR_FMT(&handler->source_pt.encoding_with_params),
payload_type);
handler->dtmf_payload_type = -1;
}
g_list_free(list);
}
// call must be locked in W
void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
const struct sdp_ng_flags *flags)
@ -262,6 +316,10 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// that the sink specified. determine this first.
struct rtp_payload_type *pref_dest_codec = NULL;
int sink_transcoding = 0;
// keep track of telephone-event payload types. we hash them by clock rate
// in case there's several of them. the clock rates of the destination
// codec and the telephone-event codec must match.
GHashTable *dtmf_sinks = g_hash_table_new(g_direct_hash, g_direct_equal);
for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
__ensure_codec_def(pt, sink);
@ -285,8 +343,26 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// previously enabled on the sink, but no transcoding codecs are actually present,
// we can disable the transcoding engine.
if (MEDIA_ISSET(sink, TRANSCODE)) {
if (!g_hash_table_lookup(receiver->codec_names_send, &pt->encoding))
struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send,
&pt->payload_type);
if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) {
sink_transcoding = 1;
// can the sink receive RFC DTMF but the receiver can't send it?
if (pt->codec_def && pt->codec_def->dtmf) {
if (!g_hash_table_lookup(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate)))
g_hash_table_insert(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate),
pt);
}
}
}
else if (flags && flags->always_transcode) {
// with always-transcode, we must keep track of potential output DTMF payload
// types as well
if (pt->codec_def && pt->codec_def->dtmf) {
if (!g_hash_table_lookup(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate)))
g_hash_table_insert(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate),
pt);
}
}
}
@ -295,30 +371,52 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
if (MEDIA_ISSET(sink, TRANSCODE)) {
for (GList *l = sink->codecs_prefs_recv.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
GQueue *recv_pts = g_hash_table_lookup(receiver->codec_names_recv, &pt->encoding);
if (!recv_pts) {
struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send,
&pt->payload_type);
if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) {
sink_transcoding = 1;
// can the sink receive RFC DTMF but the receiver can't send it?
if (pt->codec_def && pt->codec_def->dtmf) {
if (!g_hash_table_lookup(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate)))
g_hash_table_insert(dtmf_sinks, GUINT_TO_POINTER(pt->clock_rate),
pt);
}
continue;
}
// even if the receiver can receive the same codec that the sink can
// send, we might still have it configured as a transcoder due to
// always-transcode in the offer
for (GList *k = recv_pts->head; k; k = k->next) {
// XXX codec_handlers can be converted to g_direct_hash table
int pt_num = GPOINTER_TO_INT(k->data);
struct codec_handler *ch_recv =
g_hash_table_lookup(sink->codec_handlers, &pt_num);
if (!ch_recv)
continue;
if (ch_recv->transcoder) {
sink_transcoding = 1;
break;
}
// XXX codec_handlers can be converted to g_direct_hash table
struct codec_handler *ch_recv =
g_hash_table_lookup(sink->codec_handlers, &recv_pt->payload_type);
if (!ch_recv)
continue;
if (ch_recv->transcoder) {
sink_transcoding = 1;
break;
}
}
}
ilog(LOG_DEBUG, "%i DTMF sink entries", g_hash_table_size(dtmf_sinks));
int dtmf_payload_type = -1;
if (g_hash_table_size(dtmf_sinks) && pref_dest_codec) {
// find the telephone-event codec entry with a matching clock rate
struct rtp_payload_type *pt = g_hash_table_lookup(dtmf_sinks,
GUINT_TO_POINTER(pref_dest_codec->clock_rate));
if (!pt)
ilog(LOG_INFO, "Not transcoding PCM DTMF tones to telephone-event packets as "
"no payload type with a matching clock rate for '" STR_FORMAT
"' was found", STR_FMT(&pref_dest_codec->encoding_with_params));
else {
dtmf_payload_type = pt->payload_type;
ilog(LOG_DEBUG, "Output DTMF payload type is %i", dtmf_payload_type);
}
}
g_hash_table_destroy(dtmf_sinks);
// stop transcoding if we've determined that we don't need it
if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding) {
ilog(LOG_DEBUG, "Disabling transcoding engine (not needed)");
@ -403,8 +501,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// if the other side is transcoding, we may come across a receiver entry
// (recv->recv) that wasn't originally offered (recv->send). we must eliminate
// those
// XXX sufficient to check against payload type?
if (!g_hash_table_lookup(receiver->codec_names_send, &pt->encoding)) {
if (!g_hash_table_lookup(receiver->codecs_send, &pt->payload_type)) {
ilog(LOG_DEBUG, "Eliminating transcoded codec " STR_FORMAT,
STR_FMT(&pt->encoding_with_params));
@ -452,8 +549,16 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
struct rtp_payload_type *dest_pt; // transcode to this
GQueue *dest_codecs = NULL;
if (!flags || !flags->always_transcode)
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
if (!flags || !flags->always_transcode) {
// we ignore output codec matches if we must transcode DTMF
if (dtmf_payload_type == -1)
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
}
else if (flags->always_transcode) {
// with always-transcode, we still accept DTMF payloads if possible
if (pt->codec_def && pt->codec_def->supplemental)
dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding);
}
if (dest_codecs) {
// the sink supports this codec - check offered formats
dest_pt = NULL;
@ -482,6 +587,8 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// XXX check format parameters as well
ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding_with_params));
__make_passthrough_gsl(handler, &passthrough_handlers);
if (pt->codec_def && pt->codec_def->dtmf)
__dtmf_dsp_shutdown(sink, pt->payload_type);
goto next;
}
@ -502,7 +609,7 @@ transcode:;
dest_pt->bitrate = reverse_pt->bitrate;
}
MEDIA_SET(receiver, TRANSCODE);
__make_transcoder(handler, dest_pt, output_transcoders);
__make_transcoder(handler, dest_pt, output_transcoders, dtmf_payload_type);
next:
l = l->next;
@ -539,10 +646,10 @@ next:
// if the sink does not support DTMF but we can receive it, we must transcode
// DTMF event packets to PCM. this requires all codecs to be transcoded to the
// sink's preferred destination codec.
if (!transcode_dtmf || !pref_dest_codec)
if ((!transcode_dtmf && dtmf_payload_type == -1) || !pref_dest_codec)
__make_passthrough_ssrc(handler);
else
__make_transcoder(handler, pref_dest_codec, output_transcoders);
__make_transcoder(handler, pref_dest_codec, output_transcoders, dtmf_payload_type);
passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers);
}
@ -724,7 +831,7 @@ static void __output_rtp(struct media_packet *mp, struct codec_ssrc_handler *ch,
char *buf, // malloc'd, room for rtp_header + filled-in payload
unsigned int payload_len,
unsigned long payload_ts,
int marker, int seq, int seq_inc)
int marker, int seq, int seq_inc, int payload_type)
{
struct rtp_header *rh = (void *) buf;
struct ssrc_ctx *ssrc_out = mp->ssrc_out;
@ -733,7 +840,9 @@ static void __output_rtp(struct media_packet *mp, struct codec_ssrc_handler *ch,
unsigned long ts = payload_ts;
ZERO(*rh);
rh->v_p_x_cc = 0x80;
rh->m_pt = handler->dest_pt.payload_type | (marker ? 0x80 : 0);
if (payload_type == -1)
payload_type = handler->dest_pt.payload_type;
rh->m_pt = payload_type | (marker ? 0x80 : 0);
if (seq != -1)
rh->seq_num = htons(seq);
else
@ -784,17 +893,79 @@ static void __output_rtp(struct media_packet *mp, struct codec_ssrc_handler *ch,
atomic64_set(&ssrc_out->last_ts, ts);
}
static struct codec_ssrc_handler *__output_ssrc_handler(struct codec_ssrc_handler *ch, struct media_packet *mp) {
struct codec_handler *handler = ch->handler;
if (handler->output_handler == handler)
return ch;
// our encoder is in a different codec handler
ilog(LOG_DEBUG, "Switching context from decoder to encoder");
handler = handler->output_handler;
struct codec_ssrc_handler *new_ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, handler->ssrc_hash);
if (G_UNLIKELY(!new_ch)) {
ilog(LOG_ERR, "Switched from input to output codec context, but no codec handler present");
return ch;
}
return new_ch;
}
static void packet_dtmf_fwd(struct codec_ssrc_handler *ch, struct transcode_packet *packet,
struct media_packet *mp, int seq_inc)
{
int payload_type = -1; // take from handler's output config
if (ch->handler->dtmf_scaler) {
// this is actually a DTMF -> PCM handler
// grab our underlying PCM transcoder
struct codec_ssrc_handler *output_ch = __output_ssrc_handler(ch, mp);
// init some vars
if (!ch->first_ts)
ch->first_ts = output_ch->first_ts;
// the correct output TS is the encoder's FIFO PTS at the start of the DTMF
// event. however, we must shift the FIFO PTS forward as the DTMF event goes on
// as the DTMF event replaces the audio samples. therefore we must remember
// the TS at the start of the event and the last seen event duration.
if (ch->dtmf_ts != packet->ts) {
// this is a new event
ch->dtmf_ts = packet->ts; // start TS
ch->last_dtmf_event_ts = 0; // last shifted FIFO PTS
}
unsigned long ts = output_ch->encoder->fifo_pts;
// roll back TS to start of event
ts -= ch->last_dtmf_event_ts;
// adjust to output RTP TS
unsigned long packet_ts = ts + output_ch->first_ts;
ilog(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);
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);
dtmf->duration = htons(duration);
// shift forward our output RTP TS
output_ch->encoder->fifo_pts = ts + duration;
ch->last_dtmf_event_ts = duration;
}
payload_type = ch->handler->dtmf_payload_type;
}
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->ignore_seq) // inject original seq
__output_rtp(mp, ch, packet->handler ? : ch->handler, buf, packet->payload->len, packet->ts,
packet->marker, packet->p.seq, -1);
packet->marker, packet->p.seq, -1, payload_type);
else // use our own sequencing
__output_rtp(mp, ch, packet->handler ? : ch->handler, buf, packet->payload->len, packet->ts,
packet->marker, -1, seq_inc);
packet->marker, -1, seq_inc, payload_type);
}
static int packet_dtmf(struct codec_ssrc_handler *ch, struct transcode_packet *packet, struct media_packet *mp)
{
@ -812,11 +983,12 @@ static int packet_dtmf(struct codec_ssrc_handler *ch, struct transcode_packet *p
packet_dtmf_fwd(ch, packet, mp, 0);
return 0;
}
static void packet_dtmf_dup(struct codec_ssrc_handler *ch, struct transcode_packet *packet,
static int packet_dtmf_dup(struct codec_ssrc_handler *ch, struct transcode_packet *packet,
struct media_packet *mp)
{
if (!mp->call->block_dtmf && !mp->media->monologue->block_dtmf)
packet_dtmf_fwd(ch, packet, mp, 0);
return 0;
}
static int handler_func_dtmf(struct codec_handler *h, struct media_packet *mp) {
@ -1003,15 +1175,33 @@ static struct ssrc_entry *__ssrc_handler_new(void *p) {
return &ch->h;
}
static void __dtmf_dsp_callback(void *ptr, int code, int level, int delay) {
struct codec_ssrc_handler *ch = ptr;
struct dtmf_event *ev = g_slice_alloc(sizeof(*ev));
*ev = (struct dtmf_event) { .code = code, .volume = level, .ts = ch->last_dtmf_event_ts + delay };
ch->last_dtmf_event_ts = ev->ts;
ev->ts = av_rescale(ev->ts, ch->encoder_format.clockrate, ch->dtmf_format.clockrate);
ilog(LOG_DEBUG, "DTMF event state change: code %i, volume %i, TS %lu",
ev->code, ev->volume, (unsigned long) ev->ts);
g_queue_push_tail(&ch->dtmf_events, ev);
}
static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
struct codec_handler *h = p;
ilog(LOG_DEBUG, "Creating SSRC transcoder from %s/%u/%i to "
"%s/%u/%i",
h->source_pt.codec_def->rtpname, h->source_pt.clock_rate,
h->source_pt.channels,
h->dest_pt.codec_def->rtpname, h->dest_pt.clock_rate,
h->dest_pt.channels);
if (h->dtmf_scaler)
ilog(LOG_DEBUG, "Creating SSRC DTMF transcoder from %s/%u/%i to "
"PT %i",
h->source_pt.codec_def->rtpname, h->source_pt.clock_rate,
h->source_pt.channels,
h->dtmf_payload_type);
else
ilog(LOG_DEBUG, "Creating SSRC transcoder from %s/%u/%i to "
"%s/%u/%i",
h->source_pt.codec_def->rtpname, h->source_pt.clock_rate,
h->source_pt.channels,
h->dest_pt.codec_def->rtpname, h->dest_pt.clock_rate,
h->dest_pt.channels);
struct codec_ssrc_handler *ch = obj_alloc0("codec_ssrc_handler", sizeof(*ch), __free_ssrc_handler);
ch->handler = h;
@ -1033,6 +1223,16 @@ static struct ssrc_entry *__ssrc_handler_transcode_new(void *p) {
&enc_format, &ch->encoder_format, &h->dest_pt.format_parameters))
goto err;
if (h->dtmf_payload_type != -1) {
ilog(LOG_DEBUG, "Inserting DTMF DSP for output payload type %i", h->dtmf_payload_type);
ch->dtmf_format = (format_t) { .clockrate = 8000, .channels = 1, .format = AV_SAMPLE_FMT_S16 };
ch->dtmf_dsp = dtmf_rx_init(NULL, NULL, NULL);
if (!ch->dtmf_dsp)
ilog(LOG_ERR, "Failed to allocate DTMF RX context");
else
dtmf_rx_set_realtime_callback(ch->dtmf_dsp, __dtmf_dsp_callback, ch);
}
ch->decoder = decoder_new_fmtp(h->source_pt.codec_def, h->source_pt.clock_rate, h->source_pt.channels,
&ch->encoder_format, &h->source_pt.format_parameters);
if (!ch->decoder)
@ -1074,6 +1274,10 @@ static void __free_ssrc_handler(void *chp) {
}
if (ch->sample_buffer)
g_string_free(ch->sample_buffer, TRUE);
if (ch->dtmf_dsp)
dtmf_rx_free(ch->dtmf_dsp);
resample_shutdown(&ch->dtmf_resampler);
g_queue_clear_full(&ch->dtmf_events, dtmf_event_free);
}
static int __packet_encoded(encoder_t *enc, void *u1, void *u2) {
@ -1089,7 +1293,8 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) {
while (1) {
// figure out how big of a buffer we need
unsigned int payload_len = MAX(enc->avpkt.size, ch->bytes_per_packet);
unsigned int payload_len = MAX(MAX(enc->avpkt.size, ch->bytes_per_packet),
sizeof(struct telephone_event_payload));
unsigned int pkt_len = sizeof(struct rtp_header) + payload_len + RTP_BUFFER_TAIL_ROOM;
// prepare our buffers
char *buf = malloc(pkt_len);
@ -1110,12 +1315,30 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) {
}
ilog(LOG_DEBUG, "Received packet of %i bytes from packetizer", inout.len);
__output_rtp(mp, ch, ch->handler, buf, inout.len, ch->first_ts
+ enc->avpkt.pts / enc->def->clockrate_mult,
ch->rtp_mark ? 1 : 0, -1, 0);
mp->ssrc_out->parent->seq_diff++;
//mp->iter_out++;
ch->rtp_mark = 0;
unsigned int repeats = 0;
int is_dtmf = dtmf_event_payload(&inout, (uint64_t *) &enc->avpkt.pts, enc->avpkt.duration,
&ch->dtmf_event, &ch->dtmf_events);
if (is_dtmf == 1)
ch->rtp_mark = 1; // DTMF start event
else if (is_dtmf == 3)
repeats = 2; // DTMF end event
do {
char *send_buf = buf;
if (repeats > 0) {
// need to duplicate the payload as __output_rtp consumes it
send_buf = malloc(pkt_len);
memcpy(send_buf, buf, pkt_len);
}
__output_rtp(mp, ch, ch->handler, send_buf, inout.len, ch->first_ts
+ enc->avpkt.pts / enc->def->clockrate_mult,
ch->rtp_mark ? 1 : 0, -1, 0,
is_dtmf ? ch->handler->dtmf_payload_type : -1);
mp->ssrc_out->parent->seq_diff++;
//mp->iter_out++;
ch->rtp_mark = 0;
} while (repeats--);
if (ret == 0) {
// no more to go
@ -1130,6 +1353,48 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) {
return 0;
}
static void __dtmf_detect(struct codec_ssrc_handler *ch, AVFrame *frame) {
if (!ch->dtmf_dsp)
return;
if (ch->handler->dtmf_payload_type == -1) {
ch->dtmf_event.code = 0;
return;
}
AVFrame *dsp_frame = resample_frame(&ch->dtmf_resampler, frame, &ch->dtmf_format);
if (!dsp_frame) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Failed to resample audio for DTMF DSP");
return;
}
ilog(LOG_DEBUG, "DTMF detect, TS %lu -> %lu, %u -> %u samples",
(unsigned long) frame->pts,
(unsigned long) dsp_frame->pts,
frame->nb_samples,
dsp_frame->nb_samples);
if (dsp_frame->pts > ch->dtmf_ts)
dtmf_rx_fillin(ch->dtmf_dsp, dsp_frame->pts - ch->dtmf_ts);
else if (dsp_frame->pts < ch->dtmf_ts)
ilog(LOG_ERR | LOG_FLAG_LIMIT, "DTMF TS seems to run backwards (%lu < %lu)",
(unsigned long) dsp_frame->pts,
(unsigned long) ch->dtmf_ts);
int num_samples = dsp_frame->nb_samples;
int16_t *samples = (void *) dsp_frame->extended_data[0];
while (num_samples > 0) {
int ret = dtmf_rx(ch->dtmf_dsp, samples, num_samples);
if (ret < 0 || ret >= num_samples) {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "DTMF DSP returned error %i", ret);
break;
}
samples += num_samples - ret;
num_samples = ret;
}
ch->dtmf_ts = dsp_frame->pts + dsp_frame->nb_samples;
av_frame_free(&dsp_frame);
}
static int __packet_decoded(decoder_t *decoder, AVFrame *frame, void *u1, void *u2) {
struct codec_ssrc_handler *ch = u1;
struct media_packet *mp = u2;
@ -1138,16 +1403,8 @@ static int __packet_decoded(decoder_t *decoder, AVFrame *frame, void *u1, void *
(unsigned long long) frame->pts, frame->nb_samples);
// switch from input codec context to output context if necessary
struct codec_handler *handler = ch->handler;
if (handler->output_handler != handler) {
// our encoder is in a different codec handler
ilog(LOG_DEBUG, "Switching context from decoder to encoder");
handler = handler->output_handler;
struct codec_ssrc_handler *new_ch = get_ssrc(mp->ssrc_in->parent->h.ssrc, handler->ssrc_hash);
if (G_UNLIKELY(!new_ch)) {
ilog(LOG_ERR, "Switched from input to output codec context, but no codec handler present");
return -1;
}
struct codec_ssrc_handler *new_ch = __output_ssrc_handler(ch, mp);
if (new_ch != ch) {
// copy some essential parameters
if (!new_ch->first_ts)
new_ch->first_ts = ch->first_ts;
@ -1155,6 +1412,8 @@ static int __packet_decoded(decoder_t *decoder, AVFrame *frame, void *u1, void *
ch = new_ch;
}
__dtmf_detect(ch, frame);
encoder_input_fifo(ch->encoder, frame, __packet_encoded, ch, mp);
av_frame_free(&frame);
@ -1192,6 +1451,11 @@ static int handler_func_transcode(struct codec_handler *h, struct media_packet *
packet->rtp = *mp->rtp;
packet->handler = h;
if (h->dtmf_scaler) {
packet->func = packet_dtmf;
packet->dup_func = packet_dtmf_dup;
}
int ret = __handler_func_sequencer(mp, packet);
//ilog(LOG_DEBUG, "tc iters: in %u out %u", mp->iter_in, mp->iter_out);
@ -1268,7 +1532,40 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct
return pt;
}
// handle special meaning "clock rate == 1": add one instance of this PT for each clock rate
// that is already present
static int __codec_synth_transcode_options(struct rtp_payload_type *pt, struct sdp_ng_flags *flags,
struct call_media *media)
{
if (pt->clock_rate != 1)
return 0;
struct call *call = media->call;
GHashTable *clockrates = g_hash_table_new(g_direct_hash, g_direct_equal);
// special handling - add one instance for each clock rate that is present
for (GList *k = media->codecs_prefs_recv.head; k; k = k->next) {
struct rtp_payload_type *pt_r = k->data;
if (g_hash_table_lookup(clockrates, GUINT_TO_POINTER(pt_r->clock_rate)))
continue;
char *pt_s;
if (asprintf(&pt_s, STR_FORMAT "/%u", STR_FMT(&pt->encoding), pt_r->clock_rate) < 0)
continue;
pt_s = call_strdup(call, pt_s);
// XXX optimise this -^ call buffer can probably be replaced with a gstringchunk
// and made lock free
g_hash_table_insert(clockrates, GUINT_TO_POINTER(pt_r->clock_rate), (void *) 1);
str pt_str;
str_init(&pt_str, pt_s);
ilog(LOG_DEBUG, "Synthesised transcoding option for '%s'", pt_s);
g_queue_push_tail(&flags->codec_transcode, str_slice_dup(&pt_str));
}
payload_type_free(pt);
g_hash_table_destroy(clockrates);
return 1;
}
#endif
@ -1384,7 +1681,7 @@ static void __codec_options_set(struct rtp_payload_type *pt, GHashTable *codec_s
return;
}
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, const struct sdp_ng_flags *flags)
GQueue *types, struct sdp_ng_flags *flags)
{
if (!flags)
return;
@ -1480,11 +1777,13 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
if (!pt)
continue;
if (__codec_synth_transcode_options(pt, flags, media))
continue;
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' added for transcoding with payload type %u",
STR_FMT(&pt->encoding_with_params), pt->payload_type);
__rtp_payload_type_add_recv(media, pt);
}
#endif
g_hash_table_destroy(removed);


+ 69
- 0
daemon/dtmf.c View File

@ -71,3 +71,72 @@ int dtmf_event(struct media_packet *mp, str *payload, int clockrate) {
return ret;
}
void dtmf_event_free(void *e) {
g_slice_free1(sizeof(struct dtmf_event), e);
}
// returns: 0 = no DTMF. 1 = DTMF start event. 2 = DTMF in progress. 3 = DTMF end event.
int dtmf_event_payload(str *buf, uint64_t *pts, uint64_t duration, struct dtmf_event *cur_event, GQueue *events) {
// do we have a relevant state change?
struct dtmf_event prev_event = *cur_event;
while (events->length) {
struct dtmf_event *ev = g_queue_peek_head(events);
ilog(LOG_DEBUG, "Next DTMF event starts at %lu. PTS now %li", (unsigned long) ev->ts,
(unsigned long) *pts);
if (ev->ts > *pts)
break; // future event
ilog(LOG_DEBUG, "DTMF state change at %lu: %i -> %i, duration %lu", (unsigned long) ev->ts,
cur_event->code, ev->code, (unsigned long) duration);
g_queue_pop_head(events);
*cur_event = *ev;
dtmf_event_free(ev);
cur_event->ts = *pts; // canonicalise start TS
}
int ret = 2; // normal: in progress
if (cur_event->code == 0) {
if (prev_event.code == 0)
return 0;
// state change from DTMF back to audio. send DTMF end code.
ret = 3;
cur_event = &prev_event;
}
else if (prev_event.code == 0)
ret = 1; // start event
int dtmf_code = -1;
if (cur_event->code >= '0' && cur_event->code <= '9')
dtmf_code = cur_event->code - '0';
else if (cur_event->code == '*')
dtmf_code = 10;
else if (cur_event->code == '#')
dtmf_code = 11;
else if (cur_event->code >= 'A' && cur_event->code <= 'D')
dtmf_code = cur_event->code - 'A' + 12;
else {
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unknown DTMF event code %i", cur_event->code);
return 0;
}
// replace audio RTP frame with DTMF payload
struct telephone_event_payload *ev_pt = (void *) buf->s;
buf->len = sizeof(*ev_pt);
ZERO(*ev_pt);
ev_pt->event = dtmf_code;
if (cur_event->volume > 0)
ev_pt->volume = 0;
else if (cur_event->volume >= -63)
ev_pt->volume = -1 * cur_event->volume;
else
ev_pt->volume = 63;
ev_pt->end = (ret == 3) ? 1 : 0;
ev_pt->duration = htons(*pts - cur_event->ts + duration);
// fix up timestamp
*pts = cur_event->ts;
return ret;
}

+ 1
- 0
debian/control View File

@ -32,6 +32,7 @@ Build-Depends:
libpcap0.8-dev,
libpcre3-dev,
libsocket6-perl,
libspandsp-dev,
libssl-dev (>= 1.0.1),
libswresample-dev (>= 6:10),
libsystemd-dev,


+ 1
- 1
include/call.h View File

@ -417,7 +417,7 @@ struct call *call_get_opmode(const str *callid, enum call_opmode opmode);
struct call_monologue *call_get_mono_dialogue(struct call *call, const str *fromtag, const str *totag,
const str *viabranch);
struct call *call_get(const str *callid);
int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, const struct sdp_ng_flags *flags);
int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, struct sdp_ng_flags *flags);
int call_delete_branch(const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay);
void call_destroy(struct call *);


+ 3
- 1
include/codec.h View File

@ -25,9 +25,11 @@ typedef int codec_handler_func(struct codec_handler *, struct media_packet *);
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;
codec_handler_func *func;
int kernelize:1;
int transcoder:1;
int dtmf_scaler:1;
struct ssrc_hash *ssrc_hash;
struct codec_handler *output_handler; // == self, or other PT handler
@ -55,7 +57,7 @@ void codec_add_raw_packet(struct media_packet *mp);
void codec_packet_free(void *);
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media,
GQueue *types, const struct sdp_ng_flags *flags);
GQueue *types, struct sdp_ng_flags *flags);
// special return value `(void *) 0x1` to signal type mismatch
struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media);


+ 10
- 0
include/dtmf.h View File

@ -1,12 +1,22 @@
#ifndef _DTMF_H_
#define _DTMF_H_
#include <inttypes.h>
#include <glib.h>
#include "str.h"
struct media_packet;
struct dtmf_event {
int code;
int volume;
uint64_t ts;
};
int dtmf_event(struct media_packet *, str *, int);
int dtmf_event_payload(str *, uint64_t *, uint64_t, struct dtmf_event *, GQueue *);
void dtmf_event_free(void *);
#endif

+ 18
- 1
lib/codeclib.c View File

@ -57,6 +57,9 @@ static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static const char *dtmf_decoder_init(decoder_t *, const str *);
static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out);
static const char *dtmf_encoder_init(encoder_t *enc, const str *);
static int dtmf_encoder_input(encoder_t *enc, AVFrame **frame);
static void dtmf_encoder_close(encoder_t *enc);
@ -82,6 +85,9 @@ static const codec_type_t codec_type_amr = {
static const codec_type_t codec_type_dtmf = {
.decoder_init = dtmf_decoder_init,
.decoder_input = dtmf_decoder_input,
.encoder_init = dtmf_encoder_init,
.encoder_input = dtmf_encoder_input,
.encoder_close = dtmf_encoder_close,
};
#ifdef HAVE_BCG729
@ -350,7 +356,7 @@ static codec_def_t __codec_defs[] = {
.media_type = MT_AUDIO,
.supplemental = 1,
.dtmf = 1,
.default_clockrate = 8000,
.default_clockrate = 1, // special handling
.default_channels = 1,
.default_fmtp = "0-15",
.codec_type = &codec_type_dtmf,
@ -1785,3 +1791,14 @@ static int dtmf_decoder_input(decoder_t *dec, const str *data, GQueue *out) {
return 0;
}
static const char *dtmf_encoder_init(encoder_t *enc, const str *fmtp) {
return NULL;
}
static int dtmf_encoder_input(encoder_t *enc, AVFrame **frame) {
return 0;
}
static void dtmf_encoder_close(encoder_t *enc) {
}

+ 1
- 0
t/.gitignore View File

@ -49,3 +49,4 @@ tests-preload.so
timerthread.c
media_player.c
dtmflib.c
test-dtmf-detect

+ 6
- 2
t/Makefile View File

@ -16,6 +16,7 @@ CFLAGS+= $(shell pkg-config --cflags libavformat)
CFLAGS+= $(shell pkg-config --cflags libavutil)
CFLAGS+= $(shell pkg-config --cflags libswresample)
CFLAGS+= $(shell pkg-config --cflags libavfilter)
CFLAGS+= $(shell pkg-config --cflags spandsp)
CFLAGS+= -DWITH_TRANSCODING
CFLAGS+= $(shell pkg-config --cflags zlib)
CFLAGS+= $(shell pkg-config --cflags json-glib-1.0)
@ -43,6 +44,7 @@ LDLIBS+= $(shell pkg-config --libs libavformat)
LDLIBS+= $(shell pkg-config --libs libavutil)
LDLIBS+= $(shell pkg-config --libs libswresample)
LDLIBS+= $(shell pkg-config --libs libavfilter)
LDLIBS+= $(shell pkg-config --libs spandsp)
LDLIBS+= $(shell pkg-config --libs zlib)
LDLIBS+= $(shell pkg-config --libs json-glib-1.0)
LDLIBS+= -lpcap
@ -60,7 +62,7 @@ DAEMONSRCS= crypto.c ssrc.c aux.c rtp.c
HASHSRCS=
ifeq ($(with_transcoding),yes)
SRCS+= transcode-test.c
SRCS+= transcode-test.c test-dtmf-detect.c
ifeq ($(with_amr_tests),yes)
SRCS+= amr-decode-test.c amr-encode-test.c
endif
@ -84,7 +86,7 @@ include .depend
TESTS= bitstr-test aes-crypt payload-tracker-test const_str_hash-test.strhash
ifeq ($(with_transcoding),yes)
TESTS+= transcode-test
TESTS+= transcode-test test-dtmf-detect
ifeq ($(with_amr_tests),yes)
TESTS+= amr-decode-test amr-encode-test
endif
@ -113,6 +115,8 @@ amr-decode-test: amr-decode-test.o $(COMMONOBJS) codeclib.o resample.o dtmflib.o
amr-encode-test: amr-encode-test.o $(COMMONOBJS) codeclib.o resample.o dtmflib.o
test-dtmf-detect: test-dtmf-detect.o
aes-crypt: aes-crypt.o $(COMMONOBJS) crypto.o
transcode-test: transcode-test.o $(COMMONOBJS) codeclib.o resample.o codec.o ssrc.o call.o ice.o aux.o \


+ 377
- 4
t/auto-daemon-tests.pl View File

@ -2968,6 +2968,26 @@ snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160));
($seq, $ssrc) = rcv($sock_b, $port_a, rtpm(0, -1, 3000, -1, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, $seq+1, 3160, $ssrc, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1002, 3000+160*2, 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"));
# DTMF not detected yet
rcv($sock_b, $port_a, rtpm(0, $seq+2, 3000+160*2, $ssrc, "\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"));
snd($sock_a, $port_b, rtp(0, 1003, 3000+160*3, 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"));
# DTMF detection kicking in mid-frame
rcv($sock_b, $port_a, rtpm(0, $seq+3, 3000+160*3, $ssrc, "\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"));
snd($sock_a, $port_b, rtp(0, 1004, 3000+160*4, 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"));
# DTMF detected now
rcv($sock_b, $port_a, rtpm(96 | 0x80, $seq+4, 3000+160*4, $ssrc, "\x08\x0f\x00\xa0")); # start event 8, vol -15, duration 160
snd($sock_a, $port_b, rtp(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
# reverting to audio, but DTMF event still progressing
rcv($sock_b, $port_a, rtpm(96, $seq+5, 3000+160*4, $ssrc, "\x08\x0f\x01\x40")); # event 8, vol -15, duration 320
snd($sock_a, $port_b, rtp(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
# end event, 3 times
rcv($sock_b, $port_a, rtpm(96, $seq+6, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+7, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+8, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
# audio passing through again
snd($sock_a, $port_b, rtp(0, 1007, 3000+160*7, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, $seq+9, 3000+160*7, $ssrc, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 2000, 4000, 0x5678, "\x00" x 160));
($seq, $ssrc) = rcv($sock_a, $port_b, rtpm(0, -1, 4000, -1, "\x00" x 160));
@ -3045,10 +3065,30 @@ a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160));
($seq, $ssrc) = rcv($sock_b, $port_a, rtpm(8, -1, 3000, -1, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, $seq+1, 3160, $ssrc, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1000, 3000+160*0, 0x1234, "\x00" x 160));
($seq, $ssrc) = rcv($sock_b, $port_a, rtpm(8, -1, 3000+160*0, -1, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3000+160*1, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, $seq+1, 3000+160*1, $ssrc, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1002, 3000+160*2, 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"));
# DTMF not detected yet
rcv($sock_b, $port_a, rtpm(8, $seq+2, 3000+160*2, $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"));
snd($sock_a, $port_b, rtp(0, 1003, 3000+160*3, 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"));
# DTMF detection kicking in mid-frame
rcv($sock_b, $port_a, rtpm(8, $seq+3, 3000+160*3, $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"));
snd($sock_a, $port_b, rtp(0, 1004, 3000+160*4, 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"));
# DTMF detected now
rcv($sock_b, $port_a, rtpm(96 | 0x80, $seq+4, 3000+160*4, $ssrc, "\x08\x0f\x00\xa0")); # start event 8, vol -15, duration 160
snd($sock_a, $port_b, rtp(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
# reverting to audio, but DTMF event still progressing
rcv($sock_b, $port_a, rtpm(96, $seq+5, 3000+160*4, $ssrc, "\x08\x0f\x01\x40")); # event 8, vol -15, duration 320
snd($sock_a, $port_b, rtp(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
# end event, 3 times
rcv($sock_b, $port_a, rtpm(96, $seq+6, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+7, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+8, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
# audio passing through again
snd($sock_a, $port_b, rtp(0, 1007, 3000+160*7, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, $seq+9, 3000+160*7, $ssrc, "\x2a" x 160));
snd($sock_b, $port_a, rtp(8, 2000, 4000, 0x5678, "\x2a" x 160));
($seq, $ssrc) = rcv($sock_a, $port_b, rtpm(0, -1, 4000, -1, "\x00" x 160));
@ -3124,6 +3164,26 @@ snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160));
($seq, $ssrc) = rcv($sock_b, $port_a, rtpm(8, -1, 3000, -1, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, $seq+1, 3160, $ssrc, "\x2a" x 160));
snd($sock_a, $port_b, rtp(0, 1002, 3000+160*2, 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"));
# DTMF not detected yet
rcv($sock_b, $port_a, rtpm(8, $seq+2, 3000+160*2, $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"));
snd($sock_a, $port_b, rtp(0, 1003, 3000+160*3, 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"));
# DTMF detection kicking in mid-frame
rcv($sock_b, $port_a, rtpm(8, $seq+3, 3000+160*3, $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"));
snd($sock_a, $port_b, rtp(0, 1004, 3000+160*4, 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"));
# DTMF detected now
rcv($sock_b, $port_a, rtpm(96 | 0x80, $seq+4, 3000+160*4, $ssrc, "\x08\x0f\x00\xa0")); # start event 8, vol -15, duration 160
snd($sock_a, $port_b, rtp(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
# reverting to audio, but DTMF event still progressing
rcv($sock_b, $port_a, rtpm(96, $seq+5, 3000+160*4, $ssrc, "\x08\x0f\x01\x40")); # event 8, vol -15, duration 320
snd($sock_a, $port_b, rtp(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
# end event, 3 times
rcv($sock_b, $port_a, rtpm(96, $seq+6, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+7, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
rcv($sock_b, $port_a, rtpm(96, $seq+8, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0")); # end event 8, vol -15, duration 480
# audio passing through again
snd($sock_a, $port_b, rtp(0, 1007, 3000+160*7, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(8, $seq+9, 3000+160*7, $ssrc, "\x2a" x 160));
snd($sock_b, $port_a, rtp(8, 2000, 4000, 0x5678, "\x2a" x 160));
($seq, $ssrc) = rcv($sock_a, $port_b, rtpm(0, -1, 4000, -1, "\x00" x 160));
@ -3144,6 +3204,319 @@ rcv($sock_a, $port_b, rtpm(0, $seq+5, 4000+800, $ssrc, "\x00" x 160));
# test telephone-event synth options
new_call;
offer('several clock rates input w/ transcode DTMF',
{ ICE => 'remove', replace => ['origin'],
codec => { transcode => ['telephone-event'] } }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 2000 RTP/AVP 0 96 8 97 9
c=IN IP4 198.51.100.1
a=rtpmap:96 opus/48000
a=rtpmap:8 PCMA/8000
a=rtpmap:97 speex/16000
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 0 96 8 97 9 98 99 100
c=IN IP4 203.0.113.1
a=rtpmap:0 PCMU/8000
a=rtpmap:96 opus/48000
a=rtpmap:8 PCMA/8000
a=rtpmap:97 speex/16000
a=rtpmap:9 G722/8000
a=rtpmap:98 telephone-event/8000
a=rtpmap:99 telephone-event/48000
a=rtpmap:100 telephone-event/16000
a=fmtp:98 0-15
a=fmtp:99 0-15
a=fmtp:100 0-15
a=sendrecv
a=rtcp:PORT
SDP
($sock_a, $sock_b) = new_call([qw(198.51.100.1 7050)], [qw(198.51.100.3 7052)]);
($port_a) = offer('reverse DTMF transcoding - no-op', { ICE => 'remove', replace => ['origin'],
flags => ['always transcode'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 7050 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.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=fmtp:101 0-15
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('reverse DTMF transcoding - no-op', { replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 7052 RTP/AVP 0 101
c=IN IP4 198.51.100.3
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.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=fmtp:101 0-15
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1000, 3000, 0x1234, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1001, 3160, 0x1234, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1002, 3000+160*2, 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, 1002, 3000+160*2, 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"));
snd($sock_a, $port_b, rtp(0, 1003, 3000+160*3, 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, 1003, 3000+160*3, 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"));
snd($sock_a, $port_b, rtp(0, 1004, 3000+160*4, 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"));
rcv($sock_b, $port_a, rtpm(0, 1004, 3000+160*4, 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"));
snd($sock_a, $port_b, rtp(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
snd($sock_a, $port_b, rtp(101 | 0x80, 1007, 3000+160*7, 0x1234, "\x08\x10\x00\xa0"));
rcv($sock_b, $port_a, rtpm(101 | 0x80, 1007, 3000+160*7, 0x1234, "\x08\x10\x00\xa0"));
snd($sock_b, $port_a, rtp(0, 1000, 3000, 0x3456, "\x00" x 160));
($ssrc) = rcv($sock_a, $port_b, rtpm(0, 1000, 3000, -1, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 1001, 3160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 1001, 3160, $ssrc, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 1002, 3000+160*2, 0x3456, "\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_a, $port_b, rtpm(0, 1002, 3000+160*2, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1003, 3000+160*3, 0x3456, "\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_a, $port_b, rtpm(0, 1003, 3000+160*3, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1004, 3000+160*4, 0x3456, "\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"));
rcv($sock_a, $port_b, rtpm(0, 1004, 3000+160*4, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1005, 3000+160*5, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 1005, 3000+160*5, $ssrc, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 1006, 3000+160*6, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, 1006, 3000+160*6, $ssrc, "\x00" x 160));
snd($sock_b, $port_a, rtp(101 | 0x80, 1007, 3000+160*7, 0x3456, "\x08\x10\x00\xa0"));
rcv($sock_a, $port_b, rtpm(101 | 0x80, 1007, 3000+160*7, $ssrc, "\x08\x10\x00\xa0"));
($sock_a, $sock_b) = new_call([qw(198.51.100.1 7050)], [qw(198.51.100.3 7052)]);
($port_a) = offer('reverse DTMF transcoding - active', { ICE => 'remove', replace => ['origin'],
flags => ['always transcode'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 7050 RTP/AVP 0 101
c=IN IP4 198.51.100.1
a=rtpmap:0 PCMU/8000
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.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=fmtp:101 0-15
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('reverse DTMF transcoding - active', { replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 7052 RTP/AVP 0
c=IN IP4 198.51.100.3
a=rtpmap:0 PCMU/8000
a=fmtp:101 0-15
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.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=fmtp:101 0-15
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(0, 1000, 3000, 0x1234, "\x00" x 160));
($seq, $ssrc) = rcv($sock_b, $port_a, rtpm(0, -1, 3000, -1, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1001, 3160, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, $seq+1, 3160, $ssrc, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1002, 3000+160*2, 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, $seq+2, 3000+160*2, $ssrc, "\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"));
snd($sock_a, $port_b, rtp(0, 1003, 3000+160*3, 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, $seq+3, 3000+160*3, $ssrc, "\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"));
snd($sock_a, $port_b, rtp(0, 1004, 3000+160*4, 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"));
rcv($sock_b, $port_a, rtpm(0, $seq+4, 3000+160*4, $ssrc, "\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"));
snd($sock_a, $port_b, rtp(0, 1005, 3000+160*5, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, $seq+5, 3000+160*5, $ssrc, "\x00" x 160));
snd($sock_a, $port_b, rtp(0, 1006, 3000+160*6, 0x1234, "\x00" x 160));
rcv($sock_b, $port_a, rtpm(0, $seq+6, 3000+160*6, $ssrc, "\x00" x 160));
snd($sock_a, $port_b, rtp(101 | 0x80, 1007, 3000+160*7, 0x1234, "\x08\x10\x00\xa0"));
rcv($sock_b, $port_a, rtpm(0, $seq+7, 3000+160*7, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1000, 3000, 0x3456, "\x00" x 160));
($seq, $ssrc) = rcv($sock_a, $port_b, rtpm(0, -1, 3000, -1, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 1001, 3160, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+1, 3160, $ssrc, "\x00" x 160));
snd($sock_b, $port_a, rtp(0, 1002, 3000+160*2, 0x3456, "\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"));
# DTMF not detected yet
rcv($sock_a, $port_b, rtpm(0, $seq+2, 3000+160*2, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1003, 3000+160*3, 0x3456, "\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"));
# DTMF detection kicking in mid-frame
rcv($sock_a, $port_b, rtpm(0, $seq+3, 3000+160*3, $ssrc, "\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"));
snd($sock_b, $port_a, rtp(0, 1004, 3000+160*4, 0x3456, "\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"));
# DTMF detected now
rcv($sock_a, $port_b, rtpm(101 | 0x80, $seq+4, 3000+160*4, $ssrc, "\x08\x0f\x00\xa0"));
snd($sock_b, $port_a, rtp(0, 1005, 3000+160*5, 0x3456, "\x00" x 160));
# reverting to audio, but DTMF event still progressing
rcv($sock_a, $port_b, rtpm(101, $seq+5, 3000+160*4, $ssrc, "\x08\x0f\x01\x40"));
snd($sock_b, $port_a, rtp(0, 1006, 3000+160*6, 0x3456, "\x00" x 160));
# end event, 3 times
rcv($sock_a, $port_b, rtpm(101, $seq+6, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0"));
rcv($sock_a, $port_b, rtpm(101, $seq+7, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0"));
rcv($sock_a, $port_b, rtpm(101, $seq+8, 3000+160*4, $ssrc, "\x08\x8f\x01\xe0"));
# audio passing through again
snd($sock_b, $port_a, rtp(0, 1007, 3000+160*7, 0x3456, "\x00" x 160));
rcv($sock_a, $port_b, rtpm(0, $seq+9, 3000+160*7, $ssrc, "\x00" x 160));
($sock_a, $sock_b) = new_call([qw(198.51.100.1 7060)], [qw(198.51.100.3 7062)]);
($port_a) = offer('DTMF scaling', { ICE => 'remove', replace => ['origin'],
codec => { transcode => ['PCMA', 'telephone-event/8000'] },
flags => ['always transcode'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.1
s=tester
t=0 0
m=audio 7060 RTP/AVP 100 101
c=IN IP4 198.51.100.1
a=rtpmap:100 PCMU/16000
a=rtpmap:101 telephone-event/16000
a=fmtp:101 0-15
a=sendrecv
----------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 100 101 8 96
c=IN IP4 203.0.113.1
a=rtpmap:100 PCMU/16000
a=rtpmap:101 telephone-event/16000
a=rtpmap:8 PCMA/8000
a=rtpmap:96 telephone-event/8000
a=fmtp:101 0-15
a=fmtp:96 0-15
a=sendrecv
a=rtcp:PORT
SDP
($port_b) = answer('DTMF scaling', { replace => ['origin'] }, <<SDP);
v=0
o=- 1545997027 1 IN IP4 198.51.100.3
s=tester
t=0 0
m=audio 7062 RTP/AVP 8 96
c=IN IP4 198.51.100.3
a=rtpmap:8 PCMA/8000
a=rtpmap:96 telephone-event/8000
a=fmtp:96 0-15
a=sendrecv
--------------------------------------
v=0
o=- 1545997027 1 IN IP4 203.0.113.1
s=tester
t=0 0
m=audio PORT RTP/AVP 100 101
c=IN IP4 203.0.113.1
a=rtpmap:100 PCMU/16000
a=rtpmap:101 telephone-event/16000
a=fmtp:101 0-15
a=sendrecv
a=rtcp:PORT
SDP
snd($sock_a, $port_b, rtp(100, 1000, 3000+320*0, 0x1234, "\x00" x 320));
# resample buffer is stalling
Time::HiRes::usleep(20000); # 20 ms, needed to ensure that packet 1000 is received first
snd($sock_a, $port_b, rtp(100, 1001, 3000+320*1, 0x1234, "\x00" x 320));
($ssrc) = rcv($sock_b, $port_a, rtpm(8, 1000, 3000+160*0, -1, "\x2a" x 160));
snd($sock_a, $port_b, rtp(100, 1002, 3000+320*2, 0x1234, "\x00" x 320));
rcv($sock_b, $port_a, rtpm(8, 1001, 3000+160*1, $ssrc, "\x2a" x 160));
# start dtmf
snd($sock_a, $port_b, rtp(101 | 0x80, 1003, 3000+320*3, 0x1234, "\x08\x0f\x01\x40"));
rcv($sock_b, $port_a, rtpm(96 | 0x80, 1002, 3000+160*2, $ssrc, "\x08\x0f\x00\xa0"));
snd($sock_a, $port_b, rtp(101, 1004, 3000+320*3, 0x1234, "\x08\x0f\x02\x80"));
rcv($sock_b, $port_a, rtpm(96, 1003, 3000+160*2, $ssrc, "\x08\x0f\x01\x40"));
# end event
snd($sock_a, $port_b, rtp(101, 1005, 3000+320*3, 0x1234, "\x08\x8f\x03\xc0"));
rcv($sock_b, $port_a, rtpm(96, 1004, 3000+160*2, $ssrc, "\x08\x8f\x01\xe0"));
snd($sock_a, $port_b, rtp(101, 1006, 3000+320*3, 0x1234, "\x08\x8f\x03\xc0"));
rcv($sock_b, $port_a, rtpm(96, 1005, 3000+160*2, $ssrc, "\x08\x8f\x01\xe0"));
snd($sock_a, $port_b, rtp(101, 1007, 3000+320*3, 0x1234, "\x08\x8f\x03\xc0"));
rcv($sock_b, $port_a, rtpm(96, 1006, 3000+160*2, $ssrc, "\x08\x8f\x01\xe0"));
# back to audio
snd($sock_a, $port_b, rtp(100, 1008, 3000+320*6, 0x1234, "\x00" x 320));
rcv($sock_b, $port_a, rtpm(8, 1007, 3000+160*5, $ssrc, "\x2a" x 160));
END {


+ 649
- 0
t/test-dtmf-detect.c View File

@ -0,0 +1,649 @@
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <endian.h>
#include <spandsp/telephony.h>
#include <spandsp/super_tone_rx.h>
#include <spandsp/logging.h>
#include <spandsp/dtmf.h>
#include <glib.h>
static unsigned char samples[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xcb, 0x01, 0x66, 0x04, 0x40, 0x03, 0xf7, 0xfd, 0xa1, 0xf9,
0x90, 0xfa, 0xf2, 0xfe, 0x89, 0x00, 0x02, 0xfd, 0x43, 0xfa, 0x57, 0x00,
0x8b, 0x0e, 0x13, 0x18, 0x3d, 0x0f, 0x18, 0xf5, 0xa7, 0xdc, 0xc3, 0xdb,
0x0f, 0xf6, 0x88, 0x17, 0xc4, 0x25, 0xff, 0x17, 0x93, 0xfe, 0x68, 0xf1,
0x83, 0xf7, 0x29, 0x01, 0x26, 0xfb, 0x55, 0xe7, 0x2d, 0xde, 0x57, 0xf5,
0x22, 0x24, 0xa1, 0x44, 0xdf, 0x34, 0xdc, 0xf9, 0xfe, 0xc2, 0x3c, 0xba,
0xb5, 0xe1, 0xa0, 0x14, 0xcc, 0x2a, 0x6c, 0x1c, 0x94, 0x03, 0x15, 0xfd,
0xda, 0x09, 0x75, 0x10, 0x02, 0xfc, 0x5a, 0xd7, 0x48, 0xc6, 0x20, 0xe2,
0xb2, 0x1c, 0xb3, 0x48, 0x49, 0x41, 0xec, 0x0b, 0xc7, 0xd3, 0xcf, 0xc2,
0xee, 0xdd, 0x48, 0x05, 0x28, 0x16, 0xe6, 0x0b, 0x16, 0xff, 0xec, 0x06,
0x4a, 0x1d, 0x45, 0x24, 0x59, 0x07, 0xc5, 0xd5, 0x59, 0xb9, 0xad, 0xcf,
0x42, 0x0c, 0x7f, 0x40, 0x8c, 0x44, 0xbe, 0x19, 0x03, 0xe7, 0x7d, 0xd3,
0x58, 0xe4, 0xdd, 0xfd, 0xc4, 0x03, 0xfd, 0xf6, 0x81, 0xf1, 0xd1, 0x06,
0x36, 0x2a, 0xd2, 0x37, 0xc6, 0x18, 0x1c, 0xde, 0xc6, 0xb5, 0x15, 0xc2,
0xf0, 0xf9, 0x92, 0x30, 0x91, 0x3d, 0x43, 0x1f, 0x73, 0xf7, 0x0d, 0xe8,
0xa6, 0xf3, 0x74, 0x00, 0x4d, 0xf8, 0xe0, 0xe2, 0x4b, 0xde, 0x08, 0xfd,
0x90, 0x2d, 0x0a, 0x46, 0x71, 0x2b, 0xbe, 0xed, 0x67, 0xbc, 0x28, 0xbd,
0x04, 0xeb, 0x48, 0x1d, 0xf8, 0x2d, 0xa0, 0x1a, 0x94, 0x00, 0x4b, 0xfb,
0x1f, 0x08, 0x8c, 0x0c, 0x83, 0xf6, 0x92, 0xd4, 0x66, 0xca, 0x72, 0xec,
0xce, 0x26, 0x4a, 0x4b, 0x22, 0x3a, 0x22, 0x00, 0x59, 0xcb, 0x7a, 0xc2,
0xd1, 0xe3, 0xe5, 0x0b, 0xa1, 0x19, 0xb2, 0x0c, 0xdd, 0xff, 0x55, 0x08,
0xb9, 0x1c, 0x02, 0x1f, 0xaa, 0xfe, 0x72, 0xcf, 0x0d, 0xbb, 0xaa, 0xd9,
0x2d, 0x18, 0x3d, 0x46, 0xb5, 0x40, 0x01, 0x10, 0x8c, 0xde, 0xdd, 0xd0,
0xa6, 0xe6, 0x20, 0x01, 0xb3, 0x05, 0xcf, 0xf8, 0x61, 0xf5, 0xfb, 0x0b,
0x54, 0x2c, 0x0e, 0x33, 0x4b, 0x0e, 0x88, 0xd4, 0x31, 0xb4, 0xff, 0xc9,
0xdf, 0x05, 0x63, 0x38, 0x19, 0x3d, 0xd0, 0x18, 0xc0, 0xf0, 0xcc, 0xe4,
0x1d, 0xf3, 0xee, 0xff, 0x38, 0xf7, 0xde, 0xe3, 0xd5, 0xe3, 0x87, 0x05,
0x2e, 0x33, 0x6c, 0x43, 0xff, 0x20, 0x17, 0xe2, 0x8f, 0xb7, 0xe2, 0xc1,
0x23, 0xf5, 0x7c, 0x25, 0x03, 0x30, 0xd2, 0x17, 0x10, 0xfd, 0x3f, 0xf9,
0x42, 0x06, 0xba, 0x08, 0xba, 0xf1, 0x1a, 0xd3, 0xc7, 0xcf, 0x03, 0xf7,
0xc6, 0x2f, 0xe2, 0x4b, 0x78, 0x31, 0x2e, 0xf4, 0x2d, 0xc4, 0xc5, 0xc3,
0xc8, 0xea, 0xa4, 0x12, 0x9d, 0x1c, 0xf5, 0x0c, 0x1e, 0x00, 0x16, 0x09,
0x58, 0x1b, 0x3e, 0x19, 0x66, 0xf6, 0x97, 0xca, 0x84, 0xbe, 0x8c, 0xe4,
0x6d, 0x23, 0x32, 0x4a, 0x15, 0x3b, 0x9a, 0x05, 0xa7, 0xd6, 0x6e, 0xcf,
0xf4, 0xe9, 0xdd, 0x04, 0xd4, 0x07, 0xb1, 0xfa, 0x1a, 0xf9, 0x5c, 0x10,
0x29, 0x2d, 0x1a, 0x2d, 0xb8, 0x03, 0x3b, 0xcc, 0x95, 0xb4, 0x5b, 0xd3,
0xc1, 0x11, 0xe0, 0x3e, 0xf7, 0x3a, 0x5f, 0x11, 0x09, 0xea, 0x13, 0xe2,
0x30, 0xf3, 0xee, 0xff, 0xcb, 0xf6, 0x9a, 0xe5, 0xc8, 0xe9, 0x7d, 0x0d,
0x46, 0x37, 0x1f, 0x3f, 0xc7, 0x15, 0x48, 0xd7, 0x8d, 0xb4, 0x57, 0xc8,
0xcb, 0xff, 0xfa, 0x2c, 0xd0, 0x30, 0x0c, 0x14, 0x17, 0xf9, 0x1d, 0xf7,
0x5a, 0x04, 0x2a, 0x05, 0xc1, 0xed, 0xea, 0xd2, 0x3c, 0xd6, 0x87, 0x01,
0x5f, 0x37, 0x7d, 0x4a, 0x81, 0x27, 0x6b, 0xe8, 0x72, 0xbe, 0xbb, 0xc6,
0xa8, 0xf2, 0x49, 0x19, 0x03, 0x1f, 0x8c, 0x0c, 0xdd, 0xff, 0x2f, 0x09,
0x4d, 0x19, 0x26, 0x13, 0xcd, 0xee, 0x43, 0xc7, 0xa3, 0xc3, 0xff, 0xef,
0xc0, 0x2d, 0x33, 0x4c, 0xda, 0x33, 0xc2, 0xfa, 0x9e, 0xcf, 0x49, 0xcf,
0x32, 0xee, 0x01, 0x09, 0xfc, 0x09, 0x8a, 0xfc, 0x81, 0xfc, 0xe7, 0x13,
0xb6, 0x2c, 0x36, 0x26, 0x50, 0xf9, 0x69, 0xc5, 0xf2, 0xb6, 0xe0, 0xdd,
0x4c, 0x1d, 0xd1, 0x43, 0x2c, 0x37, 0x21, 0x09, 0x7c, 0xe3, 0x14, 0xe0,
0xe6, 0xf3, 0x7e, 0x00, 0xf2, 0xf6, 0xfb, 0xe7, 0xea, 0xef, 0xb9, 0x14,
0xc8, 0x39, 0x45, 0x39, 0x21, 0x0a, 0x90, 0xcd, 0x82, 0xb3, 0x5b, 0xd0,
0xb4, 0x0a, 0x87, 0x33, 0x47, 0x30, 0x59, 0x0f, 0xd3, 0xf4, 0xf7, 0xf4,
0x96, 0x02, 0xf6, 0x01, 0xa8, 0xea, 0xf9, 0xd3, 0x82, 0xdd, 0xc0, 0x0b,
0x60, 0x3d, 0x2f, 0x47, 0x7e, 0x1c, 0x2c, 0xdd, 0x59, 0xba, 0x51, 0xcb,
0x40, 0xfb, 0xa1, 0x1f, 0xa5, 0x20, 0x74, 0x0b, 0x10, 0xff, 0xbd, 0x08,
0xab, 0x16, 0xfc, 0x0c, 0x02, 0xe8, 0x8f, 0xc5, 0x30, 0xca, 0xc1, 0xfb,
0xcd, 0x36, 0x44, 0x4c, 0x1c, 0x2b, 0xd9, 0xef, 0x9d, 0xc9, 0x84, 0xd0,
0x51, 0xf3, 0x62, 0x0d, 0x0e, 0x0c, 0x37, 0xfe, 0x7a, 0xff, 0x8c, 0x16,
0x12, 0x2b, 0x9a, 0x1e, 0x5b, 0xef, 0x40, 0xc0, 0x2c, 0xbb, 0x4c, 0xe9,
0x2c, 0x28, 0x09, 0x47, 0xcf, 0x31, 0x41, 0x00, 0x61, 0xdd, 0xde, 0xde,
0x59, 0xf5, 0x8c, 0x01, 0xa8, 0xf7, 0xd2, 0xea, 0x12, 0xf6, 0x04, 0x1b,
0xb7, 0x3a, 0x08, 0x32, 0x64, 0xfe, 0x2f, 0xc5, 0x7a, 0xb4, 0xb9, 0xd9,
0x92, 0x15, 0xea, 0x38, 0x5b, 0x2e, 0xd2, 0x09, 0x65, 0xf0, 0xfa, 0xf2,
0x0b, 0x01, 0x3d, 0xff, 0x77, 0xe8, 0x2b, 0xd6, 0x66, 0xe5, 0x5d, 0x15,
0xae, 0x41, 0x0e, 0x42, 0xbd, 0x10, 0xc2, 0xd2, 0x02, 0xb8, 0x77, 0xd1,
0x50, 0x04, 0x7b, 0x25, 0x5e, 0x21, 0xaf, 0x09, 0xb8, 0xfd, 0xd7, 0x07,
0x9c, 0x13, 0xed, 0x06, 0x36, 0xe2, 0x6e, 0xc5, 0x03, 0xd2, 0x6e, 0x07,
0x6e, 0x3e, 0x48, 0x4a, 0x2f, 0x21, 0x12, 0xe5, 0xec, 0xc4, 0x1c, 0xd3,
0x39, 0xf9, 0xd5, 0x11, 0xec, 0x0d, 0x98, 0xff, 0xf0, 0x01, 0x44, 0x18,
0x5d, 0x28, 0x7f, 0x16, 0x24, 0xe6, 0xd5, 0xbc, 0x25, 0xc1, 0x52, 0xf5,
0x0b, 0x32, 0x79, 0x48, 0xed, 0x2a, 0x0a, 0xf7, 0xdd, 0xd7, 0x9d, 0xde,
0x80, 0xf7, 0x17, 0x03, 0xca, 0xf8, 0x02, 0xee, 0x05, 0xfc, 0x45, 0x20,
0x0e, 0x3a, 0xad, 0x29, 0xd9, 0xf2, 0x65, 0xbe, 0x69, 0xb7, 0x34, 0xe4,
0x1d, 0x20, 0xed, 0x3c, 0x0d, 0x2b, 0x99, 0x03, 0xf2, 0xeb, 0x49, 0xf1,
0xda, 0xff, 0x0a, 0xfd, 0x3b, 0xe7, 0x55, 0xd9, 0xad, 0xed, 0x1d, 0x1e,
0x34, 0x44, 0x41, 0x3b, 0x93, 0x04, 0x72, 0xc9, 0x8c, 0xb7, 0x01, 0xd9,
0xa3, 0x0d, 0x99, 0x2a, 0x21, 0x21, 0x2d, 0x07, 0xf8, 0xfb, 0x87, 0x06,
0x4f, 0x10, 0x26, 0x01, 0x85, 0xdd, 0xd9, 0xc6, 0xd9, 0xda, 0xbf, 0x12,
0x5c, 0x44, 0x65, 0x46, 0x3a, 0x16, 0xd6, 0xda, 0xa1, 0xc1, 0x1d, 0xd7,
0xbb, 0xff, 0x38, 0x16, 0x6d, 0x0f, 0x94, 0x00, 0xd7, 0x03, 0x10, 0x19,
0xbc, 0x24, 0x27, 0x0e, 0xe0, 0xdd, 0x3c, 0xbb, 0xb4, 0xc8, 0x92, 0x01,
0xb6, 0x3a, 0xf8, 0x47, 0xbf, 0x22, 0xb2, 0xed, 0x2c, 0xd3, 0x64, 0xdf,
0x53, 0xfa, 0x0e, 0x05, 0x3b, 0xfa, 0x63, 0xf1, 0x97, 0x01, 0x60, 0x24,
0xe4, 0x37, 0x73, 0x20, 0xd1, 0xe7, 0x59, 0xb9, 0x42, 0xbc, 0x86, 0xef,
0x04, 0x2a, 0x6c, 0x3f, 0x64, 0x26, 0xd4, 0xfc, 0xac, 0xe7, 0x00, 0xf0,
0x18, 0xff, 0x73, 0xfb, 0xda, 0xe6, 0x64, 0xdd, 0x05, 0xf6, 0xd6, 0x25,
0xe1, 0x44, 0x00, 0x33, 0x4d, 0xf8, 0x87, 0xc1, 0xf7, 0xb8, 0xcd, 0xe1,
0xee, 0x16, 0xcd, 0x2e, 0xd9, 0x1f, 0xff, 0x03, 0xda, 0xf9, 0xf4, 0x04,
0xe9, 0x0c, 0xd3, 0xfb, 0x02, 0xda, 0xbc, 0xc9, 0x66, 0xe4, 0x6b, 0x1d,
0x7a, 0x48, 0xa4, 0x40, 0x99, 0x0a, 0x5c, 0xd1, 0xf1, 0xbf, 0x6a, 0xdc,
0xb6, 0x06, 0x58, 0x1a, 0x74, 0x10, 0x19, 0x01, 0x20, 0x05, 0x02, 0x19,
0x57, 0x20, 0xd1, 0x05, 0xc7, 0xd6, 0x73, 0xbb, 0x9f, 0xd1, 0xc0, 0x0d,
0xdf, 0x41, 0x94, 0x45, 0x6f, 0x19, 0x7f, 0xe4, 0x81, 0xcf, 0x32, 0xe1,
0xd6, 0xfd, 0x4a, 0x07, 0xea, 0xfb, 0xc2, 0xf4, 0xa8, 0x06, 0x41, 0x27,
0x59, 0x34, 0x9b, 0x16, 0x9a, 0xdd, 0x2f, 0xb6, 0xde, 0xc2, 0x64, 0xfb,
0xfe, 0x32, 0x47, 0x40, 0x7a, 0x20, 0xb0, 0xf5, 0xbb, 0xe3, 0x44, 0xef,
0xd0, 0xfe, 0x7e, 0xfa, 0x4c, 0xe7, 0x1b, 0xe2, 0x42, 0xfe, 0x4b, 0x2c,
0xc5, 0x43, 0x7f, 0x29, 0x4b, 0xec, 0x2b, 0xbb, 0x51, 0xbc, 0x96, 0xeb,
0xf5, 0x1f, 0xe7, 0x31, 0x7f, 0x1d, 0x31, 0x00, 0x83, 0xf7, 0x37, 0x03,
0x95, 0x09, 0x12, 0xf7, 0xbe, 0xd7, 0xed, 0xcd, 0x6e, 0xee, 0x22, 0x27,
0xad, 0x4a, 0x33, 0x39, 0x96, 0xfe, 0xf1, 0xc8, 0xee, 0xbf, 0xee, 0xe2,
0xf3, 0x0d, 0x0d, 0x1e, 0xe4, 0x10, 0x19, 0x01, 0xca, 0x05, 0x2d, 0x18,
0x5a, 0x1b, 0xbd, 0xfd, 0xff, 0xd0, 0x71, 0xbd, 0xa2, 0xdb, 0x86, 0x19,
0x57, 0x47, 0x56, 0x41, 0x34, 0x0f, 0xbb, 0xdb, 0x00, 0xcd, 0x15, 0xe4,
0xe8, 0x01, 0xba, 0x09, 0xa8, 0xfd, 0x07, 0xf8, 0x0b, 0x0b, 0xeb, 0x28,
0x8e, 0x2f, 0x75, 0x0c, 0x74, 0xd4, 0xf4, 0xb4, 0x17, 0xcb, 0x7a, 0x07,
0xc6, 0x3a, 0x73, 0x3f, 0x63, 0x19, 0x67, 0xee, 0x51, 0xe0, 0x21, 0xef,
0x1c, 0xff, 0x1a, 0xfa, 0x81, 0xe8, 0x4d, 0xe7, 0x22, 0x06, 0x60, 0x31,
0xe7, 0x40, 0x0f, 0x1f, 0xd2, 0xe0, 0x9f, 0xb6, 0x76, 0xc1, 0x2a, 0xf6,
0x67, 0x28, 0xcb, 0x33, 0x0c, 0x1a, 0xe8, 0xfb, 0x02, 0xf5, 0x7f, 0x01,
0x6b, 0x06, 0x09, 0xf3, 0xb8, 0xd6, 0x3e, 0xd3, 0xa8, 0xf8, 0xa5, 0x2f,
0xea, 0x4a, 0x47, 0x30, 0x81, 0xf2, 0xd7, 0xc1, 0xa3, 0xc1, 0x84, 0xea,
0x3e, 0x15, 0x28, 0x21, 0xa9, 0x10, 0x8c, 0x00, 0xde, 0x05, 0xa8, 0x16,
0xfa, 0x15, 0x23, 0xf6, 0xa1, 0xcc, 0x25, 0xc1, 0x71, 0xe6, 0x95, 0x24,
0xfa, 0x4a, 0x4a, 0x3b, 0x5f, 0x04, 0xa2, 0xd3, 0xcb, 0xcb, 0x0b, 0xe8,
0x69, 0x06, 0x3f, 0x0c, 0x53, 0xff, 0x0d, 0xfb, 0xb1, 0x0e, 0x59, 0x29,
0xb9, 0x29, 0x43, 0x02, 0x9c, 0xcc, 0xb3, 0xb5, 0xab, 0xd4, 0x77, 0x13,
0x24, 0x41, 0xe0, 0x3c, 0x55, 0x11, 0x2b, 0xe7, 0x8f, 0xdd, 0xbd, 0xef,
0xed, 0xff, 0x4e, 0xfa, 0x52, 0xea, 0xcb, 0xec, 0x71, 0x0d, 0xf7, 0x34,
0x70, 0x3c, 0xed, 0x13, 0x43, 0xd6, 0xef, 0xb3, 0x5a, 0xc8, 0x30, 0x01,
0x18, 0x30, 0x46, 0x34, 0x9e, 0x15, 0x32, 0xf7, 0x86, 0xf2, 0xe3, 0xff,
0x97, 0x03, 0xc6, 0xef, 0xe2, 0xd6, 0x87, 0xd9, 0xc1, 0x02, 0xc4, 0x36,
0x34, 0x49, 0x1a, 0x26, 0xb0, 0xe6, 0x48, 0xbc, 0x0a, 0xc5, 0x03, 0xf3,
0x5b, 0x1c, 0x83, 0x23, 0xb2, 0x0f, 0x70, 0xff, 0x6b, 0x05, 0x91, 0x14,
0x6b, 0x10, 0x2f, 0xef, 0xcc, 0xc9, 0x60, 0xc6, 0xc5, 0xf1, 0xa1, 0x2e,
0xa5, 0x4c, 0xa4, 0x33, 0x2e, 0xf9, 0x79, 0xcc, 0xfd, 0xcb, 0xfd, 0xec,
0x3f, 0x0b, 0xad, 0x0e, 0xd5, 0x00, 0xae, 0xfd, 0x89, 0x11, 0x9f, 0x28,
0x08, 0x23, 0x54, 0xf8, 0x41, 0xc6, 0x5e, 0xb8, 0x5d, 0xdf, 0x07, 0x1f,
0xdd, 0x45, 0xa2, 0x38, 0x78, 0x08, 0x39, 0xe0, 0xa4, 0xdb, 0x13, 0xf1,
0x50, 0x01, 0xfe, 0xfa, 0xa2, 0xec, 0x64, 0xf2, 0xfa, 0x13, 0x0e, 0x37,
0x79, 0x36, 0x7c, 0x08, 0xce, 0xcc, 0x49, 0xb3, 0xb7, 0xd0, 0x79, 0x0c,
0xa9, 0x36, 0x64, 0x33, 0x2f, 0x10, 0x45, 0xf2, 0x2d, 0xf0, 0x87, 0xfe,
0x29, 0x01, 0x5f, 0xed, 0x25, 0xd8, 0x91, 0xe0, 0x75, 0x0c, 0x52, 0x3c,
0x9b, 0x45, 0xf4, 0x1a, 0x79, 0xdb, 0x67, 0xb8, 0x22, 0xca, 0x2b, 0xfc,
0x15, 0x23, 0xf8, 0x24, 0xf5, 0x0d, 0xd1, 0xfd, 0x7e, 0x04, 0x14, 0x12,
0xd2, 0x0a, 0x18, 0xe9, 0x7b, 0xc8, 0xfc, 0xcc, 0x4c, 0xfd, 0x60, 0x37,
0x54, 0x4c, 0x8d, 0x2a, 0xf2, 0xed, 0x7e, 0xc6, 0xa0, 0xcd, 0xd4, 0xf2,
0x42, 0x10, 0xe3, 0x10, 0x08, 0x02, 0xdf, 0xff, 0x82, 0x13, 0xd6, 0x26,
0xb8, 0x1b, 0xe9, 0xee, 0x88, 0xc1, 0xdf, 0xbc, 0xdf, 0xea, 0xda, 0x29,
0xd1, 0x48, 0xc2, 0x32, 0x0d, 0xff, 0xca, 0xd9, 0xab, 0xda, 0x35, 0xf3,
0x2d, 0x03, 0x1a, 0xfc, 0x4e, 0xef, 0xe4, 0xf7, 0x9e, 0x19, 0x99, 0x37,
0x43, 0x2f, 0xfb, 0xfc, 0xc8, 0xc4, 0x98, 0xb4, 0x75, 0xda, 0x97, 0x17,
0x01, 0x3c, 0xff, 0x30, 0xf4, 0x09, 0x3d, 0xed, 0x1f, 0xee, 0x87, 0xfd,
0x39, 0xff, 0xd1, 0xeb, 0x6d, 0xda, 0x1a, 0xe8, 0x86, 0x15, 0x31, 0x40,
0x3b, 0x40, 0x26, 0x0f, 0x23, 0xd1, 0x63, 0xb6, 0xc3, 0xd0, 0xca, 0x05,
0x28, 0x29, 0x72, 0x25, 0x6b, 0x0b, 0xc1, 0xfb, 0x31, 0x03, 0x4c, 0x0f,
0x6d, 0x05, 0xf5, 0xe3, 0xb2, 0xc8, 0xbe, 0xd4, 0xb2, 0x08, 0x9f, 0x3e,
0x06, 0x4a, 0x40, 0x20, 0xfe, 0xe2, 0xdf, 0xc1, 0xba, 0xd0, 0x73, 0xf9,
0x3e, 0x15, 0xc1, 0x12, 0xd4, 0x02, 0x85, 0x01, 0xa9, 0x14, 0x16, 0x24,
0x04, 0x14, 0x45, 0xe6, 0x85, 0xbe, 0x13, 0xc3, 0xe7, 0xf6, 0x98, 0x33,
0xed, 0x49, 0x5e, 0x2b, 0x56, 0xf5, 0x15, 0xd4, 0xc0, 0xda, 0x19, 0xf6,
0x7a, 0x05, 0x84, 0xfd, 0x2f, 0xf2, 0x21, 0xfd, 0x3c, 0x1e, 0xa3, 0x36,
0x07, 0x27, 0xbf, 0xf1, 0x5f, 0xbe, 0xe0, 0xb7, 0x42, 0xe5, 0x4a, 0x22,
0xe1, 0x3f, 0x2b, 0x2d, 0x06, 0x03, 0x4d, 0xe8, 0x81, 0xec, 0xf2, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xcb, 0x01, 0x66, 0x04, 0x40, 0x03, 0xf7, 0xfd, 0xa1, 0xf9,
0x90, 0xfa, 0xf2, 0xfe, 0x89, 0x00, 0x02, 0xfd, 0x43, 0xfa, 0x57, 0x00,
0x8b, 0x0e, 0x13, 0x18, 0x3d, 0x0f, 0x18, 0xf5, 0xa7, 0xdc, 0xc3, 0xdb,
0x0f, 0xf6, 0x88, 0x17, 0xc4, 0x25, 0xff, 0x17, 0x93, 0xfe, 0x68, 0xf1,
0x83, 0xf7, 0x29, 0x01, 0x26, 0xfb, 0x55, 0xe7, 0x2d, 0xde, 0x57, 0xf5,
0x22, 0x24, 0xa1, 0x44, 0xdf, 0x34, 0xdc, 0xf9, 0xfe, 0xc2, 0x3c, 0xba,
0xb5, 0xe1, 0xa0, 0x14, 0xcc, 0x2a, 0x6c, 0x1c, 0x94, 0x03, 0x15, 0xfd,
0xda, 0x09, 0x75, 0x10, 0x02, 0xfc, 0x5a, 0xd7, 0x48, 0xc6, 0x20, 0xe2,
0xb2, 0x1c, 0xb3, 0x48, 0x49, 0x41, 0xec, 0x0b, 0xc7, 0xd3, 0xcf, 0xc2,
0xee, 0xdd, 0x48, 0x05, 0x28, 0x16, 0xe6, 0x0b, 0x16, 0xff, 0xec, 0x06,
0x4a, 0x1d, 0x45, 0x24, 0x59, 0x07, 0xc5, 0xd5, 0x59, 0xb9, 0xad, 0xcf,
0x42, 0x0c, 0x7f, 0x40, 0x8c, 0x44, 0xbe, 0x19, 0x03, 0xe7, 0x7d, 0xd3,
0x58, 0xe4, 0xdd, 0xfd, 0xc4, 0x03, 0xfd, 0xf6, 0x81, 0xf1, 0xd1, 0x06,
0x36, 0x2a, 0xd2, 0x37, 0xc6, 0x18, 0x1c, 0xde, 0xc6, 0xb5, 0x15, 0xc2,
0xf0, 0xf9, 0x92, 0x30, 0x91, 0x3d, 0x43, 0x1f, 0x73, 0xf7, 0x0d, 0xe8,
0xa6, 0xf3, 0x74, 0x00, 0x4d, 0xf8, 0xe0, 0xe2, 0x4b, 0xde, 0x08, 0xfd,
0x90, 0x2d, 0x0a, 0x46, 0x71, 0x2b, 0xbe, 0xed, 0x67, 0xbc, 0x28, 0xbd,
0x04, 0xeb, 0x48, 0x1d, 0xf8, 0x2d, 0xa0, 0x1a, 0x94, 0x00, 0x4b, 0xfb,
0x1f, 0x08, 0x8c, 0x0c, 0x83, 0xf6, 0x92, 0xd4, 0x66, 0xca, 0x72, 0xec,
0xce, 0x26, 0x4a, 0x4b, 0x22, 0x3a, 0x22, 0x00, 0x59, 0xcb, 0x7a, 0xc2,
0xd1, 0xe3, 0xe5, 0x0b, 0xa1, 0x19, 0xb2, 0x0c, 0xdd, 0xff, 0x55, 0x08,
0xb9, 0x1c, 0x02, 0x1f, 0xaa, 0xfe, 0x72, 0xcf, 0x0d, 0xbb, 0xaa, 0xd9,
0x2d, 0x18, 0x3d, 0x46, 0xb5, 0x40, 0x01, 0x10, 0x8c, 0xde, 0xdd, 0xd0,
0xa6, 0xe6, 0x20, 0x01, 0xb3, 0x05, 0xcf, 0xf8, 0x61, 0xf5, 0xfb, 0x0b,
0x54, 0x2c, 0x0e, 0x33, 0x4b, 0x0e, 0x88, 0xd4, 0x31, 0xb4, 0xff, 0xc9,
0xdf, 0x05, 0x63, 0x38, 0x19, 0x3d, 0xd0, 0x18, 0xc0, 0xf0, 0xcc, 0xe4,
0x1d, 0xf3, 0xee, 0xff, 0x38, 0xf7, 0xde, 0xe3, 0xd5, 0xe3, 0x87, 0x05,
0x2e, 0x33, 0x6c, 0x43, 0xff, 0x20, 0x17, 0xe2, 0x8f, 0xb7, 0xe2, 0xc1,
0x23, 0xf5, 0x7c, 0x25, 0x03, 0x30, 0xd2, 0x17, 0x10, 0xfd, 0x3f, 0xf9,
0x42, 0x06, 0xba, 0x08, 0xba, 0xf1, 0x1a, 0xd3, 0xc7, 0xcf, 0x03, 0xf7,
0xc6, 0x2f, 0xe2, 0x4b, 0x78, 0x31, 0x2e, 0xf4, 0x2d, 0xc4, 0xc5, 0xc3,
0xc8, 0xea, 0xa4, 0x12, 0x9d, 0x1c, 0xf5, 0x0c, 0x1e, 0x00, 0x16, 0x09,
0x58, 0x1b, 0x3e, 0x19, 0x66, 0xf6, 0x97, 0xca, 0x84, 0xbe, 0x8c, 0xe4,
0x6d, 0x23, 0x32, 0x4a, 0x15, 0x3b, 0x9a, 0x05, 0xa7, 0xd6, 0x6e, 0xcf,
0xf4, 0xe9, 0xdd, 0x04, 0xd4, 0x07, 0xb1, 0xfa, 0x1a, 0xf9, 0x5c, 0x10,
0x29, 0x2d, 0x1a, 0x2d, 0xb8, 0x03, 0x3b, 0xcc, 0x95, 0xb4, 0x5b, 0xd3,
0xc1, 0x11, 0xe0, 0x3e, 0xf7, 0x3a, 0x5f, 0x11, 0x09, 0xea, 0x13, 0xe2,
0x30, 0xf3, 0xee, 0xff, 0xcb, 0xf6, 0x9a, 0xe5, 0xc8, 0xe9, 0x7d, 0x0d,
0x46, 0x37, 0x1f, 0x3f, 0xc7, 0x15, 0x48, 0xd7, 0x8d, 0xb4, 0x57, 0xc8,
0xcb, 0xff, 0xfa, 0x2c, 0xd0, 0x30, 0x0c, 0x14, 0x17, 0xf9, 0x1d, 0xf7,
0x5a, 0x04, 0x2a, 0x05, 0xc1, 0xed, 0xea, 0xd2, 0x3c, 0xd6, 0x87, 0x01,
0x5f, 0x37, 0x7d, 0x4a, 0x81, 0x27, 0x6b, 0xe8, 0x72, 0xbe, 0xbb, 0xc6,
0xa8, 0xf2, 0x49, 0x19, 0x03, 0x1f, 0x8c, 0x0c, 0xdd, 0xff, 0x2f, 0x09,
0x4d, 0x19, 0x26, 0x13, 0xcd, 0xee, 0x43, 0xc7, 0xa3, 0xc3, 0xff, 0xef,
0xc0, 0x2d, 0x33, 0x4c, 0xda, 0x33, 0xc2, 0xfa, 0x9e, 0xcf, 0x49, 0xcf,
0x32, 0xee, 0x01, 0x09, 0xfc, 0x09, 0x8a, 0xfc, 0x81, 0xfc, 0xe7, 0x13,
0xb6, 0x2c, 0x36, 0x26, 0x50, 0xf9, 0x69, 0xc5, 0xf2, 0xb6, 0xe0, 0xdd,
0x4c, 0x1d, 0xd1, 0x43, 0x2c, 0x37, 0x21, 0x09, 0x7c, 0xe3, 0x14, 0xe0,
0xe6, 0xf3, 0x7e, 0x00, 0xf2, 0xf6, 0xfb, 0xe7, 0xea, 0xef, 0xb9, 0x14,
0xc8, 0x39, 0x45, 0x39, 0x21, 0x0a, 0x90, 0xcd, 0x82, 0xb3, 0x5b, 0xd0,
0xb4, 0x0a, 0x87, 0x33, 0x47, 0x30, 0x59, 0x0f, 0xd3, 0xf4, 0xf7, 0xf4,
0x96, 0x02, 0xf6, 0x01, 0xa8, 0xea, 0xf9, 0xd3, 0x82, 0xdd, 0xc0, 0x0b,
0x60, 0x3d, 0x2f, 0x47, 0x7e, 0x1c, 0x2c, 0xdd, 0x59, 0xba, 0x51, 0xcb,
0x40, 0xfb, 0xa1, 0x1f, 0xa5, 0x20, 0x74, 0x0b, 0x10, 0xff, 0xbd, 0x08,
0xab, 0x16, 0xfc, 0x0c, 0x02, 0xe8, 0x8f, 0xc5, 0x30, 0xca, 0xc1, 0xfb,
0xcd, 0x36, 0x44, 0x4c, 0x1c, 0x2b, 0xd9, 0xef, 0x9d, 0xc9, 0x84, 0xd0,
0x51, 0xf3, 0x62, 0x0d, 0x0e, 0x0c, 0x37, 0xfe, 0x7a, 0xff, 0x8c, 0x16,
0x12, 0x2b, 0x9a, 0x1e, 0x5b, 0xef, 0x40, 0xc0, 0x2c, 0xbb, 0x4c, 0xe9,
0x2c, 0x28, 0x09, 0x47, 0xcf, 0x31, 0x41, 0x00, 0x61, 0xdd, 0xde, 0xde,
0x59, 0xf5, 0x8c, 0x01, 0xa8, 0xf7, 0xd2, 0xea, 0x12, 0xf6, 0x04, 0x1b,
0xb7, 0x3a, 0x08, 0x32, 0x64, 0xfe, 0x2f, 0xc5, 0x7a, 0xb4, 0xb9, 0xd9,
0x92, 0x15, 0xea, 0x38, 0x5b, 0x2e, 0xd2, 0x09, 0x65, 0xf0, 0xfa, 0xf2,
0x0b, 0x01, 0x3d, 0xff, 0x77, 0xe8, 0x2b, 0xd6, 0x66, 0xe5, 0x5d, 0x15,
0xae, 0x41, 0x0e, 0x42, 0xbd, 0x10, 0xc2, 0xd2, 0x02, 0xb8, 0x77, 0xd1,
0x50, 0x04, 0x7b, 0x25, 0x5e, 0x21, 0xaf, 0x09, 0xb8, 0xfd, 0xd7, 0x07,
0x9c, 0x13, 0xed, 0x06, 0x36, 0xe2, 0x6e, 0xc5, 0x03, 0xd2, 0x6e, 0x07,
0x6e, 0x3e, 0x48, 0x4a, 0x2f, 0x21, 0x12, 0xe5, 0xec, 0xc4, 0x1c, 0xd3,
0x39, 0xf9, 0xd5, 0x11, 0xec, 0x0d, 0x98, 0xff, 0xf0, 0x01, 0x44, 0x18,
0x5d, 0x28, 0x7f, 0x16, 0x24, 0xe6, 0xd5, 0xbc, 0x25, 0xc1, 0x52, 0xf5,
0x0b, 0x32, 0x79, 0x48, 0xed, 0x2a, 0x0a, 0xf7, 0xdd, 0xd7, 0x9d, 0xde,
0x80, 0xf7, 0x17, 0x03, 0xca, 0xf8, 0x02, 0xee, 0x05, 0xfc, 0x45, 0x20,
0x0e, 0x3a, 0xad, 0x29, 0xd9, 0xf2, 0x65, 0xbe, 0x69, 0xb7, 0x34, 0xe4,
0x1d, 0x20, 0xed, 0x3c, 0x0d, 0x2b, 0x99, 0x03, 0xf2, 0xeb, 0x49, 0xf1,
0xda, 0xff, 0x0a, 0xfd, 0x3b, 0xe7, 0x55, 0xd9, 0xad, 0xed, 0x1d, 0x1e,
0x34, 0x44, 0x41, 0x3b, 0x93, 0x04, 0x72, 0xc9, 0x8c, 0xb7, 0x01, 0xd9,
0xa3, 0x0d, 0x99, 0x2a, 0x21, 0x21, 0x2d, 0x07, 0xf8, 0xfb, 0x87, 0x06,
0x4f, 0x10, 0x26, 0x01, 0x85, 0xdd, 0xd9, 0xc6, 0xd9, 0xda, 0xbf, 0x12,
0x5c, 0x44, 0x65, 0x46, 0x3a, 0x16, 0xd6, 0xda, 0xa1, 0xc1, 0x1d, 0xd7,
0xbb, 0xff, 0x38, 0x16, 0x6d, 0x0f, 0x94, 0x00, 0xd7, 0x03, 0x10, 0x19,
0xbc, 0x24, 0x27, 0x0e, 0xe0, 0xdd, 0x3c, 0xbb, 0xb4, 0xc8, 0x92, 0x01,
0xb6, 0x3a, 0xf8, 0x47, 0xbf, 0x22, 0xb2, 0xed, 0x2c, 0xd3, 0x64, 0xdf,
0x53, 0xfa, 0x0e, 0x05, 0x3b, 0xfa, 0x63, 0xf1, 0x97, 0x01, 0x60, 0x24,
0xe4, 0x37, 0x73, 0x20, 0xd1, 0xe7, 0x59, 0xb9, 0x42, 0xbc, 0x86, 0xef,
0x04, 0x2a, 0x6c, 0x3f, 0x64, 0x26, 0xd4, 0xfc, 0xac, 0xe7, 0x00, 0xf0,
0x18, 0xff, 0x73, 0xfb, 0xda, 0xe6, 0x64, 0xdd, 0x05, 0xf6, 0xd6, 0x25,
0xe1, 0x44, 0x00, 0x33, 0x4d, 0xf8, 0x87, 0xc1, 0xf7, 0xb8, 0xcd, 0xe1,
0xee, 0x16, 0xcd, 0x2e, 0xd9, 0x1f, 0xff, 0x03, 0xda, 0xf9, 0xf4, 0x04,
0xe9, 0x0c, 0xd3, 0xfb, 0x02, 0xda, 0xbc, 0xc9, 0x66, 0xe4, 0x6b, 0x1d,
0x7a, 0x48, 0xa4, 0x40, 0x99, 0x0a, 0x5c, 0xd1, 0xf1, 0xbf, 0x6a, 0xdc,
0xb6, 0x06, 0x58, 0x1a, 0x74, 0x10, 0x19, 0x01, 0x20, 0x05, 0x02, 0x19,
0x57, 0x20, 0xd1, 0x05, 0xc7, 0xd6, 0x73, 0xbb, 0x9f, 0xd1, 0xc0, 0x0d,
0xdf, 0x41, 0x94, 0x45, 0x6f, 0x19, 0x7f, 0xe4, 0x81, 0xcf, 0x32, 0xe1,
0xd6, 0xfd, 0x4a, 0x07, 0xea, 0xfb, 0xc2, 0xf4, 0xa8, 0x06, 0x41, 0x27,
0x59, 0x34, 0x9b, 0x16, 0x9a, 0xdd, 0x2f, 0xb6, 0xde, 0xc2, 0x64, 0xfb,
0xfe, 0x32, 0x47, 0x40, 0x7a, 0x20, 0xb0, 0xf5, 0xbb, 0xe3, 0x44, 0xef,
0xd0, 0xfe, 0x7e, 0xfa, 0x4c, 0xe7, 0x1b, 0xe2, 0x42, 0xfe, 0x4b, 0x2c,
0xc5, 0x43, 0x7f, 0x29, 0x4b, 0xec, 0x2b, 0xbb, 0x51, 0xbc, 0x96, 0xeb,
0xf5, 0x1f, 0xe7, 0x31, 0x7f, 0x1d, 0x31, 0x00, 0x83, 0xf7, 0x37, 0x03,
0x95, 0x09, 0x12, 0xf7, 0xbe, 0xd7, 0xed, 0xcd, 0x6e, 0xee, 0x22, 0x27,
0xad, 0x4a, 0x33, 0x39, 0x96, 0xfe, 0xf1, 0xc8, 0xee, 0xbf, 0xee, 0xe2,
0xf3, 0x0d, 0x0d, 0x1e, 0xe4, 0x10, 0x19, 0x01, 0xca, 0x05, 0x2d, 0x18,
0x5a, 0x1b, 0xbd, 0xfd, 0xff, 0xd0, 0x71, 0xbd, 0xa2, 0xdb, 0x86, 0x19,
0x57, 0x47, 0x56, 0x41, 0x34, 0x0f, 0xbb, 0xdb, 0x00, 0xcd, 0x15, 0xe4,
0xe8, 0x01, 0xba, 0x09, 0xa8, 0xfd, 0x07, 0xf8, 0x0b, 0x0b, 0xeb, 0x28,
0x8e, 0x2f, 0x75, 0x0c, 0x74, 0xd4, 0xf4, 0xb4, 0x17, 0xcb, 0x7a, 0x07,
0xc6, 0x3a, 0x73, 0x3f, 0x63, 0x19, 0x67, 0xee, 0x51, 0xe0, 0x21, 0xef,
0x1c, 0xff, 0x1a, 0xfa, 0x81, 0xe8, 0x4d, 0xe7, 0x22, 0x06, 0x60, 0x31,
0xe7, 0x40, 0x0f, 0x1f, 0xd2, 0xe0, 0x9f, 0xb6, 0x76, 0xc1, 0x2a, 0xf6,
0x67, 0x28, 0xcb, 0x33, 0x0c, 0x1a, 0xe8, 0xfb, 0x02, 0xf5, 0x7f, 0x01,
0x6b, 0x06, 0x09, 0xf3, 0xb8, 0xd6, 0x3e, 0xd3, 0xa8, 0xf8, 0xa5, 0x2f,
0xea, 0x4a, 0x47, 0x30, 0x81, 0xf2, 0xd7, 0xc1, 0xa3, 0xc1, 0x84, 0xea,
0x3e, 0x15, 0x28, 0x21, 0xa9, 0x10, 0x8c, 0x00, 0xde, 0x05, 0xa8, 0x16,
0xfa, 0x15, 0x23, 0xf6, 0xa1, 0xcc, 0x25, 0xc1, 0x71, 0xe6, 0x95, 0x24,
0xfa, 0x4a, 0x4a, 0x3b, 0x5f, 0x04, 0xa2, 0xd3, 0xcb, 0xcb, 0x0b, 0xe8,
0x69, 0x06, 0x3f, 0x0c, 0x53, 0xff, 0x0d, 0xfb, 0xb1, 0x0e, 0x59, 0x29,
0xb9, 0x29, 0x43, 0x02, 0x9c, 0xcc, 0xb3, 0xb5, 0xab, 0xd4, 0x77, 0x13,
0x24, 0x41, 0xe0, 0x3c, 0x55, 0x11, 0x2b, 0xe7, 0x8f, 0xdd, 0xbd, 0xef,
0xed, 0xff, 0x4e, 0xfa, 0x52, 0xea, 0xcb, 0xec, 0x71, 0x0d, 0xf7, 0x34,
0x70, 0x3c, 0xed, 0x13, 0x43, 0xd6, 0xef, 0xb3, 0x5a, 0xc8, 0x30, 0x01,
0x18, 0x30, 0x46, 0x34, 0x9e, 0x15, 0x32, 0xf7, 0x86, 0xf2, 0xe3, 0xff,
0x97, 0x03, 0xc6, 0xef, 0xe2, 0xd6, 0x87, 0xd9, 0xc1, 0x02, 0xc4, 0x36,
0x34, 0x49, 0x1a, 0x26, 0xb0, 0xe6, 0x48, 0xbc, 0x0a, 0xc5, 0x03, 0xf3,
0x5b, 0x1c, 0x83, 0x23, 0xb2, 0x0f, 0x70, 0xff, 0x6b, 0x05, 0x91, 0x14,
0x6b, 0x10, 0x2f, 0xef, 0xcc, 0xc9, 0x60, 0xc6, 0xc5, 0xf1, 0xa1, 0x2e,
0xa5, 0x4c, 0xa4, 0x33, 0x2e, 0xf9, 0x79, 0xcc, 0xfd, 0xcb, 0xfd, 0xec,
0x3f, 0x0b, 0xad, 0x0e, 0xd5, 0x00, 0xae, 0xfd, 0x89, 0x11, 0x9f, 0x28,
0x08, 0x23, 0x54, 0xf8, 0x41, 0xc6, 0x5e, 0xb8, 0x5d, 0xdf, 0x07, 0x1f,
0xdd, 0x45, 0xa2, 0x38, 0x78, 0x08, 0x39, 0xe0, 0xa4, 0xdb, 0x13, 0xf1,
0x50, 0x01, 0xfe, 0xfa, 0xa2, 0xec, 0x64, 0xf2, 0xfa, 0x13, 0x0e, 0x37,
0x79, 0x36, 0x7c, 0x08, 0xce, 0xcc, 0x49, 0xb3, 0xb7, 0xd0, 0x79, 0x0c,
0xa9, 0x36, 0x64, 0x33, 0x2f, 0x10, 0x45, 0xf2, 0x2d, 0xf0, 0x87, 0xfe,
0x29, 0x01, 0x5f, 0xed, 0x25, 0xd8, 0x91, 0xe0, 0x75, 0x0c, 0x52, 0x3c,
0x9b, 0x45, 0xf4, 0x1a, 0x79, 0xdb, 0x67, 0xb8, 0x22, 0xca, 0x2b, 0xfc,
0x15, 0x23, 0xf8, 0x24, 0xf5, 0x0d, 0xd1, 0xfd, 0x7e, 0x04, 0x14, 0x12,
0xd2, 0x0a, 0x18, 0xe9, 0x7b, 0xc8, 0xfc, 0xcc, 0x4c, 0xfd, 0x60, 0x37,
0x54, 0x4c, 0x8d, 0x2a, 0xf2, 0xed, 0x7e, 0xc6, 0xa0, 0xcd, 0xd4, 0xf2,
0x42, 0x10, 0xe3, 0x10, 0x08, 0x02, 0xdf, 0xff, 0x82, 0x13, 0xd6, 0x26,
0xb8, 0x1b, 0xe9, 0xee, 0x88, 0xc1, 0xdf, 0xbc, 0xdf, 0xea, 0xda, 0x29,
0xd1, 0x48, 0xc2, 0x32, 0x0d, 0xff, 0xca, 0xd9, 0xab, 0xda, 0x35, 0xf3,
0x2d, 0x03, 0x1a, 0xfc, 0x4e, 0xef, 0xe4, 0xf7, 0x9e, 0x19, 0x99, 0x37,
0x43, 0x2f, 0xfb, 0xfc, 0xc8, 0xc4, 0x98, 0xb4, 0x75, 0xda, 0x97, 0x17,
0x01, 0x3c, 0xff, 0x30, 0xf4, 0x09, 0x3d, 0xed, 0x1f, 0xee, 0x87, 0xfd,
0x39, 0xff, 0xd1, 0xeb, 0x6d, 0xda, 0x1a, 0xe8, 0x86, 0x15, 0x31, 0x40,
0x3b, 0x40, 0x26, 0x0f, 0x23, 0xd1, 0x63, 0xb6, 0xc3, 0xd0, 0xca, 0x05,
0x28, 0x29, 0x72, 0x25, 0x6b, 0x0b, 0xc1, 0xfb, 0x31, 0x03, 0x4c, 0x0f,
0x6d, 0x05, 0xf5, 0xe3, 0xb2, 0xc8, 0xbe, 0xd4, 0xb2, 0x08, 0x9f, 0x3e,
0x06, 0x4a, 0x40, 0x20, 0xfe, 0xe2, 0xdf, 0xc1, 0xba, 0xd0, 0x73, 0xf9,
0x3e, 0x15, 0xc1, 0x12, 0xd4, 0x02, 0x85, 0x01, 0xa9, 0x14, 0x16, 0x24,
0x04, 0x14, 0x45, 0xe6, 0x85, 0xbe, 0x13, 0xc3, 0xe7, 0xf6, 0x98, 0x33,
0xed, 0x49, 0x5e, 0x2b, 0x56, 0xf5, 0x15, 0xd4, 0xc0, 0xda, 0x19, 0xf6,
0x7a, 0x05, 0x84, 0xfd, 0x2f, 0xf2, 0x21, 0xfd, 0x3c, 0x1e, 0xa3, 0x36,
0x07, 0x27, 0xbf, 0xf1, 0x5f, 0xbe, 0xe0, 0xb7, 0x42, 0xe5, 0x4a, 0x22,
0xe1, 0x3f, 0x2b, 0x2d, 0x06, 0x03, 0x4d, 0xe8, 0x81, 0xec, 0xf2, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static void report_func(void *ptr, int code, int level, int delay) {
GString *output = ptr;
printf("code %i level %i delay %i\n", code, level, delay);
g_string_append_printf(output, "code %i level %i delay %i, ", code, level, delay);
}
int main(int argc, char **argv) {
if (htole16(0x1234) != 0x1234) {
printf("Wrong native byte order - skipping test\n");
return 0;
}
GString *output = g_string_new("");
dtmf_rx_state_t *dtmf_dsp;
dtmf_dsp = dtmf_rx_init(NULL, NULL, NULL);
dtmf_rx_set_realtime_callback(dtmf_dsp, report_func, output);
unsigned char *reader = samples;
unsigned char *end = samples + sizeof(samples);
int packetise = 160;
int iter = 0;
while (1) {
unsigned char *packet_end = reader + packetise * 2;
if (packet_end > end)
break;
int ret = dtmf_rx(dtmf_dsp, (void *) reader, packetise);
printf("ret %i\n", ret);
if (ret != 0)
abort();
int digit = dtmf_rx_status(dtmf_dsp);
printf("status %i %c\n", digit, digit);
reader += packetise * 2;
iter++;
}
printf("result: %s\n", output->str);
if (strcmp(output->str, "code 56 level -4 delay 1020, "
"code 0 level -99 delay 918, "
"code 56 level -4 delay 510, "
"code 0 level -99 delay 816, "))
abort();
return 0;
}

Loading…
Cancel
Save