From 9b144d30e0ea8ce900c37b96ba2cbdda14b0ae88 Mon Sep 17 00:00:00 2001 From: kwacker Date: Sun, 11 Apr 2010 13:46:11 +0200 Subject: Burn 0.2.0-beta3 und Streamdev mit Paches aktualisiert --- plugins/streamdev/streamdev-cvs/server/CVS/Entries | 34 + .../streamdev/streamdev-cvs/server/CVS/Entries.Log | 1 + .../streamdev/streamdev-cvs/server/CVS/Repository | 1 + plugins/streamdev/streamdev-cvs/server/CVS/Root | 1 + plugins/streamdev/streamdev-cvs/server/component.c | 49 + plugins/streamdev/streamdev-cvs/server/component.h | 51 + .../streamdev/streamdev-cvs/server/componentHTTP.c | 18 + .../streamdev/streamdev-cvs/server/componentHTTP.h | 18 + .../streamdev/streamdev-cvs/server/componentIGMP.c | 447 +++++ .../streamdev/streamdev-cvs/server/componentIGMP.h | 62 + .../streamdev/streamdev-cvs/server/componentVTP.c | 18 + .../streamdev/streamdev-cvs/server/componentVTP.h | 18 + .../streamdev/streamdev-cvs/server/connection.c | 255 +++ .../streamdev/streamdev-cvs/server/connection.h | 99 ++ .../streamdev-cvs/server/connectionHTTP.c | 296 ++++ .../streamdev-cvs/server/connectionHTTP.h | 70 + .../streamdev-cvs/server/connectionIGMP.c | 64 + .../streamdev-cvs/server/connectionIGMP.h | 45 + .../streamdev/streamdev-cvs/server/connectionVTP.c | 1768 ++++++++++++++++++++ .../streamdev/streamdev-cvs/server/connectionVTP.h | 93 + .../streamdev-cvs/server/http/CVS/Entries | 1 + .../streamdev-cvs/server/http/CVS/Repository | 1 + .../streamdev/streamdev-cvs/server/http/CVS/Root | 1 + .../streamdev/streamdev-cvs/server/livefilter.c | 39 + .../streamdev/streamdev-cvs/server/livefilter.h | 32 + .../streamdev/streamdev-cvs/server/livestreamer.c | 689 ++++++++ .../streamdev/streamdev-cvs/server/livestreamer.h | 82 + plugins/streamdev/streamdev-cvs/server/menuHTTP.c | 429 +++++ plugins/streamdev/streamdev-cvs/server/menuHTTP.h | 143 ++ plugins/streamdev/streamdev-cvs/server/recplayer.c | 288 ++++ plugins/streamdev/streamdev-cvs/server/recplayer.h | 63 + plugins/streamdev/streamdev-cvs/server/server.c | 173 ++ plugins/streamdev/streamdev-cvs/server/server.h | 49 + plugins/streamdev/streamdev-cvs/server/setup.c | 141 ++ plugins/streamdev/streamdev-cvs/server/setup.h | 48 + plugins/streamdev/streamdev-cvs/server/streamer.c | 162 ++ plugins/streamdev/streamdev-cvs/server/streamer.h | 102 ++ plugins/streamdev/streamdev-cvs/server/suspend.c | 57 + plugins/streamdev/streamdev-cvs/server/suspend.dat | 1206 +++++++++++++ plugins/streamdev/streamdev-cvs/server/suspend.h | 36 + 40 files changed, 7150 insertions(+) create mode 100644 plugins/streamdev/streamdev-cvs/server/CVS/Entries create mode 100644 plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log create mode 100644 plugins/streamdev/streamdev-cvs/server/CVS/Repository create mode 100644 plugins/streamdev/streamdev-cvs/server/CVS/Root create mode 100644 plugins/streamdev/streamdev-cvs/server/component.c create mode 100644 plugins/streamdev/streamdev-cvs/server/component.h create mode 100644 plugins/streamdev/streamdev-cvs/server/componentHTTP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/componentHTTP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/componentIGMP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/componentIGMP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/componentVTP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/componentVTP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/connection.c create mode 100644 plugins/streamdev/streamdev-cvs/server/connection.h create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionHTTP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionHTTP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionIGMP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionIGMP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionVTP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/connectionVTP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/http/CVS/Entries create mode 100644 plugins/streamdev/streamdev-cvs/server/http/CVS/Repository create mode 100644 plugins/streamdev/streamdev-cvs/server/http/CVS/Root create mode 100644 plugins/streamdev/streamdev-cvs/server/livefilter.c create mode 100644 plugins/streamdev/streamdev-cvs/server/livefilter.h create mode 100644 plugins/streamdev/streamdev-cvs/server/livestreamer.c create mode 100644 plugins/streamdev/streamdev-cvs/server/livestreamer.h create mode 100644 plugins/streamdev/streamdev-cvs/server/menuHTTP.c create mode 100644 plugins/streamdev/streamdev-cvs/server/menuHTTP.h create mode 100644 plugins/streamdev/streamdev-cvs/server/recplayer.c create mode 100644 plugins/streamdev/streamdev-cvs/server/recplayer.h create mode 100644 plugins/streamdev/streamdev-cvs/server/server.c create mode 100644 plugins/streamdev/streamdev-cvs/server/server.h create mode 100644 plugins/streamdev/streamdev-cvs/server/setup.c create mode 100644 plugins/streamdev/streamdev-cvs/server/setup.h create mode 100644 plugins/streamdev/streamdev-cvs/server/streamer.c create mode 100644 plugins/streamdev/streamdev-cvs/server/streamer.h create mode 100644 plugins/streamdev/streamdev-cvs/server/suspend.c create mode 100644 plugins/streamdev/streamdev-cvs/server/suspend.dat create mode 100644 plugins/streamdev/streamdev-cvs/server/suspend.h (limited to 'plugins/streamdev/streamdev-cvs/server') diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Entries b/plugins/streamdev/streamdev-cvs/server/CVS/Entries new file mode 100644 index 0000000..1a9cebe --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/CVS/Entries @@ -0,0 +1,34 @@ +/component.c/1.4/Fri Feb 13 10:39:22 2009// +/component.h/1.3/Fri Feb 13 10:39:22 2009// +/componentHTTP.c/1.2/Mon May 9 20:22:29 2005// +/componentHTTP.h/1.2/Mon May 9 20:22:29 2005// +/componentIGMP.c/1.2/Fri Jul 3 21:44:19 2009// +/componentIGMP.h/1.1/Fri Feb 13 10:39:22 2009// +/componentVTP.c/1.2/Mon May 9 20:22:29 2005// +/componentVTP.h/1.2/Mon May 9 20:22:29 2005// +/connection.c/1.13/Fri Sep 18 10:43:26 2009// +/connection.h/1.8/Fri Sep 18 10:43:26 2009// +/connectionHTTP.c/1.17/Fri Jun 19 06:32:45 2009// +/connectionHTTP.h/1.6/Tue Oct 14 11:05:48 2008// +/connectionIGMP.c/1.1/Fri Feb 13 10:39:22 2009// +/connectionIGMP.h/1.1/Fri Feb 13 10:39:22 2009// +/connectionVTP.c/1.27/Fri Jan 29 12:03:02 2010// +/connectionVTP.h/1.11/Wed Jul 1 10:46:16 2009// +/livefilter.c/1.7/Fri Feb 13 13:02:40 2009// +/livefilter.h/1.5/Mon Apr 7 14:27:31 2008// +/livestreamer.c/1.30/Sat Feb 20 23:02:10 2010// +/livestreamer.h/1.20/Mon Jul 6 06:23:29 2009// +/menuHTTP.c/1.6/Sat Feb 20 22:18:14 2010// +/menuHTTP.h/1.3/Tue Sep 15 10:39:18 2009// +/recplayer.c/1.1/Wed Jul 1 11:00:49 2009// +/recplayer.h/1.1/Wed Jul 1 11:00:49 2009// +/server.c/1.10/Fri Feb 13 10:39:22 2009// +/server.h/1.6/Wed Oct 22 11:59:32 2008// +/setup.c/1.9/Tue Oct 13 06:38:47 2009// +/setup.h/1.3/Fri Sep 18 10:43:26 2009// +/streamer.c/1.19/Fri Jun 19 06:32:45 2009// +/streamer.h/1.11/Fri Jun 19 06:32:45 2009// +/suspend.c/1.3/Wed Oct 22 11:59:32 2008// +/suspend.dat/1.1.1.1/Thu Dec 30 22:44:26 2004// +/suspend.h/1.2/Wed Oct 22 11:59:32 2008// +D diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log b/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log new file mode 100644 index 0000000..5674fc6 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/CVS/Entries.Log @@ -0,0 +1 @@ +A D/http//// diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Repository b/plugins/streamdev/streamdev-cvs/server/CVS/Repository new file mode 100644 index 0000000..785255b --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/CVS/Repository @@ -0,0 +1 @@ +streamdev/server diff --git a/plugins/streamdev/streamdev-cvs/server/CVS/Root b/plugins/streamdev/streamdev-cvs/server/CVS/Root new file mode 100644 index 0000000..2c7f6ce --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/CVS/Root @@ -0,0 +1 @@ +:pserver:anoncvs@vdr-developer.org:/var/cvsroot diff --git a/plugins/streamdev/streamdev-cvs/server/component.c b/plugins/streamdev/streamdev-cvs/server/component.c new file mode 100644 index 0000000..70d861a --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/component.c @@ -0,0 +1,49 @@ +/* + * $Id: component.c,v 1.4 2009/02/13 10:39:22 schmirl Exp $ + */ + +#include "server/component.h" +#include "server/connection.h" + +cServerComponent::cServerComponent(const char *Protocol, const char *ListenIp, + uint ListenPort, int Type, int IpProto): + m_Protocol(Protocol), + m_Listen(Type, IpProto), + m_ListenIp(ListenIp), + m_ListenPort(ListenPort) +{ +} + +cServerComponent::~cServerComponent() +{ +} + +bool cServerComponent::Initialize(void) +{ + if (!m_Listen.Listen(m_ListenIp, m_ListenPort, 5)) { + esyslog("Streamdev: Couldn't listen (%s) %s:%d: %m", + m_Protocol, m_ListenIp, m_ListenPort); + return false; + } + isyslog("Streamdev: Listening (%s) on port %d", m_Protocol, m_ListenPort); + return true; +} + +void cServerComponent::Destruct(void) +{ + m_Listen.Close(); +} + +cServerConnection *cServerComponent::Accept(void) +{ + cServerConnection *client = NewClient(); + if (client->Accept(m_Listen)) { + isyslog("Streamdev: Accepted new client (%s) %s:%d", m_Protocol, + client->RemoteIp().c_str(), client->RemotePort()); + return client; + } else { + esyslog("Streamdev: Couldn't accept (%s): %m", m_Protocol); + delete client; + } + return NULL; +} diff --git a/plugins/streamdev/streamdev-cvs/server/component.h b/plugins/streamdev/streamdev-cvs/server/component.h new file mode 100644 index 0000000..7efd4ba --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/component.h @@ -0,0 +1,51 @@ +/* + * $Id: component.h,v 1.3 2009/02/13 10:39:22 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVERS_COMPONENT_H +#define VDR_STREAMDEV_SERVERS_COMPONENT_H + +#include "tools/socket.h" +#include "tools/select.h" + +#include + +class cServerConnection; + +/* Basic TCP listen server, all functions virtual if a derivation wants to do + things different */ + +class cServerComponent: public cListObject { +private: + const char *m_Protocol; + cTBSocket m_Listen; + const char *m_ListenIp; + uint m_ListenPort; + +protected: + /* Returns a new connection object for Accept() */ + virtual cServerConnection *NewClient(void) = 0; + +public: + cServerComponent(const char *Protocol, const char *ListenIp, uint ListenPort, int Type = SOCK_STREAM, int IpProto = 0); + virtual ~cServerComponent(); + + /* Starts listening on the specified Port, override if you want to do things + different */ + virtual bool Initialize(void); + + /* Stops listening, override if you want to do things different */ + virtual void Destruct(void); + + /* Get the listening socket's file number */ + virtual int Socket(void) const { return (int)m_Listen; } + + /* Adds the listening socket to the Select object */ + virtual void Add(cTBSelect &Select) const { Select.Add(m_Listen); } + + /* Accepts the connection on a NewClient() object and calls the + Welcome() on it, override if you want to do things different */ + virtual cServerConnection *Accept(void); +}; + +#endif // VDR_STREAMDEV_SERVERS_COMPONENT_H diff --git a/plugins/streamdev/streamdev-cvs/server/componentHTTP.c b/plugins/streamdev/streamdev-cvs/server/componentHTTP.c new file mode 100644 index 0000000..70e95e9 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentHTTP.c @@ -0,0 +1,18 @@ +/* + * $Id: componentHTTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ + */ + +#include "server/componentHTTP.h" +#include "server/connectionHTTP.h" +#include "server/setup.h" + +cComponentHTTP::cComponentHTTP(void): + cServerComponent("HTTP", StreamdevServerSetup.HTTPBindIP, + StreamdevServerSetup.HTTPServerPort) +{ +} + +cServerConnection *cComponentHTTP::NewClient(void) +{ + return new cConnectionHTTP; +} diff --git a/plugins/streamdev/streamdev-cvs/server/componentHTTP.h b/plugins/streamdev/streamdev-cvs/server/componentHTTP.h new file mode 100644 index 0000000..29dc087 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentHTTP.h @@ -0,0 +1,18 @@ +/* + * $Id: componentHTTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ + */ + +#ifndef VDR_STREAMDEV_HTTPSERVER_H +#define VDR_STREAMDEV_HTTPSERVER_H + +#include "server/component.h" + +class cComponentHTTP: public cServerComponent { +protected: + virtual cServerConnection *NewClient(void); + +public: + cComponentHTTP(void); +}; + +#endif // VDR_STREAMDEV_HTTPSERVER_H diff --git a/plugins/streamdev/streamdev-cvs/server/componentIGMP.c b/plugins/streamdev/streamdev-cvs/server/componentIGMP.c new file mode 100644 index 0000000..d9f8811 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentIGMP.c @@ -0,0 +1,447 @@ +/* + * $Id: componentIGMP.c,v 1.2 2009/07/03 21:44:19 schmirl Exp $ + */ +#include +#include + +#include "server/componentIGMP.h" +#include "server/connectionIGMP.h" +#include "server/setup.h" + +#ifndef IGMP_ALL_HOSTS +#define IGMP_ALL_HOSTS htonl(0xE0000001L) +#endif +#ifndef IGMP_ALL_ROUTER +#define IGMP_ALL_ROUTER htonl(0xE0000002L) +#endif + +// IGMP parameters according to RFC2236. All time values in seconds. +#define IGMP_ROBUSTNESS 2 +#define IGMP_QUERY_INTERVAL 125 +#define IGMP_QUERY_RESPONSE_INTERVAL 10 +#define IGMP_GROUP_MEMBERSHIP_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL) +#define IGMP_OTHER_QUERIER_PRESENT_INTERVAL (2 * IGMP_QUERY_INTERVAL + IGMP_QUERY_RESPONSE_INTERVAL / 2) +#define IGMP_STARTUP_QUERY_INTERVAL (IGMP_QUERY_INTERVAL / 4) +#define IGMP_STARTUP_QUERY_COUNT IGMP_ROBUSTNESS +// This value is 1/10 sec. RFC default is 10. Reduced to minimum to free unused channels ASAP +#define IGMP_LAST_MEMBER_QUERY_INTERVAL_TS 1 +#define IGMP_LAST_MEMBER_QUERY_COUNT IGMP_ROBUSTNESS + +// operations on struct timeval +#define TV_CMP(a, cmp, b) (a.tv_sec == b.tv_sec ? a.tv_usec cmp b.tv_usec : a.tv_sec cmp b.tv_sec) +#define TV_SET(tv) (tv.tv_sec || tv.tv_usec) +#define TV_CLR(tv) memset(&tv, 0, sizeof(tv)) +#define TV_CPY(dst, src) memcpy(&dst, &src, sizeof(dst)) +#define TV_ADD(dst, ts) dst.tv_sec += ts / 10; dst.tv_usec += (ts % 10) * 100000; if (dst.tv_usec >= 1000000) { dst.tv_usec -= 1000000; dst.tv_sec++; } + +class cMulticastGroup: public cListObject +{ +public: + cConnectionIGMP *connection; + in_addr_t group; + in_addr_t reporter; + struct timeval timeout; + struct timeval v1timer; + struct timeval retransmit; + + cMulticastGroup(in_addr_t Group); +}; + +cMulticastGroup::cMulticastGroup(in_addr_t Group) : + connection(NULL), + group(Group), + reporter(0) +{ + TV_CLR(timeout); + TV_CLR(v1timer); + TV_CLR(retransmit); +} + +void logIGMP(uint8_t type, struct in_addr Src, struct in_addr Dst, struct in_addr Grp) +{ + const char* msg; + switch (type) { + case IGMP_MEMBERSHIP_QUERY: msg = "membership query"; break; + case IGMP_V1_MEMBERSHIP_REPORT: msg = "V1 membership report"; break; + case IGMP_V2_MEMBERSHIP_REPORT: msg = "V2 membership report"; break; + case IGMP_V2_LEAVE_GROUP: msg = "leave group"; break; + default: msg = "unknown"; break; + } + char* s = strdup(inet_ntoa(Src)); + char* d = strdup(inet_ntoa(Dst)); + dsyslog("streamdev-server IGMP: Received %s from %s (dst %s) for %s", msg, s, d, inet_ntoa(Grp)); + free(s); + free(d); +} + +/* Taken from http://tools.ietf.org/html/rfc1071 */ +uint16_t inetChecksum(uint16_t *addr, int count) +{ + uint32_t sum = 0; + while (count > 1) { + sum += *addr++; + count -= 2; + } + + if( count > 0 ) + sum += * (uint8_t *) addr; + + while (sum>>16) + sum = (sum & 0xffff) + (sum >> 16); + + return ~sum; +} + +cComponentIGMP::cComponentIGMP(void): + cServerComponent("IGMP", "0.0.0.0", 0, SOCK_RAW, IPPROTO_IGMP), + cThread("IGMP timeout handler"), + m_BindIp(inet_addr(StreamdevServerSetup.IGMPBindIP)), + m_MaxChannelNumber(0), + m_StartupQueryCount(IGMP_STARTUP_QUERY_COUNT), + m_Querier(true) +{ +} + +cComponentIGMP::~cComponentIGMP(void) +{ +} + +cMulticastGroup* cComponentIGMP::FindGroup(in_addr_t Group) const +{ + cMulticastGroup *group = m_Groups.First(); + while (group && group->group != Group) + group = m_Groups.Next(group); + return group; +} + +bool cComponentIGMP::Initialize(void) +{ + if (cServerComponent::Initialize() && IGMPMembership(IGMP_ALL_ROUTER)) + { + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) + { + if (channel->GroupSep()) + continue; + int num = channel->Number(); + if (!IGMPMembership(htonl(MULTICAST_PRIV_MIN + num))) + break; + m_MaxChannelNumber = num; + } + if (m_MaxChannelNumber == 0) + { + IGMPMembership(IGMP_ALL_ROUTER, false); + esyslog("streamdev-server IGMP: no multicast group joined"); + } + else + { + Start(); + } + } + return m_MaxChannelNumber > 0; +} + +void cComponentIGMP::Destruct(void) +{ + if (m_MaxChannelNumber > 0) + { + Cancel(3); + for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) + { + if (channel->GroupSep()) + continue; + int num = channel->Number(); + if (num > m_MaxChannelNumber) + break; + IGMPMembership(htonl(MULTICAST_PRIV_MIN + num), false); + } + IGMPMembership(IGMP_ALL_ROUTER, false); + } + m_MaxChannelNumber = 0; + cServerComponent::Destruct(); +} + +cServerConnection *cComponentIGMP::NewClient(void) +{ + return new cConnectionIGMP("IGMP", StreamdevServerSetup.IGMPClientPort, (eStreamType) StreamdevServerSetup.IGMPStreamType); +} + +cServerConnection* cComponentIGMP::Accept(void) +{ + ssize_t recv_len; + int ip_hdrlen, ip_datalen; + struct ip *ip; + struct igmp *igmp; + + while ((recv_len = ::recvfrom(Socket(), m_ReadBuffer, sizeof(m_ReadBuffer), 0, NULL, NULL)) < 0 && errno == EINTR) + errno = 0; + + if (recv_len < 0) { + esyslog("streamdev-server IGMP: read failed: %m"); + return NULL; + } + else if (recv_len < (ssize_t) sizeof(struct ip)) { + esyslog("streamdev-server IGMP: IP packet too short"); + return NULL; + } + + ip = (struct ip*) m_ReadBuffer; + + // filter out my own packets + if (ip->ip_src.s_addr == m_BindIp) + return NULL; + + ip_hdrlen = ip->ip_hl << 2; +#ifdef __FreeBSD__ + ip_datalen = ip->ip_len; +#else + ip_datalen = ntohs(ip->ip_len) - ip_hdrlen; +#endif + if (ip->ip_p != IPPROTO_IGMP) { + esyslog("streamdev-server IGMP: Unexpected protocol %hhu", ip->ip_p); + return NULL; + } + if (recv_len < ip_hdrlen + IGMP_MINLEN) { + esyslog("streamdev-server IGMP: packet too short"); + return NULL; + } + igmp = (struct igmp*) (m_ReadBuffer + ip_hdrlen); + uint16_t chksum = igmp->igmp_cksum; + igmp->igmp_cksum = 0; + if (chksum != inetChecksum((uint16_t *)igmp, ip_datalen)) + { + esyslog("INVALID CHECKSUM %d %d %d %lu 0x%x 0x%x", (int) ntohs(ip->ip_len), ip_hdrlen, ip_datalen, (unsigned long int) recv_len, chksum, inetChecksum((uint16_t *)igmp, ip_datalen)); + return NULL; + } + logIGMP(igmp->igmp_type, ip->ip_src, ip->ip_dst, igmp->igmp_group); + return ProcessMessage(igmp, igmp->igmp_group.s_addr, ip->ip_src.s_addr); +} + +cServerConnection* cComponentIGMP::ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender) +{ + cServerConnection* conn = NULL; + cMulticastGroup* group; + LOCK_THREAD; + switch (Igmp->igmp_type) { + case IGMP_MEMBERSHIP_QUERY: + if (ntohl(Sender) < ntohl(m_BindIp)) + IGMPStartOtherQuerierPresentTimer(); + break; + case IGMP_V1_MEMBERSHIP_REPORT: + case IGMP_V2_MEMBERSHIP_REPORT: + group = FindGroup(Group); + if (!group) { + group = new cMulticastGroup(Group); + m_Groups.Add(group); + } + if (!group->connection) { + IGMPStartMulticast(group); + conn = group->connection; + } + IGMPStartTimer(group, Sender); + if (Igmp->igmp_type == IGMP_V1_MEMBERSHIP_REPORT) + IGMPStartV1HostTimer(group); + break; + case IGMP_V2_LEAVE_GROUP: + group = FindGroup(Group); + if (group && !TV_SET(group->v1timer)) { + if (group->reporter == Sender) { + IGMPStartTimerAfterLeave(group, m_Querier ? IGMP_LAST_MEMBER_QUERY_INTERVAL_TS : Igmp->igmp_code); + if (m_Querier) + IGMPSendGroupQuery(group); + IGMPStartRetransmitTimer(group); + } + m_CondWait.Signal(); + } + break; + default: + break; + } + return conn; +} + +void cComponentIGMP::Action() +{ + while (Running()) { + struct timeval now; + struct timeval next; + + gettimeofday(&now, NULL); + TV_CPY(next, now); + next.tv_sec += IGMP_QUERY_INTERVAL; + + cMulticastGroup *del = NULL; + { + LOCK_THREAD; + if (TV_CMP(m_GeneralQueryTimer, <, now)) { + dsyslog("General Query"); + IGMPSendGeneralQuery(); + IGMPStartGeneralQueryTimer(); + } + if (TV_CMP(next, >, m_GeneralQueryTimer)) + TV_CPY(next, m_GeneralQueryTimer); + + for (cMulticastGroup *group = m_Groups.First(); group; group = m_Groups.Next(group)) { + if (TV_CMP(group->timeout, <, now)) { + IGMPStopMulticast(group); + IGMPClearRetransmitTimer(group); + if (del) + m_Groups.Del(del); + del = group; + } + else if (m_Querier && TV_SET(group->retransmit) && TV_CMP(group->retransmit, <, now)) { + IGMPSendGroupQuery(group); + IGMPStartRetransmitTimer(group); + if (TV_CMP(next, >, group->retransmit)) + TV_CPY(next, group->retransmit); + } + else if (TV_SET(group->v1timer) && TV_CMP(group->v1timer, <, now)) { + TV_CLR(group->v1timer); + } + else { + if (TV_CMP(next, >, group->timeout)) + TV_CPY(next, group->timeout); + if (TV_SET(group->retransmit) && TV_CMP(next, >, group->retransmit)) + TV_CPY(next, group->retransmit); + if (TV_SET(group->v1timer) && TV_CMP(next, >, group->v1timer)) + TV_CPY(next, group->v1timer); + } + } + if (del) + m_Groups.Del(del); + } + + int sleep = (next.tv_sec - now.tv_sec) * 1000; + sleep += (next.tv_usec - now.tv_usec) / 1000; + if (next.tv_usec < now.tv_usec) + sleep += 1000; + dsyslog("Sleeping %d ms", sleep); + m_CondWait.Wait(sleep); + } +} + +bool cComponentIGMP::IGMPMembership(in_addr_t Group, bool Add) +{ + struct ip_mreqn mreq; + mreq.imr_multiaddr.s_addr = Group; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(Socket(), IPPROTO_IP, Add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + esyslog("streamdev-server IGMP: unable to %s %s: %m", Add ? "join" : "leave", inet_ntoa(mreq.imr_multiaddr)); + if (errno == ENOBUFS) + esyslog("consider increasing sys.net.ipv4.igmp_max_memberships"); + return false; + } + return true; +} + +void cComponentIGMP::IGMPSendQuery(in_addr_t Group, int Timeout) +{ + struct sockaddr_in dst; + struct igmp query; + + dst.sin_family = AF_INET; + dst.sin_port = IPPROTO_IGMP; + dst.sin_addr.s_addr = Group; + query.igmp_type = IGMP_MEMBERSHIP_QUERY; + query.igmp_code = Timeout * 10; + query.igmp_cksum = 0; + query.igmp_group.s_addr = (Group == IGMP_ALL_HOSTS) ? 0 : Group; + query.igmp_cksum = inetChecksum((uint16_t *) &query, sizeof(query)); + + for (int i = 0; i < 5 && ::sendto(Socket(), &query, sizeof(query), 0, (sockaddr*)&dst, sizeof(dst)) == -1; i++) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + esyslog("streamdev-server IGMP: unable to query group %s: %m", inet_ntoa(dst.sin_addr)); + break; + } + cCondWait::SleepMs(10); + } +} + +// Querier state actions +void cComponentIGMP::IGMPStartGeneralQueryTimer() +{ + m_Querier = true; + if (m_StartupQueryCount) { + gettimeofday(&m_GeneralQueryTimer, NULL); + m_GeneralQueryTimer.tv_sec += IGMP_STARTUP_QUERY_INTERVAL; + m_StartupQueryCount--; + } + else { + gettimeofday(&m_GeneralQueryTimer, NULL); + m_GeneralQueryTimer.tv_sec += IGMP_QUERY_INTERVAL; + } +} + +void cComponentIGMP::IGMPStartOtherQuerierPresentTimer() +{ + m_Querier = false; + m_StartupQueryCount = 0; + gettimeofday(&m_GeneralQueryTimer, NULL); + m_GeneralQueryTimer.tv_sec += IGMP_OTHER_QUERIER_PRESENT_INTERVAL; +} + +void cComponentIGMP::IGMPSendGeneralQuery() +{ + IGMPSendQuery(IGMP_ALL_HOSTS, IGMP_QUERY_RESPONSE_INTERVAL); +} + +// Group state actions +void cComponentIGMP::IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member) +{ + gettimeofday(&Group->timeout, NULL); + Group->timeout.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL; + TV_CLR(Group->retransmit); + Group->reporter = Member; + +} + +void cComponentIGMP::IGMPStartV1HostTimer(cMulticastGroup* Group) +{ + gettimeofday(&Group->v1timer, NULL); + Group->v1timer.tv_sec += IGMP_GROUP_MEMBERSHIP_INTERVAL; +} + +void cComponentIGMP::IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTimeTs) +{ + //Group->Update(time(NULL) + MaxResponseTime * IGMP_LAST_MEMBER_QUERY_COUNT / 10); + MaxResponseTimeTs *= IGMP_LAST_MEMBER_QUERY_COUNT; + gettimeofday(&Group->timeout, NULL); + TV_ADD(Group->timeout, MaxResponseTimeTs); + TV_CLR(Group->retransmit); + Group->reporter = 0; +} + +void cComponentIGMP::IGMPStartRetransmitTimer(cMulticastGroup* Group) +{ + gettimeofday(&Group->retransmit, NULL); + TV_ADD(Group->retransmit, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS); +} + +void cComponentIGMP::IGMPClearRetransmitTimer(cMulticastGroup* Group) +{ + TV_CLR(Group->retransmit); +} + +void cComponentIGMP::IGMPSendGroupQuery(cMulticastGroup* Group) +{ + IGMPSendQuery(Group->group, IGMP_LAST_MEMBER_QUERY_INTERVAL_TS); +} + +void cComponentIGMP::IGMPStartMulticast(cMulticastGroup* Group) +{ + in_addr_t g = ntohl(Group->group); + if (g > MULTICAST_PRIV_MIN && g <= MULTICAST_PRIV_MAX) { + cChannel *channel = Channels.GetByNumber(g - MULTICAST_PRIV_MIN); + Group->connection = (cConnectionIGMP*) NewClient(); + if (!Group->connection->Start(channel, Group->group)) { + DELETENULL(Group->connection); + } + } +} + +void cComponentIGMP::IGMPStopMulticast(cMulticastGroup* Group) +{ + if (Group->connection) + Group->connection->Stop(); +} diff --git a/plugins/streamdev/streamdev-cvs/server/componentIGMP.h b/plugins/streamdev/streamdev-cvs/server/componentIGMP.h new file mode 100644 index 0000000..09d8fde --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentIGMP.h @@ -0,0 +1,62 @@ +/* + * $Id: componentIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_IGMPSERVER_H +#define VDR_STREAMDEV_IGMPSERVER_H + +#include +#include +#include +#include "server/component.h" + +class cConnectionIGMP; +class cMulticastGroup; + +class cComponentIGMP: public cServerComponent, public cThread { +private: + char m_ReadBuffer[2048]; + cList m_Groups; + in_addr_t m_BindIp; + int m_MaxChannelNumber; + struct timeval m_GeneralQueryTimer; + int m_StartupQueryCount; + bool m_Querier; + cCondWait m_CondWait; + + cMulticastGroup* FindGroup(in_addr_t Group) const; + + /* Add or remove local host to multicast group */ + bool IGMPMembership(in_addr_t Group, bool Add = true); + void IGMPSendQuery(in_addr_t Group, int Timeout); + + cServerConnection* ProcessMessage(struct igmp *Igmp, in_addr_t Group, in_addr_t Sender); + + void IGMPStartGeneralQueryTimer(); + void IGMPStartOtherQuerierPresentTimer(); + void IGMPSendGeneralQuery(); + + void IGMPStartTimer(cMulticastGroup* Group, in_addr_t Member); + void IGMPStartV1HostTimer(cMulticastGroup* Group); + void IGMPStartTimerAfterLeave(cMulticastGroup* Group, unsigned int MaxResponseTime); + void IGMPStartRetransmitTimer(cMulticastGroup* Group); + void IGMPClearRetransmitTimer(cMulticastGroup* Group); + void IGMPSendGroupQuery(cMulticastGroup* Group); + void IGMPStartMulticast(cMulticastGroup* Group); + void IGMPStopMulticast(cMulticastGroup* Group); + + virtual void Action(); + +protected: + virtual cServerConnection *NewClient(void); + +public: + virtual bool Initialize(void); + virtual void Destruct(void); + virtual cServerConnection* Accept(void); + + cComponentIGMP(void); + ~cComponentIGMP(void); +}; + +#endif // VDR_STREAMDEV_IGMPSERVER_H diff --git a/plugins/streamdev/streamdev-cvs/server/componentVTP.c b/plugins/streamdev/streamdev-cvs/server/componentVTP.c new file mode 100644 index 0000000..ed2df1c --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentVTP.c @@ -0,0 +1,18 @@ +/* + * $Id: componentVTP.c,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ + */ + +#include "server/componentVTP.h" +#include "server/connectionVTP.h" +#include "server/setup.h" + +cComponentVTP::cComponentVTP(void): + cServerComponent("VTP", StreamdevServerSetup.VTPBindIP, + StreamdevServerSetup.VTPServerPort) +{ +} + +cServerConnection *cComponentVTP::NewClient(void) +{ + return new cConnectionVTP; +} diff --git a/plugins/streamdev/streamdev-cvs/server/componentVTP.h b/plugins/streamdev/streamdev-cvs/server/componentVTP.h new file mode 100644 index 0000000..4f61eb5 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/componentVTP.h @@ -0,0 +1,18 @@ +/* + * $Id: componentVTP.h,v 1.2 2005/05/09 20:22:29 lordjaxom Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVERS_SERVERVTP_H +#define VDR_STREAMDEV_SERVERS_SERVERVTP_H + +#include "server/component.h" + +class cComponentVTP: public cServerComponent { +protected: + virtual cServerConnection *NewClient(void); + +public: + cComponentVTP(void); +}; + +#endif // VDR_STREAMDEV_SERVERS_SERVERVTP_H diff --git a/plugins/streamdev/streamdev-cvs/server/connection.c b/plugins/streamdev/streamdev-cvs/server/connection.c new file mode 100644 index 0000000..eccb3c4 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connection.c @@ -0,0 +1,255 @@ +/* + * $Id: connection.c,v 1.13 2009/09/18 10:43:26 schmirl Exp $ + */ + +#include "server/connection.h" +#include "server/setup.h" +#include "server/suspend.h" +#include "common.h" + +#include +#include +#include +#include + +cServerConnection::cServerConnection(const char *Protocol, int Type): + cTBSocket(Type), + m_Protocol(Protocol), + m_DeferClose(false), + m_Pending(false), + m_ReadBytes(0), + m_WriteBytes(0), + m_WriteIndex(0) +{ +} + +cServerConnection::~cServerConnection() +{ +} + +const cChannel* cServerConnection::ChannelFromString(const char *String, int *Apid) { + const cChannel *channel = NULL; + char *string = strdup(String); + char *ptr, *end; + int apididx = 0; + + if ((ptr = strrchr(string, '+')) != NULL) { + *(ptr++) = '\0'; + apididx = strtoul(ptr, &end, 10); + Dprintf("found apididx: %d\n", apididx); + } + + if (isnumber(string)) { + int temp = strtol(String, NULL, 10); + if (temp >= 1 && temp <= Channels.MaxNumber()) + channel = Channels.GetByNumber(temp); + } else { + channel = Channels.GetByChannelID(tChannelID::FromString(string)); + + if (channel == NULL) { + int i = 1; + while ((channel = Channels.GetByNumber(i, 1)) != NULL) { + if (String == channel->Name()) + break; + + i = channel->Number() + 1; + } + } + } + + if (channel != NULL && apididx > 0) { + int apid = 0, index = 1; + + for (int i = 0; channel->Apid(i) != 0; ++i, ++index) { + if (index == apididx) { + apid = channel->Apid(i); + break; + } + } + + if (apid == 0) { + for (int i = 0; channel->Dpid(i) != 0; ++i, ++index) { + if (index == apididx) { + apid = channel->Dpid(i); + break; + } + } + } + + if (Apid != NULL) + *Apid = apid; + } + + free(string); + return channel; +} + +bool cServerConnection::Read(void) +{ + int b; + if ((b = cTBSocket::Read(m_ReadBuffer + m_ReadBytes, + sizeof(m_ReadBuffer) - m_ReadBytes - 1)) < 0) { + esyslog("ERROR: read from client (%s) %s:%d failed: %m", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + if (b == 0) { + isyslog("client (%s) %s:%d has closed connection", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + m_ReadBytes += b; + m_ReadBuffer[m_ReadBytes] = '\0'; + + char *end; + bool result = true; + while ((end = strchr(m_ReadBuffer, '\012')) != NULL) { + *end = '\0'; + if (end > m_ReadBuffer && *(end - 1) == '\015') + *(end - 1) = '\0'; + + if (!Command(m_ReadBuffer)) + return false; + + m_ReadBytes -= ++end - m_ReadBuffer; + if (m_ReadBytes > 0) + memmove(m_ReadBuffer, end, m_ReadBytes); + } + + if (m_ReadBytes == sizeof(m_ReadBuffer) - 1) { + esyslog("ERROR: streamdev: input buffer overflow (%s) for %s:%d", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + return result; +} + +bool cServerConnection::Write(void) +{ + int b; + if ((b = cTBSocket::Write(m_WriteBuffer + m_WriteIndex, + m_WriteBytes - m_WriteIndex)) < 0) { + esyslog("ERROR: streamdev: write to client (%s) %s:%d failed: %m", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + m_WriteIndex += b; + if (m_WriteIndex == m_WriteBytes) { + m_WriteIndex = 0; + m_WriteBytes = 0; + if (m_Pending) + Command(NULL); + if (m_DeferClose) + return false; + Flushed(); + } + return true; +} + +bool cServerConnection::Respond(const char *Message, bool Last, ...) +{ + char *buffer; + int length; + va_list ap; + va_start(ap, Last); + length = vasprintf(&buffer, Message, ap); + va_end(ap); + + if (length < 0) { + esyslog("ERROR: streamdev: buffer allocation failed (%s) for %s:%d", + m_Protocol, RemoteIp().c_str(), RemotePort()); + return false; + } + + if (m_WriteBytes + length + 2 > sizeof(m_WriteBuffer)) { + esyslog("ERROR: streamdev: output buffer overflow (%s) for %s:%d", + m_Protocol, RemoteIp().c_str(), RemotePort()); + free(buffer); + return false; + } + Dprintf("OUT: |%s|\n", buffer); + memcpy(m_WriteBuffer + m_WriteBytes, buffer, length); + free(buffer); + + m_WriteBytes += length; + m_WriteBuffer[m_WriteBytes++] = '\015'; + m_WriteBuffer[m_WriteBytes++] = '\012'; + m_Pending = !Last; + return true; +} + +cDevice *cServerConnection::GetDevice(const cChannel *Channel, int Priority) +{ + cDevice *device = NULL; + + /*Dprintf("+ Statistics:\n"); + Dprintf("+ Current Channel: %d\n", cDevice::CurrentChannel()); + Dprintf("+ Current Device: %d\n", cDevice::ActualDevice()->CardIndex()); + Dprintf("+ Transfer Mode: %s\n", cDevice::ActualDevice() + == cDevice::PrimaryDevice() ? "false" : "true"); + Dprintf("+ Replaying: %s\n", cDevice::PrimaryDevice()->Replaying() ? "true" + : "false");*/ + + Dprintf(" * GetDevice(const cChannel*, int)\n"); + Dprintf(" * -------------------------------\n"); + + device = cDevice::GetDevice(Channel, Priority, false); + + Dprintf(" * Found following device: %p (%d)\n", device, + device ? device->CardIndex() + 1 : 0); + if (device == cDevice::ActualDevice()) + Dprintf(" * is actual device\n"); + if (!cSuspendCtl::IsActive() && StreamdevServerSetup.SuspendMode != smAlways) + Dprintf(" * NOT suspended\n"); + + if (!device || (device == cDevice::ActualDevice() + && !cSuspendCtl::IsActive() + && StreamdevServerSetup.SuspendMode != smAlways)) { + // mustn't switch actual device + // maybe a device would be free if THIS connection did turn off its streams? + Dprintf(" * trying again...\n"); + const cChannel *current = Channels.GetByNumber(cDevice::CurrentChannel()); + isyslog("streamdev-server: Detaching current receiver"); + Detach(); + device = cDevice::GetDevice(Channel, Priority, false); + Attach(); + Dprintf(" * Found following device: %p (%d)\n", device, + device ? device->CardIndex() + 1 : 0); + if (device == cDevice::ActualDevice()) + Dprintf(" * is actual device\n"); + if (!cSuspendCtl::IsActive() + && StreamdevServerSetup.SuspendMode != smAlways) + Dprintf(" * NOT suspended\n"); + if (current && !TRANSPONDER(Channel, current)) + Dprintf(" * NOT same transponder\n"); + if (device && (device == cDevice::ActualDevice() + && !cSuspendCtl::IsActive() + && StreamdevServerSetup.SuspendMode != smAlways + && current != NULL + && !TRANSPONDER(Channel, current))) { + // now we would have to switch away live tv...let's see if live tv + // can be handled by another device + cDevice *newdev = NULL; + for (int i = 0; i < cDevice::NumDevices(); ++i) { + cDevice *dev = cDevice::GetDevice(i); + if (dev->ProvidesChannel(current, 0) && dev != device) { + newdev = dev; + break; + } + } + Dprintf(" * Found device for live tv: %p (%d)\n", newdev, + newdev ? newdev->CardIndex() + 1 : 0); + if (newdev == NULL || newdev == device) + // no suitable device to continue live TV, giving up... + device = NULL; + else + newdev->SwitchChannel(current, true); + } + } + + return device; +} diff --git a/plugins/streamdev/streamdev-cvs/server/connection.h b/plugins/streamdev/streamdev-cvs/server/connection.h new file mode 100644 index 0000000..73cb3d5 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connection.h @@ -0,0 +1,99 @@ +/* + * $Id: connection.h,v 1.8 2009/09/18 10:43:26 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H +#define VDR_STREAMDEV_SERVER_CONNECTION_H + +#include "tools/socket.h" +#include "common.h" + +class cChannel; +class cDevice; + +/* Basic capabilities of a straight text-based protocol, most functions + virtual to support more complicated protocols */ + +class cServerConnection: public cListObject, public cTBSocket +{ +private: + const char *m_Protocol; + bool m_DeferClose; + bool m_Pending; + + char m_ReadBuffer[MAXPARSEBUFFER]; + uint m_ReadBytes; + + char m_WriteBuffer[MAXPARSEBUFFER]; + uint m_WriteBytes; + uint m_WriteIndex; + +protected: + /* Will be called when a command terminated by a newline has been + received */ + virtual bool Command(char *Cmd) = 0; + + /* Will put Message into the response queue, which will be sent in the next + server cycle. Note that Message will be line-terminated by Respond. + Only one line at a time may be sent. If there are lines to follow, set + Last to false. Command(NULL) will be called in the next cycle, so you can + post the next line. */ + virtual bool Respond(const char *Message, bool Last = true, ...); + //__attribute__ ((format (printf, 2, 4))); + + static const cChannel *ChannelFromString(const char *String, int *Apid = NULL); + +public: + /* If you derive, specify a short string such as HTTP for Protocol, which + will be displayed in error messages */ + cServerConnection(const char *Protocol, int Type = SOCK_STREAM); + virtual ~cServerConnection(); + + /* If true, any client IP will be accepted */ + virtual bool CanAuthenticate(void) { return false; } + + /* Gets called if the client has been accepted by the core */ + virtual void Welcome(void) { } + + /* Gets called if the client has been rejected by the core */ + virtual void Reject(void) { DeferClose(); } + + /* Get the client socket's file number */ + virtual int Socket(void) const { return (int)*this; } + + /* Determine if there is data to send or any command pending */ + virtual bool HasData(void) const; + + /* Gets called by server when the socket can accept more data. Writes + the buffer filled up by Respond(). Calls Command(NULL) if there is a + command pending. Returns false in case of an error */ + virtual bool Write(void); + + /* Gets called by server when there is incoming data to read. Calls + Command() for each line. Returns false in case of an error, or if + the connection shall be closed and removed by the server */ + virtual bool Read(void); + + /* Is polled regularely by the server. Returns true if the connection + needs to be terminated. */ + virtual bool Abort(void) const = 0; + + /* Will make the socket close after sending all queued output data */ + void DeferClose(void) { m_DeferClose = true; } + + /* Will retrieve an unused device for transmitting data. Use the returned + cDevice in a following call to StartTransfer */ + cDevice *GetDevice(const cChannel *Channel, int Priority); + + virtual void Flushed(void) {} + + virtual void Detach(void) = 0; + virtual void Attach(void) = 0; +}; + +inline bool cServerConnection::HasData(void) const +{ + return m_WriteBytes > 0 || m_Pending || m_DeferClose; +} + +#endif // VDR_STREAMDEV_SERVER_CONNECTION_H diff --git a/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c new file mode 100644 index 0000000..83e568d --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.c @@ -0,0 +1,296 @@ +/* + * $Id: connectionHTTP.c,v 1.17 2009/06/19 06:32:45 schmirl Exp $ + */ + +#include + +#include "server/connectionHTTP.h" +#include "server/menuHTTP.h" +#include "server/server.h" +#include "server/setup.h" + +cConnectionHTTP::cConnectionHTTP(void): + cServerConnection("HTTP"), + m_Status(hsRequest), + m_LiveStreamer(NULL), + m_StreamerParameter(""), + m_Channel(NULL), + m_Apid(0), + m_StreamType((eStreamType)StreamdevServerSetup.HTTPStreamType), + m_ChannelList(NULL) +{ + Dprintf("constructor hsRequest\n"); +} + +cConnectionHTTP::~cConnectionHTTP() +{ + delete m_LiveStreamer; +} + +bool cConnectionHTTP::CanAuthenticate(void) +{ + return opt_auth != NULL; +} + +bool cConnectionHTTP::Command(char *Cmd) +{ + Dprintf("command %s\n", Cmd); + switch (m_Status) { + case hsRequest: + Dprintf("Request\n"); + m_Request = Cmd; + m_Status = hsHeaders; + return true; + + case hsHeaders: + if (*Cmd == '\0') { + m_Status = hsBody; + return ProcessRequest(); + } + if (strncasecmp(Cmd, "Host:", 5) == 0) { + Dprintf("Host-Header\n"); + m_Host = (std::string) skipspace(Cmd + 5); + return true; + } + else if (strncasecmp(Cmd, "Authorization:", 14) == 0) { + Cmd = skipspace(Cmd + 14); + if (strncasecmp(Cmd, "Basic", 5) == 0) { + Dprintf("'Authorization Basic'-Header\n"); + m_Authorization = (std::string) skipspace(Cmd + 5); + return true; + } + } + Dprintf("header\n"); + return true; + default: + // skip additional blank lines + if (*Cmd == '\0') + return true; + break; + } + return false; // ??? shouldn't happen +} + +bool cConnectionHTTP::ProcessRequest(void) +{ + Dprintf("process\n"); + if (!StreamdevHosts.Acceptable(RemoteIpAddr())) + { + if (!opt_auth || m_Authorization.empty() || m_Authorization.compare(opt_auth) != 0) { + isyslog("streamdev-server: HTTP authorization required"); + DeferClose(); + return Respond("HTTP/1.0 401 Authorization Required") + && Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")") + && Respond(""); + } + } + if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) { + switch (m_Job) { + case hjListing: + if (m_ChannelList) + return Respond("%s", true, m_ChannelList->HttpHeader().c_str()); + break; + + case hjTransfer: + if (m_Channel == NULL) { + DeferClose(); + return Respond("HTTP/1.0 404 not found"); + } + + m_LiveStreamer = new cStreamdevLiveStreamer(0, m_StreamerParameter); + cDevice *device = GetDevice(m_Channel, 0); + if (device != NULL) { + device->SwitchChannel(m_Channel, false); + if (m_LiveStreamer->SetChannel(m_Channel, m_StreamType, m_Apid)) { + m_LiveStreamer->SetDevice(device); + if (!SetDSCP()) + LOG_ERROR_STR("unable to set DSCP sockopt"); + if (m_StreamType == stES && (m_Apid != 0 || ISRADIO(m_Channel))) { + return Respond("HTTP/1.0 200 OK") + && Respond("Content-Type: audio/mpeg") + && Respond("icy-name: %s", true, m_Channel->Name()) + && Respond(""); + } else { + return Respond("HTTP/1.0 200 OK") + && Respond("Content-Type: video/mpeg") + && Respond(""); + } + } + } + DELETENULL(m_LiveStreamer); + DeferClose(); + return Respond("HTTP/1.0 409 Channel not available") + && Respond(""); + } + } + + DeferClose(); + return Respond("HTTP/1.0 400 Bad Request") + && Respond(""); +} + +void cConnectionHTTP::Flushed(void) +{ + std::string line; + + if (m_Status != hsBody) + return; + + switch (m_Job) { + case hjListing: + if (m_ChannelList) { + if (m_ChannelList->HasNext()) { + if (!Respond("%s", true, m_ChannelList->Next().c_str())) + DeferClose(); + } + else { + DELETENULL(m_ChannelList); + m_Status = hsFinished; + DeferClose(); + } + return; + } + // should never be reached + esyslog("streamdev-server cConnectionHTTP::Flushed(): no channel list"); + m_Status = hsFinished; + break; + + case hjTransfer: + Dprintf("streamer start\n"); + m_LiveStreamer->Start(this); + m_Status = hsFinished; + break; + } +} + +bool cConnectionHTTP::CmdGET(const std::string &Opts) +{ + const char *ptr, *sp, *pp, *fp, *xp, *qp, *ep; + const cChannel *chan; + int apid = 0; + + ptr = Opts.c_str(); + + // find begin of URL + sp = skipspace(ptr); + // find end of URL (\0 or first space character) + for (ep = sp; *ep && !isspace(*ep); ep++) + ; + // find begin of query string (first ?) + for (qp = sp; qp < ep && *qp != '?'; qp++) + ; + // find begin of filename (last /) + for (fp = qp; fp > sp && *fp != '/'; --fp) + ; + // find begin of section params (first ;) + for (pp = sp; pp < fp && *pp != ';'; pp++) + ; + // find filename extension (first .) + for (xp = fp; xp < qp && *xp != '.'; xp++) + ; + if (qp - xp > 5) // too long for a filename extension + xp = qp; + + std::string type, filespec, fileext, query; + // Streamtype with leading / stripped off + if (pp > sp) + type = Opts.substr(sp - ptr + 1, pp - sp - 1); + // Section parameters with leading ; stripped off + if (fp > pp) + m_StreamerParameter = Opts.substr(pp - ptr + 1, fp - pp - 1); + // file basename with leading / stripped off + if (xp > fp) + filespec = Opts.substr(fp - ptr + 1, xp - fp - 1); + // file extension including leading . + fileext = Opts.substr(xp - ptr, qp - xp); + // query string including leading ? + query = Opts.substr(qp - ptr, ep - qp); + + Dprintf("before channelfromstring: type(%s) param(%s) filespec(%s) fileext(%s) query(%s)\n", type.c_str(), m_StreamerParameter.c_str(), filespec.c_str(), fileext.c_str(), query.c_str()); + + const char* pType = type.c_str(); + if (strcasecmp(pType, "PS") == 0) { + m_StreamType = stPS; + } else if (strcasecmp(pType, "PES") == 0) { + m_StreamType = stPES; + } else if (strcasecmp(pType, "TS") == 0) { + m_StreamType = stTS; + } else if (strcasecmp(pType, "ES") == 0) { + m_StreamType = stES; + } else if (strcasecmp(pType, "Extern") == 0) { + m_StreamType = stExtern; + } + + std::string groupTarget; + cChannelIterator *iterator = NULL; + + if (filespec.compare("tree") == 0) { + const cChannel* c = NULL; + size_t groupIndex = query.find("group="); + if (groupIndex != std::string::npos) + c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6)); + iterator = new cListTree(c); + groupTarget = filespec + fileext; + } else if (filespec.compare("groups") == 0) { + iterator = new cListGroups(); + groupTarget = (std::string) "group" + fileext; + } else if (filespec.compare("group") == 0) { + const cChannel* c = NULL; + size_t groupIndex = query.find("group="); + if (groupIndex != std::string::npos) + c = cChannelList::GetGroup(atoi(query.c_str() + groupIndex + 6)); + iterator = new cListGroup(c); + } else if (filespec.compare("channels") == 0) { + iterator = new cListChannels(); + } else if (filespec.compare("all") == 0 || + (filespec.empty() && fileext.empty())) { + iterator = new cListAll(); + } + + if (iterator) { + if (filespec.empty() || fileext.compare(".htm") == 0 || fileext.compare(".html") == 0) { + m_ChannelList = new cHtmlChannelList(iterator, m_StreamType, (filespec + fileext + query).c_str(), groupTarget.c_str()); + m_Job = hjListing; + } else if (fileext.compare(".m3u") == 0) { + std::string base; + if (*(m_Host.c_str())) + base = "http://" + m_Host + "/"; + else + base = (std::string) "http://" + LocalIp() + ":" + + (const char*) itoa(StreamdevServerSetup.HTTPServerPort) + "/"; + if (type.empty()) + { + switch (m_StreamType) + { + case stTS: base += "TS/"; break; + case stPS: base += "PS/"; break; + case stPES: base += "PES/"; break; + case stES: base += "ES/"; break; + case stExtern: base += "Extern/"; break; + default: break; + + } + } else { + base += type; + if (!m_StreamerParameter.empty()) + base += ";" + m_StreamerParameter; + base += "/"; + } + m_ChannelList = new cM3uChannelList(iterator, base.c_str()); + m_Job = hjListing; + } else { + delete iterator; + return false; + } + } else if ((chan = ChannelFromString(filespec.c_str(), &apid)) != NULL) { + m_Channel = chan; + m_Apid = apid; + Dprintf("Apid is %d\n", apid); + m_Job = hjTransfer; + } else + return false; + + Dprintf("after channelfromstring\n"); + return true; +} + diff --git a/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h new file mode 100644 index 0000000..0548959 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionHTTP.h @@ -0,0 +1,70 @@ +/* + * $Id: connectionHTTP.h,v 1.6 2008/10/14 11:05:48 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H +#define VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H + +#include "connection.h" +#include "server/livestreamer.h" + +#include + +class cChannel; +class cStreamdevLiveStreamer; +class cChannelList; + +class cConnectionHTTP: public cServerConnection { +private: + enum eHTTPStatus { + hsRequest, + hsHeaders, + hsBody, + hsFinished, + }; + + enum eHTTPJob { + hjTransfer, + hjListing, + }; + + std::string m_Request; + std::string m_Host; + std::string m_Authorization; + //std::map m_Headers; TODO: later? + eHTTPStatus m_Status; + eHTTPJob m_Job; + // job: transfer + cStreamdevLiveStreamer *m_LiveStreamer; + std::string m_StreamerParameter; + const cChannel *m_Channel; + int m_Apid; + eStreamType m_StreamType; + // job: listing + cChannelList *m_ChannelList; + +protected: + bool ProcessRequest(void); + +public: + cConnectionHTTP(void); + virtual ~cConnectionHTTP(); + + virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } + virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } + + virtual bool CanAuthenticate(void); + + virtual bool Command(char *Cmd); + bool CmdGET(const std::string &Opts); + + virtual bool Abort(void) const; + virtual void Flushed(void); +}; + +inline bool cConnectionHTTP::Abort(void) const +{ + return m_LiveStreamer && m_LiveStreamer->Abort(); +} + +#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H diff --git a/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c new file mode 100644 index 0000000..dc08798 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.c @@ -0,0 +1,64 @@ +/* + * $Id: connectionIGMP.c,v 1.1 2009/02/13 10:39:22 schmirl Exp $ + */ + +#include + +#include "server/connectionIGMP.h" +#include "server/server.h" +#include "server/setup.h" +#include + +cConnectionIGMP::cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType) : + cServerConnection(Name, SOCK_DGRAM), + m_LiveStreamer(NULL), + m_ClientPort(ClientPort), + m_StreamType(StreamType) +{ +} + +cConnectionIGMP::~cConnectionIGMP() +{ + delete m_LiveStreamer; +} + +bool cConnectionIGMP::Start(cChannel *Channel, in_addr_t Dst) +{ + if (Channel != NULL) { + cDevice *device = GetDevice(Channel, 0); + if (device != NULL) { + device->SwitchChannel(Channel, false); + struct in_addr ip; + ip.s_addr = Dst; + if (Connect(inet_ntoa(ip), m_ClientPort)) { + m_LiveStreamer = new cStreamdevLiveStreamer(0); + if (m_LiveStreamer->SetChannel(Channel, m_StreamType)) { + m_LiveStreamer->SetDevice(device); + if (!SetDSCP()) + LOG_ERROR_STR("unable to set DSCP sockopt"); + Dprintf("streamer start\n"); + m_LiveStreamer->Start(this); + return true; + } + else + esyslog("streamdev-server IGMP: SetDevice failed"); + DELETENULL(m_LiveStreamer); + } + else + esyslog("streamdev-server IGMP: Connect failed: %m"); + } + else + esyslog("streamdev-server IGMP: GetDevice failed"); + } + else + esyslog("streamdev-server IGMP: Channel not found"); + return false; +} + +void cConnectionIGMP::Stop() +{ + if (m_LiveStreamer) { + m_LiveStreamer->Stop(); + DELETENULL(m_LiveStreamer); + } +} diff --git a/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h new file mode 100644 index 0000000..90abd58 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionIGMP.h @@ -0,0 +1,45 @@ +/* + * $Id: connectionIGMP.h,v 1.1 2009/02/13 10:39:22 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H +#define VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H + +#include "connection.h" +#include "server/livestreamer.h" + +#include + +#define MULTICAST_PRIV_MIN ((uint32_t) 0xefff0000) +#define MULTICAST_PRIV_MAX ((uint32_t) 0xeffffeff) + +class cStreamdevLiveStreamer; + +class cConnectionIGMP: public cServerConnection { +private: + cStreamdevLiveStreamer *m_LiveStreamer; + int m_ClientPort; + eStreamType m_StreamType; + +public: + cConnectionIGMP(const char* Name, int ClientPort, eStreamType StreamType); + virtual ~cConnectionIGMP(); + + bool Start(cChannel *Channel, in_addr_t Dst); + void Stop(); + + /* Not used here */ + virtual bool Command(char *Cmd) { return false; } + + virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } + virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } + + virtual bool Abort(void) const; +}; + +inline bool cConnectionIGMP::Abort(void) const +{ + return !m_LiveStreamer || m_LiveStreamer->Abort(); +} + +#endif // VDR_STREAMDEV_SERVERS_CONNECTIONIGMP_H diff --git a/plugins/streamdev/streamdev-cvs/server/connectionVTP.c b/plugins/streamdev/streamdev-cvs/server/connectionVTP.c new file mode 100644 index 0000000..7ff60e5 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionVTP.c @@ -0,0 +1,1768 @@ +/* + * $Id: connectionVTP.c,v 1.27 2010/01/29 12:03:02 schmirl Exp $ + */ + +#include "server/connectionVTP.h" +#include "server/livestreamer.h" +#include "server/suspend.h" +#include "setup.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* VTP Response codes: + 220: Service ready + 221: Service closing connection + 451: Requested action aborted: try again + 500: Syntax error or Command unrecognized + 501: Wrong parameters or missing parameters + 550: Requested action not taken + 551: Data connection not accepted + 560: Channel not available currently + 561: Capability not known + 562: Pid not available currently + 563: Recording not available (currently?) +*/ + +enum eDumpModeStreamdev { dmsdAll, dmsdPresent, dmsdFollowing, dmsdAtTime, dmsdFromToTime }; + +// --- cLSTEHandler ----------------------------------------------------------- + +class cLSTEHandler +{ +private: +#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) + enum eStates { Channel, Event, Title, Subtitle, Description, Vps, Content, + EndEvent, EndChannel, EndEPG }; +#elif APIVERSNUM >= 10711 + enum eStates { Channel, Event, Title, Subtitle, Description, Vps, Content, Rating, + EndEvent, EndChannel, EndEPG }; +#else + enum eStates { Channel, Event, Title, Subtitle, Description, Vps, + EndEvent, EndChannel, EndEPG }; +#endif /* PARENTALRATING */ + cConnectionVTP *m_Client; + cSchedulesLock *m_SchedulesLock; + const cSchedules *m_Schedules; + const cSchedule *m_Schedule; + const cEvent *m_Event; + int m_Errno; + cString m_Error; + eStates m_State; + bool m_Traverse; + time_t m_ToTime; +public: + cLSTEHandler(cConnectionVTP *Client, const char *Option); + ~cLSTEHandler(); + bool Next(bool &Last); +}; + +cLSTEHandler::cLSTEHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_SchedulesLock(new cSchedulesLock(false, 500)), + m_Schedules(cSchedules::Schedules(*m_SchedulesLock)), + m_Schedule(NULL), + m_Event(NULL), + m_Errno(0), + m_State(Channel), + m_Traverse(false), + m_ToTime(0) +{ + eDumpModeStreamdev dumpmode = dmsdAll; + time_t attime = 0; + time_t fromtime = 0; + + if (m_Schedules != NULL && *Option) { + char buf[strlen(Option) + 1]; + strcpy(buf, Option); + const char *delim = " \t"; + char *strtok_next; + char *p = strtok_r(buf, delim, &strtok_next); + while (p && dumpmode == dmsdAll) { + if (strcasecmp(p, "NOW") == 0) + dumpmode = dmsdPresent; + else if (strcasecmp(p, "NEXT") == 0) + dumpmode = dmsdFollowing; + else if (strcasecmp(p, "AT") == 0) { + dumpmode = dmsdAtTime; + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (isnumber(p)) + attime = strtol(p, NULL, 10); + else { + m_Errno = 501; + m_Error = "Invalid time"; + break; + } + } else { + m_Errno = 501; + m_Error = "Missing time"; + break; + } + } + else if (strcasecmp(p, "FROM") == 0) { + dumpmode = dmsdFromToTime; + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (isnumber(p)) + fromtime = strtol(p, NULL, 10); + else { + m_Errno = 501; + m_Error = "Invalid time"; + break; + } + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (strcasecmp(p, "TO") == 0) { + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (isnumber(p)) + m_ToTime = strtol(p, NULL, 10); + else { + m_Errno = 501; + m_Error = "Invalid time"; + break; + } + } else { + m_Errno = 501; + m_Error = "Missing time"; + break; + } + } + } + } else { + m_Errno = 501; + m_Error = "Missing time"; + break; + } + } else if (!m_Schedule) { + cChannel* Channel = NULL; + if (isnumber(p)) + Channel = Channels.GetByNumber(strtol(Option, NULL, 10)); + else + Channel = Channels.GetByChannelID(tChannelID::FromString( + Option)); + if (Channel) { + m_Schedule = m_Schedules->GetSchedule(Channel->GetChannelID()); + if (!m_Schedule) { + m_Errno = 550; + m_Error = "No schedule found"; + break; + } + } else { + m_Errno = 550; + m_Error = cString::sprintf("Channel \"%s\" not defined", p); + break; + } + } else { + m_Errno = 501; + m_Error = cString::sprintf("Unknown option: \"%s\"", p); + break; + } + p = strtok_r(NULL, delim, &strtok_next); + } + } else if (m_Schedules == NULL) { + m_Errno = 451; + m_Error = "EPG data is being modified, try again"; + } + + if (*m_Error == NULL) { + if (m_Schedule != NULL) + m_Schedules = NULL; + else if (m_Schedules != NULL) + m_Schedule = m_Schedules->First(); + + if (m_Schedule != NULL && m_Schedule->Events() != NULL) { + switch (dumpmode) { + case dmsdAll: m_Event = m_Schedule->Events()->First(); + m_Traverse = true; + break; + case dmsdPresent: m_Event = m_Schedule->GetPresentEvent(); + break; + case dmsdFollowing: m_Event = m_Schedule->GetFollowingEvent(); + break; + case dmsdAtTime: m_Event = m_Schedule->GetEventAround(attime); + break; + case dmsdFromToTime: + if (m_Schedule->Events()->Count() <= 1) { + m_Event = m_Schedule->Events()->First(); + break; + } + if (fromtime < m_Schedule->Events()->First()->StartTime()) { + fromtime = m_Schedule->Events()->First()->StartTime(); + } + if (m_ToTime > m_Schedule->Events()->Last()->EndTime()) { + m_ToTime = m_Schedule->Events()->Last()->EndTime(); + } + m_Event = m_Schedule->GetEventAround(fromtime); + m_Traverse = true; + break; + } + } + } +} + +cLSTEHandler::~cLSTEHandler() +{ + delete m_SchedulesLock; +} + +bool cLSTEHandler::Next(bool &Last) +{ + if (*m_Error != NULL) { + Last = true; + cString str(m_Error); + m_Error = NULL; + return m_Client->Respond(m_Errno, "%s", *str); + } + + Last = false; + switch (m_State) { + case Channel: + if (m_Schedule != NULL) { + cChannel *channel = Channels.GetByChannelID(m_Schedule->ChannelID(), + true); + if (channel != NULL) { + m_State = Event; + return m_Client->Respond(-215, "C %s %s", + *channel->GetChannelID().ToString(), + channel->Name()); + } else { + esyslog("ERROR: vdr streamdev: unable to find channel %s by ID", + *m_Schedule->ChannelID().ToString()); + m_State = EndChannel; + return Next(Last); + } + } else { + m_State = EndEPG; + return Next(Last); + } + break; + + case Event: + if (m_Event != NULL) { + m_State = Title; +#ifdef __FreeBSD__ + return m_Client->Respond(-215, "E %u %d %d %X", m_Event->EventID(), +#else + return m_Client->Respond(-215, "E %u %ld %d %X", m_Event->EventID(), +#endif + m_Event->StartTime(), m_Event->Duration(), + m_Event->TableID()); + } else { + m_State = EndChannel; + return Next(Last); + } + break; + + case Title: + m_State = Subtitle; + if (!isempty(m_Event->Title())) + return m_Client->Respond(-215, "T %s", m_Event->Title()); + else + return Next(Last); + break; + + case Subtitle: + m_State = Description; + if (!isempty(m_Event->ShortText())) + return m_Client->Respond(-215, "S %s", m_Event->ShortText()); + else + return Next(Last); + break; + + case Description: + m_State = Vps; + if (!isempty(m_Event->Description())) { + char *copy = strdup(m_Event->Description()); + cString cpy(copy, true); + strreplace(copy, '\n', '|'); + return m_Client->Respond(-215, "D %s", copy); + } else + return Next(Last); + break; + + case Vps: +#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) || APIVERSNUM >= 10711 + m_State = Content; +#else + m_State = EndEvent; +#endif /* PARENTALRATING */ + if (m_Event->Vps()) +#ifdef __FreeBSD__ + return m_Client->Respond(-215, "V %d", m_Event->Vps()); +#else + return m_Client->Respond(-215, "V %ld", m_Event->Vps()); +#endif + else + return Next(Last); + break; + +#if defined(USE_PARENTALRATING) || defined(PARENTALRATINGCONTENTVERSNUM) + case Content: + m_State = EndEvent; + if (!isempty(m_Event->GetContentsString())) { + char *copy = strdup(m_Event->GetContentsString()); + cString cpy(copy, true); + strreplace(copy, '\n', '|'); + return m_Client->Respond(-215, "G %i %i %s", m_Event->Contents() & 0xF0, m_Event->Contents() & 0x0F, copy); + } else + return Next(Last); + break; +#elif APIVERSNUM >= 10711 + case Content: + m_State = Rating; + if (!isempty(m_Event->ContentToString(m_Event->Contents()))) { + char *copy = strdup(m_Event->ContentToString(m_Event->Contents())); + cString cpy(copy, true); + strreplace(copy, '\n', '|'); + return m_Client->Respond(-215, "G %i %i %s", m_Event->Contents() & 0xF0, m_Event->Contents() & 0x0F, copy); + } else + return Next(Last); + break; + + case Rating: + m_State = EndEvent; + if (m_Event->ParentalRating()) + return m_Client->Respond(-215, "R %d", m_Event->ParentalRating()); + else + return Next(Last); + break; +#endif + + case EndEvent: + if (m_Traverse) { + m_Event = m_Schedule->Events()->Next(m_Event); + if ((m_Event != NULL) && (m_ToTime != 0) && (m_Event->StartTime() > m_ToTime)) { + m_Event = NULL; + } + } + else + m_Event = NULL; + + if (m_Event != NULL) + m_State = Event; + else + m_State = EndChannel; + + return m_Client->Respond(-215, "e"); + + case EndChannel: + if (m_Schedules != NULL) { + m_Schedule = m_Schedules->Next(m_Schedule); + if (m_Schedule != NULL) { + if (m_Schedule->Events() != NULL) + m_Event = m_Schedule->Events()->First(); + m_State = Channel; + } + } + + if (m_Schedules == NULL || m_Schedule == NULL) + m_State = EndEPG; + + return m_Client->Respond(-215, "c"); + + case EndEPG: + Last = true; + return m_Client->Respond(215, "End of EPG data"); + } + return false; +} + +// --- cLSTCHandler ----------------------------------------------------------- + +class cLSTCHandler +{ +private: + cConnectionVTP *m_Client; + const cChannel *m_Channel; + char *m_Option; + int m_Errno; + cString m_Error; + bool m_Traverse; +public: + cLSTCHandler(cConnectionVTP *Client, const char *Option); + ~cLSTCHandler(); + bool Next(bool &Last); +}; + +cLSTCHandler::cLSTCHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_Channel(NULL), + m_Option(NULL), + m_Errno(0), + m_Traverse(false) +{ + if (!Channels.Lock(false, 500)) { + m_Errno = 451; + m_Error = "Channels are being modified - try again"; + } else if (*Option) { + if (isnumber(Option)) { + m_Channel = Channels.GetByNumber(strtol(Option, NULL, 10)); + if (m_Channel == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Channel \"%s\" not defined", Option); + return; + } + } else { + int i = 1; + m_Traverse = true; + m_Option = strdup(Option); + while (i <= Channels.MaxNumber()) { + m_Channel = Channels.GetByNumber(i, 1); + if (strcasestr(m_Channel->Name(), Option) != NULL) + break; + i = m_Channel->Number() + 1; + } + + if (i > Channels.MaxNumber()) { + m_Errno = 501; + m_Error = cString::sprintf("Channel \"%s\" not defined", Option); + return; + } + } + } else if (Channels.MaxNumber() >= 1) { + m_Channel = Channels.GetByNumber(1, 1); + m_Traverse = true; + } else { + m_Errno = 550; + m_Error = "No channels defined"; + } +} + +cLSTCHandler::~cLSTCHandler() +{ + Channels.Unlock(); + if (m_Option != NULL) + free(m_Option); +} + +bool cLSTCHandler::Next(bool &Last) +{ + if (*m_Error != NULL) { + Last = true; + cString str(m_Error); + m_Error = NULL; + return m_Client->Respond(m_Errno, "%s", *str); + } + + int number; + char *buffer; + + number = m_Channel->Number(); + buffer = strdup(*m_Channel->ToText()); + buffer[strlen(buffer) - 1] = '\0'; // remove \n + cString str(buffer, true); + + Last = true; + if (m_Traverse) { + int i = m_Channel->Number() + 1; + while (i <= Channels.MaxNumber()) { + m_Channel = Channels.GetByNumber(i, 1); + if (m_Channel != NULL) { + if (m_Option == NULL || strcasestr(m_Channel->Name(), + m_Option) != NULL) + break; + i = m_Channel->Number() + 1; + } else { + m_Errno = 501; + m_Error = cString::sprintf("Channel \"%d\" not found", i); + } + } + + if (i < Channels.MaxNumber() + 1) + Last = false; + } + + return m_Client->Respond(Last ? 250 : -250, "%d %s", number, buffer); +} + +// --- cLSTTHandler ----------------------------------------------------------- + +class cLSTTHandler +{ +private: + cConnectionVTP *m_Client; + cTimer *m_Timer; + int m_Index; + int m_Errno; + cString m_Error; + bool m_Traverse; +public: + cLSTTHandler(cConnectionVTP *Client, const char *Option); + ~cLSTTHandler(); + bool Next(bool &Last); +}; + +cLSTTHandler::cLSTTHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_Timer(NULL), + m_Index(0), + m_Errno(0), + m_Traverse(false) +{ + if (*Option) { + if (isnumber(Option)) { + m_Timer = Timers.Get(strtol(Option, NULL, 10) - 1); + if (m_Timer == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Timer \"%s\" not defined", Option); + } + } else { + m_Errno = 501; + m_Error = cString::sprintf("Error in timer number \"%s\"", Option); + } + } else if (Timers.Count()) { + m_Traverse = true; + m_Index = 0; + m_Timer = Timers.Get(m_Index); + if (m_Timer == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1); + } + } else { + m_Errno = 550; + m_Error = "No timers defined"; + } +} + +cLSTTHandler::~cLSTTHandler() +{ +} + +bool cLSTTHandler::Next(bool &Last) +{ + if (*m_Error != NULL) { + Last = true; + cString str(m_Error); + m_Error = NULL; + return m_Client->Respond(m_Errno, "%s", *str); + } + + bool result; + char *buffer; + Last = !m_Traverse || m_Index >= Timers.Count() - 1; + buffer = strdup(*m_Timer->ToText()); + buffer[strlen(buffer) - 1] = '\0'; // strip \n + result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Timer->Index() + 1, + buffer); + free(buffer); + + if (m_Traverse && !Last) { + m_Timer = Timers.Get(++m_Index); + if (m_Timer == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Timer \"%d\" not found", m_Index + 1); + } + } + return result; +} + +// --- cLSTRHandler ----------------------------------------------------------- + +class cLSTRHandler +{ +private: + enum eStates { Recording, Event, Title, Subtitle, Description, Components, Vps, + EndRecording }; + cConnectionVTP *m_Client; + cRecording *m_Recording; + const cEvent *m_Event; + int m_Index; + int m_Errno; + cString m_Error; + bool m_Traverse; + bool m_Info; + eStates m_State; + int m_CurrentComponent; +public: + cLSTRHandler(cConnectionVTP *Client, const char *Option); + ~cLSTRHandler(); + bool Next(bool &Last); +}; + +cLSTRHandler::cLSTRHandler(cConnectionVTP *Client, const char *Option): + m_Client(Client), + m_Recording(NULL), + m_Event(NULL), + m_Index(0), + m_Errno(0), + m_Traverse(false), + m_Info(false), + m_State(Recording), + m_CurrentComponent(0) +{ + if (*Option) { + if (isnumber(Option)) { + m_Recording = Recordings.Get(strtol(Option, NULL, 10) - 1); +#if defined(USE_STREAMDEVEXT) || APIVERSNUM >= 10705 + m_Event = m_Recording->Info()->GetEvent(); +#endif + m_Info = true; + if (m_Recording == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Recording \"%s\" not found", Option); + } + } + else { + m_Errno = 501; + m_Error = cString::sprintf("Error in Recording number \"%s\"", Option); + } + } + else if (Recordings.Count()) { + m_Traverse = true; + m_Index = 0; + m_Recording = Recordings.Get(m_Index); + if (m_Recording == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1); + } + } + else { + m_Errno = 550; + m_Error = "No recordings available"; + } +} + +cLSTRHandler::~cLSTRHandler() +{ +} + +bool cLSTRHandler::Next(bool &Last) +{ + if (*m_Error != NULL) { + Last = true; + cString str(m_Error); + m_Error = NULL; + return m_Client->Respond(m_Errno, "%s", *str); + } + + if (m_Info) { + Last = false; + switch (m_State) { + case Recording: + if (m_Recording != NULL) { + m_State = Event; + return m_Client->Respond(-215, "C %s%s%s", + *m_Recording->Info()->ChannelID().ToString(), + m_Recording->Info()->ChannelName() ? " " : "", + m_Recording->Info()->ChannelName() ? m_Recording->Info()->ChannelName() : ""); + } + else { + m_State = EndRecording; + return Next(Last); + } + break; + + case Event: + m_State = Title; + if (m_Event != NULL) { + return m_Client->Respond(-215, "E %u %ld %d %X %X", (unsigned int) m_Event->EventID(), + m_Event->StartTime(), m_Event->Duration(), + m_Event->TableID(), m_Event->Version()); + } + return Next(Last); + + case Title: + m_State = Subtitle; + return m_Client->Respond(-215, "T %s", m_Recording->Info()->Title()); + + case Subtitle: + m_State = Description; + if (!isempty(m_Recording->Info()->ShortText())) { + return m_Client->Respond(-215, "S %s", m_Recording->Info()->ShortText()); + } + return Next(Last); + + case Description: + m_State = Components; + if (!isempty(m_Recording->Info()->Description())) { + m_State = Components; + char *copy = strdup(m_Recording->Info()->Description()); + cString cpy(copy, true); + strreplace(copy, '\n', '|'); + return m_Client->Respond(-215, "D %s", copy); + } + return Next(Last); + + case Components: + if (m_Recording->Info()->Components()) { + if (m_CurrentComponent < m_Recording->Info()->Components()->NumComponents()) { + tComponent *p = m_Recording->Info()->Components()->Component(m_CurrentComponent); + m_CurrentComponent++; + if (!Setup.UseDolbyDigital && p->stream == 0x02 && p->type == 0x05) + return Next(Last); + + return m_Client->Respond(-215, "X %s", *p->ToString()); + } + } + m_State = Vps; + return Next(Last); + + case Vps: + m_State = EndRecording; + if (m_Event != NULL) { + if (m_Event->Vps()) { + return m_Client->Respond(-215, "V %ld", m_Event->Vps()); + } + } + return Next(Last); + + case EndRecording: + Last = true; + return m_Client->Respond(215, "End of recording information"); + } + } + else { + bool result; + Last = !m_Traverse || m_Index >= Recordings.Count() - 1; + result = m_Client->Respond(Last ? 250 : -250, "%d %s", m_Recording->Index() + 1, m_Recording->Title(' ', true)); + + if (m_Traverse && !Last) { + m_Recording = Recordings.Get(++m_Index); + if (m_Recording == NULL) { + m_Errno = 501; + m_Error = cString::sprintf("Recording \"%d\" not found", m_Index + 1); + } + } + return result; + } + return false; +} + +// --- cConnectionVTP --------------------------------------------------------- + +cConnectionVTP::cConnectionVTP(void): + cServerConnection("VTP"), + m_LiveSocket(NULL), + m_LiveStreamer(NULL), + m_FilterSocket(NULL), + m_FilterStreamer(NULL), + m_RecSocket(NULL), + m_DataSocket(NULL), + m_LastCommand(NULL), + m_StreamType(stTSPIDS), + m_FiltersSupport(false), + m_RecPlayer(NULL), + m_LSTEHandler(NULL), + m_LSTCHandler(NULL), + m_LSTTHandler(NULL), + m_LSTRHandler(NULL) +{ +} + +cConnectionVTP::~cConnectionVTP() +{ + if (m_LastCommand != NULL) + free(m_LastCommand); + delete m_LiveStreamer; + delete m_LiveSocket; + delete m_RecSocket; + delete m_FilterStreamer; + delete m_FilterSocket; + delete m_DataSocket; + delete m_LSTTHandler; + delete m_LSTCHandler; + delete m_LSTEHandler; + delete m_LSTRHandler; + delete m_RecPlayer; +} + +inline bool cConnectionVTP::Abort(void) const +{ + return m_LiveStreamer && m_LiveStreamer->Abort(); +} + +void cConnectionVTP::Welcome(void) +{ + Respond(220, "Welcome to Video Disk Recorder (VTP)"); +} + +void cConnectionVTP::Reject(void) +{ + Respond(221, "Too many clients or client not allowed to connect"); + cServerConnection::Reject(); +} + +void cConnectionVTP::Detach(void) +{ + if (m_LiveStreamer) m_LiveStreamer->Detach(); + if (m_FilterStreamer) m_FilterStreamer->Detach(); +} + +void cConnectionVTP::Attach(void) +{ + if (m_LiveStreamer) m_LiveStreamer->Attach(); + if (m_FilterStreamer) m_FilterStreamer->Attach(); +} + +bool cConnectionVTP::Command(char *Cmd) +{ + char *param = NULL; + + if (Cmd != NULL) { + if (m_LastCommand != NULL) { + esyslog("ERROR: streamdev: protocol violation (VTP) from %s:%d", + RemoteIp().c_str(), RemotePort()); + return false; + } + + if ((param = strchr(Cmd, ' ')) != NULL) + *(param++) = '\0'; + else + param = Cmd + strlen(Cmd); + m_LastCommand = strdup(Cmd); + } else { + Cmd = m_LastCommand; + param = NULL; + } + + if (strcasecmp(Cmd, "LSTE") == 0) return CmdLSTE(param); + else if (strcasecmp(Cmd, "LSTR") == 0) return CmdLSTR(param); + else if (strcasecmp(Cmd, "LSTT") == 0) return CmdLSTT(param); + else if (strcasecmp(Cmd, "LSTC") == 0) return CmdLSTC(param); + + if (param == NULL) { + esyslog("ERROR: streamdev: this seriously shouldn't happen at %s:%d", + __FILE__, __LINE__); + return false; + } + + if (strcasecmp(Cmd, "CAPS") == 0) return CmdCAPS(param); + else if (strcasecmp(Cmd, "PROV") == 0) return CmdPROV(param); + else if (strcasecmp(Cmd, "PORT") == 0) return CmdPORT(param); + else if (strcasecmp(Cmd, "READ") == 0) return CmdREAD(param); + else if (strcasecmp(Cmd, "TUNE") == 0) return CmdTUNE(param); + else if (strcasecmp(Cmd, "PLAY") == 0) return CmdPLAY(param); + else if (strcasecmp(Cmd, "ADDP") == 0) return CmdADDP(param); + else if (strcasecmp(Cmd, "DELP") == 0) return CmdDELP(param); + else if (strcasecmp(Cmd, "ADDF") == 0) return CmdADDF(param); + else if (strcasecmp(Cmd, "DELF") == 0) return CmdDELF(param); + else if (strcasecmp(Cmd, "ABRT") == 0) return CmdABRT(param); + else if (strcasecmp(Cmd, "QUIT") == 0) return CmdQUIT(); + else if (strcasecmp(Cmd, "SUSP") == 0) return CmdSUSP(); + // Commands adopted from SVDRP + else if (strcasecmp(Cmd, "STAT") == 0) return CmdSTAT(param); + else if (strcasecmp(Cmd, "MODT") == 0) return CmdMODT(param); + else if (strcasecmp(Cmd, "NEWT") == 0) return CmdNEWT(param); + else if (strcasecmp(Cmd, "DELT") == 0) return CmdDELT(param); + else if (strcasecmp(Cmd, "NEXT") == 0) return CmdNEXT(param); + else if (strcasecmp(Cmd, "NEWC") == 0) return CmdNEWC(param); + else if (strcasecmp(Cmd, "MODC") == 0) return CmdMODC(param); + else if (strcasecmp(Cmd, "MOVC") == 0) return CmdMOVC(param); + else if (strcasecmp(Cmd, "DELC") == 0) return CmdDELC(param); + else if (strcasecmp(Cmd, "DELR") == 0) return CmdDELR(param); + else if (strcasecmp(Cmd, "RENR") == 0) return CmdRENR(param); + else + return Respond(500, "Unknown Command \"%s\"", Cmd); +} + +bool cConnectionVTP::CmdCAPS(char *Opts) +{ + if (strcasecmp(Opts, "TS") == 0) { + m_StreamType = stTS; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + if (strcasecmp(Opts, "TSPIDS") == 0) { + m_StreamType = stTSPIDS; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + if (strcasecmp(Opts, "PS") == 0) { + m_StreamType = stPS; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + if (strcasecmp(Opts, "PES") == 0) { + m_StreamType = stPES; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + if (strcasecmp(Opts, "EXTERN") == 0) { + m_StreamType = stExtern; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + // + // Deliver section filters data in separate, channel-independent data stream + // + if (strcasecmp(Opts, "FILTERS") == 0) { + m_FiltersSupport = true; + return Respond(220, "Capability \"%s\" accepted", Opts); + } + + return Respond(561, "Capability \"%s\" not known", Opts); +} + +bool cConnectionVTP::CmdPROV(char *Opts) +{ + const cChannel *chan; + int prio; + char *ep; + + prio = strtol(Opts, &ep, 10); + if (ep == Opts || !isspace(*ep)) + return Respond(501, "Use: PROV Priority Channel"); + + Opts = skipspace(ep); + if ((chan = ChannelFromString(Opts)) == NULL) + return Respond(550, "Undefined channel \"%s\"", Opts); + + return GetDevice(chan, prio) != NULL + ? Respond(220, "Channel available") + : Respond(560, "Channel not available"); +} + +bool cConnectionVTP::CmdPORT(char *Opts) +{ + uint id, dataport = 0; + char dataip[20]; + char *ep, *ipoffs; + int n; + + id = strtoul(Opts, &ep, 10); + if (ep == Opts || !isspace(*ep)) + return Respond(500, "Use: PORT Id Destination"); + + if (id >= si_Count) + return Respond(501, "Wrong connection id %d", id); + + Opts = skipspace(ep); + n = 0; + ipoffs = dataip; + while ((ep = strchr(Opts, ',')) != NULL) { + if (n < 4) { + memcpy(ipoffs, Opts, ep - Opts); + ipoffs += ep - Opts; + if (n < 3) *(ipoffs++) = '.'; + } else if (n == 4) { + *ep = 0; + dataport = strtoul(Opts, NULL, 10) << 8; + } else + break; + Opts = ep + 1; + ++n; + } + *ipoffs = '\0'; + + if (n != 5) + return Respond(501, "Argument count invalid (must be 6 values)"); + + dataport |= strtoul(Opts, NULL, 10); + + isyslog("Streamdev: Setting data connection to %s:%d", dataip, dataport); + + switch (id) { + case siLiveFilter: + m_FiltersSupport = true; + if(m_FilterStreamer) + m_FilterStreamer->Stop(); + delete m_FilterSocket; + + m_FilterSocket = new cTBSocket(SOCK_STREAM); + if (!m_FilterSocket->Connect(dataip, dataport)) { + esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s", + dataip, dataport, strerror(errno)); + DELETENULL(m_FilterSocket); + return Respond(551, "Couldn't open data connection"); + } + + if(!m_FilterStreamer) + m_FilterStreamer = new cStreamdevFilterStreamer; + m_FilterStreamer->Start(m_FilterSocket); + m_FilterStreamer->Activate(true); + + return Respond(220, "Port command ok, data connection opened"); + break; + + case siLive: + if(m_LiveSocket && m_LiveStreamer) + m_LiveStreamer->Stop(); + delete m_LiveSocket; + + m_LiveSocket = new cTBSocket(SOCK_STREAM); + if (!m_LiveSocket->Connect(dataip, dataport)) { + esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s", + dataip, dataport, strerror(errno)); + DELETENULL(m_LiveSocket); + return Respond(551, "Couldn't open data connection"); + } + + if (!m_LiveSocket->SetDSCP()) + LOG_ERROR_STR("unable to set DSCP sockopt"); + if (m_LiveStreamer) + m_LiveStreamer->Start(m_LiveSocket); + + return Respond(220, "Port command ok, data connection opened"); + break; + + case siReplay: + delete m_RecSocket; + + m_RecSocket = new cTBSocket(SOCK_STREAM); + if (!m_RecSocket->Connect(dataip, dataport)) { + esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s", + dataip, dataport, strerror(errno)); + DELETENULL(m_RecSocket); + return Respond(551, "Couldn't open data connection"); + } + + if (!m_RecSocket->SetDSCP()) + LOG_ERROR_STR("unable to set DSCP sockopt"); + + return Respond(220, "Port command ok, data connection opened"); + break; + + case siDataRespond: + delete m_DataSocket; + + m_DataSocket = new cTBSocket(SOCK_STREAM); + if (!m_DataSocket->Connect(dataip, dataport)) { + esyslog("ERROR: Streamdev: Couldn't open data connection to %s:%d: %s", + dataip, dataport, strerror(errno)); + DELETENULL(m_DataSocket); + return Respond(551, "Couldn't open data connection"); + } + return Respond(220, "Port command ok, data connection opened"); + break; + + default: + return Respond(501, "No handler for id %u", id); + } +} + +bool cConnectionVTP::CmdREAD(char *Opts) +{ + if (*Opts) { + char *tail; + uint64_t position = strtoll(Opts, &tail, 10); + if (tail && tail != Opts) { + tail = skipspace(tail); + if (tail && tail != Opts) { + int size = strtol(tail, NULL, 10); + uint8_t* data = (uint8_t*)malloc(size+4); + unsigned long count_readed = m_RecPlayer->getBlock(data, position, size); + unsigned long count_written = m_RecSocket->SysWrite(data, count_readed); + + free(data); + return Respond(220, "%lu Bytes submitted", count_written); + } + else { + return Respond(501, "Missing position"); + } + } + else { + return Respond(501, "Missing size"); + } + } + else { + return Respond(501, "Missing position"); + } +} + +bool cConnectionVTP::CmdTUNE(char *Opts) +{ + const cChannel *chan; + cDevice *dev; + + if ((chan = ChannelFromString(Opts)) == NULL) + return Respond(550, "Undefined channel \"%s\"", Opts); + + if ((dev = GetDevice(chan, 0)) == NULL) + return Respond(560, "Channel not available"); + + if (!dev->SwitchChannel(chan, false)) + return Respond(560, "Channel not available"); + + delete m_LiveStreamer; + m_LiveStreamer = new cStreamdevLiveStreamer(1); + m_LiveStreamer->SetChannel(chan, m_StreamType); + m_LiveStreamer->SetDevice(dev); + if(m_LiveSocket) + m_LiveStreamer->Start(m_LiveSocket); + + if(m_FiltersSupport) { + if(!m_FilterStreamer) + m_FilterStreamer = new cStreamdevFilterStreamer; + m_FilterStreamer->SetDevice(dev); + //m_FilterStreamer->SetChannel(chan); + } + + return Respond(220, "Channel tuned"); +} + +bool cConnectionVTP::CmdPLAY(char *Opts) +{ + if (*Opts) { + if (isnumber(Opts)) { + cRecording *recording = Recordings.Get(strtol(Opts, NULL, 10) - 1); + if (recording) { + if (m_RecPlayer) { + delete m_RecPlayer; + } + m_RecPlayer = new RecPlayer(recording); + return Respond(220, "%llu (Bytes), %u (Frames)", (long long unsigned int) m_RecPlayer->getLengthBytes(), (unsigned int) m_RecPlayer->getLengthFrames()); + } + else { + return Respond(550, "Recording \"%s\" not found", Opts); + } + } + else { + return Respond(500, "Use: PLAY record"); + } + } + else { + return Respond(500, "Use: PLAY record"); + } +} + +bool cConnectionVTP::CmdADDP(char *Opts) +{ + int pid; + char *end; + + pid = strtoul(Opts, &end, 10); + if (end == Opts || (*end != '\0' && *end != ' ')) + return Respond(500, "Use: ADDP Pid"); + + return m_LiveStreamer && m_LiveStreamer->SetPid(pid, true) + ? Respond(220, "Pid %d available", pid) + : Respond(560, "Pid %d not available", pid); +} + +bool cConnectionVTP::CmdDELP(char *Opts) +{ + int pid; + char *end; + + pid = strtoul(Opts, &end, 10); + if (end == Opts || (*end != '\0' && *end != ' ')) + return Respond(500, "Use: DELP Pid"); + + return m_LiveStreamer && m_LiveStreamer->SetPid(pid, false) + ? Respond(220, "Pid %d stopped", pid) + : Respond(560, "Pid %d not transferring", pid); +} + +bool cConnectionVTP::CmdADDF(char *Opts) +{ + int pid, tid, mask; + char *ep; + + if (m_FilterStreamer == NULL) + return Respond(560, "Can't set filters without a filter stream"); + + pid = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != ' ')) + return Respond(500, "Use: ADDF Pid Tid Mask"); + Opts = skipspace(ep); + tid = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != ' ')) + return Respond(500, "Use: ADDF Pid Tid Mask"); + Opts = skipspace(ep); + mask = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != '\0' && *ep != ' ')) + return Respond(500, "Use: ADDF Pid Tid Mask"); + + return m_FilterStreamer->SetFilter(pid, tid, mask, true) + ? Respond(220, "Filter %d transferring", pid) + : Respond(560, "Filter %d not available", pid); +} + +bool cConnectionVTP::CmdDELF(char *Opts) +{ + int pid, tid, mask; + char *ep; + + if (m_FilterStreamer == NULL) + return Respond(560, "Can't delete filters without a stream"); + + pid = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != ' ')) + return Respond(500, "Use: DELF Pid Tid Mask"); + Opts = skipspace(ep); + tid = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != ' ')) + return Respond(500, "Use: DELF Pid Tid Mask"); + Opts = skipspace(ep); + mask = strtol(Opts, &ep, 10); + if (ep == Opts || (*ep != '\0' && *ep != ' ')) + return Respond(500, "Use: DELF Pid Tid Mask"); + + m_FilterStreamer->SetFilter(pid, tid, mask, false); + return Respond(220, "Filter %d stopped", pid); +} + +bool cConnectionVTP::CmdABRT(char *Opts) +{ + uint id; + char *ep; + + id = strtoul(Opts, &ep, 10); + if (ep == Opts || (*ep != '\0' && *ep != ' ')) + return Respond(500, "Use: ABRT Id"); + + switch (id) { + case siLive: + DELETENULL(m_LiveStreamer); + DELETENULL(m_LiveSocket); + break; + case siLiveFilter: + DELETENULL(m_FilterStreamer); + DELETENULL(m_FilterSocket); + break; + case siReplay: + DELETENULL(m_RecPlayer); + DELETENULL(m_RecSocket); + break; + case siDataRespond: + DELETENULL(m_DataSocket); + break; + default: + return Respond(501, "Wrong connection id %d", id); + break; + + } + + return Respond(220, "Data connection closed"); +} + +bool cConnectionVTP::CmdQUIT(void) +{ + DeferClose(); + return Respond(221, "Video Disk Recorder closing connection"); +} + +bool cConnectionVTP::CmdSUSP(void) +{ + if (StreamdevServerSetup.SuspendMode == smAlways || cSuspendCtl::IsActive()) + return Respond(220, "Server is suspended"); + else if (StreamdevServerSetup.SuspendMode == smOffer + && StreamdevServerSetup.AllowSuspend) { + cControl::Launch(new cSuspendCtl); + return Respond(220, "Server is suspended"); + } else + return Respond(550, "Client may not suspend server"); +} + +// Functions extended from SVDRP + +template +bool cConnectionVTP::CmdLSTX(cHandler *&Handler, char *Option) +{ + if (Option != NULL) { + delete Handler; + Handler = new cHandler(this, Option); + } + + bool last = false; + bool result = false; + if (Handler != NULL) + result = Handler->Next(last); + else + esyslog("ERROR: vdr streamdev: Handler in LSTX command is NULL"); + if (!result || last) + DELETENULL(Handler); + + return result; +} + +bool cConnectionVTP::CmdLSTE(char *Option) +{ + return CmdLSTX(m_LSTEHandler, Option); +} + +bool cConnectionVTP::CmdLSTC(char *Option) +{ + return CmdLSTX(m_LSTCHandler, Option); +} + +bool cConnectionVTP::CmdLSTT(char *Option) +{ + return CmdLSTX(m_LSTTHandler, Option); +} + +bool cConnectionVTP::CmdLSTR(char *Option) +{ + return CmdLSTX(m_LSTRHandler, Option); +} + +// Functions adopted from SVDRP +#define INIT_WRAPPER() bool _res +#define Reply(c,m...) _res = Respond(c,m) +#define EXIT_WRAPPER() return _res + +bool cConnectionVTP::CmdSTAT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + if (strcasecmp(Option, "DISK") == 0) { + int FreeMB, UsedMB; + int Percent = VideoDiskSpace(&FreeMB, &UsedMB); + Reply(250, "%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent); + } + else if (strcasecmp(Option, "NAME") == 0) { + Reply(250, "vdr - The Video Disk Recorder with Streamdev-Server"); + } + else if (strcasecmp(Option, "VERSION") == 0) { + Reply(250, "VDR: %s | Streamdev: %s", VDRVERSION, VERSION); + } + else if (strcasecmp(Option, "RECORDS") == 0) { + bool recordings = Recordings.Load(); + Recordings.Sort(); + if (recordings) { + cRecording *recording = Recordings.Last(); + Reply(250, "%d", recording->Index() + 1); + } + else { + Reply(250, "0"); + } + } + else if (strcasecmp(Option, "CHANNELS") == 0) { + Reply(250, "%d", Channels.MaxNumber()); + } + else if (strcasecmp(Option, "TIMERS") == 0) { + Reply(250, "%d", Timers.Count()); + } + else if (strcasecmp(Option, "CHARSET") == 0) { + Reply(250, "%s", cCharSetConv::SystemCharacterTable()); + } + else if (strcasecmp(Option, "TIME") == 0) { + time_t timeNow = time(NULL); + struct tm* timeStruct = localtime(&timeNow); + int timeOffset = timeStruct->tm_gmtoff; + + Reply(250, "%lu %i", (unsigned long) timeNow, timeOffset); + } + else + Reply(501, "Invalid Option \"%s\"", Option); + } + else + Reply(501, "No option given"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdMODT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + char *tail; + int n = strtol(Option, &tail, 10); + if (tail && tail != Option) { + tail = skipspace(tail); + cTimer *timer = Timers.Get(n - 1); + if (timer) { + cTimer t = *timer; + if (strcasecmp(tail, "ON") == 0) + t.SetFlags(tfActive); + else if (strcasecmp(tail, "OFF") == 0) + t.ClrFlags(tfActive); + else if (!t.Parse(tail)) { + Reply(501, "Error in timer settings"); + EXIT_WRAPPER(); + } + *timer = t; + Timers.SetModified(); + isyslog("timer %s modified (%s)", *timer->ToDescr(), + timer->HasFlags(tfActive) ? "active" : "inactive"); + Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + } else + Reply(501, "Timer \"%d\" not defined", n); + } else + Reply(501, "Error in timer number"); + } else + Reply(501, "Missing timer settings"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdNEWT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + cTimer *timer = new cTimer; + if (timer->Parse(Option)) { + cTimer *t = Timers.GetTimer(timer); + if (!t) { + Timers.Add(timer); + Timers.SetModified(); + isyslog("timer %s added", *timer->ToDescr()); + Reply(250, "%d %s", timer->Index() + 1, *timer->ToText()); + EXIT_WRAPPER(); + } else + Reply(550, "Timer already defined: %d %s", t->Index() + 1, + *t->ToText()); + } else + Reply(501, "Error in timer settings"); + delete timer; + } else + Reply(501, "Missing timer settings"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdDELT(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + int number = 0; + bool force = false; + char buf[strlen(Option) + 1]; + strcpy(buf, Option); + const char *delim = " \t"; + char *strtok_next; + char *p = strtok_r(buf, delim, &strtok_next); + + if (isnumber(p)) { + number = strtol(p, NULL, 10) - 1; + } + else if (strcasecmp(p, "FORCE") == 0) { + force = true; + } + if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) { + if (isnumber(p)) { + number = strtol(p, NULL, 10) - 1; + } + else if (strcasecmp(p, "FORCE") == 0) { + force = true; + } + else { + Reply(501, "Timer not found or wrong syntax"); + } + } + + cTimer *timer = Timers.Get(number); + if (timer) { + if (timer->Recording()) { + if (force) { + timer->Skip(); + cRecordControls::Process(time(NULL)); + } + else { + Reply(550, "Timer \"%i\" is recording", number); + EXIT_WRAPPER(); + } + } + isyslog("deleting timer %s", *timer->ToDescr()); + Timers.Del(timer); + Timers.SetModified(); + Reply(250, "Timer \"%i\" deleted", number); + } else + Reply(501, "Timer \"%i\" not defined", number); + } else + Reply(501, "Missing timer option"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdNEXT(const char *Option) +{ + INIT_WRAPPER(); + cTimer *t = Timers.GetNextActiveTimer(); + if (t) { + time_t Start = t->StartTime(); + int Number = t->Index() + 1; + if (!*Option) + Reply(250, "%d %s", Number, *TimeToString(Start)); + else if (strcasecmp(Option, "ABS") == 0) + Reply(250, "%d %ld", Number, Start); + else if (strcasecmp(Option, "REL") == 0) + Reply(250, "%d %ld", Number, Start - time(NULL)); + else + Reply(501, "Unknown option: \"%s\"", Option); + } + else + Reply(550, "No active timers"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdNEWC(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + cChannel ch; + if (ch.Parse(Option)) { + if (Channels.HasUniqueChannelID(&ch)) { + cChannel *channel = new cChannel; + *channel = ch; + Channels.Add(channel); + Channels.ReNumber(); + Channels.SetModified(true); + isyslog("new channel %d %s", channel->Number(), *channel->ToText()); + Reply(250, "%d %s", channel->Number(), *channel->ToText()); + } + else { + Reply(501, "Channel settings are not unique"); + } + } + else { + Reply(501, "Error in channel settings"); + } + } + else { + Reply(501, "Missing channel settings"); + } + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdMODC(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + char *tail; + int n = strtol(Option, &tail, 10); + if (tail && tail != Option) { + tail = skipspace(tail); + if (!Channels.BeingEdited()) { + cChannel *channel = Channels.GetByNumber(n); + if (channel) { + cChannel ch; + if (ch.Parse(tail)) { + if (Channels.HasUniqueChannelID(&ch, channel)) { + *channel = ch; + Channels.ReNumber(); + Channels.SetModified(true); + isyslog("modifed channel %d %s", channel->Number(), *channel->ToText()); + Reply(250, "%d %s", channel->Number(), *channel->ToText()); + } + else { + Reply(501, "Channel settings are not unique"); + } + } + else { + Reply(501, "Error in channel settings"); + } + } + else { + Reply(501, "Channel \"%d\" not defined", n); + } + } + else { + Reply(550, "Channels are being edited - try again later"); + } + } + else { + Reply(501, "Error in channel number"); + } + } + else { + Reply(501, "Missing channel settings"); + } + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdMOVC(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + if (!Channels.BeingEdited() && !Timers.BeingEdited()) { + char *tail; + int From = strtol(Option, &tail, 10); + if (tail && tail != Option) { + tail = skipspace(tail); + if (tail && tail != Option) { + int To = strtol(tail, NULL, 10); + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); + cChannel *FromChannel = Channels.GetByNumber(From); + if (FromChannel) { + cChannel *ToChannel = Channels.GetByNumber(To); + if (ToChannel) { + int FromNumber = FromChannel->Number(); + int ToNumber = ToChannel->Number(); + if (FromNumber != ToNumber) { + Channels.Move(FromChannel, ToChannel); + Channels.ReNumber(); + Channels.SetModified(true); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) { + Channels.SwitchTo(CurrentChannel->Number()); + } + else { + cDevice::SetCurrentChannel(CurrentChannel); + } + } + isyslog("channel %d moved to %d", FromNumber, ToNumber); + Reply(250,"Channel \"%d\" moved to \"%d\"", From, To); + } + else { + Reply(501, "Can't move channel to same postion"); + } + } + else { + Reply(501, "Channel \"%d\" not defined", To); + } + } + else { + Reply(501, "Channel \"%d\" not defined", From); + } + } + else { + Reply(501, "Error in channel number"); + } + } + else { + Reply(501, "Error in channel number"); + } + } + else { + Reply(550, "Channels or timers are being edited - try again later"); + } + } + else { + Reply(501, "Missing channel number"); + } + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdDELC(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + if (isnumber(Option)) { + if (!Channels.BeingEdited()) { + cChannel *channel = Channels.GetByNumber(strtol(Option, NULL, 10)); + if (channel) { + for (cTimer *timer = Timers.First(); timer; timer = Timers.Next(timer)) { + if (timer->Channel() == channel) { + Reply(550, "Channel \"%s\" is in use by timer %d", Option, timer->Index() + 1); + return false; + } + } + int CurrentChannelNr = cDevice::CurrentChannel(); + cChannel *CurrentChannel = Channels.GetByNumber(CurrentChannelNr); + if (CurrentChannel && channel == CurrentChannel) { + int n = Channels.GetNextNormal(CurrentChannel->Index()); + if (n < 0) + n = Channels.GetPrevNormal(CurrentChannel->Index()); + CurrentChannel = Channels.Get(n); + CurrentChannelNr = 0; // triggers channel switch below + } + Channels.Del(channel); + Channels.ReNumber(); + Channels.SetModified(true); + isyslog("channel %s deleted", Option); + if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { + if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) + Channels.SwitchTo(CurrentChannel->Number()); + else + cDevice::SetCurrentChannel(CurrentChannel); + } + Reply(250, "Channel \"%s\" deleted", Option); + } + else + Reply(501, "Channel \"%s\" not defined", Option); + } + else + Reply(550, "Channels are being edited - try again later"); + } + else + Reply(501, "Error in channel number \"%s\"", Option); + } + else { + Reply(501, "Missing channel number"); + } + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdDELR(const char *Option) +{ + INIT_WRAPPER(); + if (*Option) { + if (isnumber(Option)) { + cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1); + if (recording) { + cRecordControl *rc = cRecordControls::GetRecordControl(recording->FileName()); + if (!rc) { + if (recording->Delete()) { + Reply(250, "Recording \"%s\" deleted", Option); + ::Recordings.DelByName(recording->FileName()); + } + else + Reply(554, "Error while deleting recording!"); + } + else + Reply(550, "Recording \"%s\" is in use by timer %d", Option, rc->Timer()->Index() + 1); + } + else + Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before deleting)"); + } + else + Reply(501, "Error in recording number \"%s\"", Option); + } + else + Reply(501, "Missing recording number"); + EXIT_WRAPPER(); +} + +bool cConnectionVTP::CmdRENR(const char *Option) +{ + INIT_WRAPPER(); +#if defined(LIEMIKUUTIO) + bool recordings = Recordings.Update(true); + if (recordings) { + if (*Option) { + char *tail; + int n = strtol(Option, &tail, 10); + cRecording *recording = Recordings.Get(n - 1); + if (recording && tail && tail != Option) { +#if APIVERSNUM < 10704 + int priority = recording->priority; + int lifetime = recording->lifetime; +#endif + char *oldName = strdup(recording->Name()); + tail = skipspace(tail); +#if APIVERSNUM < 10704 + if (recording->Rename(tail, &priority, &lifetime)) { +#else + if (recording->Rename(tail)) { +#endif + Reply(250, "Renamed \"%s\" to \"%s\"", oldName, recording->Name()); + Recordings.ChangeState(); + Recordings.TouchUpdate(); + } + else { + Reply(501, "Renaming \"%s\" to \"%s\" failed", oldName, tail); + } + free(oldName); + } + else { + Reply(501, "Recording not found or wrong syntax"); + } + } + else { + Reply(501, "Missing Input settings"); + } + } + else { + Reply(550, "No recordings available"); + } +#else + Reply(501, "Rename not supported, please use LIEMIEXT"); +#endif /* LIEMIKUUTIO */ + EXIT_WRAPPER(); +} + +bool cConnectionVTP::Respond(int Code, const char *Message, ...) +{ + va_list ap; + va_start(ap, Message); +#if APIVERSNUM < 10515 + char *buffer; + if (vasprintf(&buffer, Message, ap) < 0) + buffer = strdup("???"); + cString str(buffer, true); +#else + cString str = cString::sprintf(Message, ap); +#endif + va_end(ap); + + if (Code >= 0 && m_LastCommand != NULL) { + free(m_LastCommand); + m_LastCommand = NULL; + } + + return cServerConnection::Respond("%03d%c%s", Code >= 0, + Code < 0 ? -Code : Code, + Code < 0 ? '-' : ' ', *str); +} diff --git a/plugins/streamdev/streamdev-cvs/server/connectionVTP.h b/plugins/streamdev/streamdev-cvs/server/connectionVTP.h new file mode 100644 index 0000000..b938fe6 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/connectionVTP.h @@ -0,0 +1,93 @@ +#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H +#define VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H + +#include "server/connection.h" +#include "server/recplayer.h" + +class cTBSocket; +class cStreamdevLiveStreamer; +class cStreamdevFilterStreamer; +class cLSTEHandler; +class cLSTCHandler; +class cLSTTHandler; +class cLSTRHandler; + +class cConnectionVTP: public cServerConnection { + friend class cLSTEHandler; +#if !defined __GNUC__ || __GNUC__ >= 3 + using cServerConnection::Respond; +#endif + +private: + cTBSocket *m_LiveSocket; + cStreamdevLiveStreamer *m_LiveStreamer; + cTBSocket *m_FilterSocket; + cStreamdevFilterStreamer *m_FilterStreamer; + cTBSocket *m_RecSocket; + cTBSocket *m_DataSocket; + + char *m_LastCommand; + eStreamType m_StreamType; + bool m_FiltersSupport; + RecPlayer *m_RecPlayer; + + // Members adopted for SVDRP + cLSTEHandler *m_LSTEHandler; + cLSTCHandler *m_LSTCHandler; + cLSTTHandler *m_LSTTHandler; + cLSTRHandler *m_LSTRHandler; + +protected: + template + bool CmdLSTX(cHandler *&Handler, char *Option); + +public: + cConnectionVTP(void); + virtual ~cConnectionVTP(); + + virtual void Welcome(void); + virtual void Reject(void); + + virtual bool Abort(void) const; + virtual void Detach(void); + virtual void Attach(void); + + virtual bool Command(char *Cmd); + bool CmdCAPS(char *Opts); + bool CmdPROV(char *Opts); + bool CmdPORT(char *Opts); + bool CmdREAD(char *Opts); + bool CmdTUNE(char *Opts); + bool CmdPLAY(char *Opts); + bool CmdADDP(char *Opts); + bool CmdDELP(char *Opts); + bool CmdADDF(char *Opts); + bool CmdDELF(char *Opts); + bool CmdABRT(char *Opts); + bool CmdQUIT(void); + bool CmdSUSP(void); + + // Thread-safe implementations of SVDRP commands + bool CmdLSTE(char *Opts); + bool CmdLSTC(char *Opts); + bool CmdLSTT(char *Opts); + bool CmdLSTR(char *Opts); + + // Commands adopted from SVDRP + bool CmdSTAT(const char *Option); + bool CmdMODT(const char *Option); + bool CmdNEWT(const char *Option); + bool CmdDELT(const char *Option); + bool CmdNEXT(const char *Option); + bool CmdNEWC(const char *Option); + bool CmdMODC(const char *Option); + bool CmdMOVC(const char *Option); + bool CmdDELC(const char *Option); + bool CmdDELR(const char *Option); + bool CmdRENR(const char *Option); + + bool Respond(int Code, const char *Message, ...) + __attribute__ ((format (printf, 3, 4))); +}; + +#endif // VDR_STREAMDEV_SERVERS_CONNECTIONVTP_H diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries b/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries new file mode 100644 index 0000000..1784810 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Entries @@ -0,0 +1 @@ +D diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository b/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository new file mode 100644 index 0000000..49fadb8 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Repository @@ -0,0 +1 @@ +streamdev/server/http diff --git a/plugins/streamdev/streamdev-cvs/server/http/CVS/Root b/plugins/streamdev/streamdev-cvs/server/http/CVS/Root new file mode 100644 index 0000000..2c7f6ce --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/http/CVS/Root @@ -0,0 +1 @@ +:pserver:anoncvs@vdr-developer.org:/var/cvsroot diff --git a/plugins/streamdev/streamdev-cvs/server/livefilter.c b/plugins/streamdev/streamdev-cvs/server/livefilter.c new file mode 100644 index 0000000..67b0e37 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/livefilter.c @@ -0,0 +1,39 @@ +/* + * $Id: livefilter.c,v 1.7 2009/02/13 13:02:40 schmirl Exp $ + */ + +#include "server/livefilter.h" +#include "server/streamer.h" +#include "common.h" + +#ifndef TS_SYNC_BYTE +# define TS_SYNC_BYTE 0x47 +#endif + +cStreamdevLiveFilter::cStreamdevLiveFilter(cStreamdevStreamer *Streamer) { + m_Streamer = Streamer; +} + +void cStreamdevLiveFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) +{ + uchar buffer[TS_SIZE]; + int length = Length; + int pos = 0; + + while (length > 0) { + int chunk = min(length, TS_SIZE - 5); + buffer[0] = TS_SYNC_BYTE; + buffer[1] = ((Pid >> 8) & 0x3f) | (pos==0 ? 0x40 : 0); /* bit 6: payload unit start indicator (PUSI) */ + buffer[2] = Pid & 0xff; + buffer[3] = Tid; + // this makes it a proprietary stream + buffer[4] = (uchar)chunk; + memcpy(buffer + 5, Data + pos, chunk); + length -= chunk; + pos += chunk; + + int p = m_Streamer->Put(buffer, TS_SIZE); + if (p != TS_SIZE) + m_Streamer->ReportOverflow(TS_SIZE - p); + } +} diff --git a/plugins/streamdev/streamdev-cvs/server/livefilter.h b/plugins/streamdev/streamdev-cvs/server/livefilter.h new file mode 100644 index 0000000..13e8956 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/livefilter.h @@ -0,0 +1,32 @@ +/* + * $Id: livefilter.h,v 1.5 2008/04/07 14:27:31 schmirl Exp $ + */ + +#ifndef VDR_STREAMEV_LIVEFILTER_H +#define VDR_STREAMEV_LIVEFILTER_H + +#include + +#include + +class cStreamdevStreamer; + +class cStreamdevLiveFilter: public cFilter { +private: + cStreamdevStreamer *m_Streamer; + +protected: + virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); + +public: + cStreamdevLiveFilter(cStreamdevStreamer *Streamer); + + void Set(u_short Pid, u_char Tid, u_char Mask) { + cFilter::Set(Pid, Tid, Mask); + } + void Del(u_short Pid, u_char Tid, u_char Mask) { + cFilter::Del(Pid, Tid, Mask); + } +}; + +#endif // VDR_STREAMEV_LIVEFILTER_H diff --git a/plugins/streamdev/streamdev-cvs/server/livestreamer.c b/plugins/streamdev/streamdev-cvs/server/livestreamer.c new file mode 100644 index 0000000..3162fa2 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/livestreamer.c @@ -0,0 +1,689 @@ +#include + +#include +#include + +#include "remux/ts2ps.h" +#include "remux/ts2pes.h" +#include "remux/ts2es.h" +#include "remux/extern.h" + +#include + +#include "server/livestreamer.h" +#include "server/livefilter.h" +#include "common.h" + +using namespace Streamdev; + +// --- cStreamdevLiveReceiver ------------------------------------------------- + +class cStreamdevLiveReceiver: public cReceiver { + friend class cStreamdevStreamer; + +private: + cStreamdevStreamer *m_Streamer; + +protected: + virtual void Activate(bool On); + virtual void Receive(uchar *Data, int Length); + +public: + cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, int Priority, const int *Pids); + virtual ~cStreamdevLiveReceiver(); +}; + +cStreamdevLiveReceiver::cStreamdevLiveReceiver(cStreamdevStreamer *Streamer, tChannelID ChannelID, + int Priority, const int *Pids): + cReceiver(ChannelID, Priority, 0, Pids), + m_Streamer(Streamer) +{ +} + +cStreamdevLiveReceiver::~cStreamdevLiveReceiver() +{ + Dprintf("Killing live receiver\n"); + Detach(); +} + +void cStreamdevLiveReceiver::Receive(uchar *Data, int Length) { + int p = m_Streamer->Receive(Data, Length); + if (p != Length) + m_Streamer->ReportOverflow(Length - p); +} + +inline void cStreamdevLiveReceiver::Activate(bool On) +{ + Dprintf("LiveReceiver->Activate(%d)\n", On); + m_Streamer->Activate(On); +} + +// --- cStreamdevPatFilter ---------------------------------------------------- + +class cStreamdevPatFilter : public cFilter { +private: + int pmtPid; + int pmtSid; + int pmtVersion; + uchar tspat_buf[TS_SIZE]; + cStreamdevBuffer siBuffer; + + const cChannel *m_Channel; + cStreamdevLiveStreamer *m_Streamer; + + virtual void Process(u_short Pid, u_char Tid, const u_char *Data, int Length); + + int GetPid(SI::PMT::Stream& stream); +public: + cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel); + uchar* Get(int &Count) { return siBuffer.Get(Count); } + void Del(int Count) { return siBuffer.Del(Count); } +}; + +cStreamdevPatFilter::cStreamdevPatFilter(cStreamdevLiveStreamer *Streamer, const cChannel *Channel): siBuffer(10 * TS_SIZE, TS_SIZE) +{ + Dprintf("cStreamdevPatFilter(\"%s\")\n", Channel->Name()); + assert(Streamer); + m_Channel = Channel; + m_Streamer = Streamer; + pmtPid = 0; + pmtSid = 0; + pmtVersion = -1; + Set(0x00, 0x00); // PAT + // initialize PAT buffer. Only some values are dynamic (see comments) + memset(tspat_buf, 0xff, TS_SIZE); + tspat_buf[0] = TS_SYNC_BYTE; // Transport packet header sunchronization byte (1000011 = 0x47h) + tspat_buf[1] = 0x40; // Set payload unit start indicator bit + tspat_buf[2] = 0x0; // PID + tspat_buf[3] = 0x10; // Set payload flag, DYNAMIC: Continuity counter + tspat_buf[4] = 0x0; // SI pointer field + tspat_buf[5] = 0x0; // PAT table id + tspat_buf[6] = 0xb0; // Section syntax indicator bit and reserved bits set + tspat_buf[7] = 12 + 1; // Section length (12 bit): PAT_TABLE_LEN + 1 + tspat_buf[8] = 0; // DYNAMIC: Transport stream ID (bits 8-15) + tspat_buf[9] = 0; // DYNAMIC: Transport stream ID (bits 0-7) + tspat_buf[10] = 0xc0; // Reserved, DYNAMIC: Version number, DYNAMIC: Current next indicator + tspat_buf[11] = 0x0; // Section number + tspat_buf[12] = 0x0; // Last section number + tspat_buf[13] = 0; // DYNAMIC: Program number (bits 8-15) + tspat_buf[14] = 0; // DYNAMIC: Program number (bits 0-7) + tspat_buf[15] = 0xe0; // Reserved, DYNAMIC: Network ID (bits 8-12) + tspat_buf[16] = 0; // DYNAMIC: Network ID (bits 0-7) + tspat_buf[17] = 0; // DYNAMIC: Checksum + tspat_buf[18] = 0; // DYNAMIC: Checksum + tspat_buf[19] = 0; // DYNAMIC: Checksum + tspat_buf[20] = 0; // DYNAMIC: Checksum +} + +static const char * const psStreamTypes[] = { + "UNKNOWN", + "ISO/IEC 11172 Video", + "ISO/IEC 13818-2 Video", + "ISO/IEC 11172 Audio", + "ISO/IEC 13818-3 Audio", + "ISO/IEC 13818-1 Privete sections", + "ISO/IEC 13818-1 Private PES data", + "ISO/IEC 13512 MHEG", + "ISO/IEC 13818-1 Annex A DSM CC", + "0x09", + "ISO/IEC 13818-6 Multiprotocol encapsulation", + "ISO/IEC 13818-6 DSM-CC U-N Messages", + "ISO/IEC 13818-6 Stream Descriptors", + "ISO/IEC 13818-6 Sections (any type, including private data)", + "ISO/IEC 13818-1 auxiliary", + "ISO/IEC 13818-7 Audio with ADTS transport sytax", + "ISO/IEC 14496-2 Visual (MPEG-4)", + "ISO/IEC 14496-3 Audio with LATM transport syntax", + "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19", "0x1a", + "ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264)", + "", +}; + +int cStreamdevPatFilter::GetPid(SI::PMT::Stream& stream) +{ + SI::Descriptor *d; + + if (!stream.getPid()) + return 0; + + switch (stream.getStreamType()) { + case 0x01: // ISO/IEC 11172 Video + case 0x02: // ISO/IEC 13818-2 Video + case 0x03: // ISO/IEC 11172 Audio + case 0x04: // ISO/IEC 13818-3 Audio +#if 0 + case 0x07: // ISO/IEC 13512 MHEG + case 0x08: // ISO/IEC 13818-1 Annex A DSM CC + case 0x0a: // ISO/IEC 13818-6 Multiprotocol encapsulation + case 0x0b: // ISO/IEC 13818-6 DSM-CC U-N Messages + case 0x0c: // ISO/IEC 13818-6 Stream Descriptors + case 0x0d: // ISO/IEC 13818-6 Sections (any type, including private data) + case 0x0e: // ISO/IEC 13818-1 auxiliary +#endif + case 0x0f: // ISO/IEC 13818-7 Audio with ADTS transport syntax + case 0x10: // ISO/IEC 14496-2 Visual (MPEG-4) + case 0x11: // ISO/IEC 14496-3 Audio with LATM transport syntax + case 0x1b: // ISO/IEC 14496-10 Video (MPEG-4 part 10/AVC, aka H.264) + Dprintf("cStreamdevPatFilter PMT scanner adding PID %d (%s)\n", + stream.getPid(), psStreamTypes[stream.getStreamType()]); + return stream.getPid(); + case 0x05: // ISO/IEC 13818-1 private sections + case 0x06: // ISO/IEC 13818-1 PES packets containing private data + for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { + switch (d->getDescriptorTag()) { + case SI::AC3DescriptorTag: + Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", + stream.getPid(), psStreamTypes[stream.getStreamType()], "AC3"); + delete d; + return stream.getPid(); + case SI::TeletextDescriptorTag: + Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", + stream.getPid(), psStreamTypes[stream.getStreamType()], "Teletext"); + delete d; + return stream.getPid(); + case SI::SubtitlingDescriptorTag: + Dprintf("cStreamdevPatFilter PMT scanner: adding PID %d (%s) %s\n", + stream.getPid(), psStreamTypes[stream.getStreamType()], "DVBSUB"); + delete d; + return stream.getPid(); + default: + Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n", + stream.getPid(), psStreamTypes[stream.getStreamType()], "UNKNOWN"); + break; + } + delete d; + } + break; + default: + /* This following section handles all the cases where the audio track + * info is stored in PMT user info with stream id >= 0x80 + * we check the registration format identifier to see if it + * holds "AC-3" + */ + if (stream.getStreamType() >= 0x80) { + bool found = false; + for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { + switch (d->getDescriptorTag()) { + case SI::RegistrationDescriptorTag: + /* unfortunately libsi does not implement RegistrationDescriptor */ + if (d->getLength() >= 4) { + found = true; + SI::CharArray rawdata = d->getData(); + if (/*rawdata[0] == 5 && rawdata[1] >= 4 && */ + rawdata[2] == 'A' && rawdata[3] == 'C' && + rawdata[4] == '-' && rawdata[5] == '3') { + isyslog("cStreamdevPatFilter PMT scanner:" + "Adding pid %d (type 0x%x) RegDesc len %d (%c%c%c%c)", + stream.getPid(), stream.getStreamType(), + d->getLength(), rawdata[2], rawdata[3], + rawdata[4], rawdata[5]); + delete d; + return stream.getPid(); + } + } + break; + default: + break; + } + delete d; + } + if(!found) { + isyslog("Adding pid %d (type 0x%x) RegDesc not found -> assume AC-3", + stream.getPid(), stream.getStreamType()); + return stream.getPid(); + } + } + Dprintf("cStreamdevPatFilter PMT scanner: NOT adding PID %d (%s) %s\n", + stream.getPid(), psStreamTypes[stream.getStreamType()<0x1c?stream.getStreamType():0], "UNKNOWN"); + break; + } + return 0; +} + +void cStreamdevPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length) +{ + if (Pid == 0x00) { + if (Tid == 0x00) { + SI::PAT pat(Data, false); + if (!pat.CheckCRCAndParse()) + return; + SI::PAT::Association assoc; + for (SI::Loop::Iterator it; pat.associationLoop.getNext(assoc, it); ) { + if (!assoc.isNITPid()) { + const cChannel *Channel = Channels.GetByServiceID(Source(), Transponder(), assoc.getServiceId()); + if (Channel && (Channel == m_Channel)) { + int prevPmtPid = pmtPid; + if (0 != (pmtPid = assoc.getPid())) { + Dprintf("cStreamdevPatFilter: PMT pid for channel %s: %d\n", Channel->Name(), pmtPid); + pmtSid = assoc.getServiceId(); + // repack PAT to TS frame and send to client + int ts_id; + unsigned int crc, i, len; + uint8_t *tmp; + static uint8_t ccounter = 0; + ccounter = (ccounter + 1) % 16; + ts_id = Channel->Tid(); // Get transport stream id of the channel + tspat_buf[3] = 0x10 | ccounter; // Set payload flag, Continuity counter + tspat_buf[8] = (ts_id >> 8); // Transport stream ID (bits 8-15) + tspat_buf[9] = (ts_id & 0xff); // Transport stream ID (bits 0-7) + tspat_buf[10] = 0xc0 | ((pat.getVersionNumber() << 1) & 0x3e) | + pat.getCurrentNextIndicator();// Version number, Current next indicator + tspat_buf[13] = (pmtSid >> 8); // Program number (bits 8-15) + tspat_buf[14] = (pmtSid & 0xff); // Program number (bits 0-7) + tspat_buf[15] = 0xe0 | (pmtPid >> 8); // Network ID (bits 8-12) + tspat_buf[16] = (pmtPid & 0xff); // Network ID (bits 0-7) + crc = 0xffffffff; + len = 12; // PAT_TABLE_LEN + tmp = &tspat_buf[4 + 1]; // TS_HDR_LEN + 1 + while (len--) { + crc ^= *tmp++ << 24; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x80000000) ? 0x04c11db7 : 0); // CRC32POLY + } + tspat_buf[17] = crc >> 24 & 0xff; // Checksum + tspat_buf[18] = crc >> 16 & 0xff; // Checksum + tspat_buf[19] = crc >> 8 & 0xff; // Checksum + tspat_buf[20] = crc & 0xff; // Checksum + int written = siBuffer.PutTS(tspat_buf, TS_SIZE); + if (written != TS_SIZE) + siBuffer.ReportOverflow(TS_SIZE - written); + if (pmtPid != prevPmtPid) { + m_Streamer->SetPids(pmtPid); + Add(pmtPid, 0x02); + pmtVersion = -1; + } + return; + } + } + } + } + } + } else if (Pid == pmtPid && Tid == SI::TableIdPMT && Source() && Transponder()) { + SI::PMT pmt(Data, false); + if (!pmt.CheckCRCAndParse()) + return; + if (pmt.getServiceId() != pmtSid) + return; // skip broken PMT records + if (pmtVersion != -1) { + if (pmtVersion != pmt.getVersionNumber()) { + Dprintf("cStreamdevPatFilter: PMT version changed, detaching all pids\n"); + cFilter::Del(pmtPid, 0x02); + pmtPid = 0; // this triggers PAT scan + } + return; + } + pmtVersion = pmt.getVersionNumber(); + + SI::PMT::Stream stream; + int pids[MAXRECEIVEPIDS + 1], npids = 0; + pids[npids++] = pmtPid; +#if 0 + pids[npids++] = 0x10; // pid 0x10, tid 0x40: NIT + pids[npids++] = 0x11; // pid 0x11, tid 0x42: SDT + pids[npids++] = 0x14; // pid 0x14, tid 0x70: TDT +#endif + pids[npids++] = 0x12; // pid 0x12, tid 0x4E...0x6F: EIT + for (SI::Loop::Iterator it; pmt.streamLoop.getNext(stream, it); ) + if (0 != (pids[npids] = GetPid(stream)) && npids < MAXRECEIVEPIDS) + npids++; + + pids[npids] = 0; + m_Streamer->SetPids(pmt.getPCRPid(), pids); + } +} + +// --- cStreamdevLiveStreamer ------------------------------------------------- + +cStreamdevLiveStreamer::cStreamdevLiveStreamer(int Priority, std::string Parameter): + cStreamdevStreamer("streamdev-livestreaming"), + m_Priority(Priority), + m_Parameter(Parameter), + m_NumPids(0), + m_StreamType(stTSPIDS), + m_Channel(NULL), + m_Device(NULL), + m_Receiver(NULL), + m_PatFilter(NULL), + m_Remux(NULL) +{ +} + +cStreamdevLiveStreamer::~cStreamdevLiveStreamer() +{ + Dprintf("Desctructing Live streamer\n"); + Stop(); + if(m_PatFilter) { + Detach(); + DELETENULL(m_PatFilter); + } + DELETENULL(m_Receiver); + delete m_Remux; +} + +bool cStreamdevLiveStreamer::HasPid(int Pid) +{ + int idx; + for (idx = 0; idx < m_NumPids; ++idx) + if (m_Pids[idx] == Pid) + return true; + return false; +} + +bool cStreamdevLiveStreamer::SetPid(int Pid, bool On) +{ + int idx; + + if (Pid == 0) + return true; + + if (On) { + for (idx = 0; idx < m_NumPids; ++idx) { + if (m_Pids[idx] == Pid) + return true; // No change needed + } + + if (m_NumPids == MAXRECEIVEPIDS) { + esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid); + return false; + } + + m_Pids[m_NumPids++] = Pid; + m_Pids[m_NumPids] = 0; + } else { + for (idx = 0; idx < m_NumPids; ++idx) { + if (m_Pids[idx] == Pid) { + --m_NumPids; + memmove(&m_Pids[idx], &m_Pids[idx + 1], sizeof(int) * (m_NumPids - idx)); + } + } + } + + StartReceiver(); + return true; +} + +bool cStreamdevLiveStreamer::SetPids(int Pid, const int *Pids1, const int *Pids2, const int *Pids3) +{ + m_NumPids = 0; + + if (Pid) + m_Pids[m_NumPids++] = Pid; + + if (Pids1) + for ( ; *Pids1 && m_NumPids < MAXRECEIVEPIDS; Pids1++) + if (!HasPid(*Pids1)) + m_Pids[m_NumPids++] = *Pids1; + + if (Pids2) + for ( ; *Pids2 && m_NumPids < MAXRECEIVEPIDS; Pids2++) + if (!HasPid(*Pids2)) + m_Pids[m_NumPids++] = *Pids2; + + if (Pids3) + for ( ; *Pids3 && m_NumPids < MAXRECEIVEPIDS; Pids3++) + if (!HasPid(*Pids3)) + m_Pids[m_NumPids++] = *Pids3; + + if (m_NumPids >= MAXRECEIVEPIDS) { + esyslog("ERROR: Streamdev: No free slot to receive pid %d\n", Pid); + return false; + } + + m_Pids[m_NumPids] = 0; + StartReceiver(); + return true; +} + +void cStreamdevLiveStreamer::StartReceiver(void) +{ + DELETENULL(m_Receiver); + if (m_NumPids > 0) { + Dprintf("Creating Receiver to respect changed pids\n"); + m_Receiver = new cStreamdevLiveReceiver(this, m_Channel->GetChannelID(), m_Priority, m_Pids); + if (IsRunning() && m_Device != NULL) { + Dprintf("Attaching new receiver\n"); + Attach(); + } + } +} + +bool cStreamdevLiveStreamer::SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid) +{ + Dprintf("Initializing Remuxer for full channel transfer\n"); + //printf("ca pid: %d\n", Channel->Ca()); + m_Channel = Channel; + m_StreamType = StreamType; + + int apid[2] = { Apid, 0 }; + const int *Apids = Apid ? apid : m_Channel->Apids(); + const int *Dpids = Apid ? NULL : m_Channel->Dpids(); + + switch (m_StreamType) { + case stES: + { + int pid = ISRADIO(m_Channel) ? m_Channel->Apid(0) : m_Channel->Vpid(); + if (Apid != 0) + pid = Apid; + m_Remux = new cTS2ESRemux(pid); + return SetPids(pid); + } + + case stPES: + m_Remux = new cTS2PESRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), + m_Channel->Spids()); + return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); + + case stPS: + m_Remux = new cTS2PSRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), + m_Channel->Spids()); + return SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); + + case stExtern: + m_Remux = new cExternRemux(m_Channel->Vpid(), m_Channel->Apids(), m_Channel->Dpids(), + m_Channel->Spids(), m_Parameter); + // fall through + case stTS: + // This should never happen, but ... + if (m_PatFilter) { + Detach(); + DELETENULL(m_PatFilter); + } + // Set pids from cChannel + SetPids(m_Channel->Vpid(), Apids, Dpids, m_Channel->Spids()); + if (m_Channel->Vpid() != m_Channel->Ppid()) + SetPid(m_Channel->Ppid(), true); + // Set pids from PMT + m_PatFilter = new cStreamdevPatFilter(this, m_Channel); + return true; + + case stTSPIDS: + Dprintf("pid streaming mode\n"); + return true; + default: + return false; + } +} + +int cStreamdevLiveStreamer::Put(const uchar *Data, int Count) +{ + // insert si data + if (m_PatFilter) { + int siCount; + uchar *siData = m_PatFilter->Get(siCount); + if (siData) { + if (m_Remux) + siCount = m_Remux->Put(siData, siCount); + else + siCount = cStreamdevStreamer::Put(siData, siCount); + if (siCount) + m_PatFilter->Del(siCount); + } + } + if (m_Remux) + return m_Remux->Put(Data, Count); + else + return cStreamdevStreamer::Put(Data, Count); +} + +uchar *cStreamdevLiveStreamer::Get(int &Count) +{ + if (m_Remux) + return m_Remux->Get(Count); + else + return cStreamdevStreamer::Get(Count); +} + +void cStreamdevLiveStreamer::Del(int Count) +{ + if (m_Remux) + m_Remux->Del(Count); + else + cStreamdevStreamer::Del(Count); +} + +void cStreamdevLiveStreamer::Attach(void) +{ + Dprintf("cStreamdevLiveStreamer::Attach()\n"); + if (m_Device) { + if (m_Receiver) { + m_Device->Detach(m_Receiver); + m_Device->AttachReceiver(m_Receiver); + } + if (m_PatFilter) { + m_Device->Detach(m_PatFilter); + m_Device->AttachFilter(m_PatFilter); + } + } +} + +void cStreamdevLiveStreamer::Detach(void) +{ + Dprintf("cStreamdevLiveStreamer::Detach()\n"); + if (m_Device) { + if (m_Receiver) + m_Device->Detach(m_Receiver); + if (m_PatFilter) + m_Device->Detach(m_PatFilter); + } +} + +std::string cStreamdevLiveStreamer::Report(void) +{ + std::string result; + + if (m_Device != NULL) + result += (std::string)"+- Device is " + (const char*)itoa(m_Device->CardIndex()) + "\n"; + if (m_Receiver != NULL) + result += "+- Receiver is allocated\n"; + + result += "+- Pids are "; + for (int i = 0; i < MAXRECEIVEPIDS; ++i) + if (m_Pids[i] != 0) + result += (std::string)(const char*)itoa(m_Pids[i]) + ", "; + result += "\n"; + return result; +} + +// --- cStreamdevFilterStreamer ------------------------------------------------- + +cStreamdevFilterStreamer::cStreamdevFilterStreamer(): + cStreamdevStreamer("streamdev-filterstreaming"), + m_Device(NULL), + m_Filter(NULL)/*, + m_Channel(NULL)*/ +{ +} + +cStreamdevFilterStreamer::~cStreamdevFilterStreamer() +{ + Dprintf("Desctructing Filter streamer\n"); + Detach(); + m_Device = NULL; + DELETENULL(m_Filter); + Stop(); +} + +void cStreamdevFilterStreamer::Attach(void) +{ + Dprintf("cStreamdevFilterStreamer::Attach()\n"); + LOCK_THREAD; + if(m_Device && m_Filter) + m_Device->AttachFilter(m_Filter); +} + +void cStreamdevFilterStreamer::Detach(void) +{ + Dprintf("cStreamdevFilterStreamer::Detach()\n"); + LOCK_THREAD; + if(m_Device && m_Filter) + m_Device->Detach(m_Filter); +} + +#if 0 +void cStreamdevFilterStreamer::SetChannel(const cChannel *Channel) +{ + LOCK_THREAD; + Dprintf("cStreamdevFilterStreamer::SetChannel(%s : %s)", Channel?Channel->Name():"", + Channel ? *Channel->GetChannelID().ToString() : ""); + m_Channel = Channel; +} +#endif + +void cStreamdevFilterStreamer::SetDevice(cDevice *Device) +{ + Dprintf("cStreamdevFilterStreamer::SetDevice()\n"); + LOCK_THREAD; + if(Device != m_Device) { + Detach(); + m_Device = Device; + //m_Channel = NULL; + Attach(); + } +} + +bool cStreamdevFilterStreamer::SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On) +{ + Dprintf("cStreamdevFilterStreamer::SetFilter(%u,0x%x,0x%x,%s)\n", Pid, Tid, Mask, On?"On":"Off"); + + if(!m_Device) + return false; + + if (On) { + if (m_Filter == NULL) { + m_Filter = new cStreamdevLiveFilter(this); + Dprintf("attaching filter to device\n"); + Attach(); + } + m_Filter->Set(Pid, Tid, Mask); + } else if (m_Filter != NULL) + m_Filter->Del(Pid, Tid, Mask); + + return true; +} + +#if 0 +void cStreamdevFilterStreamer::ChannelSwitch(const cDevice *Device, int ChannelNumber) { + LOCK_THREAD; + if(Device == m_Device) { + if(ChannelNumber > 0) { + cChannel *ch = Channels.GetByNumber(ChannelNumber); + if(ch != NULL) { + if(m_Filter != NULL && + m_Channel != NULL && + (! TRANSPONDER(ch, m_Channel))) { + + isyslog("***** LiveFilterStreamer: transponder changed ! %s", + *ch->GetChannelID().ToString()); + + uchar buffer[TS_SIZE] = {TS_SYNC_BYTE, 0xff, 0xff, 0xff, 0x7f, 0}; + strcpy((char*)(buffer + 5), ch->GetChannelID().ToString()); + int p = Put(buffer, TS_SIZE); + if (p != TS_SIZE) + ReportOverflow(TS_SIZE - p); + } + m_Channel = ch; + } + } + } +} +#endif diff --git a/plugins/streamdev/streamdev-cvs/server/livestreamer.h b/plugins/streamdev/streamdev-cvs/server/livestreamer.h new file mode 100644 index 0000000..92448bb --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/livestreamer.h @@ -0,0 +1,82 @@ +#ifndef VDR_STREAMDEV_LIVESTREAMER_H +#define VDR_STREAMDEV_LIVESTREAMER_H + +#include +#include + +#include "server/streamer.h" +#include "common.h" + +namespace Streamdev { + class cTSRemux; +} +class cStreamdevPatFilter; +class cStreamdevLiveReceiver; + +// --- cStreamdevLiveStreamer ------------------------------------------------- + +class cStreamdevLiveStreamer: public cStreamdevStreamer { +private: + int m_Priority; + std::string m_Parameter; + int m_Pids[MAXRECEIVEPIDS + 1]; + int m_NumPids; + eStreamType m_StreamType; + const cChannel *m_Channel; + cDevice *m_Device; + cStreamdevLiveReceiver *m_Receiver; + cStreamdevPatFilter *m_PatFilter; + Streamdev::cTSRemux *m_Remux; + + void StartReceiver(void); + bool HasPid(int Pid); + +public: + cStreamdevLiveStreamer(int Priority, std::string Parameter = ""); + virtual ~cStreamdevLiveStreamer(); + + void SetDevice(cDevice *Device) { m_Device = Device; } + bool SetPid(int Pid, bool On); + bool SetPids(int Pid, const int *Pids1 = NULL, const int *Pids2 = NULL, const int *Pids3 = NULL); + bool SetChannel(const cChannel *Channel, eStreamType StreamType, int Apid = 0); + + virtual int Put(const uchar *Data, int Count); + virtual uchar *Get(int &Count); + virtual void Del(int Count); + + virtual void Attach(void); + virtual void Detach(void); + + // Statistical purposes: + virtual std::string Report(void); +}; + + +// --- cStreamdevFilterStreamer ------------------------------------------------- + +//#include + +class cStreamdevLiveFilter; + +class cStreamdevFilterStreamer: public cStreamdevStreamer /*, public cStatus*/ { +private: + cDevice *m_Device; + cStreamdevLiveFilter *m_Filter; + //const cChannel *m_Channel; + +public: + cStreamdevFilterStreamer(); + virtual ~cStreamdevFilterStreamer(); + + void SetDevice(cDevice *Device); + //void SetChannel(const cChannel *Channel); + bool SetFilter(u_short Pid, u_char Tid, u_char Mask, bool On); + + virtual void Attach(void); + virtual void Detach(void); + + // cStatus message handlers + //virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber); +}; + +#endif // VDR_STREAMDEV_LIVESTREAMER_H diff --git a/plugins/streamdev/streamdev-cvs/server/menuHTTP.c b/plugins/streamdev/streamdev-cvs/server/menuHTTP.c new file mode 100644 index 0000000..84a4fb7 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/menuHTTP.c @@ -0,0 +1,429 @@ +#include +#include "server/menuHTTP.h" + +//**************************** cChannelIterator ************** +cChannelIterator::cChannelIterator(cChannel *First): channel(First) +{} + +const cChannel* cChannelIterator::Next() +{ + const cChannel *current = channel; + channel = NextChannel(channel); + return current; +} + +//**************************** cListAll ************** +cListAll::cListAll(): cChannelIterator(Channels.First()) +{} + +const cChannel* cListAll::NextChannel(const cChannel *Channel) +{ + if (Channel) + Channel = Channels.Next(Channel); + return Channel; +} + +//**************************** cListChannels ************** +cListChannels::cListChannels(): cChannelIterator(Channels.Get(Channels.GetNextNormal(-1))) +{} + +const cChannel* cListChannels::NextChannel(const cChannel *Channel) +{ + if (Channel) + Channel = Channels.Get(Channels.GetNextNormal(Channel->Index())); + return Channel; +} + +// ********************* cListGroups **************** +cListGroups::cListGroups(): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1))) +{} + +const cChannel* cListGroups::NextChannel(const cChannel *Channel) +{ + if (Channel) + Channel = Channels.Get(Channels.GetNextGroup(Channel->Index())); + return Channel; +} +// +// ********************* cListGroup **************** +cListGroup::cListGroup(const cChannel *Group): cChannelIterator((Group && Group->GroupSep() && Channels.Next(Group) && !Channels.Next(Group)->GroupSep()) ? Channels.Next(Group) : NULL) +{} + +const cChannel* cListGroup::NextChannel(const cChannel *Channel) +{ + if (Channel) + Channel = Channels.Next(Channel); + return (Channel && !Channel->GroupSep()) ? Channel : NULL; +} +// +// ********************* cListTree **************** +cListTree::cListTree(const cChannel *SelectedGroup): cChannelIterator(Channels.Get(Channels.GetNextGroup(-1))) +{ + selectedGroup = SelectedGroup; + currentGroup = Channels.Get(Channels.GetNextGroup(-1)); +} + +const cChannel* cListTree::NextChannel(const cChannel *Channel) +{ + if (currentGroup == selectedGroup) + { + if (Channel) + Channel = Channels.Next(Channel); + if (Channel && Channel->GroupSep()) + currentGroup = Channel; + } + else + { + if (Channel) + Channel = Channels.Get(Channels.GetNextGroup(Channel->Index())); + currentGroup = Channel; + } + return Channel; +} + +// ******************** cChannelList ****************** +cChannelList::cChannelList(cChannelIterator *Iterator) : iterator(Iterator) +{} + +cChannelList::~cChannelList() +{ + delete iterator; +} + +int cChannelList::GetGroupIndex(const cChannel *Group) +{ + int index = 0; + for (int curr = Channels.GetNextGroup(-1); curr >= 0; curr = Channels.GetNextGroup(curr)) + { + if (Channels.Get(curr) == Group) + return index; + index++; + } + return -1; +} + +const cChannel* cChannelList::GetGroup(int Index) +{ + int group = Channels.GetNextGroup(-1); + while (Index-- && group >= 0) + group = Channels.GetNextGroup(group); + return group >= 0 ? Channels.Get(group) : NULL; +} + +// ******************** cHtmlChannelList ****************** +const char* cHtmlChannelList::menu = + "[Home (no script)] " + "[Tree View] " + "[Groups (Playlist)] " + "[Channels (Playlist)] "; + +const char* cHtmlChannelList::css = + ""; + +const char* cHtmlChannelList::js = + ""; + + +std::string cHtmlChannelList::StreamTypeMenu() +{ + std::string typeMenu; + typeMenu += (streamType == stTS ? (std::string) "[TS] " : + (std::string) "[TS] "); + typeMenu += (streamType == stPS ? (std::string) "[PS] " : + (std::string) "[PS] "); + typeMenu += (streamType == stPES ? (std::string) "[PES] " : + (std::string) "[PES] "); + typeMenu += (streamType == stES ? (std::string) "[ES] " : + (std::string) "[ES] "); + typeMenu += (streamType == stExtern ? (std::string) "[Extern] " : + (std::string) "[Extern] "); + return typeMenu; +} + +cHtmlChannelList::cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget): cChannelList(Iterator) +{ + streamType = StreamType; + self = strdup(Self); + groupTarget = (GroupTarget && *GroupTarget) ? strdup(GroupTarget) : NULL; + htmlState = hsRoot; + current = NULL; +} + +cHtmlChannelList::~cHtmlChannelList() +{ + free((void *) self); + free((void *) groupTarget); +} + +bool cHtmlChannelList::HasNext() +{ + return htmlState != hsPageBottom; +} + +std::string cHtmlChannelList::Next() +{ + switch (htmlState) + { + case hsRoot: + htmlState = hsHtmlHead; + break; + case hsHtmlHead: + htmlState = hsCss; + break; + case hsCss: + htmlState = *self ? hsPageTop : hsJs; + break; + case hsJs: + htmlState = hsPageTop; + break; + case hsPageTop: + current = NextChannel(); + htmlState = current ? (current->GroupSep() ? hsGroupTop : hsPlainTop) : hsPageBottom; + break; + case hsPlainTop: + htmlState = hsPlainItem; + break; + case hsPlainItem: + current = NextChannel(); + htmlState = current && !current->GroupSep() ? hsPlainItem : hsPlainBottom; + break; + case hsPlainBottom: + htmlState = current ? hsGroupTop : hsPageBottom; + break; + case hsGroupTop: + current = NextChannel(); + htmlState = current && !current->GroupSep() ? hsItemsTop : hsGroupBottom; + break; + case hsItemsTop: + htmlState = hsItem; + break; + case hsItem: + current = NextChannel(); + htmlState = current && !current->GroupSep() ? hsItem : hsItemsBottom; + break; + case hsItemsBottom: + htmlState = hsGroupBottom; + break; + case hsGroupBottom: + htmlState = current ? hsGroupTop : hsPageBottom; + break; + case hsPageBottom: + default: + esyslog("streamdev-server cHtmlChannelList: invalid call to Next()"); + break; + } + switch (htmlState) + { + // NOTE: JavaScript requirements: + // Group title is identified by

tag + // Channel list must be a sibling of

with class "items" + case hsHtmlHead: return "" + HtmlHead(); + case hsCss: return css; + case hsJs: return js; + case hsPageTop: return "" + PageTop() + "
"; + case hsGroupTop: return "

" + GroupTitle() + "

"; + case hsItemsTop: + case hsPlainTop: return "
    "; + case hsItem: + case hsPlainItem: return ItemText(); + case hsItemsBottom: + case hsPlainBottom: return "
"; + case hsGroupBottom: return "
"; + case hsPageBottom: return "
" + PageBottom() + ""; + default: return ""; + } +} + +std::string cHtmlChannelList::HtmlHead() +{ + return (std::string) ""; +} + +std::string cHtmlChannelList::PageTop() +{ + return (std::string) "
" + menu + "
" + StreamTypeMenu() + "
"; +} + +std::string cHtmlChannelList::PageBottom() +{ + return (std::string) ""; +} + +std::string cHtmlChannelList::GroupTitle() +{ + if (groupTarget) + { + return (std::string) "" + current->Name() + ""; + } + else + { + return (std::string) current->Name(); + } +} + +std::string cHtmlChannelList::ItemText() +{ + std::string line; + std::string suffix; + + switch (streamType) { + case stTS: suffix = (std::string) ".ts"; break; + case stPS: suffix = (std::string) ".vob"; break; + // for Network Media Tank + case stPES: suffix = (std::string) ".vdr"; break; + default: suffix = ""; + } + line += (std::string) "
  • Number()) + "\">"; + line += (std::string) "GetChannelID().ToString() + suffix + "\""; + + // for Network Media Tank + line += (std::string) " vod "; + if (current->Number() < 1000) + line += (std::string) " tvid=\"" + (const char*) itoa(current->Number()) + "\""; + + line += (std::string) ">" + current->Name() + ""; + + int count = 0; + for (int i = 0; current->Apid(i) != 0; ++i, ++count) + ; + for (int i = 0; current->Dpid(i) != 0; ++i, ++count) + ; + + if (count > 1) + { + int index = 1; + for (int i = 0; current->Apid(i) != 0; ++i, ++index) { + line += (std::string) " GetChannelID().ToString() + + "+" + (const char*)itoa(index) + suffix + "\" class=\"apid\" vod>" + current->Alang(i) + ""; + } + for (int i = 0; current->Dpid(i) != 0; ++i, ++index) { + line += (std::string) " GetChannelID().ToString() + + "+" + (const char*)itoa(index) + suffix + "\" class=\"dpid\" vod>" + current->Dlang(i) + ""; + } + } + line += "
  • "; + return line; +} + +// ******************** cM3uChannelList ****************** +cM3uChannelList::cM3uChannelList(cChannelIterator *Iterator, const char* Base) +: cChannelList(Iterator), + m_IConv(cCharSetConv::SystemCharacterTable(), "UTF-8") +{ + base = strdup(Base); + m3uState = msFirst; +} + +cM3uChannelList::~cM3uChannelList() +{ + free(base); +} + +bool cM3uChannelList::HasNext() +{ + return m3uState != msLast; +} + +std::string cM3uChannelList::Next() +{ + if (m3uState == msFirst) + { + m3uState = msContinue; + return "#EXTM3U"; + } + + const cChannel *channel = NextChannel(); + if (!channel) + { + m3uState = msLast; + return ""; + } + + std::string name = (std::string) m_IConv.Convert(channel->Name()); + + if (channel->GroupSep()) + { + return (std::string) "#EXTINF:-1," + name + "\r\n" + + base + "group.m3u?group=" + + (const char*) itoa(cChannelList::GetGroupIndex(channel)); + } + else + { + return (std::string) "#EXTINF:-1," + + (const char*) itoa(channel->Number()) + " " + name + "\r\n" + + base + (std::string) channel->GetChannelID().ToString(); + } +} + diff --git a/plugins/streamdev/streamdev-cvs/server/menuHTTP.h b/plugins/streamdev/streamdev-cvs/server/menuHTTP.h new file mode 100644 index 0000000..cbd7b59 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/menuHTTP.h @@ -0,0 +1,143 @@ +#ifndef VDR_STREAMDEV_SERVERS_MENUHTTP_H +#define VDR_STREAMDEV_SERVERS_MENUHTTP_H + +#include +#include "../common.h" + +class cChannel; + +// ******************** cChannelIterator ****************** +class cChannelIterator +{ + private: + const cChannel *channel; + protected: + virtual const cChannel* NextChannel(const cChannel *Channel) = 0; + public: + const cChannel* Next(); + cChannelIterator(cChannel *First); + virtual ~cChannelIterator() {}; +}; + +class cListAll: public cChannelIterator +{ + protected: + virtual const cChannel* NextChannel(const cChannel *Channel); + public: + cListAll(); + virtual ~cListAll() {}; +}; + +class cListChannels: public cChannelIterator +{ + protected: + virtual const cChannel* NextChannel(const cChannel *Channel); + public: + cListChannels(); + virtual ~cListChannels() {}; +}; + +class cListGroups: public cChannelIterator +{ + protected: + virtual const cChannel* NextChannel(const cChannel *Channel); + public: + cListGroups(); + virtual ~cListGroups() {}; +}; + +class cListGroup: public cChannelIterator +{ + protected: + virtual const cChannel* NextChannel(const cChannel *Channel); + public: + cListGroup(const cChannel *Group); + virtual ~cListGroup() {}; +}; + +class cListTree: public cChannelIterator +{ + private: + const cChannel* selectedGroup; + const cChannel* currentGroup; + protected: + virtual const cChannel* NextChannel(const cChannel *Channel); + public: + cListTree(const cChannel *SelectedGroup); + virtual ~cListTree() {}; +}; + +// ******************** cChannelList ****************** +class cChannelList +{ + private: + cChannelIterator *iterator; + protected: + const cChannel* NextChannel() { return iterator->Next(); } + public: + // Helper which returns the group index + static int GetGroupIndex(const cChannel* Group); + // Helper which returns the group by its index + static const cChannel* GetGroup(int Index); + + virtual std::string HttpHeader() { return "HTTP/1.0 200 OK\r\n"; }; + virtual bool HasNext() = 0; + virtual std::string Next() = 0; + cChannelList(cChannelIterator *Iterator); + virtual ~cChannelList(); +}; + +class cHtmlChannelList: public cChannelList +{ + private: + static const char* menu; + static const char* css; + static const char* js; + + enum eHtmlState { + hsRoot, hsHtmlHead, hsCss, hsJs, hsPageTop, hsPageBottom, + hsGroupTop, hsGroupBottom, + hsPlainTop, hsPlainItem, hsPlainBottom, + hsItemsTop, hsItem, hsItemsBottom + }; + eHtmlState htmlState; + const cChannel *current; + eStreamType streamType; + const char* self; + const char* groupTarget; + + std::string StreamTypeMenu(); + std::string HtmlHead(); + std::string PageTop(); + std::string GroupTitle(); + std::string ItemText(); + std::string PageBottom(); + public: + virtual std::string HttpHeader() { + return cChannelList::HttpHeader() + + "Content-type: text/html; charset=" + + (cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8") + + "\r\n"; + } + virtual bool HasNext(); + virtual std::string Next(); + cHtmlChannelList(cChannelIterator *Iterator, eStreamType StreamType, const char *Self, const char *GroupTarget); + virtual ~cHtmlChannelList(); +}; + +class cM3uChannelList: public cChannelList +{ + private: + char *base; + enum eM3uState { msFirst, msContinue, msLast }; + eM3uState m3uState; + cCharSetConv m_IConv; + public: + virtual std::string HttpHeader() { return cChannelList::HttpHeader() + "Content-type: audio/x-mpegurl; charset=UTF-8\r\n"; }; + virtual bool HasNext(); + virtual std::string Next(); + cM3uChannelList(cChannelIterator *Iterator, const char* Base); + virtual ~cM3uChannelList(); +}; + +#endif diff --git a/plugins/streamdev/streamdev-cvs/server/recplayer.c b/plugins/streamdev/streamdev-cvs/server/recplayer.c new file mode 100644 index 0000000..f45d8c3 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/recplayer.c @@ -0,0 +1,288 @@ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + and adopted for streamdev to play recordings + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "recplayer.h" + +#define _XOPEN_SOURCE 600 +#include + +RecPlayer::RecPlayer(cRecording* rec) +{ + file = NULL; + fileOpen = 0; + lastPosition = 0; + recording = rec; + for(int i = 1; i < 1000; i++) segments[i] = NULL; + + // FIXME find out max file path / name lengths + +#if VDRVERSNUM >= 10703 + indexFile = new cIndexFile(recording->FileName(), false, rec->IsPesRecording()); +#else + indexFile = new cIndexFile(recording->FileName(), false); +#endif + if (!indexFile) esyslog("ERROR: Streamdev: Failed to create indexfile!"); + + scan(); +} + +void RecPlayer::scan() +{ + if (file) fclose(file); + totalLength = 0; + fileOpen = 0; + totalFrames = 0; + + int i = 1; + while(segments[i++]) delete segments[i]; + + char fileName[2048]; + for(i = 1; i < 1000; i++) + { + +#if APIVERSNUM < 10703 + snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); + //log->log("RecPlayer", Log::DEBUG, "FILENAME: %s", fileName); + file = fopen(fileName, "r"); +#else + snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), i); + file = fopen(fileName, "r"); + if (!file) { + snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), i); + file = fopen(fileName, "r"); + } +#endif + if (!file) break; + + segments[i] = new Segment(); + segments[i]->start = totalLength; + fseek(file, 0, SEEK_END); + totalLength += ftell(file); + totalFrames = indexFile->Last(); + //log->log("RecPlayer", Log::DEBUG, "File %i found, totalLength now %llu, numFrames = %lu", i, totalLength, totalFrames); + segments[i]->end = totalLength; + fclose(file); + } + + file = NULL; +} + +RecPlayer::~RecPlayer() +{ + //log->log("RecPlayer", Log::DEBUG, "destructor"); + int i = 1; + while(segments[i++]) delete segments[i]; + if (file) fclose(file); +} + +int RecPlayer::openFile(int index) +{ + if (file) fclose(file); + + char fileName[2048]; + +#if APIVERSNUM >= 10703 + snprintf(fileName, 2047, "%s/%05i.ts", recording->FileName(), index); + isyslog("openFile called for index %i string:%s", index, fileName); + + file = fopen(fileName, "r"); + if (file) + { + fileOpen = index; + return 1; + } +#endif + + snprintf(fileName, 2047, "%s/%03i.vdr", recording->FileName(), index); + isyslog("openFile called for index %i string:%s", index, fileName); + //log->log("RecPlayer", Log::DEBUG, "openFile called for index %i string:%s", index, fileName); + + file = fopen(fileName, "r"); + if (file) + { + fileOpen = index; + return 1; + } + + //log->log("RecPlayer", Log::DEBUG, "file failed to open"); + fileOpen = 0; + return 0; +} + +uint64_t RecPlayer::getLengthBytes() +{ + return totalLength; +} + +uint32_t RecPlayer::getLengthFrames() +{ + return totalFrames; +} + +unsigned long RecPlayer::getBlock(unsigned char* buffer, uint64_t position, unsigned long amount) +{ + if ((amount > totalLength) || (amount > 500000)) + { + //log->log("RecPlayer", Log::DEBUG, "Amount %lu requested and rejected", amount); + return 0; + } + + if (position >= totalLength) + { + //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); + return 0; + } + + if ((position + amount) > totalLength) + { + //log->log("RecPlayer", Log::DEBUG, "Client asked for some data past the end of recording, adjusting amount"); + amount = totalLength - position; + } + + // work out what block position is in + int segmentNumber; + for(segmentNumber = 1; segmentNumber < 1000; segmentNumber++) + { + if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break; + // position is in this block + } + + // we could be seeking around + if (segmentNumber != fileOpen) + { + if (!openFile(segmentNumber)) return 0; + } + + uint64_t currentPosition = position; + uint32_t yetToGet = amount; + uint32_t got = 0; + uint32_t getFromThisSegment = 0; + uint32_t filePosition; + + while(got < amount) + { + if (got) + { + // if(got) then we have already got some and we are back around + // advance the file pointer to the next file + if (!openFile(++segmentNumber)) return 0; + } + + // is the request completely in this block? + if ((currentPosition + yetToGet) <= segments[segmentNumber]->end) + getFromThisSegment = yetToGet; + else + getFromThisSegment = segments[segmentNumber]->end - currentPosition; + + filePosition = currentPosition - segments[segmentNumber]->start; + fseek(file, filePosition, SEEK_SET); + if (fread(&buffer[got], getFromThisSegment, 1, file) != 1) return 0; // umm, big problem. + + // Tell linux not to bother keeping the data in the FS cache + posix_fadvise(file->_fileno, filePosition, getFromThisSegment, POSIX_FADV_DONTNEED); + + got += getFromThisSegment; + currentPosition += getFromThisSegment; + yetToGet -= getFromThisSegment; + } + + lastPosition = position; + return got; +} + +uint64_t RecPlayer::getLastPosition() +{ + return lastPosition; +} + +cRecording* RecPlayer::getCurrentRecording() +{ + return recording; +} + +uint64_t RecPlayer::positionFromFrameNumber(uint32_t frameNumber) +{ + if (!indexFile) return 0; + +#if VDRVERSNUM >= 10703 + uint16_t retFileNumber; + off_t retFileOffset; +#else + uchar retFileNumber; + int retFileOffset; +#endif + + if (!indexFile->Get((int)frameNumber, &retFileNumber, &retFileOffset)) + { + return 0; + } + +// log->log("RecPlayer", Log::DEBUG, "FN: %u FO: %i", retFileNumber, retFileOffset); + if (!segments[retFileNumber]) return 0; + uint64_t position = segments[retFileNumber]->start + retFileOffset; +// log->log("RecPlayer", Log::DEBUG, "Pos: %llu", position); + + return position; +} + +uint32_t RecPlayer::frameNumberFromPosition(uint64_t position) +{ + if (!indexFile) return 0; + + if (position >= totalLength) + { + //log->log("RecPlayer", Log::DEBUG, "Client asked for data starting past end of recording!"); + return 0; + } + + uint8_t segmentNumber; + for(segmentNumber = 1; segmentNumber < 255; segmentNumber++) + { + if ((position >= segments[segmentNumber]->start) && (position < segments[segmentNumber]->end)) break; + // position is in this block + } + uint32_t askposition = position - segments[segmentNumber]->start; + return indexFile->Get((int)segmentNumber, askposition); + +} + + +bool RecPlayer::getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength) +{ + // 0 = backwards + // 1 = forwards + + if (!indexFile) return false; + + int iframeLength; + int indexReturnFrameNumber; + + indexReturnFrameNumber = (uint32_t)indexFile->GetNextIFrame(frameNumber, (direction==1 ? true : false), NULL, NULL, &iframeLength); + //log->log("RecPlayer", Log::DEBUG, "GNIF input framenumber:%lu, direction=%lu, output:framenumber=%i, framelength=%i", frameNumber, direction, indexReturnFrameNumber, iframeLength); + + if (indexReturnFrameNumber == -1) return false; + + *rfilePosition = positionFromFrameNumber(indexReturnFrameNumber); + *rframeNumber = (uint32_t)indexReturnFrameNumber; + *rframeLength = (uint32_t)iframeLength; + + return true; +} diff --git a/plugins/streamdev/streamdev-cvs/server/recplayer.h b/plugins/streamdev/streamdev-cvs/server/recplayer.h new file mode 100644 index 0000000..3da6c89 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/recplayer.h @@ -0,0 +1,63 @@ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef RECPLAYER_H +#define RECPLAYER_H + +#include +#include + +#include "server/streamer.h" + +class Segment +{ + public: + uint64_t start; + uint64_t end; +}; + +class RecPlayer +{ + public: + RecPlayer(cRecording* rec); + ~RecPlayer(); + uint64_t getLengthBytes(); + uint32_t getLengthFrames(); + unsigned long getBlock(unsigned char* buffer, uint64_t position, unsigned long amount); + int openFile(int index); + uint64_t getLastPosition(); + cRecording* getCurrentRecording(); + void scan(); + uint64_t positionFromFrameNumber(uint32_t frameNumber); + uint32_t frameNumberFromPosition(uint64_t position); + bool getNextIFrame(uint32_t frameNumber, uint32_t direction, uint64_t* rfilePosition, uint32_t* rframeNumber, uint32_t* rframeLength); + + private: + cRecording* recording; + cIndexFile* indexFile; + FILE* file; + int fileOpen; + Segment* segments[1000]; + uint64_t totalLength; + uint64_t lastPosition; + uint32_t totalFrames; +}; + +#endif diff --git a/plugins/streamdev/streamdev-cvs/server/server.c b/plugins/streamdev/streamdev-cvs/server/server.c new file mode 100644 index 0000000..1bdb20a --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/server.c @@ -0,0 +1,173 @@ +/* + * $Id: server.c,v 1.10 2009/02/13 10:39:22 schmirl Exp $ + */ + +#include "server/server.h" +#include "server/componentVTP.h" +#include "server/componentHTTP.h" +#include "server/componentIGMP.h" +#include "server/setup.h" + +#include +#include +#include +#include + +cSVDRPhosts StreamdevHosts; +char *opt_auth = NULL; +char *opt_remux = NULL; + +cStreamdevServer *cStreamdevServer::m_Instance = NULL; +cList cStreamdevServer::m_Servers; +cList cStreamdevServer::m_Clients; + +cStreamdevServer::cStreamdevServer(void): + cThread("streamdev server") +{ + Start(); +} + +cStreamdevServer::~cStreamdevServer() +{ + Stop(); +} + +void cStreamdevServer::Initialize(void) +{ + if (m_Instance == NULL) { + if (StreamdevServerSetup.StartVTPServer) Register(new cComponentVTP); + if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP); + if (StreamdevServerSetup.StartIGMPServer) { + if (strcmp(StreamdevServerSetup.IGMPBindIP, "0.0.0.0") == 0) + esyslog("streamdev-server: Not starting IGMP. IGMP must be bound to a local IP"); + else + Register(new cComponentIGMP); + } + + m_Instance = new cStreamdevServer; + } +} + +void cStreamdevServer::Destruct(void) +{ + DELETENULL(m_Instance); +} + +void cStreamdevServer::Stop(void) +{ + if (Running()) + Cancel(3); +} + +void cStreamdevServer::Register(cServerComponent *Server) +{ + m_Servers.Add(Server); +} + +void cStreamdevServer::Action(void) +{ + /* Initialize Server components, deleting those that failed */ + for (cServerComponent *c = m_Servers.First(); c;) { + cServerComponent *next = m_Servers.Next(c); + if (!c->Initialize()) + m_Servers.Del(c); + c = next; + } + + if (m_Servers.Count() == 0) { + esyslog("ERROR: no streamdev server activated, exiting"); + Cancel(-1); + } + + cTBSelect select; + while (Running()) { + select.Clear(); + + /* Ask all Server components to register to the selector */ + for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)) + select.Add(c->Socket(), false); + + /* Ask all Client connections to register to the selector */ + for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) + { + select.Add(s->Socket(), false); + if (s->HasData()) + select.Add(s->Socket(), true); + } + + int sel; + do + { + sel = select.Select(400); + if (sel < 0 && errno == ETIMEDOUT) { + // check for aborted clients + for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) { + if (s->Abort()) + sel = 0; + } + } + } while (sel < 0 && errno == ETIMEDOUT && Running()); + + if (!Running()) + break; + if (sel < 0) { + esyslog("fatal error, server exiting: %m"); + break; + } + + /* Ask all Server components to act on signalled sockets */ + for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){ + if (sel && select.CanRead(c->Socket())) { + cServerConnection *client = c->Accept(); + if (!client) + continue; + m_Clients.Add(client); + + if (m_Clients.Count() > StreamdevServerSetup.MaxClients) { + esyslog("streamdev: too many clients, rejecting %s:%d", + client->RemoteIp().c_str(), client->RemotePort()); + client->Reject(); + } else if (!client->CanAuthenticate() && !StreamdevHosts.Acceptable(client->RemoteIpAddr())) { + esyslog("streamdev: client %s:%d not allowed to connect", + client->RemoteIp().c_str(), client->RemotePort()); + client->Reject(); + } else + client->Welcome(); + } + } + + /* Ask all Client connections to act on signalled sockets */ + for (cServerConnection *s = m_Clients.First(); s;) { + bool result = true; + + if (sel && select.CanWrite(s->Socket())) + result = s->Write(); + + if (sel && result && select.CanRead(s->Socket())) + result = s->Read(); + + result &= !s->Abort(); + + cServerConnection *next = m_Clients.Next(s); + if (!result) { + isyslog("streamdev: closing streamdev connection to %s:%d", + s->RemoteIp().c_str(), s->RemotePort()); + s->Close(); + m_Clients.Del(s); + } + s = next; + } + } + + while (m_Clients.Count() > 0) { + cServerConnection *s = m_Clients.First(); + s->Close(); + m_Clients.Del(s); + } + + while (m_Servers.Count() > 0) { + cServerComponent *c = m_Servers.First(); + c->Destruct(); + m_Servers.Del(c); + } +} diff --git a/plugins/streamdev/streamdev-cvs/server/server.h b/plugins/streamdev/streamdev-cvs/server/server.h new file mode 100644 index 0000000..a44df1c --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/server.h @@ -0,0 +1,49 @@ +/* + * $Id: server.h,v 1.6 2008/10/22 11:59:32 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SERVER_H +#define VDR_STREAMDEV_SERVER_H + +#include + +#include "server/component.h" +#include "server/connection.h" + +#define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh")) +#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf")) + +extern char *opt_auth; +extern char *opt_remux; + +class cStreamdevServer: public cThread { +private: + static cStreamdevServer *m_Instance; + static cList m_Servers; + static cList m_Clients; + +protected: + void Stop(void); + + virtual void Action(void); + + static void Register(cServerComponent *Server); + +public: + cStreamdevServer(void); + virtual ~cStreamdevServer(); + + static void Initialize(void); + static void Destruct(void); + static bool Active(void); +}; + +inline bool cStreamdevServer::Active(void) +{ + return m_Instance != NULL + && m_Instance->m_Clients.Count() > 0; +} + +extern cSVDRPhosts StreamdevHosts; + +#endif // VDR_STREAMDEV_SERVER_H diff --git a/plugins/streamdev/streamdev-cvs/server/setup.c b/plugins/streamdev/streamdev-cvs/server/setup.c new file mode 100644 index 0000000..db709db --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/setup.c @@ -0,0 +1,141 @@ +/* + * $Id: setup.c,v 1.9 2009/10/13 06:38:47 schmirl Exp $ + */ + +#include + +#include "server/setup.h" +#include "server/server.h" + +cStreamdevServerSetup StreamdevServerSetup; + +cStreamdevServerSetup::cStreamdevServerSetup(void) { + MaxClients = 5; + StartVTPServer = true; + VTPServerPort = 2004; + StartHTTPServer = true; + HTTPServerPort = 3000; + HTTPStreamType = stTS; + StartIGMPServer = false; + IGMPClientPort = 1234; + IGMPStreamType = stTS; + SuspendMode = smAlways; + AllowSuspend = false; + strcpy(VTPBindIP, "0.0.0.0"); + strcpy(HTTPBindIP, "0.0.0.0"); + strcpy(IGMPBindIP, "0.0.0.0"); +} + +bool cStreamdevServerSetup::SetupParse(const char *Name, const char *Value) { + if (strcmp(Name, "MaxClients") == 0) MaxClients = atoi(Value); + else if (strcmp(Name, "StartServer") == 0) StartVTPServer = atoi(Value); + else if (strcmp(Name, "ServerPort") == 0) VTPServerPort = atoi(Value); + else if (strcmp(Name, "VTPBindIP") == 0) strcpy(VTPBindIP, Value); + else if (strcmp(Name, "StartHTTPServer") == 0) StartHTTPServer = atoi(Value); + else if (strcmp(Name, "HTTPServerPort") == 0) HTTPServerPort = atoi(Value); + else if (strcmp(Name, "HTTPStreamType") == 0) HTTPStreamType = atoi(Value); + else if (strcmp(Name, "HTTPBindIP") == 0) strcpy(HTTPBindIP, Value); + else if (strcmp(Name, "StartIGMPServer") == 0) StartIGMPServer = atoi(Value); + else if (strcmp(Name, "IGMPClientPort") == 0) IGMPClientPort = atoi(Value); + else if (strcmp(Name, "IGMPStreamType") == 0) IGMPStreamType = atoi(Value); + else if (strcmp(Name, "IGMPBindIP") == 0) strcpy(IGMPBindIP, Value); + else if (strcmp(Name, "SuspendMode") == 0) SuspendMode = atoi(Value); + else if (strcmp(Name, "AllowSuspend") == 0) AllowSuspend = atoi(Value); + else return false; + return true; +} + +const char* cStreamdevServerMenuSetupPage::StreamTypes[st_Count - 1] = { + "TS", + "PES", + "PS", + "ES", + "Extern" +}; + +const char* cStreamdevServerMenuSetupPage::SuspendModes[sm_Count] = { + trNOOP("Offer suspend mode"), + trNOOP("Always suspended"), + trNOOP("Never suspended") +}; + +cStreamdevServerMenuSetupPage::cStreamdevServerMenuSetupPage(void) { + m_NewSetup = StreamdevServerSetup; + + static const char* modes[sm_Count]; + for (int i = 0; i < sm_Count; i++) + modes[i] = tr(SuspendModes[i]); + + AddCategory (tr("Common Settings")); + Add(new cMenuEditIntItem (tr("Maximum Number of Clients"), &m_NewSetup.MaxClients, 0, 100)); + + Add(new cMenuEditStraItem(tr("Suspend behaviour"), &m_NewSetup.SuspendMode, sm_Count, modes)); + Add(new cMenuEditBoolItem(tr("Client may suspend"), &m_NewSetup.AllowSuspend)); + + AddCategory (tr("VDR-to-VDR Server")); + Add(new cMenuEditBoolItem(tr("Start VDR-to-VDR Server"), &m_NewSetup.StartVTPServer)); + Add(new cMenuEditIntItem (tr("VDR-to-VDR Server Port"), &m_NewSetup.VTPServerPort, 0, 65535)); + Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.VTPBindIP)); + + AddCategory (tr("HTTP Server")); + Add(new cMenuEditBoolItem(tr("Start HTTP Server"), &m_NewSetup.StartHTTPServer)); + Add(new cMenuEditIntItem (tr("HTTP Server Port"), &m_NewSetup.HTTPServerPort, 0, 65535)); + Add(new cMenuEditStraItem(tr("HTTP Streamtype"), &m_NewSetup.HTTPStreamType, st_Count - 1, StreamTypes)); + Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.HTTPBindIP)); + AddCategory (tr("Multicast Streaming Server")); + Add(new cMenuEditBoolItem(tr("Start IGMP Server"), &m_NewSetup.StartIGMPServer)); + Add(new cMenuEditIntItem (tr("Multicast Client Port"), &m_NewSetup.IGMPClientPort, 0, 65535)); + Add(new cMenuEditStraItem(tr("Multicast Streamtype"), &m_NewSetup.IGMPStreamType, st_Count - 1, StreamTypes)); + Add(new cMenuEditIpItem (tr("Bind to IP"), m_NewSetup.IGMPBindIP)); + SetCurrent(Get(1)); +} + +cStreamdevServerMenuSetupPage::~cStreamdevServerMenuSetupPage() { +} + +void cStreamdevServerMenuSetupPage::AddCategory(const char *Title) { + + cString str = cString::sprintf("--- %s -------------------------------------------------" + "---------------", Title ); + + cOsdItem *item = new cOsdItem(*str); + item->SetSelectable(false); + Add(item); +} + +void cStreamdevServerMenuSetupPage::Store(void) { + bool restart = false; + if (m_NewSetup.StartVTPServer != StreamdevServerSetup.StartVTPServer + || m_NewSetup.VTPServerPort != StreamdevServerSetup.VTPServerPort + || strcmp(m_NewSetup.VTPBindIP, StreamdevServerSetup.VTPBindIP) != 0 + || m_NewSetup.StartHTTPServer != StreamdevServerSetup.StartHTTPServer + || m_NewSetup.HTTPServerPort != StreamdevServerSetup.HTTPServerPort + || strcmp(m_NewSetup.HTTPBindIP, StreamdevServerSetup.HTTPBindIP) != 0 + || m_NewSetup.StartIGMPServer != StreamdevServerSetup.StartIGMPServer + || m_NewSetup.IGMPClientPort != StreamdevServerSetup.IGMPClientPort + || strcmp(m_NewSetup.IGMPBindIP, StreamdevServerSetup.IGMPBindIP) != 0) { + restart = true; + cStreamdevServer::Destruct(); + } + + SetupStore("MaxClients", m_NewSetup.MaxClients); + SetupStore("StartServer", m_NewSetup.StartVTPServer); + SetupStore("ServerPort", m_NewSetup.VTPServerPort); + SetupStore("VTPBindIP", m_NewSetup.VTPBindIP); + SetupStore("StartHTTPServer", m_NewSetup.StartHTTPServer); + SetupStore("HTTPServerPort", m_NewSetup.HTTPServerPort); + SetupStore("HTTPStreamType", m_NewSetup.HTTPStreamType); + SetupStore("HTTPBindIP", m_NewSetup.HTTPBindIP); + SetupStore("StartIGMPServer", m_NewSetup.StartIGMPServer); + SetupStore("IGMPClientPort", m_NewSetup.IGMPClientPort); + SetupStore("IGMPStreamType", m_NewSetup.IGMPStreamType); + SetupStore("IGMPBindIP", m_NewSetup.IGMPBindIP); + SetupStore("SuspendMode", m_NewSetup.SuspendMode); + SetupStore("AllowSuspend", m_NewSetup.AllowSuspend); + + StreamdevServerSetup = m_NewSetup; + + if (restart) + cStreamdevServer::Initialize(); +} + diff --git a/plugins/streamdev/streamdev-cvs/server/setup.h b/plugins/streamdev/streamdev-cvs/server/setup.h new file mode 100644 index 0000000..d22ab34 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/setup.h @@ -0,0 +1,48 @@ +/* + * $Id: setup.h,v 1.3 2009/09/18 10:43:26 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SETUPSERVER_H +#define VDR_STREAMDEV_SETUPSERVER_H + +#include "common.h" + +struct cStreamdevServerSetup { + cStreamdevServerSetup(void); + + bool SetupParse(const char *Name, const char *Value); + + int MaxClients; + int StartVTPServer; + int VTPServerPort; + char VTPBindIP[20]; + int StartHTTPServer; + int HTTPServerPort; + int HTTPStreamType; + char HTTPBindIP[20]; + int StartIGMPServer; + int IGMPClientPort; + int IGMPStreamType; + char IGMPBindIP[20]; + int SuspendMode; + int AllowSuspend; +}; + +extern cStreamdevServerSetup StreamdevServerSetup; + +class cStreamdevServerMenuSetupPage: public cMenuSetupPage { +private: + static const char* StreamTypes[]; + static const char* SuspendModes[]; + cStreamdevServerSetup m_NewSetup; + + void AddCategory(const char *Title); +protected: + virtual void Store(void); + +public: + cStreamdevServerMenuSetupPage(void); + virtual ~cStreamdevServerMenuSetupPage(); +}; + +#endif // VDR_STREAMDEV_SETUPSERVER_H diff --git a/plugins/streamdev/streamdev-cvs/server/streamer.c b/plugins/streamdev/streamdev-cvs/server/streamer.c new file mode 100644 index 0000000..42e7efa --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/streamer.c @@ -0,0 +1,162 @@ +/* + * $Id: streamer.c,v 1.19 2009/06/19 06:32:45 schmirl Exp $ + */ + +#include +#include +#include +#include + +#include "server/streamer.h" +#include "server/suspend.h" +#include "server/setup.h" +#include "tools/socket.h" +#include "tools/select.h" +#include "common.h" + +// --- cStreamdevBuffer ------------------------------------------------------- + +cStreamdevBuffer::cStreamdevBuffer(int Size, int Margin, bool Statistics, const char *Description): + cRingBufferLinear(Size, Margin, Statistics, Description) +{ +} + +// --- cStreamdevWriter ------------------------------------------------------- + +cStreamdevWriter::cStreamdevWriter(cTBSocket *Socket, + cStreamdevStreamer *Streamer): + cThread("streamdev-writer"), + m_Streamer(Streamer), + m_Socket(Socket) +{ +} + +cStreamdevWriter::~cStreamdevWriter() +{ + Dprintf("destructing writer\n"); + if (Running()) + Cancel(3); +} + +void cStreamdevWriter::Action(void) +{ + cTBSelect sel; + Dprintf("Writer start\n"); + int max = 0; + uchar *block = NULL; + int count, offset = 0; + + sel.Clear(); + sel.Add(*m_Socket, true); + while (Running()) { + if (block == NULL) { + block = m_Streamer->Get(count); + offset = 0; + } + + if (block != NULL) { + if (sel.Select(15000) == -1) { + esyslog("ERROR: streamdev-server: couldn't send data: %m"); + break; + } + + if (sel.CanWrite(*m_Socket)) { + int written; + int pkgsize = count; + // SOCK_DGRAM indicates multicast + if (m_Socket->Type() == SOCK_DGRAM) { + // don't fragment multicast packets + // max. payload on standard local ethernet is 1416 to 1456 bytes + // and some STBs expect complete TS packets + // so let's always limit to 7 * TS_SIZE = 1316 + if (pkgsize > 7 * TS_SIZE) + pkgsize = 7 * TS_SIZE; + else + pkgsize -= pkgsize % TS_SIZE; + } + if ((written = m_Socket->Write(block + offset, pkgsize)) == -1) { + esyslog("ERROR: streamdev-server: couldn't send %d bytes: %m", pkgsize); + break; + } + + // statistics + if (count > max) + max = count; + + offset += written; + count -= written; + + // less than one TS packet left: + // delete what we've written so far and get next chunk + if (count < TS_SIZE) { + m_Streamer->Del(offset); + block = NULL; + } + } + } + } + Dprintf("Max. Transmit Blocksize was: %d\n", max); +} + +// --- cStreamdevStreamer ----------------------------------------------------- + +cStreamdevStreamer::cStreamdevStreamer(const char *Name): + cThread(Name), + m_Writer(NULL), + m_RingBuffer(new cStreamdevBuffer(STREAMERBUFSIZE, TS_SIZE * 2, + true, "streamdev-streamer")), + m_SendBuffer(new cStreamdevBuffer(WRITERBUFSIZE, TS_SIZE * 2)) +{ + m_RingBuffer->SetTimeouts(0, 100); + m_SendBuffer->SetTimeouts(100, 100); +} + +cStreamdevStreamer::~cStreamdevStreamer() +{ + Dprintf("Desctructing streamer\n"); + delete m_RingBuffer; + delete m_SendBuffer; +} + +void cStreamdevStreamer::Start(cTBSocket *Socket) +{ + Dprintf("start streamer\n"); + m_Writer = new cStreamdevWriter(Socket, this); + Attach(); +} + +void cStreamdevStreamer::Activate(bool On) +{ + if (On && !Active()) { + Dprintf("activate streamer\n"); + m_Writer->Start(); + cThread::Start(); + } +} + +void cStreamdevStreamer::Stop(void) +{ + if (Running()) { + Dprintf("stopping streamer\n"); + Cancel(3); + } + if (m_Writer) { + Detach(); + DELETENULL(m_Writer); + } +} + +void cStreamdevStreamer::Action(void) +{ + while (Running()) { + int got; + uchar *block = m_RingBuffer->Get(got); + + if (block) { + int count = Put(block, got); + if (count) + m_RingBuffer->Del(count); + } + } +} + diff --git a/plugins/streamdev/streamdev-cvs/server/streamer.h b/plugins/streamdev/streamdev-cvs/server/streamer.h new file mode 100644 index 0000000..6561bc2 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/streamer.h @@ -0,0 +1,102 @@ +/* + * $Id: streamer.h,v 1.11 2009/06/19 06:32:45 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_STREAMER_H +#define VDR_STREAMDEV_STREAMER_H + +#include +#include +#include + +class cTBSocket; +class cStreamdevStreamer; + +#ifndef TS_SIZE +#define TS_SIZE 188 +#endif + +#define STREAMERBUFSIZE (20000 * TS_SIZE) +#define WRITERBUFSIZE (5000 * TS_SIZE) + +// --- cStreamdevBuffer ------------------------------------------------------- + +class cStreamdevBuffer: public cRingBufferLinear { +public: + // make public + void WaitForPut(void) { cRingBuffer::WaitForPut(); } + // Always write complete TS packets + // (assumes Count is a multiple of TS_SIZE) + int PutTS(const uchar *Data, int Count); + cStreamdevBuffer(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL); +}; + +inline int cStreamdevBuffer::PutTS(const uchar *Data, int Count) +{ + int free = Free(); + if (free < Count) + Count = free; + + Count -= Count % TS_SIZE; + if (Count) + Count = Put(Data, Count); + else + WaitForPut(); + return Count; +} + +// --- cStreamdevWriter ------------------------------------------------------- + +class cStreamdevWriter: public cThread { +private: + cStreamdevStreamer *m_Streamer; + cTBSocket *m_Socket; + +protected: + virtual void Action(void); + +public: + cStreamdevWriter(cTBSocket *Socket, cStreamdevStreamer *Streamer); + virtual ~cStreamdevWriter(); +}; + +// --- cStreamdevStreamer ----------------------------------------------------- + +class cStreamdevStreamer: public cThread { +private: + cStreamdevWriter *m_Writer; + cStreamdevBuffer *m_RingBuffer; + cStreamdevBuffer *m_SendBuffer; + +protected: + virtual void Action(void); + + bool IsRunning(void) const { return m_Writer; } + +public: + cStreamdevStreamer(const char *Name); + virtual ~cStreamdevStreamer(); + + virtual void Start(cTBSocket *Socket); + virtual void Stop(void); + bool Abort(void); + + void Activate(bool On); + int Receive(uchar *Data, int Length) { return m_RingBuffer->PutTS(Data, Length); } + void ReportOverflow(int Bytes) { m_RingBuffer->ReportOverflow(Bytes); } + + virtual int Put(const uchar *Data, int Count) { return m_SendBuffer->PutTS(Data, Count); } + virtual uchar *Get(int &Count) { return m_SendBuffer->Get(Count); } + virtual void Del(int Count) { m_SendBuffer->Del(Count); } + + virtual void Detach(void) {} + virtual void Attach(void) {} +}; + +inline bool cStreamdevStreamer::Abort(void) +{ + return Active() && !m_Writer->Active(); +} + +#endif // VDR_STREAMDEV_STREAMER_H + diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.c b/plugins/streamdev/streamdev-cvs/server/suspend.c new file mode 100644 index 0000000..b6e1382 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/suspend.c @@ -0,0 +1,57 @@ +/* + * $Id: suspend.c,v 1.3 2008/10/22 11:59:32 schmirl Exp $ + */ + +#include "server/suspend.h" +#include "server/suspend.dat" +#include "common.h" + +cSuspendLive::cSuspendLive(void) + : cThread("Streamdev: server suspend") +{ +} + +cSuspendLive::~cSuspendLive() { + Stop(); + Detach(); +} + +void cSuspendLive::Activate(bool On) { + Dprintf("Activate cSuspendLive %d\n", On); + if (On) + Start(); + else + Stop(); +} + +void cSuspendLive::Stop(void) { + if (Running()) + Cancel(3); +} + +void cSuspendLive::Action(void) { + while (Running()) { + DeviceStillPicture(suspend_mpg, sizeof(suspend_mpg)); + cCondWait::SleepMs(100); + } +} + +bool cSuspendCtl::m_Active = false; + +cSuspendCtl::cSuspendCtl(void): + cControl(m_Suspend = new cSuspendLive) { + m_Active = true; +} + +cSuspendCtl::~cSuspendCtl() { + m_Active = false; + DELETENULL(m_Suspend); +} + +eOSState cSuspendCtl::ProcessKey(eKeys Key) { + if (!m_Suspend->Active() || Key == kBack) { + DELETENULL(m_Suspend); + return osEnd; + } + return osContinue; +} diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.dat b/plugins/streamdev/streamdev-cvs/server/suspend.dat new file mode 100644 index 0000000..7b1b890 --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/suspend.dat @@ -0,0 +1,1206 @@ +const unsigned char suspend_mpg[] = { + 0x00, 0x00, 0x01, 0xb3, 0x2d, 0x02, 0x40, 0x83, 0x02, 0xd0, 0x20, 0xa0, + 0x00, 0x00, 0x01, 0xb2, 0x4d, 0x50, 0x45, 0x47, 0x0a, 0x00, 0x00, 0x01, + 0xb8, 0x00, 0x08, 0x00, 0x40, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0a, 0xbb, + 0x58, 0x00, 0x00, 0x01, 0x01, 0x52, 0x97, 0xe6, 0x54, 0xa5, 0x2f, 0xdc, + 0xaf, 0x9a, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x45, 0xe2, + 0x2c, 0x52, 0x67, 0x98, 0x6c, 0x9a, 0x2e, 0xb9, 0x9e, 0xb4, 0x6c, 0xdd, + 0x34, 0x8c, 0x8b, 0xab, 0xa6, 0x61, 0xb5, 0xbd, 0x33, 0x8d, 0xa7, 0x22, + 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, + 0x91, 0xb3, 0xf4, 0xd6, 0xf4, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, + 0x37, 0x4d, 0x77, 0x4c, 0xdd, 0x34, 0xe4, 0x43, 0xdd, 0x43, 0x66, 0xe9, + 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, + 0xce, 0x36, 0x8c, 0x89, 0xbd, 0xd3, 0xd3, 0x3f, 0x4d, 0x68, 0xd9, 0x86, + 0xd3, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, + 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0xf4, 0xd1, 0x91, 0x37, + 0xba, 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, + 0x4c, 0xfd, 0x35, 0xa3, 0x66, 0x1b, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9c, + 0x6d, 0x67, 0x4c, 0xfd, 0x34, 0x64, 0x43, 0xdd, 0x43, 0x66, 0xe9, 0xad, + 0xe9, 0x9c, 0x6d, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x7e, 0x9a, 0xd1, 0xb3, + 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcd, 0xd3, 0x5c, 0x36, 0x61, 0xb4, + 0x0c, 0x9b, 0xdd, 0x43, 0x66, 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x72, 0x26, + 0xf7, 0x50, 0xd9, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, + 0x1b, 0x30, 0xda, 0xd1, 0xb3, 0x8d, 0xa3, 0x22, 0x6f, 0x74, 0x8d, 0x9c, + 0x6d, 0x68, 0xd9, 0xba, 0x69, 0xc8, 0x87, 0xba, 0xba, 0x66, 0x1b, 0x5c, + 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xdd, 0x33, + 0x74, 0xd1, 0x91, 0x37, 0xba, 0x9e, 0x6e, 0x9a, 0xde, 0x99, 0xfa, 0x68, + 0xc8, 0x9b, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0x79, 0xc6, 0xd3, 0x91, 0x37, + 0xba, 0x7a, 0x67, 0xe9, 0xad, 0x1b, 0x30, 0xda, 0x32, 0x26, 0xf7, 0x50, + 0xd9, 0xba, 0x6b, 0x7a, 0x67, 0x1b, 0x46, 0x44, 0x3d, 0xd4, 0x36, 0x61, + 0xb5, 0xbd, 0x33, 0xbd, 0x39, 0x13, 0x7b, 0xa7, 0xa6, 0x71, 0xb5, 0xbf, + 0xcc, 0x36, 0x8c, 0x89, 0xbd, 0xd4, 0xf3, 0x0d, 0xae, 0xe9, 0x9b, 0xa6, + 0x9c, 0x89, 0xbd, 0xd4, 0xf3, 0x74, 0xd7, 0x74, 0xcc, 0x36, 0x8c, 0x89, + 0xbd, 0xd2, 0x36, 0x7e, 0x9a, 0xd1, 0xb3, 0x0d, 0xa3, 0x22, 0x6f, 0x75, + 0x74, 0xcf, 0xd3, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x10, 0xf7, 0x53, 0xcc, + 0x36, 0xb7, 0xa6, 0x71, 0xb4, 0xe4, 0x4d, 0xee, 0x91, 0xb3, 0xf4, 0xd6, + 0x8d, 0x98, 0x6d, 0x03, 0x26, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x2b, 0xf9, + 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x45, 0xe2, 0x2c, 0x52, 0x71, 0xb3, 0x74, 0xc9, 0xa2, + 0xeb, 0x79, 0x86, 0xd6, 0xf4, 0xcc, 0x36, 0x9c, 0x88, 0xba, 0xba, 0x66, + 0xe9, 0xad, 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x50, 0xd9, 0xba, 0x6b, + 0x7a, 0x67, 0xe9, 0xa7, 0x22, 0x6f, 0x74, 0x8d, 0x9f, 0xa6, 0xb4, 0x6c, + 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x37, 0x4d, + 0x19, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xc3, 0x66, 0xe9, 0xa3, 0x22, + 0x1e, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, 0x69, 0xc8, 0x9b, 0xdd, + 0x23, 0x67, 0x1b, 0x5b, 0xd3, 0x37, 0x4d, 0x19, 0x13, 0x7b, 0xa8, 0x6c, + 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0xe9, 0x9b, 0xa6, + 0xb7, 0xa6, 0x7e, 0x9a, 0x72, 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xa3, + 0x67, 0xe9, 0xa3, 0x22, 0x6f, 0x74, 0xf4, 0xce, 0x36, 0xb7, 0xa6, 0x67, + 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0xf5, 0xc3, 0x66, 0x1b, 0x4e, 0x44, + 0xde, 0xea, 0xe9, 0x9b, 0xa6, 0xb4, 0x6c, 0xfd, 0x34, 0x0c, 0x87, 0xba, + 0x46, 0xcf, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4d, 0xee, 0xa1, 0xb3, + 0xf4, 0xd6, 0x8d, 0x9b, 0xa6, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, 0x30, 0xda, + 0xee, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0x86, 0xcc, 0x36, 0xb8, 0x6c, + 0xc3, 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x74, 0xd6, 0x8d, 0x9c, 0x6d, + 0x39, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, 0x7a, 0x66, 0xe9, 0xa3, 0x22, + 0x6f, 0x75, 0x3c, 0xdd, 0x35, 0xdd, 0x33, 0x0d, 0xa3, 0x22, 0x1e, 0xea, + 0xe9, 0x9b, 0xa6, 0xb8, 0x6c, 0xdd, 0x34, 0xe4, 0x4d, 0xee, 0xa1, 0xb3, + 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, 0x6c, 0xe3, 0x6b, + 0x7a, 0x67, 0x1b, 0x46, 0x44, 0xde, 0xe9, 0x1b, 0x38, 0xda, 0xd1, 0xb3, + 0x3d, 0x39, 0x13, 0x7b, 0xab, 0xa6, 0x61, 0xb5, 0xdd, 0x33, 0x74, 0xd1, + 0x91, 0x37, 0xba, 0x5e, 0x7e, 0x9a, 0xee, 0x99, 0x9e, 0x8c, 0x88, 0x7b, + 0xa4, 0x6c, 0xfd, 0x35, 0xbf, 0xcc, 0x36, 0x9c, 0x89, 0xbd, 0xd5, 0xd3, + 0x33, 0xd7, 0x0d, 0x9b, 0xa6, 0x8c, 0x89, 0xbd, 0xd5, 0xd3, 0x37, 0x4d, + 0x68, 0xd9, 0xc6, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5b, 0xd3, + 0x33, 0xd3, 0x91, 0x37, 0xba, 0x86, 0xce, 0x36, 0xb7, 0xa6, 0x61, 0xb4, + 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x74, 0xd7, 0x0d, 0x98, 0x6d, 0x19, 0x13, + 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0xf4, 0xd3, 0x91, 0x0f, 0x74, + 0x8d, 0x9f, 0xa6, 0xb4, 0x6c, 0xe3, 0x68, 0xc8, 0x9b, 0xdd, 0x23, 0x66, + 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, + 0xba, 0x66, 0x1b, 0x4e, 0x44, 0xde, 0x00, 0x00, 0x01, 0x03, 0x2b, 0xf9, + 0x95, 0x29, 0x4b, 0xf7, 0x2b, 0xe6, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x28, 0xf3, 0x3c, 0x9b, 0xf8, 0xb5, 0xa5, 0x67, 0x4c, + 0xe3, 0x64, 0xd1, 0x75, 0x3c, 0xc3, 0x6b, 0x7a, 0x66, 0x1b, 0x4e, 0x44, + 0x3d, 0xd5, 0xd3, 0x30, 0xda, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, 0xba, + 0x86, 0xcc, 0x36, 0xb4, 0x6c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0x9e, 0x99, + 0xc6, 0xd6, 0x8d, 0x9c, 0x6d, 0x38, 0xa9, 0xbd, 0xd2, 0x36, 0x71, 0xb5, + 0xbd, 0x33, 0x0d, 0xa3, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xbb, 0xa6, + 0x6e, 0x9a, 0x31, 0x53, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xd1, 0xb3, 0x8d, + 0xa7, 0x15, 0x37, 0xba, 0xba, 0x66, 0x1b, 0x5a, 0x36, 0x6e, 0x9a, 0x06, + 0x43, 0xdd, 0x5d, 0x33, 0x0d, 0xae, 0x1b, 0x30, 0xda, 0x72, 0x26, 0xf7, + 0x57, 0x4c, 0xc3, 0x6b, 0x86, 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, + 0x37, 0x4d, 0x6f, 0x4c, 0xe3, 0x68, 0x19, 0x37, 0xba, 0x86, 0xcd, 0xd3, + 0x5a, 0x36, 0x61, 0xb4, 0xe4, 0x4d, 0xee, 0xae, 0x99, 0x86, 0xd7, 0x74, + 0xcd, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x30, 0xda, 0xde, 0x99, 0xfa, + 0x68, 0xc8, 0x9b, 0xdd, 0x5d, 0x33, 0x0d, 0xad, 0x1b, 0x3f, 0x4d, 0x23, + 0x26, 0xf7, 0x4f, 0x4c, 0xfd, 0x35, 0xbd, 0x33, 0x74, 0xd1, 0x91, 0x0f, + 0x75, 0x0d, 0x9f, 0xa6, 0xb7, 0xa6, 0x71, 0xb4, 0x64, 0x4d, 0xee, 0x9e, + 0x99, 0xba, 0x6b, 0x86, 0xcd, 0xd3, 0x4e, 0x44, 0xde, 0xea, 0x1b, 0x37, + 0x4d, 0x6f, 0x4c, 0xfd, 0x34, 0x64, 0x4d, 0xee, 0xa1, 0xb3, 0x0d, 0xad, + 0x1b, 0x38, 0xda, 0x32, 0x26, 0xf7, 0x4f, 0x4c, 0xe3, 0x6b, 0x46, 0xcd, + 0xd3, 0x4e, 0x44, 0xde, 0xea, 0xe9, 0x98, 0x6d, 0x70, 0xd9, 0xba, 0x68, + 0xc8, 0x87, 0xba, 0x86, 0xcd, 0xd3, 0x5d, 0xd3, 0x30, 0xda, 0x32, 0x26, + 0xf7, 0x57, 0x4c, 0xdd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, + 0x1b, 0x38, 0xda, 0xde, 0x99, 0x86, 0xd1, 0x91, 0x37, 0xba, 0xba, 0x66, + 0x1b, 0x5c, 0x36, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x57, 0x4c, 0xdd, 0x35, + 0xc3, 0x66, 0xe9, 0xa7, 0x22, 0x6f, 0x75, 0x74, 0xcc, 0x36, 0xb5, 0xe6, + 0xe9, 0xa3, 0x22, 0x1e, 0xea, 0xe9, 0x99, 0xeb, 0xba, 0x66, 0xe9, 0xa3, + 0x22, 0x6f, 0x75, 0x0d, 0x98, 0x6d, 0x70, 0xd9, 0x86, 0xd3, 0x91, 0x37, + 0xba, 0x86, 0xcd, 0xd3, 0x5a, 0x36, 0x61, 0xb4, 0x64, 0x4e, 0xea, 0xe9, + 0x9b, 0xa6, 0xbb, 0xa6, 0x6e, 0x9a, 0x32, 0x26, 0xf7, 0x53, 0xcc, 0x36, + 0xbb, 0xa6, 0x6e, 0x9a, 0x72, 0x26, 0xf7, 0x57, 0x4c, 0xc3, 0x6b, 0x46, + 0xcf, 0xd3, 0x46, 0x44, 0xde, 0xea, 0x1b, 0x37, 0x4d, 0x6f, 0x4c, 0xfd, + 0x34, 0x64, 0x43, 0xdd, 0x23, 0x67, 0xe9, 0xad, 0xe9, 0x98, 0x6d, 0x39, + 0x13, 0x7b, 0xab, 0xa6, 0x6e, 0x9a, 0xe1, 0xb3, 0x74, 0xd1, 0x91, 0x37, + 0xba, 0xba, 0x66, 0xe9, 0xad, 0x1b, 0x3f, 0x4d, 0x19, 0x13, 0x7b, 0xa4, + 0x6c, 0xfd, 0x35, 0xa3, 0x67, 0x1b, 0x4e, 0x44, 0xde, 0xe9, 0xe9, 0x9c, + 0x6d, 0x6f, 0x4c, 0xdd, 0x34, 0x64, 0x4d, 0xe0, 0x00, 0x00, 0x01, 0x04, + 0x2b, 0xf9, 0x95, 0x22, 0xf3, 0x74, 0xdf, 0xb8, 0xb5, 0xf3, 0x56, 0x91, + 0x7a, 0xce, 0x99, 0xc6, 0xc8, 0x64, 0x5d, 0x23, 0x67, 0xe9, 0xad, 0xe9, + 0x9b, 0xa6, 0x9c, 0x89, 0xdd, 0xe0, 0x19, 0xf4, 0x80, 0x54, 0x5f, 0x41, + 0x2c, 0x20, 0x61, 0x7f, 0x14, 0x4b, 0x25, 0xa5, 0x05, 0x27, 0x25, 0x25, + 0x20, 0x95, 0xd0, 0x58, 0xd2, 0xd1, 0xba, 0x33, 0x73, 0x4d, 0xb1, 0xe0, + 0x0c, 0x00, 0x60, 0x1a, 0x8d, 0xf2, 0x40, 0xaf, 0xc9, 0xcf, 0xbe, 0x01, + 0x88, 0x0e, 0xce, 0xe6, 0xe0, 0x0a, 0x6d, 0x1d, 0x32, 0x00, 0x36, 0xf8, + 0xa2, 0x62, 0x40, 0x74, 0xb0, 0x42, 0x08, 0x49, 0x29, 0x0f, 0xbe, 0x00, + 0xa3, 0xe1, 0xa1, 0x8c, 0xff, 0xff, 0x90, 0x8e, 0x06, 0x06, 0xba, 0xd2, + 0x57, 0xbe, 0x6c, 0x01, 0xa2, 0x51, 0xd0, 0x58, 0x67, 0xdb, 0xfd, 0xc9, + 0x79, 0xf6, 0xea, 0x3b, 0x91, 0x2f, 0x54, 0x03, 0x1c, 0x9d, 0x90, 0x5a, + 0x3f, 0xc6, 0x75, 0xde, 0xc0, 0x13, 0x10, 0xd2, 0x94, 0x16, 0x50, 0x6f, + 0x1a, 0x1a, 0x05, 0x0b, 0x49, 0x30, 0x37, 0xb3, 0x3f, 0x43, 0x74, 0x24, + 0x90, 0x84, 0x01, 0x72, 0xba, 0x0b, 0xe3, 0x30, 0xcd, 0xd2, 0xc6, 0x59, + 0x7a, 0x6d, 0x20, 0x21, 0xc0, 0x07, 0xc0, 0x19, 0xe4, 0xac, 0x60, 0x09, + 0xca, 0x18, 0x49, 0xed, 0x89, 0xa4, 0x27, 0xea, 0x43, 0x30, 0xd2, 0xc3, + 0x3f, 0x7e, 0xa5, 0x13, 0x31, 0xf0, 0x7b, 0xbc, 0x03, 0x10, 0x29, 0x88, + 0x40, 0x55, 0x39, 0x19, 0x90, 0x90, 0x94, 0x08, 0xe4, 0x0f, 0x72, 0x00, + 0xe8, 0x34, 0x0c, 0x86, 0x23, 0x9a, 0x77, 0xba, 0xba, 0x66, 0xe9, 0xad, + 0xe9, 0x9f, 0xa6, 0xec, 0xc8, 0xb9, 0xde, 0xe9, 0xe9, 0xb4, 0x13, 0x12, + 0x1a, 0x02, 0x14, 0x96, 0x80, 0x28, 0xdb, 0x06, 0x27, 0x20, 0xb2, 0x19, + 0x78, 0x68, 0x66, 0xfb, 0xf2, 0x59, 0x45, 0x6c, 0x94, 0x24, 0xb2, 0xd0, + 0xcc, 0x96, 0xf9, 0x08, 0xfd, 0x69, 0xe9, 0x47, 0x45, 0x79, 0x0d, 0x25, + 0xfc, 0x82, 0x83, 0x3e, 0xc1, 0xa4, 0xd2, 0xd0, 0x18, 0x57, 0x7d, 0xbe, + 0xe9, 0xeb, 0x2d, 0x19, 0x3b, 0xe4, 0xf0, 0xc2, 0x86, 0x86, 0x8c, 0x6e, + 0xf9, 0x1e, 0xf2, 0xc8, 0x41, 0x40, 0x3a, 0x0c, 0xdb, 0x29, 0x05, 0x63, + 0x7b, 0x87, 0xff, 0xd7, 0xff, 0x37, 0xf5, 0xfe, 0xbf, 0x78, 0x80, 0x0f, + 0x80, 0x34, 0x40, 0x6e, 0x03, 0x25, 0xa0, 0x6e, 0x76, 0x46, 0x1f, 0xf1, + 0xbc, 0x7e, 0xbd, 0x10, 0x10, 0x80, 0xef, 0x86, 0x32, 0x10, 0x7f, 0x27, + 0x55, 0x3d, 0x0c, 0x02, 0x02, 0x94, 0x35, 0x21, 0xa5, 0xf6, 0x67, 0xe5, + 0xa5, 0x0e, 0x02, 0x20, 0xce, 0x52, 0x1c, 0x5b, 0x55, 0x80, 0x1d, 0x13, + 0x07, 0x16, 0x09, 0x3f, 0x81, 0xa0, 0x9b, 0xfb, 0x64, 0x09, 0x4a, 0xdc, + 0x65, 0xeb, 0x50, 0x03, 0xb0, 0x1d, 0x97, 0x9c, 0x96, 0x56, 0x5a, 0x73, + 0x12, 0xbf, 0x5a, 0x3f, 0xdd, 0x60, 0x65, 0x6b, 0xc7, 0xaa, 0xf0, 0x53, + 0x8a, 0x2b, 0x3a, 0x7a, 0x36, 0x38, 0xe2, 0x05, 0xe9, 0xa0, 0x96, 0x5f, + 0x57, 0xe1, 0x75, 0x9f, 0x9d, 0x01, 0xb2, 0x05, 0x80, 0x46, 0x92, 0x0d, + 0x6f, 0x21, 0x15, 0xc0, 0x98, 0x24, 0xfe, 0x96, 0x00, 0x31, 0xbb, 0x24, + 0xb0, 0xd0, 0x26, 0x80, 0x08, 0xb8, 0x00, 0xc6, 0x80, 0x64, 0x37, 0x1a, + 0x8e, 0x1d, 0xf1, 0x17, 0xdb, 0x0d, 0x28, 0x7e, 0x0a, 0xb1, 0x18, 0x9b, + 0xce, 0x1a, 0x05, 0x06, 0xaf, 0x06, 0x96, 0x80, 0xa6, 0xaf, 0x46, 0x42, + 0x3e, 0x41, 0x7b, 0xef, 0xce, 0xe3, 0x52, 0xdc, 0x9e, 0x78, 0xab, 0x22, + 0x08, 0x60, 0x12, 0xe6, 0x17, 0xbb, 0xe2, 0x25, 0xc5, 0x2c, 0xac, 0x5a, + 0x3e, 0xeb, 0xde, 0xe6, 0xcc, 0x29, 0xae, 0x8e, 0xf8, 0xfb, 0xd2, 0x03, + 0x62, 0x85, 0x80, 0x46, 0x92, 0x0d, 0x00, 0x6c, 0x50, 0xee, 0x01, 0x1a, + 0x48, 0x37, 0xad, 0x1a, 0x43, 0x43, 0xa0, 0x97, 0x94, 0x3d, 0x0b, 0xf8, + 0xce, 0x49, 0x02, 0x34, 0x25, 0x93, 0x40, 0x25, 0x40, 0x04, 0x5c, 0x00, + 0x63, 0x69, 0xc6, 0x63, 0x70, 0x55, 0x8b, 0x26, 0xfc, 0x98, 0x0d, 0x8a, + 0x1d, 0xc0, 0x23, 0x49, 0x06, 0x80, 0x36, 0x28, 0x77, 0x00, 0x8d, 0x24, + 0x1b, 0xd7, 0x92, 0xc8, 0x60, 0x12, 0xa0, 0x02, 0x2e, 0x00, 0x31, 0x89, + 0x2c, 0x86, 0x01, 0x2a, 0x00, 0x22, 0x00, 0x17, 0xd4, 0xe5, 0x0f, 0xc1, + 0x54, 0x8c, 0x4d, 0xf9, 0x40, 0x0b, 0x0a, 0x30, 0x6e, 0x01, 0x1f, 0xe4, + 0x7f, 0xf5, 0xd9, 0x2c, 0x31, 0x25, 0x2f, 0x04, 0xe0, 0x08, 0x44, 0xa9, + 0x5c, 0x39, 0xab, 0xb1, 0x0c, 0x35, 0x07, 0xb2, 0x44, 0x2b, 0x91, 0xfd, + 0xe3, 0xd0, 0x9c, 0x5e, 0xdf, 0x1b, 0x0c, 0x8e, 0x3b, 0xd6, 0x12, 0x8a, + 0x9f, 0xdd, 0xe2, 0xf2, 0x76, 0xfb, 0xef, 0xbe, 0x7d, 0xf2, 0xf7, 0xdc, + 0xdd, 0xe2, 0xf5, 0x00, 0x1a, 0x86, 0x00, 0x1f, 0x00, 0xef, 0xa3, 0x12, + 0x03, 0x77, 0x0d, 0x28, 0x53, 0x7d, 0x8b, 0x46, 0x51, 0xdb, 0xfd, 0xf3, + 0xf0, 0x3e, 0xd9, 0x5a, 0xdc, 0xf7, 0xb6, 0x1a, 0x18, 0x06, 0x13, 0xf0, + 0x50, 0x8b, 0xc9, 0x28, 0xb2, 0x5a, 0x37, 0xba, 0x9e, 0x77, 0xad, 0x79, + 0x9e, 0xd1, 0xa8, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, + 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x77, + 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, + 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, + 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, + 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, + 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, + 0xef, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, + 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, + 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x33, + 0xd7, 0x3c, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x1a, + 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, + 0xf3, 0x3d, 0x2d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, + 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, + 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd1, 0x75, 0x3c, + 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, + 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, + 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, + 0xba, 0x9e, 0x67, 0xac, 0x8d, 0x2d, 0x16, 0xaf, 0x17, 0x8d, 0x29, 0x9c, + 0x00, 0x00, 0x01, 0x05, 0x3b, 0xf9, 0xd3, 0xce, 0xf5, 0xaf, 0x33, 0xdf, + 0xb5, 0x35, 0xf3, 0x97, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, + 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe7, 0x28, 0xb0, 0x1b, + 0x00, 0xec, 0x31, 0x26, 0x06, 0xb9, 0x31, 0x21, 0x06, 0x9e, 0x57, 0xe9, + 0x1d, 0xbb, 0xa3, 0x61, 0xe1, 0xb5, 0x8f, 0x68, 0x26, 0xee, 0x59, 0x49, + 0xdb, 0xed, 0xc0, 0xf7, 0xf9, 0xc6, 0x27, 0x2d, 0x09, 0x61, 0xe8, 0xed, + 0xf0, 0xbd, 0x71, 0x01, 0x44, 0x81, 0x80, 0xd4, 0x24, 0xc3, 0xb8, 0x8f, + 0x6e, 0x1a, 0x82, 0x5a, 0x3e, 0xab, 0x78, 0x80, 0x3b, 0x47, 0x48, 0x67, + 0xf9, 0x7d, 0x01, 0x38, 0xc7, 0x63, 0x03, 0xf3, 0xe0, 0xfb, 0xab, 0xbe, + 0xfb, 0x7d, 0xdf, 0x73, 0x6f, 0x3a, 0x52, 0x49, 0x68, 0xdc, 0x7e, 0x5e, + 0x1d, 0xcd, 0xc1, 0xdd, 0x57, 0xae, 0xc0, 0x5b, 0xa4, 0xa0, 0x25, 0xc5, + 0x12, 0x3a, 0xd0, 0x1c, 0xca, 0x0e, 0xf7, 0x8a, 0x43, 0x43, 0xa3, 0x71, + 0xc4, 0x6b, 0xd2, 0x26, 0x76, 0x7d, 0x53, 0xe3, 0x79, 0x9e, 0xb9, 0xeb, + 0x12, 0x9c, 0x8d, 0xff, 0xfb, 0x6c, 0xbf, 0xfa, 0xb6, 0xc3, 0xfd, 0xef, + 0x64, 0x3d, 0xe4, 0xbd, 0x6d, 0xce, 0x2b, 0x3a, 0x5c, 0x09, 0x62, 0x7f, + 0x20, 0x62, 0x3c, 0x84, 0x34, 0xb3, 0x6c, 0x69, 0x1b, 0x00, 0xfa, 0xe6, + 0x86, 0x66, 0x46, 0x73, 0xfa, 0xf8, 0x07, 0xc7, 0x5e, 0xb5, 0x3c, 0x6a, + 0x53, 0xc7, 0x2b, 0x11, 0x40, 0x3e, 0xbe, 0x47, 0x04, 0x6b, 0xdd, 0x3f, + 0xd4, 0xfe, 0x02, 0xfa, 0x09, 0x33, 0xa8, 0xb0, 0x47, 0xfb, 0x21, 0x82, + 0x5f, 0xd9, 0x77, 0xcf, 0xaf, 0xa3, 0x06, 0x70, 0x1c, 0x82, 0x3f, 0xd9, + 0x0c, 0x12, 0xfe, 0xd3, 0x74, 0x80, 0x9c, 0x10, 0xc1, 0x81, 0x23, 0xf7, + 0x11, 0xbd, 0x00, 0x30, 0x24, 0xf1, 0x17, 0x8d, 0xee, 0x70, 0x60, 0x05, + 0xc0, 0x21, 0x26, 0x95, 0x83, 0x71, 0x41, 0x85, 0x15, 0xf3, 0x80, 0x52, + 0x5a, 0x50, 0x5b, 0x0f, 0x19, 0x83, 0x0b, 0x65, 0x2f, 0x64, 0xf4, 0x73, + 0x3e, 0xd7, 0x7c, 0x9a, 0x9d, 0x8a, 0x2d, 0xd7, 0x86, 0x77, 0x10, 0xd7, + 0x9b, 0x21, 0x80, 0x27, 0x2c, 0xa4, 0x96, 0x05, 0x39, 0x34, 0x07, 0x41, + 0x84, 0xde, 0x4d, 0x6c, 0x94, 0xe2, 0xf9, 0x41, 0x85, 0x37, 0x7c, 0x5e, + 0x35, 0x39, 0x38, 0x61, 0xed, 0xdd, 0xec, 0x00, 0x1e, 0x00, 0x60, 0x80, + 0x18, 0xe2, 0xfa, 0x51, 0xd2, 0x30, 0xcc, 0x47, 0xb9, 0x08, 0x64, 0xd2, + 0x53, 0x2d, 0xab, 0x4e, 0xe7, 0x37, 0x7c, 0x1d, 0xc7, 0x72, 0x27, 0xb8, + 0x84, 0x24, 0x9e, 0x81, 0xc7, 0x08, 0x1c, 0x1c, 0x40, 0xaf, 0x21, 0xfe, + 0x94, 0x10, 0x8b, 0x3b, 0xa7, 0x37, 0x0a, 0x7b, 0xa0, 0x19, 0xd8, 0x35, + 0x02, 0xdf, 0x13, 0x9c, 0x89, 0x57, 0x41, 0x29, 0x2c, 0x67, 0xb5, 0xeb, + 0xd5, 0x47, 0x28, 0x01, 0xe9, 0x4d, 0x83, 0x09, 0xb8, 0x0f, 0x24, 0x94, + 0x93, 0xc9, 0x05, 0xf6, 0xc1, 0x45, 0xb1, 0xe7, 0x6f, 0xee, 0xfb, 0xde, + 0x34, 0x03, 0x32, 0x80, 0xc8, 0x0c, 0x08, 0x68, 0xca, 0xe4, 0xa4, 0xb7, + 0xc7, 0xef, 0xb3, 0xef, 0x83, 0xef, 0x6f, 0xb2, 0x53, 0xb7, 0xc8, 0x4f, + 0xdf, 0x3e, 0x57, 0x36, 0xe9, 0x02, 0xa5, 0x38, 0xc4, 0xe1, 0x1c, 0x3a, + 0x60, 0x51, 0x2a, 0xfb, 0xda, 0x3c, 0xef, 0x02, 0x51, 0x60, 0x49, 0x21, + 0x5e, 0x7e, 0x50, 0x6a, 0xc0, 0xb1, 0x7d, 0x87, 0x21, 0x3f, 0x85, 0x6d, + 0xff, 0xbb, 0xc9, 0x49, 0x1d, 0xee, 0x21, 0xb8, 0x7e, 0xb8, 0xf7, 0x97, + 0x00, 0x70, 0xe0, 0x60, 0x9a, 0xee, 0x08, 0x7f, 0x6a, 0x70, 0x47, 0x04, + 0x28, 0xe1, 0x44, 0x57, 0xbd, 0x08, 0x60, 0x09, 0x80, 0x76, 0x43, 0x61, + 0x85, 0x72, 0xb7, 0x3d, 0x21, 0xbb, 0xa1, 0x81, 0x20, 0x0f, 0xb1, 0x7f, + 0x8a, 0x6d, 0x85, 0xeb, 0xc2, 0x10, 0x88, 0x48, 0x02, 0x81, 0x88, 0x0c, + 0x41, 0xd8, 0xd1, 0xff, 0xf7, 0xff, 0xb3, 0x67, 0xfd, 0x7e, 0xf3, 0x08, + 0x41, 0x9d, 0x0f, 0xfa, 0x9f, 0x33, 0x5e, 0x81, 0x0c, 0xbd, 0xf3, 0x65, + 0xdf, 0xa4, 0x00, 0x66, 0xa0, 0x03, 0xee, 0x00, 0x29, 0x4e, 0x13, 0xb9, + 0x49, 0xec, 0x17, 0xb7, 0x0e, 0xbd, 0x2b, 0xdc, 0xa0, 0x07, 0x64, 0xdc, + 0x1a, 0x43, 0x28, 0xac, 0xbc, 0x4c, 0xdd, 0x3c, 0xf2, 0x4a, 0x73, 0x61, + 0x44, 0x87, 0x71, 0x3a, 0xee, 0xbc, 0xc0, 0x77, 0x82, 0x76, 0x1c, 0x85, + 0xd8, 0x10, 0x94, 0x55, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf7, 0x6b, 0x5c, + 0xd7, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, + 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0x5d, + 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, + 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, + 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, + 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, 0xa2, 0xea, 0x79, 0x9e, + 0xb9, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, + 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd2, 0xf3, 0x3d, 0x73, + 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, + 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x17, 0x4b, 0xce, 0xf5, 0xaf, 0x3b, + 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, + 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, + 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, + 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x45, + 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, + 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, + 0xe6, 0x7a, 0xca, 0x52, 0xd3, 0xb5, 0x79, 0xa9, 0x49, 0x45, 0xca, 0x52, + 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x06, 0x43, 0xf1, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xc4, 0xd7, + 0xcf, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde, + 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd2, 0xd3, + 0xad, 0xf9, 0x04, 0xce, 0xdf, 0xe0, 0xfc, 0x45, 0x27, 0xac, 0x3e, 0xb4, + 0x9b, 0x99, 0x3b, 0x8f, 0x57, 0x23, 0xe2, 0x2d, 0xd3, 0x71, 0x9f, 0x71, + 0xc4, 0x63, 0x48, 0x21, 0xd7, 0x94, 0x0d, 0x40, 0xd4, 0x7c, 0x69, 0x82, + 0x71, 0xd7, 0xcd, 0x60, 0x9c, 0x4e, 0xbd, 0x9e, 0xaf, 0x54, 0x7a, 0x97, + 0xa1, 0x48, 0x00, 0xb4, 0x9a, 0x18, 0x18, 0x3f, 0x8d, 0x4a, 0xd8, 0x20, + 0xa6, 0xfc, 0x4e, 0xfd, 0x91, 0xf0, 0xe4, 0xda, 0xd0, 0x58, 0xd4, 0x64, + 0xe7, 0x5e, 0xdd, 0x67, 0xae, 0xf3, 0xe0, 0x0f, 0x8a, 0x72, 0x59, 0x34, + 0x7f, 0x25, 0x21, 0x6a, 0x64, 0x3b, 0xfd, 0x8c, 0xfd, 0x2f, 0xb8, 0xf1, + 0x9a, 0xf4, 0x40, 0xa0, 0x60, 0x17, 0x58, 0x1e, 0x58, 0x7d, 0xc4, 0x4d, + 0x2d, 0x8a, 0x13, 0x7c, 0xa0, 0x13, 0x93, 0x40, 0x2c, 0x21, 0x16, 0x9c, + 0x7b, 0x32, 0x30, 0xf4, 0xf5, 0x6c, 0xb1, 0xfe, 0xf7, 0x28, 0xc4, 0xa0, + 0xcc, 0x83, 0xfe, 0xcb, 0xec, 0xcf, 0xcd, 0xec, 0x2f, 0xaf, 0xdc, 0x80, + 0x07, 0xa1, 0xa4, 0xb2, 0x68, 0x61, 0x33, 0x1c, 0x80, 0x14, 0xa5, 0x4e, + 0x29, 0x18, 0x52, 0xc2, 0x77, 0x01, 0xe5, 0xd8, 0xfe, 0xf3, 0xc0, 0xaf, + 0x59, 0x45, 0x00, 0x8f, 0x89, 0xf7, 0x41, 0x31, 0x0c, 0x57, 0xe3, 0xef, + 0xe6, 0x97, 0xd3, 0x11, 0xc0, 0x72, 0x08, 0xff, 0x84, 0xd0, 0x4b, 0xfc, + 0x4d, 0xf3, 0x4b, 0xea, 0x3b, 0xac, 0xb0, 0x47, 0xfc, 0x26, 0x82, 0x5f, + 0xe2, 0x68, 0x02, 0xab, 0x0c, 0x01, 0x1e, 0x23, 0x7b, 0x60, 0x1d, 0x12, + 0x78, 0x8b, 0xcf, 0xca, 0x01, 0xbe, 0x4a, 0x3b, 0xe4, 0x0c, 0x4f, 0x0d, + 0x4e, 0x01, 0x21, 0x5b, 0x71, 0xa9, 0x16, 0xd8, 0xec, 0xcb, 0xff, 0x6b, + 0xe9, 0xc0, 0x54, 0x01, 0xb9, 0x41, 0xa9, 0x0d, 0x0d, 0xc4, 0xc0, 0x2e, + 0x92, 0xd3, 0xf3, 0x99, 0xbf, 0xff, 0x28, 0xe7, 0x61, 0xd7, 0x89, 0x00, + 0x6e, 0x05, 0x4b, 0x00, 0xd0, 0x01, 0xd0, 0x40, 0x42, 0x40, 0x35, 0x4b, + 0x3e, 0x3c, 0x02, 0xc2, 0x97, 0x97, 0xbf, 0x24, 0xfe, 0xfb, 0x81, 0x24, + 0xdf, 0x6b, 0xbe, 0x60, 0x03, 0x04, 0x24, 0x06, 0x01, 0x8a, 0xff, 0x74, + 0xaf, 0xc1, 0x35, 0x8b, 0xeb, 0x32, 0xbc, 0xfc, 0xd6, 0x4c, 0xe0, 0x16, + 0x90, 0x80, 0x7a, 0x82, 0x21, 0x07, 0x5b, 0xca, 0x01, 0xca, 0x04, 0xf2, + 0x37, 0xbd, 0x80, 0x06, 0x45, 0xf0, 0x32, 0x30, 0x7e, 0x23, 0x5f, 0x1c, + 0x49, 0x4c, 0x1a, 0xe6, 0x33, 0x87, 0x9c, 0x46, 0xba, 0x13, 0x9d, 0x70, + 0x65, 0x63, 0x8d, 0x24, 0x8e, 0x23, 0xda, 0x03, 0x3e, 0xe5, 0x21, 0xbe, + 0x3b, 0x4c, 0x43, 0x48, 0xc0, 0xc2, 0xf8, 0xed, 0xef, 0xaa, 0x64, 0x6d, + 0x99, 0x6a, 0x1d, 0xfe, 0x22, 0xde, 0xab, 0xed, 0x96, 0xf6, 0xe8, 0x43, + 0xd5, 0x3f, 0x1c, 0x7c, 0x3f, 0x49, 0x7d, 0x98, 0x95, 0xb0, 0x1f, 0x0d, + 0x04, 0x9f, 0xb0, 0xd0, 0x01, 0x8d, 0xc2, 0x01, 0xd0, 0x05, 0xe5, 0x80, + 0xe9, 0x3b, 0x06, 0xfe, 0x59, 0x33, 0x9e, 0x9e, 0x49, 0x0c, 0x16, 0x2d, + 0x03, 0xcd, 0xc7, 0xae, 0xac, 0x92, 0x92, 0xad, 0xfe, 0xe2, 0xec, 0xd2, + 0x4c, 0xfb, 0x56, 0xfe, 0x09, 0x25, 0xa1, 0x07, 0xf6, 0xbd, 0x59, 0x60, + 0x55, 0xd4, 0x80, 0xbe, 0x03, 0x5b, 0xc9, 0xa5, 0x18, 0xbc, 0xde, 0xfa, + 0x71, 0x68, 0xe5, 0x76, 0xe1, 0xdc, 0x78, 0x0b, 0xe8, 0x76, 0xab, 0x35, + 0x53, 0xab, 0x29, 0xcd, 0xff, 0xbf, 0xfc, 0xff, 0xc7, 0xfa, 0x3d, 0x6b, + 0xcc, 0xf7, 0xab, 0xdf, 0xf7, 0xf7, 0x93, 0x75, 0x3c, 0xef, 0x5a, 0xf3, + 0x3c, 0x9a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd1, 0x75, 0x3c, + 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe8, + 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0xde, + 0xb1, 0xe7, 0x7a, 0x5a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, + 0xba, 0x5e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, + 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x4e, 0xe9, + 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x4b, 0xce, 0xf5, 0xaf, 0x33, + 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, 0x9d, 0xd2, 0xf3, + 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0x5d, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x4b, + 0x4e, 0xea, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, + 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, + 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, + 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x2d, 0x17, 0x53, + 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x78, 0xd2, + 0xd3, 0xba, 0xde, 0x67, 0x9e, 0x94, 0xce, 0xc4, 0x69, 0x49, 0x45, 0xca, + 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, + 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, + 0x01, 0x07, 0x4b, 0xf3, 0x2f, 0x33, 0xd6, 0xbc, 0xef, 0x7e, 0xb2, 0xd7, + 0x83, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, + 0xd7, 0x9d, 0xe9, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, + 0xd3, 0xb7, 0xdf, 0x76, 0xfb, 0xab, 0xee, 0x77, 0xbc, 0x80, 0x06, 0x41, + 0xa0, 0x64, 0x85, 0xb7, 0x3c, 0xae, 0x4a, 0xef, 0xf7, 0xea, 0x3b, 0xf6, + 0x35, 0x28, 0x6e, 0x1f, 0x7b, 0x47, 0xb9, 0x40, 0x19, 0x06, 0x92, 0x88, + 0x63, 0x3f, 0x50, 0x6e, 0x6d, 0x80, 0x7a, 0x87, 0xea, 0x12, 0x87, 0x71, + 0x1a, 0xf3, 0x40, 0xa7, 0x6f, 0xf0, 0xf2, 0x3e, 0xbb, 0x48, 0x68, 0x74, + 0x38, 0xea, 0xe3, 0x7a, 0x70, 0xc4, 0xfd, 0x87, 0xbe, 0x0f, 0x03, 0xb7, + 0x83, 0xd8, 0x62, 0x36, 0x1d, 0xad, 0xa9, 0x28, 0x69, 0x68, 0x48, 0xe7, + 0x0f, 0xb4, 0x90, 0xd2, 0xc8, 0x17, 0xd5, 0x79, 0x3b, 0x5e, 0x8b, 0xbb, + 0x55, 0x3e, 0xc3, 0x86, 0x80, 0xdc, 0xb2, 0xc3, 0x40, 0x25, 0x4f, 0x65, + 0x01, 0xec, 0xac, 0x27, 0x75, 0xa7, 0x09, 0x8b, 0xd7, 0xab, 0x66, 0xca, + 0xd8, 0xc8, 0x1f, 0x7a, 0xff, 0x92, 0xcb, 0x71, 0xfe, 0xf2, 0x19, 0x90, + 0xc2, 0xaf, 0xe4, 0xf7, 0xde, 0x40, 0x4e, 0x43, 0x0d, 0x03, 0x21, 0xa1, + 0x85, 0x27, 0x14, 0x12, 0x5f, 0x4a, 0x59, 0xd2, 0xb0, 0xd2, 0xd4, 0x83, + 0x31, 0xc7, 0xf1, 0xea, 0xbe, 0x0f, 0x7d, 0x30, 0x10, 0x46, 0x8b, 0x48, + 0x15, 0x26, 0x96, 0x94, 0x95, 0x90, 0x1b, 0xdd, 0x5d, 0x3d, 0x2e, 0x37, + 0xf5, 0xee, 0x8d, 0xb2, 0xbb, 0x2b, 0x99, 0x7c, 0xb0, 0x09, 0x80, 0x2c, + 0xe1, 0xb8, 0x02, 0xd2, 0x46, 0xef, 0x7b, 0xa3, 0x7a, 0x32, 0x58, 0xfa, + 0xf3, 0xd6, 0xb0, 0x0d, 0xd9, 0x09, 0xe0, 0x2a, 0xe3, 0x80, 0xe5, 0xf4, + 0x94, 0xa3, 0xa3, 0xfd, 0xcc, 0x55, 0xf0, 0xa0, 0x03, 0xce, 0x3f, 0x5c, + 0x20, 0x03, 0xc2, 0x11, 0xfd, 0x0f, 0xc4, 0xf6, 0xbe, 0x5c, 0xbe, 0x51, + 0x9e, 0xd3, 0x57, 0xff, 0x58, 0x01, 0xd7, 0x25, 0x7c, 0x5f, 0x17, 0xaf, + 0x90, 0xa0, 0x87, 0x8b, 0x4f, 0xc7, 0x0e, 0x89, 0x34, 0x98, 0x35, 0x86, + 0x66, 0xd8, 0xca, 0xdc, 0x4c, 0x28, 0x31, 0x69, 0x5f, 0xc1, 0x5a, 0xcc, + 0x0b, 0x74, 0x0b, 0xad, 0x6f, 0x54, 0xa0, 0x03, 0x72, 0x68, 0xc2, 0x90, + 0x19, 0xba, 0xcb, 0x4e, 0x70, 0x15, 0x21, 0x7c, 0x2c, 0xe0, 0xfb, 0xdb, + 0xbc, 0xa4, 0x32, 0x1f, 0x01, 0xd9, 0x37, 0x93, 0x7b, 0xe7, 0x52, 0x37, + 0x03, 0xc8, 0x20, 0xf3, 0xf8, 0x76, 0x32, 0xd2, 0xf7, 0xe8, 0xe4, 0xdf, + 0xbf, 0x6c, 0x72, 0x15, 0x78, 0x41, 0x88, 0x19, 0xae, 0xa7, 0x99, 0xeb, + 0x9e, 0x67, 0xba, 0x5a, 0xc2, 0xb4, 0xf1, 0x00, 0x3a, 0x0d, 0x01, 0x27, + 0x01, 0x11, 0x00, 0x81, 0x79, 0xc2, 0x68, 0x60, 0x19, 0x43, 0x1a, 0x67, + 0x17, 0x7d, 0x75, 0xcd, 0xc7, 0xe5, 0xe1, 0x37, 0xc8, 0x09, 0x81, 0xb8, + 0x35, 0x0e, 0x1d, 0xc4, 0xde, 0x7d, 0xea, 0x27, 0x35, 0x55, 0xf5, 0xbe, + 0xf9, 0xf7, 0xdf, 0x7c, 0xaf, 0xb9, 0xfb, 0xe3, 0xf4, 0xef, 0x78, 0xd0, + 0x07, 0x20, 0x3a, 0x19, 0xc0, 0x2d, 0x46, 0x3d, 0xb1, 0x6d, 0x8d, 0x3b, + 0x24, 0x89, 0xc3, 0x85, 0xde, 0xe9, 0xed, 0xfa, 0x1a, 0xe1, 0x42, 0x46, + 0xeb, 0xa5, 0xe7, 0x7a, 0xd7, 0x9d, 0xe6, 0xd6, 0x77, 0x4b, 0xce, 0xf5, + 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, + 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x9d, 0xeb, 0x5e, + 0x77, 0xa5, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x17, 0x53, + 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, + 0x96, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, + 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, + 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, + 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, + 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, + 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, + 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, + 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xb1, 0xa5, 0xa2, 0xeb, 0x79, 0x9e, 0x6a, + 0x4a, 0x77, 0x29, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x08, 0x53, 0xe4, 0x3c, 0xef, + 0x5a, 0xf3, 0x3d, 0xfa, 0x83, 0x5e, 0x25, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, + 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, + 0xef, 0x69, 0x49, 0xc1, 0x41, 0xf9, 0xd7, 0x8c, 0x67, 0xca, 0x73, 0x2d, + 0x3c, 0x6a, 0x0b, 0x42, 0x3a, 0x73, 0xaf, 0xf0, 0x1e, 0xa0, 0xed, 0xc7, + 0x39, 0xc9, 0x53, 0x29, 0x2c, 0x65, 0xc4, 0x51, 0x30, 0x6a, 0x3f, 0x7c, + 0xa3, 0xed, 0x7a, 0x5b, 0xb5, 0xf3, 0xde, 0x61, 0xbd, 0x1f, 0x7c, 0x7e, + 0xfb, 0xe3, 0x17, 0x96, 0xb1, 0x43, 0xed, 0x43, 0x06, 0x97, 0xca, 0x61, + 0xa4, 0x95, 0x30, 0xd7, 0x00, 0xa3, 0xa4, 0xef, 0xbe, 0xed, 0xce, 0x54, + 0xe4, 0x81, 0x9b, 0xab, 0x8c, 0xef, 0x8e, 0x61, 0x9d, 0x95, 0x9b, 0x33, + 0xee, 0x3b, 0x65, 0x3e, 0xcb, 0xb9, 0x30, 0x14, 0xf9, 0x6e, 0xbd, 0xed, + 0x49, 0x6e, 0xaa, 0xf7, 0xe7, 0x00, 0x37, 0x28, 0x7e, 0xbd, 0x70, 0x6a, + 0x72, 0x52, 0xa5, 0xec, 0x47, 0xba, 0xe9, 0xcc, 0x93, 0x42, 0xb6, 0x23, + 0x4e, 0xc4, 0x63, 0xc3, 0x88, 0x26, 0x5e, 0x5b, 0x5d, 0x95, 0x55, 0x2f, + 0x11, 0xac, 0xbb, 0x5a, 0x12, 0x35, 0x1b, 0x8f, 0xdb, 0x85, 0xd4, 0x96, + 0x8e, 0x82, 0x5f, 0xe8, 0x6c, 0x4a, 0x42, 0x50, 0x96, 0xc8, 0x74, 0xed, + 0x94, 0xf8, 0xa0, 0x97, 0xcf, 0x97, 0xff, 0x37, 0x1d, 0x7a, 0xa1, 0x84, + 0xce, 0x97, 0x15, 0x71, 0x25, 0x2c, 0x1f, 0x5c, 0x7c, 0xd8, 0x02, 0xd0, + 0xc0, 0x27, 0x82, 0xf4, 0xa0, 0x37, 0x0c, 0x02, 0x78, 0x2f, 0x5f, 0x54, + 0xea, 0xce, 0x24, 0x89, 0x64, 0x4e, 0x6c, 0xe7, 0x2c, 0xdc, 0xc0, 0x6c, + 0x4d, 0xe5, 0x30, 0xab, 0xd0, 0x6a, 0xbf, 0xe6, 0xc0, 0x07, 0xa4, 0x20, + 0x45, 0xfc, 0x40, 0x5e, 0xad, 0xe0, 0x27, 0x21, 0x60, 0x12, 0xa3, 0x88, + 0xee, 0x46, 0xbe, 0x9b, 0xca, 0x01, 0x42, 0x3e, 0x4a, 0xfa, 0x0a, 0xe9, + 0x14, 0xfb, 0x2f, 0x61, 0xce, 0x78, 0x1b, 0xb5, 0xa7, 0xab, 0x88, 0xab, + 0xb2, 0x19, 0x44, 0xfa, 0x72, 0x37, 0xaf, 0xbe, 0x7b, 0xc0, 0xae, 0x33, + 0x7e, 0x4e, 0xbd, 0x8a, 0x53, 0xd2, 0x78, 0x55, 0x52, 0x76, 0xce, 0xc6, + 0x71, 0xd9, 0x41, 0x56, 0xaf, 0x82, 0x1d, 0x66, 0x99, 0x87, 0x8f, 0x3e, + 0xcd, 0x74, 0x55, 0x57, 0xbc, 0xcf, 0x56, 0x82, 0x93, 0xd0, 0x35, 0x08, + 0x25, 0x81, 0x76, 0xfb, 0xed, 0xce, 0xdb, 0xe3, 0xd1, 0xd8, 0xd5, 0xe6, + 0xe2, 0xd5, 0x5e, 0x19, 0x89, 0x68, 0x2d, 0xb3, 0xfd, 0xbb, 0x65, 0x25, + 0xf0, 0xd6, 0x01, 0xee, 0xea, 0x1e, 0xea, 0xff, 0xb2, 0xae, 0xc0, 0x0b, + 0x0e, 0x59, 0xf7, 0x38, 0xd3, 0xee, 0x3c, 0xcf, 0x63, 0x42, 0x30, 0x0d, + 0xc0, 0xa2, 0x3a, 0x18, 0x97, 0xf0, 0xd2, 0x94, 0xa6, 0x2f, 0x20, 0xe3, + 0xb6, 0xe8, 0x6e, 0x31, 0xa6, 0x26, 0x94, 0x18, 0x51, 0x48, 0x25, 0x7d, + 0xc0, 0xba, 0x50, 0xf9, 0x05, 0x21, 0x94, 0x48, 0xf8, 0xa5, 0x3a, 0x9f, + 0x7e, 0x22, 0x90, 0x0b, 0x0f, 0x15, 0x50, 0xd3, 0xaf, 0x63, 0xdc, 0x80, + 0x10, 0x00, 0x9c, 0x00, 0xf0, 0x35, 0xff, 0x02, 0xc9, 0x51, 0x62, 0x87, + 0x24, 0x38, 0xd1, 0x42, 0x2b, 0x80, 0xc0, 0x46, 0x74, 0x36, 0xe4, 0x20, + 0xc4, 0xe4, 0xb6, 0x40, 0x17, 0x25, 0x6e, 0x78, 0xc7, 0x4b, 0x9d, 0xdf, + 0xf3, 0xff, 0x32, 0x25, 0x13, 0x06, 0x90, 0x89, 0x79, 0x09, 0xfc, 0x6a, + 0x70, 0x40, 0x61, 0x49, 0x03, 0xc8, 0x35, 0x39, 0xf0, 0x75, 0x8e, 0x26, + 0x32, 0x4f, 0xaf, 0xfd, 0xf5, 0x77, 0xf3, 0x2e, 0x1a, 0x19, 0xf3, 0x0d, + 0xc1, 0x7a, 0xfa, 0xef, 0x6d, 0xfe, 0xdd, 0xf0, 0xfa, 0xb2, 0xb6, 0xfb, + 0xbf, 0x02, 0x4b, 0x6d, 0xcf, 0x71, 0xf6, 0x84, 0xa9, 0x3e, 0x8f, 0x95, + 0x5e, 0xaa, 0xae, 0x79, 0x9e, 0xb9, 0xe6, 0x7b, 0xb5, 0xae, 0x5b, 0xa5, + 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, + 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, + 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, + 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, + 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, + 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, + 0x9e, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, + 0x3b, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, + 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, + 0xad, 0x79, 0xde, 0x96, 0x9d, 0xd4, 0xf3, 0x3d, 0x64, 0x69, 0x68, 0xb4, + 0x79, 0xa3, 0x4a, 0x67, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, + 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, + 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, + 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, + 0x11, 0x72, 0x94, 0xa4, 0x44, 0x00, 0x00, 0x01, 0x09, 0x53, 0xa1, 0xe6, + 0x7a, 0xd7, 0x9d, 0xef, 0xd2, 0x1a, 0xf1, 0xee, 0x97, 0x9d, 0xeb, 0x5e, + 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, + 0xed, 0x08, 0x46, 0x41, 0xc1, 0x46, 0x6c, 0x3b, 0x0a, 0xa3, 0xcc, 0xf6, + 0x59, 0xf0, 0x7c, 0xed, 0xf1, 0x5b, 0x66, 0xca, 0x5b, 0x88, 0x1c, 0xc6, + 0x28, 0xea, 0xc4, 0x14, 0x84, 0x66, 0x3c, 0xf3, 0xf6, 0x34, 0xfc, 0xdb, + 0x6c, 0xb3, 0x54, 0xba, 0xa7, 0x9d, 0xec, 0x46, 0xa5, 0x99, 0xfa, 0xeb, + 0x6d, 0x76, 0xe2, 0x99, 0x23, 0x46, 0xff, 0xc4, 0x21, 0x62, 0x7e, 0x46, + 0xe4, 0xf8, 0xf2, 0xb7, 0xee, 0xfc, 0x42, 0x8d, 0x3d, 0x6c, 0xf8, 0x3b, + 0xea, 0xb7, 0x9d, 0xec, 0xc9, 0x69, 0x77, 0x7e, 0xe7, 0x4d, 0xb5, 0xd4, + 0xf6, 0xb2, 0x56, 0x0e, 0x66, 0x66, 0x43, 0x30, 0xe3, 0x9d, 0x85, 0x3d, + 0x5b, 0xcc, 0xf1, 0xbf, 0x41, 0xdd, 0x9e, 0x47, 0xd8, 0x65, 0xbd, 0x68, + 0xcc, 0xe3, 0xce, 0xcc, 0xa3, 0x0e, 0x5b, 0x3f, 0x61, 0x7e, 0x0f, 0x33, + 0xd1, 0x92, 0x9c, 0x6f, 0xa9, 0xb5, 0xbf, 0x2b, 0x2f, 0xaf, 0x7e, 0xa7, + 0x14, 0xb5, 0x08, 0x5f, 0xa9, 0x7a, 0xe7, 0x99, 0xe4, 0xff, 0x9f, 0xe2, + 0xd4, 0x3f, 0x81, 0x17, 0x62, 0x75, 0x6a, 0x50, 0xea, 0x75, 0x2d, 0x4c, + 0x8e, 0x79, 0xe7, 0xa3, 0xc1, 0xe6, 0x7b, 0x2c, 0x94, 0xe3, 0x66, 0xda, + 0xd7, 0x81, 0xb3, 0xbb, 0x89, 0x7d, 0xe2, 0x1f, 0xc4, 0x89, 0xac, 0x79, + 0x9e, 0x4f, 0xc7, 0x9d, 0x3b, 0x53, 0x18, 0x0e, 0x11, 0x9b, 0x4c, 0x1f, + 0xcf, 0x7e, 0x2f, 0xaf, 0xac, 0x3f, 0xce, 0xf3, 0x3d, 0x0f, 0xc7, 0xf9, + 0xdd, 0x7f, 0xf1, 0xe2, 0x8e, 0xc7, 0x0e, 0x76, 0x72, 0x7c, 0xcf, 0xce, + 0x75, 0x0e, 0x64, 0x75, 0x8f, 0x5e, 0xf5, 0x8f, 0x3b, 0xd0, 0xe9, 0x1f, + 0x36, 0xd6, 0x86, 0x35, 0x1e, 0xb5, 0xe6, 0x79, 0x2e, 0x2e, 0xa7, 0x99, + 0xeb, 0x9e, 0x67, 0x86, 0x8b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe9, 0x69, + 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, + 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x9e, 0x67, 0xa5, 0xa7, 0x75, + 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x99, + 0xe8, 0x69, 0xdd, 0x4f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, + 0x9e, 0xb9, 0xe6, 0x7a, 0x5a, 0x2e, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, + 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, + 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0x68, 0x69, 0xdd, + 0x6f, 0x33, 0xd6, 0x52, 0x99, 0xda, 0xbc, 0xd4, 0xa4, 0xa2, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, + 0x01, 0x0a, 0x53, 0xd0, 0xf3, 0xbd, 0x6b, 0xcc, 0xf7, 0xdf, 0xb5, 0xe5, + 0x5d, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, 0x45, 0xd2, 0xf3, 0xbd, 0x6b, + 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa1, 0xa7, 0x75, + 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, + 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xcf, 0x43, 0x4e, 0xea, 0x79, + 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, + 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xad, 0xe6, 0x7a, + 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x4b, 0x4e, + 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, + 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, + 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, + 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, + 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x86, + 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0xb4, 0xee, 0x97, 0x9d, 0xeb, + 0x5e, 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, + 0x53, 0xcc, 0xf5, 0xcf, 0x1a, 0x5a, 0x77, 0x5b, 0xcc, 0xf3, 0xd2, 0x99, + 0xd8, 0x8d, 0x29, 0x28, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x0b, 0x53, 0xeb, 0xbc, 0xef, + 0x5a, 0xf3, 0xbd, 0xf6, 0xcd, 0x79, 0x97, 0x4b, 0xce, 0xf5, 0xaf, 0x33, + 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, + 0x3d, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x67, 0xa5, + 0xa7, 0x75, 0x3c, 0xcf, 0x5c, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, + 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x9e, 0x67, 0xad, 0x79, 0xde, 0x96, 0x9d, + 0xd2, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x99, 0xeb, 0x5e, + 0x77, 0xa1, 0xa7, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, + 0xe7, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, + 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, + 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0x3d, 0x0d, + 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, 0xd6, + 0xbc, 0xef, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe7, 0x7a, 0x1a, 0x77, + 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x77, 0xac, 0x9a, + 0x86, 0x8b, 0xad, 0xe6, 0x79, 0xa9, 0x29, 0xd8, 0x8d, 0x29, 0x28, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, + 0x01, 0x0c, 0x53, 0xf4, 0xef, 0x33, 0xd6, 0xbc, 0xef, 0x7d, 0x73, 0x5e, + 0x7d, 0xd2, 0xf3, 0xbd, 0x6b, 0xce, 0xf4, 0x34, 0xee, 0x97, 0x99, 0xeb, + 0x9e, 0x67, 0xa1, 0xa7, 0x75, 0x3c, 0xef, 0x58, 0xf3, 0xbd, 0x2d, 0x3b, + 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x68, 0xba, 0x5e, 0x77, 0xad, 0x79, + 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0xbd, 0x6b, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, + 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, 0xef, 0x5a, 0xf3, 0xbd, + 0x0d, 0x3b, 0xa5, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, 0x69, 0xdd, 0x4f, 0x33, + 0xd7, 0x3c, 0xcf, 0x43, 0x45, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, 0xf4, 0x34, + 0xee, 0x97, 0x9d, 0xeb, 0x5e, 0x67, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x5a, + 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xe7, 0x99, 0xe8, 0x69, 0xdd, + 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb5, 0xe6, + 0x7a, 0x1a, 0x77, 0x53, 0xce, 0xf5, 0x91, 0xa5, 0xa2, 0xd1, 0xe6, 0x8d, + 0x29, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, + 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, + 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, + 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, + 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, + 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, + 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, + 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, + 0x29, 0x4a, 0x44, 0x40, 0x00, 0x00, 0x01, 0x0d, 0x53, 0xf6, 0xcf, 0x3b, + 0xd6, 0x3c, 0xef, 0x7d, 0x2b, 0x5c, 0x57, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, + 0xd0, 0xd3, 0xba, 0x5e, 0x77, 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, + 0x7a, 0xd7, 0x99, 0xe8, 0x69, 0xdd, 0x4f, 0x33, 0xd6, 0xbc, 0xef, 0x43, + 0x4e, 0xe9, 0x79, 0xde, 0xb5, 0xe7, 0x7a, 0x5a, 0x77, 0x4b, 0xce, 0xf5, + 0xaf, 0x33, 0xd0, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, + 0xd4, 0xf3, 0x3d, 0x73, 0xcc, 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, + 0x77, 0xa1, 0xa2, 0xe9, 0x79, 0xde, 0xb5, 0xe6, 0x7a, 0x1a, 0x77, 0x53, + 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd3, 0xba, 0x9e, 0x67, 0xae, 0x79, 0x9e, + 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xcc, 0xf4, 0x34, 0xee, 0xa7, 0x9d, + 0xeb, 0x29, 0x4c, 0xed, 0x5e, 0x31, 0xa4, 0xa2, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, + 0x0e, 0x53, 0xfa, 0x2b, 0xce, 0xf5, 0xaf, 0x33, 0xde, 0xe3, 0x5c, 0xb7, + 0x53, 0xcc, 0xf5, 0xcf, 0x33, 0xd2, 0xd1, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, + 0xbd, 0x0d, 0x3b, 0xa9, 0xe6, 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, + 0x3b, 0xd6, 0xbc, 0xcf, 0x4b, 0x4e, 0xea, 0x79, 0x9e, 0xb9, 0xe6, 0x7a, + 0x1a, 0x77, 0x53, 0xcc, 0xf5, 0xaf, 0x3b, 0xd0, 0xd3, 0xba, 0x5e, 0x77, + 0xad, 0x79, 0x9e, 0x96, 0x8b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe8, 0x69, + 0xdd, 0x4f, 0x33, 0xd7, 0x3c, 0xcf, 0x43, 0x4e, 0xea, 0x79, 0x9e, 0xb5, + 0xe6, 0xa5, 0xa7, 0x75, 0x3c, 0xef, 0x3d, 0x29, 0x9d, 0x88, 0xd2, 0x92, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x88, 0x00, 0x00, 0x01, 0x0f, 0x53, 0xfa, 0xab, 0xcc, 0xf5, 0xaf, 0x3b, + 0xde, 0xa3, 0x5c, 0xf7, 0x4b, 0xce, 0xf5, 0xaf, 0x33, 0xd0, 0xd3, 0xba, + 0x9e, 0x67, 0xae, 0x79, 0x9e, 0x86, 0x9d, 0xd4, 0xf3, 0x3d, 0x6b, 0xce, + 0xf4, 0xb4, 0xee, 0xa7, 0x99, 0xeb, 0x5e, 0x77, 0xa1, 0xa7, 0x74, 0xbc, + 0xef, 0x5a, 0xf3, 0x3d, 0x0d, 0x3b, 0xa9, 0xe7, 0x7a, 0xd7, 0x99, 0xe9, + 0x68, 0xba, 0x9e, 0x67, 0xad, 0x78, 0xd0, 0xd3, 0xb4, 0x79, 0x9e, 0x6a, + 0x4a, 0x76, 0x23, 0x4a, 0x4a, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x97, 0xcf, + 0xca, 0x00, 0x58, 0x90, 0xd0, 0x1d, 0x00, 0x1f, 0xa0, 0x0a, 0x86, 0x01, + 0x81, 0x89, 0x4b, 0xb1, 0x6e, 0xc5, 0x3b, 0xa9, 0x9c, 0xcf, 0x6e, 0x58, + 0x6a, 0x03, 0x18, 0x6f, 0x67, 0xed, 0x84, 0xd3, 0xb7, 0xf7, 0xf4, 0x0a, + 0x5c, 0x22, 0x10, 0x0c, 0x0a, 0x21, 0x93, 0x40, 0x6c, 0x82, 0x1a, 0x1f, + 0xf4, 0x21, 0x1c, 0x61, 0x6c, 0x9e, 0x90, 0xe5, 0x31, 0xaf, 0x54, 0x01, + 0xa8, 0x06, 0x98, 0x03, 0x44, 0x92, 0xb9, 0x34, 0xa2, 0xcb, 0xcc, 0xe8, + 0x40, 0xd5, 0x38, 0xff, 0xf3, 0x07, 0x1e, 0xb9, 0x12, 0xd0, 0x34, 0x61, + 0xac, 0xec, 0xdc, 0xe8, 0x6e, 0x7d, 0xfa, 0x2a, 0x52, 0x96, 0x35, 0x5c, + 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0xf1, 0xa0, 0x18, + 0x00, 0x38, 0x48, 0x05, 0xe8, 0x4f, 0x28, 0x9a, 0x56, 0xe8, 0x61, 0xa8, + 0xf9, 0x2a, 0x77, 0x65, 0xb3, 0x73, 0xad, 0x8b, 0x2d, 0x01, 0x98, 0x6a, + 0x7b, 0x61, 0xf4, 0xec, 0xf7, 0xec, 0xa9, 0x72, 0x80, 0x62, 0x05, 0x10, + 0x03, 0xa2, 0xf9, 0x40, 0x67, 0x16, 0x94, 0x25, 0x28, 0x4b, 0x64, 0xe6, + 0xe8, 0xec, 0x6e, 0xce, 0x1d, 0x71, 0x09, 0x80, 0x0f, 0x31, 0x34, 0x07, + 0x5c, 0x07, 0x60, 0x15, 0x86, 0x20, 0xa4, 0x29, 0x23, 0x72, 0x5f, 0x25, + 0xb7, 0x18, 0x72, 0xfa, 0xcc, 0x3e, 0x7c, 0xb4, 0x6d, 0xbf, 0xea, 0xe7, + 0x2b, 0x9b, 0x26, 0xf7, 0xec, 0x29, 0x78, 0x00, 0x13, 0x00, 0x2e, 0x48, + 0x06, 0xa1, 0x9c, 0x34, 0x98, 0x43, 0x0c, 0xfb, 0x71, 0x9d, 0xf1, 0x79, + 0x6d, 0xb8, 0xe1, 0x76, 0xde, 0x00, 0xe9, 0xdb, 0x13, 0x00, 0x62, 0x03, + 0xa2, 0x6a, 0x7f, 0x24, 0x62, 0xdb, 0x9d, 0xfb, 0x84, 0xb6, 0xb5, 0x00, + 0xb4, 0x31, 0xfe, 0x1a, 0xbc, 0x7a, 0xba, 0x84, 0xaa, 0x43, 0x3d, 0xfb, + 0x2a, 0x5c, 0xd0, 0x1d, 0x80, 0xc0, 0x9a, 0x02, 0x62, 0xc9, 0x44, 0xd2, + 0xba, 0x39, 0x8e, 0x77, 0x5b, 0x3e, 0xe6, 0xeb, 0x20, 0x06, 0x80, 0x26, + 0x00, 0x37, 0x0d, 0xc4, 0xcc, 0x5a, 0x0a, 0x4f, 0x24, 0x6f, 0xba, 0xdb, + 0x7f, 0xd7, 0xb2, 0x9b, 0x63, 0x6c, 0x39, 0x68, 0xc8, 0x1a, 0xea, 0x67, + 0x73, 0xd5, 0x53, 0x2d, 0xaf, 0xbb, 0x4b, 0xe7, 0x25, 0x80, 0x2a, 0xe1, + 0x81, 0xa8, 0x0c, 0x48, 0x0c, 0x40, 0xa2, 0x0b, 0x4b, 0x0c, 0x47, 0x1a, + 0xfd, 0xfb, 0x66, 0x51, 0xbe, 0xf5, 0x60, 0x27, 0x00, 0xc8, 0x0a, 0x80, + 0x65, 0xd1, 0x89, 0xa8, 0x61, 0xbb, 0xfd, 0xf7, 0xdf, 0x76, 0x3d, 0x78, + 0xdd, 0x6c, 0x82, 0x18, 0x61, 0x7f, 0x6c, 0x9d, 0xdd, 0x78, 0x58, 0x81, + 0xf2, 0xe8, 0xfa, 0xfb, 0x74, 0xa5, 0xe4, 0x00, 0x60, 0x00, 0xd8, 0x0a, + 0x80, 0x9c, 0x0a, 0x16, 0x43, 0x43, 0x64, 0x23, 0x76, 0xdc, 0xf2, 0xc6, + 0x65, 0xec, 0xc1, 0x4b, 0xb1, 0x49, 0x68, 0x2b, 0x3a, 0x7b, 0x63, 0xaa, + 0xd9, 0xef, 0xd7, 0xd2, 0x94, 0xb2, 0x9d, 0xca, 0x52, 0x91, 0x17, 0x29, + 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, + 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xb8, + 0x40, 0x1a, 0x00, 0x98, 0x00, 0xdc, 0x37, 0x13, 0x31, 0x68, 0x29, 0x3c, + 0x91, 0xbe, 0xeb, 0x6d, 0xff, 0x5e, 0xca, 0x6d, 0x8d, 0xad, 0x29, 0xc8, + 0xdf, 0xf5, 0x6c, 0x7c, 0x8f, 0xbe, 0xed, 0x2f, 0x22, 0x00, 0xec, 0x03, + 0x04, 0x00, 0x5c, 0x90, 0xc2, 0x80, 0xcf, 0x2c, 0xb4, 0xa0, 0x6a, 0x73, + 0x36, 0xc6, 0x66, 0xe6, 0xbf, 0xdc, 0x2a, 0xf5, 0xd5, 0x14, 0x5e, 0x2d, + 0x19, 0x28, 0x5e, 0xe7, 0x4b, 0xe6, 0xb9, 0x4a, 0x52, 0xca, 0xab, 0x94, + 0xbe, 0x6a, 0x03, 0xa0, 0x05, 0x88, 0x26, 0x90, 0xb8, 0x0e, 0xd2, 0x05, + 0x70, 0x0d, 0x86, 0x25, 0x2e, 0x84, 0x24, 0x68, 0x16, 0xfd, 0xd4, 0x96, + 0x1d, 0xef, 0xa5, 0x5d, 0x25, 0x06, 0xa0, 0xbe, 0x9c, 0x8e, 0xe3, 0x3b, + 0x71, 0x23, 0xa8, 0xfb, 0xeb, 0x94, 0xa5, 0x2e, 0x6a, 0xee, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x88, + 0x00, 0x00, 0x01, 0x10, 0x53, 0xfb, 0x23, 0xcc, 0xf5, 0xcf, 0x33, 0xdd, + 0x6d, 0x65, 0x75, 0x3c, 0xcf, 0x5a, 0xf3, 0xbd, 0x2d, 0x3b, 0xa5, 0xe7, + 0x7a, 0xd7, 0x9d, 0xe8, 0x69, 0xdd, 0x2f, 0x3b, 0xd6, 0xbc, 0xcf, 0x43, + 0x4e, 0xea, 0x79, 0x9e, 0xb6, 0x34, 0xb4, 0x5a, 0x3c, 0xd1, 0xa5, 0x33, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x7c, 0x24, 0x00, 0xb8, 0x37, + 0x06, 0x80, 0xe8, 0x9a, 0x4c, 0xf9, 0xc6, 0x25, 0xd6, 0xbc, 0x30, 0xbc, + 0xcc, 0xc0, 0x22, 0xee, 0x26, 0xfb, 0xb5, 0xf2, 0xb0, 0x02, 0xac, 0x90, + 0x03, 0xf2, 0x6f, 0x58, 0xc0, 0x30, 0xdc, 0x73, 0xf4, 0xe6, 0x71, 0x6c, + 0x35, 0x83, 0xef, 0x64, 0x03, 0x54, 0x38, 0x14, 0x2c, 0x53, 0x7c, 0x27, + 0x7b, 0x00, 0xce, 0xc9, 0xd5, 0xff, 0x41, 0x00, 0x84, 0x0c, 0x8e, 0x9e, + 0x49, 0xef, 0x91, 0x8d, 0xc3, 0xfd, 0xea, 0x4a, 0x29, 0x21, 0xa9, 0x6c, + 0x1f, 0xee, 0x70, 0x15, 0x4f, 0x48, 0x6a, 0x45, 0x76, 0x0a, 0xbe, 0x58, + 0x03, 0xad, 0x8a, 0x26, 0x72, 0xc7, 0xbf, 0xe1, 0xd6, 0xbf, 0x8c, 0xce, + 0x6f, 0xa2, 0xfd, 0xa0, 0x05, 0x48, 0xdf, 0x23, 0x96, 0x13, 0xcc, 0x65, + 0x0a, 0xbd, 0x29, 0x64, 0xce, 0x18, 0x96, 0x4f, 0x0f, 0xb9, 0x64, 0x2d, + 0xc3, 0x49, 0xbb, 0xf7, 0xeb, 0x7f, 0xc0, 0x3f, 0xbc, 0x88, 0x0e, 0xd8, + 0xb0, 0xd2, 0x8a, 0x0b, 0x0f, 0x6a, 0x9f, 0xe4, 0xec, 0xee, 0xa7, 0xe2, + 0xa2, 0xfd, 0x29, 0x60, 0x50, 0x94, 0x50, 0xde, 0x91, 0xcc, 0x2e, 0x72, + 0x60, 0x14, 0xc1, 0xa8, 0xe9, 0xfd, 0xb6, 0xbc, 0x58, 0x09, 0xfb, 0x92, + 0x8a, 0xc6, 0xf7, 0x20, 0x11, 0x6e, 0x71, 0x60, 0x28, 0x02, 0x8d, 0xbb, + 0xbe, 0x61, 0x2e, 0x7b, 0x13, 0xee, 0xd1, 0xbc, 0x66, 0x1f, 0xf9, 0x3a, + 0xcd, 0xaf, 0xec, 0xa1, 0x85, 0x01, 0x94, 0xb3, 0xb5, 0xe3, 0x80, 0x0f, + 0x00, 0xbb, 0x6e, 0xac, 0x7f, 0xe4, 0x6c, 0x1d, 0x56, 0x02, 0x62, 0x80, + 0xbf, 0xff, 0x12, 0x09, 0xdc, 0x07, 0xf7, 0x64, 0x0a, 0x86, 0xfe, 0x1b, + 0xdb, 0x92, 0x15, 0x88, 0xb7, 0x07, 0xff, 0xa5, 0xb7, 0x32, 0xd0, 0xca, + 0xb3, 0xd4, 0x00, 0xc4, 0x99, 0x80, 0xa1, 0x0c, 0xb0, 0xcc, 0x9c, 0x5e, + 0x47, 0x61, 0xb8, 0xd0, 0x3b, 0x8e, 0xe2, 0xef, 0x00, 0x00, 0x91, 0x3c, + 0x02, 0xb0, 0xd4, 0x3a, 0xb8, 0x63, 0xfc, 0x2f, 0x27, 0x31, 0x1f, 0xdf, + 0x53, 0xbe, 0x7e, 0x00, 0xb0, 0x34, 0x94, 0x02, 0x1c, 0x86, 0x01, 0xca, + 0x12, 0x81, 0x0d, 0x86, 0x80, 0x0c, 0xae, 0x50, 0x2b, 0x9c, 0x84, 0x5e, + 0x3c, 0x23, 0x93, 0xad, 0x10, 0x49, 0xd5, 0xc6, 0xf0, 0x1d, 0x94, 0x08, + 0x5f, 0x29, 0xd8, 0xdf, 0x82, 0xfa, 0xc8, 0xfe, 0xa4, 0x0a, 0xa7, 0x23, + 0x20, 0x99, 0x8e, 0x28, 0xfe, 0x3d, 0x26, 0x85, 0x1d, 0x78, 0x52, 0x1a, + 0x00, 0xc0, 0x69, 0x64, 0x3e, 0x3b, 0x0c, 0x01, 0x5f, 0xcc, 0x4e, 0x67, + 0xbe, 0xd8, 0x7f, 0xb4, 0x28, 0x27, 0x61, 0x5b, 0x05, 0xd3, 0xea, 0xdf, + 0x81, 0x00, 0x98, 0x10, 0x82, 0x40, 0x1d, 0x72, 0xc0, 0x70, 0x9e, 0x51, + 0x69, 0x70, 0x1e, 0xf7, 0xc8, 0xe1, 0xe1, 0x0d, 0x7e, 0x81, 0xbe, 0x00, + 0x00, 0xec, 0x10, 0x81, 0xc0, 0x30, 0x41, 0x30, 0xf7, 0x25, 0x06, 0x39, + 0xac, 0x8c, 0xee, 0xc0, 0x22, 0x27, 0xdf, 0x68, 0x01, 0x80, 0x14, 0xe0, + 0x3b, 0x26, 0x94, 0x19, 0x80, 0xff, 0x5f, 0x1d, 0x8d, 0xe1, 0x57, 0x50, + 0x0e, 0xbe, 0x18, 0x1a, 0x1b, 0xc0, 0xfa, 0x7f, 0x71, 0x5a, 0x84, 0x9f, + 0xb5, 0x7f, 0xf2, 0x32, 0x18, 0x49, 0x09, 0x1f, 0x9e, 0x11, 0xc2, 0xf5, + 0xe9, 0xc0, 0x40, 0x87, 0x2f, 0xf6, 0x3b, 0x13, 0xc0, 0x7f, 0x79, 0x80, + 0x13, 0xa7, 0x64, 0x64, 0x63, 0x56, 0x1d, 0xa0, 0x02, 0x0c, 0x10, 0x9e, + 0x3f, 0xb0, 0x50, 0x1b, 0xbd, 0x27, 0x01, 0x47, 0x16, 0x70, 0x7d, 0x91, + 0xb7, 0xeb, 0xc8, 0x63, 0x4b, 0x21, 0xfc, 0x85, 0xb3, 0x6c, 0x28, 0x08, + 0xde, 0x4c, 0x04, 0x29, 0xc3, 0x36, 0x35, 0xdc, 0x3a, 0x50, 0x28, 0x4d, + 0xe1, 0xa5, 0xf5, 0x6c, 0xaf, 0xd5, 0x96, 0x7e, 0x36, 0xfa, 0x81, 0x0d, + 0x03, 0x03, 0x53, 0x9f, 0xf1, 0x22, 0x2e, 0x3d, 0x82, 0x76, 0x38, 0x66, + 0xae, 0xfb, 0xbb, 0x3f, 0x1d, 0x7c, 0x9f, 0x86, 0x24, 0x30, 0x99, 0xf9, + 0x2d, 0x5c, 0xcc, 0x77, 0x7b, 0xea, 0x48, 0x40, 0xd4, 0xec, 0x3f, 0xb5, + 0xf1, 0x90, 0x13, 0xf5, 0x21, 0x1b, 0x0b, 0x11, 0xb6, 0xb8, 0x53, 0xd6, + 0x8d, 0x6b, 0xaf, 0xe8, 0x85, 0x13, 0x1d, 0x04, 0xb4, 0xf6, 0x45, 0x9c, + 0xb2, 0x16, 0x42, 0x31, 0x68, 0x48, 0xdd, 0x79, 0x50, 0x28, 0x5e, 0xd8, + 0x0c, 0x17, 0x8f, 0x39, 0xc5, 0x99, 0x7a, 0xa0, 0x2a, 0x18, 0x03, 0x6e, + 0xdb, 0xf6, 0xeb, 0xad, 0xf8, 0xd6, 0x0a, 0x1d, 0xf4, 0xaf, 0xd9, 0x6c, + 0xfd, 0xb5, 0xf3, 0xc0, 0x0a, 0x91, 0xbe, 0x41, 0x69, 0x64, 0xf1, 0x4a, + 0x0b, 0xbd, 0xda, 0x1d, 0xd4, 0x7e, 0x58, 0x55, 0xf0, 0xa0, 0x2c, 0x03, + 0x14, 0x0e, 0x4e, 0x0b, 0xc0, 0x72, 0x82, 0xfb, 0x08, 0x34, 0x54, 0xf5, + 0xfd, 0x40, 0x02, 0xc6, 0x2c, 0x95, 0xfe, 0xdc, 0xc3, 0xd7, 0x12, 0x19, + 0x33, 0x13, 0x06, 0x67, 0x7f, 0xcf, 0xb8, 0x60, 0x3a, 0x47, 0xe4, 0xd5, + 0xbb, 0xa3, 0x72, 0x05, 0x8c, 0x37, 0x00, 0xdc, 0x86, 0xcf, 0x9b, 0x36, + 0x03, 0x82, 0x5f, 0xda, 0x3f, 0x1a, 0x7a, 0xb6, 0x7f, 0x4d, 0xf6, 0x52, + 0x05, 0x32, 0x03, 0x31, 0x7d, 0x2b, 0xec, 0xa1, 0x20, 0x72, 0xe8, 0x94, + 0x4c, 0x74, 0x12, 0xd3, 0xd9, 0x17, 0xcf, 0x00, 0x4f, 0xff, 0x18, 0x8c, + 0x2f, 0xf0, 0xe0, 0x23, 0x74, 0x40, 0xa1, 0x7b, 0x60, 0x30, 0x5e, 0x3c, + 0xe7, 0x16, 0x65, 0xd1, 0x92, 0x91, 0x8e, 0x2c, 0x9d, 0x49, 0xb7, 0xea, + 0x8b, 0x21, 0x64, 0x23, 0x16, 0x84, 0x8d, 0xd7, 0x19, 0x00, 0x3a, 0xdc, + 0xa1, 0xbd, 0x2a, 0x5f, 0x56, 0x33, 0x5d, 0x90, 0x2a, 0x18, 0x03, 0x6f, + 0xb6, 0xfd, 0xba, 0xef, 0x98, 0x80, 0x4c, 0x03, 0xac, 0x23, 0x70, 0xff, + 0xdc, 0x0d, 0x8a, 0x8e, 0x9e, 0xd9, 0xb9, 0xfe, 0x2f, 0xe8, 0xc1, 0x80, + 0x50, 0x96, 0x7f, 0xdb, 0xb3, 0xec, 0x68, 0x1a, 0xbb, 0xc0, 0x36, 0x49, + 0x2f, 0xa3, 0x67, 0xf8, 0x07, 0xf7, 0xce, 0x00, 0x4d, 0x9f, 0x96, 0x5f, + 0x0b, 0x27, 0xfb, 0xc6, 0x06, 0x77, 0xc9, 0x2d, 0x41, 0x3f, 0xf7, 0x15, + 0x95, 0x9e, 0xd3, 0x97, 0xf7, 0xe6, 0x9c, 0x17, 0xe4, 0xd7, 0xf5, 0xa0, + 0x0a, 0xb0, 0xd2, 0x51, 0x2f, 0x0d, 0x30, 0x70, 0x7d, 0xaf, 0x10, 0x80, + 0xb9, 0x2b, 0xa7, 0x6e, 0xe4, 0xe6, 0x7b, 0x86, 0x18, 0x92, 0x8a, 0xef, + 0x9c, 0xb4, 0xfb, 0x92, 0x03, 0xa7, 0x2c, 0x34, 0xb4, 0xa4, 0xfc, 0xa0, + 0x0e, 0x2c, 0x3f, 0xfb, 0x7e, 0x28, 0x9f, 0x55, 0xfa, 0xa2, 0xc0, 0xa0, + 0xc4, 0xb6, 0x49, 0x8f, 0xff, 0x56, 0xcb, 0x69, 0x49, 0xa0, 0x50, 0x06, + 0xc8, 0xdd, 0xdc, 0xcf, 0x72, 0x12, 0x02, 0x90, 0x2a, 0x30, 0xc7, 0x58, + 0xb6, 0x01, 0xf5, 0x84, 0x06, 0x2e, 0x5a, 0x72, 0x31, 0x87, 0x89, 0xfd, + 0x96, 0x02, 0xf9, 0xef, 0xdd, 0x19, 0xcc, 0x7f, 0x2b, 0xf7, 0x01, 0x84, + 0x24, 0x86, 0xbf, 0x3b, 0x63, 0x7d, 0xe1, 0x48, 0x41, 0x85, 0x12, 0xdd, + 0x09, 0xfc, 0x63, 0xba, 0x83, 0xae, 0x78, 0x0e, 0xf8, 0x18, 0x0c, 0xc5, + 0x63, 0x54, 0x1d, 0xae, 0x68, 0x0e, 0x91, 0x8a, 0x2b, 0xf3, 0x4f, 0xe2, + 0x80, 0x82, 0xee, 0xd4, 0xa7, 0xf4, 0x7e, 0x3d, 0x5b, 0x50, 0xd7, 0xf4, + 0xd2, 0x1b, 0x86, 0x93, 0x7e, 0xcf, 0xd9, 0x41, 0x47, 0x93, 0xe4, 0x00, + 0xab, 0x23, 0xec, 0x94, 0xed, 0xf9, 0xdb, 0x2d, 0x62, 0xef, 0x0c, 0x03, + 0x04, 0x70, 0x92, 0xf9, 0xe9, 0x02, 0x37, 0xa4, 0xc9, 0x0c, 0x41, 0x7c, + 0xbf, 0xfa, 0x52, 0xe7, 0x63, 0x8d, 0x1e, 0x7d, 0xc6, 0xe6, 0x63, 0x35, + 0x77, 0xec, 0x0b, 0x26, 0x24, 0xa4, 0x8c, 0x3f, 0xf3, 0x9a, 0xf3, 0xdc, + 0x0a, 0x62, 0xb0, 0x17, 0x4b, 0x39, 0x99, 0x77, 0xcf, 0x88, 0x43, 0x4b, + 0x2d, 0x25, 0xa7, 0x37, 0xc1, 0x1b, 0x0e, 0x36, 0xfa, 0x2e, 0x04, 0x2f, + 0xa2, 0x6b, 0xf3, 0x9c, 0x9c, 0x66, 0x0f, 0xa4, 0x6f, 0xe1, 0xd9, 0x73, + 0xab, 0x7c, 0x28, 0x0e, 0xf0, 0x01, 0xb0, 0x03, 0xac, 0x18, 0x37, 0xbf, + 0xd9, 0x0e, 0xcc, 0x03, 0x9e, 0x35, 0x39, 0x8c, 0x53, 0xeb, 0xef, 0x77, + 0xc4, 0x40, 0x16, 0xf4, 0xa4, 0x06, 0xc1, 0x88, 0x5a, 0x8b, 0x2d, 0x28, + 0xc3, 0xb6, 0x52, 0x78, 0x70, 0xdb, 0xec, 0x25, 0x93, 0x03, 0x43, 0x18, + 0x6a, 0x3a, 0x1f, 0x6f, 0x9f, 0x1f, 0xbd, 0x90, 0x09, 0x80, 0x70, 0x37, + 0x9e, 0x5f, 0xe9, 0xa8, 0xb3, 0xc4, 0x5e, 0xda, 0x58, 0xf9, 0x30, 0x35, + 0x08, 0xc5, 0xf4, 0x27, 0x21, 0x27, 0x76, 0x57, 0x68, 0x00, 0xd8, 0x34, + 0x94, 0x4b, 0x19, 0xc6, 0x3b, 0x0d, 0x65, 0x3d, 0xe2, 0x3e, 0x1f, 0x74, + 0x5e, 0xda, 0x52, 0x94, 0x4e, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x20, 0x00, 0x00, 0x01, 0x11, 0x6b, 0xfb, 0x9b, 0xce, + 0xf5, 0xaf, 0x35, 0x4d, 0x4d, 0xd4, 0xf3, 0x3d, 0x64, 0x65, 0x16, 0xaf, + 0x18, 0xd2, 0x99, 0xdc, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, + 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, + 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, + 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, 0x17, 0x2f, 0x99, 0x00, + 0x64, 0x01, 0x98, 0x0e, 0x80, 0x34, 0xc5, 0xb2, 0x09, 0xa9, 0xe5, 0xfc, + 0xff, 0x8c, 0xc7, 0x1c, 0x68, 0xbf, 0x7d, 0x36, 0x97, 0x49, 0x65, 0x60, + 0xdc, 0xdc, 0xea, 0x37, 0x6b, 0xc4, 0x1b, 0xd1, 0xd2, 0xe8, 0xe9, 0xee, + 0x7f, 0x4b, 0x3b, 0x87, 0x7f, 0x79, 0x60, 0x13, 0x00, 0x5f, 0xc0, 0xa1, + 0x31, 0x24, 0x24, 0xa0, 0x96, 0x51, 0x6e, 0xeb, 0x6f, 0xb2, 0x73, 0x25, + 0x8c, 0x10, 0x2e, 0xf6, 0xd4, 0xa5, 0x25, 0x64, 0xe6, 0xeb, 0x9b, 0xb5, + 0xfc, 0xd4, 0x30, 0x01, 0xf9, 0x08, 0x04, 0xc8, 0x4e, 0x01, 0xd1, 0x31, + 0x2e, 0x93, 0xf9, 0x7d, 0xce, 0x38, 0xe1, 0xcb, 0xbd, 0x38, 0x14, 0x01, + 0x3a, 0x49, 0x80, 0x31, 0x40, 0x15, 0x2d, 0xcb, 0xef, 0xdc, 0x7f, 0x7d, + 0xf1, 0x15, 0x57, 0xa3, 0xa5, 0xae, 0x26, 0x23, 0x0d, 0x51, 0x25, 0x5f, + 0xf3, 0xf8, 0x7d, 0x2f, 0xc2, 0xaf, 0xc0, 0x80, 0x6a, 0x00, 0xff, 0x00, + 0xef, 0xf2, 0x50, 0x66, 0x43, 0xa0, 0x60, 0xde, 0x49, 0x18, 0xee, 0xc3, + 0xb2, 0xdc, 0x4e, 0xf7, 0x2c, 0x86, 0x00, 0xcc, 0x0c, 0x86, 0x21, 0x08, + 0x26, 0x64, 0x27, 0x2f, 0x73, 0xf1, 0x5c, 0xed, 0xd1, 0x87, 0xb7, 0xe7, + 0x9d, 0xef, 0xa9, 0x52, 0xd0, 0x02, 0xc2, 0x99, 0x19, 0x89, 0x07, 0x2f, + 0xc8, 0x6f, 0xbf, 0x9c, 0x80, 0x64, 0x00, 0xfc, 0xa0, 0x13, 0x06, 0x06, + 0xb9, 0x49, 0x48, 0x69, 0x7d, 0xc6, 0xb9, 0xea, 0x56, 0xe1, 0xee, 0x17, + 0x7d, 0x01, 0x29, 0xe5, 0xa7, 0xa5, 0x3d, 0xfa, 0x7f, 0xff, 0x8f, 0xfe, + 0xba, 0x96, 0x18, 0xa4, 0x23, 0xa5, 0x3b, 0x2d, 0x27, 0x87, 0x54, 0xf7, + 0x2e, 0x00, 0x06, 0x80, 0x57, 0x00, 0xc0, 0x0a, 0x94, 0x1a, 0x9d, 0xfe, + 0x40, 0xd5, 0x63, 0xd6, 0x8f, 0x88, 0xb7, 0x7e, 0x96, 0x5f, 0x64, 0xef, + 0xb9, 0xdd, 0x55, 0x1d, 0x7e, 0x18, 0x01, 0xf8, 0x06, 0x20, 0x06, 0xfc, + 0xac, 0x8d, 0xd0, 0x84, 0x63, 0xb8, 0x13, 0xe6, 0xa8, 0x51, 0xc1, 0xf7, + 0x7c, 0x06, 0x05, 0x86, 0x06, 0x06, 0xa0, 0xb2, 0xc6, 0x81, 0x64, 0xa1, + 0x3b, 0x23, 0x65, 0xb7, 0x75, 0xbf, 0xd7, 0x6a, 0x96, 0xa8, 0x29, 0x08, + 0x40, 0xcc, 0xad, 0x4b, 0xbf, 0x7b, 0xf9, 0x28, 0x0c, 0x40, 0x1f, 0x80, + 0x80, 0x04, 0xf9, 0x18, 0x34, 0x98, 0x4d, 0x08, 0xfd, 0xbe, 0xfb, 0x75, + 0x9f, 0x76, 0x4a, 0x00, 0x7e, 0x50, 0x0c, 0x32, 0x70, 0x0e, 0x88, 0x59, + 0x0e, 0xc0, 0x5c, 0xbe, 0xe9, 0x1e, 0xb3, 0x02, 0xaf, 0xa0, 0xd2, 0xe9, + 0xc0, 0x50, 0xad, 0x86, 0x2d, 0x87, 0x3f, 0x6a, 0x06, 0xfb, 0xe7, 0x16, + 0x4d, 0x49, 0x0c, 0xb1, 0xa4, 0xd4, 0xf4, 0x9c, 0xfc, 0xd6, 0x14, 0x65, + 0xf3, 0xa2, 0x10, 0x03, 0xf0, 0x28, 0x42, 0x01, 0xd7, 0x21, 0x21, 0x01, + 0xab, 0xc9, 0xc4, 0xb0, 0x8d, 0xdd, 0x2e, 0xb3, 0xcf, 0xc4, 0x4b, 0xea, + 0xd4, 0xb1, 0x48, 0x62, 0x0b, 0xdb, 0x73, 0xb2, 0xf6, 0xca, 0x3a, 0xad, + 0xfe, 0xbf, 0x2e, 0x02, 0x00, 0x13, 0x80, 0x1c, 0x10, 0xc9, 0x7b, 0x0d, + 0x47, 0x74, 0x7d, 0xcf, 0x19, 0xf9, 0x1d, 0x55, 0x60, 0x19, 0x81, 0x52, + 0x60, 0x0c, 0x43, 0x78, 0x69, 0x58, 0xb4, 0x27, 0xb3, 0xee, 0xcc, 0xac, + 0xcf, 0x9b, 0x88, 0xbd, 0x65, 0x28, 0x25, 0x20, 0x69, 0x2c, 0xd6, 0x60, + 0xe9, 0xba, 0x9a, 0xf8, 0x80, 0x20, 0x02, 0xa0, 0x07, 0x80, 0x54, 0xa0, + 0xd6, 0x71, 0x8e, 0xf8, 0x57, 0xdf, 0x11, 0x6f, 0x2e, 0x02, 0x10, 0x07, + 0x7c, 0x86, 0x90, 0x14, 0x20, 0x94, 0x76, 0xe9, 0xeb, 0x4a, 0x76, 0x58, + 0xce, 0x63, 0x3b, 0xa8, 0xd3, 0x6f, 0xa1, 0xd2, 0xae, 0x18, 0x8e, 0x9c, + 0x9f, 0x97, 0xb3, 0x49, 0xfb, 0x35, 0xfc, 0xcc, 0x34, 0x02, 0xf0, 0x28, + 0x02, 0x74, 0x20, 0x00, 0xfc, 0x98, 0x92, 0x5a, 0x00, 0xf9, 0x5f, 0x65, + 0x30, 0xb1, 0x66, 0x5e, 0x98, 0x03, 0x50, 0x0d, 0x4b, 0x00, 0xd5, 0x3b, + 0xa7, 0x64, 0x64, 0x12, 0x11, 0x8c, 0x73, 0xd6, 0x2e, 0xf4, 0xd4, 0xa9, + 0x04, 0xc4, 0x23, 0xef, 0xb1, 0xce, 0xc2, 0x9c, 0xf6, 0x8f, 0xff, 0xbf, + 0x36, 0x01, 0xa0, 0x08, 0x52, 0x01, 0xa1, 0x7d, 0x28, 0xd9, 0x3b, 0xe6, + 0xfb, 0x90, 0x4f, 0xd7, 0x97, 0x00, 0x13, 0x81, 0x42, 0x11, 0x65, 0x93, + 0x40, 0xa1, 0x31, 0x28, 0x2c, 0xbd, 0xf0, 0x4f, 0xc4, 0xac, 0xeb, 0x7d, + 0xb0, 0xbd, 0x95, 0x7d, 0x32, 0x92, 0x49, 0x31, 0x09, 0xef, 0xf1, 0xf9, + 0xba, 0xcf, 0xc2, 0xe1, 0xfd, 0xf8, 0x00, 0x0c, 0x00, 0x1f, 0x80, 0x1e, + 0x16, 0x4d, 0x48, 0x6e, 0xe5, 0x81, 0x96, 0xff, 0xe6, 0x77, 0xdf, 0x91, + 0xdf, 0xda, 0x80, 0x04, 0xe0, 0x85, 0xf3, 0x89, 0xb8, 0x98, 0x56, 0xd9, + 0x29, 0x2c, 0x62, 0xd6, 0x94, 0xe6, 0x7f, 0xcd, 0x6e, 0xbc, 0xa5, 0x5e, + 0xd6, 0x93, 0x01, 0xb1, 0x4f, 0xd2, 0xcb, 0x4e, 0x6e, 0x22, 0x06, 0xfb, + 0xf9, 0xe0, 0x08, 0x40, 0x1d, 0xf2, 0x1a, 0x40, 0x50, 0x82, 0x51, 0xdb, + 0xa7, 0xad, 0x29, 0xd9, 0x63, 0x39, 0x8c, 0xee, 0xa3, 0x4d, 0xb3, 0x06, + 0x80, 0x5e, 0x05, 0x00, 0x4e, 0x84, 0x00, 0x1f, 0x93, 0x12, 0x4b, 0x40, + 0x1f, 0x2b, 0xec, 0xa6, 0x16, 0x2c, 0xcb, 0xe9, 0x94, 0xac, 0x02, 0xc2, + 0xb6, 0xcd, 0x98, 0x61, 0xdf, 0x7e, 0xe1, 0x72, 0x1b, 0xeb, 0xff, 0x1a, + 0x58, 0x6a, 0x40, 0xa9, 0x68, 0xc7, 0x19, 0x83, 0xef, 0x97, 0x00, 0xc5, + 0x28, 0xc3, 0x32, 0x77, 0xfb, 0xbf, 0x51, 0xea, 0x03, 0x77, 0xdb, 0x8a, + 0x49, 0x29, 0x39, 0x2c, 0xec, 0x65, 0xcd, 0x26, 0x86, 0x80, 0xdd, 0x2e, + 0xcc, 0xa6, 0xbb, 0x3a, 0x17, 0x87, 0x67, 0xb3, 0x7a, 0xbb, 0x92, 0x1a, + 0x01, 0x7b, 0x13, 0x00, 0x2c, 0x02, 0x85, 0x01, 0x82, 0x99, 0xbb, 0xad, + 0x3d, 0xcf, 0x71, 0x2b, 0x0e, 0xaa, 0x01, 0x00, 0x05, 0xe0, 0x16, 0x06, + 0x24, 0x98, 0x1a, 0x8e, 0x59, 0x5f, 0x76, 0xd8, 0xfc, 0xe7, 0xe1, 0x1e, + 0xfa, 0x15, 0x2e, 0x20, 0x1b, 0x15, 0xf3, 0x31, 0xae, 0x2d, 0xc7, 0xb8, + 0xfb, 0x47, 0xf7, 0xe5, 0xb8, 0x15, 0x0c, 0x0c, 0xe9, 0x00, 0xb0, 0x33, + 0xa3, 0xf5, 0x64, 0xa5, 0x2e, 0x9d, 0xba, 0x3e, 0xec, 0xcd, 0xb7, 0x59, + 0xeb, 0xbc, 0xf0, 0x06, 0x80, 0x17, 0x86, 0x00, 0x80, 0x99, 0xd0, 0x4a, + 0x4a, 0x53, 0xff, 0xee, 0xad, 0x8e, 0x56, 0x73, 0xd8, 0x83, 0x7d, 0x2a, + 0x94, 0x24, 0x31, 0x09, 0xcd, 0xd6, 0x1e, 0x3a, 0x6f, 0xf5, 0xf9, 0x20, + 0x0c, 0x80, 0x4e, 0x00, 0x78, 0x43, 0xe5, 0xa7, 0x74, 0x8d, 0xdf, 0x71, + 0xeb, 0xfb, 0xba, 0xc5, 0xeb, 0x30, 0x0c, 0x40, 0x2f, 0x49, 0x34, 0xb7, + 0x2b, 0x01, 0x8c, 0x37, 0x25, 0x47, 0xa7, 0xa0, 0x91, 0xd2, 0x23, 0x6c, + 0xd8, 0xeb, 0xdb, 0x52, 0x40, 0x61, 0x03, 0x46, 0xb2, 0xd9, 0xa1, 0xfb, + 0x35, 0xfc, 0xbc, 0x07, 0x60, 0x0e, 0xca, 0x21, 0xe2, 0x81, 0x08, 0x1b, + 0x74, 0xa1, 0x6e, 0x95, 0xed, 0xd4, 0x76, 0x34, 0xe3, 0x6b, 0xc0, 0xa0, + 0x03, 0xdc, 0x4c, 0x02, 0x9c, 0x98, 0x90, 0xc2, 0x5f, 0xee, 0xb7, 0x24, + 0x8d, 0xfd, 0xc6, 0x9a, 0x72, 0xaf, 0xa4, 0xd2, 0xdc, 0x02, 0xc2, 0x86, + 0x0c, 0x65, 0x3b, 0x9f, 0xe9, 0x1b, 0xef, 0xe7, 0x00, 0x30, 0x00, 0x72, + 0x1a, 0x4c, 0x2d, 0x3c, 0xb0, 0x94, 0x24, 0xcc, 0xa1, 0x8a, 0x50, 0xc1, + 0x46, 0x35, 0xd6, 0xe0, 0x3b, 0x02, 0x85, 0x16, 0x4b, 0x21, 0x13, 0x3e, + 0xe7, 0xe2, 0x5f, 0x7c, 0x8e, 0xee, 0xad, 0x89, 0xcf, 0xef, 0x63, 0x4b, + 0x20, 0x30, 0x8d, 0xff, 0xd9, 0x9c, 0xf6, 0x36, 0xa7, 0xf7, 0xf3, 0xe0, + 0x0c, 0x00, 0x2f, 0x00, 0x3c, 0x25, 0xe2, 0xf2, 0x4b, 0xc9, 0xe3, 0x45, + 0x8d, 0x37, 0xac, 0x79, 0x12, 0xe8, 0xa0, 0x03, 0x5c, 0x51, 0x34, 0xa2, + 0xd0, 0x4c, 0x29, 0x08, 0x47, 0xfc, 0x95, 0x9d, 0x7f, 0xa1, 0x9f, 0xfe, + 0x7b, 0x1f, 0x87, 0x5e, 0xc2, 0x90, 0x06, 0x10, 0xff, 0xb1, 0x24, 0xf8, + 0x7f, 0x7f, 0x36, 0x01, 0xd8, 0x03, 0xb0, 0x32, 0x5e, 0xe5, 0x15, 0xb6, + 0xc3, 0x06, 0xb9, 0xe5, 0xa3, 0x77, 0x19, 0xc7, 0x67, 0x77, 0x1f, 0x6a, + 0x00, 0xcc, 0x01, 0xf8, 0x01, 0xd9, 0x45, 0x15, 0x90, 0x33, 0x66, 0x4f, + 0x01, 0x5b, 0x32, 0x85, 0x2e, 0xf7, 0xf4, 0xab, 0x13, 0x0a, 0xc3, 0x1b, + 0x12, 0x0e, 0x90, 0xdf, 0x7f, 0x36, 0x01, 0x88, 0x03, 0xb0, 0x0a, 0xc0, + 0xa6, 0x26, 0x62, 0xb1, 0x4e, 0x49, 0x1c, 0xdb, 0xbe, 0xc8, 0xa0, 0x00, + 0x84, 0x10, 0x82, 0x80, 0x2c, 0xc5, 0x6d, 0xc6, 0x12, 0x9c, 0xc1, 0x8c, + 0xdf, 0x89, 0xf7, 0xd2, 0x69, 0x30, 0x0b, 0x0a, 0xdb, 0x66, 0x3b, 0x8d, + 0x34, 0xf8, 0x1b, 0xee, 0x52, 0x94, 0xbc, 0xfb, 0x5b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0x80, 0x00, 0x00, 0x01, 0x12, + 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4b, 0x31, 0x5b, 0x25, 0x3b, 0x7f, 0xf3, 0x8e, 0xce, + 0x45, 0xd7, 0x9c, 0x0d, 0x01, 0x32, 0x03, 0x08, 0x60, 0x16, 0x01, 0x90, + 0x97, 0xd9, 0xfa, 0x19, 0x0b, 0xdb, 0x9f, 0xbf, 0xe6, 0x81, 0x2b, 0x5e, + 0x9d, 0xbb, 0x07, 0xd2, 0xd7, 0xeb, 0x29, 0x63, 0x0d, 0x0c, 0x49, 0x30, + 0xbe, 0x52, 0x73, 0x74, 0x76, 0xf6, 0xcb, 0x33, 0xe7, 0x72, 0x94, 0xa4, + 0xa7, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x5e, 0x5c, 0x06, 0x00, 0x3a, 0x28, + 0x9a, 0x51, 0x34, 0xb4, 0xa5, 0x38, 0x0c, 0x21, 0xdb, 0x6c, 0xb1, 0xcb, + 0xfc, 0x2d, 0x57, 0xa8, 0xb4, 0xdf, 0x7f, 0xf0, 0xea, 0x6e, 0x52, 0x94, + 0xb3, 0xaa, 0xe5, 0x29, 0x79, 0x70, 0x1d, 0x00, 0xe8, 0xa2, 0x69, 0x44, + 0xd2, 0xd2, 0x94, 0xe0, 0x30, 0x87, 0x6d, 0xb2, 0xc7, 0x2f, 0xf0, 0xbb, + 0x4f, 0xf2, 0x33, 0xfa, 0x4f, 0xbf, 0x51, 0x4a, 0x52, 0xce, 0xab, 0x94, + 0xa9, 0x4a, 0x3a, 0x0f, 0xcc, 0xa7, 0x0e, 0xf5, 0xd1, 0x17, 0x29, 0x4a, + 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x94, 0xa4, 0x45, 0xca, 0x52, 0x91, + 0x17, 0x29, 0x4a, 0x44, 0x5c, 0xa5, 0x29, 0x11, 0x72, 0x97, 0x9a, 0x00, + 0x3e, 0x40, 0x01, 0xe7, 0x21, 0x8d, 0xe4, 0x9c, 0x8d, 0x98, 0xfe, 0x69, + 0xbc, 0xc1, 0x16, 0xe0, 0x1b, 0x60, 0x0b, 0x30, 0x69, 0x2d, 0x0e, 0x34, + 0x61, 0x28, 0x0f, 0x89, 0xb6, 0x1b, 0x89, 0xec, 0x1f, 0x4b, 0x5f, 0x5e, + 0x94, 0xa5, 0x8d, 0x57, 0x29, 0x4b, 0x20, 0x61, 0x5c, 0x34, 0xb4, 0x23, + 0x01, 0xff, 0xb4, 0xe2, 0xf5, 0xd2, 0xf2, 0x21, 0x80, 0x26, 0x28, 0x9b, + 0xcb, 0x18, 0x1b, 0xd8, 0x33, 0x7f, 0xd2, 0x4a, 0xdb, 0x36, 0x1c, 0x05, + 0xdd, 0x66, 0x2a, 0xb0, 0x98, 0x03, 0xa0, 0xc2, 0x19, 0xc4, 0xdf, 0x82, + 0x50, 0x53, 0x72, 0x8d, 0x49, 0xc7, 0x27, 0x8e, 0xba, 0x09, 0x79, 0x62, + 0x85, 0x7a, 0x1a, 0xfd, 0x55, 0x2e, 0x50, 0x15, 0x26, 0x62, 0x10, 0x6a, + 0x4a, 0x1a, 0x37, 0xa3, 0xb7, 0xec, 0xcb, 0x14, 0xbf, 0x64, 0x26, 0x81, + 0x44, 0x13, 0x0a, 0x42, 0x49, 0x7d, 0xcb, 0xcf, 0x91, 0xd0, 0xdd, 0x9d, + 0x9f, 0x84, 0xf6, 0x39, 0x47, 0xd9, 0x8d, 0xca, 0x16, 0x74, 0xda, 0xfd, + 0x05, 0x29, 0x4b, 0x2a, 0xae, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, 0x00, + 0x00, 0x01, 0x13, 0x73, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xad, 0xfc, 0xc8, 0x0e, 0x90, 0x34, 0x9a, + 0x50, 0xae, 0xe1, 0x4b, 0xb8, 0x5c, 0xaf, 0xc8, 0x7c, 0xb4, 0x84, 0x20, + 0x6b, 0xbf, 0x22, 0x0f, 0xb7, 0x80, 0xeb, 0xa0, 0xa1, 0xa9, 0xff, 0x6e, + 0x33, 0xe4, 0x09, 0xbd, 0x87, 0xbd, 0x13, 0xd3, 0xc4, 0x35, 0x8d, 0x7f, + 0xf3, 0x07, 0x41, 0x35, 0x25, 0xa7, 0xec, 0xdb, 0x1f, 0xdc, 0x89, 0xee, + 0xea, 0x32, 0x0b, 0x3b, 0x36, 0x46, 0x74, 0xf3, 0x78, 0x1d, 0xb4, 0xe4, + 0xec, 0x4c, 0xf9, 0xd6, 0x94, 0x93, 0xb5, 0x90, 0x84, 0x5e, 0xd8, 0xb4, + 0x7e, 0x30, 0x6e, 0xcd, 0xc5, 0xeb, 0xa1, 0x98, 0x89, 0x45, 0xb8, 0xcc, + 0x4a, 0xfd, 0x38, 0x60, 0x12, 0x1e, 0x06, 0xaa, 0xba, 0x10, 0x8c, 0x8f, + 0xf0, 0xd4, 0x7c, 0x79, 0xaf, 0x76, 0xc2, 0x1f, 0x3f, 0x75, 0xaf, 0x90, + 0x2e, 0x08, 0xd4, 0x21, 0x05, 0xa1, 0xc6, 0xee, 0x71, 0x06, 0xca, 0xaa, + 0xdf, 0x6a, 0x0a, 0xc0, 0x60, 0x97, 0xb0, 0xd6, 0xfc, 0x97, 0xb9, 0x98, + 0xc0, 0x37, 0x7a, 0x42, 0xc6, 0x74, 0xf1, 0x9b, 0xec, 0x66, 0xf7, 0x88, + 0x28, 0xb0, 0xc2, 0x62, 0x72, 0x50, 0x97, 0x47, 0x40, 0x4a, 0x39, 0xee, + 0xc7, 0x01, 0xdb, 0xe8, 0x1e, 0xc3, 0x1f, 0xea, 0xaf, 0xfe, 0x7e, 0x8c, + 0xc4, 0x2f, 0xf8, 0x1f, 0xe4, 0xec, 0x38, 0x3e, 0xd6, 0x33, 0x24, 0x98, + 0x37, 0x6d, 0xb8, 0xd6, 0x53, 0xac, 0x2f, 0xd4, 0x27, 0xa1, 0x21, 0x85, + 0x23, 0x76, 0x18, 0x7b, 0x8b, 0x97, 0x06, 0xa3, 0x24, 0xac, 0x07, 0x90, + 0x4f, 0xe3, 0xda, 0xec, 0x59, 0x3e, 0xce, 0xf7, 0x96, 0x8c, 0x19, 0xc6, + 0x6c, 0x35, 0x26, 0x89, 0x1d, 0xae, 0x70, 0x68, 0x66, 0x03, 0x38, 0xa4, + 0x36, 0x52, 0xc8, 0xff, 0xda, 0xb7, 0x42, 0x03, 0x77, 0x1a, 0xeb, 0x3c, + 0x70, 0x59, 0xf7, 0x3f, 0x06, 0xa3, 0x13, 0x7f, 0x77, 0xdd, 0xdf, 0xb0, + 0xf9, 0xac, 0x9c, 0x4f, 0x8b, 0xf4, 0x19, 0x25, 0x6f, 0xf0, 0xc7, 0xc6, + 0x0f, 0x12, 0x7d, 0xe6, 0x83, 0x0a, 0xfc, 0x85, 0xc0, 0x7a, 0x95, 0x88, + 0xcb, 0x70, 0xb5, 0x5e, 0xcd, 0x39, 0x08, 0x3b, 0x8d, 0xbc, 0x40, 0x0d, + 0xf1, 0x40, 0x63, 0x65, 0xed, 0x82, 0x43, 0xcf, 0xa3, 0x8a, 0x01, 0xf4, + 0xef, 0xda, 0xa5, 0x25, 0x33, 0x21, 0x01, 0xff, 0xac, 0x81, 0x02, 0x66, + 0x1a, 0x5e, 0xc3, 0xfc, 0x4b, 0xc5, 0x23, 0x84, 0x71, 0xa2, 0xee, 0x58, + 0x14, 0x4e, 0x42, 0x7f, 0xfd, 0x95, 0xd8, 0x4d, 0xcc, 0x65, 0x77, 0xcd, + 0x04, 0x3f, 0x90, 0xde, 0x73, 0x1e, 0xe2, 0xb5, 0x68, 0x62, 0x10, 0x4d, + 0x4f, 0x46, 0xcd, 0x9b, 0xac, 0xd1, 0x35, 0xa1, 0x85, 0xa1, 0x1d, 0x38, + 0xc4, 0xa3, 0xa0, 0x69, 0x15, 0xa7, 0x42, 0x50, 0x18, 0x84, 0x9a, 0x94, + 0xa8, 0x46, 0xba, 0x96, 0x46, 0x6b, 0x3a, 0xc3, 0xda, 0xde, 0x19, 0x08, + 0x0c, 0x40, 0x0c, 0x30, 0x6a, 0x4a, 0x43, 0xf2, 0xdb, 0x12, 0x5d, 0x3b, + 0x61, 0xef, 0xf6, 0x16, 0x4e, 0xbd, 0xd5, 0xe4, 0xc0, 0x62, 0x1a, 0x84, + 0xa0, 0x84, 0x52, 0x3b, 0xe2, 0x8a, 0x58, 0x0a, 0xf9, 0xf7, 0x12, 0x5f, + 0xb6, 0x16, 0xbb, 0x5d, 0x5f, 0xe3, 0x19, 0xbe, 0xe5, 0xe4, 0x1f, 0xd9, + 0x08, 0x23, 0xd1, 0x28, 0x46, 0x25, 0x6e, 0xaf, 0xc9, 0x2c, 0x66, 0x20, + 0xd9, 0x72, 0x72, 0x49, 0x99, 0x03, 0x46, 0x74, 0x7e, 0x3d, 0x7b, 0x88, + 0xbb, 0xc8, 0xc5, 0x7e, 0xdf, 0xfc, 0xa5, 0x71, 0x63, 0xfd, 0xd6, 0x16, + 0xab, 0x3b, 0xca, 0x5e, 0xc5, 0x3a, 0x12, 0x86, 0xe6, 0x30, 0xe2, 0x2c, + 0x03, 0x72, 0x32, 0x0a, 0x53, 0x7f, 0x9f, 0x91, 0x0f, 0x90, 0xb2, 0xf7, + 0x41, 0x48, 0xeb, 0xf8, 0xd1, 0x43, 0xae, 0x68, 0x6a, 0x4a, 0xe5, 0x8d, + 0x1a, 0xe7, 0x32, 0x8e, 0x85, 0xa8, 0x3e, 0x2f, 0xa2, 0x43, 0x46, 0x28, + 0x66, 0x65, 0xb3, 0xfb, 0x0a, 0x50, 0x8c, 0x18, 0x57, 0x66, 0xfd, 0x64, + 0x03, 0x42, 0xed, 0xc4, 0x34, 0xa0, 0xb2, 0xdf, 0x77, 0x16, 0x2a, 0x62, + 0x6a, 0x50, 0x92, 0xf3, 0x70, 0x8c, 0xda, 0x79, 0xd4, 0x17, 0xe2, 0xf9, + 0xa1, 0xa8, 0x42, 0x32, 0x3b, 0x33, 0xa0, 0xe7, 0x34, 0x8f, 0x10, 0xd0, + 0xcd, 0xd2, 0x52, 0x11, 0x9b, 0xfd, 0x9c, 0x73, 0xdb, 0x8b, 0x2f, 0x16, + 0x4d, 0x70, 0x3d, 0x94, 0x1d, 0xae, 0x20, 0x19, 0x42, 0x08, 0x63, 0x5d, + 0xf2, 0xed, 0x16, 0x4e, 0xf4, 0xd6, 0x1b, 0x0a, 0x26, 0x06, 0x0d, 0x4f, + 0xe8, 0x62, 0x12, 0x1f, 0x72, 0xfb, 0x8d, 0x4a, 0x94, 0xc2, 0xdd, 0xba, + 0xb3, 0x5f, 0x42, 0xbc, 0xd1, 0x34, 0x94, 0x8f, 0xc0, 0x52, 0x03, 0x7e, + 0xb6, 0x3c, 0xd3, 0x7f, 0xe7, 0xf1, 0x43, 0xef, 0x47, 0x8f, 0x5d, 0xe7, + 0xa0, 0x7a, 0xed, 0xbd, 0x7f, 0xe1, 0xcb, 0xe3, 0x03, 0x03, 0x10, 0x02, + 0x3f, 0x69, 0x0d, 0xc8, 0x42, 0x0a, 0x6c, 0xdf, 0xe7, 0xe1, 0xe7, 0x59, + 0x88, 0x69, 0x29, 0x29, 0xcd, 0xdf, 0x2b, 0x08, 0xd3, 0x90, 0xd2, 0x84, + 0x96, 0xfb, 0xb9, 0x19, 0xae, 0xcc, 0x61, 0xe6, 0xfb, 0x2b, 0xb8, 0x9b, + 0xb6, 0xc5, 0x6d, 0xb6, 0xec, 0xfc, 0xfb, 0x9b, 0xc3, 0x10, 0xe2, 0x90, + 0x8c, 0x34, 0x48, 0xea, 0xe2, 0x1a, 0x51, 0xcb, 0x1b, 0xbb, 0x9c, 0xd8, + 0x74, 0xc4, 0xc2, 0xf0, 0xc2, 0xd1, 0xd2, 0x33, 0xa5, 0x97, 0x52, 0xdb, + 0x45, 0x69, 0xa5, 0x25, 0x23, 0x13, 0x3f, 0x2d, 0x19, 0x09, 0xff, 0x12, + 0xb6, 0x3c, 0x5b, 0x9e, 0xa6, 0x1f, 0x7b, 0x17, 0x6b, 0x9c, 0x58, 0xcc, + 0xad, 0xc6, 0x80, 0xf4, 0xe0, 0xec, 0x41, 0xba, 0x37, 0x9b, 0xad, 0xaf, + 0xc0, 0x93, 0x00, 0xb0, 0x0c, 0x08, 0x69, 0x02, 0x7f, 0x23, 0x11, 0x49, + 0xd7, 0xad, 0x26, 0x24, 0x61, 0x68, 0xe3, 0x93, 0xb8, 0x57, 0xbc, 0xa8, + 0x6f, 0x46, 0x02, 0x85, 0x14, 0x82, 0xd1, 0xfb, 0xf4, 0xa7, 0x0b, 0x22, + 0xde, 0xef, 0x2a, 0xe9, 0x42, 0xc6, 0x8b, 0xfc, 0x81, 0x47, 0x32, 0xb0, + 0xf0, 0x00, 0x3b, 0x26, 0x20, 0x00, 0xe4, 0x34, 0x6f, 0x08, 0xc0, 0x5d, + 0x22, 0x3e, 0xdd, 0x82, 0xfb, 0x5e, 0xc4, 0x9a, 0x1b, 0xc9, 0xa5, 0xf4, + 0xf3, 0x49, 0xc2, 0xaf, 0x1a, 0x01, 0x98, 0x15, 0x28, 0x37, 0xb1, 0x4d, + 0xc6, 0xe4, 0x72, 0xd2, 0x6f, 0xfb, 0x33, 0x08, 0x22, 0xdf, 0x42, 0x90, + 0x63, 0x8c, 0xdf, 0x8e, 0x69, 0xfb, 0x94, 0xa5, 0x2e, 0x0b, 0x5b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x88, 0x00, 0x00, 0x01, 0x14, 0x83, 0xfb, 0xfd, + 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0x00, 0x00, 0x01, 0x15, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x16, 0x83, + 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0x00, 0x00, 0x01, 0x17, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, + 0x18, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x19, 0x83, 0xfb, 0xfd, 0x29, + 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, + 0x00, 0x01, 0x1a, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1b, 0x83, 0xfb, + 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0x00, 0x00, 0x01, 0x1c, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1d, + 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x1e, 0x83, 0xfb, 0xfd, 0x29, 0x49, + 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, + 0x01, 0x1f, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x20, 0x83, 0xfb, 0xfd, + 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0x00, 0x00, 0x01, 0x21, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0x22, 0x83, + 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, + 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, + 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, + 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, + 0x52, 0x22, 0x00, 0x00, 0x01, 0x23, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, + 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, + 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, + 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, + 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, + 0x24, 0x83, 0xfb, 0xfd, 0x29, 0x49, 0x4a, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, + 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, + 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, 0x22, 0xe5, 0x29, + 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, 0xb9, 0x4a, 0x52, + 0x22, 0xe5, 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x2e, 0x52, 0x94, 0x88, + 0xb9, 0x4a, 0x52, 0x22, 0x00, 0x00, 0x01, 0xb7, +}; diff --git a/plugins/streamdev/streamdev-cvs/server/suspend.h b/plugins/streamdev/streamdev-cvs/server/suspend.h new file mode 100644 index 0000000..bea25ee --- /dev/null +++ b/plugins/streamdev/streamdev-cvs/server/suspend.h @@ -0,0 +1,36 @@ +/* + * $Id: suspend.h,v 1.2 2008/10/22 11:59:32 schmirl Exp $ + */ + +#ifndef VDR_STREAMDEV_SUSPEND_H +#define VDR_STREAMDEV_SUSPEND_H + +#include + +class cSuspendLive: public cPlayer, public cThread { +protected: + virtual void Activate(bool On); + virtual void Action(void); + + void Stop(void); + +public: + cSuspendLive(void); + virtual ~cSuspendLive(); +}; + +class cSuspendCtl: public cControl { +private: + cSuspendLive *m_Suspend; + static bool m_Active; + +public: + cSuspendCtl(void); + virtual ~cSuspendCtl(); + virtual void Hide(void) {} + virtual eOSState ProcessKey(eKeys Key); + + static bool IsActive(void) { return m_Active; } +}; + +#endif // VDR_STREAMDEV_SUSPEND_H -- cgit v1.2.3