From 94f30b25e992f0a00e3a3181841e1aa80e523ed2 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Thu, 19 Feb 2026 15:27:32 +0300 Subject: [PATCH 1/9] - improving the decoder --- src/engine/agent/Agent_Impl.cpp | 2 + src/engine/endpoint/EP_Session.cpp | 1 + src/engine/endpoint/EP_Session.h | 1 + src/engine/media/MT_AmrCodec.cpp | 30 +++++--- src/engine/media/MT_AmrCodec.h | 30 ++++---- src/engine/media/MT_AudioReceiver.cpp | 83 +++++++++++++++-------- src/engine/media/MT_AudioReceiver.h | 32 +++++---- src/engine/media/MT_SingleAudioStream.cpp | 3 +- src/engine/media/MT_Statistics.h | 1 + 9 files changed, 119 insertions(+), 64 deletions(-) diff --git a/src/engine/agent/Agent_Impl.cpp b/src/engine/agent/Agent_Impl.cpp index 411058d7..ddec55bb 100644 --- a/src/engine/agent/Agent_Impl.cpp +++ b/src/engine/agent/Agent_Impl.cpp @@ -526,6 +526,8 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an answer["rtt"] = result[SessionInfo_Rtt].asFloat(); if (result.exists(SessionInfo_BitrateSwitchCounter)) answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt(); + if (result.exists(SessionInfo_CngCounter)) + answer["cng_counter"] = result[SessionInfo_CngCounter].asInt(); if (result.exists(SessionInfo_SSRC)) answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt(); if (result.exists(SessionInfo_RemotePeer)) diff --git a/src/engine/endpoint/EP_Session.cpp b/src/engine/endpoint/EP_Session.cpp index 41b453ea..95dd70f9 100644 --- a/src/engine/endpoint/EP_Session.cpp +++ b/src/engine/endpoint/EP_Session.cpp @@ -483,6 +483,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info) info[SessionInfo_Rtt] = static_cast(stat.mRttDelay * 1000); #if defined(USE_AMR_CODEC) info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter; + info[SessionInfo_CngCounter] = stat.mCng; #endif info[SessionInfo_SSRC] = stat.mSsrc; info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString(); diff --git a/src/engine/endpoint/EP_Session.h b/src/engine/endpoint/EP_Session.h index 625a3d6e..ac8ac894 100644 --- a/src/engine/endpoint/EP_Session.h +++ b/src/engine/endpoint/EP_Session.h @@ -72,6 +72,7 @@ enum SessionInfo SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only SessionInfo_RemotePeer, SessionInfo_SSRC, + SessionInfo_CngCounter // For AMR codecs only }; diff --git a/src/engine/media/MT_AmrCodec.cpp b/src/engine/media/MT_AmrCodec.cpp index bafe051e..f7136b6a 100644 --- a/src/engine/media/MT_AmrCodec.cpp +++ b/src/engine/media/MT_AmrCodec.cpp @@ -61,7 +61,7 @@ struct AmrPayload // Header // Table of Contents // Frames -static AmrPayload parseAmrPayload(AmrPayloadInfo& input) +static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter) { AmrPayload result; @@ -128,6 +128,8 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input) frame.mTimestamp = input.mCurrentTimestamp; result.mFrames.push_back(frame); input.mCurrentTimestamp += input.mWideband ? 320 : 160; + if (FT == SID_FT) + cngCounter++; } while (F != 0); @@ -140,13 +142,17 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input) // avoid the loss of data synchronization in the depacketization // process, which can result in a huge degradation in speech quality. bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14); - // discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15; if (discard) { result.mDiscardPacket = true; continue; } + if (input.mWideband && f.mMode == 0xFF /* CNG */) + { + int a = 1; + } + if (input.mWideband && f.mFrameType == 15) { // DTX, no sense to decode the data @@ -165,8 +171,8 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input) continue; } - size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType]; - size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType]; + size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType]; + size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType]; if (bitsLength > 0) { @@ -260,8 +266,7 @@ PCodec AmrNbCodec::CodecFactory::create() AmrNbCodec::AmrNbCodec(const AmrCodecConfig& config) - :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config), mCurrentDecoderTimestamp(0), - mSwitchCounter(0), mPreviousPacketLength(0) + :mConfig(config) { mEncoderCtx = Encoder_Interface_init(1); mDecoderCtx = Decoder_Interface_init(); @@ -397,7 +402,7 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp AmrPayload ap; try { - ap = parseAmrPayload(info); + ap = parseAmrPayload(info, mCngCounter); } catch(...) { @@ -459,6 +464,11 @@ int AmrNbCodec::getSwitchCounter() const return mSwitchCounter; } +int AmrNbCodec::getCngCounter() const +{ + return mCngCounter; +} + // -------- AMR WB codec AmrWbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config) :mConfig(config) @@ -600,7 +610,7 @@ int AmrWbCodec::decodePlain(std::span input, std::span o AmrPayload ap; try { - ap = parseAmrPayload(info); + ap = parseAmrPayload(info, mCngCounter); } catch(...) { @@ -674,6 +684,10 @@ int AmrWbCodec::getSwitchCounter() const return mSwitchCounter; } +int AmrWbCodec::getCngCounter() const +{ + return mCngCounter; +} // ------------- GSM EFR ----------------- diff --git a/src/engine/media/MT_AmrCodec.h b/src/engine/media/MT_AmrCodec.h index 00919fd0..dd35192e 100644 --- a/src/engine/media/MT_AmrCodec.h +++ b/src/engine/media/MT_AmrCodec.h @@ -26,13 +26,13 @@ struct AmrCodecConfig class AmrNbCodec : public Codec { protected: - void* mEncoderCtx; - void* mDecoderCtx; + void* mEncoderCtx = nullptr; + void* mDecoderCtx = nullptr; AmrCodecConfig mConfig; - unsigned mCurrentDecoderTimestamp; - int mSwitchCounter; - int mPreviousPacketLength; - + unsigned mCurrentDecoderTimestamp = 0; + int mPreviousPacketLength = 0; + size_t mCngCounter = 0; + size_t mSwitchCounter = 0; public: class CodecFactory: public Factory { @@ -65,6 +65,7 @@ public: int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; int plc(int lostFrames, void* output, int outputCapacity) override; int getSwitchCounter() const; + int getCngCounter() const; }; struct AmrWbStatistics @@ -77,11 +78,13 @@ extern AmrWbStatistics GAmrWbStatistics; class AmrWbCodec : public Codec { protected: - void* mEncoderCtx; - void* mDecoderCtx; + void* mEncoderCtx = nullptr; + void* mDecoderCtx = nullptr; AmrCodecConfig mConfig; - uint64_t mCurrentDecoderTimestamp; - int mSwitchCounter; + uint64_t mCurrentDecoderTimestamp = 0; + size_t mSwitchCounter = 0; + size_t mCngCounter = 0; + int mPreviousPacketLength; int decodeIuup(std::span input, std::span output); @@ -119,14 +122,15 @@ public: int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; int plc(int lostFrames, void* output, int outputCapacity) override; int getSwitchCounter() const; + int getCngCounter() const; }; class GsmEfrCodec : public Codec { protected: - void* mEncoderCtx; - void* mDecoderCtx; - bool mIuUP; + void* mEncoderCtx = nullptr; + void* mDecoderCtx = nullptr; + bool mIuUP = false; public: class GsmEfrFactory: public Factory diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index f88b65aa..6021687f 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -553,7 +553,7 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind } } -AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output, DecodeOptions options) +AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output, DecodeOptions options) { ICELogDebug(<< "Gap detected."); @@ -588,20 +588,21 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output, if (mDecodedLength) { processDecoded(output, options); - return DecodeResult_Ok; + return {.mStatus = DecodeResult::Status::Ok,.mChannels = mCodec->channels(), .mSamplerate = mCodec->samplerate()}; } else - return DecodeResult_Skip; + return {.mStatus = DecodeResult::Status::Skip}; } -AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate) +AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const RtpBuffer::ResultList& rl) { - DecodeResult result = DecodeResult_Skip; + DecodeResult result = {.mStatus = DecodeResult::Status::Skip}; mFailedCount = 0; for (const std::shared_ptr& p: rl) { assert(p); + // Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed. if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) { @@ -635,8 +636,8 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultL mCodec = codecIter->second; if (mCodec) { - if (rate) - *rate = mCodec->samplerate(); + result.mChannels = mCodec->channels(); + result.mSamplerate = mCodec->samplerate(); // Check if it is CNG packet if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6) @@ -654,7 +655,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultL if (mDecodedLength) processDecoded(output, options); } - result = DecodeResult_Ok; + result.mStatus = DecodeResult::Status::Ok; } else { @@ -691,7 +692,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultL processDecoded(output, options); } } - result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip; + result.mStatus = mFrameCount > 0 ? DecodeResult::Status::Ok : DecodeResult::Status::Skip; // Check for bitrate counter updateAmrCodecStats(mCodec.get()); @@ -699,7 +700,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultL else { // RTP packet with tail - it should not happen - result = DecodeResult_BadPacket; + result.mStatus = DecodeResult::Status::BadPacket; } } } @@ -707,39 +708,55 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultL return result; } -AudioReceiver::DecodeResult AudioReceiver::decodeNone(Audio::DataWindow& output, DecodeOptions options) +AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options) { - // ICELogDebug(<< "No packet available in jitter buffer"); + // No packet available in jitter buffer - just increase the counter for now mFailedCount++; - return DecodeResult_Skip; + return {.mStatus = DecodeResult::Status::Skip}; } -AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, DecodeOptions options, int* rate) +AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, DecodeOptions options) { - DecodeResult result = DecodeResult_Skip; + DecodeResult result = {.mStatus = DecodeResult::Status::Skip}; - // Get next packet from buffer - RtpBuffer::ResultList rl; - RtpBuffer::FetchResult fr = mBuffer.fetch(rl); - switch (fr) + size_t initialOffset = output.filled(); // Size in bytes + std::chrono::milliseconds decoded = 0ms; + do { - case RtpBuffer::FetchResult::Gap: result = decodeGap(output, options); break; - case RtpBuffer::FetchResult::NoPacket: result = decodeNone(output, options); break; - case RtpBuffer::FetchResult::RegularPacket: result = decodePacket(rl, output, options, rate); break; - default: - assert(0); - } + // Get next packet from buffer + RtpBuffer::ResultList rl; + RtpBuffer::FetchResult fr = mBuffer.fetch(rl); + switch (fr) + { + case RtpBuffer::FetchResult::Gap: result = decodeGapTo(output, options); break; + case RtpBuffer::FetchResult::NoPacket: result = decodeEmptyTo(output, options); break; + case RtpBuffer::FetchResult::RegularPacket: result = decodePacketTo(output, options, rl); break; + default: + assert(0); + } - if (result == DecodeResult_Ok) + size_t available = output.filled() - initialOffset; + if (!available) + break; + initialOffset = output.filled(); + + // ToDo: calculate how much milliseconds was decoded + int samplerate = options.mResampleToMainRate ? AUDIO_SAMPLERATE : result.mSamplerate; + decoded += std::chrono::milliseconds(available / sizeof(short) / (samplerate / 1000)); + } + while (decoded < options.mElapsed); + + // Time statistics + if (result.mStatus == DecodeResult::Status::Ok) { // Decode statistics - if (!mLastDecodeTimestamp) - mLastDecodeTimestamp = std::chrono::steady_clock::now(); + if (!mDecodeTimestamp) + mDecodeTimestamp = std::chrono::steady_clock::now(); else { auto t = std::chrono::steady_clock::now(); - mStat.mDecodingInterval.process(std::chrono::duration_cast(t - *mLastDecodeTimestamp).count()); - mLastDecodeTimestamp = t; + mStat.mDecodingInterval.process(std::chrono::duration_cast(t - *mDecodeTimestamp).count()); + mDecodeTimestamp = t; } } return result; @@ -795,10 +812,16 @@ void AudioReceiver::updateAmrCodecStats(Codec* c) AmrWbCodec* wb = dynamic_cast(c); if (nb != nullptr) + { mStat.mBitrateSwitchCounter = nb->getSwitchCounter(); + mStat.mCng = nb->getCngCounter(); + } else if (wb != nullptr) + { mStat.mBitrateSwitchCounter = wb->getSwitchCounter(); + mStat.mCng = wb->getCngCounter(); + } #endif } diff --git a/src/engine/media/MT_AudioReceiver.h b/src/engine/media/MT_AudioReceiver.h index 1859bdfd..26c8d6d6 100644 --- a/src/engine/media/MT_AudioReceiver.h +++ b/src/engine/media/MT_AudioReceiver.h @@ -144,19 +144,27 @@ public: struct DecodeOptions { - bool mResampleToMainRate = true; - bool mFillGapByCNG = false; - bool mSkipDecode = false; + bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE + bool mFillGapByCNG = false; // Use CNG information if available + bool mSkipDecode = false; // Don't do decode, just dry run - fetch packets, remove them from the jitter buffer + std::chrono::milliseconds mElapsed = 0ms; // How much milliseconds should be decoded; zero value means "decode just next packet from the buffer" }; - enum DecodeResult + struct DecodeResult { - DecodeResult_Ok, // Decoded ok - DecodeResult_Skip, // Just no data - emit silence instead - DecodeResult_BadPacket // Error happened during the decode + enum class Status + { + Ok, // Decoded ok + Skip, // Just no data - emit silence instead + BadPacket // Error happened during the decode + }; + + Status mStatus = Status::Ok; + int mSamplerate = 0; + int mChannels = 0; }; - DecodeResult getAudio(Audio::DataWindow& output, DecodeOptions options = {.mResampleToMainRate = true, .mFillGapByCNG = false, .mSkipDecode = false}, int* rate = nullptr); + DecodeResult getAudioTo(Audio::DataWindow& output, DecodeOptions options); // Looks for codec by payload type Codec* findCodec(int payloadType); @@ -204,7 +212,7 @@ protected: Audio::PWavFileWriter mDecodedDump; - std::optional mLastDecodeTimestamp; // Time last call happened to codec->decode() + std::optional mDecodeTimestamp; // Time last call happened to codec->decode() float mIntervalSum = 0.0f; int mIntervalCount = 0; @@ -220,9 +228,9 @@ protected: // Calculate bitrate switch statistics for AMR codecs void updateAmrCodecStats(Codec* c); - DecodeResult decodeGap(Audio::DataWindow& output, DecodeOptions options); - DecodeResult decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate = nullptr); - DecodeResult decodeNone(Audio::DataWindow& output, DecodeOptions options); + DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options); + DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const RtpBuffer::ResultList& rl); + DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options); }; class DtmfReceiver: public Receiver diff --git a/src/engine/media/MT_SingleAudioStream.cpp b/src/engine/media/MT_SingleAudioStream.cpp index effdda73..0baa426e 100644 --- a/src/engine/media/MT_SingleAudioStream.cpp +++ b/src/engine/media/MT_SingleAudioStream.cpp @@ -33,9 +33,10 @@ void SingleAudioStream::process(const std::shared_ptr& packe void SingleAudioStream::copyPcmTo(Audio::DataWindow& output, int needed) { + // Packet by packet while (output.filled() < needed) { - if (mReceiver.getAudio(output, {}) != AudioReceiver::DecodeResult_Ok) + if (mReceiver.getAudioTo(output, {}).mStatus != AudioReceiver::DecodeResult::Status::Ok) break; } diff --git a/src/engine/media/MT_Statistics.h b/src/engine/media/MT_Statistics.h index cf2c505e..d637f6e2 100644 --- a/src/engine/media/MT_Statistics.h +++ b/src/engine/media/MT_Statistics.h @@ -86,6 +86,7 @@ public: // AMR codec bitrate switch counter int mBitrateSwitchCounter = 0; + int mCng = 0; std::string mCodecName; float mJitter = 0.0f; // Jitter TestResult mRttDelay; // RTT delay From 78d77c4e697064f4c6a40e2d73cbc323433a02d0 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Fri, 20 Feb 2026 13:16:13 +0300 Subject: [PATCH 2/9] - work to improve the decoding process - many problems fixes + however there are problems yet --- src/engine/audio/Audio_DataWindow.cpp | 21 +- src/engine/audio/Audio_DataWindow.h | 41 ++-- src/engine/audio/Audio_Interface.h | 5 + src/engine/engine_config.h | 20 +- src/engine/media/MT_AmrCodec.cpp | 65 +++--- src/engine/media/MT_AudioCodec.cpp | 14 +- src/engine/media/MT_AudioCodec.h | 73 ++++--- src/engine/media/MT_AudioReceiver.cpp | 301 +++++++++++++++----------- src/engine/media/MT_AudioReceiver.h | 59 +++-- src/engine/media/MT_Codec.cpp | 24 +- src/engine/media/MT_Codec.h | 25 ++- 11 files changed, 375 insertions(+), 273 deletions(-) diff --git a/src/engine/audio/Audio_DataWindow.cpp b/src/engine/audio/Audio_DataWindow.cpp index f4036154..c33f5040 100644 --- a/src/engine/audio/Audio_DataWindow.cpp +++ b/src/engine/audio/Audio_DataWindow.cpp @@ -11,7 +11,7 @@ using namespace Audio; DataWindow::DataWindow() { mFilled = 0; - mData = NULL; + mData = nullptr; mCapacity = 0; } @@ -166,6 +166,25 @@ void DataWindow::zero(int length) memset(mData, 0, mFilled); } +size_t DataWindow::moveTo(DataWindow& dst, size_t size) +{ + Lock l(mMutex); + + size_t avail = std::min(size, (size_t)filled()); + if (avail != 0) + { + dst.add(mData, avail); + erase(avail); + } + return avail; +} + +std::chrono::milliseconds DataWindow::getTimeLength(int samplerate, int channels) const +{ + Lock l(mMutex); + return std::chrono::milliseconds(mFilled / sizeof(short) / channels / (samplerate / 1000)); +} + void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src) { Lock lockDst(dst.mMutex), lockSrc(src.mMutex); diff --git a/src/engine/audio/Audio_DataWindow.h b/src/engine/audio/Audio_DataWindow.h index a0e7f306..1d13e52e 100644 --- a/src/engine/audio/Audio_DataWindow.h +++ b/src/engine/audio/Audio_DataWindow.h @@ -11,37 +11,40 @@ namespace Audio { - class DataWindow - { - public: +class DataWindow +{ +public: DataWindow(); ~DataWindow(); - void setCapacity(int capacity); - int capacity() const; + void setCapacity(int capacity); + int capacity() const; - void addZero(int length); - void add(const void* data, int length); - void add(short sample); - int read(void* buffer, int length); - void erase(int length = -1); + void addZero(int length); + void add(const void* data, int length); + void add(short sample); + int read(void* buffer, int length); + void erase(int length = -1); const char* data() const; - char* mutableData(); - int filled() const; - void setFilled(int filled); - void clear(); + char* mutableData(); + int filled() const; + void setFilled(int filled); + void clear(); - short shortAt(int index) const; - void setShortAt(short value, int index); - void zero(int length); + short shortAt(int index) const; + void setShortAt(short value, int index); + void zero(int length); + size_t moveTo(DataWindow& dst, size_t size); + + std::chrono::milliseconds getTimeLength(int samplerate, int channels) const; static void makeStereoFromMono(DataWindow& dst, DataWindow& src); - protected: +protected: mutable Mutex mMutex; char* mData; int mFilled; int mCapacity; - }; +}; } #endif diff --git a/src/engine/audio/Audio_Interface.h b/src/engine/audio/Audio_Interface.h index 82f0f864..e08f763d 100644 --- a/src/engine/audio/Audio_Interface.h +++ b/src/engine/audio/Audio_Interface.h @@ -51,6 +51,11 @@ struct Format return float((milliseconds * mRate) / 500.0 * mChannels); } + size_t sizeFromTime(std::chrono::milliseconds ms) const + { + return sizeFromTime(ms.count()); + } + std::string toString() { char buffer[64]; diff --git a/src/engine/engine_config.h b/src/engine/engine_config.h index 8c78d164..3433e1e1 100644 --- a/src/engine/engine_config.h +++ b/src/engine/engine_config.h @@ -38,11 +38,11 @@ //#define AUDIO_DUMPOUTPUT -#define UA_REGISTRATION_TIME 3600 -#define UA_MEDIA_PORT_START 20000 -#define UA_MEDIA_PORT_FINISH 30000 -#define UA_MAX_UDP_PACKET_SIZE 576 -#define UA_PUBLICATION_ID "314" +#define UA_REGISTRATION_TIME 3600 +#define UA_MEDIA_PORT_START 20000 +#define UA_MEDIA_PORT_FINISH 30000 +#define UA_MAX_UDP_PACKET_SIZE 576 +#define UA_PUBLICATION_ID "314" #define MT_SAMPLERATE AUDIO_SAMPLERATE @@ -50,13 +50,11 @@ #define MT_MAXRTPPACKET 1500 #define MT_DTMF_END_PACKETS 3 -#define RTP_BUFFER_HIGH 0 -#define RTP_BUFFER_LOW 0 -#define RTP_BUFFER_PREBUFFER 0 +// Milliseconds before +#define RTP_BUFFER_HIGH (2000) +#define RTP_BUFFER_LOW (0) +#define RTP_BUFFER_PREBUFFER (100) -// #define RTP_BUFFER_HIGH 160 -// #define RTP_BUFFER_LOW 10 -// #define RTP_BUFFER_PREBUFFER 160 #define RTP_DECODED_CAPACITY 2048 #define DEFAULT_SUBSCRIPTION_TIME 1200 diff --git a/src/engine/media/MT_AmrCodec.cpp b/src/engine/media/MT_AmrCodec.cpp index f7136b6a..4085360a 100644 --- a/src/engine/media/MT_AmrCodec.cpp +++ b/src/engine/media/MT_AmrCodec.cpp @@ -31,30 +31,30 @@ const uint16_t amrwb_framelenbits[10] = struct AmrPayloadInfo { - const uint8_t* mPayload; - int mPayloadLength; - bool mOctetAligned; - bool mInterleaving; - bool mWideband; - uint64_t mCurrentTimestamp; + const uint8_t* mPayload = nullptr; + int mPayloadLength = 0; + bool mOctetAligned = false; + bool mInterleaving = false; + bool mWideband = false; + uint64_t mCurrentTimestamp = 0; }; struct AmrFrame { - uint8_t mFrameType; - uint8_t mMode; - bool mGoodQuality; - uint64_t mTimestamp; + uint8_t mFrameType = 0; + uint8_t mMode = 0; + bool mGoodQuality = false; + uint64_t mTimestamp = 0; std::shared_ptr mData; - uint8_t mSTI; + uint8_t mSTI = 0; }; struct AmrPayload { - uint8_t mCodeModeRequest; + uint8_t mCodeModeRequest = 0; std::vector mFrames; - bool mDiscardPacket; + bool mDiscardPacket = false; }; // ARM RTP payload has next structure @@ -148,10 +148,10 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter) continue; } - if (input.mWideband && f.mMode == 0xFF /* CNG */) - { - int a = 1; - } + // if (input.mWideband && f.mMode == 0xFF /* CNG */) + // { + // int a = 1; + // } if (input.mWideband && f.mFrameType == 15) { @@ -600,12 +600,12 @@ int AmrWbCodec::decodeIuup(std::span input, std::span ou int AmrWbCodec::decodePlain(std::span input, std::span output) { AmrPayloadInfo info; - info.mCurrentTimestamp = mCurrentDecoderTimestamp; - info.mOctetAligned = mConfig.mOctetAligned; - info.mPayload = input.data(); - info.mPayloadLength = input.size(); - info.mWideband = true; - info.mInterleaving = false; + info.mCurrentTimestamp = mCurrentDecoderTimestamp; + info.mOctetAligned = mConfig.mOctetAligned; + info.mPayload = input.data(); + info.mPayloadLength = input.size(); + info.mWideband = true; + info.mInterleaving = false; AmrPayload ap; try @@ -628,21 +628,30 @@ int AmrWbCodec::decodePlain(std::span input, std::span o return 0; } - // Check for output buffer capacity - if (output.size() < (int)ap.mFrames.size() * pcmLength()) + // Find the required output capacity + size_t capacity = 0; + for (AmrFrame& frame: ap.mFrames) + capacity += frame.mMode == 0xFF /* CNG */ ? pcmLength() * 8 : pcmLength(); + + if (output.size() < capacity) return 0; short* dataOut = (short*)output.data(); size_t dataOutSizeInBytes = 0; for (AmrFrame& frame: ap.mFrames) { - memset(dataOut, 0, static_cast(pcmLength())); + size_t frameOutputSize = frame.mMode == 0xFF ? pcmLength() * 8 : pcmLength(); + memset(dataOut, 0, frameOutputSize); if (frame.mData) { + if (frame.mMode == 0xFF) + { + // int bp = 1; + } D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0); - dataOut += pcmLength() / 2; - dataOutSizeInBytes += pcmLength(); + dataOut += frameOutputSize / 2; + dataOutSizeInBytes += frameOutputSize; } } return dataOutSizeInBytes; diff --git a/src/engine/media/MT_AudioCodec.cpp b/src/engine/media/MT_AudioCodec.cpp index 36ee8fd7..e08e65f4 100644 --- a/src/engine/media/MT_AudioCodec.cpp +++ b/src/engine/media/MT_AudioCodec.cpp @@ -635,10 +635,10 @@ int IlbcCodec::samplerate() return 8000; } -int IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int outputCapacity) +Codec::EncodeResult IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int outputCapacity) { if (inputBytes % pcmLength()) - return 0; + return {}; // Declare the data input pointer short *dataIn = (short *)input; @@ -657,10 +657,10 @@ int IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int dataOut += rtpLength(); } - return frames * rtpLength(); + return {frames * rtpLength()}; } -int IlbcCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult IlbcCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) { unsigned frames = inputBytes / rtpLength(); @@ -675,12 +675,12 @@ int IlbcCodec::decode(const void* input, int inputBytes, void* output, int outpu dataOut += pcmLength() / 2; } - return frames * pcmLength(); + return {frames * pcmLength()}; } -int IlbcCodec::plc(int lostFrames, void* output, int outputCapacity) +int IlbcCodec::plc(int lostFrames, std::span output) { - return 2 * WebRtcIlbcfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output, lostFrames); + return sizeof(short) * WebRtcIlbcfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output.data(), lostFrames); } // --- IlbcFactory --- diff --git a/src/engine/media/MT_AudioCodec.h b/src/engine/media/MT_AudioCodec.h index 049d24b4..da11bec4 100644 --- a/src/engine/media/MT_AudioCodec.h +++ b/src/engine/media/MT_AudioCodec.h @@ -58,9 +58,10 @@ public: int frameTime() override; int samplerate() override; int channels() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; class OpusCodec: public Codec @@ -112,9 +113,10 @@ public: int frameTime(); int samplerate(); int channels(); - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostFrames, void* output, int outputCapacity); + + EncodeResult encode(std::span input, std::span output); + DecodeResult decode(std::span input, std::span output); + size_t plc(int lostFrames, std::span output); }; @@ -146,14 +148,15 @@ public: IlbcCodec(int packetTime); virtual ~IlbcCodec(); - const char* name(); - int pcmLength(); - int rtpLength(); - int frameTime(); - int samplerate(); - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostFrames, void* output, int outputCapacity); + const char* name() override; + int pcmLength() override; + int rtpLength() override; + int frameTime() override; + int samplerate() override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; class G711Codec: public Codec @@ -186,15 +189,15 @@ public: G711Codec(int type); ~G711Codec(); - const char* name(); - int pcmLength(); - int frameTime(); - int rtpLength(); - int samplerate(); + const char* name() override; + int pcmLength() override; + int frameTime() override; + int rtpLength() override; + int samplerate() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostSamples, void* output, int outputCapacity); + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostSamples, std::span output) override ; protected: int mType; /// Determines if it is u-law or a-law codec. Its value is ALaw or ULaw. @@ -237,15 +240,15 @@ public: IsacCodec(int sampleRate); ~IsacCodec(); - const char* name(); - int pcmLength(); - int rtpLength(); - int frameTime(); - int samplerate(); + const char* name() override; + int pcmLength() override; + int rtpLength() override; + int frameTime() override; + int samplerate() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostFrames, void* output, int outputCapacity); + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; @@ -311,11 +314,11 @@ public: /*! Destructor. */ virtual ~GsmCodec(); - const char* name(); - int pcmLength(); - int rtpLength(); - int frameTime(); - int samplerate(); + const char* name() override; + int pcmLength() override; + int rtpLength() override; + int frameTime() override; + int samplerate() override; int encode(const void* input, int inputBytes, void* output, int outputCapacity); int decode(const void* input, int inputBytes, void* output, int outputCapacity); diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 6021687f..6f21e5c2 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -115,7 +115,7 @@ bool SequenceSort(const std::shared_ptr& p1, const std::share return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber(); } -std::shared_ptr RtpBuffer::add(std::shared_ptr packet, std::chrono::milliseconds timelength, int rate) +std::shared_ptr RtpBuffer::add(const std::shared_ptr& packet, std::chrono::milliseconds timelength, int rate) { if (!packet) return std::shared_ptr(); @@ -191,12 +191,11 @@ std::shared_ptr RtpBuffer::add(std::shared_ptr(); } -RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) +RtpBuffer::FetchResult RtpBuffer::fetch() { Lock l(mGuard); - FetchResult result = FetchResult::NoPacket; - rl.clear(); + FetchResult result; // See if there is enough information in buffer auto total = findTimelength(); @@ -217,10 +216,10 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) mStat.mPacketDropped++; } - if (total < mLow) + if (total < mLow || total == 0ms) { // Still not prebuffered - result = FetchResult::NoPacket; + result = {FetchResult::Status::NoPacket}; } else { @@ -228,8 +227,8 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) { if (mPacketList.empty()) { - result = FetchResult::NoPacket; // Don't increase counter of lost packets here; maybe it is DTX + result = {FetchResult::Status::NoPacket}; } else { @@ -237,7 +236,6 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) auto& packet = *mPacketList.front(); uint32_t seqno = packet.rtp()->GetExtendedSequenceNumber(); - // Gap between new packet and previous on int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1; gap = std::min(gap, 127); @@ -255,16 +253,15 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) mLastSeqno = *mLastSeqno + 1; // As we deal with the audio gap - return the silence and increase last seqno - result = FetchResult::Gap; + result = {FetchResult::Status::Gap}; } else { - result = FetchResult::RegularPacket; - rl.push_back(mPacketList.front()); + result = {FetchResult::Status::RegularPacket, mPacketList.front()}; // Save last returned normal packet - mFetchedPacket = mPacketList.front(); - mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); + mFetchedPacket = result.mPacket; + mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber(); // Remove returned packet from the list mPacketList.erase(mPacketList.begin()); @@ -277,14 +274,11 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) if (findTimelength() >= mPrebuffer && !mPacketList.empty()) { // Normal packet will be returned - result = FetchResult::RegularPacket; - - // Put it to output list - rl.push_back(mPacketList.front()); + result = {FetchResult::Status::RegularPacket, mPacketList.front()}; // Remember returned packet - mFetchedPacket = mPacketList.front(); - mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); + mFetchedPacket = result.mPacket; + mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber(); // Remove returned packet from buffer list mPacketList.erase(mPacketList.begin()); @@ -292,12 +286,12 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) else { ICELogMedia(<< "Jitter buffer was not prebuffered yet; resulting no packet"); - result = FetchResult::NoPacket; + result = {FetchResult::Status::NoPacket}; } } } - if (result != FetchResult::NoPacket) + if (result.mStatus != FetchResult::Status::NoPacket) mReturnedCounter++; return result; @@ -333,8 +327,7 @@ Receiver::~Receiver() //-------------- AudioReceiver ---------------- AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat) - :Receiver(stat), mBuffer(stat), mCodecSettings(settings), - mCodecList(settings) + :Receiver(stat), mBuffer(stat), mCodecSettings(settings), mCodecList(settings) { // Init resamplers mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE); @@ -346,6 +339,8 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics mCodecList.setSettings(settings); mCodecList.fillCodecMap(mCodecMap); + mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short)); + #if defined(DUMP_DECODED) mDecodedDump = std::make_shared(); mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS); @@ -560,10 +555,14 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output mDecodedLength = mResampledLength = 0; if (mCngPacket && mCodec) { - // Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer. - // Do not forget to send this noise to analysis - mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, - reinterpret_cast(mDecodedFrame), false); + if (mCngPacket->rtp()->GetPayloadType() == 13) + { + // Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer. + // Do not forget to send this noise to analysis + mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, reinterpret_cast(mDecodedFrame), false); + } + else + decodePacketTo(output, options, mCngPacket); } else if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode) @@ -594,114 +593,110 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output return {.mStatus = DecodeResult::Status::Skip}; } -AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const RtpBuffer::ResultList& rl) +AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr& packet) { + if (!packet || !packet->rtp()) + return {DecodeResult::Status::Skip}; + DecodeResult result = {.mStatus = DecodeResult::Status::Skip}; + auto& rtp = *packet->rtp(); // Syntax sugar mFailedCount = 0; - for (const std::shared_ptr& p: rl) + // Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed. + if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) { - assert(p); - - // Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed. - if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) + int units = rtp.GetTimestamp() - *mLastPacketTimestamp; + int milliseconds = units / (mCodec->samplerate() / 1000); + if (milliseconds > mLastPacketTimeLength) { - int units = p->rtp()->GetTimestamp() - *mLastPacketTimestamp; - int milliseconds = units / (mCodec->samplerate() / 1000); - if (milliseconds > mLastPacketTimeLength) - { - auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength); + auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength); - if (mCngPacket && options.mFillGapByCNG) - produceCNG(silenceLength, output, options); - else - produceSilence(silenceLength, output, options); - } + if (mCngPacket && options.mFillGapByCNG) + produceCNG(silenceLength, output, options); + else + produceSilence(silenceLength, output, options); } + } - mLastPacketTimestamp = p->rtp()->GetTimestamp(); + mLastPacketTimestamp = rtp.GetTimestamp(); - // Find codec by payload type - int ptype = p->rtp()->GetPayloadType(); + // Find codec by payload type + int ptype = rtp.GetPayloadType(); - // Look into mCodecMap if exists - auto codecIter = mCodecMap.find(ptype); - if (codecIter == mCodecMap.end()) - return {}; + // Look into mCodecMap if exists + auto codecIter = mCodecMap.find(ptype); + if (codecIter == mCodecMap.end()) + return {}; + if (!codecIter->second) + codecIter->second = mCodecList.createCodecByPayloadType(ptype); - if (!codecIter->second) - codecIter->second = mCodecList.createCodecByPayloadType(ptype); + mCodec = codecIter->second; + if (mCodec) + { + result.mChannels = mCodec->channels(); + result.mSamplerate = mCodec->samplerate(); - mCodec = codecIter->second; - if (mCodec) + // Check if it is CNG packet + if (((ptype == 0 || ptype == 8) && rtp.GetPayloadLength() >= 1 && rtp.GetPayloadLength() <= 6) || rtp.GetPayloadType() == 13) { - result.mChannels = mCodec->channels(); - result.mSamplerate = mCodec->samplerate(); - - // Check if it is CNG packet - if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6) + if (options.mSkipDecode) + mDecodedLength = 0; + else { - if (options.mSkipDecode) - mDecodedLength = 0; - else - { - mCngPacket = p->rtp(); - mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength()); + mCngPacket = packet; + mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength()); - // Emit CNG mLastPacketLength milliseconds - mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, - (short*)mDecodedFrame, true); - if (mDecodedLength) - processDecoded(output, options); + // Emit CNG mLastPacketLength milliseconds + mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, (short*)mDecodedFrame, true); + if (mDecodedLength) + processDecoded(output, options); + } + result.mStatus = DecodeResult::Status::Ok; + } + else + { + // Reset CNG packet as we get regular RTP packet + mCngPacket.reset(); + + // Handle here regular RTP packets + // Check if payload length is ok + size_t payload_length = rtp.GetPayloadLength(); + size_t rtp_frame_length = mCodec->rtpLength(); + + int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0; + + if (!tail) + { + // Find number of frames + mFrameCount = mCodec->rtpLength() ? rtp.GetPayloadLength() / mCodec->rtpLength() : 1; + int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)rtp.GetPayloadLength(); + + // Save last packet time length + mLastPacketTimeLength = mFrameCount * mCodec->frameTime(); + + // Decode + for (int i=0; idecode(rtp.GetPayloadData() + i * mCodec->rtpLength(), frameLength, mDecodedFrame, sizeof mDecodedFrame); + if (mDecodedLength > 0) + processDecoded(output, options); + } } - result.mStatus = DecodeResult::Status::Ok; + result.mStatus = mFrameCount > 0 ? DecodeResult::Status::Ok : DecodeResult::Status::Skip; + + // Check for bitrate counter + updateAmrCodecStats(mCodec.get()); } else { - // Reset CNG packet as we get regular RTP packet - mCngPacket.reset(); - - // Handle here regular RTP packets - // Check if payload length is ok - size_t payload_length = p->rtp()->GetPayloadLength(); - size_t rtp_frame_length = mCodec->rtpLength(); - - int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0; - - if (!tail) - { - // Find number of frames - mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1; - int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength(); - - // Save last packet time length - mLastPacketTimeLength = mFrameCount * mCodec->frameTime(); - - // Decode - for (int i=0; idecode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(), - frameLength, mDecodedFrame, sizeof mDecodedFrame); - if (mDecodedLength > 0) - processDecoded(output, options); - } - } - result.mStatus = mFrameCount > 0 ? DecodeResult::Status::Ok : DecodeResult::Status::Skip; - - // Check for bitrate counter - updateAmrCodecStats(mCodec.get()); - } - else - { - // RTP packet with tail - it should not happen - result.mStatus = DecodeResult::Status::BadPacket; - } + // RTP packet with tail - it should not happen + result.mStatus = DecodeResult::Status::BadPacket; } } } @@ -710,7 +705,16 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options) { - // No packet available in jitter buffer - just increase the counter for now + // No packet available at all (and no previous CNG packet) - so return the silence + if (options.mElapsed != 0ms && mCodec) + { + Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat(); + // Emit silence if codec information is available - it is to properly handle the gaps + auto avail = output.getTimeLength(fmt.rate(), fmt.channels()); + if (options.mElapsed > avail) + mAvailable.addZero(fmt.sizeFromTime(options.mElapsed - avail)); + } + mFailedCount++; return {.mStatus = DecodeResult::Status::Skip}; } @@ -719,32 +723,71 @@ AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, { DecodeResult result = {.mStatus = DecodeResult::Status::Skip}; - size_t initialOffset = output.filled(); // Size in bytes + auto produced = 0ms; + if (mAvailable.filled() && mCodec && options.mElapsed != 0ms) + { + Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat(); + auto initiallyAvailable = mCodec ? mAvailable.getTimeLength(fmt.rate(), fmt.channels()) : 0ms; + if (initiallyAvailable != 0ms) + { + std::chrono::milliseconds resultTime = std::min(initiallyAvailable, options.mElapsed); + auto resultLen = fmt.sizeFromTime(resultTime); + mAvailable.moveTo(output, resultLen); + produced += resultTime; + + // Maybe request is satisfied ? + if (produced >= options.mElapsed) + return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()}; + } + } + std::chrono::milliseconds decoded = 0ms; do { // Get next packet from buffer RtpBuffer::ResultList rl; - RtpBuffer::FetchResult fr = mBuffer.fetch(rl); - switch (fr) + RtpBuffer::FetchResult fr = mBuffer.fetch(); + // ICELogDebug(<< fr.toString() << " " << mBuffer.findTimelength()); + + switch (fr.mStatus) { - case RtpBuffer::FetchResult::Gap: result = decodeGapTo(output, options); break; - case RtpBuffer::FetchResult::NoPacket: result = decodeEmptyTo(output, options); break; - case RtpBuffer::FetchResult::RegularPacket: result = decodePacketTo(output, options, rl); break; + case RtpBuffer::FetchResult::Status::Gap: result = decodeGapTo(mAvailable, options); break; + case RtpBuffer::FetchResult::Status::NoPacket: result = decodeEmptyTo(mAvailable, options); break; + case RtpBuffer::FetchResult::Status::RegularPacket: result = decodePacketTo(mAvailable, options, fr.mPacket); break; default: assert(0); } - size_t available = output.filled() - initialOffset; - if (!available) - break; - initialOffset = output.filled(); + // Was there decoding at all ? + if (!mCodec) + break; // No sense to continue - we have no information at all - // ToDo: calculate how much milliseconds was decoded - int samplerate = options.mResampleToMainRate ? AUDIO_SAMPLERATE : result.mSamplerate; - decoded += std::chrono::milliseconds(available / sizeof(short) / (samplerate / 1000)); + Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat(); + result.mSamplerate = fmt.rate(); + result.mChannels = fmt.channels(); + + // Have we anything interesting in the buffer ? + auto bufferAvailable = mAvailable.getTimeLength(fmt.rate(), fmt.channels()); + if (bufferAvailable == 0ms) + break; // No sense to continue - decoding / CNG / PLC stopped totally + + // How much data should be moved to result buffer ? + if (options.mElapsed != 0ms) + { + std::chrono::milliseconds resultTime = std::min(bufferAvailable, options.mElapsed - produced); + auto resultLen = fmt.sizeFromTime(resultTime); + mAvailable.moveTo(output, resultLen); + produced += resultTime; + } + else + mAvailable.moveTo(output, mAvailable.filled()); + + decoded += bufferAvailable; } - while (decoded < options.mElapsed); + while (produced < options.mElapsed); + + if (produced != 0ms) + result.mStatus = DecodeResult::Status::Ok; // Time statistics if (result.mStatus == DecodeResult::Status::Ok) diff --git a/src/engine/media/MT_AudioReceiver.h b/src/engine/media/MT_AudioReceiver.h index 26c8d6d6..ed76c310 100644 --- a/src/engine/media/MT_AudioReceiver.h +++ b/src/engine/media/MT_AudioReceiver.h @@ -28,13 +28,6 @@ using jrtplib::RTPPacket; class RtpBuffer { public: - enum class FetchResult - { - RegularPacket, - Gap, - NoPacket - }; - // Owns rtp packet data class Packet { @@ -59,6 +52,29 @@ public: std::chrono::microseconds mTimestamp = 0us; }; + struct FetchResult + { + enum class Status + { + RegularPacket, + Gap, + NoPacket + }; + + Status mStatus = Status::NoPacket; + std::shared_ptr mPacket; + + std::string toString() const + { + switch (mStatus) + { + case Status::RegularPacket: return "packet"; + case Status::Gap: return "gap"; + case Status::NoPacket: return "empty"; + } + } + }; + RtpBuffer(Statistics& stat); ~RtpBuffer(); @@ -81,12 +97,12 @@ public: int getCount() const; // Returns false if packet was not add - maybe too old or too new or duplicate - std::shared_ptr add(std::shared_ptr packet, std::chrono::milliseconds timelength, int rate); + std::shared_ptr add(const std::shared_ptr& packet, std::chrono::milliseconds timelength, int rate); typedef std::vector> ResultList; typedef std::shared_ptr PResultList; - FetchResult fetch(ResultList& rl); + FetchResult fetch(); protected: unsigned mSsrc = 0; @@ -133,15 +149,6 @@ public: // Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container). bool add(const std::shared_ptr& p, Codec** codec = nullptr); - // Returns false when there is no rtp data from jitter - /*enum DecodeOptions - { - DecodeOptions_ResampleToMainRate = 0, - DecodeOptions_DontResample = 1, - DecodeOptions_FillCngGap = 2, - DecodeOptions_SkipDecode = 4 - };*/ - struct DecodeOptions { bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE @@ -168,7 +175,7 @@ public: // Looks for codec by payload type Codec* findCodec(int payloadType); - RtpBuffer& getRtpBuffer() { return mBuffer; } + RtpBuffer& getRtpBuffer() { return mBuffer; } // Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.) int getSize() const; @@ -187,11 +194,14 @@ protected: CodecList::Settings mCodecSettings; CodecList mCodecList; JitterStatistics mJitterStats; - std::shared_ptr mCngPacket; + std::shared_ptr mCngPacket; CngDecoder mCngDecoder; size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action - // Buffer to hold decoded data + // Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval + Audio::DataWindow mAvailable; + + // Temporary buffer to hold decoded data (it is better than allocate data on stack) int16_t mDecodedFrame[MT_MAX_DECODEBUFFER]; size_t mDecodedLength = 0; @@ -208,7 +218,10 @@ protected: std::optional mLastPacketTimestamp; int mFailedCount = 0; - Audio::Resampler mResampler8, mResampler16, mResampler32, mResampler48; + Audio::Resampler mResampler8, + mResampler16, + mResampler32, + mResampler48; Audio::PWavFileWriter mDecodedDump; @@ -229,7 +242,7 @@ protected: void updateAmrCodecStats(Codec* c); DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options); - DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const RtpBuffer::ResultList& rl); + DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr& p); DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options); }; diff --git a/src/engine/media/MT_Codec.cpp b/src/engine/media/MT_Codec.cpp index 134291c7..69336264 100644 --- a/src/engine/media/MT_Codec.cpp +++ b/src/engine/media/MT_Codec.cpp @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) +/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -9,31 +9,31 @@ using namespace MT; int Codec::Factory::channels() { - return 1; + return 1; } void Codec::Factory::create(CodecMap& codecs) { - codecs[payloadType()] = std::shared_ptr(create()); + codecs[payloadType()] = std::shared_ptr(create()); } void Codec::Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) { - codecs.push_back(resipCodec()); + codecs.push_back(resipCodec()); } resip::Codec Codec::Factory::resipCodec() { - resip::Codec c(this->name(), this->payloadType(), this->samplerate()); - return c; + resip::Codec c(this->name(), this->payloadType(), this->samplerate()); + return c; } int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) { - for (resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter = codecs.begin(); codecIter != codecs.end(); ++codecIter) - { - if (resipCodec() == *codecIter) - return codecIter->payloadType(); - } - return -1; + for (resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter = codecs.begin(); codecIter != codecs.end(); ++codecIter) + { + if (resipCodec() == *codecIter) + return codecIter->payloadType(); + } + return -1; } diff --git a/src/engine/media/MT_Codec.h b/src/engine/media/MT_Codec.h index 0dd04676..ea43f100 100644 --- a/src/engine/media/MT_Codec.h +++ b/src/engine/media/MT_Codec.h @@ -10,7 +10,7 @@ #include "../helper/HL_Types.h" #include #include "../helper/HL_Pointer.h" - +#include "../audio/Audio_Interface.h" namespace MT { @@ -18,8 +18,7 @@ class Codec; typedef std::shared_ptr PCodec; class CodecMap: public std::map -{ -}; +{}; class Codec { @@ -58,18 +57,28 @@ public: // Number of audio channels virtual int channels() { return 1; } - // Returns size of encoded data (RTP) in bytes - virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; + struct EncodeResult + { + size_t mEncoded = 0; // Number of encoded bytes + }; + virtual EncodeResult encode(std::span input, std::span output) = 0; // Returns size of decoded data (PCM signed short) in bytes - virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; + struct DecodeResult + { + size_t mDecoded = 0; // Number of decoded bytes + bool mIsCng = false; // Should this packet to be used as CNG ? (used for AMR codecs) + }; + virtual DecodeResult decode(std::span input, std::span output) = 0; // Returns size of produced data (PCM signed short) in bytes - virtual int plc(int lostFrames, void* output, int outputCapacity) = 0; + virtual size_t plc(int lostFrames, std::span output) = 0; // Returns size of codec in memory - virtual int getSize() const { return 0; }; + virtual size_t getSize() const { return 0; }; + + virtual Audio::Format getAudioFormat() { return Audio::Format(this->samplerate(), this->channels());}; }; } #endif From 783359c616ab649c2f039addb75d93e360f2578d Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Mon, 23 Feb 2026 20:40:59 +0300 Subject: [PATCH 3/9] - fix clang build (build_linux.py) + fix problems reported by Kimi LLM --- build_linux.py | 11 +- src/engine/agent/Agent_Impl.cpp | 6 +- src/engine/endpoint/EP_Session.cpp | 2 +- src/engine/helper/HL_IuUP.cpp | 2245 +++++++++++++------------ src/engine/helper/HL_ThreadPool.cpp | 3 +- src/engine/helper/HL_VariantMap.cpp | 2 +- src/engine/media/MT_AmrCodec.cpp | 355 ++-- src/engine/media/MT_AmrCodec.h | 45 +- src/engine/media/MT_AudioCodec.cpp | 546 +++--- src/engine/media/MT_AudioCodec.h | 121 +- src/engine/media/MT_AudioReceiver.cpp | 59 +- src/engine/media/MT_AudioStream.cpp | 9 +- src/engine/media/MT_Codec.h | 38 +- src/engine/media/MT_EvsCodec.cpp | 65 +- src/engine/media/MT_EvsCodec.h | 20 +- src/engine/media/MT_SrtpHelper.cpp | 6 +- src/engine/media/MT_SrtpHelper.h | 23 +- 17 files changed, 1661 insertions(+), 1895 deletions(-) diff --git a/build_linux.py b/build_linux.py index a4850098..b390dc20 100755 --- a/build_linux.py +++ b/build_linux.py @@ -17,18 +17,19 @@ def make_build() -> Path: if Path(DIR_BUILD).exists(): shutil.rmtree(DIR_BUILD) os.mkdir(DIR_BUILD) - os.chdir(DIR_BUILD) - - cmd = f'cmake ../src -G Ninja' + os.chdir(DIR_BUILD) + + # OPUS_X86_MAY_HAVE_SSE4_1 is for clang builds + cmd = f'cmake ../src -G Ninja -D OPUS_X86_MAY_HAVE_SSE4_1=ON' retcode = os.system(cmd) if retcode != 0: raise RuntimeError('Problem when configuring the project') - + cmd = f'cmake --build . -j {multiprocessing.cpu_count()}' retcode = os.system(cmd) if retcode != 0: raise RuntimeError('Problem when building the project') - + os.chdir('..') return Path(DIR_BUILD) / 'librtphone.a' diff --git a/src/engine/agent/Agent_Impl.cpp b/src/engine/agent/Agent_Impl.cpp index ddec55bb..939fbf8d 100644 --- a/src/engine/agent/Agent_Impl.cpp +++ b/src/engine/agent/Agent_Impl.cpp @@ -687,7 +687,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val answer["status"] = Status_Ok; } else - answer["status"] = Status_AccountNotFound; + answer["status"] = Status_NoCommand; } else answer["status"] = Status_NoMediaAction; @@ -709,11 +709,13 @@ void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection PDataProvider AgentImpl::onProviderNeeded(const std::string& name) { + assert(mTerminal); + EVENT_WITH_NAME("provider_needed"); v["provider_name"] = name; addEvent(v); - return PDataProvider(new AudioProvider(*this, *mTerminal)); + return std::make_shared(*this, *mTerminal); } // Called on new session offer diff --git a/src/engine/endpoint/EP_Session.cpp b/src/engine/endpoint/EP_Session.cpp index 95dd70f9..5d6a0e3a 100644 --- a/src/engine/endpoint/EP_Session.cpp +++ b/src/engine/endpoint/EP_Session.cpp @@ -883,7 +883,7 @@ void Session::refreshMediaPath() // Bring new socket to provider and stream RtpPair s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ), - s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()); + s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX()); p->setSocket(s4, s6); s.setSocket4(s4); diff --git a/src/engine/helper/HL_IuUP.cpp b/src/engine/helper/HL_IuUP.cpp index 28a5aac7..4a84fd6f 100644 --- a/src/engine/helper/HL_IuUP.cpp +++ b/src/engine/helper/HL_IuUP.cpp @@ -5,29 +5,29 @@ bool IuUP::TwoBytePseudoheader = false; bool IuUP::parse(const uint8_t *packet, int size, IuUP::Frame &result) { - // Wrap incoming packet in byte buffer - BitReader reader(packet, size); + // Wrap incoming packet in byte buffer + BitReader reader(packet, size); - // Read frame - result.mPduType = (PduType)reader.readBits(4); + // Read frame + result.mPduType = (PduType)reader.readBits(4); - // For now we are interested only in Data with CRC - PDU type 0 (zero) - if (result.mPduType != PduType::DataWithCrc) - return false; + // For now we are interested only in Data with CRC - PDU type 0 (zero) + if (result.mPduType != PduType::DataWithCrc) + return false; - result.mFrameNumber = reader.readBits(4); + result.mFrameNumber = reader.readBits(4); - result.mFqc = reader.readBits(2); - result.mRfci = reader.readBits(6); - result.mHeaderCrc = reader.readBits(6); - result.mPayloadCrc = reader.readBits(10); - result.mPayload = packet + 4; - result.mPayloadSize = size - 4; + result.mFqc = reader.readBits(2); + result.mRfci = reader.readBits(6); + result.mHeaderCrc = reader.readBits(6); + result.mPayloadCrc = reader.readBits(10); + result.mPayload = packet + 4; + result.mPayloadSize = size - 4; - if (result.mFqc /*|| result.mRfci != 1*/) - return false; + if (result.mFqc /*|| result.mRfci != 1*/) + return false; - return true; + return true; } @@ -36,1083 +36,1086 @@ uint16_t update_crc10_by_bytes(uint16_t crc10, const uint8_t *data_blk_ptr, int bool IuUP::parse2(const uint8_t* packet, int size, Frame& result) { - if (TwoBytePseudoheader) - { - packet += 2; - size -= 2; - } + if (size < 2) + return false; - BitReader reader(packet, size); - result.mPduType = (PduType)reader.readBits(4); + if (TwoBytePseudoheader) + { + packet += 2; + size -= 2; + } - // Ignore control commands for now - if (result.mPduType != PduType::DataNoCrc && result.mPduType != PduType::DataWithCrc) - return false; + BitReader reader(packet, size); + result.mPduType = (PduType)reader.readBits(4); - result.mFrameNumber = reader.readBits(4); - result.mFqc = reader.readBits(2); - result.mRfci = reader.readBits(6); - result.mHeaderCrc = reader.readBits(6); + // Ignore control commands for now + if (result.mPduType != PduType::DataNoCrc && result.mPduType != PduType::DataWithCrc) + return false; - // Check for header type - result.mHeaderCrcOk = update_crc6_by_bytes(result.mHeaderCrc, packet[0], packet[1]) == 0; + result.mFrameNumber = reader.readBits(4); + result.mFqc = reader.readBits(2); + result.mRfci = reader.readBits(6); + result.mHeaderCrc = reader.readBits(6); - if (result.mPduType == PduType::DataWithCrc) - { - result.mPayloadCrc = reader.readBits(10); - result.mPayload = packet + 4; - result.mPayloadSize = size - 4; + // Check for header type + result.mHeaderCrcOk = update_crc6_by_bytes(result.mHeaderCrc, packet[0], packet[1]) == 0; - uint16_t crc = update_crc10_by_bytes(0, result.mPayload, result.mPayloadSize); - uint16_t secondWord = ntohs(((uint16_t*)packet)[1]); - secondWord &= 0x3FF; - uint8_t bytes[2]; - bytes[0] = secondWord >> 2; - bytes[1] = (secondWord << 6) & 0xFF; - crc = update_crc10_by_bytes(crc, bytes, 2); - result.mPayloadCrcOk = crc == 0; - } - else - { - result.mPayloadCrc = 0; - result.mPayload = packet + 3; - result.mPayloadSize = size - 3; - } + if (result.mPduType == PduType::DataWithCrc) + { + result.mPayloadCrc = reader.readBits(10); + result.mPayload = packet + 4; + result.mPayloadSize = size - 4; - // Skip all frames except good (mFqc == 0) - if (result.mFqc /*|| result.mRfci != 1*/) - return false; + uint16_t crc = update_crc10_by_bytes(0, result.mPayload, result.mPayloadSize); + uint16_t secondWord = ntohs(((uint16_t*)packet)[1]); + secondWord &= 0x3FF; + uint8_t bytes[2]; + bytes[0] = secondWord >> 2; + bytes[1] = (secondWord << 6) & 0xFF; + crc = update_crc10_by_bytes(crc, bytes, 2); + result.mPayloadCrcOk = crc == 0; + } + else + { + result.mPayloadCrc = 0; + result.mPayload = packet + 3; + result.mPayloadSize = size - 3; + } - return true; + // Skip all frames except good (mFqc == 0) + if (result.mFqc /*|| result.mRfci != 1*/) + return false; + + return true; } static const unsigned char crc6_table[] = -{ - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, - 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, - 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, - 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, - 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, - 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, - 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, - 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, - 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, - 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, - 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, - 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, - 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, - 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, - 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, - 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, - 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, - 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, - 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, - 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, - 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, - 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, - 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, - 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, - 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, - 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, - 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, - 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, - 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, - 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, - 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, - 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, - 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, - 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, - 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, - 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, - 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, - 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, - 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, - 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, - 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, - 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, - 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, - 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, - 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, - 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, - 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, - 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, - 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, - 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, - 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, - 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, - 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, - 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, - 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, - 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, - 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, - 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, - 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, - 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, - 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, - 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, - 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, - 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26,0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e, + 0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36,0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e, + 0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06,0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e, + 0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16,0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e, + 0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09,0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01, + 0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19,0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11, + 0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29,0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21, + 0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39,0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38,0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30, + 0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20, + 0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10, + 0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b,0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23, + 0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b,0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33, + 0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b,0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13, + 0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c, + 0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c, + 0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24,0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c, + 0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c, + 0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12, + 0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a,0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02, + 0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32, + 0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22, + 0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35,0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d, + 0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25,0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d, + 0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15,0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d, + 0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05,0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d, + 0x3b,0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34, + 0x2b,0x2a,0x29,0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25,0x24, + 0x1b,0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14, + 0x0b,0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04, + 0x14,0x15,0x16,0x17,0x10,0x11,0x12,0x13,0x1c,0x1d,0x1e,0x1f,0x18,0x19,0x1a,0x1b, + 0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03,0x0c,0x0d,0x0e,0x0f,0x08,0x09,0x0a,0x0b, + 0x34,0x35,0x36,0x37,0x30,0x31,0x32,0x33,0x3c,0x3d,0x3e,0x3f,0x38,0x39,0x3a,0x3b, + 0x24,0x25,0x26,0x27,0x20,0x21,0x22,0x23,0x2c,0x2d,0x2e,0x2f,0x28,0x29,0x2a,0x2b, + 0x0a,0x0b,0x08,0x09,0x0e,0x0f,0x0c,0x0d,0x02,0x03,0x00,0x01,0x06,0x07,0x04,0x05, + 0x1a,0x1b,0x18,0x19,0x1e,0x1f,0x1c,0x1d,0x12,0x13,0x10,0x11,0x16,0x17,0x14,0x15, + 0x2a,0x2b,0x28,0x29,0x2e,0x2f,0x2c,0x2d,0x22,0x23,0x20,0x21,0x26,0x27,0x24,0x25, + 0x3a,0x3b,0x38,0x39,0x3e,0x3f,0x3c,0x3d,0x32,0x33,0x30,0x31,0x36,0x37,0x34,0x35, + 0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x2d,0x2c,0x2f,0x2e,0x29,0x28,0x2b,0x2a, + 0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x32,0x3d,0x3c,0x3f,0x3e,0x39,0x38,0x3b,0x3a, + 0x05,0x04,0x07,0x06,0x01,0x00,0x03,0x02,0x0d,0x0c,0x0f,0x0e,0x09,0x08,0x0b,0x0a, + 0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x1d,0x1c,0x1f,0x1e,0x19,0x18,0x1b,0x1a, + 0x36,0x37,0x34,0x35,0x32,0x33,0x30,0x31,0x3e,0x3f,0x3c,0x3d,0x3a,0x3b,0x38,0x39, + 0x26,0x27,0x24,0x25,0x22,0x23,0x20,0x21,0x2e,0x2f,0x2c,0x2d,0x2a,0x2b,0x28,0x29, + 0x16,0x17,0x14,0x15,0x12,0x13,0x10,0x11,0x1e,0x1f,0x1c,0x1d,0x1a,0x1b,0x18,0x19, + 0x06,0x07,0x04,0x05,0x02,0x03,0x00,0x01,0x0e,0x0f,0x0c,0x0d,0x0a,0x0b,0x08,0x09, + 0x19,0x18,0x1b,0x1a,0x1d,0x1c,0x1f,0x1e,0x11,0x10,0x13,0x12,0x15,0x14,0x17,0x16, + 0x09,0x08,0x0b,0x0a,0x0d,0x0c,0x0f,0x0e,0x01,0x00,0x03,0x02,0x05,0x04,0x07,0x06, + 0x39,0x38,0x3b,0x3a,0x3d,0x3c,0x3f,0x3e,0x31,0x30,0x33,0x32,0x35,0x34,0x37,0x36, + 0x29,0x28,0x2b,0x2a,0x2d,0x2c,0x2f,0x2e,0x21,0x20,0x23,0x22,0x25,0x24,0x27,0x26, + 0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x0f,0x0e,0x0d,0x0c,0x0b,0x0a,0x09,0x08, + 0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x1f,0x1e,0x1d,0x1c,0x1b,0x1a,0x19,0x18, + 0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x2f,0x2e,0x2d,0x2c,0x2b,0x2a,0x29,0x28, + 0x37,0x36,0x35,0x34,0x33,0x32,0x31,0x30,0x3f,0x3e,0x3d,0x3c,0x3b,0x3a,0x39,0x38, + 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, + 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17, }; @@ -1171,50 +1174,50 @@ uint16_t crc6_compute(const uint8_t *data_blk_ptr, int data_blk_size) * routine at run time, and with various data type cleanups. */ static const uint16_t byte_crc10_table[256] = { - 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff, - 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe, - 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce, - 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf, - 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d, - 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c, - 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac, - 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad, - 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b, - 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a, - 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a, - 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b, - 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259, - 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158, - 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268, - 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169, - 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377, - 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076, - 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346, - 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047, - 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315, - 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014, - 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324, - 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025, - 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3, - 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2, - 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382, - 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083, - 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1, - 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0, - 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0, - 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1 + 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff, + 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe, + 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce, + 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf, + 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d, + 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c, + 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac, + 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad, + 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b, + 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a, + 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a, + 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b, + 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259, + 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158, + 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268, + 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169, + 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377, + 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076, + 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346, + 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047, + 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315, + 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014, + 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324, + 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025, + 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3, + 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2, + 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382, + 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083, + 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1, + 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0, + 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0, + 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1 }; /* Update the data block's CRC-10 remainder one byte at a time */ uint16_t update_crc10_by_bytes(uint16_t crc10_accum, const uint8_t *data_blk_ptr, int data_blk_size) { - /*register*/ int i; + /*register*/ int i; - for (i = 0; i < data_blk_size; i++) { - crc10_accum = ((crc10_accum << 8) & 0x3ff) - ^ byte_crc10_table[( crc10_accum >> 2) & 0xff] - ^ *data_blk_ptr++; - } - return crc10_accum; + for (i = 0; i < data_blk_size; i++) { + crc10_accum = ((crc10_accum << 8) & 0x3ff) + ^ byte_crc10_table[( crc10_accum >> 2) & 0xff] + ^ *data_blk_ptr++; + } + return crc10_accum; } diff --git a/src/engine/helper/HL_ThreadPool.cpp b/src/engine/helper/HL_ThreadPool.cpp index ae5a1091..be356dea 100644 --- a/src/engine/helper/HL_ThreadPool.cpp +++ b/src/engine/helper/HL_ThreadPool.cpp @@ -65,6 +65,7 @@ void thread_pool::run_worker() tasks.pop(); } } - t(); // function type + if (t) + t(); // function type } } diff --git a/src/engine/helper/HL_VariantMap.cpp b/src/engine/helper/HL_VariantMap.cpp index 95e0657e..09aaa0b0 100644 --- a/src/engine/helper/HL_VariantMap.cpp +++ b/src/engine/helper/HL_VariantMap.cpp @@ -240,7 +240,7 @@ int64_t Variant::asInt64() const if (mType != VTYPE_INT64) throw Exception(ERR_BAD_VARIANT_TYPE); - return mInt; + return mInt64; } bool Variant::asBool() const diff --git a/src/engine/media/MT_AmrCodec.cpp b/src/engine/media/MT_AmrCodec.cpp index 4085360a..3a561df1 100644 --- a/src/engine/media/MT_AmrCodec.cpp +++ b/src/engine/media/MT_AmrCodec.cpp @@ -3,11 +3,8 @@ #include "MT_AmrCodec.h" #include "../helper/HL_ByteBuffer.h" -#include "../helper/HL_Log.h" #include "../helper/HL_IuUP.h" -#include "../helper/HL_Exception.h" - -#include +#include "../helper/HL_Log.h" #define LOG_SUBSYSTEM "AmrCodec" using namespace MT; @@ -287,44 +284,31 @@ AmrNbCodec::~AmrNbCodec() } } -const char* AmrNbCodec::name() +Codec::Info AmrNbCodec::info() { - return MT_AMRNB_CODECNAME; + return { + .mName = MT_AMRNB_CODECNAME, + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 20 * 16, + .mFrameTime = 20, + .mRtpLength = 0 + }; } -int AmrNbCodec::pcmLength() +Codec::EncodeResult AmrNbCodec::encode(std::span input, std::span output) { - return 20 * 16; -} - -int AmrNbCodec::rtpLength() -{ - return 0; -} - -int AmrNbCodec::frameTime() -{ - return 20; -} - -int AmrNbCodec::samplerate() -{ - return 8000; -} - -int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - if (inputBytes % pcmLength()) - return 0; + if (input.size_bytes() % pcmLength()) + return {.mEncoded = 0}; // Declare the data input pointer - auto *dataIn = (const short *)input; + auto *dataIn = (const short *)input.data(); // Declare the data output pointer - auto *dataOut = (unsigned char *)output; + auto *dataOut = (unsigned char *)output.data(); // Find how much RTP frames will be generated - unsigned int frames = inputBytes / pcmLength(); + unsigned int frames = input.size_bytes() / pcmLength(); // Generate frames for (unsigned int i = 0; i < frames; i++) @@ -333,28 +317,28 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp dataIn += pcmLength() / 2; } - return dataOut - (unsigned char*)output; + return {.mEncoded = (size_t)(dataOut - (unsigned char*)output.data())}; } #define L_FRAME 160 #define AMR_BITRATE_DTX 15 -int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult AmrNbCodec::decode(std::span input, std::span output) { if (mConfig.mOctetAligned) - return 0; + return {.mDecoded = 0}; if (mConfig.mIuUP) { // Try to parse IuUP frame IuUP::Frame frame; - if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame)) - return 0; + if (!IuUP::parse2((const uint8_t*)input.data(), input.size_bytes(), frame)) + return {0}; // Check if CRC failed - it is check from IuUP data if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) { ICELogInfo(<< "CRC check failed."); - return 0; + return {0}; } // Build NB frame to decode @@ -371,31 +355,31 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp // Check if frameType comparing is correct if (frameType == 0xFF) - return 0; + return {0}; dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); - Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0); - return pcmLength(); + Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0); + return {.mDecoded = (size_t)pcmLength()}; } else { - if (outputCapacity < pcmLength()) - return 0; + if (output.size_bytes() < pcmLength()) + return {.mDecoded = 0}; - if (inputBytes == 0) + if (input.size_bytes() == 0) { // PLC part unsigned char buffer[32]; buffer[0] = (AMR_BITRATE_DTX << 3)|4; - Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output, 0); // Handle missing data - return pcmLength(); + Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output.data(), 0); // Handle missing data + return {.mDecoded = (size_t)pcmLength()}; } AmrPayloadInfo info; info.mCurrentTimestamp = mCurrentDecoderTimestamp; info.mOctetAligned = mConfig.mOctetAligned; - info.mPayload = (const uint8_t*)input; - info.mPayloadLength = inputBytes; + info.mPayload = input.data(); + info.mPayloadLength = input.size_bytes(); info.mWideband = false; info.mInterleaving = false; @@ -407,25 +391,25 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp catch(...) { ICELogDebug(<< "Failed to decode AMR payload."); - return 0; + return {.mDecoded = 0}; } // Save current timestamp mCurrentDecoderTimestamp = info.mCurrentTimestamp; // Check if packet is corrupted if (ap.mDiscardPacket) - return 0; + return {.mDecoded = 0}; // Check for output buffer capacity - if (outputCapacity < (int)ap.mFrames.size() * pcmLength()) - return 0; + if (output.size_bytes() < (int)ap.mFrames.size() * pcmLength()) + return {.mDecoded = 0}; if (ap.mFrames.empty()) { ICELogError(<< "No AMR frames"); } - short* dataOut = (short*)output; + short* dataOut = (short*)output.data(); for (AmrFrame& frame: ap.mFrames) { if (frame.mData) @@ -435,18 +419,18 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp dataOut += pcmLength() / 2; } } - return pcmLength() * ap.mFrames.size(); + return {.mDecoded = pcmLength() * ap.mFrames.size()}; } - return pcmLength(); + return {.mDecoded = (size_t)pcmLength()}; } -int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t AmrNbCodec::plc(int lostFrames, std::span output) { - if (outputCapacity < lostFrames * pcmLength()) + if (output.size_bytes() < lostFrames * pcmLength()) return 0; - short* dataOut = (short*)output; + short* dataOut = (short*)output.data(); for (int i=0; i < lostFrames; i++) { @@ -510,11 +494,9 @@ PCodec AmrWbCodec::CodecFactory::create() AmrWbStatistics MT::GAmrWbStatistics; AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config) - :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config), - mSwitchCounter(0), mPreviousPacketLength(0) + :mConfig(config) { mDecoderCtx = D_IF_init(); - mCurrentDecoderTimestamp = 0; } AmrWbCodec::~AmrWbCodec() @@ -532,34 +514,22 @@ AmrWbCodec::~AmrWbCodec() } } -const char* AmrWbCodec::name() -{ - return MT_AMRWB_CODECNAME; +Codec::Info AmrWbCodec::info() { + return { + .mName = MT_AMRWB_CODECNAME, + .mSamplerate = 16000, + .mChannels = 1, + .mPcmLength = 20 * 16 * 2, + .mFrameTime = 20, + .mRtpLength = 0 /* There is complex structure inside AMR packet which may include multilple frames with various length. */ + }; } -int AmrWbCodec::pcmLength() -{ - return 20 * 16 * 2; -} -int AmrWbCodec::rtpLength() +Codec::EncodeResult AmrWbCodec::encode(std::span input, std::span output) { - return 0; // VBR -} - -int AmrWbCodec::frameTime() -{ - return 20; -} - -int AmrWbCodec::samplerate() -{ - return 16000; -} - -int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - throw Exception(ERR_NOT_IMPLEMENTED); + // Still no support for encoding - emit silence instead + return {.mEncoded = 0}; } #define L_FRAME 160 @@ -657,35 +627,36 @@ int AmrWbCodec::decodePlain(std::span input, std::span o return dataOutSizeInBytes; } -int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult AmrWbCodec::decode(std::span input, std::span output) { - auto inputBuffer = std::span((uint8_t*)input, (size_t)inputBytes); - auto outputBuffer = std::span((uint8_t*)output, (size_t)outputCapacity); - if (mConfig.mIuUP) - return decodeIuup(inputBuffer, outputBuffer); + return {.mDecoded = (size_t)decodeIuup(input, output)}; else - return decodePlain(inputBuffer, outputBuffer); + return {.mDecoded = (size_t)decodePlain(input, output)}; - return 0; + return {.mDecoded = 0}; } -int AmrWbCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t AmrWbCodec::plc(int lostFrames, std::span output) { - /* if (outputCapacity < lostFrames * pcmLength()) - return 0; - - short* dataOut = (short*)output; - - for (int i=0; i < lostFrames; i++) - { - unsigned char buffer[32]; - buffer[0] = (AMR_BITRATE_DTX << 3)|4; - Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data - dataOut += L_FRAME; - } - */ + // ToDo: Check again if PLC works for AMR-WB + // For now return the silence + memset(output.data(), 0, output.size_bytes()); return lostFrames * pcmLength(); + /* + if (outputCapacity < lostFrames * pcmLength()) + return 0; + + short* dataOut = (short*)output; + + for (int i=0; i < lostFrames; i++) + { + unsigned char buffer[32]; + buffer[0] = (AMR_BITRATE_DTX << 3)|4; + Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data + dataOut += L_FRAME; + } + */ } int AmrWbCodec::getSwitchCounter() const @@ -702,9 +673,7 @@ int AmrWbCodec::getCngCounter() const GsmEfrCodec::GsmEfrFactory::GsmEfrFactory(bool iuup, int ptype) :mIuUP(iuup), mPayloadType(ptype) -{ - -} +{} const char* GsmEfrCodec::GsmEfrFactory::name() { @@ -722,9 +691,7 @@ int GsmEfrCodec::GsmEfrFactory::payloadType() } void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) -{ - -} +{} int GsmEfrCodec::GsmEfrFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) { @@ -742,7 +709,7 @@ PCodec GsmEfrCodec::GsmEfrFactory::create() } GsmEfrCodec::GsmEfrCodec(bool iuup) - :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mIuUP(iuup) + :mIuUP(iuup) { mEncoderCtx = Encoder_Interface_init(1); mDecoderCtx = Decoder_Interface_init(); @@ -763,44 +730,31 @@ GsmEfrCodec::~GsmEfrCodec() } } -const char* GsmEfrCodec::name() +Codec::Info GsmEfrCodec::info() { - return MT_GSMEFR_CODECNAME; + return { + .mName = MT_GSMEFR_CODECNAME, + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 20 * 16, + .mFrameTime = 20, + .mRtpLength = 0 + }; } -int GsmEfrCodec::pcmLength() +Codec::EncodeResult GsmEfrCodec::encode(std::span input, std::span output) { - return 20 * 16; -} - -int GsmEfrCodec::rtpLength() -{ - return 0; -} - -int GsmEfrCodec::frameTime() -{ - return 20; -} - -int GsmEfrCodec::samplerate() -{ - return 8000; -} - -int GsmEfrCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - if (inputBytes % pcmLength()) - return 0; + if (input.size_bytes() % pcmLength()) + return {.mEncoded = 0}; // Declare the data input pointer - const short *dataIn = (const short *)input; + const short *dataIn = (const short *)input.data(); // Declare the data output pointer - unsigned char *dataOut = (unsigned char *)output; + unsigned char *dataOut = (unsigned char *)output.data(); // Find how much RTP frames will be generated - unsigned int frames = inputBytes / pcmLength(); + unsigned int frames = input.size_bytes() / pcmLength(); // Generate frames for (unsigned int i = 0; i < frames; i++) @@ -809,7 +763,7 @@ int GsmEfrCodec::encode(const void* input, int inputBytes, void* output, int out dataIn += pcmLength() / 2; } - return frames * rtpLength(); + return {.mEncoded = frames * rtpLength()}; } #define L_FRAME 160 @@ -866,100 +820,61 @@ const uint16_t gsm690_12_2_bitorder[244] = { 237, 236, 96, 199, }; -int GsmEfrCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult GsmEfrCodec::decode(std::span input, std::span output) { - /* if (mIuUP) - { - // Try to parse IuUP frame - IuUP::Frame frame; - if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame)) - return 0; + if (output.size_bytes() < pcmLength()) + return {.mDecoded = 0}; - // Check if CRC failed - it is check from IuUP data - if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) - { - ICELogInfo(<< "CRC check failed."); - return 0; + if (input.size_bytes() == 0) + { // PLC part + unsigned char buffer[32]; + buffer[0] = (AMR_BITRATE_DTX << 3)|4; + Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output.data(), 0); // Handle missing data } - - // Build NB frame to decode - ByteBuffer dataToDecode; - dataToDecode.resize(1 + frame.mPayloadSize); // Reserve place - - // Copy AMR data - memmove(dataToDecode.mutableData() + 1, frame.mPayload, frame.mPayloadSize); - - uint8_t frameType = 0xFF; - for (uint8_t ftIndex = 0; ftIndex <= 9 && frameType == 0xFF; ftIndex++) - if (amrnb_framelen[ftIndex] == frame.mPayloadSize) - frameType = ftIndex; - - // Check if frameType comparing is correct - if (frameType == 0xFF) - return 0; - - dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); - - Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0); - return pcmLength(); - } - else */ + else { - if (outputCapacity < pcmLength()) - return 0; + // Reorder bytes from input to dst + uint8_t dst[GSM_EFR_FRAME_LEN]; + const uint8_t* src = input.data(); + for (int i=0; i<(GSM_EFR_FRAME_LEN-1); i++) + dst[i] = (src[i] << 4) | (src[i+1] >> 4); + dst[GSM_EFR_FRAME_LEN-1] = src[GSM_EFR_FRAME_LEN-1] << 4; - if (inputBytes == 0) - { // PLC part - unsigned char buffer[32]; - buffer[0] = (AMR_BITRATE_DTX << 3)|4; - Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output, 0); // Handle missing data - } - else + unsigned char in[GSM_EFR_FRAME_LEN + 1]; + + // Reorder bits + in[0] = 0x3c; /* AMR mode 7 = GSM-EFR, Quality bit is set */ + in[GSM_EFR_FRAME_LEN] = 0x0; + + for (int i=0; i<244; i++) { - // Reorder bytes from input to dst - uint8_t dst[GSM_EFR_FRAME_LEN]; - const uint8_t* src = (const uint8_t*)input; - for (int i=0; i<(GSM_EFR_FRAME_LEN-1); i++) - dst[i] = (src[i] << 4) | (src[i+1] >> 4); - dst[GSM_EFR_FRAME_LEN-1] = src[GSM_EFR_FRAME_LEN-1] << 4; + int si = gsm690_12_2_bitorder[i]; + int di = i; + msb_put_bit(in + 1, di, msb_get_bit(dst, si)); + } - unsigned char in[GSM_EFR_FRAME_LEN + 1]; + // Decode + memset(output.data(), 0, pcmLength()); + Decoder_Interface_Decode(mDecoderCtx, in, (short*)output.data(), 0); - // Reorder bits - in[0] = 0x3c; /* AMR mode 7 = GSM-EFR, Quality bit is set */ - in[GSM_EFR_FRAME_LEN] = 0x0; - - for (int i=0; i<244; i++) - { - int si = gsm690_12_2_bitorder[i]; - int di = i; - msb_put_bit(in + 1, di, msb_get_bit(dst, si)); - } - - // Decode - memset(output, 0, pcmLength()); - Decoder_Interface_Decode(mDecoderCtx, in, (short*)output, 0); - - - uint8_t* pcm = (uint8_t*)output; - for (int i=0; i<160; i++) - { - uint16_t w = ((uint16_t*)output)[i]; - pcm[(i<<1) ] = w & 0xff; - pcm[(i<<1)+1] = (w >> 8) & 0xff; - } + uint8_t* pcm = (uint8_t*)output.data(); + for (int i=0; i<160; i++) + { + uint16_t w = ((uint16_t*)output.data())[i]; + pcm[(i<<1) ] = w & 0xff; + pcm[(i<<1)+1] = (w >> 8) & 0xff; } } - return pcmLength(); + return {.mDecoded = (size_t)pcmLength()}; } -int GsmEfrCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t GsmEfrCodec::plc(int lostFrames, std::span output) { - if (outputCapacity < lostFrames * pcmLength()) + if (output.size_bytes() < lostFrames * pcmLength()) return 0; - short* dataOut = (short*)output; + short* dataOut = (short*)output.data(); for (int i=0; i < lostFrames; i++) { diff --git a/src/engine/media/MT_AmrCodec.h b/src/engine/media/MT_AmrCodec.h index dd35192e..8eca3e12 100644 --- a/src/engine/media/MT_AmrCodec.h +++ b/src/engine/media/MT_AmrCodec.h @@ -54,16 +54,14 @@ public: }; AmrNbCodec(const AmrCodecConfig& config); + ~AmrNbCodec(); + + Info info() override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; - virtual ~AmrNbCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; int getSwitchCounter() const; int getCngCounter() const; }; @@ -113,14 +111,11 @@ public: AmrWbCodec(const AmrCodecConfig& config); virtual ~AmrWbCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; + Info info() override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; int getSwitchCounter() const; int getCngCounter() const; }; @@ -147,23 +142,19 @@ public: void create(CodecMap& codecs) override; PCodec create() override; - protected: bool mIuUP; int mPayloadType; }; GsmEfrCodec(bool iuup = false); + ~GsmEfrCodec(); - virtual ~GsmEfrCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; + Info info() override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; } // End of MT namespace diff --git a/src/engine/media/MT_AudioCodec.cpp b/src/engine/media/MT_AudioCodec.cpp index e08e65f4..e41746e6 100644 --- a/src/engine/media/MT_AudioCodec.cpp +++ b/src/engine/media/MT_AudioCodec.cpp @@ -91,38 +91,20 @@ G729Codec::~G729Codec() } } -const char* G729Codec::name() +Codec::Info G729Codec::info() { - return "G729"; -} - -int G729Codec::pcmLength() -{ - return 10 * 8 * 2; -} - -int G729Codec::rtpLength() -{ - return 10; -} - -int G729Codec::frameTime() -{ - return 10; -} - -int G729Codec::samplerate() -{ - return 8000; -} - -int G729Codec::channels() -{ - return 1; + return { + .mName = "G729", + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 10 * 8 * 2, + .mFrameTime = 10, + .mRtpLength = 10 + }; } // static const int SamplesPerFrame = 80; -int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::EncodeResult G729Codec::encode(std::span input, std::span output) { // Create encoder if it is not done yet if (!mEncoder) @@ -131,24 +113,24 @@ int G729Codec::encode(const void* input, int inputBytes, void* output, int outpu if (mEncoder) Init_Pre_Process(mEncoder); } - int result = 0; + size_t result = 0; if (mEncoder) { - int nrOfFrames = inputBytes / 160; // 10ms frames - Word16 parm[PRM_SIZE]; // ITU's service buffer + int nrOfFrames = input.size_bytes() / 160; // 10ms frames + Word16 parm[PRM_SIZE]; // ITU's service buffer for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++) { - Copy((int16_t*)input + frameIndex * pcmLength() / 2, mEncoder->new_speech, pcmLength() / 2); - Pre_Process(mEncoder, mEncoder->new_speech, pcmLength() / 2); + Copy((int16_t*)input.data() + frameIndex * info().mPcmLength / 2, mEncoder->new_speech, info().mPcmLength / 2); + Pre_Process(mEncoder, mEncoder->new_speech, info().mPcmLength / 2); Coder_ld8a(mEncoder, parm); - Store_Params(parm, (uint8_t*)output + frameIndex * rtpLength()); - result += rtpLength(); + Store_Params(parm, output.data() + frameIndex * info().mRtpLength); + result += info().mRtpLength; } } - return result; + return {result}; } -int G729Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult G729Codec::decode(std::span input, std::span output) { if (!mDecoder) { @@ -160,29 +142,29 @@ int G729Codec::decode(const void* input, int inputBytes, void* output, int outpu } } - int result = 0; + size_t result = 0; if (mDecoder) { // See if there are silence bytes in the end - bool isSilence = (inputBytes % rtpLength()) / 2 != 0; + bool isSilence = (input.size_bytes() % info().mRtpLength) / 2 != 0; // Find number of frames - int nrOfFrames = inputBytes / rtpLength(); - nrOfFrames = std::min(outputCapacity / pcmLength(), nrOfFrames); + int nrOfFrames = input.size_bytes() / info().mRtpLength; + nrOfFrames = std::min(output.size_bytes() / info().mPcmLength, (size_t)nrOfFrames); for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++) - decodeFrame((const uint8_t*)input + frameIndex * rtpLength(), (int16_t*)output + frameIndex * pcmLength()); + decodeFrame(input.data() + frameIndex * info().mRtpLength, (int16_t*)output.data() + frameIndex * info().mPcmLength); - result += nrOfFrames * pcmLength(); + result += nrOfFrames * info().mPcmLength; - if (isSilence && nrOfFrames < outputCapacity / pcmLength()) + if (isSilence && nrOfFrames < output.size_bytes() / info().mPcmLength) { - memset((uint8_t*)output + nrOfFrames * pcmLength(), 0, pcmLength()); - result += pcmLength(); + memset(output.data() + nrOfFrames * info().mPcmLength, 0, info().mPcmLength); + result += info().mPcmLength; } } - return result; + return {.mDecoded = result, .mIsCng = false}; } void G729Codec::decodeFrame(const uint8_t* rtp, int16_t* pcm) @@ -217,7 +199,7 @@ void G729Codec::decodeFrame(const uint8_t* rtp, int16_t* pcm) } -int G729Codec::plc(int lostFrames, void* output, int outputCapacity) +size_t G729Codec::plc(int lostFrames, std::span output) { return 0; } @@ -390,7 +372,7 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium PCodec OpusCodec::OpusFactory::create() { - OpusCodec* result = new OpusCodec(mSamplerate, mChannels, mParams.mPtime); + OpusCodec* result = new OpusCodec(Audio::Format(mSamplerate, mChannels), mParams.mPtime); result->applyParams(mParams); PCodec c(result); mCodecList.push_back(c); @@ -398,8 +380,8 @@ PCodec OpusCodec::OpusFactory::create() return c; } -OpusCodec::OpusCodec(int samplerate, int channels, int ptime) - :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0) +OpusCodec::OpusCodec(Audio::Format fmt, int ptime) + :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(fmt.channels()), mPTime(ptime), mSamplerate(fmt.rate()), mDecoderChannels(0) { int status; mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status); @@ -441,52 +423,34 @@ OpusCodec::~OpusCodec() } } -const char* OpusCodec::name() -{ - return OPUS_CODEC_NAME; +Codec::Info OpusCodec::info() { + return { + .mName = OPUS_CODEC_NAME, + .mSamplerate = mSamplerate, + .mChannels = mChannels, + .mPcmLength = (int)(mSamplerate / 1000 * sizeof(short) * mChannels * mPTime), + .mFrameTime = mPTime, + .mRtpLength = 0 /* VBR */ + }; } -int OpusCodec::pcmLength() -{ - return (samplerate() / 1000 ) * frameTime() * sizeof(short) * channels(); -} - -int OpusCodec::channels() -{ - return mChannels; -} - -int OpusCodec::rtpLength() -{ - return 0; // VBR -} - -int OpusCodec::frameTime() -{ - return mPTime; -} - -int OpusCodec::samplerate() -{ - return mSamplerate; -} - -int OpusCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::EncodeResult OpusCodec::encode(std::span input, std::span output) { // Send number of samples for input and number of bytes for output - int written = opus_encode(mEncoderCtx, (const opus_int16*)input, inputBytes / (sizeof(short) * channels()), (unsigned char*)output, outputCapacity / (sizeof(short) * channels())); + int written = opus_encode(mEncoderCtx, (const opus_int16*)input.data(), input.size_bytes() / (sizeof(short) * channels()), + output.data(), output.size_bytes() / (sizeof(short) * channels())); if (written < 0) - return 0; + return {.mEncoded = 0}; else - return written; + return {.mEncoded = (size_t)written}; } -int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult OpusCodec::decode(std::span input, std::span output) { int result = 0; // Examine the number of channels available in incoming packet - int nr_of_channels = opus_packet_get_nb_channels((const unsigned char *) input); + int nr_of_channels = opus_packet_get_nb_channels(input.data()); // Recreate decoder if needed if (mDecoderChannels != nr_of_channels) @@ -504,24 +468,22 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu int status = 0; mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status); if (status) - return 0; + return {0}; } - int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input, - inputBytes); + int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, input.data(), input.size_bytes()); if (nr_of_frames <= 0) - return 0; + return {0}; // We support stereo and mono here. int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels; opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity); - int decoded = opus_decode(mDecoderCtx, - reinterpret_cast(input), inputBytes, + int decoded = opus_decode(mDecoderCtx, input.data(), input.size_bytes(), buffer_decode, nr_of_frames, 0); if (decoded < 0) { ICELogCritical(<< "opus_decode() returned " << decoded); - return 0; + return {0}; } opus_int16 *buffer_stereo = nullptr; @@ -535,14 +497,14 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu buffer_stereo[i * 2 + 1] = buffer_decode[i]; buffer_stereo[i * 2] = buffer_decode[i]; } - assert(buffer_stereo_capacity <= outputCapacity); - memcpy(output, buffer_stereo, buffer_stereo_capacity); + assert(buffer_stereo_capacity <= output.size_bytes()); + memcpy(output.data(), buffer_stereo, buffer_stereo_capacity); result = buffer_stereo_capacity; break; case 2: - assert(buffer_capacity <= outputCapacity); - memcpy(output, buffer_decode, buffer_capacity); + assert(buffer_capacity <= output.size_bytes()); + memcpy(output.data(), buffer_decode, buffer_capacity); result = buffer_capacity; break; @@ -550,17 +512,17 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu assert(0); } - return result; + return {.mDecoded = (size_t)result}; } -int OpusCodec::plc(int lostPackets, void* output, int outputCapacity) +size_t OpusCodec::plc(int lostPackets, std::span output) { // Find how much frames do we need to produce and prefill it with silence int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels()); - memset(output, 0, outputCapacity); + memset(output.data(), 0, output.size_bytes()); // Use this pointer as output - opus_int16* data_output = reinterpret_cast(output); + opus_int16* data_output = reinterpret_cast(output.data()); int nr_of_decoded_frames = 0; @@ -575,10 +537,7 @@ int OpusCodec::plc(int lostPackets, void* output, int outputCapacity) case 1: // Convert mono to stereo for (int i=0; i < nr_of_decoded_frames; i++) - { - data_output[i * 2] = buffer_plc[i]; - data_output[i * 2 + 1] = buffer_plc[i+1]; - } + data_output[i * 2] = data_output[i * 2 + 1] = buffer_plc[i]; data_output += frames_per_packet * mChannels; break; @@ -589,14 +548,14 @@ int OpusCodec::plc(int lostPackets, void* output, int outputCapacity) break; } } - return ((char*)data_output - (char*)output) * sizeof(opus_int16); + return ((uint8_t*)data_output - output.data()); } // -------------- ILBC ------------------- #define ILBC_CODEC_NAME "ILBC" IlbcCodec::IlbcCodec(int packetTime) - :mPacketTime(packetTime), mEncoderCtx(nullptr), mDecoderCtx(nullptr) + :mPacketTime(packetTime) { WebRtcIlbcfix_EncoderCreate(&mEncoderCtx); WebRtcIlbcfix_DecoderCreate(&mDecoderCtx); @@ -610,44 +569,31 @@ IlbcCodec::~IlbcCodec() WebRtcIlbcfix_EncoderFree(mEncoderCtx); } -const char* IlbcCodec::name() +Codec::Info IlbcCodec::info() { - return "ilbc"; + return { + .mName = ILBC_CODEC_NAME, + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = mPacketTime * 8 * (int)sizeof(short), + .mFrameTime = mPacketTime, + .mRtpLength = (mPacketTime == 20) ? 38 : 50 + }; } -int IlbcCodec::rtpLength() +Codec::EncodeResult IlbcCodec::encode(std::span input, std::span output) { - return (mPacketTime == 20 ) ? 38 : 50; -} - -int IlbcCodec::pcmLength() -{ - return mPacketTime * 16; -} - -int IlbcCodec::frameTime() -{ - return mPacketTime; -} - -int IlbcCodec::samplerate() -{ - return 8000; -} - -Codec::EncodeResult IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int outputCapacity) -{ - if (inputBytes % pcmLength()) + if (input.size_bytes() % pcmLength()) return {}; // Declare the data input pointer - short *dataIn = (short *)input; + short *dataIn = (short *)input.data(); // Declare the data output pointer - char *dataOut = (char *)outputBuffer; + char *dataOut = (char *)output.data(); // Find how much RTP frames will be generated - unsigned int frames = inputBytes / pcmLength(); + unsigned int frames = input.size_bytes() / pcmLength(); // Generate frames for (unsigned int i=0; i input, std::span output) { - unsigned frames = inputBytes / rtpLength(); + unsigned frames = input.size_bytes() / rtpLength(); - char* dataIn = (char*)input; - short* dataOut = (short*)output; + char* dataIn = (char*)input.data(); + short* dataOut = (short*)output.data(); for (unsigned i=0; i < frames; ++i) { @@ -678,7 +624,7 @@ Codec::DecodeResult IlbcCodec::decode(const void* input, int inputBytes, void* o return {frames * pcmLength()}; } -int IlbcCodec::plc(int lostFrames, std::span output) +size_t IlbcCodec::plc(int lostFrames, std::span output) { return sizeof(short) * WebRtcIlbcfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output.data(), lostFrames); } @@ -795,38 +741,24 @@ IsacCodec::~IsacCodec() WebRtcIsacfix_Free(mDecoderCtx); mDecoderCtx = NULL; } -const char* IsacCodec::name() -{ - return "isac"; +Codec::Info IsacCodec::info() { + return { + .mName = "isac", + .mSamplerate = mSamplerate, + .mChannels = 1, + .mPcmLength = 60 * mSamplerate / 1000 * 2, + .mFrameTime = 60, + .mRtpLength = 0 + }; } -int IsacCodec::frameTime() +Codec::EncodeResult IsacCodec::encode(std::span input, std::span output) { - return 60; -} - -int IsacCodec::samplerate() -{ - return mSamplerate; -} - -int IsacCodec::pcmLength() -{ - return frameTime() * samplerate() / 1000 * sizeof(short); -} - -int IsacCodec::rtpLength() -{ - return 0; -} - -int IsacCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - unsigned nrOfSamples = inputBytes / 2; + unsigned nrOfSamples = input.size_bytes() / 2; unsigned timeLength = nrOfSamples / (mSamplerate / 1000); int encoded = 0; - char* dataOut = (char*)output; - const WebRtc_Word16* dataIn = (const WebRtc_Word16*)input; + char* dataOut = (char*)output.data(); + const WebRtc_Word16* dataIn = (const WebRtc_Word16*)input.data(); // Iterate 10 milliseconds chunks for (unsigned i=0; i 0) dataOut += encoded; } - return dataOut - (char*)output; + return {.mEncoded = (size_t)(dataOut - (char*)output.data())}; } -int IsacCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult IsacCodec::decode(std::span input, std::span output) { WebRtc_Word16 speechType = 0; - unsigned produced = WebRtcIsacfix_Decode(mDecoderCtx, (const WebRtc_UWord16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); + unsigned produced = WebRtcIsacfix_Decode(mDecoderCtx, (const WebRtc_UWord16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType); if (produced == (unsigned)-1) - return 0; + return {.mDecoded = 0}; - return produced * 2; + return {.mDecoded = produced * 2}; } -int IsacCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t IsacCodec::plc(int lostFrames, std::span output) { // lostFrames are 30-milliseconds frames; but used encoding mode is 60 milliseconds. // So lostFrames * 2 lostFrames *=2 ; - if (-1 == WebRtcIsacfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output, lostFrames )) + if (-1 == WebRtcIsacfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output.data(), lostFrames )) return 0; return lostFrames * 30 * (samplerate()/1000 * sizeof(short)); @@ -916,71 +848,55 @@ PCodec IsacCodec::IsacFactory32K::create() G711Codec::G711Codec(int type) :mType(type) -{ -} +{} G711Codec::~G711Codec() -{ +{} + +Codec::Info G711Codec::info() { + return { + .mName = mType == ALaw ? "PCMA" : "PCMU", + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 10 * 16, + .mFrameTime = 10, + .mRtpLength = 10 * 8 + }; } -const char* G711Codec::name() -{ - return "g711"; -} - -int G711Codec::pcmLength() -{ - return frameTime() * 16; -} - -int G711Codec::rtpLength() -{ - return frameTime() * 8; -} - -int G711Codec::frameTime() -{ - return 10; -} - -int G711Codec::samplerate() -{ - return 8000; -} - -int G711Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::EncodeResult G711Codec::encode(std::span input, std::span output) { int result; if (mType == ALaw) - result = WebRtcG711_EncodeA(NULL, (WebRtc_Word16*)input, inputBytes/2, (WebRtc_Word16*)output); + result = WebRtcG711_EncodeA(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes() / 2, (WebRtc_Word16*)output.data()); else - result = WebRtcG711_EncodeU(NULL, (WebRtc_Word16*)input, inputBytes/2, (WebRtc_Word16*)output); + result = WebRtcG711_EncodeU(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes() / 2, (WebRtc_Word16*)output.data()); - if (result == -1) - throw Exception(ERR_WEBRTC, -1); + if (result < 0) + return {.mEncoded = 0}; - return result; + return {.mEncoded = (size_t) result}; } -int G711Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult G711Codec::decode(std::span input, std::span output) { - assert(outputCapacity >= inputBytes * 2); + assert(output.size_bytes() >= input.size_bytes() * 2); int result; WebRtc_Word16 speechType; if (mType == ALaw) - result = WebRtcG711_DecodeA(NULL, (WebRtc_Word16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); + result = WebRtcG711_DecodeA(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType); else - result = WebRtcG711_DecodeU(NULL, (WebRtc_Word16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); + result = WebRtcG711_DecodeU(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType); - if (result == -1) - throw Exception(ERR_WEBRTC, -1); + if (result < 0) + return {.mDecoded = 0}; - return result * 2; + return {.mDecoded = (size_t)result * 2}; } -int G711Codec::plc(int lostSamples, void* output, int outputCapacity) +size_t G711Codec::plc(int lostSamples, std::span output) { return 0; } @@ -1060,86 +976,64 @@ GsmCodec::GsmCodec(Type codecType) GsmCodec::~GsmCodec() { - gsm_destroy(mGSM); + gsm_destroy(mGSM); mGSM = nullptr; } -const char* GsmCodec::name() -{ - return "GSM-06.10"; -} - -int GsmCodec::rtpLength() -{ +Codec::Info GsmCodec::info() { + int rtpLength = 0; switch (mCodecType) { - case Type::Bytes_31: - return GSM_RTPFRAME_SIZE_31; - break; - - case Type::Bytes_32: - return GSM_RTPFRAME_SIZE_32; - - case Type::Bytes_33: - return GSM_RTPFRAME_SIZE_33; - - case Type::Bytes_65: - return GSM_RTPFRAME_SIZE_32 + GSM_RTPFRAME_SIZE_33; - + case Type::Bytes_31: rtpLength = GSM_RTPFRAME_SIZE_31; break; + case Type::Bytes_32: rtpLength = GSM_RTPFRAME_SIZE_32; break; + case Type::Bytes_33: rtpLength = GSM_RTPFRAME_SIZE_33; break; + case Type::Bytes_65: rtpLength = GSM_RTPFRAME_SIZE_32 + GSM_RTPFRAME_SIZE_33; break; + default: rtpLength = GSM_RTPFRAME_SIZE_33; } - return GSM_RTPFRAME_SIZE_33; + return { + .mName = "GSM-06.10", + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = GSM_AUDIOFRAME_TIME * 16, + .mFrameTime = GSM_AUDIOFRAME_TIME, + .mRtpLength = rtpLength + }; } -int GsmCodec::pcmLength() -{ - return GSM_AUDIOFRAME_TIME * 16; -} - -int GsmCodec::frameTime() -{ - return GSM_AUDIOFRAME_TIME; -} - -int GsmCodec::samplerate() -{ - return 8000; -} - -int GsmCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::EncodeResult GsmCodec::encode(std::span input, std::span output) { int outputBytes = 0; + char* outputBuffer = (char*)output.data(); - char* outputBuffer = (char*)output; - - for (int i = 0; i < inputBytes/pcmLength(); i++) + for (int i = 0; i < input.size_bytes() / pcmLength(); i++) { - gsm_encode(mGSM, (gsm_signal *)input+160*i, (gsm_byte*)outputBuffer); + gsm_encode(mGSM, (gsm_signal *)input.data()+160*i, (gsm_byte*)outputBuffer); outputBuffer += rtpLength(); outputBytes += rtpLength(); } - return outputBytes; + return {.mEncoded = (size_t)outputBytes}; } -int GsmCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult GsmCodec::decode(std::span input, std::span output) { - if (inputBytes % rtpLength() != 0) - return 0; + if (input.size_bytes() % rtpLength() != 0) + return {.mDecoded = 0}; int i=0; - for (i = 0; i < inputBytes/rtpLength(); i++) - gsm_decode(mGSM, (gsm_byte *)input + 33 * i, (gsm_signal *)output + 160 * i); + for (i = 0; i < input.size_bytes() / rtpLength(); i++) + gsm_decode(mGSM, (gsm_byte *)input.data() + 33 * i, (gsm_signal *)output.data() + 160 * i); - return i * 320; + return {.mDecoded = (size_t)i * 320}; } -int GsmCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t GsmCodec::plc(int lostFrames, std::span output) { - if (outputCapacity < lostFrames * pcmLength()) + if (output.size_bytes() < lostFrames * pcmLength()) return 0; // Return silence frames - memset(output, 0, lostFrames * pcmLength()); + memset(output.data(), 0, lostFrames * pcmLength()); return lostFrames * pcmLength(); } @@ -1155,58 +1049,52 @@ G722Codec::G722Codec() G722Codec::~G722Codec() { - g722_decode_release((g722_decode_state_t*)mDecoder); - g722_encode_release((g722_encode_state_t*)mEncoder); + g722_decode_release((g722_decode_state_t*)mDecoder); mDecoder = nullptr; + g722_encode_release((g722_encode_state_t*)mEncoder); mEncoder = nullptr; } -const char* G722Codec::name() -{ - return G722_MIME_NAME; +Codec::Info G722Codec::info() { + // ToDo: double check the G722 calls - remember RFC has bug about samplerate + return { + .mName = G722_MIME_NAME, + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 640, + .mFrameTime = 20, + .mRtpLength = 160 + }; } -int G722Codec::pcmLength() +Codec::EncodeResult G722Codec::encode(std::span input, std::span output) { - return 640; + if (output.size_bytes() < input.size_bytes() / 4) + return {.mEncoded = 0}; // Destination buffer not big enough + + int r = g722_encode((g722_encode_state_t *)mEncoder, (unsigned char*)output.data(), ( short*)input.data(), input.size_bytes() / 2); + if (r < 0) + return {.mEncoded = 0}; + + return {.mEncoded = (size_t)r}; } -int G722Codec::frameTime() +Codec::DecodeResult G722Codec::decode(std::span input, std::span output) { - return 20; + if (output.size_bytes() < input.size_bytes() * 4) + return {.mDecoded = 0}; // Destination buffer not big enough + + int r = g722_decode((g722_decode_state_t *)mDecoder, (short*)output.data(), (unsigned char*)input.data(), input.size_bytes()) * 2; + if (r < 0) + return {.mDecoded = 0}; + return {.mDecoded = (size_t)r}; } -int G722Codec::rtpLength() +size_t G722Codec::plc(int lostFrames, std::span output) { - return 160; -} - -int G722Codec::samplerate() -{ - return 8000; -} - -int G722Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - if (outputCapacity < inputBytes / 4) - return 0; // Destination buffer not big enough - - return g722_encode((g722_encode_state_t *)mEncoder, (unsigned char*)output, ( short*)input, inputBytes / 2); -} - -int G722Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - if (outputCapacity < inputBytes * 4) - return 0; // Destination buffer not big enough - - return g722_decode((g722_decode_state_t *)mDecoder, ( short*)output, (unsigned char*)input, inputBytes) * 2; -} - -int G722Codec::plc(int lostFrames, void* output, int outputCapacity) -{ - if (outputCapacity < lostFrames * pcmLength()) + if (output.size_bytes() < lostFrames * pcmLength()) return 0; // Return silence frames - memset(output, 0, lostFrames * pcmLength()); + memset(output.data(), 0, lostFrames * pcmLength()); return lostFrames * pcmLength(); } @@ -1318,7 +1206,6 @@ static bool repackHalfRate(BitReader& br, uint16_t frame[22], bool& lastItem) } GsmHrCodec::GsmHrCodec() - :mDecoder(nullptr) { mDecoder = new GsmHr::Codec(); } @@ -1329,34 +1216,21 @@ GsmHrCodec::~GsmHrCodec() mDecoder = nullptr; } -const char* GsmHrCodec::name() -{ - return "GSM-HR-08"; +Codec::Info GsmHrCodec::info() { + return { + .mName = "GSM-HR-08", + .mSamplerate = 8000, + .mChannels = 1, + .mPcmLength = 20 * 8 * 2, + .mFrameTime = 20, + .mRtpLength = 0 + }; } -int GsmHrCodec::pcmLength() +Codec::EncodeResult GsmHrCodec::encode(std::span input, std::span output) { - return frameTime() * 8 * 2; -} - -int GsmHrCodec::rtpLength() -{ - return 0; -} - -int GsmHrCodec::frameTime() -{ - return 20; -} - -int GsmHrCodec::samplerate() -{ - return 8000; -} - -int GsmHrCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) -{ - return 0; + // Not supported yet + return {.mEncoded = 0}; } static const int params_unvoiced[] = { @@ -1447,23 +1321,23 @@ hr_ref_from_canon(uint16_t *hr_ref, const uint8_t *canon) [+] PQ: Adding conversion from canon to rawpcm-s16le (for codec pcm) [+] PQ: Adding file output (blk_len=320) */ -int GsmHrCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::DecodeResult GsmHrCodec::decode(std::span input, std::span output) { - ByteBuffer bb(input, inputBytes, ByteBuffer::CopyBehavior::UseExternal); + ByteBuffer bb(input, ByteBuffer::CopyBehavior::UseExternal); BitReader br(bb); uint16_t hr_ref[22]; - hr_ref_from_canon(hr_ref, (const uint8_t*)input + 1); + hr_ref_from_canon(hr_ref, input.data() + 1); hr_ref[18] = 0; /* BFI : 1 bit */ hr_ref[19] = 0; /* UFI : 1 bit */ hr_ref[20] = 0; /* SID : 2 bit */ hr_ref[21] = 0; /* TAF : 1 bit */ - reinterpret_cast(mDecoder)->speechDecoder((int16_t*)hr_ref, (int16_t*)output); - return 320; + reinterpret_cast(mDecoder)->speechDecoder((int16_t*)hr_ref, (int16_t*)output.data()); + return {.mDecoded = 320}; } -int GsmHrCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t GsmHrCodec::plc(int lostFrames, std::span output) { return 0; } diff --git a/src/engine/media/MT_AudioCodec.h b/src/engine/media/MT_AudioCodec.h index da11bec4..d6a564a7 100644 --- a/src/engine/media/MT_AudioCodec.h +++ b/src/engine/media/MT_AudioCodec.h @@ -40,24 +40,19 @@ public: { public: const char* name() override; - int channels() override; - int samplerate() override; - int payloadType() override; + int channels() override; + int samplerate() override; + int payloadType() override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; - int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; + int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; PCodec create() override; }; G729Codec(); ~G729Codec() override; - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; - int channels() override; + Info info() override; EncodeResult encode(std::span input, std::span output) override; DecodeResult decode(std::span input, std::span output) override; @@ -67,16 +62,19 @@ public: class OpusCodec: public Codec { protected: - OpusEncoder *mEncoderCtx; - OpusDecoder *mDecoderCtx; - int mPTime, mSamplerate, mChannels; - // Audio::SpeexResampler mDecodeResampler; - int mDecoderChannels; + OpusEncoder *mEncoderCtx = nullptr; + OpusDecoder *mDecoderCtx = nullptr; + int mPTime = 0, mSamplerate = 0, mChannels = 0; + int mDecoderChannels = 0; public: struct Params { - bool mUseDtx, mUseInbandFec, mStereo; - int mPtime, mTargetBitrate, mExpectedPacketLoss; + bool mUseDtx = false, + mUseInbandFec = false, + mStereo = false; + int mPtime = 0, + mTargetBitrate = 0, + mExpectedPacketLoss = 0; Params(); resip::Data toString() const; @@ -103,29 +101,24 @@ public: PCodec create() override; }; - OpusCodec(int samplerate, int channels, int ptime); + OpusCodec(Audio::Format fmt, int ptime); ~OpusCodec(); void applyParams(const Params& params); - const char* name(); - int pcmLength(); - int rtpLength(); - int frameTime(); - int samplerate(); - int channels(); + Info info() override; - EncodeResult encode(std::span input, std::span output); - DecodeResult decode(std::span input, std::span output); - size_t plc(int lostFrames, std::span output); + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; class IlbcCodec: public Codec { protected: - int mPacketTime; /// Single frame time (20 or 30 ms) - iLBC_encinst_t* mEncoderCtx; - iLBC_decinst_t* mDecoderCtx; + int mPacketTime = 0; /// Single frame time (20 or 30 ms) + iLBC_encinst_t* mEncoderCtx = nullptr; + iLBC_decinst_t* mDecoderCtx = nullptr; public: class IlbcFactory: public Factory @@ -148,11 +141,7 @@ public: IlbcCodec(int packetTime); virtual ~IlbcCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; + Info info() override; EncodeResult encode(std::span input, std::span output) override; DecodeResult decode(std::span input, std::span output) override; @@ -189,11 +178,7 @@ public: G711Codec(int type); ~G711Codec(); - const char* name() override; - int pcmLength() override; - int frameTime() override; - int rtpLength() override; - int samplerate() override; + Info info() override; EncodeResult encode(std::span input, std::span output) override; DecodeResult decode(std::span input, std::span output) override; @@ -206,9 +191,9 @@ protected: class IsacCodec: public Codec { protected: - int mSamplerate; - ISACFIX_MainStruct* mEncoderCtx; - ISACFIX_MainStruct* mDecoderCtx; + int mSamplerate = 0; + ISACFIX_MainStruct* mEncoderCtx = nullptr; + ISACFIX_MainStruct* mDecoderCtx = nullptr; public: class IsacFactory16K: public Factory { @@ -240,11 +225,7 @@ public: IsacCodec(int sampleRate); ~IsacCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; + Info info() override; EncodeResult encode(std::span input, std::span output) override; DecodeResult decode(std::span input, std::span output) override; @@ -312,17 +293,13 @@ public: GsmCodec(Type codecType); /*! Destructor. */ - virtual ~GsmCodec(); + ~GsmCodec(); - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; + Info info() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostFrames, void* output, int outputCapacity); + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; /// GSM MIME name @@ -361,25 +338,19 @@ public: PCodec create(); }; G722Codec(); - virtual ~G722Codec(); + ~G722Codec(); - const char* name(); - int pcmLength(); - int rtpLength(); - int frameTime(); - int samplerate(); + Info info() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity); - int decode(const void* input, int inputBytes, void* output, int outputCapacity); - int plc(int lostFrames, void* output, int outputCapacity); - - //unsigned GetSamplerate() { return 16000; } + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; class GsmHrCodec: public Codec { protected: - void* mDecoder; + void* mDecoder = nullptr; public: class GsmHrFactory: public Factory @@ -399,15 +370,11 @@ public: GsmHrCodec(); ~GsmHrCodec() override; - const char* name() override; - int pcmLength() override; - int rtpLength() override; - int frameTime() override; - int samplerate() override; + Info info() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; }; } diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 6f21e5c2..e6eee304 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -396,12 +396,10 @@ size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t out for (int i=0; i < frame_count; i++) { - auto decoded_length = codec.decode(p.GetPayloadData() + i * codec.rtpLength(), - frame_length, - output_buffer, - output_capacity); + auto r = codec.decode({p.GetPayloadData() + i * codec.rtpLength(), (size_t)frame_length}, + {(uint8_t*)output_buffer, output_capacity}); - result += decoded_length; + result += r.mDecoded; } } else @@ -572,7 +570,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output mDecodedLength = 0; else { - mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame); + mDecodedLength = mCodec->plc(mFrameCount, {(uint8_t*)mDecodedFrame, sizeof mDecodedFrame}); if (!mDecodedLength) { // PLC is not support or failed @@ -587,7 +585,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output if (mDecodedLength) { processDecoded(output, options); - return {.mStatus = DecodeResult::Status::Ok,.mChannels = mCodec->channels(), .mSamplerate = mCodec->samplerate()}; + return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = mCodec->samplerate(), .mChannels = mCodec->channels()}; } else return {.mStatus = DecodeResult::Status::Skip}; @@ -683,9 +681,15 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out else { // Decode frame by frame - mDecodedLength = mCodec->decode(rtp.GetPayloadData() + i * mCodec->rtpLength(), frameLength, mDecodedFrame, sizeof mDecodedFrame); + auto r = mCodec->decode({rtp.GetPayloadData() + i * mCodec->rtpLength(), (size_t)frameLength}, + {(uint8_t*)mDecodedFrame, sizeof mDecodedFrame}); + mDecodedLength = r.mDecoded; if (mDecodedLength > 0) processDecoded(output, options); + + // What is important - here we may have packet marked as CNG + if (r.mIsCng) + mCngPacket = packet; } } result.mStatus = mFrameCount > 0 ? DecodeResult::Status::Ok : DecodeResult::Status::Skip; @@ -705,14 +709,40 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options) { + // There are two cases + // First is we have no ready time estimated how much audio should be emitted i.e. audio is decoded right after the next packet arrives. + // In this case we just skip the analysis - we should not be called in this situation + if (options.mElapsed == 0ms || !mCodec) + return {.mStatus = DecodeResult::Status::Skip}; + // No packet available at all (and no previous CNG packet) - so return the silence if (options.mElapsed != 0ms && mCodec) { Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat(); - // Emit silence if codec information is available - it is to properly handle the gaps - auto avail = output.getTimeLength(fmt.rate(), fmt.channels()); - if (options.mElapsed > avail) - mAvailable.addZero(fmt.sizeFromTime(options.mElapsed - avail)); + if (mCngPacket) + { + // Try to decode it - replay previous audio decoded or use CNG decoder (if payload type is 13) + if (mCngPacket->rtp()->GetPayloadType() == 13) + { + // Using latest CNG packet to produce comfort noise + auto produced = mCngDecoder.produce(fmt.rate(), options.mElapsed.count(), (short*)(output.data() + output.filled()), false); + output.setFilled(output.filled() + produced); + return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()}; + } + else + { + // Here we have another packet marked as CNG - for another decoder + // Just decode it +1 time + return decodePacketTo(output, options, mCngPacket); + } + } + else + { + // Emit silence if codec information is available - it is to properly handle the gaps + auto avail = output.getTimeLength(fmt.rate(), fmt.channels()); + if (options.mElapsed > avail) + output.addZero(fmt.sizeFromTime(options.mElapsed - avail)); + } } mFailedCount++; @@ -871,11 +901,10 @@ void AudioReceiver::updateAmrCodecStats(Codec* c) int AudioReceiver::getSize() const { int result = 0; - result += sizeof(*this) + mResampler8.getSize() + mResampler16.getSize() + mResampler32.getSize() - + mResampler48.getSize(); + result += sizeof(*this) + mResampler8.getSize() + mResampler16.getSize() + mResampler32.getSize() + mResampler48.getSize(); if (mCodec) - result += mCodec->getSize(); + ; // ToDo: need the way to calculate size of codec instances return result; } diff --git a/src/engine/media/MT_AudioStream.cpp b/src/engine/media/MT_AudioStream.cpp index adba76c2..1d65dc95 100644 --- a/src/engine/media/MT_AudioStream.cpp +++ b/src/engine/media/MT_AudioStream.cpp @@ -210,18 +210,17 @@ void AudioStream::addData(const void* buffer, int bytes) if (mSendingDump) mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength()); - int produced; - produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i, - codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME); + auto r = codec->encode({(const uint8_t*)mCapturedAudio.data() + codec->pcmLength()*i, (size_t)codec->pcmLength()}, + {(uint8_t*)mFrameBuffer, MT_MAXAUDIOFRAME}); // Counter of processed input bytes of raw pcm data from microphone processed += codec->pcmLength(); encodedTime += codec->frameTime(); mEncodedTime += codec->frameTime(); - if (produced) + if (r.mEncoded) { - mEncodedAudio.appendBuffer(mFrameBuffer, produced); + mEncodedAudio.appendBuffer(mFrameBuffer, r.mEncoded); if (packetTime <= encodedTime) { // Time to send packet diff --git a/src/engine/media/MT_Codec.h b/src/engine/media/MT_Codec.h index ea43f100..f0da8002 100644 --- a/src/engine/media/MT_Codec.h +++ b/src/engine/media/MT_Codec.h @@ -23,6 +23,7 @@ class CodecMap: public std::map class Codec { public: + class Factory { public: @@ -41,21 +42,30 @@ public: resip::Codec resipCodec(); }; virtual ~Codec() {} - virtual const char* name() = 0; - virtual int samplerate() = 0; - virtual float timestampUnit() { return float(1.0 / samplerate()); } - // Size of decoded audio frame in bytes - virtual int pcmLength() = 0; + struct Info + { + std::string mName; + int mSamplerate = 0; // Hz + int mChannels = 0; + int mPcmLength = 0; // In bytes + int mFrameTime = 0; // In milliseconds + int mRtpLength = 0; // In bytes + }; + // Returns information about this codec instance + virtual Info info() = 0; - // Time length of single audio frame - virtual int frameTime() = 0; + // Helper functions to return information - they are based on info() method + int pcmLength() { return info().mPcmLength; } + int rtpLength() { return info().mRtpLength; } + int channels() { return info().mChannels; } + int samplerate() { return info().mSamplerate; } + int frameTime() { return info().mFrameTime; } + std::string name() { return info().mName; } - // Size of RTP frame in bytes. Can be zero for variable sized codecs. - virtual int rtpLength() = 0; - - // Number of audio channels - virtual int channels() { return 1; } + Audio::Format getAudioFormat() { + return Audio::Format(this->info().mSamplerate, this->info().mChannels); + } // Returns size of encoded data (RTP) in bytes struct EncodeResult @@ -75,10 +85,6 @@ public: // Returns size of produced data (PCM signed short) in bytes virtual size_t plc(int lostFrames, std::span output) = 0; - // Returns size of codec in memory - virtual size_t getSize() const { return 0; }; - - virtual Audio::Format getAudioFormat() { return Audio::Format(this->samplerate(), this->channels());}; }; } #endif diff --git a/src/engine/media/MT_EvsCodec.cpp b/src/engine/media/MT_EvsCodec.cpp index 92c79922..3eda370f 100644 --- a/src/engine/media/MT_EvsCodec.cpp +++ b/src/engine/media/MT_EvsCodec.cpp @@ -1,6 +1,6 @@ #include "MT_EvsCodec.h" - - +#include +#include /*-------------------------------------------------------------------* * rate2AMRWB_IOmode() @@ -167,67 +167,58 @@ EVSCodec::~EVSCodec() } } -int EVSCodec::samplerate() -{ - return st_dec->output_Fs; +Codec::Info EVSCodec::info() { + return { + .mName = MT_EVS_CODECNAME, + .mSamplerate = st_dec->output_Fs, + .mChannels = 1, + .mPcmLength = st_dec->output_Fs / 1000 * sp.ptime * 2, + .mFrameTime = sp.ptime, + .mRtpLength = 0 + }; } -int EVSCodec::pcmLength() -{ - return samplerate() / 50 * 2; -} -int EVSCodec::frameTime() -{ - return sp.ptime; -} - -int EVSCodec::rtpLength() -{ - // Variable sized codec - bitrate can be changed during the call - return 0; -} - -int EVSCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity) +Codec::EncodeResult EVSCodec::encode(std::span input, std::span output) { // Encoding is not supported yet. - return 0; + return {.mEncoded = 0}; } -int EVSCodec::decode(const void* input, int input_length, void* output, int outputCapacity) +Codec::DecodeResult EVSCodec::decode(std::span input, std::span output) { - if (outputCapacity < pcmLength()) - return 0; + if (output.size_bytes() < pcmLength()) + return {.mDecoded = 0}; std::string buffer; // Check if we get payload with CMR - auto payload_iter = FixedPayload_EVSPrimary.find((input_length - 2) * 8); + auto payload_iter = FixedPayload_EVSPrimary.find((input.size_bytes() - 2) * 8); if (payload_iter == FixedPayload_EVSPrimary.end()) { // Check if we get payload with ToC and without CMR - payload_iter = FixedPayload_EVSPrimary.find((input_length - 1) * 8); + payload_iter = FixedPayload_EVSPrimary.find((input.size_bytes() - 1) * 8); if (payload_iter == FixedPayload_EVSPrimary.end()) { // Maybe there is no ToC ? - payload_iter = FixedPayload_EVSPrimary.find(input_length * 8); + payload_iter = FixedPayload_EVSPrimary.find(input.size_bytes() * 8); if (payload_iter == FixedPayload_EVSPrimary.end()) { // Bad payload size at all - return 0; + return {.mDecoded = 0}; } /* Add ToC byte. * WARNING maybe it will be work incorrect with 56bit payload, * see 3GPP TS 26.445 Annex A, A.2.1.3 */ - char c = evs::rate2EVSmode(FixedPayload_EVSPrimary.find(input_length * 8)->second); + char c = evs::rate2EVSmode(FixedPayload_EVSPrimary.find(input.size_bytes() * 8)->second); buffer += c; - buffer += std::string(reinterpret_cast(input), input_length); + buffer += std::string(reinterpret_cast(input.data()), input.size_bytes()); } else - buffer = std::string(reinterpret_cast(input), input_length); + buffer = std::string(reinterpret_cast(input.data()), input.size_bytes()); } else // Skip CMR byte - buffer = std::string(reinterpret_cast(input) + 1, input_length-1); + buffer = std::string(reinterpret_cast(input.data()) + 1, input.size_bytes()-1); // Output buffer for 48 KHz @@ -263,7 +254,7 @@ int EVSCodec::decode(const void* input, int input_length, void* output, int outp } /* convert 'float' output data to 'short' */ - evs::syn_output(data, static_cast(pcmLength() / 2), static_cast(output) + offset); + evs::syn_output(data, static_cast(pcmLength() / 2), reinterpret_cast(output.data()) + offset); offset += pcmLength() / 2; if (st_dec->ini_frame < MAX_FRAME_COUNTER) { @@ -271,12 +262,12 @@ int EVSCodec::decode(const void* input, int input_length, void* output, int outp } } - return pcmLength(); + return {.mDecoded = (size_t)pcmLength()}; } -int EVSCodec::plc(int lostFrames, void* output, int outputCapacity) +size_t EVSCodec::plc(int lostFrames, std::span output) { - return 0; + return 0; } void EVSCodec::initDecoder(const StreamParameters& sp) diff --git a/src/engine/media/MT_EvsCodec.h b/src/engine/media/MT_EvsCodec.h index cd1b64ea..3c4c9cf2 100644 --- a/src/engine/media/MT_EvsCodec.h +++ b/src/engine/media/MT_EvsCodec.h @@ -2,15 +2,11 @@ #define __MT_EVS_CODEC_H #include "../engine_config.h" -#include -#include -#include #include #include #include #include #include -#include #include "MT_Codec.h" @@ -52,18 +48,14 @@ public: EVSCodec(const StreamParameters& sp); ~EVSCodec() override; - const char* name() override { return MT_EVS_CODECNAME; } - int samplerate() override; - int pcmLength() override; - int frameTime() override; - int rtpLength() override; - int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; - int plc(int lostFrames, void* output, int outputCapacity) override; + Info info() override; + + EncodeResult encode(std::span input, std::span output) override; + DecodeResult decode(std::span input, std::span output) override; + size_t plc(int lostFrames, std::span output) override; private: - evs::Decoder_State* st_dec; - //Encoder_State_fx* st_enc; + evs::Decoder_State* st_dec = nullptr; StreamParameters sp; void initDecoder(const StreamParameters& sp); }; diff --git a/src/engine/media/MT_SrtpHelper.cpp b/src/engine/media/MT_SrtpHelper.cpp index cbaf0e8e..70a68a7c 100644 --- a/src/engine/media/MT_SrtpHelper.cpp +++ b/src/engine/media/MT_SrtpHelper.cpp @@ -212,15 +212,13 @@ void SrtpSession::close() SrtpKeySalt& SrtpSession::outgoingKey(SrtpSuite suite) { - Lock l(mGuard); assert(suite > SRTP_NONE && suite <= SRTP_LAST); - return mOutgoingKey[int(suite)-1]; + Lock l(mGuard); + return mOutgoingKey[int(suite)-1]; // The automated review sometimes give the hints about the possible underflow array index access } bool SrtpSession::protectRtp(void* buffer, int* length) { - // addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing); - Lock l(mGuard); if (mOutboundSession) { diff --git a/src/engine/media/MT_SrtpHelper.h b/src/engine/media/MT_SrtpHelper.h index 43476d60..6a6af54f 100644 --- a/src/engine/media/MT_SrtpHelper.h +++ b/src/engine/media/MT_SrtpHelper.h @@ -20,20 +20,17 @@ enum SrtpSuite { - SRTP_NONE, - SRTP_AES_128_AUTH_80, - SRTP_AES_256_AUTH_80, - SRTP_AES_192_AUTH_80, - SRTP_AES_128_AUTH_32, - SRTP_AES_256_AUTH_32, - SRTP_AES_192_AUTH_32, - SRTP_AES_128_AUTH_NULL, - SRTP_AED_AES_256_GCM, - SRTP_AED_AES_128_GCM, + SRTP_NONE = 0, + SRTP_AES_128_AUTH_80 = 1, + SRTP_AES_256_AUTH_80 = 2, + SRTP_AES_192_AUTH_80 = 3, + SRTP_AES_128_AUTH_32 = 4, + SRTP_AES_256_AUTH_32 = 5, + SRTP_AES_192_AUTH_32 = 6, + SRTP_AES_128_AUTH_NULL = 7, + SRTP_AED_AES_256_GCM = 8, + SRTP_AED_AES_128_GCM = 9, SRTP_LAST = SRTP_AED_AES_128_GCM - // ToDo: - // a=crypto:1 AEAD_AES_256_GCM_8 inline:tN2A0vRjFBimpQsW2GasuJuPe7hKE26gki30APC8DVuySqCOYTs8lYBPR5I= - // a=crypto:3 AEAD_AES_128_GCM_8 inline:Ok7VL8SmBHSbZLw4dK6iQgpliYKGdY9BHLJcRw== }; extern SrtpSuite toSrtpSuite(const std::string_view& s); From bdc4858bccdc9ecd30c48e0df13bc4fcfa9f05e3 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Tue, 24 Feb 2026 09:50:44 +0300 Subject: [PATCH 4/9] - initial work to fix RTPdump decoder + more fixes --- src/CMakeLists.txt | 2 + src/engine/agent/Agent_Impl.cpp | 40 +- src/engine/audio/Audio_DataWindow.cpp | 10 + src/engine/helper/HL_NetworkSocket.cpp | 15 +- src/engine/helper/HL_Rtp.cpp | 426 +++++++++++++--- src/engine/helper/HL_Rtp.h | 101 +++- src/engine/helper/HL_Sync.cpp | 3 +- src/engine/media/MT_Codec.h | 7 +- src/engine/media/MT_Dtmf.cpp | 582 +++++++++++----------- src/engine/media/MT_SingleAudioStream.cpp | 10 +- src/engine/media/MT_SingleAudioStream.h | 14 +- test/rtp_decode/CMakeLists.txt | 6 + test/rtp_decode/main.cpp | 284 +++++++++++ 13 files changed, 1080 insertions(+), 420 deletions(-) create mode 100644 test/rtp_decode/CMakeLists.txt create mode 100644 test/rtp_decode/main.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1ff40c0..a40c1463 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -341,6 +341,8 @@ set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus if (USE_AMR_CODEC) #include (${LIB_PLATFORM}/platform_libs.cmake) + set (OPENCORE_AMRNB opencore-amrnb) + set (OPENCORE_AMRWB opencore-amrwb) message("Media: AMR NB and WB codecs will be included.") set (DEFINES ${DEFINES} -DUSE_AMR_CODEC) set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB}) diff --git a/src/engine/agent/Agent_Impl.cpp b/src/engine/agent/Agent_Impl.cpp index 939fbf8d..0d185785 100644 --- a/src/engine/agent/Agent_Impl.cpp +++ b/src/engine/agent/Agent_Impl.cpp @@ -16,6 +16,7 @@ const std::string Status_FailedToOpenFile = "failed to open file"; const std::string Status_NoActiveProvider = "no active provider"; const std::string Status_NoMediaAction = "no valid media action"; const std::string Status_NoCommand = "no valid command"; +const std::string Status_NoAudioManager = "no audio manager"; #define LOG_SUBSYSTEM "Agent" @@ -336,7 +337,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans { // Agent was not started ICELogError(<< "No audio manager installed."); - answer["status"] = "Audio manager not started. Most probably agent is not started."; + answer["status"] = Status_NoAudioManager; return; } @@ -431,28 +432,35 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an auto sessionIter = mSessionMap.find(request["session_id"].asInt()); if (sessionIter != mSessionMap.end()) { - // Ensure audio manager is here - mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); + if (!mAudioManager) + { + ICELogError(<< "No audio manager installed."); + answer["status"] = Status_NoAudioManager; + } + else + { + // Ensure audio manager is here + mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); - // Accept session on SIP level - PSession session = sessionIter->second; + // Accept session on SIP level + PSession session = sessionIter->second; - // Get user headers - Session::UserHeaders info; - JsonCpp::Value& arg = request["userinfo"]; - std::vector keys = arg.getMemberNames(); - for (const std::string& k: keys) - info[k] = arg[k].asString(); - session->setUserHeaders(info); + // Get user headers + Session::UserHeaders info; + JsonCpp::Value& arg = request["userinfo"]; + std::vector keys = arg.getMemberNames(); + for (const std::string& k: keys) + info[k] = arg[k].asString(); + session->setUserHeaders(info); - // Accept finally - session->accept(); + // Accept finally + session->accept(); - answer["status"] = Status_Ok; + answer["status"] = Status_Ok; + } } else answer["status"] = Status_SessionNotFound; - } void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer) diff --git a/src/engine/audio/Audio_DataWindow.cpp b/src/engine/audio/Audio_DataWindow.cpp index c33f5040..8a16c404 100644 --- a/src/engine/audio/Audio_DataWindow.cpp +++ b/src/engine/audio/Audio_DataWindow.cpp @@ -18,14 +18,24 @@ DataWindow::DataWindow() DataWindow::~DataWindow() { if (mData) + { free(mData); + mData = nullptr; + } } void DataWindow::setCapacity(int capacity) { Lock l(mMutex); int tail = capacity - mCapacity; + char* buffer = mData; mData = (char*)realloc(mData, capacity); + if (!mData) + { + // Realloc failed + mData = buffer; + throw std::bad_alloc(); + } if (tail > 0) memset(mData + mCapacity, 0, tail); mCapacity = capacity; diff --git a/src/engine/helper/HL_NetworkSocket.cpp b/src/engine/helper/HL_NetworkSocket.cpp index 05dc7423..2d0802ce 100644 --- a/src/engine/helper/HL_NetworkSocket.cpp +++ b/src/engine/helper/HL_NetworkSocket.cpp @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com) +/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -9,6 +9,7 @@ #include "../engine_config.h" #include "HL_NetworkSocket.h" +#include "HL_Log.h" #if defined(TARGET_OSX) || defined(TARGET_LINUX) # include @@ -19,11 +20,11 @@ #endif #include +#define LOG_SUBSYSTEM "network" + DatagramSocket::DatagramSocket() :mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) -{ - -} +{} DatagramSocket::~DatagramSocket() { @@ -160,6 +161,12 @@ void DatagramAgreggator::addSocket(PDatagramSocket socket) if (socket->mHandle == INVALID_SOCKET) return; + if (mSocketVector.size() >= 62) + { + ICELogError(<< "fd_set overflow; too much sockets"); + return; + } + FD_SET(socket->mHandle, &mReadSet); if (socket->mHandle > mMaxHandle) mMaxHandle = socket->mHandle; diff --git a/src/engine/helper/HL_Rtp.cpp b/src/engine/helper/HL_Rtp.cpp index 728d1ae0..d244bba6 100644 --- a/src/engine/helper/HL_Rtp.cpp +++ b/src/engine/helper/HL_Rtp.cpp @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) +/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -8,70 +8,95 @@ # include #endif -#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) +#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX) # include #endif #include "HL_Rtp.h" #include "HL_Exception.h" -#include "HL_String.h" +#include "HL_Log.h" -#if defined(USE_RTP_DUMP) -# include "jrtplib/src/rtprawpacket.h" -# include "jrtplib/src/rtpipv4address.h" -#endif +#include "jrtplib/src/rtprawpacket.h" +#include "jrtplib/src/rtpipv4address.h" -#if !defined(TARGET_WIN) -# include -#endif +#include +#include +#include +#include +#include -#include -#include +#define LOG_SUBSYSTEM "RtpDump" +static constexpr size_t MAX_RTP_PACKET_SIZE = 65535; +static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0"; + +// RTP fixed header (little-endian bit-field layout) struct RtpHeader { unsigned char cc:4; /* CSRC count */ unsigned char x:1; /* header extension flag */ unsigned char p:1; /* padding flag */ - unsigned char version:2; /* protocol version */ - unsigned char pt:7; /* payload type */ - unsigned char m:1; /* marker bit */ - unsigned short seq; /* sequence number */ - unsigned int ts; /* timestamp */ - unsigned int ssrc; /* synchronization source */ + unsigned char version:2; /* protocol version */ + unsigned char pt:7; /* payload type */ + unsigned char m:1; /* marker bit */ + unsigned short seq; /* sequence number */ + unsigned int ts; /* timestamp */ + unsigned int ssrc; /* synchronization source */ }; struct RtcpHeader { - unsigned char rc:5; /* reception report count */ + unsigned char rc:5; /* reception report count */ unsigned char p:1; /* padding flag */ unsigned char version:2; /* protocol version */ - unsigned char pt:8; /* payload type */ + unsigned char pt; /* payload type */ uint16_t len; /* length */ - uint32_t ssrc; /* synchronization source */ + uint32_t ssrc; /* synchronization source */ }; +// --- IPv4 address helpers --- + +static std::string ipToString(uint32_t ip) +{ + // ip in host byte order → dotted-decimal + return std::to_string((ip >> 24) & 0xFF) + "." + + std::to_string((ip >> 16) & 0xFF) + "." + + std::to_string((ip >> 8) & 0xFF) + "." + + std::to_string( ip & 0xFF); +} + +static uint32_t stringToIp(const std::string& s) +{ + unsigned a = 0, b = 0, c = 0, d = 0; + if (std::sscanf(s.c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) != 4) + return 0; + if (a > 255 || b > 255 || c > 255 || d > 255) + return 0; + return (a << 24) | (b << 16) | (c << 8) | d; +} + +// --- RtpHelper implementation --- + bool RtpHelper::isRtp(const void* buffer, size_t length) { if (length < 12) return false; const RtpHeader* h = reinterpret_cast(buffer); - if (h->version != 0b10) + if (h->version != 2) return false; unsigned char pt = h->pt; - bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35); + bool rtp = (pt >= 96 && pt <= 127) || (pt < 35); return rtp; } - bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length) { if (length < 12) return false; const RtcpHeader* h = reinterpret_cast(buffer); - return h->version == 0b10; + return h->version == 2; } bool RtpHelper::isRtcp(const void* buffer, size_t length) @@ -83,15 +108,16 @@ unsigned RtpHelper::findSsrc(const void* buffer, size_t length) { if (isRtp(buffer, length)) return ntohl(reinterpret_cast(buffer)->ssrc); - else + else if (isRtpOrRtcp(buffer, length)) return ntohl(reinterpret_cast(buffer)->ssrc); + return 0; } void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc) { if (isRtp(buffer, length)) reinterpret_cast(buffer)->ssrc = htonl(ssrc); - else + else if (isRtpOrRtcp(buffer, length)) reinterpret_cast(buffer)->ssrc = htonl(ssrc); } @@ -113,47 +139,186 @@ int RtpHelper::findPacketNo(const void *buffer, size_t length) int RtpHelper::findPayloadLength(const void* buffer, size_t length) { - if (isRtp(buffer, length)) - { - return length - 12; - } - else + if (!isRtp(buffer, length)) return -1; + + const RtpHeader* h = reinterpret_cast(buffer); + const uint8_t* p = static_cast(buffer); + + // Fixed header (12 bytes) + CSRC list (4 * CC bytes) + size_t offset = 12 + 4u * h->cc; + if (offset > length) + return -1; + + // Header extension + if (h->x) { + if (offset + 4 > length) + return -1; + uint16_t extWords = (static_cast(p[offset + 2]) << 8) | p[offset + 3]; + offset += 4 + 4u * extWords; + if (offset > length) + return -1; + } + + size_t payloadLen = length - offset; + + // Padding + if (h->p && payloadLen > 0) { + uint8_t padBytes = p[length - 1]; + if (padBytes > payloadLen) + return -1; + payloadLen -= padBytes; + } + + return static_cast(payloadLen); } -#if defined(USE_RTPDUMP) -RtpDump::RtpDump(const char *filename) - :mFilename(filename) -{} +// --- RtpDump implementation --- -RtpDump::~RtpDump() +std::shared_ptr RtpDump::parseRtpData(const uint8_t* data, size_t len) { - flush(); - for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter) - { - //free(packetIter->mData); - delete packetIter->mPacket; + if (!data || len < 12 || !RtpHelper::isRtp(data, len)) + return nullptr; + + try { + // Both are heap-allocated; RTPRawPacket takes ownership and deletes them + auto* addr = new jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0)); + uint8_t* dataCopy = new uint8_t[len]; + std::memcpy(dataCopy, data, len); + + jrtplib::RTPRawPacket raw(dataCopy, len, addr, jrtplib::RTPTime(0), true); + auto packet = std::make_shared(raw); + + if (packet->GetCreationError() != 0) + return nullptr; + + return packet; + } catch (const std::exception& e) { + ICELogInfo(<< "Failed to parse RTP packet: " << e.what()); + return nullptr; } } +RtpDump::RtpDump(const char* filename) + : mFilename(filename ? filename : "") +{ +} + +RtpDump::~RtpDump() = default; + +void RtpDump::setSource(uint32_t ip, uint16_t port) +{ + mSourceIp = ip; + mSourcePort = port; +} + void RtpDump::load() { - FILE* f = fopen(mFilename.c_str(), "rb"); - if (!f) - throw Exception(ERR_WAVFILE_FAILED); + if (mFilename.empty()) + throw std::runtime_error("No filename specified"); - while (!feof(f)) - { - RtpData data; - fread(&data.mLength, sizeof data.mLength, 1, f); - data.mData = new char[data.mLength]; - fread(data.mData, 1, data.mLength, f); - jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address); - jrtplib::RTPTime t(0); - jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true); - data.mPacket = new jrtplib::RTPPacket(*raw); - mPacketList.push_back(data); + std::ifstream input(mFilename, std::ios::binary); + if (!input.is_open()) + throw std::runtime_error("Failed to open RTP dump file: " + mFilename); + + mPacketList.clear(); + + // --- 1. Text header: "#!rtpplay1.0 /\n" --- + std::string textLine; + std::getline(input, textLine); + if (textLine.compare(0, sizeof(RTPDUMP_SHEBANG) - 1, RTPDUMP_SHEBANG) != 0) + throw std::runtime_error("Invalid rtpdump header: expected " + std::string(RTPDUMP_SHEBANG)); + + // Parse source address from the text line + size_t spacePos = textLine.find(' '); + if (spacePos != std::string::npos) { + std::string addrPart = textLine.substr(spacePos + 1); + size_t slashPos = addrPart.find('/'); + if (slashPos != std::string::npos) { + mSourceIp = stringToIp(addrPart.substr(0, slashPos)); + try { + mSourcePort = static_cast(std::stoi(addrPart.substr(slashPos + 1))); + } catch (...) { + mSourcePort = 0; + } + } } + + // --- 2. Binary file header (RD_hdr_t, 16 bytes) --- + uint32_t buf32; + uint16_t buf16; + + input.read(reinterpret_cast(&buf32), 4); + mStartSec = ntohl(buf32); + + input.read(reinterpret_cast(&buf32), 4); + mStartUsec = ntohl(buf32); + + input.read(reinterpret_cast(&buf32), 4); // source IP (already NBO in file) + // The binary header stores IP in network byte order; convert to host + mSourceIp = ntohl(buf32); + + input.read(reinterpret_cast(&buf16), 2); + mSourcePort = ntohs(buf16); + + input.read(reinterpret_cast(&buf16), 2); // padding — discard + + if (!input.good()) + throw std::runtime_error("Failed to read rtpdump binary header"); + + // --- 3. Packet records --- + size_t packetCount = 0; + + while (input.good() && input.peek() != EOF) { + // Packet header: length(2) + plen(2) + offset(4) = 8 bytes + uint16_t recLength, plen; + uint32_t offsetMs; + + input.read(reinterpret_cast(&recLength), 2); + if (input.gcount() != 2) break; + recLength = ntohs(recLength); + + input.read(reinterpret_cast(&plen), 2); + if (input.gcount() != 2) break; + plen = ntohs(plen); + + input.read(reinterpret_cast(&offsetMs), 4); + if (input.gcount() != 4) break; + offsetMs = ntohl(offsetMs); + + // All-zeros record signals end of file in some implementations + if (recLength == 0 && plen == 0 && offsetMs == 0) + break; + + if (plen == 0 || plen > MAX_RTP_PACKET_SIZE) + throw std::runtime_error("Invalid packet payload length: " + std::to_string(plen)); + + if (recLength < plen + 8) + throw std::runtime_error("Record length (" + std::to_string(recLength) + + ") smaller than payload + header (" + std::to_string(plen + 8) + ")"); + + // Read body + std::vector body(plen); + input.read(reinterpret_cast(body.data()), plen); + if (static_cast(input.gcount()) != plen) + throw std::runtime_error("Incomplete packet data in rtpdump file"); + + // Skip any padding between plen and recLength-8 + size_t pad = static_cast(recLength) - 8 - plen; + if (pad > 0) + input.seekg(static_cast(pad), std::ios::cur); + + RtpData entry; + entry.mRawData = std::move(body); + entry.mOffsetMs = offsetMs; + entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size()); + + mPacketList.push_back(std::move(entry)); + packetCount++; + } + + ICELogInfo(<< "Loaded " << packetCount << " packets from " << mFilename); + mLoaded = true; } size_t RtpDump::count() const @@ -163,39 +328,142 @@ size_t RtpDump::count() const jrtplib::RTPPacket& RtpDump::packetAt(size_t index) { + if (index >= mPacketList.size()) + throw std::out_of_range("Packet index out of range: " + std::to_string(index)); + + if (!mPacketList[index].mPacket) + throw std::runtime_error("No parsed RTP data at index " + std::to_string(index)); + return *mPacketList[index].mPacket; } +const std::vector& RtpDump::rawDataAt(size_t index) const +{ + if (index >= mPacketList.size()) + throw std::out_of_range("Packet index out of range: " + std::to_string(index)); + + return mPacketList[index].mRawData; +} + +uint32_t RtpDump::offsetAt(size_t index) const +{ + if (index >= mPacketList.size()) + throw std::out_of_range("Packet index out of range: " + std::to_string(index)); + + return mPacketList[index].mOffsetMs; +} + void RtpDump::add(const void* buffer, size_t len) { - RtpData data; - data.mData = malloc(len); - memcpy(data.mData, buffer, len); - data.mLength = len; + if (!buffer || len == 0) + return; - jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address); - jrtplib::RTPTime t(0); - jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast(data.mData), data.mLength, &addr, t, true); - data.mPacket = new jrtplib::RTPPacket(*raw); - //delete raw; - mPacketList.push_back(data); + uint32_t offsetMs = 0; + auto now = std::chrono::steady_clock::now(); + + if (!mRecording) { + mRecording = true; + mRecordStart = now; + + // Capture wall-clock start time + auto wallNow = std::chrono::system_clock::now(); + auto epoch = wallNow.time_since_epoch(); + auto sec = std::chrono::duration_cast(epoch); + auto usec = std::chrono::duration_cast(epoch - sec); + mStartSec = static_cast(sec.count()); + mStartUsec = static_cast(usec.count()); + } else { + auto elapsed = std::chrono::duration_cast(now - mRecordStart); + offsetMs = static_cast(elapsed.count()); + } + + add(buffer, len, offsetMs); +} + +void RtpDump::add(const void* buffer, size_t len, uint32_t offsetMs) +{ + if (!buffer || len == 0) + return; + + if (len > MAX_RTP_PACKET_SIZE) + throw std::runtime_error("Packet too large: " + std::to_string(len)); + + RtpData entry; + entry.mRawData.assign(static_cast(buffer), + static_cast(buffer) + len); + entry.mOffsetMs = offsetMs; + entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size()); + + mPacketList.push_back(std::move(entry)); } void RtpDump::flush() { - FILE* f = fopen(mFilename.c_str(), "wb"); - if (!f) - throw Exception(ERR_WAVFILE_FAILED); + if (mFilename.empty()) + throw std::runtime_error("No filename specified"); - PacketList::iterator packetIter = mPacketList.begin(); - for (;packetIter != mPacketList.end(); ++packetIter) - { - RtpData& data = *packetIter; - // Disabled for debugging only - //fwrite(&data.mLength, sizeof data.mLength, 1, f); - fwrite(data.mData, data.mLength, 1, f); + std::ofstream output(mFilename, std::ios::binary); + if (!output.is_open()) + throw std::runtime_error("Failed to open file for writing: " + mFilename); + + // --- 1. Text header --- + std::string textLine = std::string(RTPDUMP_SHEBANG) + " " + + ipToString(mSourceIp) + "/" + + std::to_string(mSourcePort) + "\n"; + output.write(textLine.data(), static_cast(textLine.size())); + + // --- 2. Binary file header (16 bytes) --- + uint32_t buf32; + uint16_t buf16; + + buf32 = htonl(mStartSec); + output.write(reinterpret_cast(&buf32), 4); + + buf32 = htonl(mStartUsec); + output.write(reinterpret_cast(&buf32), 4); + + buf32 = htonl(mSourceIp); + output.write(reinterpret_cast(&buf32), 4); + + buf16 = htons(mSourcePort); + output.write(reinterpret_cast(&buf16), 2); + + buf16 = 0; // padding + output.write(reinterpret_cast(&buf16), 2); + + // --- 3. Packet records --- + size_t written = 0; + + for (const auto& pkt : mPacketList) { + if (pkt.mRawData.empty()) + continue; + + uint16_t plen = static_cast(pkt.mRawData.size()); + uint16_t recLength = static_cast(plen + 8); + + buf16 = htons(recLength); + output.write(reinterpret_cast(&buf16), 2); + + buf16 = htons(plen); + output.write(reinterpret_cast(&buf16), 2); + + buf32 = htonl(pkt.mOffsetMs); + output.write(reinterpret_cast(&buf32), 4); + + output.write(reinterpret_cast(pkt.mRawData.data()), plen); + + written++; } - fclose(f); -} -#endif + if (!output.good()) + throw std::runtime_error("Failed to write rtpdump file: " + mFilename); + + ICELogInfo(<< "Wrote " << written << " packets to " << mFilename); +} + +void RtpDump::clear() +{ + mPacketList.clear(); + mLoaded = false; + mRecording = false; +} diff --git a/src/engine/helper/HL_Rtp.h b/src/engine/helper/HL_Rtp.h index cbcc4bef..a2c8798f 100644 --- a/src/engine/helper/HL_Rtp.h +++ b/src/engine/helper/HL_Rtp.h @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com) +/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -6,12 +6,14 @@ #ifndef __HL_RTP_H #define __HL_RTP_H -#if defined(USE_RTPDUMP) -# include "jrtplib/src/rtppacket.h" -#endif +#include "jrtplib/src/rtppacket.h" #include -#include +#include +#include +#include +#include +#include // Class to carry rtp/rtcp socket pair template @@ -27,7 +29,7 @@ struct RtpPair :mRtp(rtp), mRtcp(rtcp) {} - bool multiplexed() { return mRtp == mRtcp; } + bool multiplexed() const { return mRtp == mRtcp; } }; class RtpHelper @@ -35,7 +37,7 @@ class RtpHelper public: static bool isRtp(const void* buffer, size_t length); static int findPtype(const void* buffer, size_t length); - static int findPacketNo(const void* buffer, size_t length); + static int findPacketNo(const void *buffer, size_t length); static bool isRtpOrRtcp(const void* buffer, size_t length); static bool isRtcp(const void* buffer, size_t length); static unsigned findSsrc(const void* buffer, size_t length); @@ -43,31 +45,104 @@ public: static int findPayloadLength(const void* buffer, size_t length); }; -#if defined(USE_RTPDUMP) +/** + * @brief Standard rtpdump file format (rtptools / Wireshark compatible) + * + * Conforms to the rtpdump format defined by rtptools: + * https://formats.kaitai.io/rtpdump/ + * + * File layout: + * 1. Text header line: + * "#!rtpplay1.0 /\n" + * + * 2. Binary file header (RD_hdr_t, 16 bytes, all big-endian): + * uint32_t start_sec - recording start time, seconds since epoch + * uint32_t start_usec - recording start time, microseconds + * uint32_t source_ip - source IP address (network byte order) + * uint16_t source_port - source port + * uint16_t padding - always 0 + * + * 3. Packet records (repeated until EOF): + * Per-packet header (RD_packet_t, 8 bytes, all big-endian): + * uint16_t length - total record length (this 8-byte header + plen) + * uint16_t plen - RTP/RTCP payload length in bytes + * uint32_t offset - milliseconds since recording start + * Followed by plen bytes of RTP/RTCP packet data. + * + * Maximum single packet payload: 65535 bytes (enforced for safety). + */ class RtpDump { protected: struct RtpData { - jrtplib::RTPPacket* mPacket; - void* mData; - size_t mLength; + std::shared_ptr mPacket; + std::vector mRawData; + uint32_t mOffsetMs = 0; }; typedef std::vector PacketList; PacketList mPacketList; std::string mFilename; + bool mLoaded = false; + + // File header fields + uint32_t mSourceIp = 0; + uint16_t mSourcePort = 0; + uint32_t mStartSec = 0; + uint32_t mStartUsec = 0; + + // Auto-compute packet offsets during recording + bool mRecording = false; + std::chrono::steady_clock::time_point mRecordStart; + + std::shared_ptr parseRtpData(const uint8_t* data, size_t len); public: - RtpDump(const char* filename); + explicit RtpDump(const char* filename); ~RtpDump(); + /** Set source address for the file header (host byte order). */ + void setSource(uint32_t ip, uint16_t port); + uint32_t sourceIp() const { return mSourceIp; } + uint16_t sourcePort() const { return mSourcePort; } + + /** + * @brief Load packets from an rtpdump file + * @throws std::runtime_error on file/format error + */ void load(); + bool isLoaded() const { return mLoaded; } + size_t count() const; + + /** + * @brief Get parsed RTP packet at index + * @throws std::out_of_range if index is invalid + * @throws std::runtime_error if packet could not be parsed as RTP + */ jrtplib::RTPPacket& packetAt(size_t index); + + /** @brief Get raw packet bytes at index */ + const std::vector& rawDataAt(size_t index) const; + + /** @brief Get packet time offset in milliseconds */ + uint32_t offsetAt(size_t index) const; + + /** @brief Add a packet; time offset is auto-computed from first add() call */ void add(const void* data, size_t len); + + /** @brief Add a packet with an explicit millisecond offset */ + void add(const void* data, size_t len, uint32_t offsetMs); + + /** + * @brief Write all packets to file in rtpdump format + * @throws std::runtime_error on file error + */ void flush(); + + void clear(); + const std::string& filename() const { return mFilename; } }; -#endif #endif diff --git a/src/engine/helper/HL_Sync.cpp b/src/engine/helper/HL_Sync.cpp index 5a6f4592..517667a9 100644 --- a/src/engine/helper/HL_Sync.cpp +++ b/src/engine/helper/HL_Sync.cpp @@ -280,7 +280,8 @@ size_t TimerQueue::cancel(uint64_t id) { //! Cancels all timers // \return // The number of timers cancelled -size_t TimerQueue::cancelAll() { +size_t TimerQueue::cancelAll() +{ // Setting all "end" to 0 (for immediate execution) is ok, // since it maintains the heap integrity std::unique_lock lk(m_mtx); diff --git a/src/engine/media/MT_Codec.h b/src/engine/media/MT_Codec.h index f0da8002..6972c9f5 100644 --- a/src/engine/media/MT_Codec.h +++ b/src/engine/media/MT_Codec.h @@ -1,4 +1,4 @@ -/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com) +/* Copyright(C) 2007-2026 VoIP objects (voipobjects.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -6,10 +6,11 @@ #ifndef __MT_CODEC_H #define __MT_CODEC_H +#include +#include + #include "resiprocate/resip/stack/SdpContents.hxx" #include "../helper/HL_Types.h" -#include -#include "../helper/HL_Pointer.h" #include "../audio/Audio_Interface.h" namespace MT diff --git a/src/engine/media/MT_Dtmf.cpp b/src/engine/media/MT_Dtmf.cpp index 05d9be5a..5ba3838e 100644 --- a/src/engine/media/MT_Dtmf.cpp +++ b/src/engine/media/MT_Dtmf.cpp @@ -18,34 +18,34 @@ using namespace MT; void DtmfBuilder::buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output) { - assert(duration); - assert(output); - assert(tone); + assert(duration); + assert(output); + assert(tone); - unsigned char toneValue = 0; - if (tone >= '0' && tone <='9') - toneValue = tone - '0'; - else - if (tone >= 'A' && tone <='D' ) - toneValue = tone - 'A' + 12; - else - if (tone == '*') - toneValue = 10; - else - if (tone == '#') - toneValue = 11; + unsigned char toneValue = 0; + if (tone >= '0' && tone <='9') + toneValue = tone - '0'; + else + if (tone >= 'A' && tone <='D' ) + toneValue = tone - 'A' + 12; + else + if (tone == '*') + toneValue = 10; + else + if (tone == '#') + toneValue = 11; - char* packet = (char*)output; + char* packet = (char*)output; - packet[0] = toneValue; - packet[1] = 1 | (volume << 2); - if (endOfEvent) - packet[1] |= 128; - else - packet[1] &= 127; + packet[0] = toneValue; + packet[1] = 1 | (volume << 2); + if (endOfEvent) + packet[1] |= 128; + else + packet[1] &= 127; - unsigned short durationValue = htons(duration); - memcpy(packet + 2, &durationValue, 2); + unsigned short durationValue = htons(duration); + memcpy(packet + 2, &durationValue, 2); } #pragma region Inband DTMF support @@ -62,7 +62,7 @@ static bool sineTabInit = false; static double sinetab[1 << 11]; static inline double sine(unsigned int ptr) { - return sinetab[ptr >> (32-11)]; + return sinetab[ptr >> (32-11)]; } #define TWOPI (2.0 * 3.14159265358979323846) @@ -75,13 +75,13 @@ static inline double sine(unsigned int ptr) static double amptab[2] = { 8191.75, 16383.5 }; static inline int ifix(double x) { - return (x >= 0.0) ? (int) (x+0.5) : (int) (x-0.5); + return (x >= 0.0) ? (int) (x+0.5) : (int) (x-0.5); } // given frequency f, return corresponding phase increment static inline int phinc(double f) { - return ifix(TWO32 * f / (double) AUDIO_SAMPLERATE); + return ifix(TWO32 * f / (double) AUDIO_SAMPLERATE); } static char dtmfSymbols[16] = { @@ -105,32 +105,32 @@ static char dtmfSymbols[16] = { char PDTMFEncoder_DtmfChar(int i) { - - if (i < 16) - return dtmfSymbols[i]; - else - return 0; + + if (i < 16) + return dtmfSymbols[i]; + else + return 0; } // DTMF frequencies as per http://www.commlinx.com.au/DTMF_frequencies.htm static double dtmfFreqs[16][2] = { - { 941.0, 1336.0 }, // 0 - { 697.0, 1209.0 }, // 1 - { 697.0, 1336.0 }, // 2 - { 697.0, 1477.0 }, // 3 - { 770.0, 1209.0 }, // 4 - { 770.0, 1336.0 }, // 5 - { 770.0, 1477.0 }, // 6 - { 852.0, 1209.0 }, // 7 - { 852.0, 1336.0 }, // 8 - { 852.0, 1477.0 }, // 9 - { 697.0, 1633.0 }, // A - { 770.0, 1633.0 }, // B - { 852.0, 1633.0 }, // C - { 941.0, 1633.0 }, // D - { 941.0, 1209.0 }, // * - { 941.0, 1477.0 } // # + { 941.0, 1336.0 }, // 0 + { 697.0, 1209.0 }, // 1 + { 697.0, 1336.0 }, // 2 + { 697.0, 1477.0 }, // 3 + { 770.0, 1209.0 }, // 4 + { 770.0, 1336.0 }, // 5 + { 770.0, 1477.0 }, // 6 + { 852.0, 1209.0 }, // 7 + { 852.0, 1336.0 }, // 8 + { 852.0, 1477.0 }, // 9 + { 697.0, 1633.0 }, // A + { 770.0, 1633.0 }, // B + { 852.0, 1633.0 }, // C + { 941.0, 1633.0 }, // D + { 941.0, 1209.0 }, // * + { 941.0, 1477.0 } // # }; @@ -138,81 +138,81 @@ static Mutex LocalDtmfMutex; void PDTMFEncoder_MakeSineTable() { - Lock lock(LocalDtmfMutex); - - if (!sineTabInit) { - for (int k = 0; k < SINELEN; k++) { - double th = TWOPI * (double) k / (double) SINELEN; - double v = sin(th); - sinetab[k] = v; + Lock lock(LocalDtmfMutex); + + if (!sineTabInit) { + for (int k = 0; k < SINELEN; k++) { + double th = TWOPI * (double) k / (double) SINELEN; + double v = sin(th); + sinetab[k] = v; + } + sineTabInit = true; } - sineTabInit = true; - } } void PDTMFEncoder_AddTone(double f1, double f2, unsigned ms1, unsigned ms2, unsigned rate, short* result) { - int ak = 0; + int ak = 0; - PDTMFEncoder_MakeSineTable(); + PDTMFEncoder_MakeSineTable(); - int dataPtr = 0; + int dataPtr = 0; - double amp = amptab[ak]; - int phinc1 = phinc(f1), phinc2 = phinc(f2); - int ns1 = ms1 * (rate/1000); - int ns2 = ms2 * (rate/1000); - unsigned int ptr1 = 0, ptr2 = 0; - ptr1 += phinc1 * ns1; - ptr2 += phinc2 * ns1; + double amp = amptab[ak]; + int phinc1 = phinc(f1), phinc2 = phinc(f2); + int ns1 = ms1 * (rate/1000); + int ns2 = ms2 * (rate/1000); + unsigned int ptr1 = 0, ptr2 = 0; + ptr1 += phinc1 * ns1; + ptr2 += phinc2 * ns1; - for (int n = ns1; n < ns2; n++) { + for (int n = ns1; n < ns2; n++) { - double val = amp * (sine(ptr1) + sine(ptr2)); - int ival = ifix(val); - if (ival < -32768) - ival = -32768; - else if (val > 32767) - ival = 32767; + double val = amp * (sine(ptr1) + sine(ptr2)); + int ival = ifix(val); + if (ival < -32768) + ival = -32768; + else if (val > 32767) + ival = 32767; - result[dataPtr++] = ival / 2; + result[dataPtr++] = ival / 2; - ptr1 += phinc1; - ptr2 += phinc2; - } + ptr1 += phinc1; + ptr2 += phinc2; + } } void PDTMFEncoder_AddTone(char _digit, unsigned startTime, unsigned finishTime, unsigned rate, short* result) { - char digit = (char)toupper(_digit); - if ('0' <= digit && digit <= '9') - digit = digit - '0'; + char digit = (char)toupper(_digit); + if ('0' <= digit && digit <= '9') + digit = digit - '0'; - else if ('A' <= digit && digit <= 'D') - digit = digit + 10 - 'A'; + else if ('A' <= digit && digit <= 'D') + digit = digit + 10 - 'A'; - else if (digit == '*') - digit = 14; + else if (digit == '*') + digit = 14; - else if (digit == '#') - digit = 15; + else if (digit == '#') + digit = 15; - else - return ; + else + return ; - PDTMFEncoder_AddTone(dtmfFreqs[(int)digit][0], dtmfFreqs[(int)digit][1], startTime, finishTime, rate, result); + PDTMFEncoder_AddTone(dtmfFreqs[(int)digit][0], dtmfFreqs[(int)digit][1], startTime, finishTime, rate, result); } #pragma endregion void DtmfBuilder::buildInband(int tone, int startTime, int finishTime, int rate, short* buf) { - PDTMFEncoder_AddTone(tone, startTime, finishTime, rate, buf); + PDTMFEncoder_AddTone(tone, startTime, finishTime, rate, buf); } #pragma region DtmfContext DtmfContext::DtmfContext() -:mType(Dtmf_Rfc2833) + :mType(Dtmf_Rfc2833) { } @@ -222,112 +222,112 @@ DtmfContext::~DtmfContext() void DtmfContext::setType(Type t) { - mType = t; + mType = t; } DtmfContext::Type DtmfContext::type() { - return mType; + return mType; } void DtmfContext::startTone(int tone, int volume) { - Lock l(mGuard); - - // Stop current tone if needed - if (mQueue.size()) - stopTone(); - mQueue.push_back(Dtmf(tone, volume, 0)); + Lock l(mGuard); + + // Stop current tone if needed + if (mQueue.size()) + stopTone(); + mQueue.push_back(Dtmf(tone, volume, 0)); } void DtmfContext::stopTone() { - Lock l(mGuard); - - // Switch to "emit 3 terminating packets" mode - if (mQueue.size()) - { - switch (mType) - { - case Dtmf_Rfc2833: - mQueue.front().mStopped = true; - mQueue.erase(mQueue.begin()); - break; + Lock l(mGuard); - case Dtmf_Inband: - if (!mQueue.front().mFinishCount) - mQueue.front().mFinishCount = MT_DTMF_END_PACKETS; - break; + // Switch to "emit 3 terminating packets" mode + if (mQueue.size()) + { + switch (mType) + { + case Dtmf_Rfc2833: + mQueue.front().mStopped = true; + mQueue.erase(mQueue.begin()); + break; + + case Dtmf_Inband: + if (!mQueue.front().mFinishCount) + mQueue.front().mFinishCount = MT_DTMF_END_PACKETS; + break; + } } - } } void DtmfContext::queueTone(int tone, int volume, int duration) { - Lock l(mGuard); - mQueue.push_back(Dtmf(tone, volume, duration)); + Lock l(mGuard); + mQueue.push_back(Dtmf(tone, volume, duration)); } void DtmfContext::clearAllTones() { - Lock l(mGuard); - mQueue.clear(); + Lock l(mGuard); + mQueue.clear(); } bool DtmfContext::getInband(int milliseconds, int rate, ByteBuffer& output) { - Lock l(mGuard); + Lock l(mGuard); - if (!mQueue.size() || mType != Dtmf_Inband) - return false; + if (!mQueue.size() || mType != Dtmf_Inband) + return false; - // - Dtmf& d = mQueue.front(); - - output.resize(milliseconds * rate / 1000 * 2); - DtmfBuilder::buildInband(d.mTone, d.mCurrentTime, d.mCurrentTime + milliseconds, rate, (short*)output.mutableData()); - d.mCurrentTime += milliseconds; - return true; + // + Dtmf& d = mQueue.front(); + + output.resize((uint64_t)milliseconds * rate / 1000 * 2); + DtmfBuilder::buildInband(d.mTone, d.mCurrentTime, d.mCurrentTime + milliseconds, rate, (short*)output.mutableData()); + d.mCurrentTime += milliseconds; + return true; } bool DtmfContext::getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket) { - Lock l(mGuard); - if (!mQueue.size() || mType != Dtmf_Rfc2833) - return false; + Lock l(mGuard); + if (!mQueue.size() || mType != Dtmf_Rfc2833) + return false; - Dtmf& d = mQueue.front(); - // See if tone has enough duration to produce another packet - if (d.mDuration > 0) - { - // Emit rfc2833 packet - output.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); - d.mDuration -= milliseconds; - if(d.mDuration <= 0) - d.mStopped = true; - } - else - if (!d.mStopped) - { - output.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); - } - else - output.clear(); - - if (d.mStopped) - { - stopPacket.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, true, stopPacket.mutableData()); - } - else - stopPacket.clear(); - - if (d.mStopped) - mQueue.erase(mQueue.begin()); + Dtmf& d = mQueue.front(); + // See if tone has enough duration to produce another packet + if (d.mDuration > 0) + { + // Emit rfc2833 packet + output.resize(4); + DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); + d.mDuration -= milliseconds; + if(d.mDuration <= 0) + d.mStopped = true; + } + else + if (!d.mStopped) + { + output.resize(4); + DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); + } + else + output.clear(); - return true; + if (d.mStopped) + { + stopPacket.resize(4); + DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, true, stopPacket.mutableData()); + } + else + stopPacket.clear(); + + if (d.mStopped) + mQueue.erase(mQueue.begin()); + + return true; } typedef struct @@ -376,31 +376,31 @@ int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int is int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max); DTMFDetector::DTMFDetector() -:mState(NULL) + :mState(NULL) { - mState = malloc(sizeof(dtmf_detect_state_t)); + mState = malloc(sizeof(dtmf_detect_state_t)); - memset(mState, 0, sizeof(dtmf_detect_state_t)); - zap_dtmf_detect_init((dtmf_detect_state_t*)mState); + memset(mState, 0, sizeof(dtmf_detect_state_t)); + zap_dtmf_detect_init((dtmf_detect_state_t*)mState); } DTMFDetector::~DTMFDetector() { - if (mState) - free(mState); + if (mState) + free(mState); } std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size) { - char buf[16]; buf[0] = 0; - if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0)) - zap_dtmf_get((dtmf_detect_state_t*)mState, buf, 15); - return buf; + char buf[16]; buf[0] = 0; + if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0)) + zap_dtmf_get((dtmf_detect_state_t*)mState, buf, 15); + return buf; } void DTMFDetector::resetState() { - zap_dtmf_detect_init((dtmf_detect_state_t*)mState); + zap_dtmf_detect_init((dtmf_detect_state_t*)mState); } #ifndef TRUE @@ -448,12 +448,12 @@ static tone_detection_descriptor_t fax_detect; static tone_detection_descriptor_t fax_detect_2nd; static float dtmf_row[] = -{ - 697.0, 770.0, 852.0, 941.0 + { + 697.0, 770.0, 852.0, 941.0 }; static float dtmf_col[] = -{ - 1209.0, 1336.0, 1477.0, 1633.0 + { + 1209.0, 1336.0, 1477.0, 1633.0 }; static float fax_freq = 1100.0; @@ -461,10 +461,10 @@ static float fax_freq = 1100.0; static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; static void goertzel_init(goertzel_state_t *s, - tone_detection_descriptor_t *t) + tone_detection_descriptor_t *t) { s->v2 = - s->v3 = 0.0; + s->v3 = 0.0; s->fac = t->fac; } /*- End of function --------------------------------------------------------*/ @@ -497,50 +497,50 @@ static inline void _dtmf_goertzel_update(goertzel_state_t *s, //s->v3 = s->fac*s->v2 - v1 + x[0]; __asm__ __volatile__ ( - " femms;\n" + " femms;\n" - " movq 16(%%edx),%%mm2;\n" - " movq 24(%%edx),%%mm3;\n" - " movq 32(%%edx),%%mm4;\n" - " movq 40(%%edx),%%mm5;\n" - " movq 48(%%edx),%%mm6;\n" - " movq 56(%%edx),%%mm7;\n" + " movq 16(%%edx),%%mm2;\n" + " movq 24(%%edx),%%mm3;\n" + " movq 32(%%edx),%%mm4;\n" + " movq 40(%%edx),%%mm5;\n" + " movq 48(%%edx),%%mm6;\n" + " movq 56(%%edx),%%mm7;\n" - " jmp 1f;\n" - " .align 32;\n" + " jmp 1f;\n" + " .align 32;\n" - " 1: ;\n" - " prefetch (%%eax);\n" - " movq %%mm3,%%mm1;\n" - " movq %%mm2,%%mm0;\n" - " movq %%mm5,%%mm3;\n" - " movq %%mm4,%%mm2;\n" + " 1: ;\n" + " prefetch (%%eax);\n" + " movq %%mm3,%%mm1;\n" + " movq %%mm2,%%mm0;\n" + " movq %%mm5,%%mm3;\n" + " movq %%mm4,%%mm2;\n" - " pfmul %%mm7,%%mm5;\n" - " pfmul %%mm6,%%mm4;\n" - " pfsub %%mm1,%%mm5;\n" - " pfsub %%mm0,%%mm4;\n" + " pfmul %%mm7,%%mm5;\n" + " pfmul %%mm6,%%mm4;\n" + " pfsub %%mm1,%%mm5;\n" + " pfsub %%mm0,%%mm4;\n" - " movq (%%eax),%%mm0;\n" - " movq %%mm0,%%mm1;\n" - " punpckldq %%mm0,%%mm1;\n" - " add $4,%%eax;\n" - " pfadd %%mm1,%%mm5;\n" - " pfadd %%mm1,%%mm4;\n" + " movq (%%eax),%%mm0;\n" + " movq %%mm0,%%mm1;\n" + " punpckldq %%mm0,%%mm1;\n" + " add $4,%%eax;\n" + " pfadd %%mm1,%%mm5;\n" + " pfadd %%mm1,%%mm4;\n" - " dec %%ecx;\n" + " dec %%ecx;\n" - " jnz 1b;\n" + " jnz 1b;\n" - " movq %%mm2,16(%%edx);\n" - " movq %%mm3,24(%%edx);\n" - " movq %%mm4,32(%%edx);\n" - " movq %%mm5,40(%%edx);\n" + " movq %%mm2,16(%%edx);\n" + " movq %%mm3,24(%%edx);\n" + " movq %%mm4,32(%%edx);\n" + " movq %%mm5,40(%%edx);\n" - " femms;\n" - : - : "c" (samples), "a" (x), "d" (vv) - : "memory", "eax", "ecx"); + " femms;\n" + : + : "c" (samples), "a" (x), "d" (vv) + : "memory", "eax", "ecx"); s[0].v2 = vv[4]; s[1].v2 = vv[5]; @@ -555,8 +555,8 @@ static inline void _dtmf_goertzel_update(goertzel_state_t *s, /*- End of function --------------------------------------------------------*/ void zap_goertzel_update(goertzel_state_t *s, - int16_t x[], - int samples) + int16_t x[], + int samples) { int i; float v1; @@ -582,7 +582,7 @@ void zap_dtmf_detect_init (dtmf_detect_state_t *s) float theta; s->hit1 = - s->hit2 = 0; + s->hit2 = 0; for (i = 0; i < 4; i++) { @@ -591,19 +591,19 @@ void zap_dtmf_detect_init (dtmf_detect_state_t *s) theta = float(2.0*M_PI*(dtmf_col[i]/SAMPLE_RATE)); dtmf_detect_col[i].fac = float(2.0*cos(theta)); - + theta = float(2.0*M_PI*(dtmf_row[i]*2.0/SAMPLE_RATE)); dtmf_detect_row_2nd[i].fac = float(2.0*cos(theta)); theta = float(2.0*M_PI*(dtmf_col[i]*2.0/SAMPLE_RATE)); dtmf_detect_col_2nd[i].fac = float(2.0*cos(theta)); - - goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); - goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); - goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); - goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); -s->energy = 0.0; + goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); + + s->energy = 0.0; } /* Same for the fax dector */ @@ -625,9 +625,9 @@ s->energy = 0.0; /*- End of function --------------------------------------------------------*/ int zap_dtmf_detect (dtmf_detect_state_t *s, - int16_t amp[], - int samples, - int isradio) + int16_t amp[], + int samples, + int isradio) { float row_energy[4]; @@ -665,35 +665,35 @@ int zap_dtmf_detect (dtmf_detect_state_t *s, for (j = sample; j < limit; j++) { famp = amp[j]; - - s->energy += famp*famp; - + + s->energy += famp*famp; + /* With GCC 2.95, the following unrolled code seems to take about 35% (rough estimate) as long as a neat little 0-3 loop */ v1 = s->row_out[0].v2; s->row_out[0].v2 = s->row_out[0].v3; s->row_out[0].v3 = s->row_out[0].fac*s->row_out[0].v2 - v1 + famp; - + v1 = s->col_out[0].v2; s->col_out[0].v2 = s->col_out[0].v3; s->col_out[0].v3 = s->col_out[0].fac*s->col_out[0].v2 - v1 + famp; - + v1 = s->row_out[1].v2; s->row_out[1].v2 = s->row_out[1].v3; s->row_out[1].v3 = s->row_out[1].fac*s->row_out[1].v2 - v1 + famp; - + v1 = s->col_out[1].v2; s->col_out[1].v2 = s->col_out[1].v3; s->col_out[1].v3 = s->col_out[1].fac*s->col_out[1].v2 - v1 + famp; - + v1 = s->row_out[2].v2; s->row_out[2].v2 = s->row_out[2].v3; s->row_out[2].v3 = s->row_out[2].fac*s->row_out[2].v2 - v1 + famp; - + v1 = s->col_out[2].v2; s->col_out[2].v2 = s->col_out[2].v3; s->col_out[2].v3 = s->col_out[2].fac*s->col_out[2].v2 - v1 + famp; - + v1 = s->row_out[3].v2; s->row_out[3].v2 = s->row_out[3].v3; s->row_out[3].v3 = s->row_out[3].fac*s->row_out[3].v2 - v1 + famp; @@ -705,36 +705,36 @@ int zap_dtmf_detect (dtmf_detect_state_t *s, v1 = s->col_out2nd[0].v2; s->col_out2nd[0].v2 = s->col_out2nd[0].v3; s->col_out2nd[0].v3 = s->col_out2nd[0].fac*s->col_out2nd[0].v2 - v1 + famp; - + v1 = s->row_out2nd[0].v2; s->row_out2nd[0].v2 = s->row_out2nd[0].v3; s->row_out2nd[0].v3 = s->row_out2nd[0].fac*s->row_out2nd[0].v2 - v1 + famp; - + v1 = s->col_out2nd[1].v2; s->col_out2nd[1].v2 = s->col_out2nd[1].v3; s->col_out2nd[1].v3 = s->col_out2nd[1].fac*s->col_out2nd[1].v2 - v1 + famp; - + v1 = s->row_out2nd[1].v2; s->row_out2nd[1].v2 = s->row_out2nd[1].v3; s->row_out2nd[1].v3 = s->row_out2nd[1].fac*s->row_out2nd[1].v2 - v1 + famp; - + v1 = s->col_out2nd[2].v2; s->col_out2nd[2].v2 = s->col_out2nd[2].v3; s->col_out2nd[2].v3 = s->col_out2nd[2].fac*s->col_out2nd[2].v2 - v1 + famp; - + v1 = s->row_out2nd[2].v2; s->row_out2nd[2].v2 = s->row_out2nd[2].v3; s->row_out2nd[2].v3 = s->row_out2nd[2].fac*s->row_out2nd[2].v2 - v1 + famp; - + v1 = s->col_out2nd[3].v2; s->col_out2nd[3].v2 = s->col_out2nd[3].v3; s->col_out2nd[3].v3 = s->col_out2nd[3].fac*s->col_out2nd[3].v2 - v1 + famp; - + v1 = s->row_out2nd[3].v2; s->row_out2nd[3].v2 = s->row_out2nd[3].v3; s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp; -/* Update fax tone */ + /* Update fax tone */ v1 = s->fax_tone.v2; s->fax_tone.v2 = s->fax_tone.v3; s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp; @@ -748,28 +748,28 @@ int zap_dtmf_detect (dtmf_detect_state_t *s, if (s->current_sample < 102) continue; -/* Detect the fax energy, too */ -fax_energy = zap_goertzel_result(&s->fax_tone); + /* Detect the fax energy, too */ + fax_energy = zap_goertzel_result(&s->fax_tone); /* We are at the end of a DTMF detection block */ /* Find the peak row and the peak column */ row_energy[0] = zap_goertzel_result (&s->row_out[0]); col_energy[0] = zap_goertzel_result (&s->col_out[0]); -for (best_row = best_col = 0, i = 1; i < 4; i++) -{ - row_energy[i] = zap_goertzel_result (&s->row_out[i]); + for (best_row = best_col = 0, i = 1; i < 4; i++) + { + row_energy[i] = zap_goertzel_result (&s->row_out[i]); if (row_energy[i] > row_energy[best_row]) best_row = i; - col_energy[i] = zap_goertzel_result (&s->col_out[i]); + col_energy[i] = zap_goertzel_result (&s->col_out[i]); if (col_energy[i] > col_energy[best_col]) best_col = i; - } + } hit = 0; /* Basic signal level test and the twist test */ if (row_energy[best_row] >= DTMF_THRESHOLD - && - col_energy[best_col] >= DTMF_THRESHOLD + && + col_energy[best_col] >= DTMF_THRESHOLD && col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST && @@ -787,8 +787,8 @@ for (best_row = best_col = 0, i = 1; i < 4; i++) } /* ... and second harmonic test */ if (i >= 4 - && -(row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy + && + (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy && zap_goertzel_result (&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] && @@ -804,7 +804,7 @@ for (best_row = best_col = 0, i = 1; i < 4; i++) to a digit. */ if (hit == s->hit3 && s->hit3 != s->hit2) { - s->mhit = hit; + s->mhit = hit; s->digit_hits[(best_row << 2) + best_col]++; s->detected_digits++; if (s->current_digits < MAX_DTMF_DIGITS) @@ -819,60 +819,60 @@ for (best_row = best_col = 0, i = 1; i < 4; i++) } } } -if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy > s->energy * 21.0)) { -fax_energy_2nd = zap_goertzel_result(&s->fax_tone2nd); -if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) { + if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy > s->energy * 21.0)) { + fax_energy_2nd = zap_goertzel_result(&s->fax_tone2nd); + if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) { #if 0 printf("Fax energy/Second Harmonic: %f/%f\n", fax_energy, fax_energy_2nd); #endif -/* XXX Probably need better checking than just this the energy XXX */ -hit = 'f'; -s->fax_hits++; -} /* Don't reset fax hits counter */ -} else { -if (s->fax_hits > 5) { - s->mhit = 'f'; - s->detected_digits++; - if (s->current_digits < MAX_DTMF_DIGITS) - { - s->digits[s->current_digits++] = hit; - s->digits[s->current_digits] = '\0'; - } - else - { - s->lost_digits++; - } -} -s->fax_hits = 0; -} + /* XXX Probably need better checking than just this the energy XXX */ + hit = 'f'; + s->fax_hits++; + } /* Don't reset fax hits counter */ + } else { + if (s->fax_hits > 5) { + s->mhit = 'f'; + s->detected_digits++; + if (s->current_digits < MAX_DTMF_DIGITS) + { + s->digits[s->current_digits++] = hit; + s->digits[s->current_digits] = '\0'; + } + else + { + s->lost_digits++; + } + } + s->fax_hits = 0; + } s->hit1 = s->hit2; s->hit2 = s->hit3; s->hit3 = hit; /* Reinitialise the detector for the next block */ for (i = 0; i < 4; i++) { - goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); - goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); - goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); + goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); } - goertzel_init (&s->fax_tone, &fax_detect); - goertzel_init (&s->fax_tone2nd, &fax_detect_2nd); -s->energy = 0.0; + goertzel_init (&s->fax_tone, &fax_detect); + goertzel_init (&s->fax_tone2nd, &fax_detect_2nd); + s->energy = 0.0; s->current_sample = 0; } if ((!s->mhit) || (s->mhit != hit)) { -s->mhit = 0; -return(0); + s->mhit = 0; + return(0); } return (hit); } /*- End of function --------------------------------------------------------*/ int zap_dtmf_get (dtmf_detect_state_t *s, - char *buf, - int max) + char *buf, + int max) { if (max > s->current_digits) max = s->current_digits; diff --git a/src/engine/media/MT_SingleAudioStream.cpp b/src/engine/media/MT_SingleAudioStream.cpp index 0baa426e..3aa5702d 100644 --- a/src/engine/media/MT_SingleAudioStream.cpp +++ b/src/engine/media/MT_SingleAudioStream.cpp @@ -13,14 +13,12 @@ using namespace MT; SingleAudioStream::SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat) - :mReceiver(codecSettings, stat), mDtmfReceiver(stat) -{ -} + :mReceiver(codecSettings, stat), + mDtmfReceiver(stat) +{} SingleAudioStream::~SingleAudioStream() -{ - -} +{} void SingleAudioStream::process(const std::shared_ptr& packet) { diff --git a/src/engine/media/MT_SingleAudioStream.h b/src/engine/media/MT_SingleAudioStream.h index c62822b9..7c468819 100644 --- a/src/engine/media/MT_SingleAudioStream.h +++ b/src/engine/media/MT_SingleAudioStream.h @@ -13,19 +13,19 @@ #include "MT_AudioReceiver.h" namespace MT { - class SingleAudioStream - { - public: +class SingleAudioStream +{ +public: SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat); ~SingleAudioStream(); void process(const std::shared_ptr& packet); void copyPcmTo(Audio::DataWindow& output, int needed); - protected: +protected: DtmfReceiver mDtmfReceiver; AudioReceiver mReceiver; - }; - - typedef std::map AudioStreamMap; +}; + +typedef std::map AudioStreamMap; } #endif diff --git a/test/rtp_decode/CMakeLists.txt b/test/rtp_decode/CMakeLists.txt new file mode 100644 index 00000000..4258de1e --- /dev/null +++ b/test/rtp_decode/CMakeLists.txt @@ -0,0 +1,6 @@ +set (CMAKE_CXX_STANDARD 20) +set (CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(rtp_decode main.cpp) +add_subdirectory(../../src build_rtphone) +target_link_libraries(rtp_decode PRIVATE rtphone) diff --git a/test/rtp_decode/main.cpp b/test/rtp_decode/main.cpp new file mode 100644 index 00000000..220bda0e --- /dev/null +++ b/test/rtp_decode/main.cpp @@ -0,0 +1,284 @@ +/* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// rtp_decode — read an rtpdump file, decode RTP with a given codec, write WAV. +// +// Usage: +// rtp_decode --codec [--pt ] [--rate ] [--channels ] + +#include "helper/HL_Rtp.h" +#include "media/MT_CodecList.h" +#include "media/MT_Codec.h" +#include "audio/Audio_WavFile.h" + +#include +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// CLI helpers +// --------------------------------------------------------------------------- + +static void usage(const char* progname) +{ + fprintf(stderr, + "Usage: %s --codec [--pt ] [--rate ] [--channels ]\n" + "\n" + "Codecs: pcmu pcma g722 g729 opus gsm gsmhr gsmefr amrnb amrwb evs ilbc20 ilbc30 isac16 isac32\n" + "\n" + "Options:\n" + " --codec Codec name (required)\n" + " --pt Override RTP payload type\n" + " --rate Sample rate hint for Opus (default 48000)\n" + " --channels Channel count hint for Opus (default 2)\n", + progname); +} + +static const char* getOption(int argc, char* argv[], const char* name) +{ + for (int i = 1; i < argc - 1; ++i) { + if (strcmp(argv[i], name) == 0) + return argv[i + 1]; + } + return nullptr; +} + +// --------------------------------------------------------------------------- +// Default payload types for codecs without a fixed standard PT +// --------------------------------------------------------------------------- +struct CodecDefaults +{ + const char* name; + int defaultPt; // -1 = must be specified via --pt + bool needsPt; // true if --pt is required when no default exists +}; + +static const CodecDefaults kCodecTable[] = { + { "pcmu", 0, false }, + { "pcma", 8, false }, + { "g722", 9, false }, + { "g729", 18, false }, + { "gsm", 3, false }, + { "opus", 106, false }, + { "amrnb", -1, true }, + { "amrwb", -1, true }, + { "gsmhr", -1, true }, + { "gsmefr", 126, false }, + { "evs", 127, false }, + { "ilbc20", -1, true }, + { "ilbc30", -1, true }, + { "isac16", -1, true }, + { "isac32", -1, true }, +}; + +static const CodecDefaults* findCodecDefaults(const std::string& name) +{ + for (auto& c : kCodecTable) + if (name == c.name) + return &c; + return nullptr; +} + +// --------------------------------------------------------------------------- +// Build CodecList::Settings for the requested codec +// --------------------------------------------------------------------------- +static MT::CodecList::Settings buildSettings(const std::string& codecName, int pt, + int opusRate, int opusChannels) +{ + MT::CodecList::Settings s; + + if (codecName == "opus") { + s.mOpusSpec.push_back(MT::CodecList::Settings::OpusSpec(pt, opusRate, opusChannels)); + } else if (codecName == "gsm") { + s.mGsmFrPayloadType = pt; + } else if (codecName == "gsmhr") { + s.mGsmHrPayloadType = pt; + } else if (codecName == "gsmefr") { + s.mGsmEfrPayloadType = pt; + } else if (codecName == "amrnb") { + s.mAmrNbOctetPayloadType.insert(pt); + } else if (codecName == "amrwb") { + s.mAmrWbOctetPayloadType.insert(pt); + } else if (codecName == "evs") { + MT::CodecList::Settings::EvsSpec ev; + ev.mPayloadType = pt; + s.mEvsSpec.push_back(ev); + } else if (codecName == "ilbc20") { + s.mIlbc20PayloadType = pt; + } else if (codecName == "ilbc30") { + s.mIlbc30PayloadType = pt; + } else if (codecName == "isac16") { + s.mIsac16KPayloadType = pt; + } else if (codecName == "isac32") { + s.mIsac32KPayloadType = pt; + } + // pcmu, pcma, g722, g729 — fixed PT, auto-registered by CodecList::init() + + return s; +} + +// --------------------------------------------------------------------------- +// main +// --------------------------------------------------------------------------- +int main(int argc, char* argv[]) +{ + if (argc < 4) { + usage(argv[0]); + return 1; + } + + const char* inputPath = argv[1]; + const char* outputPath = argv[2]; + + const char* codecArg = getOption(argc, argv, "--codec"); + if (!codecArg) { + fprintf(stderr, "Error: --codec is required\n\n"); + usage(argv[0]); + return 1; + } + std::string codecName = codecArg; + + const auto* defaults = findCodecDefaults(codecName); + if (!defaults) { + fprintf(stderr, "Error: unknown codec '%s'\n\n", codecArg); + usage(argv[0]); + return 1; + } + + // Resolve payload type + int pt = defaults->defaultPt; + const char* ptArg = getOption(argc, argv, "--pt"); + if (ptArg) { + pt = atoi(ptArg); + } else if (defaults->needsPt) { + fprintf(stderr, "Error: --pt is required for codec '%s'\n\n", codecArg); + usage(argv[0]); + return 1; + } + + int opusRate = 48000; + int opusChannels = 2; + const char* rateArg = getOption(argc, argv, "--rate"); + if (rateArg) + opusRate = atoi(rateArg); + const char* chArg = getOption(argc, argv, "--channels"); + if (chArg) + opusChannels = atoi(chArg); + + // ----------------------------------------------------------------------- + // 1. Load rtpdump + // ----------------------------------------------------------------------- + RtpDump dump(inputPath); + try { + dump.load(); + } catch (const std::exception& e) { + fprintf(stderr, "Error loading rtpdump '%s': %s\n", inputPath, e.what()); + return 1; + } + + if (dump.count() == 0) { + fprintf(stderr, "No packets in '%s'\n", inputPath); + return 1; + } + fprintf(stderr, "Loaded %zu packets from '%s'\n", dump.count(), inputPath); + + // ----------------------------------------------------------------------- + // 2. Create codec + // ----------------------------------------------------------------------- + auto settings = buildSettings(codecName, pt, opusRate, opusChannels); + MT::CodecList codecList(settings); + MT::PCodec codec = codecList.createCodecByPayloadType(pt); + if (!codec) { + fprintf(stderr, "Error: could not create codec for payload type %d\n", pt); + return 1; + } + + auto codecInfo = codec->info(); + fprintf(stderr, "Codec: %s samplerate=%d channels=%d pcmLength=%d frameTime=%dms\n", + codecInfo.mName.c_str(), codecInfo.mSamplerate, codecInfo.mChannels, + codecInfo.mPcmLength, codecInfo.mFrameTime); + + // ----------------------------------------------------------------------- + // 3. Open WAV writer + // ----------------------------------------------------------------------- + Audio::WavFileWriter writer; + if (!writer.open(outputPath, codecInfo.mSamplerate, codecInfo.mChannels)) { + fprintf(stderr, "Error: could not open WAV file '%s' for writing\n", outputPath); + return 1; + } + + // ----------------------------------------------------------------------- + // 4. Decode loop + // ----------------------------------------------------------------------- + std::vector pcmBuffer(65536); + size_t totalDecodedBytes = 0; + size_t packetsDecoded = 0; + size_t packetsSkipped = 0; + + for (size_t i = 0; i < dump.count(); ++i) { + const auto& rawData = dump.rawDataAt(i); + + // Verify it's actually RTP + if (!RtpHelper::isRtp(rawData.data(), rawData.size())) { + ++packetsSkipped; + continue; + } + + // Parse RTP to get payload + jrtplib::RTPPacket& rtpPacket = dump.packetAt(i); + + // Check payload type matches what we expect + int pktPt = rtpPacket.GetPayloadType(); + if (pktPt != pt) { + ++packetsSkipped; + continue; + } + + uint8_t* payloadData = rtpPacket.GetPayloadData(); + size_t payloadLen = rtpPacket.GetPayloadLength(); + + if (!payloadData || payloadLen == 0) { + ++packetsSkipped; + continue; + } + + std::span input(payloadData, payloadLen); + std::span output(pcmBuffer.data(), pcmBuffer.size()); + + try { + auto result = codec->decode(input, output); + if (result.mDecoded > 0) { + writer.write(pcmBuffer.data(), result.mDecoded); + totalDecodedBytes += result.mDecoded; + ++packetsDecoded; + } + } catch (const std::exception& e) { + fprintf(stderr, "Warning: decode error at packet %zu: %s\n", i, e.what()); + ++packetsSkipped; + } + } + + // ----------------------------------------------------------------------- + // 5. Close WAV and print summary + // ----------------------------------------------------------------------- + writer.close(); + + size_t totalSamples = totalDecodedBytes / (sizeof(int16_t) * codecInfo.mChannels); + double durationSec = (codecInfo.mSamplerate > 0) + ? static_cast(totalSamples) / codecInfo.mSamplerate + : 0.0; + + fprintf(stderr, "\nDone.\n"); + fprintf(stderr, " Packets decoded: %zu\n", packetsDecoded); + fprintf(stderr, " Packets skipped: %zu\n", packetsSkipped); + fprintf(stderr, " Decoded PCM: %zu bytes\n", totalDecodedBytes); + fprintf(stderr, " Duration: %.3f seconds\n", durationSec); + fprintf(stderr, " Output: %s\n", outputPath); + + return 0; +} From b5fe9e59c4408661ffbc254efb2b2bb43084251d Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Tue, 24 Feb 2026 10:47:38 +0300 Subject: [PATCH 5/9] - fix AMR-WB decoder --- src/engine/media/MT_AmrCodec.cpp | 31 +++++++++++++-------------- src/engine/media/MT_AmrCodec.h | 4 ++-- src/engine/media/MT_AudioReceiver.cpp | 5 +++-- src/engine/media/MT_Codec.h | 14 ++++++------ 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/engine/media/MT_AmrCodec.cpp b/src/engine/media/MT_AmrCodec.cpp index 3a561df1..3c3fff27 100644 --- a/src/engine/media/MT_AmrCodec.cpp +++ b/src/engine/media/MT_AmrCodec.cpp @@ -535,16 +535,16 @@ Codec::EncodeResult AmrWbCodec::encode(std::span input, std::span #define L_FRAME 160 #define AMR_BITRATE_DTX 15 -int AmrWbCodec::decodeIuup(std::span input, std::span output) +Codec::DecodeResult AmrWbCodec::decodeIuup(std::span input, std::span output) { IuUP::Frame frame; if (!IuUP::parse2(input.data(), input.size(), frame)) - return 0; + return {.mDecoded = 0}; if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) { ICELogInfo(<< "CRC check failed."); - return 0; + return {.mDecoded = 0}; } // Reserve space @@ -559,15 +559,15 @@ int AmrWbCodec::decodeIuup(std::span input, std::span ou frameType = ftIndex; if (frameType == 0xFF) - return 0; + return {.mDecoded = 0, .mIsCng = true}; dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0); - return pcmLength(); + return {.mDecoded = (size_t)pcmLength()}; } -int AmrWbCodec::decodePlain(std::span input, std::span output) +Codec::DecodeResult AmrWbCodec::decodePlain(std::span input, std::span output) { AmrPayloadInfo info; info.mCurrentTimestamp = mCurrentDecoderTimestamp; @@ -586,7 +586,7 @@ int AmrWbCodec::decodePlain(std::span input, std::span o { GAmrWbStatistics.mNonParsed++; ICELogDebug(<< "Failed to decode AMR payload"); - return 0; + return {.mDecoded = 0}; } // Save current timestamp mCurrentDecoderTimestamp = info.mCurrentTimestamp; @@ -595,22 +595,22 @@ int AmrWbCodec::decodePlain(std::span input, std::span o if (ap.mDiscardPacket) { GAmrWbStatistics.mDiscarded++; - return 0; + return {.mDecoded = 0}; } // Find the required output capacity size_t capacity = 0; for (AmrFrame& frame: ap.mFrames) - capacity += frame.mMode == 0xFF /* CNG */ ? pcmLength() * 8 : pcmLength(); + capacity += frame.mMode == 0xFF /* CNG */ ? pcmLength() : pcmLength(); if (output.size() < capacity) - return 0; + return {.mDecoded = 0}; short* dataOut = (short*)output.data(); size_t dataOutSizeInBytes = 0; for (AmrFrame& frame: ap.mFrames) { - size_t frameOutputSize = frame.mMode == 0xFF ? pcmLength() * 8 : pcmLength(); + size_t frameOutputSize = frame.mMode == 0xFF ? pcmLength() : pcmLength(); memset(dataOut, 0, frameOutputSize); if (frame.mData) @@ -624,17 +624,16 @@ int AmrWbCodec::decodePlain(std::span input, std::span o dataOutSizeInBytes += frameOutputSize; } } - return dataOutSizeInBytes; + return {.mDecoded = dataOutSizeInBytes, + .mIsCng = ap.mFrames.size() == 1 ? (ap.mFrames.front().mMode == 0xFF) : false}; } Codec::DecodeResult AmrWbCodec::decode(std::span input, std::span output) { if (mConfig.mIuUP) - return {.mDecoded = (size_t)decodeIuup(input, output)}; + return decodeIuup(input, output); else - return {.mDecoded = (size_t)decodePlain(input, output)}; - - return {.mDecoded = 0}; + return decodePlain(input, output); } size_t AmrWbCodec::plc(int lostFrames, std::span output) diff --git a/src/engine/media/MT_AmrCodec.h b/src/engine/media/MT_AmrCodec.h index 8eca3e12..c09b512e 100644 --- a/src/engine/media/MT_AmrCodec.h +++ b/src/engine/media/MT_AmrCodec.h @@ -85,8 +85,8 @@ protected: int mPreviousPacketLength; - int decodeIuup(std::span input, std::span output); - int decodePlain(std::span input, std::span output); + DecodeResult decodeIuup(std::span input, std::span output); + DecodeResult decodePlain(std::span input, std::span output); public: class CodecFactory: public Factory diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index e6eee304..a5f550c6 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -681,8 +681,9 @@ AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& out else { // Decode frame by frame - auto r = mCodec->decode({rtp.GetPayloadData() + i * mCodec->rtpLength(), (size_t)frameLength}, - {(uint8_t*)mDecodedFrame, sizeof mDecodedFrame}); + auto codecInput = std::span{rtp.GetPayloadData() + i * mCodec->rtpLength(), (size_t)frameLength}; + auto codecOutput = std::span{(uint8_t*)mDecodedFrame, sizeof mDecodedFrame}; + auto r = mCodec->decode(codecInput, codecOutput); mDecodedLength = r.mDecoded; if (mDecodedLength > 0) processDecoded(output, options); diff --git a/src/engine/media/MT_Codec.h b/src/engine/media/MT_Codec.h index 6972c9f5..6896dad9 100644 --- a/src/engine/media/MT_Codec.h +++ b/src/engine/media/MT_Codec.h @@ -52,17 +52,19 @@ public: int mPcmLength = 0; // In bytes int mFrameTime = 0; // In milliseconds int mRtpLength = 0; // In bytes + float mTimestampUnit = 0.0f; }; // Returns information about this codec instance virtual Info info() = 0; // Helper functions to return information - they are based on info() method - int pcmLength() { return info().mPcmLength; } - int rtpLength() { return info().mRtpLength; } - int channels() { return info().mChannels; } - int samplerate() { return info().mSamplerate; } - int frameTime() { return info().mFrameTime; } - std::string name() { return info().mName; } + int pcmLength() { return info().mPcmLength; } + int rtpLength() { return info().mRtpLength; } + int channels() { return info().mChannels; } + int samplerate() { return info().mSamplerate; } + int frameTime() { return info().mFrameTime; } + std::string name() { return info().mName; } + float timestampUnit() { return info().mTimestampUnit == 0.0f ? 1.0f / info().mSamplerate : info().mTimestampUnit; } Audio::Format getAudioFormat() { return Audio::Format(this->info().mSamplerate, this->info().mChannels); From 72a7042ab9bbc40aa49ca4811e3ac709de9ce12b Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Tue, 24 Feb 2026 10:48:03 +0300 Subject: [PATCH 6/9] - allow AMR-NB/WB BWE profiles in rtp_decode --- test/rtp_decode/CMakeLists.txt | 6 +++++- test/rtp_decode/main.cpp | 13 ++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/test/rtp_decode/CMakeLists.txt b/test/rtp_decode/CMakeLists.txt index 4258de1e..3df858c3 100644 --- a/test/rtp_decode/CMakeLists.txt +++ b/test/rtp_decode/CMakeLists.txt @@ -1,6 +1,10 @@ +cmake_minimum_required(VERSION 3.20) +project(rtp_decode) + set (CMAKE_CXX_STANDARD 20) set (CMAKE_CXX_STANDARD_REQUIRED ON) -add_executable(rtp_decode main.cpp) add_subdirectory(../../src build_rtphone) + +add_executable(rtp_decode main.cpp) target_link_libraries(rtp_decode PRIVATE rtphone) diff --git a/test/rtp_decode/main.cpp b/test/rtp_decode/main.cpp index 220bda0e..2db3c613 100644 --- a/test/rtp_decode/main.cpp +++ b/test/rtp_decode/main.cpp @@ -29,7 +29,8 @@ static void usage(const char* progname) fprintf(stderr, "Usage: %s --codec [--pt ] [--rate ] [--channels ]\n" "\n" - "Codecs: pcmu pcma g722 g729 opus gsm gsmhr gsmefr amrnb amrwb evs ilbc20 ilbc30 isac16 isac32\n" + "Codecs: pcmu pcma g722 g729 opus gsm gsmhr gsmefr\n" + " amrnb amrwb amrnb-bwe amrwb-bwe evs ilbc20 ilbc30 isac16 isac32\n" "\n" "Options:\n" " --codec Codec name (required)\n" @@ -65,8 +66,10 @@ static const CodecDefaults kCodecTable[] = { { "g729", 18, false }, { "gsm", 3, false }, { "opus", 106, false }, - { "amrnb", -1, true }, - { "amrwb", -1, true }, + { "amrnb", -1, true }, + { "amrwb", -1, true }, + { "amrnb-bwe", -1, true }, + { "amrwb-bwe", -1, true }, { "gsmhr", -1, true }, { "gsmefr", 126, false }, { "evs", 127, false }, @@ -104,6 +107,10 @@ static MT::CodecList::Settings buildSettings(const std::string& codecName, int p s.mAmrNbOctetPayloadType.insert(pt); } else if (codecName == "amrwb") { s.mAmrWbOctetPayloadType.insert(pt); + } else if (codecName == "amrnb-bwe") { + s.mAmrNbPayloadType.insert(pt); + } else if (codecName == "amrwb-bwe") { + s.mAmrWbPayloadType.insert(pt); } else if (codecName == "evs") { MT::CodecList::Settings::EvsSpec ev; ev.mPayloadType = pt; From 03f662e5ce27cd6e13677bed30d564a6aa1deee5 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Tue, 24 Feb 2026 12:49:39 +0300 Subject: [PATCH 7/9] - fix packet loss reporting --- src/engine/media/MT_AudioReceiver.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index a5f550c6..70f0bbd4 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -238,11 +238,11 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // Gap between new packet and previous on int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1; - gap = std::min(gap, 127); + // gap = std::min(gap, 127); if (gap > 0) { // std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl; - mStat.mPacketLoss++; + mStat.mPacketLoss += gap; auto currentTimestamp = std::chrono::microseconds(uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000)); if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno)) @@ -251,8 +251,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch() .mGap = gap, .mTimestamp = currentTimestamp}); - mLastSeqno = *mLastSeqno + 1; // As we deal with the audio gap - return the silence and increase last seqno - + mLastSeqno = seqno; result = {FetchResult::Status::Gap}; } else From 06b39dd629ea974b55abec29e5eec86fa268cb30 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Wed, 25 Feb 2026 10:16:24 +0300 Subject: [PATCH 8/9] - improve packet loss reporting + initial DTMF RFC 2833 event reporting --- src/engine/helper/HL_Rtp.cpp | 5 ++ src/engine/helper/HL_Rtp.h | 2 + src/engine/media/MT_AmrCodec.cpp | 2 +- src/engine/media/MT_AudioReceiver.cpp | 49 ++++++++++++--- src/engine/media/MT_AudioReceiver.h | 10 ++- src/engine/media/MT_Dtmf.cpp | 88 ++++++++++++++++++--------- src/engine/media/MT_Dtmf.h | 71 ++++++++++++--------- src/engine/media/MT_Statistics.cpp | 25 ++++---- src/engine/media/MT_Statistics.h | 67 +++++++++++--------- 9 files changed, 210 insertions(+), 109 deletions(-) diff --git a/src/engine/helper/HL_Rtp.cpp b/src/engine/helper/HL_Rtp.cpp index d244bba6..0a9cce6e 100644 --- a/src/engine/helper/HL_Rtp.cpp +++ b/src/engine/helper/HL_Rtp.cpp @@ -173,6 +173,11 @@ int RtpHelper::findPayloadLength(const void* buffer, size_t length) return static_cast(payloadLen); } +std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t) +{ + return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000)); +} + // --- RtpDump implementation --- std::shared_ptr RtpDump::parseRtpData(const uint8_t* data, size_t len) diff --git a/src/engine/helper/HL_Rtp.h b/src/engine/helper/HL_Rtp.h index a2c8798f..aeb13ae3 100644 --- a/src/engine/helper/HL_Rtp.h +++ b/src/engine/helper/HL_Rtp.h @@ -43,6 +43,8 @@ public: static unsigned findSsrc(const void* buffer, size_t length); static void setSsrc(void* buffer, size_t length, uint32_t ssrc); static int findPayloadLength(const void* buffer, size_t length); + + static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t); }; /** diff --git a/src/engine/media/MT_AmrCodec.cpp b/src/engine/media/MT_AmrCodec.cpp index 3c3fff27..735ff7a1 100644 --- a/src/engine/media/MT_AmrCodec.cpp +++ b/src/engine/media/MT_AmrCodec.cpp @@ -148,7 +148,7 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter) // if (input.mWideband && f.mMode == 0xFF /* CNG */) // { // int a = 1; - // } + // }` if (input.mWideband && f.mFrameType == 15) { diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 70f0bbd4..45becfce 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -11,6 +11,7 @@ #include "MT_AudioReceiver.h" #include "MT_AudioCodec.h" #include "MT_CngHelper.h" +#include "MT_Dtmf.h" #include "../helper/HL_Log.h" #include "../helper/HL_Time.h" #include "../audio/Audio_Interface.h" @@ -207,7 +208,8 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // Save it as last packet however - to not confuse loss packet counter mFetchedPacket = mPacketList.front(); - mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); + mLastSeqno = mFetchedPacket->rtp()->GetExtendedSequenceNumber(); + mLastReceiveTime = mFetchedPacket->rtp()->GetReceiveTime(); // Erase from packet list mPacketList.erase(mPacketList.begin()); @@ -238,20 +240,27 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // Gap between new packet and previous on int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1; - // gap = std::min(gap, 127); if (gap > 0) { // std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl; mStat.mPacketLoss += gap; - auto currentTimestamp = std::chrono::microseconds(uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000)); + // Report is the onetime; there is no many sequential 1-packet gap reports if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno)) - mStat.mPacketLossTimeline.push_back({.mStartSeqno = *mLastSeqno, - .mEndSeqno = seqno, - .mGap = gap, - .mTimestamp = currentTimestamp}); + { + auto gapStart = RtpHelper::toMicroseconds(*mLastReceiveTime); + auto gapEnd = RtpHelper::toMicroseconds(packet.rtp()->GetReceiveTime()); + mStat.mPacketLossTimeline.emplace_back(PacketLossEvent{.mStartSeqno = *mLastSeqno, + .mEndSeqno = seqno, + .mGap = gap, + .mTimestampStart = gapStart, + .mTimestampEnd = gapEnd}); + } + // ToDo: here we should decide smth - 2-packet gap shoud report Status::Gap two times at least; but current implementation gives only one. + // It is not big problem - as gap is detected when we have smth to return usually mLastSeqno = seqno; + mLastReceiveTime = packet.rtp()->GetReceiveTime(); result = {FetchResult::Status::Gap}; } else @@ -261,6 +270,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // Save last returned normal packet mFetchedPacket = result.mPacket; mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber(); + mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime(); // Remove returned packet from the list mPacketList.erase(mPacketList.begin()); @@ -278,6 +288,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // Remember returned packet mFetchedPacket = result.mPacket; mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber(); + mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime(); // Remove returned packet from buffer list mPacketList.erase(mPacketList.begin()); @@ -955,5 +966,25 @@ DtmfReceiver::DtmfReceiver(Statistics& stat) DtmfReceiver::~DtmfReceiver() {} -void DtmfReceiver::add(std::shared_ptr /*p*/) -{} +void DtmfReceiver::add(const std::shared_ptr& p) +{ + // This receiver always work in context of single RTP stream; so there is no need to put SSRC map and so on + if (p->GetPayloadType() != 101) + return; + + auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()}); + if (ev.mTone != mEvent || ev.mEnd != mEventEnded) + { + // New tone is here + if (mCallback) + mCallback(ev.mTone); + + // Queue statistics item + mStat.mDtmf2833Timeline.emplace_back(Dtmf2833Event{.mTone = ev.mTone, + .mTimestamp = RtpHelper::toMicroseconds(p->GetReceiveTime())}); + + // Store to avoid triggering on the packet + mEvent = ev.mTone; + mEventEnded = ev.mEnd; + } +} diff --git a/src/engine/media/MT_AudioReceiver.h b/src/engine/media/MT_AudioReceiver.h index ed76c310..3efb8b09 100644 --- a/src/engine/media/MT_AudioReceiver.h +++ b/src/engine/media/MT_AudioReceiver.h @@ -120,6 +120,7 @@ protected: jrtplib::RTPSourceStats mRtpStats; std::shared_ptr mFetchedPacket; std::optional mLastSeqno; + std::optional mLastReceiveTime; // To calculate average interval between packet add. It is close to jitter but more useful in debugging. float mLastAddTime = 0.0f; @@ -248,11 +249,18 @@ protected: class DtmfReceiver: public Receiver { +private: + char mEvent = 0; + bool mEventEnded = false; + std::chrono::milliseconds mEventStart = 0ms; + std::function mCallback; + public: DtmfReceiver(Statistics& stat); ~DtmfReceiver(); - void add(std::shared_ptr p); + void add(const std::shared_ptr& p); + void setCallback(std::function callback); }; } diff --git a/src/engine/media/MT_Dtmf.cpp b/src/engine/media/MT_Dtmf.cpp index 5ba3838e..173bea0e 100644 --- a/src/engine/media/MT_Dtmf.cpp +++ b/src/engine/media/MT_Dtmf.cpp @@ -16,38 +16,68 @@ using namespace MT; -void DtmfBuilder::buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output) +void DtmfBuilder::buildRfc2833(const Rfc2833Event& ev, void* output) { - assert(duration); + assert(ev.mDuration != 0); assert(output); - assert(tone); + assert(ev.mTone != 0); unsigned char toneValue = 0; - if (tone >= '0' && tone <='9') - toneValue = tone - '0'; + if (ev.mTone >= '0' && ev.mTone <='9') + toneValue = ev.mTone - '0'; else - if (tone >= 'A' && tone <='D' ) - toneValue = tone - 'A' + 12; - else - if (tone == '*') - toneValue = 10; - else - if (tone == '#') - toneValue = 11; + if (ev.mTone >= 'A' && ev.mTone <='D' ) + toneValue = ev.mTone - 'A' + 12; + else + if (ev.mTone == '*') + toneValue = 10; + else + if (ev.mTone == '#') + toneValue = 11; char* packet = (char*)output; packet[0] = toneValue; - packet[1] = 1 | (volume << 2); - if (endOfEvent) + packet[1] = 1 | (ev.mVolume << 2); + if (ev.mEnd) packet[1] |= 128; else packet[1] &= 127; - unsigned short durationValue = htons(duration); + unsigned short durationValue = htons(ev.mDuration); memcpy(packet + 2, &durationValue, 2); } + +DtmfBuilder::Rfc2833Event DtmfBuilder::parseRfc2833(std::span payload) +{ + Rfc2833Event r; + if (payload.size_bytes() < 4) + return r; + + uint8_t b0 = payload[0]; + uint8_t b1 = payload[1]; + + if (b0 >=0 && b0 <= 9) + r.mTone = '0' + b0; + else + if (b0 >= 12 && b0 <= 17) + r.mTone = 'A' + b0; + else + if (b0 == 10) + r.mTone = '*'; + else + if (b0 == 11) + r.mTone = '#'; + + r.mEnd = (b1 & 128); + r.mVolume = (b1 & 127) >> 2; + r.mDuration = ntohs(*(uint16_t*)payload.data()+2); + + return r; +} + + #pragma region Inband DTMF support #include #ifndef TARGET_WIN @@ -302,24 +332,24 @@ bool DtmfContext::getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& s { // Emit rfc2833 packet output.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); + DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData()); d.mDuration -= milliseconds; if(d.mDuration <= 0) d.mStopped = true; } else - if (!d.mStopped) - { - output.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); - } - else - output.clear(); + if (!d.mStopped) + { + output.resize(4); + DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData()); + } + else + output.clear(); if (d.mStopped) { stopPacket.resize(4); - DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, true, stopPacket.mutableData()); + DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = true}, stopPacket.mutableData()); } else stopPacket.clear(); @@ -375,7 +405,7 @@ void zap_dtmf_detect_init(dtmf_detect_state_t *s); int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int isradio); int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max); -DTMFDetector::DTMFDetector() +InbandDtmfDetector::InbandDtmfDetector() :mState(NULL) { mState = malloc(sizeof(dtmf_detect_state_t)); @@ -384,13 +414,13 @@ DTMFDetector::DTMFDetector() zap_dtmf_detect_init((dtmf_detect_state_t*)mState); } -DTMFDetector::~DTMFDetector() +InbandDtmfDetector::~InbandDtmfDetector() { if (mState) free(mState); } -std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size) +std::string InbandDtmfDetector::streamPut(unsigned char* samples, unsigned int size) { char buf[16]; buf[0] = 0; if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0)) @@ -398,7 +428,7 @@ std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size) return buf; } -void DTMFDetector::resetState() +void InbandDtmfDetector::resetState() { zap_dtmf_detect_init((dtmf_detect_state_t*)mState); } diff --git a/src/engine/media/MT_Dtmf.h b/src/engine/media/MT_Dtmf.h index 998059e8..b0732377 100644 --- a/src/engine/media/MT_Dtmf.h +++ b/src/engine/media/MT_Dtmf.h @@ -15,38 +15,52 @@ namespace MT { - class DtmfBuilder - { - public: +class DtmfBuilder +{ +public: + struct Rfc2833Event + { + char mTone = 0; + int mDuration = 0; + int mVolume = 0; + bool mEnd = false; + + bool isValid() const { + return mTone != 0; + } + }; + // Output should be 4 bytes length - static void buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output); - + static void buildRfc2833(const Rfc2833Event& ev, void* output); + + static Rfc2833Event parseRfc2833(std::span payload); + // Buf receives PCM audio static void buildInband(int tone, int startTime, int finishTime, int rate, short* buf); - }; +}; - struct Dtmf - { +struct Dtmf +{ Dtmf(): mTone(0), mDuration(0), mVolume(0), mFinishCount(3), mCurrentTime(0), mStopped(false) {} Dtmf(int tone, int volume, int duration): mTone(tone), mVolume(volume), mDuration(duration), mFinishCount(3), mCurrentTime(0), mStopped(false) {} - + int mStopped; int mTone; int mDuration; // It is zero for tones generated by startTone()..stopTone() calls. int mVolume; int mFinishCount; int mCurrentTime; - }; +}; - typedef std::vector DtmfQueue; +typedef std::vector DtmfQueue; - class DtmfContext - { - public: +class DtmfContext +{ +public: enum Type { - Dtmf_Inband, - Dtmf_Rfc2833 + Dtmf_Inband, + Dtmf_Rfc2833 }; DtmfContext(); @@ -65,33 +79,34 @@ namespace MT bool getInband(int milliseconds, int rate, ByteBuffer& output); bool getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket); - protected: +protected: Mutex mGuard; Type mType; DtmfQueue mQueue; - }; +}; -class DTMFDetector +class InbandDtmfDetector { public: - /*! The default constructor. Allocates space for detector context. */ - DTMFDetector(); - - /*! The destructor. Free the detector context's memory. */ - ~DTMFDetector(); + /*! The default constructor. Allocates space for detector context. */ + InbandDtmfDetector(); - /*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation. + /*! The destructor. Free the detector context's memory. */ + ~InbandDtmfDetector(); + + /*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation. * @param samples Input PCM buffer pointer. * @param size Size of input buffer in bytes * @return Found DTMF event(s) in string representation. The returned value has variable length. */ - std::string streamPut(unsigned char* samples, unsigned int size); + std::string streamPut(unsigned char* samples, unsigned int size); - void resetState(); + void resetState(); protected: - void* mState; /// DTMF detector context + void* mState; /// DTMF detector context }; + } #endif diff --git a/src/engine/media/MT_Statistics.cpp b/src/engine/media/MT_Statistics.cpp index a0a7931e..ac3583c7 100644 --- a/src/engine/media/MT_Statistics.cpp +++ b/src/engine/media/MT_Statistics.cpp @@ -74,13 +74,16 @@ Statistics::~Statistics() void Statistics::calculateBurstr(double* burstr, double* lossr) const { - int lost = 0; - int bursts = 0; - for (int i = 0; i < 128; i++) - { - lost += i * mLoss[i]; - bursts += mLoss[i]; - } + int lost = 0; // Total packet lost + for (const auto& item: mPacketLossTimeline) + lost += item.mGap; + int bursts = mPacketLossTimeline.size(); // number of events + + // for (const auto& entry: mLoss) + // { + // lost += entry.first * entry.second; + // bursts += entry.second; + // } if (lost < 5) { @@ -109,14 +112,14 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const double Statistics::calculateMos(double maximalMos) const { // calculate lossrate and burst rate - double burstr, lossr; + double burstr = 0, lossr = 0; calculateBurstr(&burstr, &lossr); - double r; + double r = 0.0; double bpl = 8.47627; //mos = -4.23836 + 0.29873 * r - 0.00416744 * r * r + 0.0000209855 * r * r * r; - double mos; + double mos = 0.0; - if (mReceivedRtp < 100) + if (mReceivedRtp < 10) return 0.0; if (lossr == 0.0 || burstr == 0.0) diff --git a/src/engine/media/MT_Statistics.h b/src/engine/media/MT_Statistics.h index d637f6e2..349cf03f 100644 --- a/src/engine/media/MT_Statistics.h +++ b/src/engine/media/MT_Statistics.h @@ -4,16 +4,15 @@ #include #include #include -#include -#include "audio/Audio_DataWindow.h" -#include "helper/HL_Optional.hpp" #include "helper/HL_Statistics.h" #include "helper/HL_Types.h" +#include "helper/HL_InternetAddress.h" #include "jrtplib/src/rtptimeutilities.h" #include "jrtplib/src/rtppacket.h" +using namespace std::chrono_literals; namespace MT { @@ -56,47 +55,55 @@ struct PacketLossEvent uint32_t mStartSeqno = 0, mEndSeqno = 0; int mGap = 0; + std::chrono::microseconds mTimestampStart = 0us, + mTimestampEnd = 0us; +}; + +struct Dtmf2833Event +{ + char mTone; std::chrono::microseconds mTimestamp; }; class Statistics { public: - size_t mReceived = 0, // Received traffic in bytes - mSent = 0, // Sent traffic in bytes - mReceivedRtp = 0, // Number of received rtp packets - mSentRtp = 0, // Number of sent rtp packets - mReceivedRtcp = 0, // Number of received rtcp packets - mSentRtcp = 0, // Number of sent rtcp packets - mDuplicatedRtp = 0, // Number of received duplicated rtp packets - mOldRtp = 0, // Number of late rtp packets - mPacketLoss = 0, // Number of lost packets - mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б - mIllegalRtp = 0; // Number of rtp packets with bad payload type + size_t mReceived = 0, // Received traffic in bytes + mSent = 0, // Sent traffic in bytes + mReceivedRtp = 0, // Number of received rtp packets + mSentRtp = 0, // Number of sent rtp packets + mReceivedRtcp = 0, // Number of received rtcp packets + mSentRtcp = 0, // Number of sent rtcp packets + mDuplicatedRtp = 0, // Number of received duplicated rtp packets + mOldRtp = 0, // Number of late rtp packets + mPacketLoss = 0, // Number of lost packets + mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б + mIllegalRtp = 0; // Number of rtp packets with bad payload type - TestResult mDecodingInterval, // Average interval on call to packet decode - mDecodeRequested, // Average amount of requested audio frames to play - mPacketInterval; // Average interval between packet adding to jitter buffer + TestResult mDecodingInterval, // Average interval on call to packet decode + mDecodeRequested, // Average amount of requested audio frames to play + mPacketInterval; // Average interval between packet adding to jitter buffer - std::array mLoss = {0}; // Every item is number of loss of corresping length - size_t mAudioTime = 0; // Decoded/found time in milliseconds - size_t mDecodedSize = 0; // Number of decoded bytes - uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream - ice::NetworkAddress mRemotePeer; // Last known remote RTP address + std::map mLoss; // Every item is number of loss of corresping length + size_t mAudioTime = 0; // Decoded/found time in milliseconds + size_t mDecodedSize = 0; // Number of decoded bytes + uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream + ice::NetworkAddress mRemotePeer; // Last known remote RTP address // AMR codec bitrate switch counter - int mBitrateSwitchCounter = 0; - int mCng = 0; - std::string mCodecName; - float mJitter = 0.0f; // Jitter - TestResult mRttDelay; // RTT delay + int mBitrateSwitchCounter = 0; + int mCng = 0; + std::string mCodecName; + float mJitter = 0.0f; // Jitter + TestResult mRttDelay; // RTT delay // Timestamp when first RTP packet has arrived - std::optional mFirstRtpTime; + std::optional mFirstRtpTime; - std::map mCodecCount; // Stats on used codecs + std::map mCodecCount; // Stats on used codecs - std::vector mPacketLossTimeline; // Packet loss timeline + std::vector mPacketLossTimeline; // Packet loss timeline + std::vector mDtmf2833Timeline; // It is to calculate network MOS void calculateBurstr(double* burstr, double* loss) const; From 34fd9121eddfee0da2cf64af870a2bb5c9d1ebf3 Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Wed, 25 Feb 2026 13:54:37 +0300 Subject: [PATCH 9/9] - adopt RFC2833 decoder for pcap analyzer --- src/engine/media/MT_AudioReceiver.cpp | 141 ++++++++++++---------- src/engine/media/MT_AudioReceiver.h | 35 +++--- src/engine/media/MT_CodecList.cpp | 17 ++- src/engine/media/MT_CodecList.h | 3 + src/engine/media/MT_SingleAudioStream.cpp | 2 +- 5 files changed, 116 insertions(+), 82 deletions(-) diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 45becfce..9c2da62c 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -201,7 +201,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch() // See if there is enough information in buffer auto total = findTimelength(); - while (total > mHigh && mPacketList.size() && 0ms != mHigh) + while (total > mHigh && mPacketList.size() > 1 && 0ms != mHigh) { ICELogMedia( << "Dropping RTP packets from jitter buffer"); total -= mPacketList.front()->timelength(); @@ -337,7 +337,7 @@ Receiver::~Receiver() //-------------- AudioReceiver ---------------- AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat) - :Receiver(stat), mBuffer(stat), mCodecSettings(settings), mCodecList(settings) + :Receiver(stat), mBuffer(stat), mDtmfBuffer(stat), mCodecSettings(settings), mCodecList(settings), mDtmfReceiver(stat) { // Init resamplers mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE); @@ -351,6 +351,10 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short)); + mDtmfBuffer.setPrebuffer(0ms); + mDtmfBuffer.setLow(0ms); + mDtmfBuffer.setHigh(1ms); + #if defined(DUMP_DECODED) mDecodedDump = std::make_shared(); mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS); @@ -431,66 +435,72 @@ bool AudioReceiver::add(const std::shared_ptr& p, Codec** de // Increase codec counter mStat.mCodecCount[ptype]++; - // Check if codec can be handled - Codec* codec = nullptr; - auto codecIter = mCodecMap.find(ptype); - if (codecIter == mCodecMap.end()) + // Check if we deal with telephone-event + if (p->GetPayloadType() == mCodecSettings.mTelephoneEvent) { - // Well, there is no information about the codec; skip this packet + *detectedCodec = nullptr; + mDtmfBuffer.add(p, 10ms, 8000); } else { - // Check if codec is creating lazily - if (!codecIter->second) + // Look for codec + // Check if codec can be handled + Codec* codec = nullptr; + auto codecIter = mCodecMap.find(ptype); + if (codecIter != mCodecMap.end()) { - codecIter->second = mCodecList.createCodecByPayloadType(ptype); + // Check if codec is creating lazily + if (!codecIter->second) + { + codecIter->second = mCodecList.createCodecByPayloadType(ptype); + } + codec = codecIter->second.get(); + + // Return pointer to codec if needed.get() + if (detectedCodec) + *detectedCodec = codec; + + if (mStat.mCodecName.empty() && codec) + mStat.mCodecName = codec->name(); + + + if (!codec) + time_length = 10; + else + if (!codec->rtpLength()) + time_length = codec->frameTime(); + else + time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime()); + + if (codec) + samplerate = codec->samplerate(); } - codec = codecIter->second.get(); - - // Return pointer to codec if needed.get() - if (detectedCodec) - *detectedCodec = codec; - - if (mStat.mCodecName.empty() && codec) - mStat.mCodecName = codec->name(); + // Process jitter anyway - can we decode payload or not + mJitterStats.process(p.get(), samplerate); + mStat.mJitter = static_cast(mJitterStats.get()); if (!codec) - time_length = 10; + return false; // There is no sense to add this packet into jitter buffer - we can't decode this + + // Check if packet is CNG + if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8)) + time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20; else - if (!codec->rtpLength()) - time_length = codec->frameTime(); - else - time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime()); + // Check if packet is too short from time length side + if (time_length < 2) + { + // It will cause statistics to report about bad RTP packet + // I have to replay last packet payload here to avoid report about lost packet + mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate); + return false; + } - if (codec) - samplerate = codec->samplerate(); + // Queue packet to buffer + auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get(); + return packet; } - - // Process jitter - mJitterStats.process(p.get(), samplerate); - mStat.mJitter = static_cast(mJitterStats.get()); - - if (!codec) - return false; // There is no sense to add this packet into jitter buffer - we can't decode this - - // Check if packet is CNG - if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8)) - time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20; - else - // Check if packet is too short from time length side - if (time_length < 2) - { - // It will cause statistics to report about bad RTP packet - // I have to replay last packet payload here to avoid report about lost packet - mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate); - return false; - } - - // Queue packet to buffer - auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get(); - - return packet; + return {}; } void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options) @@ -764,6 +774,12 @@ AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, { DecodeResult result = {.mStatus = DecodeResult::Status::Skip}; + // Process RFC2833 here; it doesn't result in any audio - only callbacks and statistics + auto fr = mDtmfBuffer.fetch(); + if (fr.mPacket && fr.mStatus == RtpBuffer::FetchResult::Status::RegularPacket) + mDtmfReceiver.add(fr.mPacket->rtp()); + + auto produced = 0ms; if (mAvailable.filled() && mCodec && options.mElapsed != 0ms) { @@ -968,23 +984,22 @@ DtmfReceiver::~DtmfReceiver() void DtmfReceiver::add(const std::shared_ptr& p) { - // This receiver always work in context of single RTP stream; so there is no need to put SSRC map and so on - if (p->GetPayloadType() != 101) - return; - auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()}); if (ev.mTone != mEvent || ev.mEnd != mEventEnded) { - // New tone is here - if (mCallback) - mCallback(ev.mTone); + if (!(mEvent == ev.mTone && !mEventEnded && ev.mEnd)) + { + // New tone is here + if (mCallback) + mCallback(ev.mTone); - // Queue statistics item - mStat.mDtmf2833Timeline.emplace_back(Dtmf2833Event{.mTone = ev.mTone, - .mTimestamp = RtpHelper::toMicroseconds(p->GetReceiveTime())}); + // Queue statistics item + mStat.mDtmf2833Timeline.emplace_back(Dtmf2833Event{.mTone = ev.mTone, + .mTimestamp = RtpHelper::toMicroseconds(p->GetReceiveTime())}); - // Store to avoid triggering on the packet - mEvent = ev.mTone; - mEventEnded = ev.mEnd; + // Store to avoid triggering on the packet + mEvent = ev.mTone; + mEventEnded = ev.mEnd; + } } } diff --git a/src/engine/media/MT_AudioReceiver.h b/src/engine/media/MT_AudioReceiver.h index 3efb8b09..0c567800 100644 --- a/src/engine/media/MT_AudioReceiver.h +++ b/src/engine/media/MT_AudioReceiver.h @@ -136,6 +136,23 @@ protected: Statistics& mStat; }; +class DtmfReceiver: public Receiver +{ +private: + char mEvent = 0; + bool mEventEnded = false; + std::chrono::milliseconds mEventStart = 0ms; + std::function mCallback; + +public: + DtmfReceiver(Statistics& stat); + ~DtmfReceiver(); + + void add(const std::shared_ptr& p); + void setCallback(std::function callback); +}; + + class AudioReceiver: public Receiver { public: @@ -189,6 +206,9 @@ public: protected: RtpBuffer mBuffer; // Jitter buffer itself + RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver + DtmfReceiver mDtmfReceiver; + CodecMap mCodecMap; PCodec mCodec; int mFrameCount = 0; @@ -247,21 +267,6 @@ protected: DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options); }; -class DtmfReceiver: public Receiver -{ -private: - char mEvent = 0; - bool mEventEnded = false; - std::chrono::milliseconds mEventStart = 0ms; - std::function mCallback; - -public: - DtmfReceiver(Statistics& stat); - ~DtmfReceiver(); - - void add(const std::shared_ptr& p); - void setCallback(std::function callback); -}; } #endif diff --git a/src/engine/media/MT_CodecList.cpp b/src/engine/media/MT_CodecList.cpp index 2ee9282d..f4b25742 100644 --- a/src/engine/media/MT_CodecList.cpp +++ b/src/engine/media/MT_CodecList.cpp @@ -65,6 +65,8 @@ bool CodecList::Settings::contains(int ptype) const if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype) return true; + if (mTelephoneEvent == ptype) + return true; return false; } @@ -122,6 +124,9 @@ std::string CodecList::Settings::toString() const if (mGsmEfrPayloadType != -1) oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " "; + if (mTelephoneEvent != -1) + oss << "RFC2833 DTMF ptype: " << mTelephoneEvent; + for (auto& spec: mEvsSpec) { oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " "; @@ -132,6 +137,7 @@ std::string CodecList::Settings::toString() const oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl; } + return oss.str(); } @@ -151,6 +157,7 @@ void CodecList::Settings::clear() mGsmEfrPayloadType = -1; mGsmFrPayloadType = -1; mGsmHrPayloadType = -1; + mTelephoneEvent = -1; } bool CodecList::Settings::EvsSpec::isValid() const @@ -268,15 +275,16 @@ CodecList::Settings CodecList::Settings::parseSdp(const std::list& } } else if (codec_name == "EVS") { r.mEvsSpec.push_back({ptype}); - } + } else if (codec_name == "TELEPHONE-EVENT") + r.mTelephoneEvent = ptype; } return r; } bool CodecList::Settings::operator == (const Settings& rhs) const { - if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType) != - std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType)) + if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType, mTelephoneEvent) != + std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType, rhs.mTelephoneEvent)) return false; if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType) @@ -306,6 +314,9 @@ bool CodecList::Settings::operator == (const Settings& rhs) const if (mOpusSpec[i] != rhs.mOpusSpec[i]) return false; + if (mTelephoneEvent != rhs.mTelephoneEvent) + return false; + return true; } diff --git a/src/engine/media/MT_CodecList.h b/src/engine/media/MT_CodecList.h index fdfa69ce..c8a107c0 100644 --- a/src/engine/media/MT_CodecList.h +++ b/src/engine/media/MT_CodecList.h @@ -28,6 +28,9 @@ public: bool mWrapIuUP = false; bool mSkipDecode = false; + // RFC2833 DTMF + int mTelephoneEvent = -1; + // AMR payload types std::set mAmrWbPayloadType = { }; std::set mAmrNbPayloadType = { }; diff --git a/src/engine/media/MT_SingleAudioStream.cpp b/src/engine/media/MT_SingleAudioStream.cpp index 3aa5702d..1c3c737a 100644 --- a/src/engine/media/MT_SingleAudioStream.cpp +++ b/src/engine/media/MT_SingleAudioStream.cpp @@ -23,7 +23,7 @@ SingleAudioStream::~SingleAudioStream() void SingleAudioStream::process(const std::shared_ptr& packet) { ICELogMedia(<< "Processing incoming RTP/RTCP packet"); - if (packet->GetPayloadType() == 101/*resip::Codec::TelephoneEvent.payloadType()*/) + if (packet->GetPayloadType() == mReceiver.getCodecSettings().mTelephoneEvent) mDtmfReceiver.add(packet); else mReceiver.add(packet);