Compare commits

...

30 Commits
v5.54 ... v5.57

Author SHA1 Message Date
Maxim Devaev
8f0acb2176 Bump version: 5.56 → 5.57 2024-03-01 04:33:13 +02:00
Maxim Devaev
8edeff8160 fixed makefile 2024-03-01 04:21:45 +02:00
Maxim Devaev
cacec0d25c refactoring 2024-03-01 04:09:16 +02:00
Maxim Devaev
c54d7da19f Bump version: 5.55 → 5.56 2024-03-01 03:48:15 +02:00
Maxim Devaev
df610e1045 improved makefiles 2024-03-01 03:47:03 +02:00
Maxim Devaev
8d4e6a13b0 server: fixed zero frame behaviour 2024-03-01 03:20:43 +02:00
Maxim Devaev
c852e5f827 lint fixes 2024-03-01 00:43:57 +02:00
Maxim Devaev
2a37f1650b using text generator for blank images 2024-03-01 00:39:19 +02:00
Maxim Devaev
15f160c874 refactoring 2024-02-29 22:40:51 +02:00
Maxim Devaev
df63ad4678 refactoring 2024-02-29 22:40:51 +02:00
Markus Küffner
72cd61cccf docker: build images for armv6 (#262) 2024-02-29 22:13:38 +02:00
Maxim Devaev
3e5a444023 refactoring 2024-02-29 19:17:41 +02:00
Maxim Devaev
a3f4294cd8 refactoring 2024-02-29 18:21:26 +02:00
Maxim Devaev
d9401b70f5 commented unused us_queue_get_free() 2024-02-29 08:07:47 +02:00
Maxim Devaev
4296d5dada janus: using ring for audio pipeline 2024-02-29 08:06:48 +02:00
Maxim Devaev
12937b93d5 janus: using ring buffers for rtp pipelines 2024-02-29 06:57:16 +02:00
Maxim Devaev
28cd5e5b87 janus: reducing memory allocating using ring buffer 2024-02-29 06:27:11 +02:00
Maxim Devaev
7bacef7622 minor fix 2024-02-29 03:29:23 +02:00
Maxim Devaev
ca3638313b moved ftext to libs as frametext 2024-02-29 03:10:48 +02:00
Maxim Devaev
bb3e4ec2c7 moved queue from janus to common libs 2024-02-29 02:48:52 +02:00
Maxim Devaev
d52ac784f6 device: don't expose dma for jpeg 2024-02-29 02:42:44 +02:00
Maxim Devaev
f5a9214dd4 simplified 2024-02-29 01:23:41 +02:00
Maxim Devaev
5b54a8dbe2 v4p: fixed buf error handling 2024-02-29 00:52:03 +02:00
Maxim Devaev
32165e97ed simplified 2024-02-29 00:42:34 +02:00
Maxim Devaev
4b6f65881d simplified 2024-02-28 23:25:06 +02:00
Maxim Devaev
0e7d8da407 Bump version: 5.54 → 5.55 2024-02-28 00:14:42 +02:00
Maxim Devaev
1c862b21e9 v4p: better messages 2024-02-28 00:13:37 +02:00
Maxim Devaev
b202438a4d v4p: ignore ENOENT (unplugged) on restoring saved_crtc 2024-02-27 23:59:02 +02:00
Maxim Devaev
50ee2ba964 v4p: simplified 2024-02-27 23:50:27 +02:00
Maxim Devaev
9f40713ee1 v4p: check display status via sysfs 2024-02-27 07:55:33 +02:00
57 changed files with 906 additions and 1653 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 5.54
current_version = 5.57
parse = (?P<major>\d+)\.(?P<minor>\d+)
serialize =
{major}.{minor}

View File

@@ -66,7 +66,7 @@ jobs:
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/arm64,linux/amd64,linux/arm/v7
platforms: linux/arm64,linux/amd64,linux/arm/v6,linux/arm/v7
file: pkg/docker/Dockerfile.alpine
-
name: Push

View File

@@ -36,7 +36,9 @@ endif
apps:
$(MAKE) -C src
$(ECHO) ln -sf src/*.bin .
for i in src/*.bin; do \
test ! -x $$i || ln -sf $$i `basename $$i .bin`; \
done
python:
@@ -69,9 +71,8 @@ install-strip: install
regen:
tools/$(MAKE)-jpeg-h.py src/ustreamer/data/blank.jpeg src/ustreamer/data/blank_jpeg.c BLANK
tools/$(MAKE)-ico-h.py src/ustreamer/data/favicon.ico src/ustreamer/data/favicon_ico.c FAVICON
tools/$(MAKE)-html-h.py src/ustreamer/data/index.html src/ustreamer/data/index_html.c INDEX
tools/make-ico-h.py src/ustreamer/data/favicon.ico src/ustreamer/data/favicon_ico.c FAVICON
tools/make-html-h.py src/ustreamer/data/index.html src/ustreamer/data/index_html.c INDEX
release:
@@ -118,7 +119,7 @@ clean-all: linters clean
clean:
rm -rf pkg/arch/pkg pkg/arch/src pkg/arch/v*.tar.gz pkg/arch/ustreamer-*.pkg.tar.{xz,zst}
rm -f *.bin *.so
rm -f ustreamer ustreamer-* *.so
$(MAKE) -C src clean
$(MAKE) -C python clean
$(MAKE) -C janus clean

View File

@@ -51,6 +51,9 @@ typedef struct {
} _enc_buffer_s;
static _pcm_buffer_s *_pcm_buffer_init(void);
static _enc_buffer_s *_enc_buffer_init(void);
static void *_pcm_thread(void *v_audio);
static void *_encoder_thread(void *v_audio);
@@ -72,8 +75,8 @@ us_audio_s *us_audio_init(const char *name, unsigned pcm_hz) {
us_audio_s *audio;
US_CALLOC(audio, 1);
audio->pcm_hz = pcm_hz;
audio->pcm_queue = us_queue_init(8);
audio->enc_queue = us_queue_init(8);
US_RING_INIT_WITH_ITEMS(audio->pcm_ring, 8, _pcm_buffer_init);
US_RING_INIT_WITH_ITEMS(audio->enc_ring, 8, _enc_buffer_init);
atomic_init(&audio->stop, false);
int err;
@@ -151,8 +154,8 @@ void us_audio_destroy(us_audio_s *audio) {
US_DELETE(audio->res, speex_resampler_destroy);
US_DELETE(audio->pcm, snd_pcm_close);
US_DELETE(audio->pcm_params, snd_pcm_hw_params_free);
US_QUEUE_DELETE_WITH_ITEMS(audio->enc_queue, free);
US_QUEUE_DELETE_WITH_ITEMS(audio->pcm_queue, free);
US_RING_DELETE_WITH_ITEMS(audio->enc_ring, free);
US_RING_DELETE_WITH_ITEMS(audio->pcm_ring, free);
if (audio->tids_created) {
US_JLOG_INFO("audio", "Pipeline closed");
}
@@ -163,19 +166,32 @@ int us_audio_get_encoded(us_audio_s *audio, uint8_t *data, size_t *size, uint64_
if (atomic_load(&audio->stop)) {
return -1;
}
_enc_buffer_s *buf;
if (!us_queue_get(audio->enc_queue, (void **)&buf, 0.1)) {
if (*size < buf->used) {
free(buf);
return -3;
}
memcpy(data, buf->data, buf->used);
*size = buf->used;
*pts = buf->pts;
free(buf);
return 0;
const int ri = us_ring_consumer_acquire(audio->enc_ring, 0.1);
if (ri < 0) {
return -2;
}
return -2;
const _enc_buffer_s *const buf = audio->enc_ring->items[ri];
if (*size < buf->used) {
us_ring_consumer_release(audio->enc_ring, ri);
return -3;
}
memcpy(data, buf->data, buf->used);
*size = buf->used;
*pts = buf->pts;
us_ring_consumer_release(audio->enc_ring, ri);
return 0;
}
static _pcm_buffer_s *_pcm_buffer_init(void) {
_pcm_buffer_s *buf;
US_CALLOC(buf, 1);
return buf;
}
static _enc_buffer_s *_enc_buffer_init(void) {
_enc_buffer_s *buf;
US_CALLOC(buf, 1);
return buf;
}
static void *_pcm_thread(void *v_audio) {
@@ -194,13 +210,13 @@ static void *_pcm_thread(void *v_audio) {
break;
}
if (us_queue_get_free(audio->pcm_queue)) {
_pcm_buffer_s *out;
US_CALLOC(out, 1);
const int ri = us_ring_producer_acquire(audio->pcm_ring, 0);
if (ri >= 0) {
_pcm_buffer_s *const out = audio->pcm_ring->items[ri];
memcpy(out->data, in, audio->pcm_size);
assert(!us_queue_put(audio->pcm_queue, out, 0));
us_ring_producer_release(audio->pcm_ring, ri);
} else {
US_JLOG_ERROR("audio", "PCM queue is full");
US_JLOG_ERROR("audio", "PCM ring is full");
}
}
@@ -215,39 +231,44 @@ static void *_encoder_thread(void *v_audio) {
int16_t in_res[_MAX_BUF16];
while (!atomic_load(&audio->stop)) {
_pcm_buffer_s *in;
if (!us_queue_get(audio->pcm_queue, (void **)&in, 0.1)) {
int16_t *in_ptr;
if (audio->res != NULL) {
assert(audio->pcm_hz != _ENCODER_INPUT_HZ);
uint32_t in_count = audio->pcm_frames;
uint32_t out_count = _HZ_TO_FRAMES(_ENCODER_INPUT_HZ);
speex_resampler_process_interleaved_int(audio->res, in->data, &in_count, in_res, &out_count);
in_ptr = in_res;
} else {
assert(audio->pcm_hz == _ENCODER_INPUT_HZ);
in_ptr = in->data;
}
const int in_ri = us_ring_consumer_acquire(audio->pcm_ring, 0.1);
if (in_ri < 0) {
continue;
}
_pcm_buffer_s *const in = audio->pcm_ring->items[in_ri];
_enc_buffer_s *out;
US_CALLOC(out, 1);
const int size = opus_encode(audio->enc, in_ptr, _HZ_TO_FRAMES(_ENCODER_INPUT_HZ), out->data, US_ARRAY_LEN(out->data));
free(in);
if (size < 0) {
_JLOG_PERROR_OPUS(size, "audio", "Fatal: Can't encode PCM frame to OPUS");
free(out);
break;
}
int16_t *in_ptr;
if (audio->res != NULL) {
assert(audio->pcm_hz != _ENCODER_INPUT_HZ);
uint32_t in_count = audio->pcm_frames;
uint32_t out_count = _HZ_TO_FRAMES(_ENCODER_INPUT_HZ);
speex_resampler_process_interleaved_int(audio->res, in->data, &in_count, in_res, &out_count);
in_ptr = in_res;
} else {
assert(audio->pcm_hz == _ENCODER_INPUT_HZ);
in_ptr = in->data;
}
const int out_ri = us_ring_producer_acquire(audio->enc_ring, 0);
if (out_ri < 0) {
US_JLOG_ERROR("audio", "OPUS encoder queue is full");
us_ring_consumer_release(audio->pcm_ring, in_ri);
continue;
}
_enc_buffer_s *const out = audio->enc_ring->items[out_ri];
const int size = opus_encode(audio->enc, in_ptr, _HZ_TO_FRAMES(_ENCODER_INPUT_HZ), out->data, US_ARRAY_LEN(out->data));
us_ring_consumer_release(audio->pcm_ring, in_ri);
if (size >= 0) {
out->used = size;
out->pts = audio->pts;
// https://datatracker.ietf.org/doc/html/rfc7587#section-4.2
audio->pts += _HZ_TO_FRAMES(_ENCODER_INPUT_HZ);
if (us_queue_put(audio->enc_queue, out, 0) != 0) {
US_JLOG_ERROR("audio", "OPUS encoder queue is full");
free(out);
}
} else {
_JLOG_PERROR_OPUS(size, "audio", "Fatal: Can't encode PCM frame to OPUS");
}
us_ring_producer_release(audio->enc_ring, out_ri);
}
atomic_store(&audio->stop, true);

View File

@@ -37,10 +37,10 @@
#include "uslibs/tools.h"
#include "uslibs/array.h"
#include "uslibs/ring.h"
#include "uslibs/threading.h"
#include "logging.h"
#include "queue.h"
typedef struct {
@@ -52,8 +52,8 @@ typedef struct {
SpeexResamplerState *res;
OpusEncoder *enc;
us_queue_s *pcm_queue;
us_queue_s *enc_queue;
us_ring_s *pcm_ring;
us_ring_s *enc_ring;
uint32_t pts;
pthread_t pcm_tid;

View File

@@ -33,9 +33,9 @@
#include "uslibs/tools.h"
#include "uslibs/threading.h"
#include "uslibs/list.h"
#include "uslibs/ring.h"
#include "logging.h"
#include "queue.h"
#include "rtp.h"
@@ -54,10 +54,10 @@ us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_sessio
atomic_init(&client->stop, false);
client->video_queue = us_queue_init(2048);
US_RING_INIT_WITH_ITEMS(client->video_ring, 2048, us_rtp_init);
US_THREAD_CREATE(client->video_tid, _video_thread, client);
client->audio_queue = us_queue_init(64);
US_RING_INIT_WITH_ITEMS(client->audio_ring, 64, us_rtp_init);
US_THREAD_CREATE(client->audio_tid, _audio_thread, client);
return client;
@@ -65,14 +65,12 @@ us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_sessio
void us_janus_client_destroy(us_janus_client_s *client) {
atomic_store(&client->stop, true);
us_queue_put(client->video_queue, NULL, 0);
us_queue_put(client->audio_queue, NULL, 0);
US_THREAD_JOIN(client->video_tid);
US_QUEUE_DELETE_WITH_ITEMS(client->video_queue, us_rtp_destroy);
US_RING_DELETE_WITH_ITEMS(client->video_ring, us_rtp_destroy);
US_THREAD_JOIN(client->audio_tid);
US_QUEUE_DELETE_WITH_ITEMS(client->audio_queue, us_rtp_destroy);
US_RING_DELETE_WITH_ITEMS(client->audio_ring, us_rtp_destroy);
free(client);
}
@@ -82,12 +80,15 @@ void us_janus_client_send(us_janus_client_s *client, const us_rtp_s *rtp) {
atomic_load(&client->transmit)
&& (rtp->video || atomic_load(&client->transmit_audio))
) {
us_rtp_s *const new = us_rtp_dup(rtp);
if (us_queue_put((new->video ? client->video_queue : client->audio_queue), new, 0) != 0) {
US_JLOG_ERROR("client", "Session %p %s queue is full",
client->session, (new->video ? "video" : "audio"));
us_rtp_destroy(new);
us_ring_s *const ring = (rtp->video ? client->video_ring : client->audio_ring);
const int ri = us_ring_producer_acquire(ring, 0);
if (ri < 0) {
US_JLOG_ERROR("client", "Session %p %s ring is full",
client->session, (rtp->video ? "video" : "audio"));
return;
}
memcpy(ring->items[ri], rtp, sizeof(us_rtp_s));
us_ring_producer_release(ring, ri);
}
}
@@ -101,45 +102,45 @@ static void *_audio_thread(void *v_client) {
static void *_common_thread(void *v_client, bool video) {
us_janus_client_s *const client = (us_janus_client_s *)v_client;
us_queue_s *const queue = (video ? client->video_queue : client->audio_queue);
assert(queue != NULL); // Audio may be NULL
us_ring_s *const ring = (video ? client->video_ring : client->audio_ring);
assert(ring != NULL); // Audio may be NULL
while (!atomic_load(&client->stop)) {
us_rtp_s *rtp;
if (!us_queue_get(queue, (void **)&rtp, 0.1)) {
if (rtp == NULL) {
break;
}
const int ri = us_ring_consumer_acquire(ring, 0.1);
if (ri < 0) {
continue;
}
us_rtp_s rtp;
memcpy(&rtp, ring->items[ri], sizeof(us_rtp_s));
us_ring_consumer_release(ring, ri);
if (
atomic_load(&client->transmit)
&& (video || atomic_load(&client->transmit_audio))
) {
janus_plugin_rtp packet = {
.video = rtp->video,
.buffer = (char *)rtp->datagram,
.length = rtp->used,
# if JANUS_PLUGIN_API_VERSION >= 100
// The uStreamer Janus plugin places video in stream index 0 and audio
// (if available) in stream index 1.
.mindex = (rtp->video ? 0 : 1),
# endif
};
janus_plugin_rtp_extensions_reset(&packet.extensions);
/*if (rtp->zero_playout_delay) {
// https://github.com/pikvm/pikvm/issues/784
packet.extensions.min_delay = 0;
packet.extensions.max_delay = 0;
} else {
packet.extensions.min_delay = 0;
// 10s - Chromium/WebRTC default
// 3s - Firefox default
packet.extensions.max_delay = 300; // == 3s, i.e. 10ms granularity
}*/
if (
atomic_load(&client->transmit)
&& (video || atomic_load(&client->transmit_audio))
) {
janus_plugin_rtp packet = {
.video = rtp.video,
.buffer = (char*)rtp.datagram,
.length = rtp.used,
# if JANUS_PLUGIN_API_VERSION >= 100
// The uStreamer Janus plugin places video in stream index 0 and audio
// (if available) in stream index 1.
.mindex = (rtp.video ? 0 : 1),
# endif
};
janus_plugin_rtp_extensions_reset(&packet.extensions);
/*if (rtp->zero_playout_delay) {
// https://github.com/pikvm/pikvm/issues/784
packet.extensions.min_delay = 0;
packet.extensions.max_delay = 0;
} else {
packet.extensions.min_delay = 0;
// 10s - Chromium/WebRTC default
// 3s - Firefox default
packet.extensions.max_delay = 300; // == 3s, i.e. 10ms granularity
}*/
client->gw->relay_rtp(client->session, &packet);
}
us_rtp_destroy(rtp);
client->gw->relay_rtp(client->session, &packet);
}
}
return NULL;

View File

@@ -29,8 +29,8 @@
#include "uslibs/types.h"
#include "uslibs/list.h"
#include "uslibs/ring.h"
#include "queue.h"
#include "rtp.h"
@@ -44,8 +44,8 @@ typedef struct us_janus_client_sx {
pthread_t audio_tid;
atomic_bool stop;
us_queue_s *video_queue;
us_queue_s *audio_queue;
us_ring_s *video_ring;
us_ring_s *audio_ring;
US_LIST_STRUCT(struct us_janus_client_sx);
} us_janus_client_s;

View File

@@ -34,7 +34,7 @@
#include "logging.h"
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s* mem, u64 last_id) {
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s *mem, u64 last_id) {
const ldf deadline_ts = us_get_now_monotonic() + 1; // wait_timeout
ldf now_ts;
do {
@@ -57,8 +57,7 @@ int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s* mem, u64 last_id) {
return -2;
}
us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, u64 *frame_id, bool key_required) {
us_frame_s *frame = us_frame_init();
int us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, us_frame_s *frame, u64 *frame_id, bool key_required) {
us_frame_set_data(frame, mem->data, mem->used);
US_FRAME_COPY_META(mem, frame);
*frame_id = mem->id;
@@ -67,17 +66,14 @@ us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, u64 *frame
mem->key_requested = true;
}
bool ok = true;
bool retval = 0;
if (frame->format != V4L2_PIX_FMT_H264) {
US_JLOG_ERROR("video", "Got non-H264 frame from memsink");
ok = false;
retval = -1;
}
if (flock(fd, LOCK_UN) < 0) {
US_JLOG_PERROR("video", "Can't unlock memsink");
ok = false;
retval = -1;
}
if (!ok) {
US_DELETE(frame, us_frame_destroy);
}
return frame;
return retval;
}

View File

@@ -27,5 +27,5 @@
#include "uslibs/memsinksh.h"
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s* mem, u64 last_id);
us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, u64 *frame_id, bool key_required);
int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s *mem, u64 last_id);
int us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, us_frame_s *frame, u64 *frame_id, bool key_required);

View File

@@ -40,11 +40,11 @@
#include "uslibs/tools.h"
#include "uslibs/threading.h"
#include "uslibs/list.h"
#include "uslibs/ring.h"
#include "uslibs/memsinksh.h"
#include "const.h"
#include "logging.h"
#include "queue.h"
#include "client.h"
#include "audio.h"
#include "tc358743.h"
@@ -60,7 +60,7 @@ static const useconds_t _g_watchers_polling = 100000;
static us_janus_client_s *_g_clients = NULL;
static janus_callbacks *_g_gw = NULL;
static us_queue_s *_g_video_queue = NULL;
static us_ring_s *_g_video_ring = NULL;
static us_rtpv_s *_g_rtpv = NULL;
static us_rtpa_s *_g_rtpa = NULL;
@@ -105,13 +105,14 @@ static void *_video_rtp_thread(void *arg) {
atomic_store(&_g_video_rtp_tid_created, true);
while (!_STOP) {
us_frame_s *frame;
if (us_queue_get(_g_video_queue, (void **)&frame, 0.1) == 0) {
const int ri = us_ring_consumer_acquire(_g_video_ring, 0.1);
if (ri >= 0) {
const us_frame_s *const frame = _g_video_ring->items[ri];
_LOCK_VIDEO;
const bool zero_playout_delay = (frame->gop == 0);
us_rtpv_wrap(_g_rtpv, frame, zero_playout_delay);
_UNLOCK_VIDEO;
us_frame_destroy(frame);
us_ring_consumer_release(_g_video_ring, ri);
}
}
return NULL;
@@ -123,6 +124,7 @@ static void *_video_sink_thread(void *arg) {
US_THREAD_RENAME("us_video_sink");
atomic_store(&_g_video_sink_tid_created, true);
us_frame_s *drop = us_frame_init();
u64 frame_id = 0;
int once = 0;
@@ -150,30 +152,40 @@ static void *_video_sink_thread(void *arg) {
US_JLOG_INFO("video", "Memsink opened; reading frames ...");
while (!_STOP && _HAS_WATCHERS) {
const int result = us_memsink_fd_wait_frame(fd, mem, frame_id);
if (result == 0) {
us_frame_s *const frame = us_memsink_fd_get_frame(fd, mem, &frame_id, atomic_load(&_g_key_required));
if (frame == NULL) {
const int waited = us_memsink_fd_wait_frame(fd, mem, frame_id);
if (waited == 0) {
const int ri = us_ring_producer_acquire(_g_video_ring, 0);
us_frame_s *frame;
if (ri >= 0) {
frame = _g_video_ring->items[ri];
} else {
US_ONCE({ US_JLOG_PERROR("video", "Video ring is full"); });
frame = drop;
}
const int got = us_memsink_fd_get_frame(fd, mem, frame, &frame_id, atomic_load(&_g_key_required));
if (ri >= 0) {
us_ring_producer_release(_g_video_ring, ri);
}
if (got < 0) {
goto close_memsink;
}
if (frame->key) {
if (ri >= 0 && frame->key) {
atomic_store(&_g_key_required, false);
}
if (us_queue_put(_g_video_queue, frame, 0) != 0) {
US_ONCE({ US_JLOG_PERROR("video", "Video queue is full"); });
us_frame_destroy(frame);
}
} else if (result == -1) {
} else if (waited != -2) {
goto close_memsink;
}
}
close_memsink:
US_DELETE(mem, us_memsink_shared_unmap);
US_CLOSE_FD(fd, close);
US_CLOSE_FD(fd);
US_JLOG_INFO("video", "Memsink closed");
sleep(1); // error_delay
}
us_frame_destroy(drop);
return NULL;
}
@@ -258,7 +270,7 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
}
_g_gw = gw;
_g_video_queue = us_queue_init(1024);
US_RING_INIT_WITH_ITEMS(_g_video_ring, 64, us_frame_init);
_g_rtpv = us_rtpv_init(_relay_rtp_clients);
if (_g_config->audio_dev_name != NULL && us_audio_probe(_g_config->audio_dev_name)) {
_g_rtpa = us_rtpa_init(_relay_rtp_clients);
@@ -286,7 +298,7 @@ static void _plugin_destroy(void) {
us_janus_client_destroy(client);
});
US_QUEUE_DELETE_WITH_ITEMS(_g_video_queue, us_frame_destroy);
US_RING_DELETE_WITH_ITEMS(_g_video_ring, us_frame_destroy);
US_DELETE(_g_rtpa, us_rtpa_destroy);
US_DELETE(_g_rtpv, us_rtpv_destroy);

View File

@@ -31,26 +31,22 @@
#include "uslibs/tools.h"
us_rtp_s *us_rtp_init(uint payload, bool video) {
us_rtp_s *us_rtp_init(void) {
us_rtp_s *rtp;
US_CALLOC(rtp, 1);
rtp->payload = payload;
rtp->video = video;
rtp->ssrc = us_triple_u32(us_get_now_monotonic_u64());
return rtp;
}
us_rtp_s *us_rtp_dup(const us_rtp_s *rtp) {
us_rtp_s *new;
US_CALLOC(new, 1);
memcpy(new, rtp, sizeof(us_rtp_s));
return new;
}
void us_rtp_destroy(us_rtp_s *rtp) {
free(rtp);
}
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video) {
rtp->payload = payload;
rtp->video = video;
rtp->ssrc = us_triple_u32(us_get_now_monotonic_u64());
}
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked) {
u32 word0 = 0x80000000;
if (marked) {

View File

@@ -44,8 +44,8 @@ typedef struct {
typedef void (*us_rtp_callback_f)(const us_rtp_s *rtp);
us_rtp_s *us_rtp_init(uint payload, bool video);
us_rtp_s *us_rtp_dup(const us_rtp_s *rtp);
us_rtp_s *us_rtp_init(void);
void us_rtp_destroy(us_rtp_s *rtp);
void us_rtp_assign(us_rtp_s *rtp, uint payload, bool video);
void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked);

View File

@@ -32,7 +32,8 @@
us_rtpa_s *us_rtpa_init(us_rtp_callback_f callback) {
us_rtpa_s *rtpa;
US_CALLOC(rtpa, 1);
rtpa->rtp = us_rtp_init(111, false);
rtpa->rtp = us_rtp_init();
us_rtp_assign(rtpa->rtp, 111, false);
rtpa->callback = callback;
return rtpa;
}

View File

@@ -44,7 +44,8 @@ static sz _find_annexb(const u8 *data, uz size);
us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback) {
us_rtpv_s *rtpv;
US_CALLOC(rtpv, 1);
rtpv->rtp = us_rtp_init(96, true);
rtpv->rtp = us_rtp_init();
us_rtp_assign(rtpv->rtp, 96, true);
rtpv->callback = callback;
return rtpv;
}

1
janus/src/uslibs/queue.c Symbolic link
View File

@@ -0,0 +1 @@
../../../src/libs/queue.c

1
janus/src/uslibs/queue.h Symbolic link
View File

@@ -0,0 +1 @@
../../../src/libs/queue.h

1
janus/src/uslibs/ring.c Symbolic link
View File

@@ -0,0 +1 @@
../../../src/libs/ring.c

1
janus/src/uslibs/ring.h Symbolic link
View File

@@ -0,0 +1 @@
../../../src/libs/ring.h

View File

@@ -33,7 +33,7 @@ max-line-length = 160
[BASIC]
# Good variable names which should always be accepted, separated by a comma
good-names = _, __, x, y, ws, make-html-h, make-jpeg-h, make-ico-h
good-names = _, __, x, y, ws, make-html-h, make-ico-h
# Regular expression matching correct method names
method-rgx = [a-z_][a-z0-9_]{2,50}$

View File

@@ -13,7 +13,7 @@ commands = cppcheck \
--std=c17 \
--error-exitcode=1 \
--quiet \
--enable=warning,unusedFunction,portability,performance,style \
--enable=warning,portability,performance,style \
--suppress=assignmentInAssert \
--suppress=variableScope \
--inline-suppr \

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer-dump.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER-DUMP 1 "version 5.54" "January 2021"
.TH USTREAMER-DUMP 1 "version 5.57" "January 2021"
.SH NAME
ustreamer-dump \- Dump uStreamer's memory sink to file

View File

@@ -1,6 +1,6 @@
.\" Manpage for ustreamer.
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
.TH USTREAMER 1 "version 5.54" "November 2020"
.TH USTREAMER 1 "version 5.57" "November 2020"
.SH NAME
ustreamer \- stream MJPEG video from any V4L2 device to the network
@@ -103,10 +103,10 @@ NOOP ─ Don't compress MJPEG stream (do nothing).
It doesn't do anything. Still here for compatibility.
.TP
.BR \-k\ \fIpath ", " \-\-blank\ \fIpath
Path to JPEG file that will be shown when the device is disconnected during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.
It doesn't do anything. Still here for compatibility.
.TP
.BR \-K\ \fIsec ", " \-\-last\-as\-blank\ \fIsec
Show the last frame received from the camera after it was disconnected, but no more than specified time (or endlessly if 0 is specified). If the device has not yet been online, display 'NO SIGNAL' or the image specified by option \-\-blank. Note: currently this option has no effect on memory sinks. Default: disabled.
Show the last frame received from the camera after it was disconnected, but no more than specified time (or endlessly if 0 is specified). If the device has not yet been online, display some error message. Note: currently this option has no effect on memory sinks. Default: disabled.
.TP
.BR \-l ", " \-\-slowdown
Slowdown capturing to 1 FPS or less when no stream or sink clients are connected. Useful to reduce CPU consumption. Default: disabled.

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=5.54
pkgver=5.57
pkgrel=1
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
url="https://github.com/pikvm/ustreamer"

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ustreamer
PKG_VERSION:=5.54
PKG_VERSION:=5.57
PKG_RELEASE:=1
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>

View File

@@ -17,7 +17,7 @@ def _find_sources(suffix: str) -> list[str]:
if __name__ == "__main__":
setup(
name="ustreamer",
version="5.54",
version="5.57",
description="uStreamer tools",
author="Maxim Devaev",
author_email="mdevaev@gmail.com",

View File

@@ -38,7 +38,7 @@ typedef struct {
static void _MemsinkObject_destroy_internals(_MemsinkObject *self) {
US_DELETE(self->mem, us_memsink_shared_unmap);
US_CLOSE_FD(self->fd, close);
US_CLOSE_FD(self->fd);
US_DELETE(self->frame, us_frame_destroy);
}
@@ -130,7 +130,7 @@ static int _wait_frame(_MemsinkObject *self) {
if (mem->magic == US_MEMSINK_MAGIC && mem->version == US_MEMSINK_VERSION && mem->id != self->frame_id) {
if (self->drop_same_frames > 0) {
if (
US_FRAME_COMPARE_META_USED_NOTS(self->mem, self->frame)
US_FRAME_COMPARE_GEOMETRY(self->mem, self->frame)
&& (self->frame_ts + self->drop_same_frames > now_ts)
&& !memcmp(self->frame->data, mem->data, mem->used)
) {
@@ -285,7 +285,7 @@ static PyModuleDef _Module = {
.m_size = -1,
};
PyMODINIT_FUNC PyInit_ustreamer(void) { // cppcheck-suppress unusedFunction
PyMODINIT_FUNC PyInit_ustreamer(void) {
PyObject *module = PyModule_Create(&_Module);
if (module == NULL) {
return NULL;

View File

@@ -88,13 +88,16 @@ override _CFLAGS += $(shell pkg-config --cflags libdrm)
_V4P_LDFLAGS = $(shell pkg-config --libs libdrm)
endif
# =====
all: $(_TARGETS)
install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin
for i in $(subst .bin,,$(_TARGETS)); do install -m755 $$i.bin $(DESTDIR)$(PREFIX)/bin/$$i; done
for i in $(subst .bin,,$(_TARGETS)); do \
install -m755 $$i.bin $(DESTDIR)$(PREFIX)/bin/$$i; \
done
install-strip: install

View File

@@ -26,7 +26,7 @@
#define US_VERSION_MAJOR 5
#define US_VERSION_MINOR 54
#define US_VERSION_MINOR 57
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)

View File

@@ -93,6 +93,7 @@ static int _device_open_io_method(us_device_s *dev);
static int _device_open_io_method_mmap(us_device_s *dev);
static int _device_open_io_method_userptr(us_device_s *dev);
static int _device_open_queue_buffers(us_device_s *dev);
static int _device_open_export_to_dma(us_device_s *dev);
static int _device_apply_resolution(us_device_s *dev, uint width, uint height, float hz);
static void _device_apply_controls(us_device_s *dev);
@@ -176,8 +177,6 @@ int us_device_open(us_device_s *dev) {
_D_LOG_PERROR("Can't open device");
goto error;
}
_D_LOG_INFO("Device fd=%d opened", run->fd);
if (_device_open_check_cap(dev) < 0) {
goto error;
}
@@ -195,9 +194,22 @@ int us_device_open(us_device_s *dev) {
if (_device_open_queue_buffers(dev) < 0) {
goto error;
}
if (dev->dma_export && !us_is_jpeg(run->format)) {
// uStreamer doesn't have any component that could handle JPEG capture via DMA
run->dma = !_device_open_export_to_dma(dev);
if (!run->dma && dev->dma_required) {
goto error;
}
}
_device_apply_controls(dev);
_D_LOG_DEBUG("Device fd=%d initialized", run->fd);
enum v4l2_buf_type type = run->capture_type;
if (us_xioctl(run->fd, VIDIOC_STREAMON, &type) < 0) {
_D_LOG_PERROR("Can't start capturing");
goto error;
}
run->streamon = true;
_D_LOG_INFO("Capturing started");
return 0;
error:
@@ -208,14 +220,21 @@ error:
void us_device_close(us_device_s *dev) {
us_device_runtime_s *const run = dev->run;
run->persistent_timeout_reported = false;
if (run->streamon) {
enum v4l2_buf_type type = run->capture_type;
if (us_xioctl(run->fd, VIDIOC_STREAMOFF, &type) < 0) {
_D_LOG_PERROR("Can't stop capturing");
}
run->streamon = false;
_D_LOG_INFO("Capturing stopped");
}
if (run->hw_bufs != NULL) {
_D_LOG_DEBUG("Releasing device buffers ...");
for (uint index = 0; index < run->n_bufs; ++index) {
us_hw_buffer_s *hw = &run->hw_bufs[index];
US_CLOSE_FD(hw->dma_fd, close);
US_CLOSE_FD(hw->dma_fd);
if (dev->io_method == V4L2_MEMORY_MMAP) {
if (hw->raw.allocated > 0 && hw->raw.data != NULL) {
@@ -235,87 +254,38 @@ void us_device_close(us_device_s *dev) {
run->n_bufs = 0;
}
if (run->fd >= 0) {
_D_LOG_DEBUG("Closing device ...");
if (close(run->fd) < 0) {
_D_LOG_PERROR("Can't close device fd=%d", run->fd);
} else {
_D_LOG_INFO("Device fd=%d closed", run->fd);
}
run->fd = -1;
}
US_CLOSE_FD(run->fd);
run->persistent_timeout_reported = false;
}
int us_device_export_to_dma(us_device_s *dev) {
us_device_runtime_s *const run = dev->run;
for (uint index = 0; index < run->n_bufs; ++index) {
struct v4l2_exportbuffer exp = {
.type = run->capture_type,
.index = index,
};
_D_LOG_DEBUG("Exporting device buffer=%u to DMA ...", index);
if (us_xioctl(run->fd, VIDIOC_EXPBUF, &exp) < 0) {
_D_LOG_PERROR("Can't export device buffer=%u to DMA", index);
goto error;
}
run->hw_bufs[index].dma_fd = exp.fd;
}
return 0;
error:
for (uint index = 0; index < run->n_bufs; ++index) {
US_CLOSE_FD(run->hw_bufs[index].dma_fd, close);
}
return -1;
}
int us_device_switch_capturing(us_device_s *dev, bool enable) {
us_device_runtime_s *const run = dev->run;
if (enable != run->capturing) {
enum v4l2_buf_type type = run->capture_type;
_D_LOG_DEBUG("%s device capturing ...", (enable ? "Starting" : "Stopping"));
if (us_xioctl(run->fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
_D_LOG_PERROR("Can't %s capturing", (enable ? "start" : "stop"));
if (enable) {
return -1;
}
}
run->capturing = enable;
_D_LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
}
return 0;
}
int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *has_error) {
int us_device_select(us_device_s *dev, bool *has_read, bool *has_error) {
us_device_runtime_s *const run = dev->run;
# define INIT_FD_SET(x_set) \
fd_set x_set; FD_ZERO(&x_set); FD_SET(run->fd, &x_set);
INIT_FD_SET(read_fds);
INIT_FD_SET(write_fds);
INIT_FD_SET(error_fds);
# undef INIT_FD_SET
// Раньше мы проверяли и has_write, но потом выяснилось, что libcamerify зачем-то
// генерирует эвенты на запись, вероятно ошибочно. Судя по всему, игнорирование
// has_write не делает никому плохо.
struct timeval timeout;
timeout.tv_sec = dev->timeout;
timeout.tv_usec = 0;
_D_LOG_DEBUG("Calling select() on video device ...");
int retval = select(run->fd + 1, &read_fds, &write_fds, &error_fds, &timeout);
int retval = select(run->fd + 1, &read_fds, NULL, &error_fds, &timeout);
if (retval > 0) {
*has_read = FD_ISSET(run->fd, &read_fds);
*has_write = FD_ISSET(run->fd, &write_fds);
*has_error = FD_ISSET(run->fd, &error_fds);
} else {
*has_read = false;
*has_write = false;
*has_error = false;
}
_D_LOG_DEBUG("Device select() --> %d", retval);
_D_LOG_DEBUG("Device select() --> %d; has_read=%d, has_error=%d", retval, *has_read, *has_error);
if (retval > 0) {
run->persistent_timeout_reported = false;
@@ -451,7 +421,7 @@ int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw) {
int us_device_consume_event(us_device_s *dev) {
struct v4l2_event event;
_D_LOG_DEBUG("Consuming V4L2 event ...");
_D_LOG_INFO("Consuming V4L2 event ...");
if (us_xioctl(dev->run->fd, VIDIOC_DQEVENT, &event) == 0) {
switch (event.type) {
case V4L2_EVENT_SOURCE_CHANGE:
@@ -931,6 +901,30 @@ static int _device_open_queue_buffers(us_device_s *dev) {
return 0;
}
static int _device_open_export_to_dma(us_device_s *dev) {
us_device_runtime_s *const run = dev->run;
for (uint index = 0; index < run->n_bufs; ++index) {
struct v4l2_exportbuffer exp = {
.type = run->capture_type,
.index = index,
};
_D_LOG_DEBUG("Exporting device buffer=%u to DMA ...", index);
if (us_xioctl(run->fd, VIDIOC_EXPBUF, &exp) < 0) {
_D_LOG_PERROR("Can't export device buffer=%u to DMA", index);
goto error;
}
run->hw_bufs[index].dma_fd = exp.fd;
}
return 0;
error:
for (uint index = 0; index < run->n_bufs; ++index) {
US_CLOSE_FD(run->hw_bufs[index].dma_fd);
}
return -1;
}
static int _device_apply_resolution(us_device_s *dev, uint width, uint height, float hz) {
// Тут VIDEO_MIN_* не используются из-за странностей минимального разрешения при отсутствии сигнала
// у некоторых устройств, например TC358743

View File

@@ -65,9 +65,10 @@ typedef struct {
uz raw_size;
uint n_bufs;
us_hw_buffer_s *hw_bufs;
bool dma;
enum v4l2_buf_type capture_type;
bool capture_mplane;
bool capturing;
bool streamon;
bool persistent_timeout_reported;
} us_device_runtime_s;
@@ -110,6 +111,8 @@ typedef struct {
enum v4l2_memory io_method;
bool dv_timings;
uint n_bufs;
bool dma_export;
bool dma_required;
uint desired_fps;
uz min_frame_size;
bool persistent;
@@ -129,9 +132,7 @@ int us_device_parse_io_method(const char *str);
int us_device_open(us_device_s *dev);
void us_device_close(us_device_s *dev);
int us_device_export_to_dma(us_device_s *dev);
int us_device_switch_capturing(us_device_s *dev, bool enable);
int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *has_error);
int us_device_select(us_device_s *dev, bool *has_read, bool *has_error);
int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw);
int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw);
int us_device_consume_event(us_device_s *dev);

View File

@@ -74,7 +74,7 @@ void us_frame_copy(const us_frame_s *src, us_frame_s *dest) {
bool us_frame_compare(const us_frame_s *a, const us_frame_s *b) {
return (
a->allocated && b->allocated
&& US_FRAME_COMPARE_META_USED_NOTS(a, b)
&& US_FRAME_COMPARE_GEOMETRY(a, b)
&& !memcmp(a->data, b->data, b->used)
);
}
@@ -99,6 +99,10 @@ uint us_frame_get_padding(const us_frame_s *frame) {
return 0;
}
bool us_is_jpeg(uint format) {
return (format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
}
const char *us_fourcc_to_string(uint format, char *buf, uz size) {
assert(size >= 8);
buf[0] = format & 0x7F;

View File

@@ -22,8 +22,6 @@
#pragma once
#include <linux/videodev2.h>
#include "types.h"
#include "tools.h"
@@ -41,7 +39,6 @@ typedef struct {
// Stride is a bytesperline in V4L2
// https://www.kernel.org/doc/html/v4.14/media/uapi/v4l/pixfmt-v4l2.html
// https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
bool online;
bool key;
uint gop;
@@ -60,17 +57,16 @@ typedef struct {
x_dest->online = x_src->online; \
x_dest->key = x_src->key; \
x_dest->gop = x_src->gop; \
\
x_dest->grab_ts = x_src->grab_ts; \
x_dest->encode_begin_ts = x_src->encode_begin_ts; \
x_dest->encode_end_ts = x_src->encode_end_ts; \
}
static inline void us_frame_copy_meta(const us_frame_s *src, us_frame_s *dest) {
US_FRAME_COPY_META(src, dest);
}
#define US_FRAME_COMPARE_META_USED_NOTS(x_a, x_b) ( \
#define US_FRAME_COMPARE_GEOMETRY(x_a, x_b) ( \
/* Compare the used size and significant meta (no timings) */ \
x_a->used == x_b->used \
\
&& x_a->width == x_b->width \
&& x_a->height == x_b->height \
&& x_a->format == x_b->format \
@@ -83,7 +79,7 @@ static inline void us_frame_copy_meta(const us_frame_s *src, us_frame_s *dest) {
static inline void us_frame_encoding_begin(const us_frame_s *src, us_frame_s *dest, uint format) {
assert(src->used > 0);
us_frame_copy_meta(src, dest);
US_FRAME_COPY_META(src, dest);
dest->encode_begin_ts = us_get_now_monotonic();
dest->format = format;
dest->stride = 0;
@@ -108,8 +104,5 @@ bool us_frame_compare(const us_frame_s *a, const us_frame_s *b);
uint us_frame_get_padding(const us_frame_s *frame);
bool us_is_jpeg(uint format);
const char *us_fourcc_to_string(uint format, char *buf, uz size);
static inline bool us_is_jpeg(uint format) {
return (format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
}

View File

@@ -20,7 +20,7 @@
*****************************************************************************/
#include "ftext.h"
#include "frametext.h"
#include <string.h>
@@ -28,32 +28,34 @@
#include <linux/videodev2.h>
#include "../libs/tools.h"
#include "../libs/frame.h"
#include "ftext_font.h"
#include "tools.h"
#include "frame.h"
#include "frametext_font.h"
static void _ftext_draw_line(
us_ftext_s *ft, const char *line,
static void _frametext_draw_line(
us_frametext_s *ft, const char *line,
uint scale_x, uint scale_y,
uint start_x, uint start_y);
us_ftext_s *us_ftext_init(void) {
us_ftext_s *ft;
us_frametext_s *us_frametext_init(void) {
us_frametext_s *ft;
US_CALLOC(ft, 1);
ft->frame = us_frame_init();
return ft;
}
void us_ftext_destroy(us_ftext_s *ft) {
void us_frametext_destroy(us_frametext_s *ft) {
us_frame_destroy(ft->frame);
US_DELETE(ft->text, free);
free(ft);
}
void us_ftext_draw(us_ftext_s *ft, const char *text, uint width, uint height) {
void us_frametext_draw(us_frametext_s *ft, const char *text, uint width, uint height) {
assert(width > 0);
assert(height > 0);
us_frame_s *const frame = ft->frame;
if (
@@ -110,7 +112,7 @@ void us_ftext_draw(us_ftext_s *ft, const char *text, uint width, uint height) {
const uint start_x = (frame->width >= line_width
? ((frame->width - line_width) / 2)
: 0);
_ftext_draw_line(ft, line, scale_x, scale_y, start_x, start_y + n_line * 8 * scale_y);
_frametext_draw_line(ft, line, scale_x, scale_y, start_x, start_y + n_line * 8 * scale_y);
++n_line;
}
@@ -118,8 +120,8 @@ empty:
free(str);
}
void _ftext_draw_line(
us_ftext_s *ft, const char *line,
void _frametext_draw_line(
us_frametext_s *ft, const char *line,
uint scale_x, uint scale_y,
uint start_x, uint start_y) {
@@ -139,16 +141,16 @@ void _ftext_draw_line(
break;
}
const u8 ch = US_MIN((u8)line[ch_x / 8 / scale_x], sizeof(US_FTEXT_FONT) / 8 - 1);
const u8 ch = US_MIN((u8)line[ch_x / 8 / scale_x], sizeof(US_FRAMETEXT_FONT) / 8 - 1);
const uint ch_byte = (ch_y / scale_y) % 8;
const uint ch_bit = (ch_x / scale_x) % 8;
const bool pix_on = !!(US_FTEXT_FONT[ch][ch_byte] & (1 << ch_bit));
const bool pix_on = !!(US_FRAMETEXT_FONT[ch][ch_byte] & (1 << ch_bit));
u8 *const b = &frame->data[offset]; // XXX: Big endian for Raspberry
u8 *const g = b + 1;
u8 *const r = g + 1;
u8 *const r = &frame->data[offset];
u8 *const g = r + 1;
u8 *const b = r + 2;
*r = pix_on * 0x51;
*r = pix_on * 0x65; // RGB/BGR-friendly
*g = pix_on * 0x65;
*b = pix_on * 0x65;
}

View File

@@ -23,17 +23,17 @@
#pragma once
#include "../libs/types.h"
#include "../libs/frame.h"
#include "types.h"
#include "frame.h"
typedef struct {
char *text;
us_frame_s *frame;
} us_ftext_s;
} us_frametext_s;
us_ftext_s *us_ftext_init(void);
void us_ftext_destroy(us_ftext_s *ft);
us_frametext_s *us_frametext_init(void);
void us_frametext_destroy(us_frametext_s *ft);
void us_ftext_draw(us_ftext_s *ft, const char *text, uint width, uint height);
void us_frametext_draw(us_frametext_s *ft, const char *text, uint width, uint height);

View File

@@ -20,10 +20,12 @@
*****************************************************************************/
#include "ftext_font.h"
#include "frametext_font.h"
#include "types.h"
const u8 US_FTEXT_FONT[128][8] = {
const u8 US_FRAMETEXT_FONT[128][8] = {
// https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001

View File

@@ -22,7 +22,7 @@
#pragma once
#include "../libs/types.h"
#include "types.h"
extern const u8 US_FTEXT_FONT[128][8];
extern const u8 US_FRAMETEXT_FONT[128][8];

View File

@@ -129,7 +129,7 @@ bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame) {
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
return false;
}
return (has_clients || !US_FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
return (has_clients || !US_FRAME_COMPARE_GEOMETRY(sink->mem, frame));;
}
int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key_requested) {
@@ -179,7 +179,7 @@ int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key
return 0;
}
int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *key_requested, bool key_required) { // cppcheck-suppress unusedFunction
int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *key_requested, bool key_required) {
assert(!sink->server); // Client only
if (us_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {

View File

@@ -28,9 +28,9 @@
#include <pthread.h>
#include "uslibs/types.h"
#include "uslibs/tools.h"
#include "uslibs/threading.h"
#include "types.h"
#include "tools.h"
#include "threading.h"
us_queue_s *us_queue_init(uint capacity) {
@@ -104,9 +104,9 @@ int us_queue_get(us_queue_s *queue, void **item, ldf timeout) {
#undef _WAIT_OR_UNLOCK
int us_queue_get_free(us_queue_s *queue) {
/*int us_queue_get_free(us_queue_s *queue) {
US_MUTEX_LOCK(queue->mutex);
const uint size = queue->size;
US_MUTEX_UNLOCK(queue->mutex);
return queue->capacity - size;
}
}*/

View File

@@ -24,8 +24,8 @@
#include <pthread.h>
#include "uslibs/types.h"
#include "uslibs/tools.h"
#include "types.h"
#include "tools.h"
// Based on https://github.com/seifzadeh/c-pthread-queue/blob/master/queue.h
@@ -61,4 +61,4 @@ void us_queue_destroy(us_queue_s *queue);
int us_queue_put(us_queue_s *queue, void *item, ldf timeout);
int us_queue_get(us_queue_s *queue, void **item, ldf timeout);
int us_queue_get_free(us_queue_s *queue);
// int us_queue_get_free(us_queue_s *queue);

86
src/libs/ring.c Normal file
View File

@@ -0,0 +1,86 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
# #
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program 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 3 of the License, or #
# (at your option) any later version. #
# #
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include <assert.h>
#include "ring.h"
#include "types.h"
#include "tools.h"
#include "queue.h"
int _acquire(us_ring_s *ring, us_queue_s *queue, ldf timeout);
void _release(us_ring_s *ring, us_queue_s *queue, uint index);
us_ring_s *us_ring_init(uint capacity) {
us_ring_s *ring;
US_CALLOC(ring, 1);
US_CALLOC(ring->items, capacity);
US_CALLOC(ring->places, capacity);
ring->capacity = capacity;
ring->producer = us_queue_init(capacity);
ring->consumer = us_queue_init(capacity);
for (uint index = 0; index < capacity; ++index) {
ring->places[index] = index; // XXX: Just to avoid casting between pointer and uint
assert(!us_queue_put(ring->producer, (void*)(ring->places + index), 0));
}
return ring;
}
void us_ring_destroy(us_ring_s *ring) {
us_queue_destroy(ring->consumer);
us_queue_destroy(ring->producer);
free(ring->places);
free(ring->items);
free(ring);
}
int us_ring_producer_acquire(us_ring_s *ring, ldf timeout) {
return _acquire(ring, ring->producer, timeout);
}
void us_ring_producer_release(us_ring_s *ring, uint index) {
_release(ring, ring->consumer, index);
}
int us_ring_consumer_acquire(us_ring_s *ring, ldf timeout) {
return _acquire(ring, ring->consumer, timeout);
}
void us_ring_consumer_release(us_ring_s *ring, uint index) {
_release(ring, ring->producer, index);
}
int _acquire(us_ring_s *ring, us_queue_s *queue, ldf timeout) {
(void)ring;
uint *place;
if (us_queue_get(queue, (void**)&place, timeout) < 0) {
return -1;
}
return *place;
}
void _release(us_ring_s *ring, us_queue_s *queue, uint index) {
assert(!us_queue_put(queue, (void*)(ring->places + index), 0));
}

View File

@@ -22,13 +22,42 @@
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include "types.h"
#include "queue.h"
extern const unsigned US_BLANK_JPEG_WIDTH;
extern const unsigned US_BLANK_JPEG_HEIGHT;
typedef struct {
uz capacity;
void **items;
uint *places;
us_queue_s *producer;
us_queue_s *consumer;
} us_ring_s;
extern const size_t US_BLANK_JPEG_DATA_SIZE;
extern const uint8_t US_BLANK_JPEG_DATA[];
#define US_RING_INIT_WITH_ITEMS(x_ring, x_capacity, x_init_item) { \
(x_ring) = us_ring_init(x_capacity); \
for (uz m_index = 0; m_index < (x_ring)->capacity; ++m_index) { \
(x_ring)->items[m_index] = x_init_item(); \
} \
}
#define US_RING_DELETE_WITH_ITEMS(x_ring, x_destroy_item) { \
if (x_ring) { \
for (uz m_index = 0; m_index < (x_ring)->capacity; ++m_index) { \
x_destroy_item((x_ring)->items[m_index]); \
} \
us_ring_destroy(x_ring); \
} \
}
us_ring_s *us_ring_init(uint capacity);
void us_ring_destroy(us_ring_s *ring);
int us_ring_producer_acquire(us_ring_s *ring, ldf timeout);
void us_ring_producer_release(us_ring_s *ring, uint index);
int us_ring_consumer_acquire(us_ring_s *ring, ldf timeout);
void us_ring_consumer_release(us_ring_s *ring, uint index);

View File

@@ -59,7 +59,7 @@
#define US_CALLOC(x_dest, x_nmemb) assert(((x_dest) = calloc((x_nmemb), sizeof(*(x_dest)))) != NULL)
#define US_REALLOC(x_dest, x_nmemb) assert(((x_dest) = realloc((x_dest), (x_nmemb) * sizeof(*(x_dest)))) != NULL)
#define US_DELETE(x_dest, x_free) { if (x_dest) { x_free(x_dest); x_dest = NULL; } }
#define US_CLOSE_FD(x_dest, x_close) { if (x_dest >= 0) { x_close(x_dest); x_dest = -1; } }
#define US_CLOSE_FD(x_dest) { if (x_dest >= 0) { close(x_dest); x_dest = -1; } }
#define US_MEMSET_ZERO(x_obj) memset(&(x_obj), 0, sizeof(x_obj))
#define US_SNPRINTF(x_dest, x_size, x_fmt, ...) assert(snprintf((x_dest), (x_size), (x_fmt), ##__VA_ARGS__) > 0)

View File

@@ -68,12 +68,12 @@ int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode) {
jpeg_start_decompress(&jpeg);
us_frame_copy_meta(src, dest);
dest->format = V4L2_PIX_FMT_RGB24;
dest->width = jpeg.output_width;
dest->height = jpeg.output_height;
dest->stride = jpeg.output_width * jpeg.output_components; // Row stride
dest->used = 0;
US_FRAME_COPY_META(src, dest); // cppcheck-suppress redundantAssignment
dest->format = V4L2_PIX_FMT_RGB24; // cppcheck-suppress redundantAssignment
dest->width = jpeg.output_width; // cppcheck-suppress redundantAssignment
dest->height = jpeg.output_height; // cppcheck-suppress redundantAssignment
dest->stride = jpeg.output_width * jpeg.output_components; // cppcheck-suppress redundantAssignment
dest->used = 0; // cppcheck-suppress redundantAssignment
if (decode) {
JSAMPARRAY scanlines;

View File

@@ -22,81 +22,31 @@
#include "blank.h"
#include "../libs/types.h"
#include "../libs/tools.h"
#include "../libs/frame.h"
#include "../libs/frametext.h"
static us_frame_s *_init_internal(void);
static us_frame_s *_init_external(const char *path);
#include "encoders/cpu/encoder.h"
us_frame_s *us_blank_frame_init(const char *path) {
us_frame_s *blank = NULL;
if (path && path[0] != '\0') {
blank = _init_external(path);
}
if (blank != NULL) {
US_LOG_INFO("Using external blank placeholder: %s", path);
} else {
blank = _init_internal();
US_LOG_INFO("Using internal blank placeholder");
}
us_blank_s *us_blank_init(void) {
us_blank_s *blank;
US_CALLOC(blank, 1);
blank->ft = us_frametext_init();
blank->raw = blank->ft->frame;
blank->jpeg = us_frame_init();
us_blank_draw(blank, "< NO SIGNAL >", 640, 480);
return blank;
}
static us_frame_s *_init_internal(void) {
us_frame_s *const blank = us_frame_init();
us_frame_set_data(blank, US_BLANK_JPEG_DATA, US_BLANK_JPEG_DATA_SIZE);
blank->width = US_BLANK_JPEG_WIDTH;
blank->height = US_BLANK_JPEG_HEIGHT;
blank->format = V4L2_PIX_FMT_JPEG;
return blank;
void us_blank_draw(us_blank_s *blank, const char *text, uint width, uint height) {
us_frametext_draw(blank->ft, text, width, height);
us_cpu_encoder_compress(blank->raw, blank->jpeg, 95);
}
static us_frame_s *_init_external(const char *path) {
FILE *fp = NULL;
us_frame_s *blank = us_frame_init();
blank->format = V4L2_PIX_FMT_JPEG;
if ((fp = fopen(path, "rb")) == NULL) {
US_LOG_PERROR("Can't open blank placeholder '%s'", path);
goto error;
}
const size_t chunk_size = 100 * 1024;
while (true) {
if (blank->used + chunk_size >= blank->allocated) {
us_frame_realloc_data(blank, blank->used + chunk_size * 2);
}
const size_t readed = fread(blank->data + blank->used, 1, chunk_size, fp);
blank->used += readed;
if (readed < chunk_size) {
if (feof(fp)) {
break;
} else {
US_LOG_PERROR("Can't read blank placeholder");
goto error;
}
}
}
us_frame_s *const decoded = us_frame_init();
if (us_unjpeg(blank, decoded, false) < 0) {
us_frame_destroy(decoded);
goto error;
}
blank->width = decoded->width;
blank->height = decoded->height;
us_frame_destroy(decoded);
goto ok;
error:
US_DELETE(blank, us_frame_destroy);
ok:
US_DELETE(fp, fclose);
return blank;
void us_blank_destroy(us_blank_s *blank) {
us_frame_destroy(blank->jpeg);
us_frametext_destroy(blank->ft);
free(blank);
}

View File

@@ -22,17 +22,19 @@
#pragma once
#include <stdio.h>
#include <stdbool.h>
#include <linux/videodev2.h>
#include "../libs/tools.h"
#include "../libs/logging.h"
#include "../libs/types.h"
#include "../libs/frame.h"
#include "../libs/unjpeg.h"
#include "data/blank_jpeg.h"
#include "../libs/frametext.h"
us_frame_s *us_blank_frame_init(const char *path);
typedef struct {
us_frametext_s *ft;
us_frame_s *raw;
us_frame_s *jpeg;
} us_blank_s;
us_blank_s *us_blank_init(void);
void us_blank_destroy(us_blank_s *blank);
void us_blank_draw(us_blank_s *blank, const char *text, uint width, uint height);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,723 +0,0 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
# #
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program 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 3 of the License, or #
# (at your option) any later version. #
# #
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>. #
# #
*****************************************************************************/
#include "blank_jpeg.h"
const unsigned US_BLANK_JPEG_WIDTH = 640;
const unsigned US_BLANK_JPEG_HEIGHT = 480;
const size_t US_BLANK_JPEG_DATA_SIZE = 13845;
const uint8_t US_BLANK_JPEG_DATA[] = {
0xFF, 0xD8, 0xFF, 0xE1, 0x09, 0x50, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x6E, 0x73, 0x2E, 0x61, 0x64, 0x6F, 0x62,
0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x2F, 0x78, 0x61, 0x70, 0x2F, 0x31, 0x2E, 0x30, 0x2F, 0x00, 0x3C, 0x3F, 0x78, 0x70, 0x61,
0x63, 0x6B, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6E, 0x3D, 0x22, 0xEF, 0xBB, 0xBF, 0x22, 0x20, 0x69, 0x64, 0x3D,
0x22, 0x57, 0x35, 0x4D, 0x30, 0x4D, 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7A, 0x72, 0x65, 0x53, 0x7A, 0x4E, 0x54, 0x63,
0x7A, 0x6B, 0x63, 0x39, 0x64, 0x22, 0x3F, 0x3E, 0x20, 0x3C, 0x78, 0x3A, 0x78, 0x6D, 0x70, 0x6D, 0x65, 0x74, 0x61, 0x20,
0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x78, 0x3D, 0x22, 0x61, 0x64, 0x6F, 0x62, 0x65, 0x3A, 0x6E, 0x73, 0x3A, 0x6D, 0x65,
0x74, 0x61, 0x2F, 0x22, 0x20, 0x78, 0x3A, 0x78, 0x6D, 0x70, 0x74, 0x6B, 0x3D, 0x22, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20,
0x58, 0x4D, 0x50, 0x20, 0x43, 0x6F, 0x72, 0x65, 0x20, 0x35, 0x2E, 0x36, 0x2D, 0x63, 0x31, 0x33, 0x38, 0x20, 0x37, 0x39,
0x2E, 0x31, 0x35, 0x39, 0x38, 0x32, 0x34, 0x2C, 0x20, 0x32, 0x30, 0x31, 0x36, 0x2F, 0x30, 0x39, 0x2F, 0x31, 0x34, 0x2D,
0x30, 0x31, 0x3A, 0x30, 0x39, 0x3A, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3E, 0x20, 0x3C,
0x72, 0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6D, 0x6C, 0x6E, 0x73, 0x3A, 0x72, 0x64, 0x66, 0x3D, 0x22, 0x68,
0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x33, 0x2E, 0x6F, 0x72, 0x67, 0x2F, 0x31, 0x39, 0x39,
0x39, 0x2F, 0x30, 0x32, 0x2F, 0x32, 0x32, 0x2D, 0x72, 0x64, 0x66, 0x2D, 0x73, 0x79, 0x6E, 0x74, 0x61, 0x78, 0x2D, 0x6E,
0x73, 0x23, 0x22, 0x3E, 0x20, 0x3C, 0x72, 0x64, 0x66, 0x3A, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F,
0x6E, 0x20, 0x72, 0x64, 0x66, 0x3A, 0x61, 0x62, 0x6F, 0x75, 0x74, 0x3D, 0x22, 0x22, 0x2F, 0x3E, 0x20, 0x3C, 0x2F, 0x72,
0x64, 0x66, 0x3A, 0x52, 0x44, 0x46, 0x3E, 0x20, 0x3C, 0x2F, 0x78, 0x3A, 0x78, 0x6D, 0x70, 0x6D, 0x65, 0x74, 0x61, 0x3E,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3C, 0x3F, 0x78, 0x70, 0x61, 0x63, 0x6B, 0x65, 0x74, 0x20, 0x65,
0x6E, 0x64, 0x3D, 0x22, 0x77, 0x22, 0x3F, 0x3E, 0xFF, 0xED, 0x00, 0x2C, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x73, 0x68, 0x6F,
0x70, 0x20, 0x33, 0x2E, 0x30, 0x00, 0x38, 0x42, 0x49, 0x4D, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xD4, 0x1D,
0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E, 0xFF, 0xDB, 0x00, 0x84, 0x00, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x50, 0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64,
0x6F, 0x62, 0x65, 0x00, 0x64, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80, 0x03,
0x00, 0x11, 0x00, 0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x7D, 0x00, 0x01, 0x00, 0x02, 0x03, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x07, 0x08, 0x09, 0x05, 0x06, 0x02, 0x03,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x01, 0x00, 0x01, 0x04, 0x02, 0x01, 0x03, 0x02, 0x03, 0x04, 0x08, 0x04, 0x06, 0x03, 0x00, 0x00, 0x00, 0x03, 0x02, 0x04,
0x05, 0x06, 0x01, 0x07, 0x08, 0x09, 0x11, 0x12, 0x0A, 0x13, 0x14, 0x21, 0x22, 0x15, 0x37, 0x77, 0xB6, 0x16, 0x23, 0x31,
0x35, 0x39, 0x41, 0x75, 0xB4, 0x17, 0x38, 0xB5, 0xB7, 0x18, 0x1A, 0x24, 0x32, 0x51, 0x78, 0x56, 0x97, 0xD4, 0x11, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, 0x0C,
0x03, 0x00, 0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD5, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD6, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD7, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD3, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xD5, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD6, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD7, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD5, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD6, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xD7, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x06, 0x4C, 0xE9, 0x5B, 0x0B, 0x1C, 0xAF, 0x71, 0xF5, 0x2E, 0x2F, 0x29, 0x65, 0x69, 0x92, 0xC6, 0x64, 0xBB, 0x33,
0x43, 0xB0, 0xC8, 0xE3, 0xAF, 0xED, 0xA1, 0xBC, 0xB1, 0xBF, 0xB1, 0xBC, 0xDA, 0x71, 0x56, 0xF7, 0x76, 0x57, 0xB6, 0x97,
0x14, 0x49, 0x6F, 0x75, 0x69, 0x75, 0x6F, 0x25, 0x54, 0x49, 0x1D, 0x74, 0xD5, 0x45, 0x74, 0x55, 0xCD, 0x35, 0x71, 0xCF,
0x1C, 0xF3, 0xC0, 0x24, 0x45, 0xEB, 0x5F, 0xE9, 0x19, 0x61, 0xD4, 0x3F, 0xB6, 0x7C, 0xBB, 0xF1, 0x67, 0x4E, 0xB6, 0xC6,
0x75, 0x4C, 0x95, 0x47, 0x71, 0xDC, 0x5D, 0x53, 0xAA, 0xE3, 0x20, 0xB3, 0xC6, 0x75, 0x94, 0xF5, 0x71, 0x15, 0xBF, 0xF4,
0xE7, 0x4D, 0xC2, 0x63, 0xA0, 0x8A, 0x1B, 0x1D, 0x02, 0xF6, 0x5F, 0x6E, 0x72, 0x56, 0x50, 0x51, 0xF6, 0xF0, 0xD3, 0xD7,
0xF7, 0xE2, 0xA6, 0x9B, 0x0A, 0xEB, 0xA2, 0xC4, 0x23, 0x22, 0x0D, 0xD8, 0xF4, 0xE3, 0xEB, 0xED, 0x2F, 0xB5, 0xBC, 0xE3,
0xF1, 0xA7, 0xAE, 0xBB, 0x17, 0x5C, 0xC7, 0x6D, 0xBA, 0x46, 0xDD, 0xD9, 0x36, 0x38, 0x7D, 0x97, 0x5B, 0xCB, 0x47, 0x5C,
0xB8, 0xDC, 0xC6, 0x36, 0x6B, 0x0C, 0x85, 0x72, 0x59, 0xDD, 0xD1, 0x1C, 0x91, 0x49, 0xCC, 0x55, 0x57, 0x1D, 0x3C, 0xFE,
0x9A, 0xA9, 0xE7, 0x8E, 0x78, 0xE3, 0x9E, 0x39, 0x06, 0xF5, 0xFA, 0xF4, 0x78, 0xE9, 0xD2, 0x3E, 0x34, 0xF9, 0x4D, 0xD5,
0x7A, 0x6F, 0x44, 0x75, 0xBE, 0xBB, 0xD6, 0x3A, 0xBE, 0x6B, 0xA0, 0x30, 0xBB, 0x3E, 0x57, 0x0B, 0xAD, 0x45, 0x73, 0x0D,
0x95, 0xEE, 0x7E, 0xE3, 0xB1, 0x7B, 0x23, 0x15, 0x36, 0x52, 0x6A, 0x6E, 0xAE, 0x6E, 0x6B, 0xE6, 0xEA, 0x4C, 0x76, 0x26,
0xDA, 0x2E, 0x79, 0xE2, 0xAE, 0x38, 0xF8, 0x43, 0x4F, 0xE5, 0xFF, 0x00, 0xC8, 0x6B, 0xE7, 0xA6, 0xFF, 0x00, 0xA5, 0xD7,
0x6F, 0xFA, 0x83, 0xED, 0x57, 0xD7, 0xF8, 0xEB, 0xFE, 0x3A, 0xE7, 0xA3, 0xB5, 0x1C, 0x8C, 0x56, 0x3B, 0xCF, 0x6B, 0x64,
0x71, 0xF5, 0xE4, 0x3E, 0x59, 0x0A, 0xA0, 0xA6, 0xEB, 0x8D, 0x57, 0x49, 0xC4, 0x73, 0x35, 0xA5, 0x1B, 0x1E, 0xD3, 0x25,
0xBC, 0x91, 0xD7, 0x3F, 0xCA, 0x68, 0x6D, 0x31, 0xD6, 0xF2, 0xD3, 0x2C, 0xF5, 0xF3, 0x5D, 0x76, 0xF6, 0xF7, 0x01, 0xDA,
0xEE, 0xC7, 0x83, 0xD0, 0x8F, 0xD3, 0x07, 0x27, 0xFF, 0x00, 0x0E, 0x76, 0x0E, 0xA9, 0xAF, 0xCA, 0xCE, 0xF4, 0xD7, 0x61,
0xAE, 0xD3, 0x69, 0xC6, 0x65, 0x31, 0x98, 0xCE, 0xEE, 0xCB, 0x58, 0xE5, 0xE1, 0xB3, 0xBE, 0xB0, 0xA6, 0x1D, 0xEA, 0xDF,
0x71, 0xC9, 0xE0, 0xBA, 0x47, 0x57, 0xBF, 0xAA, 0x6B, 0xA9, 0x7F, 0x11, 0x8F, 0xB1, 0xB2, 0xFD, 0xA3, 0x67, 0x55, 0x31,
0xCD, 0x2D, 0x9F, 0xDC, 0x8E, 0xD6, 0xAE, 0x43, 0x15, 0xEA, 0xFE, 0xA3, 0x9E, 0x87, 0x9D, 0xD5, 0x2D, 0x3A, 0x2F, 0x73,
0xFA, 0x79, 0xEB, 0x1D, 0x25, 0x84, 0xCA, 0xDF, 0x5B, 0xC1, 0x46, 0xE7, 0xAB, 0x74, 0xDF, 0x5C, 0x5B, 0x59, 0x63, 0x20,
0xB8, 0xB7, 0xBB, 0xB4, 0xBA, 0xBE, 0xCD, 0xEC, 0x5D, 0x39, 0xCE, 0xAD, 0xD9, 0xD8, 0xCB, 0x7B, 0x5A, 0x2E, 0x7E, 0x54,
0xD1, 0x8C, 0xB4, 0xC8, 0xD7, 0x55, 0x5E, 0xD2, 0xF1, 0x4D, 0x32, 0x45, 0x1F, 0x20, 0xF1, 0xFC, 0xCA, 0xF4, 0x2E, 0xEB,
0xAD, 0xBF, 0xAB, 0x6B, 0xF2, 0x77, 0xD3, 0x43, 0x7A, 0xA7, 0xB2, 0xB4, 0x6B, 0xDC, 0x3D, 0xCE, 0xD3, 0x1F, 0x52, 0xF1,
0xB2, 0x45, 0xBA, 0xC1, 0x9C, 0xC2, 0x5A, 0xD1, 0x27, 0x37, 0x55, 0x75, 0x06, 0xEB, 0x45, 0x73, 0x64, 0x32, 0xB9, 0x1B,
0x0F, 0xC3, 0x57, 0x4D, 0x78, 0x3C, 0xBC, 0xB7, 0x59, 0x09, 0xAE, 0x28, 0x96, 0x28, 0xEE, 0xF9, 0xB9, 0xA6, 0x3B, 0x2A,
0xC2, 0x30, 0x12, 0x47, 0x24, 0x52, 0x57, 0x14, 0xB4, 0x57, 0x14, 0xB1, 0x57, 0x54, 0x72, 0x47, 0x25, 0x3C, 0xD1, 0x24,
0x72, 0x51, 0xCF, 0x34, 0xD7, 0x45, 0x74, 0x55, 0xC7, 0x15, 0x51, 0x5D, 0x15, 0x71, 0xCF, 0x1C, 0xF1, 0xCF, 0x1E, 0xFC,
0x72, 0x0F, 0xC0, 0x3A, 0x19, 0xE9, 0xA3, 0xE0, 0x76, 0x6B, 0xD4, 0x03, 0xC8, 0x9B, 0x6E, 0xAE, 0xE7, 0x33, 0x79, 0xAA,
0x75, 0xDE, 0xAB, 0x85, 0x97, 0x75, 0xED, 0x5D, 0xBB, 0x1F, 0x14, 0x32, 0x64, 0xF1, 0x7A, 0xAD, 0xB5, 0xF5, 0x9E, 0x3A,
0x1C, 0x56, 0xBD, 0xC5, 0xE4, 0x17, 0x18, 0xFA, 0xB6, 0x8D, 0x8F, 0x25, 0x7D, 0x1D, 0xBD, 0xA7, 0xDF, 0xA2, 0xB8, 0xE0,
0x8B, 0x89, 0xEE, 0xAA, 0x8E, 0x6A, 0x6D, 0xEA, 0x86, 0xB0, 0x90, 0x37, 0x92, 0x7D, 0xB9, 0xE8, 0xB9, 0xE9, 0x95, 0x9B,
0x8F, 0xC7, 0x5B, 0x0F, 0x0C, 0x74, 0x8F, 0x21, 0x3B, 0x3F, 0x09, 0x69, 0x8C, 0x9F, 0x70, 0xB4, 0xCB, 0xE9, 0x1A, 0x5F,
0x67, 0xDF, 0xEB, 0x52, 0x5E, 0x5B, 0x71, 0x91, 0x86, 0x9D, 0xC3, 0xB1, 0x7B, 0x82, 0xBC, 0xFD, 0xF5, 0xAE, 0xC9, 0x95,
0xB5, 0xC8, 0x53, 0x77, 0xC6, 0x3B, 0x19, 0x0C, 0x90, 0x43, 0x0C, 0x94, 0x53, 0x5D, 0x36, 0x91, 0xD3, 0x6F, 0x10, 0x3F,
0x5B, 0x0F, 0x40, 0xFA, 0x35, 0xFA, 0x99, 0x78, 0xCF, 0xDA, 0x3D, 0xD7, 0xD2, 0xB0, 0x6A, 0x9E, 0x21, 0x6D, 0x5D, 0x51,
0x87, 0x97, 0x2B, 0xB9, 0x6C, 0x18, 0xDD, 0x73, 0x0F, 0xD6, 0x37, 0xBD, 0x51, 0x27, 0x18, 0xDA, 0xB8, 0xC4, 0xCB, 0xD8,
0xFD, 0x51, 0xAF, 0xE4, 0xB9, 0xEB, 0xFD, 0x9B, 0x4F, 0xCE, 0xF3, 0x8E, 0xA7, 0xED, 0x5C, 0x62, 0x6A, 0xAA, 0x6B, 0xDB,
0x88, 0xE5, 0x86, 0xD6, 0xFA, 0x3B, 0xDE, 0x6E, 0xA1, 0xE4, 0x38, 0x81, 0xE8, 0xDF, 0xD6, 0xDD, 0x77, 0xD8, 0xFE, 0xA4,
0x7D, 0x35, 0xD7, 0xDD, 0x89, 0xAA, 0xE8, 0xFD, 0xB3, 0xA1, 0xDE, 0xDB, 0x77, 0x0C, 0x77, 0xD8, 0x3D, 0xC3, 0x56, 0xB1,
0xDA, 0x74, 0xDD, 0x8E, 0x8C, 0x3F, 0x53, 0xEF, 0x97, 0xD8, 0x9C, 0x8C, 0xDA, 0xDE, 0xE1, 0x88, 0xAE, 0x1B, 0x88, 0x63,
0xBE, 0xB2, 0x86, 0xF2, 0xD7, 0x8B, 0xBB, 0x3A, 0x27, 0x86, 0x5A, 0x28, 0xAF, 0x9A, 0x23, 0x96, 0x9F, 0x6A, 0x43, 0xEB,
0xFD, 0x72, 0xFA, 0xCB, 0xAD, 0xFA, 0x93, 0xCF, 0x7D, 0x8F, 0x4E, 0xEA, 0x9E, 0xBE, 0xD2, 0x3A, 0xCB, 0x51, 0x83, 0xAC,
0xFA, 0xDA, 0xFE, 0x0D, 0x5B, 0xAF, 0x75, 0x3C, 0x0E, 0x97, 0xAE, 0x43, 0x7D, 0x7D, 0x8B, 0xBA, 0x92, 0xFA, 0xF6, 0x2C,
0x1E, 0xB9, 0x61, 0x8D, 0xC6, 0x47, 0x77, 0x79, 0x25, 0x3C, 0x55, 0x2C, 0x9C, 0x45, 0xC5, 0x72, 0x73, 0xC7, 0xBD, 0x5C,
0xF3, 0xC8, 0x35, 0xA7, 0xC0, 0x5F, 0x4F, 0xEE, 0xE3, 0xF5, 0x01, 0xED, 0x59, 0xF4, 0x3E, 0xBA, 0xAE, 0xDB, 0x58, 0xD3,
0xF5, 0x98, 0x6D, 0x32, 0x5D, 0x99, 0xDA, 0x59, 0xAB, 0x39, 0xEF, 0x35, 0xED, 0x17, 0x11, 0x7B, 0x24, 0xD4, 0x59, 0x51,
0xC5, 0x94, 0x13, 0x5A, 0xCD, 0x9F, 0xD9, 0xF3, 0x55, 0x5B, 0x4B, 0x46, 0x3B, 0x17, 0x14, 0xD0, 0xD7, 0x73, 0x54, 0x52,
0x57, 0x24, 0xB0, 0x5B, 0x45, 0x3D, 0xC4, 0x41, 0x22, 0xCE, 0xC0, 0xEA, 0x0F, 0x43, 0xCF, 0x4A, 0x3B, 0x4C, 0x56, 0xA9,
0xDC, 0xDA, 0x87, 0xFE, 0x24, 0x7B, 0xD2, 0x28, 0xAC, 0xEF, 0xF2, 0x9A, 0xEE, 0xC5, 0x8C, 0xB0, 0xEE, 0x6E, 0xC0, 0xB8,
0x8E, 0x4B, 0x39, 0x66, 0x8A, 0xEF, 0x33, 0xA2, 0xE5, 0x32, 0x1A, 0xFF, 0x00, 0x4C, 0xE8, 0xF8, 0xA9, 0x69, 0xBC, 0xF9,
0xDA, 0xDA, 0xE4, 0x28, 0xB3, 0xBC, 0xBB, 0x86, 0x68, 0xA4, 0xF7, 0xBC, 0xA6, 0x1F, 0xBF, 0x18, 0x60, 0xAD, 0x2F, 0xD4,
0xD7, 0xD1, 0x2F, 0xB3, 0xAF, 0xE6, 0xD3, 0x3B, 0x67, 0xD3, 0x9F, 0x48, 0xE9, 0xDD, 0x77, 0x31, 0x73, 0x61, 0x04, 0x7B,
0xB6, 0x1B, 0xA1, 0x7A, 0x7F, 0x23, 0x6F, 0x8C, 0x8A, 0x2B, 0x9A, 0xAE, 0xE6, 0xBC, 0xCC, 0xE4, 0x3A, 0xDA, 0xC7, 0x0B,
0xD8, 0x98, 0x4B, 0x68, 0x6B, 0xB5, 0x86, 0x9E, 0x78, 0xC3, 0xDB, 0xE4, 0xA6, 0xB9, 0xA2, 0x49, 0x22, 0x92, 0x8E, 0x22,
0xF9, 0x71, 0x20, 0x63, 0x8F, 0x52, 0xDF, 0x4B, 0xCF, 0x07, 0x35, 0x5F, 0x19, 0x24, 0xF3, 0x77, 0xC3, 0x5E, 0xEE, 0xD6,
0x35, 0x8D, 0x06, 0xE3, 0x9C, 0x75, 0x38, 0x9D, 0x16, 0xF7, 0x74, 0x9F, 0x75, 0xD1, 0xBB, 0x0A, 0xEA, 0xFE, 0xEA, 0x58,
0xEB, 0xC2, 0x75, 0xB6, 0xC7, 0x7D, 0x79, 0x95, 0xDB, 0x71, 0xDB, 0xD5, 0xAD, 0x31, 0xCF, 0x54, 0xD8, 0x8B, 0xD9, 0x6F,
0x78, 0xA7, 0x9B, 0x49, 0x63, 0x93, 0xF0, 0x1C, 0xC1, 0x2F, 0x20, 0xC1, 0x3E, 0x85, 0x7E, 0x2D, 0x74, 0x8F, 0x97, 0x1B,
0x57, 0x96, 0x9D, 0x59, 0xDD, 0xFA, 0x46, 0x1B, 0x6A, 0xC3, 0xE4, 0x3A, 0x53, 0x0B, 0x6F, 0x84, 0xCD, 0x4F, 0x8E, 0xC7,
0x49, 0xB5, 0xE8, 0x99, 0x6C, 0x86, 0xCF, 0x55, 0xBD, 0x1B, 0x4E, 0x85, 0x9F, 0xBB, 0xB3, 0xBA, 0xBB, 0xD6, 0x76, 0x4B,
0x2A, 0xA9, 0xA2, 0xAA, 0x67, 0x83, 0xF4, 0x4D, 0x4D, 0x1F, 0x66, 0xE2, 0x89, 0xAD, 0xEB, 0x92, 0x1A, 0xC3, 0x9D, 0x3E,
0x77, 0x78, 0x45, 0xDA, 0x3E, 0x07, 0xF7, 0x96, 0x5B, 0xA9, 0xBB, 0x02, 0x19, 0x72, 0x9A, 0xFD, 0xF7, 0x17, 0x19, 0xAE,
0xB2, 0xEC, 0x2B, 0x7B, 0x4A, 0xE0, 0xC2, 0x76, 0x0E, 0x9D, 0xCD, 0xCD, 0x51, 0x5B, 0x64, 0xED, 0x3F, 0x54, 0xB1, 0xD9,
0x66, 0xF1, 0xD5, 0x73, 0x4C, 0x19, 0x4C, 0x7D, 0x55, 0xD5, 0x2D, 0x95, 0xD7, 0xF9, 0xD7, 0x04, 0xB6, 0xF3, 0xCC, 0x1A,
0x5A, 0x09, 0x44, 0xFA, 0x03, 0xF8, 0x73, 0xE3, 0x0F, 0x93, 0x7D, 0x45, 0xE4, 0x06, 0x6F, 0xBE, 0xFA, 0x5F, 0x4D, 0xED,
0x0C, 0xB6, 0xAF, 0xD8, 0xFA, 0xC6, 0x2B, 0x5F, 0xBF, 0xD9, 0xAD, 0xAF, 0x26, 0xB8, 0xC5, 0xE3, 0xAF, 0x75, 0x89, 0x6E,
0xEE, 0xAD, 0x2D, 0xAB, 0xB5, 0xBC, 0xB5, 0xF6, 0x86, 0x6B, 0x9A, 0x38, 0xAF, 0x9E, 0x2A, 0xE2, 0xAF, 0xD5, 0xFD, 0x9E,
0xDE, 0xFC, 0xFB, 0x84, 0x62, 0x33, 0x10, 0xC7, 0x6F, 0x97, 0xCA, 0xC1, 0x0D, 0x1C, 0x47, 0x0C, 0x19, 0x2B, 0xE8, 0x62,
0x8E, 0x9F, 0x7F, 0x6A, 0x23, 0x8E, 0xE6, 0x5A, 0x23, 0xA3, 0x8F, 0x7F, 0x7E, 0x7D, 0xA9, 0xA6, 0x9E, 0x38, 0x07, 0x9A,
0x09, 0x48, 0xFA, 0x14, 0x7A, 0x66, 0xF5, 0x9F, 0x79, 0xF4, 0xF7, 0x72, 0x79, 0x05, 0xE4, 0x87, 0x5E, 0x61, 0x77, 0x3D,
0x47, 0xB2, 0xB1, 0x99, 0xDE, 0x9B, 0xEA, 0x5C, 0x5E, 0xC9, 0x88, 0xB0, 0xC8, 0xFE, 0x03, 0x1B, 0x17, 0xCE, 0xDB, 0x7E,
0xEC, 0xDD, 0x66, 0x4C, 0x84, 0x17, 0xB1, 0xE2, 0x76, 0x5B, 0x4C, 0xD5, 0x11, 0x63, 0x30, 0xD9, 0x6B, 0x7A, 0x62, 0xBF,
0xC6, 0x5D, 0x63, 0xEF, 0xF9, 0x8A, 0xBA, 0x7E, 0xE7, 0x1C, 0x82, 0x3D, 0xDE, 0x53, 0x78, 0xF1, 0xB9, 0x78, 0xA7, 0xDF,
0xFD, 0x9F, 0xD0, 0x7B, 0xD4, 0x33, 0x7E, 0xD8, 0xEB, 0xDD, 0x9A, 0xF7, 0x17, 0x67, 0x93, 0x92, 0xD6, 0x4B, 0x4B, 0x7D,
0x9B, 0x5B, 0x96, 0xAF, 0xC5, 0xEA, 0xDB, 0x6E, 0x3A, 0x29, 0x3D, 0xF9, 0xE3, 0x1D, 0xB3, 0xE0, 0x27, 0xB7, 0xBD, 0x8B,
0x8F, 0x7E, 0x79, 0x8F, 0x89, 0xBE, 0xDD, 0x5E, 0xD5, 0xD1, 0x57, 0x1C, 0x06, 0xBF, 0x82, 0x57, 0x7F, 0x4E, 0xFF, 0x00,
0x8E, 0x9E, 0x3E, 0x77, 0x67, 0x4E, 0x79, 0x17, 0x94, 0xEE, 0x5E, 0x8A, 0xE9, 0xBE, 0xDB, 0xC9, 0xE1, 0x3B, 0x33, 0x54,
0xB0, 0xC2, 0xE4, 0x7B, 0x3B, 0xAC, 0x74, 0x9D, 0xF6, 0xFB, 0x11, 0x63, 0x71, 0xAB, 0x4F, 0x71, 0x71, 0x65, 0x8B, 0xBB,
0xDA, 0xB0, 0x79, 0x6B, 0x8C, 0x7D, 0xA4, 0xF7, 0x1C, 0x71, 0x5D, 0x71, 0xC5, 0x55, 0x14, 0x55, 0x5F, 0x1F, 0x2E, 0x78,
0xE7, 0x9F, 0xCC, 0x11, 0x57, 0xCD, 0xC7, 0x1C, 0x59, 0x9C, 0xBC, 0x51, 0x51, 0x44, 0x51, 0x45, 0x93, 0xBF, 0x8E, 0x38,
0xE3, 0xA7, 0x8A, 0x23, 0x8E, 0x3A, 0x2E, 0xA5, 0xA6, 0x8A, 0x28, 0xA2, 0x9E, 0x38, 0xA6, 0x8A, 0x28, 0xA7, 0x8E, 0x38,
0xE3, 0x8E, 0x38, 0xF6, 0xE3, 0x80, 0x79, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x57, 0xE8, 0x6F, 0xDF, 0x97, 0x4C, 0xFF, 0x00, 0x15, 0xFA, 0xEF, 0xF9,
0xBF, 0x0E, 0x0B, 0x0F, 0x7C, 0xC4, 0xF3, 0x8B, 0xAA, 0x7C, 0x4D, 0xED, 0x0F, 0x1B, 0x7A, 0xDB, 0xBC, 0xEC, 0x31, 0x90,
0x75, 0x47, 0x93, 0x95, 0xF6, 0x96, 0x95, 0x9E, 0xDD, 0x33, 0x1F, 0x66, 0x6C, 0x26, 0x9F, 0x96, 0xC1, 0x45, 0xA0, 0xC1,
0x84, 0xE7, 0x6D, 0xB2, 0xBB, 0xA2, 0x4B, 0x09, 0x34, 0x5D, 0x82, 0x3D, 0xBE, 0xEA, 0xD3, 0x29, 0x3C, 0xDC, 0x73, 0x1D,
0x9F, 0x15, 0x45, 0x34, 0xBE, 0xD6, 0xB4, 0xDC, 0x55, 0x48, 0x45, 0x0F, 0xD6, 0x3B, 0xD2, 0xB2, 0xEF, 0xC3, 0xFD, 0xBA,
0x6E, 0xFE, 0xE8, 0xEC, 0x54, 0xF9, 0x2F, 0x17, 0x7B, 0x0B, 0x33, 0xF2, 0x92, 0xCA, 0xC2, 0x9A, 0xEE, 0xFF, 0x00, 0xE0,
0xBE, 0xD1, 0x99, 0x9A, 0xB9, 0xAD, 0xB5, 0x8B, 0xE9, 0x69, 0xAA, 0x5A, 0xEB, 0xD1, 0x33, 0x32, 0x57, 0xED, 0x83, 0xC8,
0x57, 0xCF, 0x34, 0xC3, 0x5F, 0x3C, 0x58, 0x5C, 0x55, 0xC4, 0xDF, 0x84, 0x96, 0xF4, 0x34, 0xC3, 0xD2, 0x8B, 0xFC, 0x46,
0x3C, 0x46, 0xFE, 0x2D, 0xE3, 0x3F, 0xE9, 0xD9, 0x30, 0x74, 0xFF, 0x00, 0xEA, 0x34, 0xD7, 0x72, 0x9B, 0x7F, 0x9E, 0x1E,
0x3B, 0x6A, 0x78, 0x48, 0xA8, 0xB8, 0xCD, 0x6D, 0x1E, 0x3F, 0xE9, 0x3A, 0xEE, 0x22, 0x09, 0x24, 0xA6, 0x18, 0xE6, 0xCA,
0x66, 0xBB, 0x93, 0xB4, 0x71, 0xB8, 0xF8, 0xAB, 0x96, 0xAF, 0xD3, 0x15, 0x12, 0x5D, 0xDC, 0xD1, 0xC7, 0x35, 0x73, 0xF9,
0x53, 0xC7, 0x3E, 0xE0, 0xEA, 0xF7, 0xA8, 0x47, 0x67, 0xD9, 0xFA, 0x4A, 0xFA, 0x63, 0xF5, 0xFF, 0x00, 0x49, 0x78, 0xF7,
0x3C, 0x78, 0x1D, 0xEB, 0x3D, 0x06, 0x2F, 0xA5, 0xB4, 0xCD, 0xA2, 0xCE, 0xDE, 0x2B, 0x7C, 0x95, 0xAE, 0x46, 0xFB, 0x15,
0x7F, 0x9E, 0xED, 0x4E, 0xD8, 0xAE, 0x88, 0xFE, 0xD7, 0x1C, 0x6C, 0xB9, 0x4A, 0xA1, 0xBB, 0x96, 0x39, 0xE9, 0xE7, 0xE5,
0x6B, 0x95, 0xCB, 0x43, 0x35, 0x34, 0xF3, 0x44, 0x3F, 0x00, 0x41, 0x76, 0x69, 0xA6, 0xB9, 0x9A, 0x5B, 0x8B, 0x89, 0x64,
0x9E, 0xE2, 0x79, 0x2B, 0x9A, 0x79, 0xE6, 0xAE, 0xB9, 0x66, 0x9A, 0x69, 0x6B, 0xE6, 0xB9, 0x65, 0x96, 0x5A, 0xF9, 0xAA,
0xB9, 0x24, 0x92, 0xBA, 0xB9, 0xE6, 0xAA, 0xB9, 0xE7, 0x9E, 0x79, 0xE7, 0x9F, 0x7E, 0x41, 0xFC, 0xC1, 0xDF, 0xBF, 0x40,
0x9F, 0x36, 0x76, 0xBE, 0x97, 0xF2, 0x73, 0x13, 0xE3, 0x0E, 0xC5, 0x99, 0xBC, 0xBC, 0xE9, 0xDF, 0x21, 0x6E, 0xAE, 0xB1,
0xD8, 0xEC, 0x3D, 0xD4, 0xF2, 0x4D, 0x65, 0xA9, 0x76, 0xBD, 0xAE, 0x36, 0x7B, 0xBD, 0x73, 0x3F, 0x88, 0x8A, 0x49, 0x79,
0xA2, 0xC3, 0x8D, 0xAE, 0x3B, 0x1E, 0x71, 0x17, 0xF1, 0xC3, 0x47, 0x1F, 0x8B, 0x96, 0x6B, 0x29, 0x24, 0xE7, 0xDA, 0xD6,
0x90, 0x7C, 0x6F, 0xAF, 0xBF, 0x8B, 0x9A, 0xF7, 0x40, 0x79, 0x9F, 0x47, 0x60, 0x69, 0x58, 0xEB, 0x6C, 0x4E, 0xA9, 0xE4,
0x7E, 0xB3, 0x37, 0x65, 0xDD, 0xE3, 0x2D, 0x22, 0xAE, 0x1B, 0x5B, 0x2E, 0xC5, 0xB5, 0xCB, 0x5C, 0xE2, 0xFB, 0x0E, 0xAB,
0x68, 0xFE, 0x1C, 0xC5, 0xF6, 0xF3, 0x97, 0x75, 0x5A, 0x66, 0x26, 0xE7, 0x8A, 0xF9, 0xE7, 0x9B, 0xDC, 0x9C, 0xFC, 0x7C,
0x28, 0xA3, 0x88, 0xF8, 0xA8, 0x38, 0x6C, 0x09, 0x2C, 0x7D, 0x34, 0x7D, 0x9B, 0xA8, 0x6B, 0x9E, 0x41, 0xF7, 0xF7, 0x58,
0x66, 0xB2, 0x56, 0x76, 0x1B, 0x5F, 0x66, 0xF5, 0xBE, 0xB5, 0x95, 0xD2, 0xED, 0xEE, 0xE6, 0xA2, 0x09, 0x33, 0x55, 0x75,
0xF6, 0x5F, 0x31, 0x75, 0xB0, 0xE2, 0x71, 0xBC, 0x49, 0x4F, 0x1F, 0x8B, 0xC9, 0x51, 0x8B, 0xD8, 0xF8, 0xBE, 0xFB, 0x14,
0x55, 0xF7, 0x39, 0xB4, 0xB2, 0x9E, 0x5E, 0x29, 0xAA, 0x88, 0x64, 0xAA, 0x80, 0xD3, 0x5F, 0x58, 0x8F, 0x07, 0x7C, 0x87,
0xE8, 0xCF, 0x2B, 0xBB, 0xCB, 0xBB, 0x36, 0x2D, 0x4F, 0x66, 0xDA, 0x7A, 0x67, 0xB8, 0xBB, 0x2F, 0x69, 0xEC, 0x6D, 0x5F,
0xB5, 0xF1, 0x76, 0x17, 0xB9, 0x8D, 0x67, 0x19, 0x1E, 0xE9, 0x97, 0xB9, 0xCE, 0x53, 0xA5, 0x6C, 0xF9, 0x3B, 0x68, 0xA6,
0x8B, 0x54, 0xCB, 0xEB, 0x72, 0xDE, 0x57, 0x61, 0x69, 0x6F, 0x7D, 0xCC, 0x1C, 0x5E, 0x5A, 0x5A, 0x53, 0x25, 0xAF, 0x32,
0x51, 0x4D, 0x7F, 0x6C, 0x38, 0xE2, 0x0E, 0xBD, 0x7A, 0x12, 0xFF, 0x00, 0x89, 0xEF, 0x8F, 0xBF, 0xE9, 0x3D, 0xCB, 0xFF,
0x00, 0x64, 0xFB, 0x04, 0x19, 0x07, 0xEA, 0x0D, 0xFF, 0x00, 0x11, 0xAD, 0xA3, 0xF8, 0x51, 0xD5, 0x9F, 0xF4, 0x8B, 0xC0,
0x77, 0x77, 0x44, 0xBF, 0xC6, 0x7A, 0x47, 0xFA, 0x2E, 0xE3, 0xBB, 0x13, 0x5D, 0xC2, 0xD9, 0xC3, 0xDC, 0x7B, 0x2E, 0x83,
0xAE, 0x6E, 0x57, 0x12, 0x5D, 0x58, 0x5B, 0xCD, 0x71, 0x91, 0xEF, 0x7E, 0xF1, 0x87, 0x17, 0x4E, 0x1E, 0x4D, 0x82, 0x2E,
0x7D, 0xAD, 0xF2, 0x36, 0x9D, 0x69, 0x8F, 0xC9, 0x5B, 0xC3, 0x24, 0x55, 0x57, 0xC5, 0x17, 0x16, 0x18, 0x1E, 0x63, 0xA7,
0x9E, 0x6B, 0x97, 0xDE, 0xA0, 0x84, 0x2E, 0xD1, 0xB4, 0x6C, 0x9B, 0xBE, 0xC9, 0x9D, 0xDC, 0x37, 0x0C, 0xEE, 0x57, 0x67,
0xDA, 0xB6, 0x7C, 0xAD, 0xF6, 0x73, 0x61, 0xD8, 0x73, 0x97, 0xD7, 0x19, 0x2C, 0xC6, 0x6B, 0x31, 0x92, 0xB8, 0x92, 0xEA,
0xFF, 0x00, 0x25, 0x92, 0xBF, 0xBA, 0x92, 0x5B, 0x8B, 0xBB, 0xCB, 0xBB, 0x89, 0x6A, 0xAE, 0xBA, 0xEB, 0xAB, 0x9A, 0xAA,
0xAB, 0x90, 0x78, 0x20, 0xF4, 0x79, 0xCC, 0x65, 0xAA, 0xC4, 0xC7, 0x80, 0xAB, 0x29, 0x91, 0xAB, 0x05, 0x16, 0x46, 0x6C,
0xC4, 0x58, 0x5E, 0x6F, 0x6E, 0x79, 0xC4, 0xC7, 0x96, 0xB8, 0xB6, 0x82, 0xCA, 0x7C, 0xA4, 0x78, 0xEE, 0x65, 0xFC, 0x1D,
0x19, 0x19, 0xEC, 0xED, 0xA3, 0x8A, 0xB9, 0xF8, 0xA3, 0x89, 0x6A, 0x8A, 0x3A, 0x69, 0xE6, 0xAE, 0x69, 0xA7, 0x8E, 0x38,
0x09, 0x2E, 0x7D, 0x32, 0x7F, 0xBF, 0x7F, 0x26, 0xFF, 0x00, 0x84, 0x9A, 0x9F, 0xF3, 0x8D, 0x40, 0xEB, 0x6E, 0xED, 0xB1,
0xF8, 0xBF, 0xEA, 0xF5, 0x8F, 0xF2, 0xB7, 0xC1, 0xAE, 0xCD, 0x82, 0xCB, 0x47, 0xEF, 0xBF, 0x1B, 0xBB, 0x6B, 0xB2, 0xF0,
0x1A, 0x96, 0x46, 0x3A, 0x60, 0xBC, 0xD8, 0xF0, 0xF6, 0x7A, 0x96, 0xDF, 0x93, 0xD6, 0x75, 0x5E, 0xE0, 0xD1, 0x3F, 0x15,
0x25, 0xBC, 0xD9, 0x7C, 0x3D, 0xD5, 0xBD, 0xBD, 0xBD, 0x86, 0xD1, 0x8B, 0xE2, 0x4A, 0x69, 0xE2, 0x49, 0x7E, 0x15, 0xF3,
0x0D, 0x37, 0x16, 0x17, 0x14, 0x84, 0x28, 0x7C, 0x99, 0xF1, 0xAF, 0xB5, 0x7C, 0x4A, 0xEE, 0x5D, 0xBB, 0xA3, 0xBB, 0x8B,
0x07, 0x56, 0x1B, 0x6D, 0xD5, 0x6E, 0xB8, 0xE6, 0x0B, 0xBB, 0x7F, 0xBB, 0x36, 0x0B, 0x69, 0xC0, 0x5D, 0x55, 0x25, 0x58,
0x5D, 0xBF, 0x55, 0xC8, 0xC9, 0x14, 0x3C, 0x65, 0x35, 0xCC, 0xED, 0xB4, 0x7C, 0xD7, 0x04, 0xBF, 0x1A, 0x24, 0x8A, 0x4A,
0x64, 0xB7, 0x9E, 0x88, 0x6E, 0x61, 0x9A, 0x18, 0xC2, 0x54, 0x7F, 0x4C, 0x97, 0xEE, 0x37, 0xC9, 0xEF, 0xE2, 0xBE, 0x9D,
0xFC, 0xA1, 0x70, 0x08, 0x7B, 0x67, 0xFF, 0x00, 0xBF, 0x73, 0x5F, 0xEA, 0xD9, 0x1F, 0xF7, 0x93, 0x03, 0x28, 0xF8, 0xED,
0xD1, 0x9B, 0x97, 0x92, 0xDD, 0xDF, 0xD6, 0x5D, 0x13, 0xA0, 0xC1, 0xCC, 0xBB, 0x47, 0x65, 0xED, 0x78, 0xDD, 0x72, 0xD2,
0xE3, 0x98, 0x6A, 0xB8, 0xB7, 0xC3, 0xD8, 0xCF, 0x5F, 0x33, 0xE7, 0x36, 0x4C, 0x84, 0x54, 0x57, 0x1D, 0x75, 0x62, 0xB5,
0x8C, 0x1D, 0xBD, 0xCE, 0x42, 0xEF, 0xE3, 0x57, 0x15, 0x7E, 0x1A, 0xDA, 0xBF, 0x8F, 0xBD, 0x5E, 0xDC, 0x72, 0x12, 0xE5,
0xF5, 0x32, 0xF3, 0xA6, 0xC7, 0xD2, 0xDB, 0x59, 0xF0, 0xCB, 0xC4, 0xDF, 0x18, 0x78, 0x86, 0xDE, 0xEB, 0xAF, 0x26, 0xD1,
0xB7, 0x2D, 0xDF, 0x09, 0xC4, 0xF1, 0xF1, 0x3D, 0xFF, 0x00, 0x4C, 0x68, 0xD7, 0x3F, 0xB2, 0xE0, 0xD3, 0xF3, 0xF7, 0x54,
0x53, 0x54, 0x94, 0xDF, 0xF7, 0x16, 0x56, 0xDA, 0xFE, 0x6C, 0x85, 0xDF, 0x14, 0xF1, 0x71, 0xFF, 0x00, 0xA4, 0x92, 0x5E,
0x7F, 0x3B, 0x9E, 0x2A, 0xE4, 0x30, 0xE7, 0xAF, 0x17, 0x8F, 0x9A, 0x8F, 0x92, 0xFE, 0x35, 0xF4, 0xAF, 0xA8, 0xFF, 0x00,
0x47, 0x51, 0x46, 0x77, 0x1F, 0x8C, 0xD5, 0x75, 0x8B, 0x4D, 0xC7, 0x23, 0x65, 0x6F, 0x4F, 0x17, 0x39, 0x7E, 0xA0, 0xDF,
0x2B, 0x8B, 0x23, 0xA5, 0x67, 0xF2, 0x50, 0xC3, 0xF7, 0x6B, 0x82, 0xFB, 0x49, 0xDA, 0x33, 0x35, 0x59, 0x5D, 0xC5, 0x57,
0x3C, 0xCB, 0x07, 0x19, 0x6A, 0xE9, 0x97, 0x9A, 0x78, 0xB4, 0xE7, 0x8A, 0x42, 0x22, 0xC0, 0x98, 0xFF, 0x00, 0xD3, 0x25,
0xFB, 0x8D, 0xF2, 0x7B, 0xF8, 0xAF, 0xA7, 0x7F, 0x28, 0x5C, 0x02, 0x1E, 0xD9, 0xFF, 0x00, 0xEF, 0xDC, 0xD7, 0xFA, 0xB6,
0x47, 0xFD, 0xE4, 0xC0, 0xF2, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xFF, 0xD5, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x57, 0xE8, 0x6F, 0xDF, 0x97, 0x4C, 0xFF, 0x00, 0x15, 0xFA, 0xEF, 0xF9, 0xBF, 0x0E,
0x09, 0x43, 0xFD, 0x4F, 0xFF, 0x00, 0xDC, 0x5E, 0x17, 0x7F, 0xAB, 0x77, 0xF7, 0xFB, 0x3E, 0x9D, 0x06, 0x35, 0xF4, 0x75,
0xF5, 0x2C, 0xD3, 0xFB, 0x1B, 0x50, 0x87, 0xD3, 0x8F, 0xCD, 0x6A, 0xF1, 0x7B, 0x76, 0x89, 0xBA, 0x62, 0x64, 0xD0, 0xFA,
0x83, 0x66, 0xDE, 0x64, 0xA6, 0xF3, 0x17, 0x90, 0xC5, 0x5F, 0xDB, 0xD1, 0x63, 0x6B, 0xD2, 0x7B, 0x75, 0xCD, 0xE5, 0x5C,
0x7C, 0x6D, 0xA4, 0xA3, 0x8E, 0x28, 0xD6, 0x6F, 0x2B, 0x92, 0x99, 0x20, 0x9B, 0x8A, 0x2C, 0x28, 0xAE, 0x9E, 0x78, 0xB0,
0xA6, 0x80, 0xF8, 0x7C, 0x7F, 0xA6, 0xA6, 0xE3, 0xE0, 0x17, 0xAB, 0x8F, 0x89, 0x17, 0x78, 0x58, 0xF2, 0x5B, 0x27, 0x8E,
0x9D, 0x85, 0xDE, 0x11, 0x4B, 0xD5, 0x1B, 0xC4, 0xD4, 0x57, 0x71, 0x3E, 0x2A, 0x4E, 0x71, 0x99, 0x7B, 0xD9, 0x3A, 0xE7,
0x72, 0xB9, 0xA2, 0x2A, 0x61, 0x83, 0x6D, 0xC1, 0xDA, 0xD3, 0x5F, 0xD8, 0x9B, 0xF4, 0xC7, 0x98, 0xB1, 0x8B, 0xF1, 0x51,
0x53, 0x4C, 0x94, 0x5D, 0x5B, 0xDA, 0x87, 0xD9, 0x7A, 0xE9, 0x6C, 0xB8, 0x9D, 0x2F, 0xD5, 0x33, 0xC2, 0x3D, 0xC7, 0x3D,
0x2C, 0x30, 0x60, 0xF5, 0x3D, 0x17, 0xA4, 0xB6, 0x5C, 0xCC, 0xF7, 0x32, 0xC7, 0x05, 0xBC, 0x38, 0x9C, 0x17, 0x91, 0x5B,
0xEE, 0x53, 0x23, 0x2C, 0xF3, 0x4D, 0xFD, 0x4C, 0x30, 0xC7, 0x67, 0x6B, 0x5F, 0x35, 0x55, 0x5F, 0xE9, 0xA6, 0x9E, 0x39,
0xE7, 0x9F, 0xC8, 0x1B, 0x0F, 0xF5, 0x36, 0x68, 0xF9, 0xCC, 0xB7, 0x4D, 0xF8, 0xB7, 0xD9, 0x56, 0x34, 0x49, 0x36, 0xB5,
0xA8, 0x76, 0x26, 0xFF, 0x00, 0xA9, 0xE6, 0x64, 0x86, 0x3A, 0xE5, 0x86, 0x2C, 0x87, 0x60, 0x6B, 0x9A, 0xFE, 0x57, 0x01,
0x71, 0x34, 0xB4, 0x55, 0xCC, 0x71, 0x45, 0x54, 0x3D, 0x7F, 0x7B, 0x45, 0x35, 0x55, 0xC7, 0xB5, 0x55, 0x49, 0xC7, 0x1C,
0x55, 0xC7, 0x3C, 0xFB, 0x54, 0x10, 0xEA, 0x00, 0x1B, 0xDD, 0xE9, 0x87, 0xA2, 0xE7, 0xFB, 0x0F, 0xD4, 0x1B, 0xC4, 0x1C,
0x1E, 0xB9, 0x1C, 0xF2, 0x5E, 0xE2, 0xFB, 0xDB, 0x41, 0xDE, 0xAF, 0x79, 0x82, 0x89, 0xAB, 0xE6, 0x2C, 0x07, 0x5A, 0x66,
0xAD, 0xFB, 0x0B, 0x66, 0x92, 0x5E, 0x60, 0xA6, 0xAA, 0xA8, 0x83, 0xFA, 0x3F, 0xAC, 0x5C, 0xF1, 0x5D, 0x55, 0x7B, 0x51,
0xED, 0x57, 0xEA, 0xE7, 0x8E, 0x39, 0xE4, 0x1D, 0x98, 0xFA, 0x9C, 0xF6, 0x9C, 0x35, 0xDF, 0x6B, 0x78, 0xA1, 0xA4, 0xC1,
0x24, 0x1C, 0xEC, 0x1A, 0xFF, 0x00, 0x5E, 0xF6, 0x56, 0xD3, 0x93, 0x86, 0x9F, 0x87, 0xE2, 0x68, 0xC3, 0x6E, 0x3B, 0x26,
0xB5, 0x89, 0xC1, 0x49, 0x2F, 0xB7, 0x3F, 0x73, 0xEC, 0x4B, 0x7B, 0xA3, 0x64, 0x78, 0x8F, 0xDF, 0x8E, 0x29, 0xF9, 0x51,
0x5F, 0xB7, 0xBF, 0x3E, 0xFE, 0xC1, 0x17, 0x90, 0x7D, 0x36, 0x9B, 0xB9, 0xED, 0xBD, 0x77, 0xB5, 0x60, 0x37, 0x9D, 0x13,
0x64, 0xCC, 0xEA, 0x1B, 0x8E, 0xAD, 0x93, 0xB6, 0xCC, 0xEB, 0x9B, 0x36, 0xBD, 0x90, 0xB9, 0xC5, 0x66, 0xB0, 0xB9, 0x4B,
0x3A, 0xFE, 0xE5, 0xBD, 0xF6, 0x3F, 0x21, 0x67, 0x24, 0x57, 0x16, 0xD3, 0xC7, 0x57, 0xE5, 0xEF, 0x4D, 0x5F, 0xAA, 0x9E,
0x79, 0xA7, 0x9F, 0x7E, 0x39, 0xE7, 0x8E, 0x42, 0x45, 0x7E, 0x34, 0x7D, 0x47, 0xDD, 0xDF, 0xA7, 0xD9, 0xE3, 0xF5, 0x4F,
0x2A, 0x3A, 0xA7, 0x59, 0xEF, 0x0C, 0x04, 0x76, 0x3F, 0xB3, 0x6F, 0x77, 0x5D, 0x42, 0xBB, 0x7D, 0x0F, 0xB0, 0x2E, 0x69,
0xAA, 0x9A, 0x29, 0x92, 0xFF, 0x00, 0x3B, 0x87, 0xAA, 0xDA, 0xFB, 0x43, 0xD9, 0xA4, 0x96, 0x1E, 0x2B, 0x8A, 0xBB, 0x7B,
0x6B, 0x5C, 0x0D, 0x15, 0xFC, 0xF8, 0xAF, 0x99, 0x39, 0xE6, 0x9E, 0x69, 0x90, 0x3A, 0x21, 0xB8, 0xF8, 0x41, 0xE9, 0xB5,
0xEA, 0xFD, 0xD0, 0x9B, 0x17, 0x75, 0x78, 0x85, 0x8E, 0xD7, 0xBA, 0x77, 0xB8, 0x21, 0xAE, 0xFA, 0x9A, 0xB2, 0xFA, 0xCE,
0xBB, 0x69, 0xA3, 0x5D, 0xE1, 0xB7, 0xD9, 0xED, 0xE8, 0xC9, 0x73, 0xAD, 0xF7, 0x77, 0x59, 0xE1, 0xB9, 0xAF, 0x09, 0x77,
0x1E, 0x6E, 0x4E, 0x79, 0xAA, 0x4C, 0xB5, 0x8D, 0x32, 0x4F, 0x2D, 0x72, 0x57, 0x73, 0x6F, 0x7B, 0x77, 0x4D, 0x12, 0xC3,
0x28, 0x70, 0xFB, 0xD1, 0x67, 0x50, 0xD8, 0x7A, 0xF7, 0xD5, 0xBB, 0xAA, 0x74, 0x1D, 0xBB, 0x1D, 0x26, 0x23, 0x6B, 0xD1,
0xEF, 0xFC, 0x82, 0xD4, 0x36, 0x7C, 0x4C, 0xD5, 0x51, 0x5C, 0xD8, 0xBD, 0x87, 0x5A, 0xEA, 0x8E, 0xCB, 0xC2, 0xE6, 0xB1,
0xD2, 0xD7, 0x1D, 0x55, 0xC7, 0x54, 0x96, 0x59, 0x2B, 0x29, 0x62, 0xAB, 0x9A, 0x79, 0xE6, 0x9E, 0x79, 0xA7, 0xF2, 0xE7,
0x9E, 0x01, 0xED, 0xFD, 0x41, 0xBF, 0xE2, 0x35, 0xB4, 0x7F, 0x0A, 0x3A, 0xB3, 0xFE, 0x91, 0x78, 0x0E, 0xDB, 0xFA, 0xD6,
0x63, 0x2E, 0x3B, 0x67, 0xD2, 0x2B, 0xAA, 0xBB, 0x13, 0x47, 0x8F, 0x8B, 0x9D, 0x5B, 0x05, 0x9A, 0xF1, 0xDF, 0xB5, 0xAF,
0x2B, 0xB1, 0xF6, 0x9E, 0xDA, 0x8D, 0x2F, 0x62, 0xD2, 0x72, 0x5A, 0xBE, 0x2E, 0xE6, 0x3A, 0xED, 0x7D, 0xA1, 0xAA, 0xCE,
0x9C, 0x96, 0xFF, 0x00, 0x8E, 0xE7, 0x8A, 0xF8, 0xE3, 0xED, 0xFC, 0x79, 0xE3, 0x9E, 0x3D, 0xB8, 0xF6, 0xE7, 0x80, 0x84,
0x68, 0x24, 0x81, 0xA0, 0xFD, 0x47, 0xFD, 0xDF, 0xA0, 0xE8, 0x9A, 0x56, 0x89, 0x67, 0xE3, 0x7F, 0x55, 0x64, 0x2D, 0x34,
0xAD, 0x4B, 0x5C, 0xD4, 0xAD, 0x6F, 0xEE, 0x76, 0x9D, 0xBA, 0x2B, 0x9B, 0xDB, 0x6D, 0x73, 0x0F, 0x67, 0x87, 0x82, 0xEE,
0xE2, 0x38, 0xA9, 0xFB, 0x51, 0xCD, 0x73, 0x15, 0x9F, 0x15, 0xD7, 0x4D, 0x3F, 0xA7, 0x8A, 0xAA, 0xE7, 0x8E, 0x3F, 0x20,
0x77, 0xBB, 0xC6, 0x5F, 0x30, 0xF6, 0x5F, 0x39, 0xBD, 0x36, 0xBB, 0x9B, 0xBE, 0xB6, 0xCD, 0x3B, 0x07, 0xA2, 0xE6, 0x2F,
0xF4, 0xBE, 0xFF, 0x00, 0xD5, 0xEB, 0xC0, 0xEB, 0xB7, 0xD7, 0xF9, 0x1C, 0x6D, 0x10, 0x6B, 0x7A, 0x8E, 0x4E, 0x08, 0x2E,
0xA8, 0xB9, 0xC9, 0x71, 0xC5, 0xD7, 0x32, 0xDC, 0xD3, 0x3F, 0x3C, 0xD7, 0x4F, 0x3F, 0xA7, 0x8E, 0x78, 0xFC, 0x81, 0xC3,
0xEF, 0xA6, 0x4F, 0xF7, 0xEF, 0xE4, 0xDF, 0xF0, 0x93, 0x53, 0xFE, 0x71, 0xA8, 0x1C, 0xA7, 0xF2, 0xB3, 0xB8, 0xBB, 0x1B,
0xA0, 0x3D, 0x50, 0x7C, 0x9C, 0xED, 0xFE, 0xA6, 0xD9, 0xAF, 0xB5, 0x0D, 0xFF, 0x00, 0x47, 0xF2, 0xC7, 0xBA, 0x72, 0xD8,
0x1C, 0xD5, 0x85, 0x7F, 0x9D, 0x12, 0x71, 0xD8, 0x3B, 0x1C, 0x17, 0x76, 0x17, 0xD6, 0xF5, 0x7B, 0xC1, 0x91, 0xC3, 0xE5,
0xAC, 0x66, 0x96, 0xD6, 0xF6, 0xD2, 0x6A, 0x6B, 0x82, 0xEE, 0xD6, 0x69, 0x22, 0x92, 0x9A, 0xA8, 0xAF, 0x9E, 0x39, 0x09,
0x24, 0x66, 0xF1, 0xBE, 0x3F, 0xFD, 0x40, 0x3E, 0x16, 0xF1, 0x9C, 0xC1, 0x51, 0xAF, 0x75, 0xAF, 0x98, 0x7D, 0x3D, 0x67,
0xF6, 0x68, 0x86, 0x79, 0x2A, 0x92, 0xE7, 0x47, 0xDC, 0x2E, 0x20, 0xAA, 0x7A, 0xB0, 0x79, 0x39, 0x78, 0xA2, 0x5C, 0xC6,
0x5B, 0xA5, 0x3B, 0x32, 0xAB, 0x4A, 0xEA, 0xB4, 0xB9, 0xE2, 0x99, 0xA4, 0xC7, 0xDC, 0xD1, 0xCD, 0x7C, 0x71, 0x2D, 0xCD,
0x95, 0xCD, 0xBC, 0xE1, 0xFE, 0x7D, 0x3C, 0x5D, 0x61, 0xBE, 0x74, 0xBE, 0x9F, 0xE6, 0x67, 0x55, 0xF6, 0x76, 0xB5, 0x91,
0xD4, 0x37, 0xDD, 0x1B, 0xBC, 0x35, 0x7C, 0x0E, 0xCD, 0xAF, 0x65, 0x22, 0xFB, 0x77, 0x56, 0x17, 0xF6, 0xDA, 0x84, 0xB5,
0xD3, 0x55, 0x15, 0xD3, 0xCD, 0x50, 0xDD, 0xD8, 0xDE, 0xDB, 0x49, 0x1D, 0xC5, 0xAD, 0xCC, 0x35, 0x57, 0x6F, 0x77, 0x6B,
0x2C, 0x73, 0x43, 0x5D, 0x71, 0x49, 0x45, 0x75, 0x04, 0x2E, 0x73, 0xFF, 0x00, 0xDF, 0xB9, 0xAF, 0xF5, 0x6C, 0x8F, 0xFB,
0xC9, 0x81, 0x2A, 0xDF, 0x40, 0x5F, 0x1C, 0x75, 0x4E, 0x94, 0xE9, 0xEE, 0xEB, 0xF5, 0x22, 0xEE, 0xEE, 0x6D, 0xF0, 0x1A,
0xE6, 0x3F, 0x5A, 0xDA, 0xB5, 0xDE, 0xBF, 0xCC, 0x65, 0x21, 0xE7, 0xED, 0xE2, 0x3A, 0xFB, 0x4E, 0xA2, 0xBC, 0x9F, 0x6A,
0x6F, 0x36, 0x74, 0x55, 0x4D, 0x7F, 0x7E, 0xBC, 0xA6, 0x53, 0x13, 0x46, 0x1E, 0xCE, 0xA8, 0x7E, 0x37, 0x3C, 0x55, 0x8E,
0xBE, 0x83, 0x8E, 0x2A, 0xA6, 0xE7, 0x8E, 0x2A, 0x0F, 0xE9, 0xDE, 0x1E, 0x5E, 0x7D, 0x3E, 0xBE, 0x48, 0xF6, 0x66, 0xC3,
0xDC, 0x3D, 0xD9, 0xD6, 0x5D, 0xCF, 0xBD, 0xF6, 0x36, 0xD3, 0xC6, 0x3A, 0x9C, 0xDE, 0xC5, 0x77, 0x77, 0xE4, 0x16, 0x27,
0xF1, 0x11, 0x62, 0x31, 0x96, 0x98, 0x7C, 0x6D, 0xB5, 0xAE, 0x23, 0x5E, 0xED, 0xBC, 0x46, 0x0B, 0x15, 0x67, 0x67, 0x8E,
0xB1, 0x8A, 0x3A, 0x21, 0xB4, 0xB5, 0x82, 0x2E, 0x3E, 0x3C, 0xD5, 0xF1, 0xF9, 0xD5, 0x55, 0x55, 0x07, 0x4A, 0x3C, 0x1C,
0xF2, 0x83, 0xD3, 0x33, 0xC9, 0xDE, 0xB9, 0xD9, 0xBC, 0x01, 0xF1, 0xA6, 0xCF, 0x6D, 0xB4, 0xEB, 0xD8, 0xBA, 0xC7, 0x76,
0xA6, 0x4E, 0xB1, 0xEC, 0x28, 0xF7, 0x69, 0xA9, 0xBB, 0xD1, 0x76, 0x5C, 0x8C, 0x96, 0x9B, 0x7D, 0x96, 0xBB, 0xB0, 0x76,
0x06, 0xC7, 0xB5, 0x66, 0x2E, 0xE4, 0xB5, 0xBD, 0xDA, 0xF9, 0x9E, 0x8B, 0x5E, 0x2E, 0xF9, 0xAE, 0xD2, 0x89, 0x39, 0x96,
0xDE, 0x8A, 0x63, 0x86, 0xAE, 0x63, 0x08, 0x4F, 0xF9, 0x71, 0xE3, 0x7E, 0xD9, 0xE2, 0x57, 0x91, 0x3D, 0xA5, 0xD0, 0x5B,
0x85, 0x17, 0x12, 0x5E, 0x68, 0x5B, 0x2D, 0xD5, 0xA6, 0x1B, 0x2F, 0x34, 0x15, 0x41, 0x1E, 0xCF, 0xA8, 0xDE, 0xFB, 0x64,
0x35, 0x1D, 0xAA, 0xD7, 0x8E, 0x69, 0xA6, 0x3E, 0x60, 0xCF, 0xEB, 0xD7, 0x36, 0xF7, 0x15, 0x53, 0x47, 0x35, 0x71, 0x0C,
0xD5, 0x57, 0x17, 0x3C, 0xFC, 0xE3, 0xAB, 0x8E, 0x02, 0x52, 0x1F, 0x4C, 0x97, 0xEE, 0x37, 0xC9, 0xEF, 0xE2, 0xBE, 0x9D,
0xFC, 0xA1, 0x70, 0x08, 0x7B, 0x67, 0xFF, 0x00, 0xBF, 0x73, 0x5F, 0xEA, 0xD9, 0x1F, 0xF7, 0x93, 0x03, 0xC9, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD6, 0xAF,
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x57,
0xE8, 0x6F, 0xDF, 0x97, 0x4C, 0xFF, 0x00, 0x15, 0xFA, 0xEF, 0xF9, 0xBF, 0x0E, 0x09, 0x43, 0xFD, 0x4F, 0xFF, 0x00, 0xDC,
0x5E, 0x17, 0x7F, 0xAB, 0x77, 0xF7, 0xFB, 0x3E, 0x9D, 0x04, 0x49, 0x23, 0x92, 0x48, 0xA4, 0xA2, 0x58, 0xAB, 0xAE, 0x29,
0x62, 0xAE, 0x99, 0x23, 0x92, 0x3A, 0xB9, 0xA2, 0x48, 0xE4, 0xA3, 0x9E, 0x2A, 0xA2, 0xBA, 0x2B, 0xA7, 0x9E, 0x2A, 0xA2,
0xBA, 0x2A, 0xE3, 0x8E, 0x78, 0xE7, 0x8E, 0x7D, 0xF8, 0xE4, 0x13, 0x4D, 0xF4, 0x6B, 0xF5, 0x44, 0xC0, 0x79, 0x59, 0xAF,
0xEA, 0xFE, 0x28, 0x79, 0x49, 0x79, 0x8D, 0xCC, 0x77, 0xE6, 0x85, 0x5D, 0x9E, 0x67, 0xAA, 0x37, 0x3D, 0xA2, 0x3B, 0x4B,
0x89, 0x7B, 0x52, 0xCB, 0x51, 0xA7, 0x9C, 0x8E, 0x36, 0x5E, 0x6E, 0x6E, 0xB8, 0xE7, 0x9E, 0x3B, 0x6B, 0x4A, 0xB3, 0xB7,
0xAA, 0x4E, 0x67, 0xA3, 0xE3, 0x73, 0x93, 0xC7, 0xC3, 0x55, 0xCF, 0x3C, 0xD7, 0x3C, 0x77, 0x92, 0x48, 0x1C, 0xDB, 0xFA,
0x96, 0xBF, 0xE7, 0x33, 0xA5, 0xFF, 0x00, 0xFA, 0xC7, 0xAF, 0xFF, 0x00, 0xDD, 0x5E, 0xD8, 0x07, 0x49, 0xFD, 0x3D, 0xBC,
0x99, 0xE8, 0xBF, 0x55, 0x8F, 0x08, 0x72, 0x5E, 0x08, 0x79, 0x29, 0x99, 0x8F, 0x8E, 0xEA, 0xD6, 0xF4, 0x5B, 0x6D, 0x52,
0xFE, 0x3B, 0xC9, 0xAC, 0x6D, 0x76, 0x4D, 0xAB, 0x09, 0xAB, 0x53, 0x4F, 0xF4, 0x1B, 0xB7, 0x7A, 0xFE, 0xE6, 0xF3, 0xEF,
0x71, 0x91, 0xDB, 0xB4, 0xF8, 0xAC, 0x2D, 0x2A, 0xCA, 0xD3, 0xF0, 0x92, 0x4E, 0x2E, 0xE0, 0xAA, 0x6B, 0x88, 0xEB, 0xB4,
0xBC, 0xAA, 0x9E, 0x42, 0x39, 0xDE, 0x64, 0xFA, 0x56, 0xF9, 0x75, 0xE1, 0xB6, 0xD1, 0x99, 0x83, 0x64, 0xEB, 0xBC, 0xF7,
0x62, 0xF5, 0x85, 0xBD, 0xC5, 0xD4, 0xB8, 0x0E, 0xE6, 0xEB, 0xAC, 0x16, 0x53, 0x3F, 0xA7, 0x64, 0xB0, 0xD1, 0xD7, 0x5D,
0x56, 0xB7, 0x5B, 0x1C, 0x58, 0xF8, 0xAF, 0xAF, 0x74, 0x1C, 0xBF, 0x36, 0xDC, 0x71, 0xF8, 0x8B, 0x2C, 0xA7, 0xDB, 0xA6,
0x99, 0xA9, 0x93, 0x8B, 0x69, 0xEE, 0xE0, 0xA6, 0x9B, 0x8A, 0xC3, 0x4D, 0xBA, 0x9B, 0xA1, 0xFB, 0xAB, 0xBE, 0x33, 0xD6,
0xFA, 0xCF, 0x4C, 0x75, 0x56, 0xFD, 0xD9, 0xD9, 0xAB, 0x8B, 0xB8, 0x2C, 0xBF, 0x0B, 0xA5, 0xEA, 0xF9, 0x7C, 0xF4, 0x76,
0x93, 0x4F, 0xF9, 0xD3, 0x5E, 0x56, 0xFA, 0xC6, 0xD6, 0x5B, 0x0C, 0x35, 0xA4, 0x71, 0xFB, 0xC9, 0x2D, 0xC5, 0xDC, 0xB0,
0x41, 0x0C, 0x54, 0xD5, 0x24, 0x95, 0xD3, 0x45, 0x3C, 0xD5, 0xC0, 0x4C, 0x2B, 0xD3, 0x9F, 0xC0, 0xDE, 0xB7, 0xF4, 0x90,
0xEA, 0x0D, 0xFF, 0x00, 0xCC, 0x8F, 0x34, 0xF7, 0x4D, 0x5B, 0x01, 0xDA, 0x97, 0x9A, 0xA4, 0xF8, 0xAB, 0xDA, 0x22, 0xC8,
0xDB, 0xE4, 0xF0, 0xFD, 0x65, 0xAB, 0x5C, 0xCB, 0x16, 0x47, 0x9D, 0x1F, 0x57, 0x9A, 0x0F, 0x7E, 0x77, 0x4E, 0xCF, 0xDC,
0xEF, 0xB1, 0xD0, 0x45, 0x37, 0xEC, 0xFA, 0x67, 0xF9, 0xD7, 0x15, 0x16, 0x56, 0x1C, 0xCB, 0x1D, 0x57, 0x13, 0xDD, 0x84,
0x55, 0xFC, 0xE9, 0xF2, 0xCB, 0x63, 0xF3, 0x5B, 0xC9, 0xBE, 0xC6, 0xEF, 0xDC, 0xED, 0xAD, 0xC6, 0x27, 0x1D, 0xB0, 0x5E,
0xC1, 0x89, 0xD1, 0xF5, 0x9B, 0x89, 0xA8, 0x9E, 0xBD, 0x4F, 0xAF, 0xF0, 0x31, 0xF3, 0x63, 0xAB, 0x60, 0xAB, 0x92, 0x2F,
0x78, 0x2B, 0xBE, 0xA6, 0xD3, 0x8A, 0xAE, 0xAF, 0xAB, 0x8F, 0xFA, 0xB9, 0x72, 0x37, 0x57, 0x12, 0x51, 0xC7, 0x14, 0xD7,
0xC7, 0x1C, 0x06, 0xA2, 0x03, 0xB0, 0x9E, 0x93, 0x1E, 0x9A, 0x3A, 0x07, 0xA8, 0x66, 0xC5, 0xDA, 0xB0, 0xF6, 0x07, 0x74,
0x73, 0xA0, 0xE3, 0x7A, 0xEB, 0x01, 0x1D, 0x76, 0xBA, 0x7E, 0xA1, 0x5E, 0x3A, 0x7E, 0xCB, 0xCC, 0x65, 0x33, 0x50, 0xCD,
0x6F, 0x88, 0xDA, 0x79, 0xB3, 0xCE, 0x63, 0xEE, 0xF1, 0x74, 0x68, 0x38, 0x2C, 0x97, 0x11, 0xD3, 0x7D, 0x54, 0x7C, 0x49,
0x73, 0x75, 0x35, 0x74, 0xDB, 0x71, 0x5D, 0x9F, 0x32, 0xD1, 0x72, 0x0D, 0x27, 0xF2, 0x9F, 0xC2, 0xDF, 0x21, 0xBC, 0x3E,
0xEC, 0x9C, 0x97, 0x5C, 0xF7, 0x1F, 0x5F, 0x67, 0x71, 0x9C, 0xC7, 0x97, 0xAB, 0x19, 0xAB, 0xEE, 0x36, 0x38, 0xCB, 0xEB,
0xDD, 0x23, 0x7F, 0xB7, 0x9E, 0xB9, 0xF9, 0xC5, 0x64, 0x74, 0xDD, 0x8E, 0x2B, 0x7E, 0x6C, 0x32, 0xBC, 0x65, 0x2D, 0xA0,
0xE6, 0x4E, 0x2D, 0x3E, 0x54, 0xDF, 0xDB, 0x55, 0xC5, 0x51, 0x5C, 0x43, 0x14, 0xD1, 0xD7, 0x1D, 0x21, 0x28, 0x7F, 0xA7,
0x97, 0xC4, 0x9E, 0xE8, 0xE8, 0x1D, 0x1F, 0xBD, 0x3B, 0xF7, 0xBA, 0x30, 0x39, 0x8E, 0xB1, 0xD6, 0x3B, 0x6B, 0x1B, 0xA5,
0x63, 0xB4, 0xAD, 0x67, 0x6F, 0x82, 0xE7, 0x5F, 0xCB, 0xE4, 0xF0, 0x1A, 0x7F, 0x3B, 0x1E, 0x63, 0x21, 0xD8, 0x39, 0xAC,
0x3E, 0x4E, 0x9B, 0x59, 0x70, 0xF8, 0x19, 0x28, 0xCE, 0xD1, 0x16, 0x2A, 0x6B, 0xAA, 0x68, 0x92, 0xE6, 0x0F, 0xC4, 0xDC,
0x53, 0x4D, 0x36, 0xB5, 0xDB, 0xCD, 0x70, 0x1C, 0xDF, 0xF4, 0xF6, 0xEC, 0x8D, 0x67, 0xB8, 0x3D, 0x7D, 0xB2, 0xBD, 0xA3,
0xA5, 0xD7, 0x44, 0xDA, 0x86, 0xF9, 0xDB, 0x5E, 0x58, 0xEC, 0xDA, 0xC5, 0xDD, 0x11, 0xD3, 0x17, 0x19, 0x1C, 0x0E, 0x53,
0x40, 0xED, 0x5B, 0x8C, 0x4E, 0x5A, 0xA8, 0xE9, 0xFC, 0xA8, 0x97, 0x2F, 0x63, 0x55, 0x17, 0x55, 0xF1, 0xEF, 0xCF, 0x3C,
0x57, 0x2F, 0x3E, 0xFC, 0xF3, 0xCF, 0xE7, 0xC8, 0x62, 0x7F, 0xA8, 0x37, 0xFC, 0x46, 0xB6, 0x8F, 0xE1, 0x47, 0x56, 0x7F,
0xD2, 0x2F, 0x01, 0xD5, 0x6F, 0x45, 0xFF, 0x00, 0x33, 0xFA, 0x77, 0xCA, 0x9F, 0x18, 0x72, 0x3E, 0x9B, 0x5E, 0x4B, 0xCB,
0x8A, 0xBD, 0xD8, 0xF1, 0xBA, 0x96, 0x7F, 0x47, 0xD3, 0x31, 0x5B, 0x15, 0xCC, 0x36, 0x90, 0x76, 0xB7, 0x51, 0x64, 0x6D,
0xEE, 0xE6, 0xA3, 0x5D, 0xC2, 0x5D, 0xFD, 0xC8, 0x25, 0xA3, 0x77, 0xEB, 0xAB, 0x69, 0x64, 0xA2, 0xDE, 0x3B, 0x7E, 0x63,
0xBC, 0x8F, 0x1B, 0x6D, 0x6D, 0x77, 0x6D, 0x55, 0x52, 0xDA, 0x5C, 0xCB, 0x10, 0x71, 0x87, 0xCE, 0xFF, 0x00, 0x47, 0x6F,
0x28, 0xFC, 0x41, 0xDC, 0x73, 0x39, 0x0D, 0x3F, 0x4A, 0xDA, 0xFB, 0xBB, 0xA2, 0x2E, 0xB2, 0x37, 0x32, 0x6A, 0x7D, 0x91,
0xA2, 0xE0, 0xEF, 0x36, 0x5C, 0xA6, 0x2B, 0x15, 0x24, 0xDE, 0xF6, 0x78, 0xFE, 0xCB, 0xD7, 0xB0, 0x36, 0x93, 0xE4, 0x35,
0x5C, 0xC5, 0xA5, 0x12, 0x47, 0x0C, 0x97, 0xB5, 0x41, 0x4E, 0x22, 0xF2, 0x5A, 0xA9, 0xFC, 0x3C, 0xFC, 0x49, 0x5D, 0x56,
0xD1, 0x07, 0x34, 0x3A, 0xEF, 0xA8, 0xBB, 0x53, 0xB7, 0x36, 0x28, 0xF5, 0x1E, 0xAD, 0xEB, 0x8D, 0xE3, 0xB0, 0xF6, 0x79,
0x2E, 0xA0, 0xB2, 0xFD, 0x85, 0xA6, 0x6A, 0xF9, 0x9D, 0x8F, 0x25, 0x0D, 0xC5, 0xCD, 0x72, 0xC7, 0x0D, 0x17, 0x76, 0xB8,
0xAB, 0x3B, 0x99, 0x2C, 0xA9, 0xE6, 0xAB, 0x79, 0x39, 0xE6, 0xB9, 0xB8, 0xA2, 0x8A, 0x69, 0x8E, 0xBA, 0xAA, 0xE7, 0x8E,
0x28, 0xAB, 0x9E, 0x02, 0x72, 0x5E, 0x13, 0xF4, 0x07, 0x60, 0x78, 0x5B, 0xE9, 0x1B, 0xD9, 0xFA, 0x3F, 0x91, 0x7C, 0x6B,
0xBA, 0x06, 0xCD, 0xFD, 0x02, 0xEF, 0xAD, 0xCF, 0x33, 0x63, 0x79, 0xB1, 0xE3, 0x24, 0xB6, 0xD5, 0xAC, 0xF7, 0x0D, 0x72,
0xFE, 0x3C, 0x3E, 0x2B, 0x61, 0xCC, 0xF1, 0x2D, 0x18, 0x3B, 0x7C, 0xDD, 0x55, 0xF1, 0x45, 0x32, 0x45, 0x05, 0xCD, 0xC4,
0x34, 0xC9, 0x35, 0x11, 0xF1, 0x2D, 0x52, 0x73, 0x55, 0x14, 0x87, 0x25, 0xBE, 0x99, 0x3F, 0xDF, 0xBF, 0x93, 0x7F, 0xC2,
0x4D, 0x4F, 0xF9, 0xC6, 0xA0, 0x71, 0x6B, 0xD4, 0x47, 0xFE, 0x7D, 0x7C, 0xCB, 0xFF, 0x00, 0xEC, 0xE7, 0x76, 0xFF, 0x00,
0xDC, 0x4D, 0x80, 0x1F, 0x19, 0xE2, 0x67, 0x95, 0x9D, 0xB1, 0xE1, 0xAF, 0x75, 0xEB, 0x1D, 0xDD, 0xD4, 0x39, 0x6F, 0xC2,
0x66, 0x70, 0xB3, 0x53, 0x69, 0x9E, 0xC0, 0x5D, 0xD7, 0x2F, 0x3A, 0xFE, 0xF3, 0xAA, 0x5C, 0x5C, 0x5B, 0xCB, 0x9A, 0xD3,
0x76, 0x7B, 0x48, 0xF9, 0xE3, 0x9B, 0x9C, 0x3E, 0x5E, 0x2B, 0x7A, 0x78, 0xF9, 0xD3, 0xED, 0x3D, 0xAC, 0xF4, 0x47, 0x71,
0x05, 0x71, 0xCF, 0x14, 0x72, 0x52, 0x16, 0x1F, 0xF8, 0x61, 0xE4, 0xE7, 0x45, 0xF9, 0x95, 0xD5, 0x16, 0x9E, 0x45, 0xF4,
0xCC, 0x18, 0xFB, 0x4B, 0xED, 0xBA, 0x2C, 0x6E, 0x0F, 0xB1, 0xF1, 0x72, 0x45, 0x63, 0x1E, 0xE3, 0xAC, 0x6D, 0x9A, 0xD5,
0xA5, 0x5E, 0xFA, 0x7E, 0xEB, 0x25, 0xAD, 0x34, 0xCB, 0x75, 0x79, 0x81, 0x87, 0x2B, 0xCD, 0x56, 0x53, 0xD7, 0xFD, 0x5D,
0xCD, 0x85, 0xCC, 0x53, 0xC3, 0xFD, 0x54, 0xB4, 0x71, 0xC0, 0x57, 0x61, 0xD1, 0x7D, 0x0F, 0xB8, 0x79, 0x3B, 0xE4, 0x9E,
0x91, 0xD0, 0xBA, 0x24, 0x5C, 0xD5, 0xB1, 0xF6, 0x67, 0x60, 0xD5, 0xAF, 0xC3, 0x79, 0xCC, 0x35, 0xDC, 0x41, 0x84, 0xC5,
0xF3, 0x79, 0x73, 0x79, 0xB1, 0x6C, 0xD7, 0xD1, 0x47, 0xCD, 0x32, 0x57, 0x8C, 0xD5, 0xF5, 0xEB, 0x4B, 0xAC, 0x8D, 0xCF,
0x14, 0xF3, 0xF2, 0xE6, 0xDE, 0xDA, 0xBF, 0x8F, 0xBD, 0x5E, 0xDC, 0x72, 0x12, 0x62, 0xF5, 0xD8, 0xEE, 0xFD, 0x4F, 0xC5,
0x6F, 0x15, 0xFA, 0x1B, 0xD3, 0x87, 0xA3, 0xE7, 0x8F, 0x0B, 0x8E, 0xCC, 0xEA, 0xF8, 0x1B, 0xAD, 0xC3, 0x1F, 0x6B, 0x73,
0x4F, 0x19, 0x3B, 0x0E, 0xA3, 0xEB, 0xF9, 0xAD, 0xEC, 0x75, 0x4B, 0x1C, 0xBD, 0x50, 0xF1, 0x0D, 0x53, 0xDD, 0x76, 0x1E,
0xE9, 0x8D, 0x9A, 0xFA, 0xEA, 0xE3, 0x9E, 0x3E, 0x77, 0x32, 0x61, 0xAE, 0x3E, 0xEF, 0x1C, 0xF1, 0x71, 0x57, 0x35, 0x04,
0x46, 0x01, 0xB0, 0xBE, 0x28, 0xF9, 0x09, 0xB2, 0xF8, 0xAB, 0xE4, 0x57, 0x52, 0xF7, 0xFE, 0xAB, 0xF7, 0xE5, 0xC8, 0x75,
0xBE, 0xDD, 0x61, 0x97, 0xBF, 0xC6, 0xDB, 0xCF, 0xCD, 0xBF, 0x39, 0xFD, 0x62, 0xE3, 0x89, 0x31, 0x9B, 0x7E, 0xB1, 0x24,
0xBE, 0xFC, 0x71, 0x44, 0x3B, 0x2E, 0xAF, 0x7D, 0x77, 0x63, 0x55, 0x5C, 0xFB, 0xF1, 0x47, 0x13, 0xFC, 0xBF, 0xB6, 0x9E,
0x01, 0x28, 0x5F, 0x5E, 0x8F, 0x1C, 0x75, 0x5F, 0x26, 0x3C, 0x62, 0xEA, 0x0F, 0x50, 0xBE, 0x93, 0xE2, 0x1D, 0x86, 0x8D,
0x4F, 0x59, 0xD7, 0x78, 0xD9, 0xB2, 0xD8, 0xC8, 0x38, 0xAE, 0x6D, 0x8F, 0xA3, 0x7B, 0x02, 0xB8, 0x32, 0x7A, 0xB6, 0x7A,
0xE2, 0x98, 0x63, 0x9A, 0xE2, 0xA9, 0x74, 0x7D, 0x9B, 0x35, 0x4F, 0xCE, 0x2F, 0xD3, 0xF8, 0x7B, 0x6C, 0xC5, 0xDC, 0x93,
0x73, 0xC7, 0x16, 0xDE, 0xD4, 0x87, 0xAB, 0xF4, 0xC9, 0x7E, 0xE3, 0x7C, 0x9E, 0xFE, 0x2B, 0xE9, 0xDF, 0xCA, 0x17, 0x00,
0x8D, 0x2E, 0x6F, 0xC0, 0xCF, 0x39, 0x65, 0xCC, 0xE5, 0xE5, 0x8B, 0xC3, 0x1F, 0x2B, 0xE4, 0x8A, 0x4C, 0x9D, 0xFC, 0x91,
0xC9, 0x1F, 0x8E, 0xBD, 0xBF, 0x5C, 0x72, 0x47, 0x5D, 0xD4, 0xB5, 0x51, 0x5D, 0x15, 0xD3, 0xA7, 0xF3, 0x4D, 0x74, 0x57,
0x4F, 0x3C, 0x73, 0xC7, 0x3C, 0x73, 0xED, 0xCF, 0x00, 0xC0, 0x7D, 0x99, 0xD3, 0x5D, 0xBF, 0xD2, 0xB9, 0x5C, 0x7E, 0x0B,
0xB9, 0x3A, 0xA7, 0xB2, 0x7A, 0x97, 0x39, 0x96, 0xC7, 0xFE, 0xD6, 0xC5, 0xE1, 0xBB, 0x33, 0x46, 0xD9, 0xF4, 0x3C, 0xAE,
0x4B, 0x15, 0xF8, 0x99, 0xAC, 0xFF, 0x00, 0x69, 0xE3, 0xF1, 0xDB, 0x4E, 0x2F, 0x15, 0x77, 0x79, 0x8F, 0xFC, 0x5D, 0xB4,
0x91, 0x7D, 0xE8, 0xE8, 0xAA, 0x3F, 0xB9, 0x1D, 0x54, 0xFC, 0xBE, 0x54, 0xF3, 0xC7, 0x01, 0x8D, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xD7, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xB9, 0xAC, 0x6C,
0x39, 0x1D, 0x4B, 0x64, 0xD7, 0xB6, 0xBC, 0x47, 0x30, 0xD3, 0x96, 0xD6, 0x73, 0x98, 0x9D, 0x87, 0x17, 0x55, 0xC4, 0x5F,
0x7E, 0xDE, 0x9C, 0x8E, 0x16, 0xFE, 0xDF, 0x25, 0x65, 0xCC, 0xF0, 0xFC, 0xA9, 0xFB, 0xD0, 0xF1, 0x73, 0x6D, 0x4F, 0xCA,
0x9F, 0x7E, 0x3E, 0x54, 0xFB, 0xF1, 0xEE, 0x0D, 0xC8, 0xF3, 0x1F, 0xD4, 0x3B, 0xC9, 0x1F, 0x3B, 0x2D, 0xBA, 0xFA, 0xD7,
0xBF, 0xB2, 0xBA, 0x96, 0x4A, 0x1E, 0xB2, 0x9F, 0x67, 0xB8, 0xD5, 0xB8, 0xD6, 0x35, 0x4B, 0x3D, 0x6B, 0x98, 0x64, 0xDB,
0xA3, 0xC0, 0x47, 0x99, 0xE6, 0xF6, 0xAB, 0x59, 0xA5, 0xE6, 0xF3, 0x8A, 0xE9, 0xD6, 0xED, 0x7E, 0xDF, 0x15, 0x7B, 0x7D,
0xBF, 0x6A, 0xBD, 0xBF, 0xF7, 0x7E, 0x41, 0xA3, 0x60, 0xF5, 0xF5, 0xFD, 0x83, 0x39, 0xA9, 0xE7, 0xB0, 0xBB, 0x46, 0xB1,
0x97, 0xC9, 0x6B, 0xFB, 0x26, 0xB9, 0x95, 0xC7, 0xE7, 0x70, 0x19, 0xEC, 0x35, 0xE5, 0xC6, 0x3B, 0x2D, 0x85, 0xCC, 0xE2,
0x6E, 0xE2, 0xBE, 0xC6, 0x65, 0x71, 0x97, 0xF6, 0xB2, 0x45, 0x73, 0x65, 0x90, 0xC7, 0xDE, 0xC1, 0x44, 0xB0, 0xCB, 0x1D,
0x54, 0xD7, 0x1C, 0x94, 0x71, 0x55, 0x3C, 0xF1, 0xCF, 0x1C, 0x72, 0x0D, 0xB6, 0xF3, 0x53, 0xCD, 0xCE, 0xCC, 0xF3, 0x9F,
0x6D, 0xEA, 0xFD, 0xF7, 0xB6, 0xB1, 0x58, 0x1B, 0x3D, 0xCB, 0xAE, 0xFA, 0x87, 0x03, 0xD5, 0x37, 0xF9, 0x9C, 0x15, 0x13,
0x5B, 0xD3, 0xB9, 0x57, 0x84, 0xCE, 0x6C, 0x79, 0xD9, 0x76, 0xDC, 0x9D, 0x85, 0x7C, 0xFE, 0x13, 0x1B, 0x97, 0xCB, 0x5D,
0x6C, 0x72, 0x73, 0x3C, 0x16, 0xBC, 0x51, 0x6B, 0x4D, 0x54, 0x7B, 0xC7, 0x45, 0x14, 0xF3, 0xC5, 0x14, 0x86, 0xA6, 0x6B,
0xBB, 0x26, 0xC3, 0xA8, 0x67, 0x71, 0x5B, 0x46, 0xA5, 0x9E, 0xCD, 0x6A, 0xFB, 0x36, 0x0A, 0xF6, 0x0C, 0x9E, 0x0F, 0x62,
0xD7, 0x72, 0x97, 0xD8, 0x4C, 0xEE, 0x1B, 0x25, 0x6B, 0x5F, 0xCE, 0xDB, 0x21, 0x8A, 0xCB, 0xE3, 0x67, 0xB6, 0xC8, 0x63,
0xAF, 0x6D, 0xEB, 0xE3, 0xDE, 0x89, 0x61, 0x92, 0x89, 0x28, 0xE7, 0xF3, 0xE3, 0x9E, 0x01, 0xDC, 0xCE, 0x8E, 0xFA, 0x87,
0x7C, 0xE4, 0xEA, 0xFC, 0x3E, 0x33, 0x5D, 0xEC, 0x3C, 0x6F, 0x57, 0x77, 0xD6, 0x3B, 0x1D, 0x6F, 0xCD, 0xAF, 0xED, 0xCD,
0xD7, 0x03, 0x92, 0xD7, 0xF7, 0xDB, 0x88, 0x61, 0x86, 0x58, 0xEC, 0x68, 0x9F, 0x63, 0xD3, 0x32, 0xB8, 0x6C, 0x25, 0xE5,
0x70, 0x7B, 0xC7, 0xC4, 0xB3, 0xDD, 0x62, 0x2E, 0x6F, 0x2E, 0xA9, 0x8F, 0xDE, 0x59, 0xB9, 0x9A, 0xBA, 0xE6, 0xA8, 0x33,
0x2E, 0xD3, 0xF5, 0x30, 0x79, 0x3F, 0x7F, 0x87, 0x9E, 0xD7, 0x50, 0xE8, 0x2E, 0x8D, 0xD6, 0xF3, 0x52, 0xF3, 0xF0, 0x8B,
0x2F, 0x99, 0xBA, 0xDE, 0x76, 0x9B, 0x6B, 0x58, 0xAA, 0xA2, 0x4A, 0x64, 0xAE, 0x1C, 0x4D, 0xBE, 0x7B, 0x5A, 0xF9, 0xDE,
0x53, 0x55, 0x54, 0xD5, 0x15, 0x72, 0x4F, 0x5C, 0x54, 0xD5, 0x4F, 0xEB, 0x8A, 0x4E, 0x39, 0xF6, 0xE0, 0x38, 0xAD, 0xE4,
0xFF, 0x00, 0x99, 0xDE, 0x4A, 0xF9, 0x8B, 0xB4, 0xD1, 0xB4, 0xF9, 0x03, 0xDA, 0x59, 0xDD, 0xD7, 0xF0, 0x53, 0x4D, 0x2E,
0xBF, 0xAC, 0x51, 0xCC, 0x38, 0x6D, 0x17, 0x53, 0xA2, 0x6F, 0x78, 0xFE, 0xDE, 0xB1, 0xA5, 0xE1, 0xE2, 0xB3, 0xD7, 0xF1,
0x73, 0x7E, 0x1B, 0xE3, 0x0C, 0xB7, 0x9C, 0x41, 0x5E, 0x42, 0xF2, 0x88, 0xE8, 0xE6, 0xEA, 0xE2, 0x7A, 0xF8, 0xF9, 0xF2,
0x1A, 0xBA, 0x00, 0x32, 0x7F, 0x4E, 0x77, 0x4F, 0x6A, 0x78, 0xFB, 0xD8, 0x38, 0x3E, 0xD4, 0xE9, 0x8D, 0xE3, 0x39, 0xD7,
0xBB, 0xFE, 0xB9, 0x24, 0x95, 0xE2, 0xB6, 0x2C, 0x0C, 0xF1, 0xD1, 0x3D, 0x31, 0x4F, 0x4F, 0xDB, 0xBA, 0xB0, 0xBE, 0xB3,
0xBA, 0x8A, 0xE7, 0x1B, 0x98, 0xC4, 0x5F, 0xC5, 0xFA, 0x2E, 0x6C, 0xAF, 0x21, 0x9E, 0xD2, 0xE6, 0x3F, 0xD3, 0x2C, 0x75,
0xD3, 0xF9, 0x03, 0xBE, 0x5A, 0x57, 0xD4, 0xB5, 0xE5, 0x3E, 0x23, 0x03, 0x6D, 0x8F, 0xDE, 0x3A, 0x3B, 0xA4, 0x77, 0x4C,
0xE5, 0xB7, 0xC2, 0x2A, 0xB6, 0x1C, 0x6C, 0x9B, 0x96, 0xA1, 0xCD, 0xFC, 0x11, 0xDB, 0x5B, 0xC5, 0xC4, 0xD9, 0x1C, 0x4D,
0x19, 0xBC, 0xED, 0x9F, 0x39, 0x49, 0xEE, 0x28, 0x96, 0x59, 0x64, 0xB5, 0xE6, 0xD6, 0xDB, 0x9F, 0xB9, 0xC5, 0x31, 0xDB,
0xC5, 0xC5, 0x3F, 0xA8, 0x34, 0xF3, 0xCC, 0x7F, 0x5A, 0xFF, 0x00, 0x31, 0x7C, 0xBE, 0xD5, 0x33, 0x3D, 0x69, 0x35, 0xE6,
0xB1, 0xD3, 0x1D, 0x53, 0x9F, 0xB5, 0xE7, 0x1F, 0x9F, 0xD4, 0x3A, 0xB6, 0xDF, 0x29, 0x67, 0x94, 0xDA, 0xB1, 0x72, 0x53,
0xED, 0x73, 0x8C, 0xDB, 0x37, 0x2C, 0xBE, 0x4B, 0x21, 0x9C, 0xBE, 0xC6, 0x5E, 0x7C, 0xAB, 0x8E, 0xE2, 0xD2, 0xC7, 0xF6,
0x6D, 0x95, 0xDD, 0xB5, 0x5F, 0x66, 0xE6, 0x09, 0xE9, 0xF9, 0xF3, 0x58, 0x73, 0xDB, 0xC6, 0xCF, 0x22, 0xFB, 0x2F, 0xC5,
0x1E, 0xE3, 0xD5, 0xBB, 0xD7, 0xA8, 0x6E, 0xF1, 0x16, 0x5B, 0xFE, 0x9F, 0x06, 0x7E, 0xDF, 0x0B, 0x73, 0x9D, 0xC4, 0xC3,
0x9C, 0xC5, 0xD1, 0x1E, 0xCB, 0xAE, 0xE5, 0x35, 0x7C, 0xA7, 0x17, 0x18, 0xCB, 0x8A, 0xE3, 0x8A, 0x7E, 0x6B, 0xC5, 0x66,
0x26, 0xE2, 0x8E, 0x79, 0xE7, 0x8F, 0x85, 0x7C, 0xF1, 0x57, 0xF9, 0x03, 0xD6, 0xF2, 0x93, 0xCA, 0x4E, 0xD7, 0xF3, 0x0B,
0xB5, 0xEE, 0xFB, 0xA3, 0xBA, 0x2E, 0xF0, 0x77, 0xDB, 0xC5, 0xF6, 0x0F, 0x0B, 0xAF, 0x5C, 0x5C, 0x6B, 0xD8, 0x58, 0x70,
0x38, 0xEA, 0xF1, 0xD8, 0x18, 0x65, 0x83, 0x1D, 0xC7, 0x18, 0xE8, 0x25, 0x96, 0x2A, 0x26, 0xA2, 0x29, 0x79, 0xE2, 0xAA,
0xB8, 0xE7, 0x8F, 0x97, 0xB7, 0x1F, 0x97, 0xBF, 0xF6, 0x86, 0x00, 0xC7, 0x64, 0x72, 0x18, 0x7C, 0x85, 0x86, 0x5F, 0x11,
0x7F, 0x79, 0x8B, 0xCA, 0xE2, 0xEF, 0x2D, 0x72, 0x38, 0xCC, 0x9E, 0x3A, 0xEA, 0x7B, 0x1C, 0x86, 0x3B, 0x21, 0x63, 0x3C,
0x77, 0x36, 0x57, 0xF6, 0x17, 0xB6, 0xD2, 0x45, 0x73, 0x67, 0x79, 0x67, 0x73, 0x15, 0x32, 0x45, 0x2C, 0x75, 0x53, 0x5C,
0x75, 0xD3, 0xC5, 0x54, 0xF3, 0xC7, 0x3C, 0x71, 0xCF, 0x01, 0xDB, 0xBF, 0x1E, 0xBE, 0xA0, 0x2F, 0x3A, 0x7A, 0x5F, 0x13,
0x8E, 0xD6, 0x77, 0x8B, 0x8D, 0x17, 0xC8, 0x4D, 0x7E, 0xC2, 0x3A, 0x2D, 0xE3, 0xBD, 0xED, 0x0C, 0x56, 0x52, 0x1D, 0xF6,
0x3B, 0x48, 0x21, 0xB9, 0xA6, 0x18, 0xA3, 0xDE, 0x35, 0x8C, 0xB6, 0x1A, 0x5C, 0x94, 0xF5, 0x5C, 0x4B, 0x1D, 0x53, 0x5C,
0xE5, 0xEC, 0xF2, 0xD7, 0x52, 0xD1, 0x17, 0xC7, 0xEE, 0x53, 0x55, 0x5C, 0xD7, 0xC0, 0x6C, 0x9E, 0x67, 0xEA, 0x67, 0xF2,
0x42, 0x7C, 0x65, 0xDC, 0x5A, 0xFF, 0x00, 0x8E, 0x7D, 0x23, 0x8C, 0xCC, 0x57, 0x45, 0x1C, 0x58, 0xDF, 0xE6, 0x72, 0xDB,
0xE6, 0x77, 0x19, 0x6F, 0x27, 0x12, 0xD1, 0xCC, 0x95, 0x5D, 0xE2, 0x6C, 0xB3, 0x1A, 0xED, 0xD5, 0xE5, 0x15, 0x43, 0xC5,
0x54, 0xF1, 0x4D, 0x17, 0xB0, 0x73, 0x4D, 0x5C, 0xF1, 0x57, 0xBF, 0x3C, 0x71, 0xCD, 0x35, 0x07, 0x1F, 0x7C, 0xB3, 0xF5,
0x0C, 0xF2, 0xCB, 0xCD, 0x5B, 0xC8, 0x69, 0xEF, 0x4E, 0xCF, 0xBE, 0xC8, 0xEA, 0x96, 0x17, 0x3C, 0x5E, 0x61, 0xFA, 0xDB,
0x59, 0xB6, 0x8B, 0x55, 0xEB, 0x9C, 0x4D, 0xCD, 0x14, 0xF1, 0xC4, 0x77, 0x54, 0x6B, 0x58, 0xCE, 0x68, 0xA3, 0x33, 0x91,
0x83, 0x9E, 0x6B, 0xE6, 0x2B, 0xDC, 0xA4, 0xB7, 0xF7, 0xD0, 0xD3, 0x2D, 0x74, 0x47, 0x35, 0x31, 0xD5, 0xF0, 0xE0, 0x3C,
0x9F, 0x0F, 0xBC, 0xE5, 0xEF, 0xBF, 0x06, 0xB6, 0x4D, 0xC7, 0x6B, 0xE8, 0x4C, 0x86, 0xB1, 0x8E, 0xCC, 0x6F, 0x58, 0x3B,
0x0D, 0x7B, 0x3D, 0x2E, 0xCD, 0xAD, 0xDB, 0x6C, 0x90, 0xD7, 0x8D, 0xC7, 0x5F, 0xD5, 0x92, 0xB7, 0x8E, 0xD6, 0x0B, 0x99,
0xA2, 0xA2, 0xDA, 0x5F, 0xC5, 0x55, 0xEF, 0x55, 0x7C, 0x7B, 0xF3, 0xCF, 0x1C, 0x7B, 0x7E, 0x5F, 0x9F, 0xB8, 0x6B, 0xDF,
0x6A, 0xF6, 0x56, 0xD1, 0xDC, 0x9D, 0x99, 0xBF, 0xF6, 0xD6, 0xED, 0x35, 0x9D, 0xC6, 0xE1, 0xD9, 0x7B, 0x8E, 0xC5, 0xBD,
0x6D, 0x13, 0xE3, 0xEC, 0xE8, 0xC7, 0xD8, 0x4D, 0x9F, 0xDA, 0x32, 0xB7, 0x59, 0x9C, 0xAC, 0x96, 0x76, 0x31, 0x73, 0x54,
0x76, 0x76, 0xB5, 0xDE, 0xDE, 0x57, 0xCD, 0x11, 0x53, 0xCF, 0x3C, 0x51, 0x4F, 0xB7, 0x1F, 0xE4, 0x0F, 0x81, 0x06, 0xED,
0xF8, 0x2B, 0xE7, 0x97, 0x73, 0x78, 0x0D, 0xDA, 0xB7, 0x3D, 0x8B, 0xD5, 0x92, 0xDA, 0xE6, 0xB0, 0xBB, 0x06, 0x3B, 0x9C,
0x46, 0xF9, 0xD7, 0x19, 0xEB, 0x9B, 0xD8, 0xF5, 0x2D, 0xE3, 0x1F, 0x0C, 0x77, 0x5C, 0xE2, 0x6B, 0xC9, 0xC5, 0x67, 0x25,
0x13, 0x5A, 0xE5, 0xF5, 0xFB, 0xDB, 0xAA, 0xA7, 0xB0, 0xBD, 0x8B, 0xDA, 0x7B, 0x7E, 0x6B, 0x96, 0x2F, 0x7A, 0xA0, 0x9E,
0x78, 0xE4, 0x0F, 0x94, 0xF1, 0x5F, 0xCC, 0xBE, 0xE1, 0xF0, 0xDF, 0xB3, 0x76, 0x1E, 0xDD, 0xE9, 0x58, 0xB4, 0xBB, 0x5D,
0xDF, 0x62, 0xC1, 0x64, 0x75, 0xB9, 0x32, 0x7B, 0x5E, 0xAB, 0x67, 0xB5, 0x7E, 0xCB, 0xC4, 0xE5, 0xB2, 0x76, 0x79, 0x5C,
0x84, 0x58, 0x5A, 0x2F, 0xA4, 0xA3, 0xF6, 0x6D, 0xCD, 0xDC, 0xD6, 0x11, 0x47, 0x24, 0xD4, 0x73, 0xF7, 0x2A, 0x87, 0x8E,
0x63, 0xF7, 0xF8, 0xD7, 0x5F, 0x15, 0x07, 0xC0, 0x79, 0x19, 0xE4, 0x4F, 0x6A, 0x79, 0x55, 0xDB, 0xBB, 0x3F, 0x77, 0x77,
0x2E, 0x72, 0x2C, 0xF6, 0xF7, 0xB5, 0xD3, 0x8B, 0x86, 0xFA, 0x7B, 0x3B, 0x4A, 0x31, 0xB8, 0xAB, 0x2B, 0x2C, 0x2E, 0x2E,
0xD3, 0x0F, 0x8C, 0xC6, 0xE1, 0xB1, 0x30, 0xD5, 0x55, 0xB6, 0x2F, 0x1D, 0x6B, 0x67, 0x65, 0x4F, 0xB4, 0x51, 0xFB, 0x71,
0x54, 0xB5, 0x57, 0x2D, 0x5F, 0x29, 0x24, 0xAE, 0xAA, 0x83, 0x07, 0x00, 0x0E, 0x8F, 0xF5, 0x2F, 0xAA, 0xA7, 0x96, 0xFD,
0x3B, 0xE3, 0x8C, 0xFE, 0x29, 0x60, 0x73, 0x3A, 0x2E, 0xC5, 0xD2, 0x97, 0x18, 0x3D, 0xBB, 0x57, 0x93, 0x5A, 0xDE, 0xF4,
0x7C, 0x76, 0xD7, 0x2F, 0xF4, 0x67, 0x78, 0xAF, 0x23, 0x26, 0xC1, 0xAF, 0x7E, 0x3A, 0xF6, 0x6A, 0x65, 0xAB, 0x11, 0x34,
0x99, 0x7B, 0x9E, 0x62, 0x8A, 0xAE, 0x2A, 0xE2, 0x1E, 0x26, 0xE6, 0x9A, 0x3D, 0xA9, 0xE2, 0x9A, 0x69, 0x0F, 0x99, 0xF0,
0xFB, 0xD4, 0x9F, 0xC9, 0xEF, 0x06, 0xB5, 0xBD, 0xC7, 0x54, 0xE8, 0x4C, 0xAE, 0x9B, 0x8D, 0xC4, 0xEF, 0x59, 0xCB, 0x1D,
0x87, 0x60, 0xA7, 0x66, 0xD4, 0x6C, 0xF6, 0x4B, 0x89, 0x72, 0x38, 0xEB, 0x0E, 0x71, 0xB6, 0xBC, 0xDB, 0x4D, 0x75, 0x3C,
0x5F, 0x86, 0x86, 0x8B, 0x6A, 0xB9, 0xF7, 0xA6, 0x9E, 0x3F, 0x55, 0x5C, 0xFB, 0xF3, 0xCF, 0xF6, 0x7B, 0x06, 0xE1, 0x7F,
0xE6, 0x0D, 0xF5, 0x1A, 0xFF, 0x00, 0xF2, 0x8E, 0xA8, 0xFF, 0x00, 0xF5, 0x66, 0x23, 0xFF, 0x00, 0xEB, 0x07, 0x3E, 0x7C,
0xBE, 0xF3, 0x5F, 0xBD, 0x3C, 0xE1, 0xDD, 0x75, 0x8D, 0xFB, 0xBE, 0x72, 0x3A, 0xE6, 0x47, 0x61, 0xD4, 0x75, 0x7F, 0xE8,
0x7E, 0x1A, 0x4D, 0x6B, 0x5D, 0xB6, 0xD6, 0xED, 0x23, 0xC3, 0x7E, 0xD6, 0xC8, 0x66, 0xBE, 0x13, 0xDA, 0xDA, 0xCB, 0x2D,
0x33, 0xDC, 0x7E, 0x3B, 0x27, 0x2F, 0x3F, 0x73, 0x9E, 0x7D, 0xFE, 0x3E, 0xDC, 0x7F, 0x90, 0x35, 0x28, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD5, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD6, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD7, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD4, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD5, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD6, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD7, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD3, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD4, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD5, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD6, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD7, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD0, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xD1, 0xAF, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xD2, 0xAF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFF, 0xD9,
};

View File

@@ -41,7 +41,7 @@ static void _http_request_watcher(int fd, short event, void *v_server);
static void _http_refresher(int fd, short event, void *v_server);
static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated);
static bool _expose_new_frame(us_server_s *server);
static bool _expose_frame(us_server_s *server, const us_frame_s *frame);
static const char *_http_get_header(struct evhttp_request *request, const char *key);
static char *_http_get_client_hostport(struct evhttp_request *request);
@@ -51,19 +51,15 @@ static char *_http_get_client_hostport(struct evhttp_request *request);
#define _A_EVBUFFER_ADD(x_buf, x_data, x_size) assert(!evbuffer_add(x_buf, x_data, x_size))
#define _A_EVBUFFER_ADD_PRINTF(x_buf, x_fmt, ...) assert(evbuffer_add_printf(x_buf, x_fmt, ##__VA_ARGS__) >= 0)
#define _VID(x_next) server->run->stream->run->video->x_next
#define _EX(x_next) server->run->exposed->x_next
us_server_s *us_server_init(us_stream_s *stream) {
us_exposed_s *exposed;
us_server_exposed_s *exposed;
US_CALLOC(exposed, 1);
exposed->frame = us_frame_init();
us_server_runtime_s *run;
US_CALLOC(run, 1);
run->ext_fd = -1;
run->stream = stream;
run->exposed = exposed;
us_server_s *server;
@@ -77,6 +73,7 @@ us_server_s *us_server_init(us_stream_s *stream) {
server->allow_origin = "";
server->instance_id = "";
server->timeout = 10;
server->stream = stream;
server->run = run;
assert(!evthread_use_pthreads());
@@ -100,7 +97,7 @@ void us_server_destroy(us_server_s *server) {
}
evhttp_free(run->http);
US_CLOSE_FD(run->ext_fd, close);
US_CLOSE_FD(run->ext_fd);
event_base_free(run->base);
# if LIBEVENT_VERSION_NUMBER >= 0x02010100
@@ -123,7 +120,8 @@ void us_server_destroy(us_server_s *server) {
int us_server_listen(us_server_s *server) {
us_server_runtime_s *const run = server->run;
us_stream_s *const stream = run->stream;
us_server_exposed_s *const ex = run->exposed;
us_stream_s *const stream = server->stream;
{
if (server->static_path[0] != '\0') {
@@ -138,9 +136,9 @@ int us_server_listen(us_server_s *server) {
assert(!evhttp_set_cb(run->http, "/stream", _http_callback_stream, (void *)server));
}
us_frame_copy(stream->blank, _EX(frame));
_EX(notify_last_width) = _EX(frame->width);
_EX(notify_last_height) = _EX(frame->height);
us_frame_copy(stream->run->blank->jpeg, ex->frame);
ex->notify_last_width = ex->frame->width;
ex->notify_last_height = ex->frame->height;
if (server->exit_on_no_clients > 0) {
run->last_request_ts = us_get_now_monotonic();
@@ -392,7 +390,7 @@ not_found:
goto cleanup;
cleanup:
US_CLOSE_FD(fd, close); // cppcheck-suppress unreadVariable
US_CLOSE_FD(fd); // cppcheck-suppress unreadVariable
US_DELETE(static_path, free);
US_DELETE(buf, evbuffer_free);
US_DELETE(decoded_path, free);
@@ -404,7 +402,8 @@ cleanup:
static void _http_callback_state(struct evhttp_request *request, void *v_server) {
us_server_s *const server = (us_server_s *)v_server;
us_server_runtime_s *const run = server->run;
us_stream_s *const stream = run->stream;
us_server_exposed_s *const ex = run->exposed;
us_stream_s *const stream = server->stream;
PREPROCESS_REQUEST;
@@ -455,12 +454,12 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
" \"source\": {\"resolution\": {\"width\": %u, \"height\": %u},"
" \"online\": %s, \"desired_fps\": %u, \"captured_fps\": %u},"
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
(server->fake_width ? server->fake_width : _EX(frame->width)),
(server->fake_height ? server->fake_height : _EX(frame->height)),
us_bool_to_string(_EX(frame->online)),
(server->fake_width ? server->fake_width : ex->frame->width),
(server->fake_height ? server->fake_height : ex->frame->height),
us_bool_to_string(ex->frame->online),
stream->dev->desired_fps,
_EX(captured_fps),
_EX(queued_fps),
atomic_load(&stream->run->captured_fps),
ex->queued_fps,
run->stream_clients_count
);
@@ -488,12 +487,13 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
static void _http_callback_snapshot(struct evhttp_request *request, void *v_server) {
us_server_s *const server = (us_server_s *)v_server;
us_server_exposed_s *const ex = server->run->exposed;
PREPROCESS_REQUEST;
struct evbuffer *buf;
_A_EVBUFFER_NEW(buf);
_A_EVBUFFER_ADD(buf, (const void *)_EX(frame->data), _EX(frame->used));
_A_EVBUFFER_ADD(buf, (const void*)ex->frame->data, ex->frame->used);
ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0");
ADD_HEADER("Pragma", "no-cache");
@@ -513,16 +513,16 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
ADD_TIME_HEADER("X-Timestamp", us_get_now_real());
ADD_HEADER("X-UStreamer-Online", us_bool_to_string(_EX(frame->online)));
ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", _EX(dropped));
ADD_UNSIGNED_HEADER("X-UStreamer-Width", _EX(frame->width));
ADD_UNSIGNED_HEADER("X-UStreamer-Height", _EX(frame->height));
ADD_TIME_HEADER("X-UStreamer-Grab-Timestamp", _EX(frame->grab_ts));
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Timestamp", _EX(frame->encode_begin_ts));
ADD_TIME_HEADER("X-UStreamer-Encode-End-Timestamp", _EX(frame->encode_end_ts));
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Timestamp", _EX(expose_begin_ts));
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Timestamp", _EX(expose_cmp_ts));
ADD_TIME_HEADER("X-UStreamer-Expose-End-Timestamp", _EX(expose_end_ts));
ADD_HEADER("X-UStreamer-Online", us_bool_to_string(ex->frame->online));
ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", ex->dropped);
ADD_UNSIGNED_HEADER("X-UStreamer-Width", ex->frame->width);
ADD_UNSIGNED_HEADER("X-UStreamer-Height", ex->frame->height);
ADD_TIME_HEADER("X-UStreamer-Grab-Timestamp", ex->frame->grab_ts);
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Timestamp", ex->frame->encode_begin_ts);
ADD_TIME_HEADER("X-UStreamer-Encode-End-Timestamp", ex->frame->encode_end_ts);
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Timestamp", ex->expose_begin_ts);
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Timestamp", ex->expose_cmp_ts);
ADD_TIME_HEADER("X-UStreamer-Expose-End-Timestamp", ex->expose_end_ts);
ADD_TIME_HEADER("X-UStreamer-Send-Timestamp", us_get_now_monotonic());
# undef ADD_UNSUGNED_HEADER
@@ -574,7 +574,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
US_LIST_APPEND_C(run->stream_clients, client, run->stream_clients_count);
if (run->stream_clients_count == 1) {
atomic_store(&_VID(has_clients), true);
atomic_store(&server->stream->run->http_has_clients, true);
# ifdef WITH_GPIO
us_gpio_set_has_http_clients(true);
# endif
@@ -605,6 +605,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_client) {
us_stream_client_s *const client = (us_stream_client_s *)v_client;
us_server_s *const server = client->server;
us_server_exposed_s *const ex = server->run->exposed;
const long double now = us_get_now_monotonic();
const long long now_second = us_floor_ms(now);
@@ -692,7 +693,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"Content-Length: %zu" RN
"X-Timestamp: %.06Lf" RN
"%s",
(!client->zero_data ? _EX(frame->used) : 0),
(!client->zero_data ? ex->frame->used : 0),
us_get_now_real(),
(client->extra_headers ? "" : RN)
);
@@ -712,25 +713,25 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
"X-UStreamer-Send-Time: %.06Lf" RN
"X-UStreamer-Latency: %.06Lf" RN
RN,
us_bool_to_string(_EX(frame->online)),
_EX(dropped),
_EX(frame->width),
_EX(frame->height),
us_bool_to_string(ex->frame->online),
ex->dropped,
ex->frame->width,
ex->frame->height,
client->fps,
_EX(frame->grab_ts),
_EX(frame->encode_begin_ts),
_EX(frame->encode_end_ts),
_EX(expose_begin_ts),
_EX(expose_cmp_ts),
_EX(expose_end_ts),
ex->frame->grab_ts,
ex->frame->encode_begin_ts,
ex->frame->encode_end_ts,
ex->expose_begin_ts,
ex->expose_cmp_ts,
ex->expose_end_ts,
now,
now - _EX(frame->grab_ts)
now - ex->frame->grab_ts
);
}
}
if (!client->zero_data) {
_A_EVBUFFER_ADD(buf, (void *)_EX(frame->data), _EX(frame->used));
_A_EVBUFFER_ADD(buf, (void*)ex->frame->data, ex->frame->used);
}
_A_EVBUFFER_ADD_PRINTF(buf, RN "--" BOUNDARY RN);
@@ -759,7 +760,7 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
US_LIST_REMOVE_C(run->stream_clients, client, run->stream_clients_count);
if (run->stream_clients_count == 0) {
atomic_store(&_VID(has_clients), false);
atomic_store(&server->stream->run->http_has_clients, false);
# ifdef WITH_GPIO
us_gpio_set_has_http_clients(false);
# endif
@@ -780,6 +781,7 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated) {
us_server_runtime_s *const run = server->run;
us_server_exposed_s *const ex = run->exposed;
bool has_clients = false;
bool queued = false;
@@ -822,13 +824,13 @@ static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bo
static long long queued_fps_second = 0;
const long long now = us_floor_ms(us_get_now_monotonic());
if (now != queued_fps_second) {
_EX(queued_fps) = queued_fps_accum;
ex->queued_fps = queued_fps_accum;
queued_fps_accum = 0;
queued_fps_second = now;
}
queued_fps_accum += 1;
} else if (!has_clients) {
_EX(queued_fps) = 0;
ex->queued_fps = 0;
}
}
@@ -840,7 +842,7 @@ static void _http_request_watcher(int fd, short what, void *v_server) {
us_server_runtime_s *const run = server->run;
const long double now = us_get_now_monotonic();
if (us_stream_has_clients(run->stream)) {
if (us_stream_has_clients(server->stream)) {
run->last_request_ts = now;
} else if (run->last_request_ts + server->exit_on_no_clients < now) {
US_LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...",
@@ -855,17 +857,23 @@ static void _http_refresher(int fd, short what, void *v_server) {
(void)what;
us_server_s *server = (us_server_s *)v_server;
us_server_exposed_s *ex = server->run->exposed;
us_ring_s *const ring = server->stream->run->http_jpeg_ring;
bool stream_updated = false;
bool frame_updated = false;
if (atomic_load(&_VID(updated))) {
frame_updated = _expose_new_frame(server);
const int ri = us_ring_consumer_acquire(ring, 0);
if (ri >= 0) {
const us_frame_s *const frame = ring->items[ri];
frame_updated = _expose_frame(server, frame);
stream_updated = true;
} else if (_EX(expose_end_ts) + 1 < us_get_now_monotonic()) {
us_ring_consumer_release(ring, ri);
} else if (ex->expose_end_ts + 1 < us_get_now_monotonic()) {
US_LOG_DEBUG("HTTP: Repeating exposed ...");
_EX(expose_begin_ts) = us_get_now_monotonic();
_EX(expose_cmp_ts) = _EX(expose_begin_ts);
_EX(expose_end_ts) = _EX(expose_begin_ts);
ex->expose_begin_ts = us_get_now_monotonic();
ex->expose_cmp_ts = ex->expose_begin_ts;
ex->expose_end_ts = ex->expose_begin_ts;
frame_updated = true;
stream_updated = true;
}
@@ -876,63 +884,59 @@ static void _http_refresher(int fd, short what, void *v_server) {
frame_updated
&& server->notify_parent
&& (
_EX(notify_last_online) != _EX(frame->online)
|| _EX(notify_last_width) != _EX(frame->width)
|| _EX(notify_last_height) != _EX(frame->height)
ex->notify_last_online != ex->frame->online
|| ex->notify_last_width != ex->frame->width
|| ex->notify_last_height != ex->frame->height
)
) {
_EX(notify_last_online) = _EX(frame->online);
_EX(notify_last_width) = _EX(frame->width);
_EX(notify_last_height) = _EX(frame->height);
ex->notify_last_online = ex->frame->online;
ex->notify_last_width = ex->frame->width;
ex->notify_last_height = ex->frame->height;
us_process_notify_parent();
}
}
static bool _expose_new_frame(us_server_s *server) {
bool updated = false;
static bool _expose_frame(us_server_s *server, const us_frame_s *frame) {
us_server_exposed_s *const ex = server->run->exposed;
US_MUTEX_LOCK(_VID(mutex));
US_LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", frame->online);
ex->expose_begin_ts = us_get_now_monotonic();
US_LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", _VID(frame->online));
_EX(captured_fps) = _VID(captured_fps);
_EX(expose_begin_ts) = us_get_now_monotonic();
if (server->drop_same_frames && _VID(frame->online)) {
if (server->drop_same_frames && frame->online) {
bool need_drop = false;
bool maybe_same = false;
if (
(need_drop = (_EX(dropped) < server->drop_same_frames))
&& (maybe_same = us_frame_compare(_EX(frame), _VID(frame)))
(need_drop = (ex->dropped < server->drop_same_frames))
&& (maybe_same = us_frame_compare(ex->frame, frame))
) {
_EX(expose_cmp_ts) = us_get_now_monotonic();
_EX(expose_end_ts) = _EX(expose_cmp_ts);
ex->expose_cmp_ts = us_get_now_monotonic();
ex->expose_end_ts = ex->expose_cmp_ts;
US_LOG_VERBOSE("HTTP: Dropped same frame number %u; cmp_time=%.06Lf",
_EX(dropped), _EX(expose_cmp_ts) - _EX(expose_begin_ts));
_EX(dropped) += 1;
goto not_updated;
ex->dropped, (ex->expose_cmp_ts - ex->expose_begin_ts));
ex->dropped += 1;
return false; // Not updated
} else {
_EX(expose_cmp_ts) = us_get_now_monotonic();
ex->expose_cmp_ts = us_get_now_monotonic();
US_LOG_VERBOSE("HTTP: Passed same frame check (need_drop=%d, maybe_same=%d); cmp_time=%.06Lf",
need_drop, maybe_same, (_EX(expose_cmp_ts) - _EX(expose_begin_ts)));
need_drop, maybe_same, (ex->expose_cmp_ts - ex->expose_begin_ts));
}
}
us_frame_copy(_VID(frame), _EX(frame));
if (frame->used == 0) {
// Фрейм нулевой длины означает, что мы просто должны повторить то,
// что у нас уже есть, с поправкой на онлайн.
ex->frame->online = frame->online;
} else {
us_frame_copy(frame, ex->frame);
}
_EX(dropped) = 0;
_EX(expose_cmp_ts) = _EX(expose_begin_ts);
_EX(expose_end_ts) = us_get_now_monotonic();
ex->dropped = 0;
ex->expose_cmp_ts = ex->expose_begin_ts;
ex->expose_end_ts = us_get_now_monotonic();
US_LOG_VERBOSE("HTTP: Exposed frame: online=%d, exp_time=%.06Lf",
_EX(frame->online), _EX(expose_end_ts) - _EX(expose_begin_ts));
updated = true;
not_updated:
atomic_store(&_VID(updated), false);
US_MUTEX_UNLOCK(_VID(mutex));
return updated;
ex->frame->online, (ex->expose_end_ts - ex->expose_begin_ts));
return true; // Updated
}
static const char *_http_get_header(struct evhttp_request *request, const char *key) {

View File

@@ -111,7 +111,7 @@ typedef struct {
bool notify_last_online;
unsigned notify_last_width;
unsigned notify_last_height;
} us_exposed_s;
} us_server_exposed_s;
typedef struct {
struct event_base *base;
@@ -124,8 +124,7 @@ typedef struct {
long double last_request_ts;
struct event *refresher;
us_stream_s *stream;
us_exposed_s *exposed;
us_server_exposed_s *exposed;
us_stream_client_s *stream_clients;
unsigned stream_clients_count;
@@ -159,6 +158,8 @@ typedef struct us_server_sx {
bool notify_parent;
unsigned exit_on_no_clients;
us_stream_s *stream;
us_server_runtime_s *run;
} us_server_s;

View File

@@ -254,8 +254,6 @@ void us_options_destroy(us_options_s *options) {
US_DELETE(options->raw_sink, us_memsink_destroy);
US_DELETE(options->h264_sink, us_memsink_destroy);
US_DELETE(options->blank, us_frame_destroy);
for (unsigned index = 0; index < options->argc; ++index) {
free(options->argv_copy[index]);
}
@@ -332,8 +330,6 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
break; \
}
char *blank_path = NULL;
# define ADD_SINK(x_prefix) \
char *x_prefix##_name = NULL; \
mode_t x_prefix##_mode = 0660; \
@@ -372,7 +368,7 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
case _O_QUALITY: OPT_NUMBER("--quality", dev->jpeg_quality, 1, 100, 0);
case _O_ENCODER: OPT_PARSE("encoder type", enc->type, us_encoder_parse_type, US_ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
case _O_GLITCHED_RESOLUTIONS: break; // Deprecated
case _O_BLANK: OPT_SET(blank_path, optarg);
case _O_BLANK: break; // Deprecated
case _O_LAST_AS_BLANK: OPT_NUMBER("--last-as-blank", stream->last_as_blank, 0, 86400, 0);
case _O_SLOWDOWN: OPT_SET(stream->slowdown, true);
case _O_DEVICE_TIMEOUT: OPT_NUMBER("--device-timeout", dev->timeout, 1, 60, 0);
@@ -485,9 +481,6 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
US_LOG_INFO("Starting PiKVM uStreamer %s ...", US_VERSION);
options->blank = us_blank_frame_init(blank_path);
stream->blank = options->blank;
# define ADD_SINK(x_label, x_prefix) { \
if (x_prefix##_name && x_prefix##_name[0] != '\0') { \
options->x_prefix = us_memsink_init( \
@@ -629,12 +622,12 @@ static void _help(FILE *fp, const us_device_s *dev, const us_encoder_s *enc, con
SAY(" * M2M-IMAGE ── GPU-accelerated JPEG encoding using V4L2 M2M image interface;");
SAY(" * NOOP ─────── Don't compress MJPEG stream (do nothing).\n");
SAY(" -g|--glitched-resolutions <WxH,...> ─ It doesn't do anything. Still here for compatibility.\n");
SAY(" -k|--blank <path> ─────────────────── Path to JPEG file that will be shown when the device is disconnected");
SAY(" -k|--blank <path> ─────────────────── It doesn't do anything. Still here for compatibility..\n");
SAY(" during the streaming. Default: black screen 640x480 with 'NO SIGNAL'.\n");
SAY(" -K|--last-as-blank <sec> ──────────── Show the last frame received from the camera after it was disconnected,");
SAY(" but no more than specified time (or endlessly if 0 is specified).");
SAY(" If the device has not yet been online, display 'NO SIGNAL' or the image");
SAY(" specified by option --blank. Default: disabled.");
SAY(" If the device has not yet been online, display some error text.");
SAY(" Default: disabled.");
SAY(" Note: currently this option has no effect on memory sinks.\n");
SAY(" -l|--slowdown ─────────────────────── Slowdown capturing to 1 FPS or less when no stream or sink clients");
SAY(" are connected. Useful to reduce CPU consumption. Default: disabled.\n");

View File

@@ -42,7 +42,6 @@
#include "../libs/device.h"
#include "encoder.h"
#include "blank.h"
#include "stream.h"
#include "http/server.h"
#ifdef WITH_GPIO
@@ -54,7 +53,6 @@ typedef struct {
unsigned argc;
char **argv;
char **argv_copy;
us_frame_s *blank;
us_memsink_s *sink;
us_memsink_s *raw_sink;
us_memsink_s *h264_sink;

View File

@@ -24,8 +24,7 @@
static us_workers_pool_s *_stream_init_loop(us_stream_s *stream);
static us_workers_pool_s *_stream_init_one(us_stream_s *stream);
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigned captured_fps);
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame);
#define _RUN(x_next) stream->run->x_next
@@ -47,15 +46,11 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigne
us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
us_stream_runtime_s *run;
US_CALLOC(run, 1);
US_RING_INIT_WITH_ITEMS(run->http_jpeg_ring, 4, us_frame_init);
atomic_init(&run->http_has_clients, false);
atomic_init(&run->captured_fps, 0);
atomic_init(&run->stop, false);
us_video_s *video;
US_CALLOC(video, 1);
video->frame = us_frame_init();
atomic_init(&video->updated, false);
US_MUTEX_INIT(video->mutex);
atomic_init(&video->has_clients, false);
run->video = video;
run->blank = us_blank_init();
us_stream_s *stream;
US_CALLOC(stream, 1);
@@ -70,16 +65,13 @@ us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
}
void us_stream_destroy(us_stream_s *stream) {
US_MUTEX_DESTROY(_RUN(video->mutex));
us_frame_destroy(_RUN(video->frame));
free(_RUN(video));
us_blank_destroy(stream->run->blank);
US_RING_DELETE_WITH_ITEMS(stream->run->http_jpeg_ring, us_frame_destroy);
free(stream->run);
free(stream);
}
void us_stream_loop(us_stream_s *stream) {
assert(stream->blank != NULL);
US_LOG_INFO("Using V4L2 device: %s", stream->dev->path);
US_LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
@@ -90,7 +82,6 @@ void us_stream_loop(us_stream_s *stream) {
for (us_workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
long double grab_after = 0;
unsigned fluency_passed = 0;
unsigned captured_fps = 0;
unsigned captured_fps_accum = 0;
long long captured_fps_second = 0;
@@ -111,7 +102,7 @@ void us_stream_loop(us_stream_s *stream) {
if (!ready_wr->job_failed) {
if (ready_wr->job_timely) {
_stream_expose_frame(stream, ready_job->dest, captured_fps);
_stream_expose_frame(stream, ready_job->dest);
US_LOG_PERF("##### Encoded JPEG exposed; worker=%s, latency=%.3Lf",
ready_wr->name, us_get_now_monotonic() - ready_job->dest->grab_ts);
} else {
@@ -136,9 +127,8 @@ void us_stream_loop(us_stream_s *stream) {
}
bool has_read;
bool has_write;
bool has_error;
const int selected = us_device_select(stream->dev, &has_read, &has_write, &has_error);
const int selected = us_device_select(stream->dev, &has_read, &has_error);
if (selected < 0) {
if (errno != EINTR) {
@@ -151,8 +141,6 @@ void us_stream_loop(us_stream_s *stream) {
# endif
} else {
if (has_read) {
US_LOG_DEBUG("Frame is ready");
# ifdef WITH_GPIO
us_gpio_set_stream_online(true);
# endif
@@ -175,10 +163,10 @@ void us_stream_loop(us_stream_s *stream) {
fluency_passed = 0;
if (now_second != captured_fps_second) {
captured_fps = captured_fps_accum;
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps_accum);
atomic_store(&stream->run->captured_fps, captured_fps_accum);
captured_fps_accum = 0;
captured_fps_second = now_second;
US_LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
}
captured_fps_accum += 1;
@@ -198,25 +186,13 @@ void us_stream_loop(us_stream_s *stream) {
}
}
// Это условие было добавлено из параноидальных соображений, и мы ни разу не сталкивались
// с подобными ошибками, кроме случая с libcamerify, который генерит эвенты на запись.
// Судя по всему, игнорирование has_write не делает никому плохо.
/*if (has_write) {
US_LOG_ERROR("Got unexpected writing event, seems device was disconnected");
if (has_error && us_device_consume_event(stream->dev) < 0) {
break;
}*/
if (has_error) {
US_LOG_INFO("Got V4L2 event");
if (us_device_consume_event(stream->dev) < 0) {
break;
}
}
}
}
us_workers_pool_destroy(pool);
us_device_switch_capturing(stream->dev, false);
us_device_close(stream->dev);
# ifdef WITH_GPIO
@@ -233,7 +209,7 @@ void us_stream_loop_break(us_stream_s *stream) {
bool us_stream_has_clients(us_stream_s *stream) {
return (
atomic_load(&_RUN(video->has_clients))
atomic_load(&_RUN(http_has_clients))
// has_clients синков НЕ обновляются в реальном времени
|| (stream->sink != NULL && atomic_load(&stream->sink->has_clients))
|| (_RUN(h264) != NULL && /*_RUN(h264->sink) == NULL ||*/ atomic_load(&_RUN(h264->sink->has_clients)))
@@ -241,79 +217,63 @@ bool us_stream_has_clients(us_stream_s *stream) {
}
static us_workers_pool_s *_stream_init_loop(us_stream_s *stream) {
us_workers_pool_s *pool = NULL;
int access_error = 0;
US_LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&_RUN(stop)));
int access_errno = 0;
while (!atomic_load(&_RUN(stop))) {
_stream_expose_frame(stream, NULL, 0);
atomic_store(&stream->run->captured_fps, 0);
_stream_expose_frame(stream, NULL);
if (access(stream->dev->path, R_OK|W_OK) < 0) {
if (access_error != errno) {
if (access_errno != errno) {
US_SEP_INFO('=');
US_LOG_PERROR("Can't access device");
US_LOG_INFO("Waiting for the device access ...");
access_error = errno;
access_errno = errno;
}
sleep(stream->error_delay);
continue;
} else {
US_SEP_INFO('=');
access_error = 0;
goto sleep_and_retry;
}
if ((pool = _stream_init_one(stream)) == NULL) {
US_LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
sleep(stream->error_delay);
} else {
break;
US_SEP_INFO('=');
access_errno = 0;
stream->dev->dma_export = (
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|| stream->enc->type == US_ENCODER_TYPE_M2M_IMAGE
|| _RUN(h264) != NULL
);
if (us_device_open(stream->dev) == 0) {
return us_encoder_workers_pool_init(stream->enc, stream->dev);
}
US_LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
sleep_and_retry:
sleep(stream->error_delay);
}
return pool;
return NULL;
}
static us_workers_pool_s *_stream_init_one(us_stream_s *stream) {
if (us_device_open(stream->dev) < 0) {
goto error;
}
if (
stream->enc->type == US_ENCODER_TYPE_M2M_VIDEO
|| stream->enc->type == US_ENCODER_TYPE_M2M_IMAGE
|| (_RUN(h264) && !us_is_jpeg(stream->dev->run->format))
) {
us_device_export_to_dma(stream->dev);
}
if (us_device_switch_capturing(stream->dev, true) < 0) {
goto error;
}
return us_encoder_workers_pool_init(stream->enc, stream->dev);
error:
us_device_close(stream->dev);
return NULL;
}
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigned captured_fps) {
# define VID(x_next) _RUN(video->x_next)
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame) {
us_stream_runtime_s *const run = stream->run;
us_blank_s *const blank = run->blank;
us_frame_s *new = NULL;
US_MUTEX_LOCK(VID(mutex));
if (frame != NULL) {
new = frame;
_RUN(last_as_blank_ts) = 0; // Останавливаем таймер
US_LOG_DEBUG("Exposed ALIVE video frame");
} else {
if (VID(frame->used == 0)) {
new = stream->blank; // Инициализация
_RUN(last_as_blank_ts) = 0;
unsigned width = stream->dev->run->width;
unsigned height = stream->dev->run->height;
if (width == 0 || height == 0) {
width = stream->dev->width;
height = stream->dev->height;
}
us_blank_draw(blank, "< NO SIGNAL >", width, height);
} else if (VID(frame->online)) { // Если переходим из online в offline
if (run->last_online) { // Если переходим из online в offline
if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку
new = stream->blank;
new = blank->jpeg;
US_LOG_INFO("Changed video frame to BLANK");
} else if (stream->last_as_blank > 0) { // // Если нужен таймер - запустим
_RUN(last_as_blank_ts) = us_get_now_monotonic() + stream->last_as_blank;
@@ -321,9 +281,8 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigne
} else { // last_as_blank == 0 - показываем последний фрейм вечно
US_LOG_INFO("Freezed last ALIVE video frame forever");
}
} else if (stream->last_as_blank < 0) {
new = stream->blank;
new = blank->jpeg;
// US_LOG_INFO("Changed video frame to BLANK");
}
@@ -332,28 +291,38 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigne
&& _RUN(last_as_blank_ts) != 0
&& _RUN(last_as_blank_ts) < us_get_now_monotonic()
) {
new = stream->blank;
_RUN(last_as_blank_ts) = 0; // // Останавливаем таймер
new = blank->jpeg;
_RUN(last_as_blank_ts) = 0; // Останавливаем таймер
US_LOG_INFO("Changed last ALIVE video frame to BLANK");
}
}
if (new != NULL) {
us_frame_copy(new, VID(frame));
int ri = -1;
while (
!atomic_load(&_RUN(stop))
&& ((ri = us_ring_producer_acquire(run->http_jpeg_ring, 0)) < 0)
) {
US_LOG_ERROR("Can't push JPEG to HTTP ring (no free slots)");
}
if (ri < 0) {
return;
}
VID(frame->online) = (frame != NULL);
VID(captured_fps) = captured_fps;
atomic_store(&VID(updated), true);
US_MUTEX_UNLOCK(VID(mutex));
us_frame_s *const dest = run->http_jpeg_ring->items[ri];
if (new == NULL) {
dest->used = 0;
dest->online = false;
} else {
us_frame_copy(new, dest);
dest->online = true;
}
run->last_online = (frame != NULL);
us_ring_producer_release(run->http_jpeg_ring, ri);
new = (frame ? frame : stream->blank);
_SINK_PUT(sink, new);
_SINK_PUT(sink, (frame != NULL ? frame : blank->jpeg));
if (frame == NULL) {
_SINK_PUT(raw_sink, stream->blank);
_H264_PUT(stream->blank, false);
_SINK_PUT(raw_sink, blank->raw);
_H264_PUT(blank->raw, false);
}
# undef VID
}

View File

@@ -35,6 +35,7 @@
#include "../libs/tools.h"
#include "../libs/threading.h"
#include "../libs/logging.h"
#include "../libs/ring.h"
#include "../libs/frame.h"
#include "../libs/memsink.h"
#include "../libs/device.h"
@@ -49,20 +50,17 @@
typedef struct {
us_frame_s *frame;
unsigned captured_fps;
atomic_bool updated;
pthread_mutex_t mutex;
us_ring_s *http_jpeg_ring;
atomic_bool http_has_clients;
atomic_uint captured_fps;
atomic_bool has_clients; // For slowdown
} us_video_s;
typedef struct {
us_video_s *video;
bool last_online;
long double last_as_blank_ts;
us_h264_stream_s *h264;
us_blank_s *blank;
atomic_bool stop;
} us_stream_runtime_s;
@@ -70,7 +68,6 @@ typedef struct {
us_device_s *dev;
us_encoder_s *enc;
us_frame_s *blank;
int last_as_blank;
bool slowdown;
unsigned error_delay;

View File

@@ -27,6 +27,10 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <linux/videodev2.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@@ -37,17 +41,17 @@
#include "../libs/tools.h"
#include "../libs/logging.h"
#include "../libs/frame.h"
#include "ftext.h"
#include "../libs/frametext.h"
static void _drm_vsync_callback(int fd, uint n_frame, uint sec, uint usec, void *v_run);
static int _drm_expose_raw(us_drm_s *drm, const us_frame_s *frame);
static void _drm_cleanup(us_drm_s *drm);
static void _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz);
static int _drm_init_video(us_drm_s *drm);
static int _drm_init_buffers(us_drm_s *drm);
static int _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz);
static int _drm_check_status(us_drm_s *drm);
static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz);
static int _drm_init_buffers(us_drm_s *drm);
static int _drm_start_video(us_drm_s *drm);
static u32 _find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs);
static const char *_connector_type_to_string(u32 type);
@@ -65,9 +69,8 @@ us_drm_s *us_drm_init(void) {
us_drm_runtime_s *run;
US_CALLOC(run, 1);
run->fd = -1;
run->conn_type = -1;
run->conn_type_id = -1;
run->ft = us_ftext_init();
run->status_fd = -1;
run->ft = us_frametext_init();
run->state = US_DRM_STATE_CLOSED;
us_drm_s *drm;
@@ -82,7 +85,7 @@ us_drm_s *us_drm_init(void) {
void us_drm_destroy(us_drm_s *drm) {
_drm_cleanup(drm);
us_ftext_destroy(drm->run->ft);
us_frametext_destroy(drm->run->ft);
US_DELETE(drm->run, free);
US_DELETE(drm, free); // cppcheck-suppress uselessAssignmentPtrArg
}
@@ -90,8 +93,7 @@ void us_drm_destroy(us_drm_s *drm) {
int us_drm_wait_for_vsync(us_drm_s *drm) {
us_drm_runtime_s *const run = drm->run;
_drm_ensure(drm, NULL, 0);
if (run->state != US_DRM_STATE_OK) {
if (_drm_ensure(drm, NULL, 0) < 0) {
return -1;
}
if (run->has_vsync) {
@@ -133,8 +135,7 @@ error:
int us_drm_expose(us_drm_s *drm, us_drm_expose_e ex, const us_frame_s *frame, float hz) {
us_drm_runtime_s *const run = drm->run;
_drm_ensure(drm, frame, hz);
if (run->state != US_DRM_STATE_OK) {
if (_drm_ensure(drm, frame, hz) < 0) {
return -1;
}
@@ -142,7 +143,7 @@ int us_drm_expose(us_drm_s *drm, us_drm_expose_e ex, const us_frame_s *frame, fl
bool msg_drawn = false;
# define DRAW_MSG(x_msg) { \
us_ftext_draw(run->ft, (x_msg), mode->hdisplay, mode->vdisplay); \
us_frametext_draw(run->ft, (x_msg), mode->hdisplay, mode->vdisplay); \
frame = run->ft->frame; \
msg_drawn = true; \
}
@@ -203,21 +204,18 @@ static int _drm_expose_raw(us_drm_s *drm, const us_frame_s *frame) {
_D_LOG_DEBUG("Exposing%s framebuffer n_buf=%u, vsync=%d ...",
(frame == NULL ? " EMPTY" : ""), run->next_n_buf, run->has_vsync);
run->has_vsync = false;
if (frame == NULL) {
memset(buf->data, 0, buf->allocated);
} else {
memcpy(buf->data, frame->data, US_MIN(frame->used, buf->allocated));
}
run->has_vsync = false;
const int retval = drmModePageFlip(
run->fd, run->crtc_id, buf->id,
DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_ASYNC,
run);
if (retval == 0) {
run->next_n_buf = (run->next_n_buf + 1) % run->n_bufs;
}
run->fd, run->crtc_id, buf->id,
DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_PAGE_FLIP_ASYNC,
run);
run->next_n_buf = (run->next_n_buf + 1) % run->n_bufs;
return retval;
}
@@ -231,7 +229,7 @@ static void _drm_cleanup(us_drm_s *drm) {
run->saved_crtc->crtc_id, run->saved_crtc->buffer_id,
run->saved_crtc->x, run->saved_crtc->y,
&run->conn_id, 1, &run->saved_crtc->mode
) < 0) {
) < 0 && errno != ENOENT) {
_D_LOG_PERROR("Can't restore CRTC");
}
drmModeFreeCrtc(run->saved_crtc);
@@ -258,57 +256,52 @@ static void _drm_cleanup(us_drm_s *drm) {
run->n_bufs = 0;
}
if (run->fd >= 0) {
if (close(run->fd) < 0) {
_D_LOG_PERROR("Can't close device");
}
run->fd = -1;
}
US_CLOSE_FD(run->status_fd);
US_CLOSE_FD(run->fd);
run->crtc_id = 0;
run->next_n_buf = 0;
run->has_vsync = false;
if (run->state == US_DRM_STATE_OK) {
_D_LOG_INFO("Stopped");
run->state = US_DRM_STATE_CLOSED;
}
run->state = US_DRM_STATE_CLOSED;
}
static void _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz) {
static int _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz) {
us_drm_runtime_s *const run = drm->run;
if (frame == NULL) {
if (run->state == US_DRM_STATE_OK) {
return;
}
} else {
if (
run->p_width == frame->width
&& run->p_height == frame->height
&& run->p_hz == hz
&& run->state <= US_DRM_STATE_RETRY
) {
return;
}
switch (_drm_check_status(drm)) {
case 0: break;
case -2: goto unplugged;
default: goto error;
}
if (frame == NULL && run->state == US_DRM_STATE_OK) {
return 0;
} else if (
frame != NULL
&& run->p_width == frame->width
&& run->p_height == frame->height
&& run->p_hz == hz
&& run->state <= US_DRM_STATE_CLOSED
) {
return (run->state == US_DRM_STATE_OK ? 0 : -1);
}
const us_drm_state_e saved_state = run->state;
_drm_cleanup(drm);
if (run->state <= US_DRM_STATE_RETRY) {
_D_LOG_INFO("Configuring DRM device ...");
if (saved_state > US_DRM_STATE_CLOSED) {
run->state = saved_state;
}
if (frame == NULL) {
run->p_width = 0; // Find the native resolution
run->p_height = 0;
} else {
run->p_width = frame->width;
run->p_height = frame->height;
}
run->p_width = (frame != NULL ? frame->width : 0); // 0 for find the native resolution
run->p_height = (frame != NULL ? frame->height : 0);
run->p_hz = hz;
if ((run->fd = open(drm->path, O_RDWR|O_CLOEXEC|O_NONBLOCK)) < 0) {
_D_LOG_INFO("Configuring DRM device ...");
if ((run->fd = open(drm->path, O_RDWR | O_CLOEXEC | O_NONBLOCK)) < 0) {
_D_LOG_PERROR("Can't open DRM device");
goto error;
}
@@ -328,59 +321,178 @@ static void _drm_ensure(us_drm_s *drm, const us_frame_s *frame, float hz) {
// CHECK_CAP(DRM_CAP_PRIME);
# undef CHECK_CAP
if (_drm_find_sink(drm, run->p_width, run->p_height, run->p_hz) < 0) {
goto error;
switch (_drm_find_sink(drm, run->p_width, run->p_height, run->p_hz)) {
case 0: break;
case -2: goto unplugged;
default: goto error;
}
if (run->crtc_id == 0) {
if (run->state != US_DRM_STATE_NO_DISPLAY) {
_D_LOG_INFO("Using %s mode: Display unplugged", drm->port);
run->state = US_DRM_STATE_NO_DISPLAY;
}
goto error;
} else {
const float mode_hz = _get_refresh_rate(&run->mode);
if (frame == NULL) {
run->p_width = run->mode.hdisplay;
run->p_height = run->mode.vdisplay;
run->p_hz = mode_hz;
}
_D_LOG_INFO("Using %s mode: %ux%up%.02f",
drm->port, run->mode.hdisplay, run->mode.vdisplay, mode_hz);
const float mode_hz = _get_refresh_rate(&run->mode);
if (frame == NULL) {
run->p_width = run->mode.hdisplay;
run->p_height = run->mode.vdisplay;
run->p_hz = mode_hz;
}
_D_LOG_INFO("Using %s mode: %ux%up%.02f",
drm->port, run->mode.hdisplay, run->mode.vdisplay, mode_hz);
if (_drm_init_buffers(drm) < 0) {
goto error;
}
if (_drm_init_video(drm) < 0) {
if (_drm_start_video(drm) < 0) {
goto error;
}
_D_LOG_INFO("Showing ...");
run->state = US_DRM_STATE_OK;
return;
return 0;
error:
if (run->state == US_DRM_STATE_CLOSED) {
_D_LOG_ERROR("Device destroyed due an error (prepare)");
_drm_cleanup(drm);
_D_LOG_ERROR("Device destroyed due an error (ensure)");
return -1;
unplugged:
if (run->state != US_DRM_STATE_NO_DISPLAY) {
_D_LOG_INFO("Display %s is not plugged", drm->port);
}
_drm_cleanup(drm);
run->state = US_DRM_STATE_NO_DISPLAY;
return -2;
}
static int _drm_init_video(us_drm_s *drm) {
static int _drm_check_status(us_drm_s *drm) {
us_drm_runtime_s *run = drm->run;
if (run->status_fd < 0) {
struct stat st;
if (stat(drm->path, &st) < 0) {
_D_LOG_PERROR("Can't stat() DRM device");
goto error;
}
const uint mi = minor(st.st_rdev);
char path[128];
US_SNPRINTF(path, 127, "/sys/class/drm/card%u-%s/status", mi, drm->port);
if ((run->status_fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
_D_LOG_PERROR("Can't open DRM device status file: %s", path);
goto error;
}
}
char status_ch;
if (read(run->status_fd, &status_ch, 1) != 1) {
_D_LOG_PERROR("Can't read connector status");
goto error;
}
if (lseek(run->status_fd, 0, SEEK_SET) != 0) {
_D_LOG_PERROR("Can't rewind connector status");
goto error;
}
return (status_ch == 'd' ? -2 : 0);
error:
US_CLOSE_FD(run->status_fd);
return -1;
}
static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) {
us_drm_runtime_s *const run = drm->run;
run->saved_crtc = drmModeGetCrtc(run->fd, run->crtc_id);
_D_LOG_DEBUG("Setting up CRTC ...");
if (drmModeSetCrtc(run->fd, run->crtc_id, run->bufs[0].id, 0, 0, &run->conn_id, 1, &run->mode) < 0) {
_D_LOG_PERROR("Can't set CRTC");
return -1;
run->crtc_id = 0;
_D_LOG_DEBUG("Trying to find the appropriate sink ...");
drmModeRes *res = drmModeGetResources(run->fd);
if (res == NULL) {
_D_LOG_PERROR("Can't get resources info");
goto done;
}
if (_drm_expose_raw(drm, NULL) < 0) {
_D_LOG_PERROR("Can't flip the first page");
return -1;
if (res->count_connectors <= 0) {
_D_LOG_ERROR("Can't find any connectors");
goto done;
}
return 0;
for (int ci = 0; ci < res->count_connectors; ++ci) {
drmModeConnector *conn = drmModeGetConnector(run->fd, res->connectors[ci]);
if (conn == NULL) {
_D_LOG_PERROR("Can't get connector index=%d", ci);
goto done;
}
char port[32];
US_SNPRINTF(port, 31, "%s-%u",
_connector_type_to_string(conn->connector_type),
conn->connector_type_id);
if (strcmp(port, drm->port) != 0) {
drmModeFreeConnector(conn);
continue;
}
_D_LOG_DEBUG("Found connector for port %s: conn_type=%d, conn_type_id=%d",
drm->port, conn->connector_type, conn->connector_type_id);
if (conn->connection != DRM_MODE_CONNECTED) {
_D_LOG_DEBUG("Display is not connected");
drmModeFreeConnector(conn);
goto done;
}
drmModeModeInfo *best = NULL;
drmModeModeInfo *closest = NULL;
drmModeModeInfo *pref = NULL;
for (int mi = 0; mi < conn->count_modes; ++mi) {
drmModeModeInfo *const mode = &conn->modes[mi];
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
continue; // Paranoia for size and discard interlaced
}
const float mode_hz = _get_refresh_rate(mode);
if (mode->hdisplay == width && mode->vdisplay == height) {
best = mode; // Any mode with exact resolution
if (hz > 0 && mode_hz == hz) {
break; // Exact mode with same freq
}
}
if (mode->hdisplay == width && mode->vdisplay < height) {
if (closest == NULL || _get_refresh_rate(closest) != hz) {
closest = mode; // Something like 1920x1080p60 for 1920x1200p60 source
}
}
if (pref == NULL && (mode->type & DRM_MODE_TYPE_PREFERRED)) {
pref = mode; // Preferred mode if nothing is found
}
}
if (best == NULL) { best = closest; }
if (best == NULL) { best = pref; }
if (best == NULL) { best = (conn->count_modes > 0 ? &conn->modes[0] : NULL); }
if (best == NULL) {
_D_LOG_ERROR("Can't find any appropriate resolutions");
drmModeFreeConnector(conn);
goto unplugged;
}
assert(best->hdisplay > 0);
assert(best->vdisplay > 0);
u32 taken_crtcs = 0; // Unused here
if ((run->crtc_id = _find_crtc(run->fd, res, conn, &taken_crtcs)) == 0) {
_D_LOG_ERROR("Can't find CRTC");
drmModeFreeConnector(conn);
goto done;
}
run->conn_id = conn->connector_id;
memcpy(&run->mode, best, sizeof(drmModeModeInfo));
drmModeFreeConnector(conn);
break;
}
done:
drmModeFreeResources(res);
return (run->crtc_id > 0 ? 0 : -1);
unplugged:
drmModeFreeResources(res);
return -2;
}
static int _drm_init_buffers(us_drm_s *drm) {
@@ -432,119 +544,25 @@ static int _drm_init_buffers(us_drm_s *drm) {
_D_LOG_PERROR("Can't map buffer=%u", n_buf);
return -1;
}
memset(buf->data, 0, create.size);
buf->allocated = create.size;
}
return 0;
}
static int _drm_find_sink(us_drm_s *drm, uint width, uint height, float hz) {
static int _drm_start_video(us_drm_s *drm) {
us_drm_runtime_s *const run = drm->run;
int retval = -1;
run->crtc_id = 0;
_D_LOG_DEBUG("Trying to find the appropriate sink ...");
drmModeRes *res = drmModeGetResources(run->fd);
if (res == NULL) {
_D_LOG_PERROR("Can't get resources info");
goto error;
run->saved_crtc = drmModeGetCrtc(run->fd, run->crtc_id);
_D_LOG_DEBUG("Setting up CRTC ...");
if (drmModeSetCrtc(run->fd, run->crtc_id, run->bufs[0].id, 0, 0, &run->conn_id, 1, &run->mode) < 0) {
_D_LOG_PERROR("Can't set CRTC");
return -1;
}
_D_LOG_DEBUG("Found %u connectors", res->count_connectors);
for (int ci = 0; ci < res->count_connectors; ++ci) {
drmModeConnector *conn = drmModeGetConnector(run->fd, res->connectors[ci]);
if (conn == NULL) {
_D_LOG_PERROR("Can't get connector index=%d", ci);
goto error;
}
if (run->conn_type < 0) {
char port[32];
US_SNPRINTF(port, 31, "%s-%u",
_connector_type_to_string(conn->connector_type),
conn->connector_type_id);
if (!strcmp(port, drm->port)) {
run->conn_type = conn->connector_type;
run->conn_type_id = conn->connector_type_id;
}
}
if (
(int)conn->connector_type != run->conn_type
|| (int)conn->connector_type_id != run->conn_type_id
) {
drmModeFreeConnector(conn);
continue;
}
_D_LOG_DEBUG("Found requested connector for port %s: conn_type=%d, conn_type_id=%d",
drm->port, run->conn_type, run->conn_type_id);
if (conn->connection != DRM_MODE_CONNECTED) {
_D_LOG_DEBUG("Display is not connected");
drmModeFreeConnector(conn);
break;
}
drmModeModeInfo *best = NULL;// (conn->count_modes > 0 ? &conn->modes[0] : NULL);
drmModeModeInfo *closest = NULL;
drmModeModeInfo *pref = NULL;
for (int mi = 0; mi < conn->count_modes; ++mi) {
drmModeModeInfo *const mode = &conn->modes[mi];
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
continue; // Paranoia for size and discard interlaced
}
const float mode_hz = _get_refresh_rate(mode);
if (mode->hdisplay == width && mode->vdisplay == height) {
best = mode; // Any mode with exact resolution
if (hz > 0 && mode_hz == hz) {
break; // Exact mode with same freq
}
}
if (mode->hdisplay == width && mode->vdisplay < height) {
if (closest == NULL || _get_refresh_rate(closest) != hz) {
closest = mode; // Something like 1920x1080p60 for 1920x1200p60 source
}
}
if (pref == NULL && (mode->type & DRM_MODE_TYPE_PREFERRED)) {
pref = mode; // Preferred mode if nothing is found
}
}
if (best == NULL) {
best = closest;
}
if (best == NULL) {
best = pref;
}
if (best == NULL) {
best = (conn->count_modes > 0 ? &conn->modes[0] : NULL);
}
if (best == NULL) {
_D_LOG_ERROR("Can't find any resolutions");
drmModeFreeConnector(conn);
break;
}
assert(best->hdisplay > 0);
assert(best->vdisplay > 0);
u32 taken_crtcs = 0; // Unused here
if ((run->crtc_id = _find_crtc(run->fd, res, conn, &taken_crtcs)) == 0) {
_D_LOG_ERROR("Can't find CRTC");
drmModeFreeConnector(conn);
goto error;
}
memcpy(&run->mode, best, sizeof(drmModeModeInfo));
run->conn_id = conn->connector_id;
drmModeFreeConnector(conn);
break;
if (_drm_expose_raw(drm, NULL) < 0) {
_D_LOG_PERROR("Can't flip the first page");
return -1;
}
retval = 0;
error:
drmModeFreeResources(res);
return retval;
return 0;
}
static u32 _find_crtc(int fd, drmModeRes *res, drmModeConnector *conn, u32 *taken_crtcs) {
@@ -574,22 +592,26 @@ static const char *_connector_type_to_string(u32 type) {
switch (type) {
# define CASE_NAME(x_suffix, x_name) \
case DRM_MODE_CONNECTOR_##x_suffix: return x_name;
CASE_NAME(VGA, "VGA");
CASE_NAME(DVII, "DVI-I");
CASE_NAME(DVID, "DVI-D");
CASE_NAME(DVIA, "DVI-A");
CASE_NAME(Composite, "Composite");
CASE_NAME(SVIDEO, "SVIDEO");
CASE_NAME(LVDS, "LVDS");
CASE_NAME(Component, "Component");
CASE_NAME(9PinDIN, "DIN");
CASE_NAME(DisplayPort, "DP");
CASE_NAME(HDMIA, "HDMI-A");
CASE_NAME(HDMIB, "HDMI-B");
CASE_NAME(TV, "TV");
CASE_NAME(eDP, "eDP");
CASE_NAME(VIRTUAL, "Virtual");
CASE_NAME(DSI, "DSI");
CASE_NAME(VGA, "VGA");
CASE_NAME(DVII, "DVI-I");
CASE_NAME(DVID, "DVI-D");
CASE_NAME(DVIA, "DVI-A");
CASE_NAME(Composite, "Composite");
CASE_NAME(SVIDEO, "SVIDEO");
CASE_NAME(LVDS, "LVDS");
CASE_NAME(Component, "Component");
CASE_NAME(9PinDIN, "DIN");
CASE_NAME(DisplayPort, "DP");
CASE_NAME(HDMIA, "HDMI-A");
CASE_NAME(HDMIB, "HDMI-B");
CASE_NAME(TV, "TV");
CASE_NAME(eDP, "eDP");
CASE_NAME(VIRTUAL, "Virtual");
CASE_NAME(DSI, "DSI");
CASE_NAME(DPI, "DPI");
CASE_NAME(WRITEBACK, "Writeback");
CASE_NAME(SPI, "SPI");
CASE_NAME(USB, "USB");
case DRM_MODE_CONNECTOR_Unknown: break;
# undef CASE_NAME
}

View File

@@ -27,8 +27,7 @@
#include "../libs/types.h"
#include "../libs/frame.h"
#include "ftext.h"
#include "../libs/frametext.h"
typedef enum {
@@ -40,7 +39,6 @@ typedef enum {
typedef enum {
US_DRM_STATE_OK = 0,
US_DRM_STATE_CLOSED,
US_DRM_STATE_RETRY, // Not used directly
US_DRM_STATE_NO_DISPLAY,
} us_drm_state_e;
@@ -54,19 +52,19 @@ typedef struct {
} us_drm_buffer_s;
typedef struct {
int status_fd;
int fd;
int conn_type;
int conn_type_id;
u32 crtc_id;
drmModeModeInfo mode;
u32 conn_id;
drmModeModeInfo mode;
us_drm_buffer_s *bufs;
uint n_bufs;
drmModeCrtc *saved_crtc;
uint next_n_buf;
bool has_vsync;
us_ftext_s *ft;
us_frametext_s *ft;
uint p_width;
uint p_height;

View File

@@ -194,7 +194,7 @@ static void _main_loop(void) {
if (us_drm_wait_for_vsync(drm) == 0) {
us_drm_expose(drm, US_DRM_EXPOSE_BUSY, NULL, 0);
}
if (dev->run->capturing) {
if (dev->run->fd >= 0) {
goto close;
} else {
_slowdown();
@@ -205,15 +205,9 @@ static void _main_loop(void) {
if (us_device_open(dev) < 0) {
goto close;
}
if (us_device_switch_capturing(dev, true) < 0) {
goto close;
}
while (!atomic_load(&_g_stop)) {
if (atomic_load(&_g_ustreamer_online)) {
if (us_drm_wait_for_vsync(drm) == 0) {
us_drm_expose(drm, US_DRM_EXPOSE_BUSY, NULL, 0);
}
goto close;
}
@@ -223,9 +217,8 @@ static void _main_loop(void) {
}
bool has_read;
bool has_write;
bool has_error;
const int selected = us_device_select(dev, &has_read, &has_write, &has_error);
const int selected = us_device_select(dev, &has_read, &has_error);
if (selected < 0) {
if (errno != EINTR) {
@@ -239,32 +232,28 @@ static void _main_loop(void) {
}
} else {
if (has_read) {
US_LOG_DEBUG("Frame is ready");
us_hw_buffer_s *hw;
const int buf_index = us_device_grab_buffer(dev, &hw);
if (buf_index >= 0) {
if (us_drm_expose(drm, US_DRM_EXPOSE_FRAME, &hw->raw, dev->run->hz) < 0) {
_slowdown();
continue;
}
const int exposed = us_drm_expose(drm, US_DRM_EXPOSE_FRAME, &hw->raw, dev->run->hz);
if (us_device_release_buffer(dev, hw) < 0) {
goto close;
}
} else if (buf_index == -2) {
if (exposed < 0) {
_slowdown();
continue;
}
} else if (buf_index != -2) { // -2 for broken frame
goto close;
}
}
if (has_error) {
US_LOG_INFO("Got V4L2 event");
if (us_device_consume_event(dev) < 0) {
goto close;
}
if (has_error && us_device_consume_event(dev) < 0) {
goto close;
}
}
}
close:
us_device_switch_capturing(dev, false);
us_device_close(dev);
_slowdown();
}
@@ -289,7 +278,7 @@ static void *_follower_thread(void *v_unix_follow) {
const bool online = !connect(fd, (struct sockaddr *)&addr, sizeof(addr));
atomic_store(&_g_ustreamer_online, online);
US_CLOSE_FD(fd, close); // cppcheck-suppress unreadVariable
US_CLOSE_FD(fd); // cppcheck-suppress unreadVariable
usleep(200 * 1000);
}

View File

@@ -1,94 +0,0 @@
#!/usr/bin/env -S python3 -B
# ========================================================================== #
# #
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
# #
# Copyright (C) 2018-2023 Maxim Devaev <mdevaev@gmail.com> #
# #
# This program 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 3 of the License, or #
# (at your option) any later version. #
# #
# This program 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 this program. If not, see <https://www.gnu.org/licenses/>. #
# #
# ========================================================================== #
import sys
import os
import io
import struct
import common
# =====
def _get_jpeg_size(data: bytes) -> tuple[int, int]:
# https://sheep.horse/2013/9/finding_the_dimensions_of_a_jpeg_file_in_python.html
stream = io.BytesIO(data)
while True:
marker = struct.unpack(">H", stream.read(2))[0]
if marker == 0xFFD9:
raise RuntimeError("Can't find jpeg size")
if (
marker == 0xFFD8 # Start of image
or marker == 0xFF01 # Private marker
or 0xFFD0 <= marker <= 0xFFD7 # Restart markers
):
continue
# All other markers specify chunks with lengths
length = struct.unpack(">H", stream.read(2))[0]
if marker == 0xFFC0: # Baseline DCT chunk, has the info we want
(_, height, width) = struct.unpack(">BHH", stream.read(5))
return (width, height)
# Not the chunk we want, skip it
stream.seek(length - 2, 1)
# =====
def main() -> None:
assert len(sys.argv) == 4, f"{sys.argv[0]} <file.jpeg> <file.h> <name>"
jpeg_path = sys.argv[1]
c_path = sys.argv[2]
h_path = os.path.basename(c_path[:-2]) + ".h"
name = sys.argv[3]
with open(jpeg_path, "rb") as file:
data = file.read()
(width, height) = _get_jpeg_size(data)
data_text = "{\n\t" + ",\n\t".join(
", ".join(
f"0x{ch:02X}"
for ch in data[index:index + 20]
)
for index in range(0, len(data), 20)
) + ",\n}"
text = f"{common.C_PREPEND}\n"
text += f"#include \"{h_path}\"\n\n\n"
text += f"const unsigned US_{name}_JPEG_WIDTH = {width};\n"
text += f"const unsigned US_{name}_JPEG_HEIGHT = {height};\n\n"
text += f"const size_t US_{name}_JPEG_DATA_SIZE = {len(data)};\n"
text += f"const uint8_t US_{name}_JPEG_DATA[] = {data_text};\n"
with open(c_path, "w") as file:
file.write(text)
# =====
if __name__ == "__main__":
main()