From 910c03fa62a6d4c2bc3a98c5bafa0b339f48cf33 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 15 May 2019 09:39:02 -0400 Subject: [PATCH] TT#58660 PCM to RFC DTMF transcoding Change-Id: Iea6a11c0caad1f5e7dcca966101e2969d3516b6f --- daemon/Makefile | 2 + daemon/call.c | 2 +- daemon/codec.c | 413 ++++++++++++++++++++++---- daemon/dtmf.c | 69 +++++ debian/control | 1 + include/call.h | 2 +- include/codec.h | 4 +- include/dtmf.h | 10 + lib/codeclib.c | 19 +- t/.gitignore | 1 + t/Makefile | 8 +- t/auto-daemon-tests.pl | 381 +++++++++++++++++++++++- t/test-dtmf-detect.c | 649 +++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1494 insertions(+), 67 deletions(-) create mode 100644 t/test-dtmf-detect.c diff --git a/daemon/Makefile b/daemon/Makefile index 79d26a7eb..35adf4a2a 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -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 diff --git a/daemon/call.c b/daemon/call.c index c43fc81c8..20c7794b0 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -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; diff --git a/daemon/codec.c b/daemon/codec.c index 123f4dd94..884500f23 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -3,6 +3,10 @@ #include #include #include +#include +#include +#include +#include #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); diff --git a/daemon/dtmf.c b/daemon/dtmf.c index 6019bcd21..71c277dcf 100644 --- a/daemon/dtmf.c +++ b/daemon/dtmf.c @@ -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; +} diff --git a/debian/control b/debian/control index c1649416f..71bc89292 100644 --- a/debian/control +++ b/debian/control @@ -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, diff --git a/include/call.h b/include/call.h index 106b5bc14..37538405c 100644 --- a/include/call.h +++ b/include/call.h @@ -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 *); diff --git a/include/codec.h b/include/codec.h index 49f06f65b..7c5fde770 100644 --- a/include/codec.h +++ b/include/codec.h @@ -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); diff --git a/include/dtmf.h b/include/dtmf.h index 935514212..a2b9bf660 100644 --- a/include/dtmf.h +++ b/include/dtmf.h @@ -1,12 +1,22 @@ #ifndef _DTMF_H_ #define _DTMF_H_ +#include +#include #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 diff --git a/lib/codeclib.c b/lib/codeclib.c index 296f6def5..86fc1130c 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -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) { +} diff --git a/t/.gitignore b/t/.gitignore index cef8a1293..0adddf88d 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -49,3 +49,4 @@ tests-preload.so timerthread.c media_player.c dtmflib.c +test-dtmf-detect diff --git a/t/Makefile b/t/Makefile index 253330b9f..effb90920 100644 --- a/t/Makefile +++ b/t/Makefile @@ -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 \ diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index 00b4f221b..dd12e10a2 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -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'] } }, < 'remove', replace => ['origin'], + flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], + flags => ['always transcode'] }, < ['origin'] }, < 'remove', replace => ['origin'], + codec => { transcode => ['PCMA', 'telephone-event/8000'] }, + flags => ['always transcode'] }, < ['origin'] }, < +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}