New Upstream Release - ustreamer

Ready changes

Summary

Merged new upstream version: 5.38 (was: 5.34).

Resulting package

Built on 2023-05-20T04:14 (took 4m9s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases ustreamer-dbgsymapt install -t fresh-releases ustreamer

Lintian Result

Diff

diff --git a/Makefile b/Makefile
index 332d2d4..9f3ea84 100644
--- a/Makefile
+++ b/Makefile
@@ -71,6 +71,7 @@ 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
 
 
diff --git a/README.md b/README.md
index 2d2cfcd..4601a6e 100644
--- a/README.md
+++ b/README.md
@@ -2,10 +2,8 @@
 [![CI](https://github.com/pikvm/ustreamer/workflows/CI/badge.svg)](https://github.com/pikvm/ustreamer/actions?query=workflow%3ACI)
 [![Discord](https://img.shields.io/discord/580094191938437144?logo=discord)](https://discord.gg/bpmXfz5)
 
-[[Русская версия]](README.ru.md)
-
 µStreamer is a lightweight and very quick server to stream [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) video from any V4L2 device to the net. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc.
-µStreamer is a part of the [Pi-KVM](https://github.com/pikvm/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
+µStreamer is a part of the [PiKVM](https://github.com/pikvm/pikvm) project designed to stream [VGA](https://www.amazon.com/dp/B0126O0RDC) and [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) screencast hardware data with the highest resolution and FPS possible.
 
 µStreamer is very similar to [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) with ```input_uvc.so``` and ```output_http.so``` plugins, however, there are some major differences. The key ones are:
 
@@ -34,16 +32,33 @@ Footnotes:
 If you're going to live-stream from your backyard webcam and need to control it, use mjpg-streamer. If you need a high-quality image with high FPS - µStreamer for the win.
 
 -----
-# Building
+# Installation
+
+## Building
+You need to download the µStreamer onto your system and build it from the sources.
+
+* AUR has a package for Arch Linux: https://aur.archlinux.org/packages/ustreamer.
+* Fedora: https://src.fedoraproject.org/rpms/ustreamer.
+* Ubuntu: https://packages.ubuntu.com/jammy/ustreamer.
+* Debian: https://packages.debian.org/sid/ustreamer
+* FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
+
+### Preconditions
 You'll need  ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg9```/```libjpeg-turbo``` and ```libbsd``` (only for Linux).
 
 * Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
-* Raspbian: `sudo apt install libevent-dev libjpeg8-dev libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1`.
+* Raspbian: `sudo apt install libevent-dev libjpeg9-dev libbsd-dev`. Add `libgpiod-dev` for `WITH_GPIO=1` and `libsystemd-dev` for `WITH_SYSTEMD=1` and `libasound2-dev libspeex-dev libspeexdsp-dev libopus-dev` for `WITH_JANUS=1`.
 * Debian/Ubuntu: `sudo apt install build-essential libevent-dev libjpeg-dev libbsd-dev`.
 * Alpine: `sudo apk add libevent-dev libbsd-dev libjpeg-turbo-dev musl-dev`. Build with `WITH_PTHREAD_NP=0`.
 
 To enable GPIO support install [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). For the similar error with ```setproctitle()``` add option ```WITH_SETPROCTITLE=0```.
 
+> **Note**
+> Raspian: In case your version of Raspian is too old for there to be a libjpeg9 package, use `libjpeg8-dev` instead: `E: Package 'libjpeg9-dev' has no installation candidate`.
+
+### Make
+The most convenient process is to clone the µStreamer Git repository onto your system. If you don't have Git installed and don't want to install it either, you can download and unzip the sources from GitHub using `wget https://github.com/pikvm/ustreamer/archive/refs/heads/master.zip`.
+
 ```
 $ git clone --depth=1 https://github.com/pikvm/ustreamer
 $ cd ustreamer
@@ -51,9 +66,15 @@ $ make
 $ ./ustreamer --help
 ```
 
-AUR has a package for Arch Linux: https://aur.archlinux.org/packages/ustreamer.
+## Update
+Assuming you have a µStreamer clone as discussed above you can update µStreamer as follows.
 
-FreeBSD port: https://www.freshports.org/multimedia/ustreamer.
+```
+$ cd ustreamer
+$ git pull
+$ make clean
+$ make
+```
 
 -----
 # Usage
@@ -81,8 +102,57 @@ $ ./ustreamer \
 
 You can always view the full list of options with ```ustreamer --help```.
 
+-----
+# Docker (Raspberry Pi 4 HDMI)
+
+## Preparations
+Add following lines to /boot/firmware/usercfg.txt:
+
+```
+gpu_mem=128
+dtoverlay=tc358743
+```
+
+Check size of CMA:
+
+```bash
+$ dmesg | grep cma-reserved
+[    0.000000] Memory: 7700524K/8244224K available (11772K kernel code, 1278K rwdata, 4320K rodata, 4096K init, 1077K bss, 281556K reserved, 262144K cma-reserved)
+```
+
+If it is smaller than 128M add following to /boot/firmware/cmdline.txt:
+
+```
+cma=128M
+```
+
+Save changes and reboot.
+
+## Launch
+Start container:
+
+```bash
+$ docker run --device /dev/video0:/dev/video0 -e EDID=1 -p 8080:8080 pikvm/ustreamer:latest
+```
+
+Then access the web interface at port 8080 (e.g. http://raspberrypi.local:8080).
+
+## Custom config
+```bash
+$ docker run --rm pikvm/ustreamer:latest \
+    --format=uyvy \
+    --workers=3 \
+    --persistent \
+    --dv-timings \
+    --drop-same-frames=30
+```
+
+## EDID
+Add `-e EDID=1` to set HDMI EDID before starting ustreamer. Use together with `-e EDID_HEX=xx` to specify custom EDID data.
+
 -----
 # Raspberry Pi Camera Example
+
 Example usage for the Raspberry Pi v1 camera:
 ```bash
 $ sudo modprobe bcm2835-v4l2
diff --git a/README.ru.md b/README.ru.md
deleted file mode 100644
index 128a8dc..0000000
--- a/README.ru.md
+++ /dev/null
@@ -1,145 +0,0 @@
-# µStreamer
-[![CI](https://github.com/pikvm/ustreamer/workflows/CI/badge.svg)](https://github.com/pikvm/ustreamer/actions?query=workflow%3ACI)
-[![Discord](https://img.shields.io/discord/580094191938437144?logo=discord)](https://discord.gg/bpmXfz5)
-
-[[English version]](README.md)
-
-
-µStreamer - это маленький и очень быстрый сервер, который позволяет организовать трансляцию видео в формате [MJPEG](https://en.wikipedia.org/wiki/Motion_JPEG) с любого устройства V4L2 в сеть. Этот формат нативно поддерживается всеми современными браузерами и большинством приложений для просмотра видео (mplayer, VLC и так далее). µStreamer был разработан в рамках проекта [Pi-KVM](https://github.com/pikvm/pikvm) специально для стриминга с устройств видеозахвата [VGA](https://www.amazon.com/dp/B0126O0RDC) и [HDMI](https://auvidea.com/b101-hdmi-to-csi-2-bridge-15-pin-fpc/) с максимально возможным разрешением и FPS, которые только позволяет железо.
-
-Функционально µStreamer очень похож на [mjpg-streamer](https://github.com/jacksonliam/mjpg-streamer) при использовании им плагинов ```input_uvc.so``` и ```output_http.so```, однако имеет ряд серьезных отличий. Основные приведены в этой таблице:
-
-| **Фича** | **µStreamer** | **mjpg-streamer** |
-|----------|---------------|-------------------|
-| Многопоточное кодирование JPEG | ✔ | ✘ |
-| Аппаратное кодирование на Raspberry Pi | ✔ | ✘ |
-| Поведение при физическом отключении<br>устройства от сервера во время работы | ✔ Транслирует черный экран<br>с надписью ```NO SIGNAL```,<br>пока устройство не будет подключено снова | ✘ Прерывает трансляцию <sup>1</sup> |
-| Поддержка [DV-таймингов](https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/dv-timings.html) - возможности<br>изменения  параметров разрешения<br>трансляции на лету по сигналу<br>источника (устройства видеозахвата) | ✔ | ☹ Условно есть <sup>1</sup> |
-| Возможность пропуска фреймов при передаче<br>статического изображения по HTTP<br>для экономии трафика | ✔ <sup>2</sup> | ✘ |
-| Стрим через UNIX domain socket | ✔ | ✘ |
-| Systemd socket activation | ✔ | ✘ |
-| Дебаг-логи без перекомпиляции,<br>логгирование статистики производительности,<br>возможность получения параметров<br>трансляции по HTTP | ✔ | ✘ |
-| Возможность сервить файлы встроенным<br>HTTP-сервером  | ✔ | ☹ Нет каталогов |
-| Вывод сигналов о состоянии стрима на GPIO<br>с помощью [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) | ✔ | ✘ |
-| Поддержка контролов веб-камер (фокус,<br> движение сервами) и всяких настроек,<br> типа яркости, через HTTP | ✘ | ✔ |
-| Совместимость с API mjpg-streamer'а | ✔ | :) |
-
-Сносочки:
-  * ```1``` Еще до написания µStreamer, я запилил [патч](https://github.com/jacksonliam/mjpg-streamer/pull/164), добавляющий в mjpg-streamer поддержку DV-таймингов и предотвращающий его зависание при отключении устройства. Однако патч, увы, далек от совершенства и я не гарантирую его стопроцентную работоспособность, поскольку код mjpg-streamer чрезвычайно запутан и очень плохо структурирован. Учитывая это, а также то, что в дальнейшем мне потребовались многопоточность и аппаратное кодирование JPEG, было принято решение написать свой стрим-сервер с нуля, чтобы не тратить силы на поддержку лишнего легаси.
-
-  * ```2``` Это фича позволяет в несколько раз снизить объем исходящего трафика при трансляции HDMI, однако немного увеличивает загрузку процессора. Суть в том, что HDMI - полностью цифровой интерфейс, и новый захваченный фрейм может быть идентичен предыдущему в точности до байта. В этом случае нет нужды передавать одну и ту же картинку по сети несколько раз в секунду. При использовании опции `--drop-same-frames=20`, µStreamer будет дропать все одинаковые фреймы, но не более 20 подряд. Новый фрейм сравнивается с предыдущим сначала по длине, а затем помощью ```memcmp()```.
-
------
-# TL;DR
-Если вам нужно вещать стрим с уличной камеры и управлять ее параметрами - возьмите mjpg-streamer. Если же вам нужно очень качественное изображение с высоким FPS - µStreamer ваш бро.
-
------
-# Сборка
-Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo``` и ```libbsd``` (только для Linux).
-
-* Arch: `sudo pacman -S libevent libjpeg-turbo libutil-linux libbsd`.
-* Raspbian: `sudo apt install libevent-dev libjpeg8-dev libbsd-dev`.  Добавьте `libgpiod` для `WITH_GPIO=1` и `libsystemd-dev` для `WITH_SYSTEMD=1`.
-* Debian/Ubuntu: `sudo apt install build-essential libevent-dev libjpeg-dev libbsd-dev`.
-
-Для включения сборки с поддержкой GPIO установите [libgpiod](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). При аналогичной ошибке с функцией ```setproctitle()``` добавьте параметр ```WITH_SETPROCTITLE=0```.
-
-```
-$ git clone --depth=1 https://github.com/pikvm/ustreamer
-$ cd ustreamer
-$ make
-$ ./ustreamer --help
-```
-
-Для Arch Linux в AUR есть готовый пакет: https://aur.archlinux.org/packages/ustreamer.
-
-Порт для FreeBSD: https://www.freshports.org/multimedia/ustreamer.
-
------
-# Использование
-**Для аппаратного кодирования M2M на Raspberry Pi, вам нужно ядро минимальной версии 5.15.32. Поддержка OpenMAX и MMAL для более старых ядер объявлена устаревшей и была удалена.**
-
-Будучи запущенным без аргументов, ```ustreamer``` попробует открыть устройство ```/dev/video0``` с разрешением 640x480 и начать трансляцию на ```http://127.0.0.1:8080```. Это поведение может быть изменено с помощью опций ```--device```, ```--host``` и ```--port```. Пример вещания на всю сеть по 80-м порту:
-```
-# ./ustreamer --device=/dev/video1 --host=0.0.0.0 --port=80
-```
-
-:exclamation: Обратите внимание, что начиная с версии µStreamer v2.0 кросс-доменные запросы были выключены по умолчанию [по соображениям безопасности](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). Чтобы включить старое поведение, используйте опцию `--allow-origin=\*`.
-
-Рекомендуемый способ запуска µStreamer для работы с [Auvidea B101](https://www.raspberrypi.org/forums/viewtopic.php?f=38&t=120702&start=400#p1339178) на Raspberry Pi:
-```bash
-$ ./ustreamer \
-    --format=uyvy \ # Настройка входного формата устройства
-    --encoder=m2m-image \ # Аппаратное кодирование с помощью драйвера V4L2 M2M
-    --workers=3 \ # Максимум воркеров
-    --persistent \ # Не переинициализировать устройство при таймауте (например, когда был отключен HDMI-кабель)
-    --dv-timings \ # Включение DV-таймингов
-    --drop-same-frames=30 # Экономим трафик
-```
-
-:exclamation: Обратите внимание, что для использования `--drop-same-frames` для разных браузеров нужно использовать ряд специальных параметров в `/stream` (за деталями обратитесь к урлу `/`).
-
-За полным списком опций обращайтесь ко встроенной справке: ```ustreamer --help```.
-
------
-# Камера Raspberry Pi
-Пример использования камеры Raspberry Pi v1:
-```bash
-$ sudo modprobe bcm2835-v4l2
-$ ./ustreamer --host :: -m jpeg --device-timeout=5 --buffers=3 -r 2592x1944
-```
-
-:exclamation: Обратите внимание что боле новые модели камеры имеют другое максимальное разрешение. Список поддерживаемых разрешений можно найти в [документации PiCamera](https://picamera.readthedocs.io/en/release-1.13/fov.html#sensor-modes).
-
-:exclamation: Если камера выдает низкий фреймрейт, возможно что она работает в фото-режиме, где производит более низкий фпс, но более качественную кратинку. Это происходит потому что `bcm2835-v4l2` переключает камеру в фото-режим на разрешениях выше `1280x720`. Чтобы обойти это, передайте параметры `max_video_width` и `max_video_height` при загрузке модуля, например:
-
-```bash
-$ modprobe bcm2835-v4l2 max_video_width=2592 max_video_height=1944
-```
-
------
-# Интеграция
-
-## Nginx
-Если uStreamer находится на Nginx, то последний будет буферизировать поток и создавать дополнительную задержку в стриме. Чтобы задержки не было, буферизацию можно отключить:
-
-```nginx
-location /stream {
-    postpone_output 0;
-    proxy_buffering off;
-    proxy_ignore_headers X-Accel-Buffering;
-    proxy_pass http://ustreamer;
-}
-```
-
------
-# Утилиты V4L2
-V4L2 предоставляет ряд официальных утилит для управления USB-вебкамерами и получения информации об устройствах. С их помощью можно писать всякие настроечные скрипты и запускать их по крону, если, например, вам требуется изменять настройки экспозиции в зависимости от времени суток. Пакет с этими утилитами доступен на всех дистрибутивах Linux и обычно называется `v4l-utils`.
-
-* Вывести список видеоустройств: `v4l2-ctl --list-devices`.
-* Вывести список доступных контролов устройства: `v4l2-ctl -d /dev/video0 --list-ctrls`.
-* Вывести список доступных форматов видео: `v4l2-ctl -d /dev/video0 --list-formats-ext`.
-* Показать текущее значение контрола: `v4l2-ctl -d /dev/video0 --get-ctrl=exposure_auto`.
-* Изменить значение контрола: `v4l2-ctl -d /dev/video0 --set-ctrl=exposure_auto=1`.
-
-Больше примеров вы можете найти [здесь](https://www.kurokesu.com/main/2016/01/16/manual-usb-camera-settings-in-linux/), а документацию в [`man v4l2-ctl`](https://www.mankier.com/1/v4l2-ctl).
-
------
-# Смотрите также
-* [Запуск с помощью systemd-сервиса](https://github.com/pikvm/ustreamer/issues/16).
-
------
-# Лицензия
-Copyright (C) 2018-2022 by 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/.
diff --git a/debian/changelog b/debian/changelog
index 8cb998e..07e1e15 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-ustreamer (5.4-1) UNRELEASED; urgency=medium
+ustreamer (5.38-1) UNRELEASED; urgency=medium
 
   [ Sam Reed ]
   * New upstream 5.4
@@ -8,8 +8,10 @@ ustreamer (5.4-1) UNRELEASED; urgency=medium
   * Bump debhelper from old 12 to 13.
   * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository,
     Repository-Browse.
+  * New upstream release.
+  * New upstream release.
 
- -- Sam Reed <sam@reedyboy.net>  Fri, 27 May 2022 21:46:31 +0000
+ -- Sam Reed <sam@reedyboy.net>  Sat, 20 May 2023 04:10:47 -0000
 
 ustreamer (4.9-1) unstable; urgency=medium
 
diff --git a/docs/h264.md b/docs/h264.md
index ed95f64..bf0b347 100644
--- a/docs/h264.md
+++ b/docs/h264.md
@@ -78,6 +78,17 @@ memsink: {
 EOF
 ```
 
+If you're using a TC358743-based video capture device that supports audio capture, run the following command to enable audio streaming:
+
+```sh
+cat << EOF >> /opt/janus/lib/janus/configs/janus.plugin.ustreamer.jcfg
+audio: {
+    device = "hw:1"
+    tc358743 = "/dev/video0"
+}
+EOF
+```
+
 ### Start µStreamer and the Janus WebRTC Server
 
 For µStreamer to share the video stream with the µStreamer Janus plugin, µStreamer must run with the following command-line flags:
@@ -111,13 +122,14 @@ The client-side JavaScript application uses the following control flow:
 1. The client instructs the Janus server to attach the µStreamer Janus plugin.
 1. On success, the client obtains a plugin handle through which it can send requests directly to the µStreamer Janus plugin. The client processes responses via the `attach` callbacks:
    - `onmessage` for general messages
-   - `onremotetrack` for the H.264 video stream
+   - `onremotetrack` for the H.264 video stream and (optionally) an Opus audio stream
 1. The client issues a `watch` request to the µStreamer Janus plugin, which initiates the H.264 stream in the plugin itself.
    - It takes a few seconds for uStreamer's video stream to become available to Janus. The first `watch` request may fail, so the client must retry the `watch` request.
 1. The client and server negotiate the underlying parameters of the WebRTC session. This procedure is called JavaScript Session Establishment Protocol (JSEP). The server makes a `jsepOffer` to the client, and the client responds with a `jsepAnswer`.
 1. The client issues a `start` request to the µStreamer Janus plugin to indicate that the client wants to begin consuming the video stream.
-1. The µStreamer Janus plugin delivers the H.264 video stream to the client via WebRTC.
-1. The Janus client library invokes the `onremotetrack` callback. The client attaches the video stream to the `<video>` element, rendering the video stream in the browser window.
+1. The µStreamer Janus plugin delivers the H.264 video stream and (optionally) an Opus audio stream to the client via WebRTC.
+1. The Janus client library invokes the `onremotetrack` callback with the video stream. The client attaches the video stream to the `<video>` element, rendering the video stream in the browser window.
+1. (if an audio track is available) The Janus client library invokes the `onremotetrack` callback with the Opus audio stream. The client adds the audio stream to the `<video>` element, rendering the audio in the browser window.
 
 ### Sample Code
 
@@ -176,8 +188,8 @@ The client-side JavaScript application uses the following control flow:
         // successfully.
         success: function (pluginHandle) {
           uStreamerPluginHandle = pluginHandle;
-          // Instruct the µStreamer Janus plugin to initiate the video stream.
-          uStreamerPluginHandle.send({ message: { request: "watch" } });
+          // Instruct the µStreamer Janus plugin to initiate streaming.
+          uStreamerPluginHandle.send({ message: { request: "watch", params: {audio: true} } });
         },
 
         // Callback function if the server fails to attach the plugin.
@@ -185,13 +197,6 @@ The client-side JavaScript application uses the following control flow:
 
         // Callback function for processing messages from the Janus server.
         onmessage: function (msg, jsepOffer) {
-          // 503 indicates that the plugin is not ready to stream yet. Retry the
-          // watch request until the video stream is available.
-          if (msg.error_code === 503) {
-            uStreamerPluginHandle.send({ message: { request: "watch" } });
-            return;
-          }
-
           // If there is a JSEP offer, respond to it. This starts the WebRTC
           // connection.
           if (jsepOffer) {
@@ -211,16 +216,17 @@ The client-side JavaScript application uses the following control flow:
           }
         },
 
-        // Callback function, for when the video stream arrives.
+        // Callback function, for when a media stream arrives.
         onremotetrack: function (mediaStreamTrack, mediaId, isAdded) {
           if (isAdded) {
             // Attach the received media track to the video element. Cloning the
             // mediaStreamTrack creates a new object with a distinct, globally
             // unique stream identifier.
             const videoElement = document.getElementById("webrtc-output");
-            const stream = new MediaStream();
-            stream.addTrack(mediaStreamTrack.clone());
-            videoElement.srcObject = stream;
+            if (videoElement.srcObject === null) {
+              videoElement.srcObject = new MediaStream();
+            }
+            videoElement.srcObject.addTrack(mediaStreamTrack.clone());
           }
         },
       });
diff --git a/janus/Makefile b/janus/Makefile
index 0278d30..afbad3e 100644
--- a/janus/Makefile
+++ b/janus/Makefile
@@ -9,10 +9,10 @@ LDFLAGS ?=
 # =====
 _PLUGIN = libjanus_ustreamer.so
 
-_CFLAGS = -fPIC -MD -c -std=c11 -Wall -Wextra -D_GNU_SOURCE $(shell pkg-config --cflags glib-2.0) $(CFLAGS)
-_LDFLAGS = -shared -lm -pthread -lrt -ljansson $(shell pkg-config --libs glib-2.0) $(LDFLAGS)
+_CFLAGS = -fPIC -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(shell pkg-config --cflags glib-2.0) $(CFLAGS)
+_LDFLAGS = -shared -lm -pthread -lrt -ljansson -lopus -lasound -lspeexdsp $(shell pkg-config --libs glib-2.0) $(LDFLAGS)
 
-_SRCS = $(shell ls src/*.c)
+_SRCS = $(shell ls src/uslibs/*.c src/*.c)
 
 _BUILD = build
 
diff --git a/janus/src/audio.c b/janus/src/audio.c
new file mode 100644
index 0000000..54a701f
--- /dev/null
+++ b/janus/src/audio.c
@@ -0,0 +1,255 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "audio.h"
+
+
+#define _JLOG_PERROR_ALSA(_err, _prefix, _msg, ...)	US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, snd_strerror(_err))
+#define _JLOG_PERROR_RES(_err, _prefix, _msg, ...)	US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, speex_resampler_strerror(_err))
+#define _JLOG_PERROR_OPUS(_err, _prefix, _msg, ...)	US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, opus_strerror(_err))
+
+// A number of frames per 1 channel:
+//   - https://github.com/xiph/opus/blob/7b05f44/src/opus_demo.c#L368
+#define _HZ_TO_FRAMES(_hz)	(6 * (_hz) / 50) // 120ms
+#define _HZ_TO_BUF16(_hz)	(_HZ_TO_FRAMES(_hz) * 2) // One stereo frame = (16bit L) + (16bit R)
+#define _HZ_TO_BUF8(_hz)	(_HZ_TO_BUF16(_hz) * sizeof(int16_t))
+
+#define _MIN_PCM_HZ			8000
+#define _MAX_PCM_HZ			192000
+#define _MAX_BUF16			_HZ_TO_BUF16(_MAX_PCM_HZ)
+#define _MAX_BUF8			_HZ_TO_BUF8(_MAX_PCM_HZ)
+#define _ENCODER_INPUT_HZ	48000
+
+
+typedef struct {
+	int16_t		data[_MAX_BUF16];
+} _pcm_buffer_s;
+
+typedef struct {
+	uint8_t		data[_MAX_BUF8]; // Worst case
+	size_t		used;
+	uint64_t	pts;
+} _enc_buffer_s;
+
+
+static void *_pcm_thread(void *v_audio);
+static void *_encoder_thread(void *v_audio);
+
+
+bool us_audio_probe(const char *name) {
+	snd_pcm_t *pcm;
+	int err;
+	US_JLOG_INFO("audio", "Probing PCM capture ...");
+	if ((err = snd_pcm_open(&pcm, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+		_JLOG_PERROR_ALSA(err, "audio", "Can't probe PCM capture");
+		return false;
+	}
+	snd_pcm_close(pcm);
+	US_JLOG_INFO("audio", "PCM capture is available");
+	return true;
+}
+
+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);
+	atomic_init(&audio->stop, false);
+
+	int err;
+
+	{
+		if ((err = snd_pcm_open(&audio->pcm, name, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+			audio->pcm = NULL;
+			_JLOG_PERROR_ALSA(err, "audio", "Can't open PCM capture");
+			goto error;
+		}
+		assert(!snd_pcm_hw_params_malloc(&audio->pcm_params));
+
+#		define SET_PARAM(_msg, _func, ...) { \
+				if ((err = _func(audio->pcm, audio->pcm_params, ##__VA_ARGS__)) < 0) { \
+					_JLOG_PERROR_ALSA(err, "audio", _msg); \
+					goto error; \
+				} \
+			}
+
+		SET_PARAM("Can't initialize PCM params",	snd_pcm_hw_params_any);
+		SET_PARAM("Can't set PCM access type",		snd_pcm_hw_params_set_access, SND_PCM_ACCESS_RW_INTERLEAVED);
+		SET_PARAM("Can't set PCM channels numbre",	snd_pcm_hw_params_set_channels, 2);
+		SET_PARAM("Can't set PCM sampling format",	snd_pcm_hw_params_set_format, SND_PCM_FORMAT_S16_LE);
+		SET_PARAM("Can't set PCM sampling rate",	snd_pcm_hw_params_set_rate_near, &audio->pcm_hz, 0);
+		if (audio->pcm_hz < _MIN_PCM_HZ || audio->pcm_hz > _MAX_PCM_HZ) {
+			US_JLOG_ERROR("audio", "Unsupported PCM freq: %u; should be: %u <= F <= %u",
+				audio->pcm_hz, _MIN_PCM_HZ, _MAX_PCM_HZ);
+			goto error;
+		}
+		audio->pcm_frames = _HZ_TO_FRAMES(audio->pcm_hz);
+		audio->pcm_size = _HZ_TO_BUF8(audio->pcm_hz);
+		SET_PARAM("Can't apply PCM params", snd_pcm_hw_params);
+
+#		undef SET_PARAM
+	}
+
+	if (audio->pcm_hz != _ENCODER_INPUT_HZ) {
+		audio->res = speex_resampler_init(2, audio->pcm_hz, _ENCODER_INPUT_HZ, SPEEX_RESAMPLER_QUALITY_DESKTOP, &err);
+		if (err < 0) {
+			audio->res = NULL;
+			_JLOG_PERROR_RES(err, "audio", "Can't create resampler");
+			goto error;
+		}
+	}
+
+	{
+		// OPUS_APPLICATION_VOIP, OPUS_APPLICATION_RESTRICTED_LOWDELAY
+		audio->enc = opus_encoder_create(_ENCODER_INPUT_HZ, 2, OPUS_APPLICATION_AUDIO, &err);
+		assert(err == 0);
+		assert(!opus_encoder_ctl(audio->enc, OPUS_SET_BITRATE(48000)));
+		assert(!opus_encoder_ctl(audio->enc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)));
+		assert(!opus_encoder_ctl(audio->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC)));
+		// OPUS_SET_INBAND_FEC(1), OPUS_SET_PACKET_LOSS_PERC(10): see rtpa.c
+	}
+
+	US_JLOG_INFO("audio", "Pipeline configured on %uHz; capturing ...", audio->pcm_hz);
+	audio->tids_created = true;
+	US_THREAD_CREATE(audio->enc_tid, _encoder_thread, audio);
+	US_THREAD_CREATE(audio->pcm_tid, _pcm_thread, audio);
+
+	return audio;
+
+	error:
+		us_audio_destroy(audio);
+		return NULL;
+}
+
+void us_audio_destroy(us_audio_s *audio) {
+	if (audio->tids_created) {
+		atomic_store(&audio->stop, true);
+		US_THREAD_JOIN(audio->pcm_tid);
+		US_THREAD_JOIN(audio->enc_tid);
+	}
+	US_DELETE(audio->enc, opus_encoder_destroy);
+	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);
+	if (audio->tids_created) {
+		US_JLOG_INFO("audio", "Pipeline closed");
+	}
+	free(audio);
+}
+
+int us_audio_get_encoded(us_audio_s *audio, uint8_t *data, size_t *size, uint64_t *pts) {
+	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;
+	}
+	return -2;
+}
+
+static void *_pcm_thread(void *v_audio) {
+	US_THREAD_RENAME("us_a_pcm");
+
+	us_audio_s *const audio = (us_audio_s *)v_audio;
+	uint8_t in[_MAX_BUF8];
+
+	while (!atomic_load(&audio->stop)) {
+		const int frames = snd_pcm_readi(audio->pcm, in, audio->pcm_frames);
+		if (frames < 0) {
+			_JLOG_PERROR_ALSA(frames, "audio", "Fatal: Can't capture PCM frames");
+			break;
+		} else if (frames < (int)audio->pcm_frames) {
+			US_JLOG_ERROR("audio", "Fatal: Too few PCM frames captured");
+			break;
+		}
+
+		if (us_queue_get_free(audio->pcm_queue)) {
+			_pcm_buffer_s *out;
+			US_CALLOC(out, 1);
+			memcpy(out->data, in, audio->pcm_size);
+			assert(!us_queue_put(audio->pcm_queue, out, 0));
+		} else {
+			US_JLOG_ERROR("audio", "PCM queue is full");
+		}
+	}
+
+	atomic_store(&audio->stop, true);
+	return NULL;
+}
+
+static void *_encoder_thread(void *v_audio) {
+	US_THREAD_RENAME("us_a_enc");
+
+	us_audio_s *const audio = (us_audio_s *)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;
+			}
+
+			_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;
+			}
+			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);
+			}
+		}
+	}
+
+	atomic_store(&audio->stop, true);
+	return NULL;
+}
diff --git a/janus/src/audio.h b/janus/src/audio.h
new file mode 100644
index 0000000..f87a156
--- /dev/null
+++ b/janus/src/audio.h
@@ -0,0 +1,71 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <assert.h>
+
+#include <sys/types.h>
+
+#include <pthread.h>
+#include <alsa/asoundlib.h>
+#include <speex/speex_resampler.h>
+#include <opus/opus.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/array.h"
+#include "uslibs/threading.h"
+
+#include "logging.h"
+#include "queue.h"
+
+
+typedef struct {
+	snd_pcm_t			*pcm;
+	unsigned			pcm_hz;
+	unsigned			pcm_frames;
+	size_t				pcm_size;
+	snd_pcm_hw_params_t	*pcm_params;
+	SpeexResamplerState	*res;
+	OpusEncoder			*enc;
+
+	us_queue_s			*pcm_queue;
+	us_queue_s			*enc_queue;
+	uint32_t			pts;
+
+	pthread_t			pcm_tid;
+	pthread_t			enc_tid;
+	bool				tids_created;
+	atomic_bool			stop;
+} us_audio_s;
+
+
+bool us_audio_probe(const char *name);
+
+us_audio_s *us_audio_init(const char *name, unsigned pcm_hz);
+void us_audio_destroy(us_audio_s *audio);
+
+int us_audio_get_encoded(us_audio_s *audio, uint8_t *data, size_t *size, uint64_t *pts);
diff --git a/janus/src/base64.c b/janus/src/base64.c
deleted file mode 120000
index a911067..0000000
--- a/janus/src/base64.c
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/base64.c
\ No newline at end of file
diff --git a/janus/src/base64.h b/janus/src/base64.h
deleted file mode 120000
index 1a74653..0000000
--- a/janus/src/base64.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/base64.h
\ No newline at end of file
diff --git a/janus/src/client.c b/janus/src/client.c
new file mode 100644
index 0000000..58c52e7
--- /dev/null
+++ b/janus/src/client.c
@@ -0,0 +1,128 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "client.h"
+
+
+static void *_video_thread(void *v_client);
+static void *_audio_thread(void *v_client);
+static void *_common_thread(void *v_client, bool video);
+
+
+us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_session *session) {
+	us_janus_client_s *client;
+	US_CALLOC(client, 1);
+	client->gw = gw;
+	client->session = session;
+	atomic_init(&client->transmit, false);
+	atomic_init(&client->transmit_audio, false);
+
+	atomic_init(&client->stop, false);
+
+	client->video_queue = us_queue_init(1024);
+	US_THREAD_CREATE(client->video_tid, _video_thread, client);
+
+	client->audio_queue = us_queue_init(64);
+	US_THREAD_CREATE(client->audio_tid, _audio_thread, client);
+
+	return client;
+}
+
+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_THREAD_JOIN(client->audio_tid);
+	US_QUEUE_DELETE_WITH_ITEMS(client->audio_queue, us_rtp_destroy);
+
+	free(client);
+}
+
+void us_janus_client_send(us_janus_client_s *client, const us_rtp_s *rtp) {
+	if (
+		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);
+		}
+	}
+}
+
+static void *_video_thread(void *v_client) {
+	return _common_thread(v_client, true);
+}
+
+static void *_audio_thread(void *v_client) {
+	return _common_thread(v_client, false);
+}
+
+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
+
+	while (!atomic_load(&client->stop)) {
+		us_rtp_s *rtp;
+		if (!us_queue_get(queue, (void **)&rtp, 0.1)) {
+			if (rtp == NULL) {
+				break;
+			}
+
+			if (
+				atomic_load(&client->transmit)
+				&& (video || atomic_load(&client->transmit_audio))
+			) {
+				janus_plugin_rtp packet = {0};
+				packet.video = rtp->video;
+				packet.buffer = (char *)rtp->datagram;
+				packet.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.
+				packet.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;
+					packet.extensions.max_delay = 1000;
+				}*/
+
+				client->gw->relay_rtp(client->session, &packet);
+			}
+			us_rtp_destroy(rtp);
+		}
+	}
+	return NULL;
+}
diff --git a/janus/src/client.h b/janus/src/client.h
new file mode 100644
index 0000000..3ac7919
--- /dev/null
+++ b/janus/src/client.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <string.h>
+
+#include <pthread.h>
+#include <janus/plugins/plugin.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/threading.h"
+#include "uslibs/list.h"
+
+#include "logging.h"
+#include "queue.h"
+#include "rtp.h"
+
+
+typedef struct us_janus_client_sx {
+	janus_callbacks			*gw;
+	janus_plugin_session	*session;
+	atomic_bool				transmit;
+	atomic_bool				transmit_audio;
+
+	pthread_t				video_tid;
+	pthread_t				audio_tid;
+	atomic_bool				stop;
+
+	us_queue_s				*video_queue;
+	us_queue_s				*audio_queue;
+
+    US_LIST_STRUCT(struct us_janus_client_sx);
+} us_janus_client_s;
+
+
+us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_session *session);
+void us_janus_client_destroy(us_janus_client_s *client);
+
+void us_janus_client_send(us_janus_client_s *client, const us_rtp_s *rtp);
diff --git a/janus/src/config.c b/janus/src/config.c
new file mode 100644
index 0000000..d479dcf
--- /dev/null
+++ b/janus/src/config.c
@@ -0,0 +1,95 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "config.h"
+
+
+static char *_get_value(janus_config *jcfg, const char *section, const char *option);
+// static bool _get_bool(janus_config *jcfg, const char *section, const char *option, bool def);
+
+
+us_config_s *us_config_init(const char *config_dir_path) {
+	us_config_s *config;
+	US_CALLOC(config, 1);
+
+	char *config_file_path;
+	janus_config *jcfg = NULL;
+
+	US_ASPRINTF(config_file_path, "%s/%s.jcfg", config_dir_path, US_PLUGIN_PACKAGE);
+	US_JLOG_INFO("config", "Reading config file '%s' ...", config_file_path);
+
+	jcfg = janus_config_parse(config_file_path);
+	if (jcfg == NULL) {
+		US_JLOG_ERROR("config", "Can't read config");
+		goto error;
+	}
+	janus_config_print(jcfg);
+
+	if (
+		(config->video_sink_name = _get_value(jcfg, "memsink", "object")) == NULL
+		&& (config->video_sink_name = _get_value(jcfg, "video", "sink")) == NULL
+	) {
+		US_JLOG_ERROR("config", "Missing config value: video.sink (ex. memsink.object)");
+		goto error;
+	}
+	if ((config->audio_dev_name = _get_value(jcfg, "audio", "device")) != NULL) {
+		if ((config->tc358743_dev_path = _get_value(jcfg, "audio", "tc358743")) == NULL) {
+			US_JLOG_INFO("config", "Missing config value: audio.tc358743");
+			goto error;
+		}
+	}
+
+	goto ok;
+	error:
+		us_config_destroy(config);
+		config = NULL;
+	ok:
+		US_DELETE(jcfg, janus_config_destroy);
+		free(config_file_path);
+		return config;
+}
+
+void us_config_destroy(us_config_s *config) {
+	US_DELETE(config->video_sink_name, free);
+	US_DELETE(config->audio_dev_name, free);
+	US_DELETE(config->tc358743_dev_path, free);
+	free(config);
+}
+
+static char *_get_value(janus_config *jcfg, const char *section, const char *option) {
+	janus_config_category *section_obj = janus_config_get_create(jcfg, NULL, janus_config_type_category, section);
+	janus_config_item *option_obj = janus_config_get(jcfg, section_obj, janus_config_type_item, option);
+	if (option_obj == NULL || option_obj->value == NULL || option_obj->value[0] == '\0') {
+		return NULL;
+	}
+	return us_strdup(option_obj->value);
+}
+
+/*static bool _get_bool(janus_config *jcfg, const char *section, const char *option, bool def) {
+	char *const tmp = _get_value(jcfg, section, option);
+	bool value = def;
+	if (tmp != NULL) {
+		value = (!strcasecmp(tmp, "1") || !strcasecmp(tmp, "true") || !strcasecmp(tmp, "yes"));
+		free(tmp);
+	}
+	return value;
+}*/
diff --git a/janus/src/config.h b/janus/src/config.h
deleted file mode 120000
index 4bf94da..0000000
--- a/janus/src/config.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/config.h
\ No newline at end of file
diff --git a/janus/src/config.h b/janus/src/config.h
new file mode 100644
index 0000000..4bdb852
--- /dev/null
+++ b/janus/src/config.h
@@ -0,0 +1,46 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <janus/config.h>
+#include <janus/plugins/plugin.h>
+
+#include "uslibs/tools.h"
+
+#include "const.h"
+#include "logging.h"
+
+
+typedef struct {
+	char	*video_sink_name;
+
+	char	*audio_dev_name;
+	char	*tc358743_dev_path;
+} us_config_s;
+
+
+us_config_s *us_config_init(const char *config_dir_path);
+void us_config_destroy(us_config_s *config);
diff --git a/janus/src/const.h b/janus/src/const.h
new file mode 100644
index 0000000..b8b7967
--- /dev/null
+++ b/janus/src/const.h
@@ -0,0 +1,26 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#define US_PLUGIN_NAME		"ustreamer"
+#define US_PLUGIN_PACKAGE	"janus.plugin.ustreamer"
diff --git a/janus/src/frame.c b/janus/src/frame.c
deleted file mode 120000
index 3bc454d..0000000
--- a/janus/src/frame.c
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/frame.c
\ No newline at end of file
diff --git a/janus/src/frame.h b/janus/src/frame.h
deleted file mode 120000
index 73ed450..0000000
--- a/janus/src/frame.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/frame.h
\ No newline at end of file
diff --git a/janus/src/list.h b/janus/src/list.h
deleted file mode 120000
index faaa81a..0000000
--- a/janus/src/list.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/list.h
\ No newline at end of file
diff --git a/janus/src/logging.h b/janus/src/logging.h
new file mode 100644
index 0000000..8d4f67c
--- /dev/null
+++ b/janus/src/logging.h
@@ -0,0 +1,46 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 <janus/plugins/plugin.h>
+
+#include "uslibs/tools.h"
+
+#include "const.h"
+
+
+#define US_JLOG_INFO(x_prefix, x_msg, ...)	JANUS_LOG(LOG_INFO, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
+#define US_JLOG_WARN(x_prefix, x_msg, ...)	JANUS_LOG(LOG_WARN, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
+#define US_JLOG_ERROR(x_prefix, x_msg, ...)	JANUS_LOG(LOG_ERR, "== %s/%-9s -- " x_msg "\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__)
+
+#define US_JLOG_PERROR(x_prefix, x_msg, ...) { \
+		char *const m_perror_str = us_errno_to_string(errno); \
+		JANUS_LOG(LOG_ERR, "[%s/%-9s] " x_msg ": %s\n", US_PLUGIN_NAME, x_prefix, ##__VA_ARGS__, m_perror_str); \
+		free(m_perror_str); \
+	}
+
+#define US_ONCE(...) { \
+		const int m_reported = __LINE__; \
+		if (m_reported != once) { \
+			__VA_ARGS__; \
+			once = m_reported; \
+		} \
+	}
diff --git a/janus/src/memsinkfd.c b/janus/src/memsinkfd.c
new file mode 100644
index 0000000..d2f1f96
--- /dev/null
+++ b/janus/src/memsinkfd.c
@@ -0,0 +1,73 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "memsinkfd.h"
+
+
+int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s* mem, uint64_t last_id) {
+	const long double deadline_ts = us_get_now_monotonic() + 1; // wait_timeout
+	long double now;
+	do {
+		const int result = us_flock_timedwait_monotonic(fd, 1); // lock_timeout
+		now = us_get_now_monotonic();
+		if (result < 0 && errno != EWOULDBLOCK) {
+			US_JLOG_PERROR("video", "Can't lock memsink");
+			return -1;
+		} else if (result == 0) {
+			if (mem->magic == US_MEMSINK_MAGIC && mem->version == US_MEMSINK_VERSION && mem->id != last_id) {
+				return 0;
+			}
+			if (flock(fd, LOCK_UN) < 0) {
+				US_JLOG_PERROR("video", "Can't unlock memsink");
+				return -1;
+			}
+		}
+		usleep(1000); // lock_polling
+	} while (now < deadline_ts);
+	return -2;
+}
+
+us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, uint64_t *frame_id, bool key_required) {
+	us_frame_s *frame = us_frame_init();
+	us_frame_set_data(frame, mem->data, mem->used);
+	US_FRAME_COPY_META(mem, frame);
+	*frame_id = mem->id;
+	mem->last_client_ts = us_get_now_monotonic();
+	if (key_required) {
+		mem->key_requested = true;
+	}
+
+	bool ok = true;
+	if (frame->format != V4L2_PIX_FMT_H264) {
+		US_JLOG_ERROR("video", "Got non-H264 frame from memsink");
+		ok = false;
+	}
+	if (flock(fd, LOCK_UN) < 0) {
+		US_JLOG_PERROR("video", "Can't unlock memsink");
+		ok = false;
+	}
+	if (!ok) {
+		us_frame_destroy(frame);
+		frame = NULL;
+	}
+	return frame;
+}
diff --git a/janus/src/memsinkfd.h b/janus/src/memsinkfd.h
new file mode 100644
index 0000000..29d9e5e
--- /dev/null
+++ b/janus/src/memsinkfd.h
@@ -0,0 +1,39 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <unistd.h>
+
+#include <linux/videodev2.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/frame.h"
+#include "uslibs/memsinksh.h"
+
+#include "logging.h"
+
+
+int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s* mem, uint64_t last_id);
+us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, uint64_t *frame_id, bool key_required);
diff --git a/janus/src/memsinksh.h b/janus/src/memsinksh.h
deleted file mode 120000
index 52d4728..0000000
--- a/janus/src/memsinksh.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/memsinksh.h
\ No newline at end of file
diff --git a/janus/src/plugin.c b/janus/src/plugin.c
index 6f5ec4f..243c4a3 100644
--- a/janus/src/plugin.c
+++ b/janus/src/plugin.c
@@ -24,227 +24,141 @@
 #include <stdbool.h>
 #include <stdatomic.h>
 #include <stdlib.h>
+#include <inttypes.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <assert.h>
 
 #include <sys/mman.h>
 #include <sys/stat.h>
-#include <linux/videodev2.h>
 
 #include <pthread.h>
 #include <jansson.h>
-#include <janus/config.h>
 #include <janus/plugins/plugin.h>
-
-#include "config.h"
-#include "tools.h"
-#include "threading.h"
-#include "list.h"
-#include "memsinksh.h"
-
+#include <janus/rtcp.h>
+
+#include "uslibs/const.h"
+#include "uslibs/tools.h"
+#include "uslibs/threading.h"
+#include "uslibs/list.h"
+#include "uslibs/memsinksh.h"
+
+#include "const.h"
+#include "logging.h"
+#include "queue.h"
+#include "client.h"
+#include "audio.h"
+#include "tc358743.h"
 #include "rtp.h"
+#include "rtpv.h"
+#include "rtpa.h"
+#include "memsinkfd.h"
+#include "config.h"
 
 
-static int _plugin_init(janus_callbacks *gw, const char *config_file_path);
-static void _plugin_destroy(void);
-
-static void _plugin_create_session(janus_plugin_session *session, int *error);
-static void _plugin_destroy_session(janus_plugin_session *session, int *error);
-static json_t *_plugin_query_session(janus_plugin_session *session);
-
-static void _plugin_setup_media(janus_plugin_session *session);
-static void _plugin_hangup_media(janus_plugin_session *session);
+static us_config_s	*_g_config = NULL;
+const useconds_t	_g_watchers_polling = 100000;
 
-static struct janus_plugin_result *_plugin_handle_message(
-	janus_plugin_session *session, char *transaction, json_t *msg, json_t *jsep);
-
-static int _plugin_get_api_compatibility(void);
-static int _plugin_get_version(void);
-static const char *_plugin_get_version_string(void);
-static const char *_plugin_get_description(void);
-static const char *_plugin_get_name(void);
-static const char *_plugin_get_author(void);
-static const char *_plugin_get_package(void);
-
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Woverride-init"
-static janus_plugin _plugin = JANUS_PLUGIN_INIT(
-	.init = _plugin_init,
-	.destroy = _plugin_destroy,
-
-	.create_session = _plugin_create_session,
-	.destroy_session = _plugin_destroy_session,
-	.query_session = _plugin_query_session,
-
-	.setup_media = _plugin_setup_media,
-	.hangup_media = _plugin_hangup_media,
-
-	.handle_message = _plugin_handle_message,
-
-	.get_api_compatibility = _plugin_get_api_compatibility,
-	.get_version = _plugin_get_version,
-	.get_version_string = _plugin_get_version_string,
-	.get_description = _plugin_get_description,
-	.get_name = _plugin_get_name,
-	.get_author = _plugin_get_author,
-	.get_package = _plugin_get_package,
-);
-#pragma GCC diagnostic pop
-
-janus_plugin *create(void) { // cppcheck-suppress unusedFunction
-	return &_plugin;
-}
+static us_janus_client_s	*_g_clients = NULL;
+static janus_callbacks		*_g_gw = NULL;
+static us_queue_s			*_g_video_queue = NULL;
+static us_rtpv_s			*_g_rtpv = NULL;
+static us_rtpa_s			*_g_rtpa = NULL;
 
+static pthread_t		_g_video_rtp_tid;
+static atomic_bool		_g_video_rtp_tid_created = false;
+static pthread_t		_g_video_sink_tid;
+static atomic_bool		_g_video_sink_tid_created = false;
+static pthread_t		_g_audio_tid;
+static atomic_bool		_g_audio_tid_created = false;
 
-typedef struct _client_sx {
-	janus_plugin_session *session;
-	bool transmit;
+static pthread_mutex_t	_g_video_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t	_g_audio_lock = PTHREAD_MUTEX_INITIALIZER;
+static atomic_bool		_g_ready = false;
+static atomic_bool		_g_stop = false;
+static atomic_bool		_g_has_watchers = false;
+static atomic_bool		_g_has_listeners = false;
+static atomic_bool		_g_key_required = false;
 
-	LIST_STRUCT(struct _client_sx);
-} _client_s;
 
+#define _LOCK_VIDEO		US_MUTEX_LOCK(_g_video_lock)
+#define _UNLOCK_VIDEO	US_MUTEX_UNLOCK(_g_video_lock)
 
-static char				*_g_memsink_obj = NULL;
-const long double		_g_wait_timeout = 1;
-const long double		_g_lock_timeout = 1;
-const useconds_t		_g_lock_polling = 1000;
-const useconds_t		_g_watchers_polling = 100000;
+#define _LOCK_AUDIO		US_MUTEX_LOCK(_g_audio_lock)
+#define _UNLOCK_AUDIO	US_MUTEX_UNLOCK(_g_audio_lock)
 
-static _client_s		*_g_clients = NULL;
-static janus_callbacks	*_g_gw = NULL;
-static rtp_s			*_g_rtp = NULL;
+#define _LOCK_ALL		{ _LOCK_VIDEO; _LOCK_AUDIO; }
+#define _UNLOCK_ALL		{ _UNLOCK_AUDIO; _UNLOCK_VIDEO; }
 
-static pthread_t		_g_tid;
-static pthread_mutex_t	_g_lock = PTHREAD_MUTEX_INITIALIZER;
-static atomic_bool		_g_ready = ATOMIC_VAR_INIT(false);
-static atomic_bool		_g_stop = ATOMIC_VAR_INIT(false);
-static atomic_bool		_g_has_watchers = ATOMIC_VAR_INIT(false);
+#define _READY			atomic_load(&_g_ready)
+#define _STOP			atomic_load(&_g_stop)
+#define _HAS_WATCHERS	atomic_load(&_g_has_watchers)
+#define _HAS_LISTENERS	atomic_load(&_g_has_listeners)
 
 
-#define JLOG_INFO(_msg, ...)	JANUS_LOG(LOG_INFO, "== %s -- " _msg "\n", _plugin_get_name(), ##__VA_ARGS__)
-#define JLOG_WARN(_msg, ...)	JANUS_LOG(LOG_WARN, "== %s -- " _msg "\n", _plugin_get_name(), ##__VA_ARGS__)
-#define JLOG_ERROR(_msg, ...)	JANUS_LOG(LOG_ERR, "== %s -- " _msg "\n", _plugin_get_name(), ##__VA_ARGS__)
+janus_plugin *create(void);
 
-#define JLOG_PERROR(_msg, ...) { \
-		char _perror_buf[1024] = {0}; \
-		char *_perror_ptr = errno_to_string(errno, _perror_buf, 1023); \
-		JANUS_LOG(LOG_ERR, "[%s] " _msg ": %s\n", _plugin_get_name(), ##__VA_ARGS__, _perror_ptr); \
-	}
 
-#define LOCK			A_MUTEX_LOCK(&_g_lock)
-#define UNLOCK			A_MUTEX_UNLOCK(&_g_lock)
-#define READY			atomic_load(&_g_ready)
-#define STOP			atomic_load(&_g_stop)
-#define HAS_WATCHERS	atomic_load(&_g_has_watchers)
-
-
-static void _relay_rtp_clients(const uint8_t *datagram, size_t size) {
-	janus_plugin_rtp packet = {
-		.video = true,
-		.buffer = (char *)datagram,
-		.length = size,
-	};
-	janus_plugin_rtp_extensions_reset(&packet.extensions);
-	LIST_ITERATE(_g_clients, client, {
-		if (client->transmit) {
-			_g_gw->relay_rtp(client->session, &packet);
-		}
-	});
-}
+static void *_video_rtp_thread(UNUSED void *arg) {
+	US_THREAD_RENAME("us_video_rtp");
+	atomic_store(&_g_video_rtp_tid_created, true);
 
-static int _wait_frame(int fd, memsink_shared_s* mem, uint64_t last_id) {
-	long double deadline_ts = get_now_monotonic() + _g_wait_timeout;
-	long double now;
-	do {
-		int retval = flock_timedwait_monotonic(fd, _g_lock_timeout);
-		now = get_now_monotonic();
-		if (retval < 0 && errno != EWOULDBLOCK) {
-			JLOG_PERROR("Can't lock memsink");
-			return -1;
-		} else if (retval == 0) {
-			if (mem->magic == MEMSINK_MAGIC && mem->version == MEMSINK_VERSION && mem->id != last_id) {
-				return 0;
-			}
-			if (flock(fd, LOCK_UN) < 0) {
-				JLOG_PERROR("Can't unlock memsink");
-				return -1;
-			}
+	while (!_STOP) {
+		us_frame_s *frame;
+		if (us_queue_get(_g_video_queue, (void **)&frame, 0.1) == 0) {
+			_LOCK_VIDEO;
+			us_rtpv_wrap(_g_rtpv, frame);
+			_UNLOCK_VIDEO;
+			us_frame_destroy(frame);
 		}
-		usleep(_g_lock_polling);
-	} while (now < deadline_ts);
-	return -2;
-}
-
-static int _get_frame(int fd, memsink_shared_s *mem, frame_s *frame, uint64_t *frame_id) {
-	frame_set_data(frame, mem->data, mem->used);
-	FRAME_COPY_META(mem, frame);
-	*frame_id = mem->id;
-	mem->last_client_ts = get_now_monotonic();
-	int retval = 0;
-	if (frame->format != V4L2_PIX_FMT_H264) {
-		JLOG_ERROR("Got non-H264 frame from memsink");
-		retval = -1;
-	}
-	if (flock(fd, LOCK_UN) < 0) {
-		JLOG_PERROR("Can't unlock memsink");
-		retval = -1;
 	}
-	return retval;
+	return NULL;
 }
 
-static void *_clients_thread(UNUSED void *arg) {
-	A_THREAD_RENAME("us_clients");
-	atomic_store(&_g_ready, true);
+static void *_video_sink_thread(UNUSED void *arg) {
+	US_THREAD_RENAME("us_video_sink");
+	atomic_store(&_g_video_sink_tid_created, true);
 
-	frame_s *frame = frame_init();
 	uint64_t frame_id = 0;
+	int once = 0;
 
-	unsigned error_reported = 0;
-
-#	define IF_NOT_REPORTED(_error, ...) { \
-			if (error_reported != _error) { __VA_ARGS__; error_reported = _error; } \
-		}
-
-	while (!STOP) {
-		if (!HAS_WATCHERS) {
-			IF_NOT_REPORTED(1, {
-				JLOG_INFO("No active watchers, memsink disconnected");
-			});
+	while (!_STOP) {
+		if (!_HAS_WATCHERS) {
+			US_ONCE({ US_JLOG_INFO("video", "No active watchers, memsink disconnected"); });
 			usleep(_g_watchers_polling);
 			continue;
 		}
 
 		int fd = -1;
-		memsink_shared_s *mem = NULL;
+		us_memsink_shared_s *mem = NULL;
 
-		if ((fd = shm_open(_g_memsink_obj, O_RDWR, 0)) <= 0) {
-			IF_NOT_REPORTED(2, {
-				JLOG_PERROR("Can't open memsink");
-			});
+		if ((fd = shm_open(_g_config->video_sink_name, O_RDWR, 0)) <= 0) {
+			US_ONCE({ US_JLOG_PERROR("video", "Can't open memsink"); });
 			goto close_memsink;
 		}
 
-		if ((mem = memsink_shared_map(fd)) == NULL) {
-			IF_NOT_REPORTED(3, {
-				JLOG_PERROR("Can't map memsink");
-			});
+		if ((mem = us_memsink_shared_map(fd)) == NULL) {
+			US_ONCE({ US_JLOG_PERROR("video", "Can't map memsink"); });
 			goto close_memsink;
 		}
 
-		error_reported = 0;
+		once = 0;
 
-		JLOG_INFO("Memsink opened; reading frames ...");
-		while (!STOP && HAS_WATCHERS) {
-			int result = _wait_frame(fd, mem, frame_id);
+		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) {
-				if (_get_frame(fd, mem, frame, &frame_id) != 0) {
+				us_frame_s *const frame = us_memsink_fd_get_frame(fd, mem, &frame_id, atomic_load(&_g_key_required));
+				if (frame == NULL) {
 					goto close_memsink;
 				}
-				LOCK;
-				rtp_wrap_h264(_g_rtp, frame, _relay_rtp_clients);
-				UNLOCK;
+				if (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) {
 				goto close_memsink;
 			}
@@ -252,55 +166,81 @@ static void *_clients_thread(UNUSED void *arg) {
 
 		close_memsink:
 			if (mem != NULL) {
-				JLOG_INFO("Memsink closed");
-				memsink_shared_unmap(mem);
-				mem = NULL;
+				US_JLOG_INFO("video", "Memsink closed");
+				us_memsink_shared_unmap(mem);
 			}
-			if (fd > 0) {
+			if (fd >= 0) {
 				close(fd);
-				fd = -1;
 			}
 			sleep(1); // error_delay
 	}
-
-#	undef IF_NOT_REPORTED
-
-	frame_destroy(frame);
 	return NULL;
 }
 
-static int _read_config(const char *config_dir_path) {
-	char *config_file_path;
-	janus_config *config = NULL;
+static void *_audio_thread(UNUSED void *arg) {
+	US_THREAD_RENAME("us_audio");
+	atomic_store(&_g_audio_tid_created, true);
+	assert(_g_config->audio_dev_name != NULL);
+	assert(_g_config->tc358743_dev_path != NULL);
 
-	A_ASPRINTF(config_file_path, "%s/%s.jcfg", config_dir_path, _plugin_get_package());
-	JLOG_INFO("Reading config file '%s' ...", config_file_path);
+	int once = 0;
 
-	config = janus_config_parse(config_file_path);
-	if (config == NULL) {
-		JLOG_ERROR("Can't read config");
-		goto error;
-	}
-	janus_config_print(config);
+	while (!_STOP) {
+		if (!_HAS_WATCHERS || !_HAS_LISTENERS) {
+			usleep(_g_watchers_polling);
+			continue;
+		}
 
-	janus_config_category *config_memsink = janus_config_get_create(config, NULL, janus_config_type_category, "memsink");
-	janus_config_item *config_memsink_obj = janus_config_get(config, config_memsink, janus_config_type_item, "object");
-	if (config_memsink_obj == NULL || config_memsink_obj->value == NULL || config_memsink_obj->value[0] == '\0') {
-		JLOG_ERROR("Missing config value: memsink.object");
-		goto error;
-	}
-	_g_memsink_obj = strdup(config_memsink_obj->value);
-
-	int retval = 0;
-	goto ok;
-	error:
-		retval = -1;
-	ok:
-		if (config) {
-			janus_config_destroy(config);
+		us_tc358743_info_s info = {0};
+		us_audio_s *audio = NULL;
+
+		if (us_tc358743_read_info(_g_config->tc358743_dev_path, &info) < 0) {
+			goto close_audio;
 		}
-		free(config_file_path);
-		return retval;
+		if (!info.has_audio) {
+			US_ONCE({ US_JLOG_INFO("audio", "No audio presented from the host"); });
+			goto close_audio;
+		}
+		US_ONCE({ US_JLOG_INFO("audio", "Detected host audio"); });
+		if ((audio = us_audio_init(_g_config->audio_dev_name, info.audio_hz)) == NULL) {
+			goto close_audio;
+		}
+
+		once = 0;
+
+		while (!_STOP && _HAS_WATCHERS && _HAS_LISTENERS) {
+			if (
+				us_tc358743_read_info(_g_config->tc358743_dev_path, &info) < 0
+				|| !info.has_audio
+				|| audio->pcm_hz != info.audio_hz
+			) {
+				goto close_audio;
+			}
+
+			size_t size = US_RTP_DATAGRAM_SIZE - US_RTP_HEADER_SIZE;
+			uint8_t data[size];
+			uint64_t pts;
+			const int result = us_audio_get_encoded(audio, data, &size, &pts);
+			if (result == 0) {
+				_LOCK_AUDIO;
+				us_rtpa_wrap(_g_rtpa, data, size, pts);
+				_UNLOCK_AUDIO;
+			} else if (result == -1) {
+				goto close_audio;
+			}
+		}
+
+		close_audio:
+			US_DELETE(audio, us_audio_destroy);
+			sleep(1); // error_delay
+	}
+	return NULL;
+}
+
+static void _relay_rtp_clients(const us_rtp_s *rtp) {
+	US_LIST_ITERATE(_g_clients, client, {
+		us_janus_client_send(client, rtp);
+	});
 }
 
 static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
@@ -310,115 +250,120 @@ static int _plugin_init(janus_callbacks *gw, const char *config_dir_path) {
 	// sysctl -w net.core.rmem_max=1000000 
 	// sysctl -w net.core.wmem_max=1000000
 
-	JLOG_INFO("Initializing plugin ...");
-	assert(!READY);
-	assert(!STOP);
-	if (gw == NULL || config_dir_path == NULL || _read_config(config_dir_path) < 0) {
+	US_JLOG_INFO("main", "Initializing PiKVM uStreamer plugin %s ...", US_VERSION);
+	if (gw == NULL || config_dir_path == NULL || ((_g_config = us_config_init(config_dir_path)) == NULL)) {
 		return -1;
 	}
 	_g_gw = gw;
-	_g_rtp = rtp_init();
-	A_THREAD_CREATE(&_g_tid, _clients_thread, NULL);
+
+	_g_video_queue = us_queue_init(1024);
+	_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);
+		US_THREAD_CREATE(_g_audio_tid, _audio_thread, NULL);
+	}
+	US_THREAD_CREATE(_g_video_rtp_tid, _video_rtp_thread, NULL);
+	US_THREAD_CREATE(_g_video_sink_tid, _video_sink_thread, NULL);
+
+	atomic_store(&_g_ready, true);
 	return 0;
 }
 
 static void _plugin_destroy(void) {
-	JLOG_INFO("Destroying plugin ...");
-	atomic_store(&_g_stop, true);
-	if (READY) {
-		A_THREAD_JOIN(_g_tid);
-	}
+	US_JLOG_INFO("main", "Destroying plugin ...");
 
-	LIST_ITERATE(_g_clients, client, {
-		LIST_REMOVE(_g_clients, client);
-		free(client);
+	atomic_store(&_g_stop, true);
+#	define JOIN(_tid) { if (atomic_load(&_tid##_created)) { US_THREAD_JOIN(_tid); } }
+	JOIN(_g_video_sink_tid);
+	JOIN(_g_video_rtp_tid);
+	JOIN(_g_audio_tid);
+#	undef JOIN
+
+	US_LIST_ITERATE(_g_clients, client, {
+		US_LIST_REMOVE(_g_clients, client);
+		us_janus_client_destroy(client);
 	});
-	_g_clients = NULL;
-
-	rtp_destroy(_g_rtp);
-	_g_rtp = NULL;
 
-	_g_gw = NULL;
+	US_QUEUE_DELETE_WITH_ITEMS(_g_video_queue, us_frame_destroy);
 
-	if (_g_memsink_obj) {
-		free(_g_memsink_obj);
-		_g_memsink_obj = NULL;
-	}
+	US_DELETE(_g_rtpa, us_rtpa_destroy);
+	US_DELETE(_g_rtpv, us_rtpv_destroy);
+	US_DELETE(_g_config, us_config_destroy);
 }
 
-#define IF_DISABLED(...) { if (!READY || STOP) { __VA_ARGS__ } }
-
-static void _plugin_create_session(janus_plugin_session *session, int *error) {
-	IF_DISABLED({ *error = -1; return; });
-	LOCK;
-	JLOG_INFO("Creating session %p ...", session);
-	_client_s *client;
-	A_CALLOC(client, 1);
-	client->session = session;
-	client->transmit = true;
-	LIST_APPEND(_g_clients, client);
+#define _IF_DISABLED(...) { if (!_READY || _STOP) { __VA_ARGS__ } }
+
+static void _plugin_create_session(janus_plugin_session *session, int *err) {
+	_IF_DISABLED({ *err = -1; return; });
+	_LOCK_ALL;
+	US_JLOG_INFO("main", "Creating session %p ...", session);
+	us_janus_client_s *const client = us_janus_client_init(_g_gw, session);
+	US_LIST_APPEND(_g_clients, client);
 	atomic_store(&_g_has_watchers, true);
-	UNLOCK;
+	_UNLOCK_ALL;
 }
 
-static void _plugin_destroy_session(janus_plugin_session* session, int *error) {
-	IF_DISABLED({ *error = -1; return; });
-	LOCK;
+static void _plugin_destroy_session(janus_plugin_session* session, int *err) {
+	_IF_DISABLED({ *err = -1; return; });
+	_LOCK_ALL;
 	bool found = false;
 	bool has_watchers = false;
-	LIST_ITERATE(_g_clients, client, {
+	bool has_listeners = false;
+	US_LIST_ITERATE(_g_clients, client, {
 		if (client->session == session) {
-			JLOG_INFO("Removing session %p ...", session);
-			LIST_REMOVE(_g_clients, client);
-			free(client);
+			US_JLOG_INFO("main", "Removing session %p ...", session);
+			US_LIST_REMOVE(_g_clients, client);
+			us_janus_client_destroy(client);
 			found = true;
 		} else {
-			has_watchers = (has_watchers || client->transmit);
+			has_watchers = (has_watchers || atomic_load(&client->transmit));
+			has_listeners = (has_listeners || atomic_load(&client->transmit_audio));
 		}
 	});
 	if (!found) {
-		JLOG_WARN("No session %p", session);
-		*error = -2;
+		US_JLOG_WARN("main", "No session %p", session);
+		*err = -2;
 	}
 	atomic_store(&_g_has_watchers, has_watchers);
-	UNLOCK;
+	atomic_store(&_g_has_listeners, has_listeners);
+	_UNLOCK_ALL;
 }
 
 static json_t *_plugin_query_session(janus_plugin_session *session) {
-	IF_DISABLED({ return NULL; });
+	_IF_DISABLED({ return NULL; });
 	json_t *info = NULL;
-	LOCK;
-	LIST_ITERATE(_g_clients, client, {
+	_LOCK_ALL;
+	US_LIST_ITERATE(_g_clients, client, {
 		if (client->session == session) {
 			info = json_string("session_found");
 			break;
 		}
 	});
-	UNLOCK;
+	_UNLOCK_ALL;
 	return info;
 }
 
 static void _set_transmit(janus_plugin_session *session, UNUSED const char *msg, bool transmit) {
-	IF_DISABLED({ return; });
-	LOCK;
+	_IF_DISABLED({ return; });
+	_LOCK_ALL;
 	bool found = false;
 	bool has_watchers = false;
-	LIST_ITERATE(_g_clients, client, {
+	US_LIST_ITERATE(_g_clients, client, {
 		if (client->session == session) {
-			client->transmit = transmit;
-			//JLOG_INFO("%s session %p", msg, session);
+			atomic_store(&client->transmit, transmit);
+			// US_JLOG_INFO("main", "%s session %p", msg, session);
 			found = true;
 		}
-		has_watchers = (has_watchers || client->transmit);
+		has_watchers = (has_watchers || atomic_load(&client->transmit));
 	});
 	if (!found) {
-		JLOG_WARN("No session %p", session);
+		US_JLOG_WARN("main", "No session %p", session);
 	}
 	atomic_store(&_g_has_watchers, has_watchers);
-	UNLOCK;
+	_UNLOCK_ALL;
 }
 
-#undef IF_DISABLED
+#undef _IF_DISABLED
 
 static void _plugin_setup_media(janus_plugin_session *session) { _set_transmit(session, "Unmuted", true); }
 static void _plugin_hangup_media(janus_plugin_session *session) { _set_transmit(session, "Muted", false); }
@@ -429,8 +374,8 @@ static struct janus_plugin_result *_plugin_handle_message(
 	assert(transaction != NULL);
 
 #	define FREE_MSG_JSEP { \
-			if (msg) json_decref(msg); \
-			if (jsep) json_decref(jsep); \
+			US_DELETE(msg, json_decref); \
+			US_DELETE(jsep, json_decref); \
 		}
 
 	if (session == NULL || msg == NULL) {
@@ -439,56 +384,110 @@ static struct janus_plugin_result *_plugin_handle_message(
 		return janus_plugin_result_new(JANUS_PLUGIN_ERROR, (msg ? "No session" : "No message"), NULL);
 	}
 
-#	define PUSH_ERROR(_error, _reason) { \
-			/*JLOG_ERROR("Message error in session %p: %s", session, _reason);*/ \
-			json_t *_event = json_object(); \
-			json_object_set_new(_event, "ustreamer", json_string("event")); \
-			json_object_set_new(_event, "error_code", json_integer(_error)); \
-			json_object_set_new(_event, "error", json_string(_reason)); \
-			_g_gw->push_event(session, &_plugin, transaction, _event, NULL); \
-			json_decref(_event); \
+#	define PUSH_ERROR(x_error, x_reason) { \
+			/*US_JLOG_ERROR("main", "Message error in session %p: %s", session, x_reason);*/ \
+			json_t *m_event = json_object(); \
+			json_object_set_new(m_event, "ustreamer", json_string("event")); \
+			json_object_set_new(m_event, "error_code", json_integer(x_error)); \
+			json_object_set_new(m_event, "error", json_string(x_reason)); \
+			_g_gw->push_event(session, create(), transaction, m_event, NULL); \
+			json_decref(m_event); \
 		}
 
-	json_t *request_obj = json_object_get(msg, "request");
-	if (request_obj == NULL) {
+	json_t *const request = json_object_get(msg, "request");
+	if (request == NULL) {
 		PUSH_ERROR(400, "Request missing");
 		goto ok_wait;
 	}
 
-	const char *request_str = json_string_value(request_obj);
-	if (!request_str) {
+	const char *const request_str = json_string_value(request);
+	if (request_str == NULL) {
 		PUSH_ERROR(400, "Request not a string");
 		goto ok_wait;
 	}
-	//JLOG_INFO("Message: %s", request_str);
-
-#	define PUSH_STATUS(_status, _jsep) { \
-			json_t *_event = json_object(); \
-			json_object_set_new(_event, "ustreamer", json_string("event")); \
-			json_t *_result = json_object(); \
-			json_object_set_new(_result, "status", json_string(_status)); \
-			json_object_set_new(_event, "result", _result); \
-			_g_gw->push_event(session, &_plugin, transaction, _event, _jsep); \
-			json_decref(_event); \
+	// US_JLOG_INFO("main", "Message: %s", request_str);
+
+#	define PUSH_STATUS(x_status, x_payload, x_jsep) { \
+			json_t *const m_event = json_object(); \
+			json_object_set_new(m_event, "ustreamer", json_string("event")); \
+			json_t *const m_result = json_object(); \
+			json_object_set_new(m_result, "status", json_string(x_status)); \
+			if (x_payload != NULL) { \
+				json_object_set_new(m_result, x_status, x_payload); \
+			} \
+			json_object_set_new(m_event, "result", m_result); \
+			_g_gw->push_event(session, create(), transaction, m_event, x_jsep); \
+			json_decref(m_event); \
 		}
 
 	if (!strcmp(request_str, "start")) {
-		PUSH_STATUS("started", NULL);
+		PUSH_STATUS("started", NULL, NULL);
 
 	} else if (!strcmp(request_str, "stop")) {
-		PUSH_STATUS("stopped", NULL);
+		PUSH_STATUS("stopped", NULL, NULL);
 
 	} else if (!strcmp(request_str, "watch")) {
-		char *sdp = rtp_make_sdp(_g_rtp);
-		if (sdp == NULL) {
-			PUSH_ERROR(503, "Haven't received SPS/PPS from memsink yet");
-			goto ok_wait;
+		bool with_audio = false;
+		{
+			json_t *const params = json_object_get(msg, "params");
+			if (params != NULL) {
+				json_t *const audio = json_object_get(params, "audio");
+				if (audio != NULL && json_is_boolean(audio)) {
+					with_audio = (_g_rtpa != NULL && json_boolean_value(audio));
+				}
+			}
+		}
+
+		{
+			char *sdp;
+			char *const video_sdp = us_rtpv_make_sdp(_g_rtpv);
+			char *const audio_sdp = (with_audio ? us_rtpa_make_sdp(_g_rtpa) : us_strdup(""));
+			US_ASPRINTF(sdp,
+				"v=0" RN
+				"o=- %" PRIu64 " 1 IN IP4 0.0.0.0" RN
+				"s=PiKVM uStreamer" RN
+				"t=0 0" RN
+				"%s%s",
+				us_get_now_id() >> 1,
+#				if JANUS_PLUGIN_API_VERSION >= 100
+				// Place video SDP before audio SDP so that the video and audio streams
+				// have predictable indices, even if audio is not available.
+				// See also client.c.
+				video_sdp, audio_sdp
+#				else
+				// For versions of Janus prior to 1.x, place the audio SDP first.
+				audio_sdp, video_sdp
+#				endif
+			);
+			json_t *const offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
+			PUSH_STATUS("started", NULL, offer_jsep);
+			json_decref(offer_jsep);
+			free(audio_sdp);
+			free(video_sdp);
+			free(sdp);
+		}
+
+		{
+			_LOCK_ALL;
+			bool has_listeners = false;
+			US_LIST_ITERATE(_g_clients, client, {
+				if (client->session == session) {
+					atomic_store(&client->transmit_audio, with_audio);
+				}
+				has_listeners = (has_listeners || atomic_load(&client->transmit_audio));
+			});
+			atomic_store(&_g_has_listeners, has_listeners);
+			_UNLOCK_ALL;
 		}
-		//JLOG_INFO("SDP generated:\n%s", sdp);
-		json_t *offer_jsep = json_pack("{ssss}", "type", "offer", "sdp", sdp);
-		free(sdp);
-		PUSH_STATUS("started", offer_jsep);
-		json_decref(offer_jsep);
+
+	} else if (!strcmp(request_str, "features")) {
+		json_t *const features = json_pack("{sb}", "audio", (_g_rtpa != NULL));
+		PUSH_STATUS("features", features, NULL);
+		json_decref(features);
+
+	} else if (!strcmp(request_str, "key_required")) {
+		// US_JLOG_INFO("main", "Got key_required message");
+		atomic_store(&_g_key_required, true);
 
 	} else {
 		PUSH_ERROR(405, "Not implemented");
@@ -498,25 +497,55 @@ static struct janus_plugin_result *_plugin_handle_message(
 		FREE_MSG_JSEP;
 		return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
 
+#	undef PUSH_STATUS
 #	undef PUSH_ERROR
 #	undef FREE_MSG_JSEP
 }
 
-static int _plugin_get_api_compatibility(void)		{ return JANUS_PLUGIN_API_VERSION; }
-static int _plugin_get_version(void)				{ return VERSION_U; }
-static const char *_plugin_get_version_string(void)	{ return VERSION; }
-static const char *_plugin_get_description(void)	{ return "Pi-KVM uStreamer Janus plugin for H.264 video"; }
-static const char *_plugin_get_name(void)			{ return "ustreamer"; }
-static const char *_plugin_get_author(void)			{ return "Maxim Devaev <mdevaev@gmail.com>"; }
-static const char *_plugin_get_package(void)		{ return "janus.plugin.ustreamer"; }
+static void _plugin_incoming_rtcp(UNUSED janus_plugin_session *handle, UNUSED janus_plugin_rtcp *packet) {
+	if (packet->video && janus_rtcp_has_pli(packet->buffer, packet->length)) {
+		// US_JLOG_INFO("main", "Got video PLI");
+		atomic_store(&_g_key_required, true);
+	}
+}
 
 
-#undef STOP
-#undef READY
-#undef UNLOCK
-#undef LOCK
+// ***** Plugin *****
 
-#undef JLOG_PERROR
-#undef JLOG_ERROR
-#undef JLOG_WARN
-#undef JLOG_INFO
+static int _plugin_get_api_compatibility(void)		{ return JANUS_PLUGIN_API_VERSION; }
+static int _plugin_get_version(void)				{ return US_VERSION_U; }
+static const char *_plugin_get_version_string(void)	{ return US_VERSION; }
+static const char *_plugin_get_description(void)	{ return "PiKVM uStreamer Janus plugin for H.264 video"; }
+static const char *_plugin_get_name(void)			{ return US_PLUGIN_NAME; }
+static const char *_plugin_get_author(void)			{ return "Maxim Devaev <mdevaev@gmail.com>"; }
+static const char *_plugin_get_package(void)		{ return US_PLUGIN_PACKAGE; }
+
+janus_plugin *create(void) {
+#	pragma GCC diagnostic push
+#	pragma GCC diagnostic ignored "-Woverride-init"
+	static janus_plugin plugin = JANUS_PLUGIN_INIT(
+		.init = _plugin_init,
+		.destroy = _plugin_destroy,
+
+		.create_session = _plugin_create_session,
+		.destroy_session = _plugin_destroy_session,
+		.query_session = _plugin_query_session,
+
+		.setup_media = _plugin_setup_media,
+		.hangup_media = _plugin_hangup_media,
+
+		.handle_message = _plugin_handle_message,
+
+		.get_api_compatibility = _plugin_get_api_compatibility,
+		.get_version = _plugin_get_version,
+		.get_version_string = _plugin_get_version_string,
+		.get_description = _plugin_get_description,
+		.get_name = _plugin_get_name,
+		.get_author = _plugin_get_author,
+		.get_package = _plugin_get_package,
+
+		.incoming_rtcp = _plugin_incoming_rtcp,
+	);
+#	pragma GCC diagnostic pop
+	return &plugin;
+}
diff --git a/janus/src/queue.c b/janus/src/queue.c
new file mode 100644
index 0000000..ccb0423
--- /dev/null
+++ b/janus/src/queue.c
@@ -0,0 +1,102 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "queue.h"
+
+
+us_queue_s *us_queue_init(unsigned capacity) {
+	us_queue_s *queue;
+	US_CALLOC(queue, 1);
+	US_CALLOC(queue->items, capacity);
+	queue->capacity = capacity;
+	US_MUTEX_INIT(queue->mutex);
+
+	pthread_condattr_t attrs;
+	assert(!pthread_condattr_init(&attrs));
+	assert(!pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC));
+	assert(!pthread_cond_init(&queue->full_cond, &attrs));
+	assert(!pthread_cond_init(&queue->empty_cond, &attrs));
+	assert(!pthread_condattr_destroy(&attrs));
+	return queue;
+}
+
+void us_queue_destroy(us_queue_s *queue) {
+	US_COND_DESTROY(queue->empty_cond);
+	US_COND_DESTROY(queue->full_cond);
+	US_MUTEX_DESTROY(queue->mutex);
+	free(queue->items);
+	free(queue);
+}
+
+#define _WAIT_OR_UNLOCK(x_var, x_cond) { \
+		struct timespec m_ts; \
+		assert(!clock_gettime(CLOCK_MONOTONIC, &m_ts)); \
+		us_ld_to_timespec(us_timespec_to_ld(&m_ts) + timeout, &m_ts); \
+		while (x_var) { \
+			const int err = pthread_cond_timedwait(&(x_cond), &queue->mutex, &m_ts); \
+			if (err == ETIMEDOUT) { \
+				US_MUTEX_UNLOCK(queue->mutex); \
+				return -1; \
+			} \
+			assert(!err); \
+		} \
+	}
+
+int us_queue_put(us_queue_s *queue, void *item, long double timeout) {
+	US_MUTEX_LOCK(queue->mutex);
+	if (timeout == 0) {
+		if (queue->size == queue->capacity) {
+			US_MUTEX_UNLOCK(queue->mutex);
+			return -1;
+		}
+	} else {
+		_WAIT_OR_UNLOCK(queue->size == queue->capacity, queue->full_cond);
+	}
+	queue->items[queue->in] = item;
+	++queue->size;
+	++queue->in;
+	queue->in %= queue->capacity;
+	US_MUTEX_UNLOCK(queue->mutex);
+	US_COND_BROADCAST(queue->empty_cond);
+	return 0;
+}
+
+int us_queue_get(us_queue_s *queue, void **item, long double timeout) {
+	US_MUTEX_LOCK(queue->mutex);
+	_WAIT_OR_UNLOCK(queue->size == 0, queue->empty_cond);
+	*item = queue->items[queue->out];
+	--queue->size;
+	++queue->out;
+	queue->out %= queue->capacity;
+	US_MUTEX_UNLOCK(queue->mutex);
+	US_COND_BROADCAST(queue->full_cond);
+	return 0;
+}
+
+#undef _WAIT_OR_UNLOCK
+
+int us_queue_get_free(us_queue_s *queue) {
+	US_MUTEX_LOCK(queue->mutex);
+	const unsigned size = queue->size;
+	US_MUTEX_UNLOCK(queue->mutex);
+	return queue->capacity - size;
+}
diff --git a/janus/src/queue.h b/janus/src/queue.h
new file mode 100644
index 0000000..79cf677
--- /dev/null
+++ b/janus/src/queue.h
@@ -0,0 +1,68 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+
+#include <pthread.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/threading.h"
+
+
+// Based on https://github.com/seifzadeh/c-pthread-queue/blob/master/queue.h
+
+typedef struct {
+	void			**items;
+	unsigned		size;
+	unsigned		capacity;
+	unsigned		in;
+	unsigned		out;
+
+	pthread_mutex_t	mutex;
+	pthread_cond_t	full_cond;
+	pthread_cond_t	empty_cond;
+} us_queue_s;
+
+
+#define US_QUEUE_DELETE_WITH_ITEMS(x_queue, x_free_item) { \
+		if (x_queue) { \
+			while (!us_queue_get_free(x_queue)) { \
+				void *m_ptr; \
+				if (!us_queue_get(x_queue, &m_ptr, 0)) { \
+					US_DELETE(m_ptr, x_free_item); \
+				} \
+			} \
+			us_queue_destroy(x_queue); \
+		} \
+	}
+
+
+us_queue_s *us_queue_init(unsigned capacity);
+void us_queue_destroy(us_queue_s *queue);
+
+int us_queue_put(us_queue_s *queue, void *item, long double timeout);
+int us_queue_get(us_queue_s *queue, void **item, long double timeout);
+int us_queue_get_free(us_queue_s *queue);
diff --git a/janus/src/rtp.c b/janus/src/rtp.c
index f6047fb..1c31b84 100644
--- a/janus/src/rtp.c
+++ b/janus/src/rtp.c
@@ -26,182 +26,32 @@
 #include "rtp.h"
 
 
-#define PAYLOAD 96 // Payload type
-#define PRE 3 // Annex B prefix length
-
-
-void _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, bool marked, rtp_callback_f callback);
-static void _rtp_write_header(rtp_s *rtp, uint32_t pts, bool marked);
-
-static ssize_t _find_annexb(const uint8_t *data, size_t size);
-
-
-rtp_s *rtp_init(void) {
-	rtp_s *rtp;
-	A_CALLOC(rtp, 1);
-	rtp->ssrc = triple_u32(get_now_monotonic_u64());
-	rtp->sps = frame_init();
-	rtp->pps = frame_init();
-	A_MUTEX_INIT(&rtp->mutex);
+us_rtp_s *us_rtp_init(unsigned payload, bool video) {
+	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;
 }
 
-void rtp_destroy(rtp_s *rtp) {
-	A_MUTEX_DESTROY(&rtp->mutex);
-	frame_destroy(rtp->pps);
-	frame_destroy(rtp->sps);
-	free(rtp);
-}
-
-char *rtp_make_sdp(rtp_s *rtp) {
-	A_MUTEX_LOCK(&rtp->mutex);
-
-	if (rtp->sps->used == 0 || rtp->pps->used == 0) {
-		A_MUTEX_UNLOCK(&rtp->mutex);
-		return NULL;
-	}
-
-	char *sps = NULL;
-	char *pps = NULL;
-	base64_encode(rtp->sps->data, rtp->sps->used, &sps, NULL);
-	base64_encode(rtp->pps->data, rtp->pps->used, &pps, NULL);
-
-	A_MUTEX_UNLOCK(&rtp->mutex);
-
-	// https://tools.ietf.org/html/rfc6184
-	// https://github.com/meetecho/janus-gateway/issues/2443
-	char *sdp;
-	A_ASPRINTF(sdp,
-		"v=0" RN
-		"o=- %" PRIu64 " 1 IN IP4 127.0.0.1" RN
-		"s=Pi-KVM uStreamer" RN
-		"t=0 0" RN
-		"m=video 1 RTP/SAVPF %d" RN
-		"c=IN IP4 0.0.0.0" RN
-		"a=rtpmap:%d H264/90000" RN
-		"a=fmtp:%d profile-level-id=42E01F" RN
-		"a=fmtp:%d packetization-mode=1" RN
-		"a=fmtp:%d sprop-sps=%s" RN
-		"a=fmtp:%d sprop-pps=%s" RN
-		"a=rtcp-fb:%d nack" RN
-		"a=rtcp-fb:%d nack pli" RN
-		"a=rtcp-fb:%d goog-remb" RN
-		"a=sendonly" RN,
-		get_now_id() >> 1, PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD,
-		PAYLOAD, sps,
-		PAYLOAD, pps,
-		PAYLOAD, PAYLOAD, PAYLOAD
-	);
-
-	free(sps);
-	free(pps);
-	return sdp;
-}
-
-void rtp_wrap_h264(rtp_s *rtp, const frame_s *frame, rtp_callback_f callback) {
-	// There is a complicated logic here but everything works as it should:
-	//   - https://github.com/pikvm/ustreamer/issues/115#issuecomment-893071775
-
-	assert(frame->format == V4L2_PIX_FMT_H264);
-
-	const uint32_t pts = get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
-	ssize_t last_offset = -PRE;
-
-	while (true) { // Find and iterate by nalus
-		const size_t next_start = last_offset + PRE;
-		ssize_t offset = _find_annexb(frame->data + next_start, frame->used - next_start);
-		if (offset < 0) {
-			break;
-		}
-		offset += next_start;
-
-		if (last_offset >= 0) {
-			const uint8_t *data = frame->data + last_offset + PRE;
-			size_t size = offset - last_offset - PRE;
-			if (data[size - 1] == 0) { // Check for extra 00
-				--size;
-			}
-			_rtp_process_nalu(rtp, data, size, pts, false, callback);
-		}
-
-		last_offset = offset;
-	}
-
-	if (last_offset >= 0) {
-		const uint8_t *data = frame->data + last_offset + PRE;
-		size_t size = frame->used - last_offset - PRE;
-		_rtp_process_nalu(rtp, data, size, pts, true, callback);
-	}
+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 _rtp_process_nalu(rtp_s *rtp, const uint8_t *data, size_t size, uint32_t pts, bool marked, rtp_callback_f callback) {
-	const unsigned ref_idc = (data[0] >> 5) & 3;
-	const unsigned type = data[0] & 0x1F;
-
-	frame_s *ps = NULL;
-	switch (type) {
-		case 7: ps = rtp->sps; break;
-		case 8: ps = rtp->pps; break;
-	}
-	if (ps) {
-		A_MUTEX_LOCK(&rtp->mutex);
-		frame_set_data(ps, data, size);
-		A_MUTEX_UNLOCK(&rtp->mutex);
-	}
-
-#	define HEADER_SIZE 12
-
-	if (size + HEADER_SIZE <= RTP_DATAGRAM_SIZE) {
-		_rtp_write_header(rtp, pts, marked);
-		memcpy(rtp->datagram + HEADER_SIZE, data, size);
-		callback(rtp->datagram, size + HEADER_SIZE);
-		return;
-	}
-
-	const size_t fu_overhead = HEADER_SIZE + 2; // FU-A overhead
-
-	const uint8_t *src = data + 1;
-	ssize_t remaining = size - 1;
-
-	bool first = true;
-	while (remaining > 0) {
-		ssize_t frag_size = RTP_DATAGRAM_SIZE - fu_overhead;
-		const bool last = (remaining <= frag_size);
-		if (last) {
-			frag_size = remaining;
-		}
-
-		_rtp_write_header(rtp, pts, (marked && last));
-
-		rtp->datagram[HEADER_SIZE] = 28 | (ref_idc << 5);
-
-		uint8_t fu = type;
-		if (first) {
-			fu |= 0x80;
-		}
-		if (last) {
-			fu |= 0x40;
-		}
-		rtp->datagram[HEADER_SIZE + 1] = fu;
-
-		memcpy(rtp->datagram + fu_overhead,	src, frag_size);
-
-		callback(rtp->datagram, fu_overhead + frag_size);
-
-		src += frag_size;
-		remaining -= frag_size;
-		first = false;
-	}
-
-#	undef HEADER_SIZE
+void us_rtp_destroy(us_rtp_s *rtp) {
+	free(rtp);
 }
 
-static void _rtp_write_header(rtp_s *rtp, uint32_t pts, bool marked) {
+void us_rtp_write_header(us_rtp_s *rtp, uint32_t pts, bool marked) {
 	uint32_t word0 = 0x80000000;
 	if (marked) {
 		word0 |= 1 << 23;
 	}
-	word0 |= (PAYLOAD & 0x7F) << 16;
+	word0 |= (rtp->payload & 0x7F) << 16;
 	word0 |= rtp->seq;
 	++rtp->seq;
 
@@ -211,18 +61,3 @@ static void _rtp_write_header(rtp_s *rtp, uint32_t pts, bool marked) {
 	WRITE_BE_U32(8, rtp->ssrc);
 #	undef WRITE_BE_U32
 }
-
-static ssize_t _find_annexb(const uint8_t *data, size_t size) {
-	// Parses buffer for 00 00 01 start codes
-	if (size >= PRE) {
-		for (size_t index = 0; index <= size - PRE; ++index) {
-			if (data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
-				return index;
-			}
-		}
-	}
-	return -1;
-}
-
-#undef PRE
-#undef PAYLOAD
diff --git a/janus/src/rtp.h b/janus/src/rtp.h
index b1ba606..06cc1d7 100644
--- a/janus/src/rtp.h
+++ b/janus/src/rtp.h
@@ -2,9 +2,6 @@
 #                                                                            #
 #    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
 #                                                                            #
-#    This source file is partially based on this code:                       #
-#      - https://github.com/catid/kvm/blob/master/kvm_pipeline/src           #
-#                                                                            #
 #    Copyright (C) 2018-2022  Maxim Devaev <mdevaev@gmail.com>               #
 #                                                                            #
 #    This program is free software: you can redistribute it and/or modify    #
@@ -26,44 +23,35 @@
 #pragma once
 
 #include <stdlib.h>
-#include <stdbool.h>
 #include <stdint.h>
-#include <inttypes.h>
-#include <string.h>
-#include <assert.h>
+#include <stdbool.h>
 
 #include <sys/types.h>
-#include <linux/videodev2.h>
 
-#include <pthread.h>
-
-#include "tools.h"
-#include "threading.h"
-#include "frame.h"
-#include "base64.h"
+#include "uslibs/tools.h"
 
 
 // https://stackoverflow.com/questions/47635545/why-webrtc-chose-rtp-max-packet-size-to-1200-bytes
-#define RTP_DATAGRAM_SIZE 1200
+#define US_RTP_DATAGRAM_SIZE	1200
+#define US_RTP_HEADER_SIZE		12
 
 
 typedef struct {
-	uint32_t ssrc;
-	uint16_t seq;
-
-	uint8_t datagram[RTP_DATAGRAM_SIZE];
-
-	frame_s *sps; // Actually not a frame, just a bytes storage
-	frame_s *pps;
+	unsigned	payload;
+	bool		video;
+	uint32_t	ssrc;
 
-	pthread_mutex_t mutex;
-} rtp_s;
+	uint16_t	seq;
+	uint8_t		datagram[US_RTP_DATAGRAM_SIZE];
+	size_t		used;
+	bool		zero_playout_delay;
+} us_rtp_s;
 
-typedef void (*rtp_callback_f)(const uint8_t *datagram, size_t size);
+typedef void (*us_rtp_callback_f)(const us_rtp_s *rtp);
 
 
-rtp_s *rtp_init(void);
-void rtp_destroy(rtp_s *rtp);
+us_rtp_s *us_rtp_init(unsigned payload, bool video);
+us_rtp_s *us_rtp_dup(const us_rtp_s *rtp);
+void us_rtp_destroy(us_rtp_s *rtp);
 
-char *rtp_make_sdp(rtp_s *rtp);
-void rtp_wrap_h264(rtp_s *rtp, const frame_s *frame, rtp_callback_f callback);
+void us_rtp_write_header(us_rtp_s *rtp, uint32_t pts, bool marked);
diff --git a/janus/src/rtpa.c b/janus/src/rtpa.c
new file mode 100644
index 0000000..c3be0b0
--- /dev/null
+++ b/janus/src/rtpa.c
@@ -0,0 +1,66 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "rtpa.h"
+
+
+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->callback = callback;
+	return rtpa;
+}
+
+void us_rtpa_destroy(us_rtpa_s *rtpa) {
+	us_rtp_destroy(rtpa->rtp);
+	free(rtpa);
+}
+
+char *us_rtpa_make_sdp(us_rtpa_s *rtpa) {
+#	define PAYLOAD rtpa->rtp->payload
+	char *sdp;
+	US_ASPRINTF(sdp,
+		"m=audio 1 RTP/SAVPF %u" RN
+		"c=IN IP4 0.0.0.0" RN
+		"a=rtpmap:%u OPUS/48000/2" RN
+		// "a=fmtp:%u useinbandfec=1" RN
+		"a=rtcp-fb:%u nack" RN
+		"a=rtcp-fb:%u nack pli" RN
+		"a=rtcp-fb:%u goog-remb" RN
+		"a=ssrc:%" PRIu32 " cname:ustreamer" RN
+		"a=sendonly" RN,
+		PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD, // PAYLOAD,
+		rtpa->rtp->ssrc
+	);
+#	undef PAYLOAD
+	return sdp;
+}
+
+void us_rtpa_wrap(us_rtpa_s *rtpa, const uint8_t *data, size_t size, uint32_t pts) {
+    if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
+        us_rtp_write_header(rtpa->rtp, pts, false);
+        memcpy(rtpa->rtp->datagram + US_RTP_HEADER_SIZE, data, size);
+		rtpa->rtp->used = size + US_RTP_HEADER_SIZE;
+        rtpa->callback(rtpa->rtp);
+	}
+}
diff --git a/janus/src/rtpa.h b/janus/src/rtpa.h
new file mode 100644
index 0000000..53debfc
--- /dev/null
+++ b/janus/src/rtpa.h
@@ -0,0 +1,48 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <sys/types.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/threading.h"
+
+#include "rtp.h"
+
+
+typedef struct {
+	us_rtp_s			*rtp;
+	us_rtp_callback_f	callback;
+} us_rtpa_s;
+
+
+us_rtpa_s *us_rtpa_init(us_rtp_callback_f callback);
+void us_rtpa_destroy(us_rtpa_s *rtpa);
+
+char *us_rtpa_make_sdp(us_rtpa_s *rtpa);
+void us_rtpa_wrap(us_rtpa_s *rtpa, const uint8_t *data, size_t size, uint32_t pts);
diff --git a/janus/src/rtpv.c b/janus/src/rtpv.c
new file mode 100644
index 0000000..f21c9b3
--- /dev/null
+++ b/janus/src/rtpv.c
@@ -0,0 +1,176 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    This source file is partially based on this code:                       #
+#      - https://github.com/catid/kvm/blob/master/kvm_pipeline/src           #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "rtpv.h"
+
+
+void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint32_t pts, bool marked);
+
+static ssize_t _find_annexb(const uint8_t *data, size_t 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->callback = callback;
+	return rtpv;
+}
+
+void us_rtpv_destroy(us_rtpv_s *rtpv) {
+	us_rtp_destroy(rtpv->rtp);
+	free(rtpv);
+}
+
+char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
+#	define PAYLOAD rtpv->rtp->payload
+	// https://tools.ietf.org/html/rfc6184
+	// https://github.com/meetecho/janus-gateway/issues/2443
+	char *sdp;
+	US_ASPRINTF(sdp,
+		"m=video 1 RTP/SAVPF %u" RN
+		"c=IN IP4 0.0.0.0" RN
+		"a=rtpmap:%u H264/90000" RN
+		"a=fmtp:%u profile-level-id=42E01F" RN
+		"a=fmtp:%u packetization-mode=1" RN
+		"a=rtcp-fb:%u nack" RN
+		"a=rtcp-fb:%u nack pli" RN
+		"a=rtcp-fb:%u goog-remb" RN
+		"a=ssrc:%" PRIu32 " cname:ustreamer" RN
+		"a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
+		"a=sendonly" RN,
+		PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD,
+		PAYLOAD, PAYLOAD, PAYLOAD,
+		rtpv->rtp->ssrc
+	);
+	return sdp;
+#	undef PAYLOAD
+}
+
+#define _PRE 3 // Annex B prefix length
+
+void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame) {
+	// There is a complicated logic here but everything works as it should:
+	//   - https://github.com/pikvm/ustreamer/issues/115#issuecomment-893071775
+
+	assert(frame->format == V4L2_PIX_FMT_H264);
+
+	rtpv->rtp->zero_playout_delay = (frame->gop == 0);
+
+	const uint32_t pts = us_get_now_monotonic_u64() * 9 / 100; // PTS units are in 90 kHz
+	ssize_t last_offset = -_PRE;
+
+	while (true) { // Find and iterate by nalus
+		const size_t next_start = last_offset + _PRE;
+		ssize_t offset = _find_annexb(frame->data + next_start, frame->used - next_start);
+		if (offset < 0) {
+			break;
+		}
+		offset += next_start;
+
+		if (last_offset >= 0) {
+			const uint8_t *const data = frame->data + last_offset + _PRE;
+			size_t size = offset - last_offset - _PRE;
+			if (data[size - 1] == 0) { // Check for extra 00
+				--size;
+			}
+			_rtpv_process_nalu(rtpv, data, size, pts, false);
+		}
+
+		last_offset = offset;
+	}
+
+	if (last_offset >= 0) {
+		const uint8_t *const data = frame->data + last_offset + _PRE;
+		size_t size = frame->used - last_offset - _PRE;
+		_rtpv_process_nalu(rtpv, data, size, pts, true);
+	}
+}
+
+void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint32_t pts, bool marked) {
+#	define DG rtpv->rtp->datagram
+
+	const unsigned ref_idc = (data[0] >> 5) & 3;
+	const unsigned type = data[0] & 0x1F;
+
+	if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
+		us_rtp_write_header(rtpv->rtp, pts, marked);
+		memcpy(DG + US_RTP_HEADER_SIZE, data, size);
+		rtpv->rtp->used = size + US_RTP_HEADER_SIZE;
+		rtpv->callback(rtpv->rtp);
+		return;
+	}
+
+	const size_t fu_overhead = US_RTP_HEADER_SIZE + 2; // FU-A overhead
+
+	const uint8_t *src = data + 1;
+	ssize_t remaining = size - 1;
+
+	bool first = true;
+	while (remaining > 0) {
+		ssize_t frag_size = US_RTP_DATAGRAM_SIZE - fu_overhead;
+		const bool last = (remaining <= frag_size);
+		if (last) {
+			frag_size = remaining;
+		}
+
+		us_rtp_write_header(rtpv->rtp, pts, (marked && last));
+
+		DG[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5);
+
+		uint8_t fu = type;
+		if (first) {
+			fu |= 0x80;
+		}
+		if (last) {
+			fu |= 0x40;
+		}
+		DG[US_RTP_HEADER_SIZE + 1] = fu;
+
+		memcpy(DG + fu_overhead, src, frag_size);
+		rtpv->rtp->used = fu_overhead + frag_size;
+		rtpv->callback(rtpv->rtp);
+
+		src += frag_size;
+		remaining -= frag_size;
+		first = false;
+	}
+
+#	undef DG
+}
+
+static ssize_t _find_annexb(const uint8_t *data, size_t size) {
+	// Parses buffer for 00 00 01 start codes
+	if (size >= _PRE) {
+		for (size_t index = 0; index <= size - _PRE; ++index) {
+			if (data[index] == 0 && data[index + 1] == 0 && data[index + 2] == 1) {
+				return index;
+			}
+		}
+	}
+	return -1;
+}
+
+#undef _PRE
diff --git a/src/ustreamer/h264/stream.h b/janus/src/rtpv.h
similarity index 75%
rename from src/ustreamer/h264/stream.h
rename to janus/src/rtpv.h
index fb4dab5..01c2010 100644
--- a/src/ustreamer/h264/stream.h
+++ b/janus/src/rtpv.h
@@ -22,27 +22,29 @@
 
 #pragma once
 
+#include <stdlib.h>
 #include <stdbool.h>
-#include <stdatomic.h>
+#include <stdint.h>
+#include <inttypes.h>
 #include <assert.h>
 
-#include "../../libs/tools.h"
-#include "../../libs/logging.h"
-#include "../../libs/frame.h"
-#include "../../libs/memsink.h"
-#include "../../libs/unjpeg.h"
-#include "../m2m.h"
+#include <sys/types.h>
+#include <linux/videodev2.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/frame.h"
+
+#include "rtp.h"
 
 
 typedef struct {
-	memsink_s		*sink;
-	frame_s			*tmp_src;
-	frame_s			*dest;
-	m2m_encoder_s	*enc;
-	atomic_bool		online;
-} h264_stream_s;
+	us_rtp_s			*rtp;
+	us_rtp_callback_f	callback;
+} us_rtpv_s;
+
 
+us_rtpv_s *us_rtpv_init(us_rtp_callback_f callback);
+void us_rtpv_destroy(us_rtpv_s *rtpv);
 
-h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop);
-void h264_stream_destroy(h264_stream_s *h264);
-void h264_stream_process(h264_stream_s *h264, const frame_s *frame, bool force_key);
+char *us_rtpv_make_sdp(us_rtpv_s *rtpv);
+void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame);
diff --git a/janus/src/tc358743.c b/janus/src/tc358743.c
new file mode 100644
index 0000000..1648f35
--- /dev/null
+++ b/janus/src/tc358743.c
@@ -0,0 +1,64 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "tc358743.h"
+
+
+#ifndef V4L2_CID_USER_TC358743_BASE
+#	define V4L2_CID_USER_TC358743_BASE (V4L2_CID_USER_BASE + 0x1080)
+#endif
+#ifndef TC358743_CID_AUDIO_PRESENT
+#	define TC358743_CID_AUDIO_PRESENT (V4L2_CID_USER_TC358743_BASE + 1)
+#endif
+#ifndef TC358743_CID_AUDIO_SAMPLING_RATE
+#	define TC358743_CID_AUDIO_SAMPLING_RATE (V4L2_CID_USER_TC358743_BASE + 0)
+#endif
+
+
+int us_tc358743_read_info(const char *path, us_tc358743_info_s *info) {
+	US_MEMSET_ZERO(*info);
+
+	int fd = -1;
+	if ((fd = open(path, O_RDWR)) < 0) {
+		US_JLOG_PERROR("audio", "Can't open TC358743 V4L2 device");
+		return -1;
+	}
+
+#	define READ_CID(x_cid, x_field) { \
+			struct v4l2_control m_ctl = {0}; \
+			m_ctl.id = x_cid; \
+			if (us_xioctl(fd, VIDIOC_G_CTRL, &m_ctl) < 0) { \
+				US_JLOG_PERROR("audio", "Can't get value of " #x_cid); \
+				close(fd); \
+				return -1; \
+			} \
+			info->x_field = m_ctl.value; \
+		}
+
+	READ_CID(TC358743_CID_AUDIO_PRESENT,		has_audio);
+	READ_CID(TC358743_CID_AUDIO_SAMPLING_RATE,	audio_hz);
+
+#	undef READ_CID
+
+	close(fd);
+	return 0;
+}
diff --git a/janus/src/tc358743.h b/janus/src/tc358743.h
new file mode 100644
index 0000000..4e3855d
--- /dev/null
+++ b/janus/src/tc358743.h
@@ -0,0 +1,46 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+
+#include <linux/videodev2.h>
+#include <linux/v4l2-controls.h>
+
+#include "uslibs/tools.h"
+#include "uslibs/xioctl.h"
+
+#include "logging.h"
+
+
+typedef struct {
+	bool		has_audio;
+	unsigned	audio_hz;
+} us_tc358743_info_s;
+
+
+int us_tc358743_read_info(const char *path, us_tc358743_info_s *info);
diff --git a/janus/src/threading.h b/janus/src/threading.h
deleted file mode 120000
index 44824c3..0000000
--- a/janus/src/threading.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/threading.h
\ No newline at end of file
diff --git a/janus/src/tools.h b/janus/src/tools.h
deleted file mode 120000
index c2a07b7..0000000
--- a/janus/src/tools.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/tools.h
\ No newline at end of file
diff --git a/janus/src/uslibs/array.h b/janus/src/uslibs/array.h
new file mode 120000
index 0000000..87ed614
--- /dev/null
+++ b/janus/src/uslibs/array.h
@@ -0,0 +1 @@
+../../../src/libs/array.h
\ No newline at end of file
diff --git a/janus/src/uslibs/const.h b/janus/src/uslibs/const.h
new file mode 120000
index 0000000..137d311
--- /dev/null
+++ b/janus/src/uslibs/const.h
@@ -0,0 +1 @@
+../../../src/libs/const.h
\ No newline at end of file
diff --git a/janus/src/uslibs/frame.c b/janus/src/uslibs/frame.c
new file mode 120000
index 0000000..456f8ec
--- /dev/null
+++ b/janus/src/uslibs/frame.c
@@ -0,0 +1 @@
+../../../src/libs/frame.c
\ No newline at end of file
diff --git a/janus/src/uslibs/frame.h b/janus/src/uslibs/frame.h
new file mode 120000
index 0000000..0e86d5e
--- /dev/null
+++ b/janus/src/uslibs/frame.h
@@ -0,0 +1 @@
+../../../src/libs/frame.h
\ No newline at end of file
diff --git a/janus/src/uslibs/list.h b/janus/src/uslibs/list.h
new file mode 120000
index 0000000..6c7ff6f
--- /dev/null
+++ b/janus/src/uslibs/list.h
@@ -0,0 +1 @@
+../../../src/libs/list.h
\ No newline at end of file
diff --git a/janus/src/uslibs/memsinksh.h b/janus/src/uslibs/memsinksh.h
new file mode 120000
index 0000000..6fe82d0
--- /dev/null
+++ b/janus/src/uslibs/memsinksh.h
@@ -0,0 +1 @@
+../../../src/libs/memsinksh.h
\ No newline at end of file
diff --git a/janus/src/uslibs/threading.h b/janus/src/uslibs/threading.h
new file mode 120000
index 0000000..bde3eff
--- /dev/null
+++ b/janus/src/uslibs/threading.h
@@ -0,0 +1 @@
+../../../src/libs/threading.h
\ No newline at end of file
diff --git a/janus/src/uslibs/tools.h b/janus/src/uslibs/tools.h
new file mode 120000
index 0000000..f2b790a
--- /dev/null
+++ b/janus/src/uslibs/tools.h
@@ -0,0 +1 @@
+../../../src/libs/tools.h
\ No newline at end of file
diff --git a/janus/src/uslibs/xioctl.h b/janus/src/uslibs/xioctl.h
new file mode 120000
index 0000000..4a6a363
--- /dev/null
+++ b/janus/src/uslibs/xioctl.h
@@ -0,0 +1 @@
+../../../src/libs/xioctl.h
\ No newline at end of file
diff --git a/linters/Dockerfile b/linters/Dockerfile
index 96671f4..0286a45 100644
--- a/linters/Dockerfile
+++ b/linters/Dockerfile
@@ -1,9 +1,9 @@
-FROM archlinux/archlinux:base-devel
+FROM archlinux/archlinux:base
 
 RUN mkdir -p /etc/pacman.d/hooks \
 	&& ln -s /dev/null /etc/pacman.d/hooks/30-systemd-tmpfiles.hook
 
-RUN echo "Server = http://mirror.yandex.ru/archlinux/\$repo/os/\$arch" > /etc/pacman.d/mirrorlist
+RUN echo 'Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
 
 RUN pacman -Syu --noconfirm \
 	&& pacman -S --needed --noconfirm \
diff --git a/linters/cppcheck.h b/linters/cppcheck.h
index 4918261..72c1730 100644
--- a/linters/cppcheck.h
+++ b/linters/cppcheck.h
@@ -1,3 +1,6 @@
 #define CHAR_BIT 8
 #define WITH_GPIO
 #define JANUS_PLUGIN_INIT(...) { __VA_ARGS__ }
+#define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1
+#define CLOCK_MONOTONIC_RAW 1
+#define CLOCK_MONOTONIC_FAST 1
diff --git a/linters/pylint.ini b/linters/pylint.ini
index b055a67..7976d4d 100644
--- a/linters/pylint.ini
+++ b/linters/pylint.ini
@@ -15,15 +15,8 @@ disable =
 	locally-disabled,
 	fixme,
 	missing-docstring,
-	no-init,
-	no-self-use,
 	superfluous-parens,
-	abstract-class-not-used,
-	abstract-class-little-used,
 	duplicate-code,
-	bad-continuation,
-	bad-whitespace,
-	star-args,
 	broad-except,
 	redundant-keyword-arg,
 	wrong-import-order,
@@ -39,11 +32,8 @@ msg-template = {symbol} -- {path}:{line}({obj}): {msg}
 max-line-length = 160
 
 [BASIC]
-# List of builtins function names that should not be used, separated by a comma
-bad-functions =
-
 # Good variable names which should always be accepted, separated by a comma
-good-names = _, __, x, y, ws, make-html-h, make-jpeg-h
+good-names = _, __, x, y, ws, make-html-h, make-jpeg-h, make-ico-h
 
 # Regular expression matching correct method names
 method-rgx = [a-z_][a-z0-9_]{2,50}$
diff --git a/linters/tox.ini b/linters/tox.ini
index d1e262c..0bf316c 100644
--- a/linters/tox.ini
+++ b/linters/tox.ini
@@ -9,8 +9,9 @@ changedir = /src
 [testenv:cppcheck]
 whitelist_externals = cppcheck
 commands = cppcheck \
+			-j4 \
 			--force \
-			--std=c11 \
+			--std=c17 \
 			--error-exitcode=1 \
 			--quiet \
 			--enable=warning,unusedFunction,portability,performance,style \
@@ -19,13 +20,13 @@ commands = cppcheck \
 			--inline-suppr \
 			--library=python \
 			--include=linters/cppcheck.h \
-		src python/ustreamer.c janus/rtp.h janus/rtp.c janus/plugin.c
+		src python/*.? janus/*.?
 
 [testenv:flake8]
 whitelist_externals = bash
 commands = bash -c 'flake8 --config=linters/flake8.ini tools/*.py' python/*.py
 deps =
-	flake8
+	flake8==5.0.4
 	flake8-quotes
 
 [testenv:pylint]
diff --git a/man/ustreamer-dump.1 b/man/ustreamer-dump.1
index 4b5ff83..9d0479d 100644
--- a/man/ustreamer-dump.1
+++ b/man/ustreamer-dump.1
@@ -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.4" "January 2021"
+.TH USTREAMER-DUMP 1 "version 5.38" "January 2021"
 
 .SH NAME
 ustreamer-dump \- Dump uStreamer's memory sink to file
@@ -44,6 +44,9 @@ Limit the number of frames. Default: 0 (infinite).
 .TP
 .BR \-i ", "\-\-interval\ \fIsec
 Delay between reading frames (float). Default: 0.
+.TP
+.BR \-k ", " \-\-key\-required
+Request keyframe from the sink. Default: disabled.
 
 .SS "Logging options"
 .TP
diff --git a/man/ustreamer.1 b/man/ustreamer.1
index 24d4b97..5732b6b 100644
--- a/man/ustreamer.1
+++ b/man/ustreamer.1
@@ -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.4" "November 2020"
+.TH USTREAMER 1 "version 5.38" "November 2020"
 
 .SH NAME
 ustreamer \- stream MJPEG video from any V4L2 device to the network
@@ -10,7 +10,7 @@ ustreamer \- stream MJPEG video from any V4L2 device to the network
 .RI [OPTIONS]
 
 .SH DESCRIPTION
-µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the Pi-KVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
+µStreamer (\fBustreamer\fP) is a lightweight and very quick server to stream MJPEG video from any V4L2 device to the network. All new browsers have native support of this video format, as well as most video players such as mplayer, VLC etc. µStreamer is a part of the PiKVM project designed to stream VGA and HDMI screencast hardware data with the highest resolution and FPS possible.
 
 .SH USAGE
 Without arguments, \fBustreamer\fR will try to open \fB/dev/video0\fR with 640x480 resolution and start streaming on \fBhttp://127\.0\.0\.1:8080\fR\. You can override this behavior using parameters \fB\-\-device\fR, \fB\-\-host\fR and \fB\-\-port\fR\. For example, to stream to the world, run: \fBustreamer --device=/dev/video1 --host=0.0.0.0 --port=80\fR
@@ -203,6 +203,9 @@ Default: disabled.
 .BR \-\-allow\-origin\ \fIstr
 Set Access\-Control\-Allow\-Origin header. Default: disabled.
 .TP
+.BR \-\-instance\-id\ \fIstr
+A short string identifier to be displayed in the /state handle. It must satisfy regexp ^[a-zA-Z0-9\./+_-]*$. Default: an empty string.
+.TP
 .BR \-\-server\-timeout\ \fIsec
 Timeout for client connections. Default: 10.
 
diff --git a/pkg/arch/PKGBUILD b/pkg/arch/PKGBUILD
index 9209810..cb4f04a 100644
--- a/pkg/arch/PKGBUILD
+++ b/pkg/arch/PKGBUILD
@@ -3,7 +3,7 @@
 
 
 pkgname=ustreamer
-pkgver=5.4
+pkgver=5.38
 pkgrel=1
 pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
 url="https://github.com/pikvm/ustreamer"
@@ -22,8 +22,8 @@ if [ -e /usr/bin/python3 ]; then
 	makedepends+=(python-setuptools)
 fi
 if [ -e /usr/include/janus/plugins/plugin.h ];then
-	depends+=(janus-gateway-pikvm)
-	makedepends+=(janus-gateway-pikvm)
+	depends+=(janus-gateway alsa-lib opus)
+	makedepends+=(janus-gateway alsa-lib opus)
 	_options="$_options WITH_JANUS=1"
 fi
 
diff --git a/pkg/docker/Dockerfile.alpine b/pkg/docker/Dockerfile.alpine
new file mode 100644
index 0000000..ee92237
--- /dev/null
+++ b/pkg/docker/Dockerfile.alpine
@@ -0,0 +1,34 @@
+FROM alpine:3.16 as build
+RUN apk add --no-cache \
+		alpine-sdk \
+		linux-headers \
+		libjpeg-turbo-dev \
+		libevent-dev \
+		libbsd-dev \
+		libgpiod-dev
+
+WORKDIR /build/ustreamer/
+COPY . .
+RUN make -j5 WITH_GPIO=1
+
+FROM alpine:3.16 as run
+
+RUN apk add --no-cache \
+		libevent \
+		libjpeg-turbo \
+		libevent \
+		libgpiod \
+		libbsd \
+		v4l-utils
+
+WORKDIR /ustreamer
+COPY --from=build /build/ustreamer/src/ustreamer.bin ustreamer
+
+RUN wget https://raw.githubusercontent.com/pikvm/kvmd/master/configs/kvmd/edid/v3-hdmi.hex -O /edid.hex
+COPY pkg/docker/entry.sh /
+
+EXPOSE 8080
+ENTRYPOINT ["/entry.sh"]
+CMD ["--dv-timings", "--format", "UYVY"]
+
+# vim: syntax=dockerfile
diff --git a/pkg/docker/entry.sh b/pkg/docker/entry.sh
new file mode 100755
index 0000000..4ea91af
--- /dev/null
+++ b/pkg/docker/entry.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -e
+
+[ -n "$EDID" ] && {
+	[ -n "$EDID_HEX" ] && echo "$EDID_HEX" > /edid.hex
+	while true; do
+		v4l2-ctl --device=/dev/video0 --set-edid=file=/edid.hex --fix-edid-checksums --info-edid && break
+		echo 'Failed to set EDID. Reetrying...'
+		sleep 1
+	done
+}
+
+./ustreamer --host=0.0.0.0 $@
diff --git a/pkg/openwrt/Makefile b/pkg/openwrt/Makefile
index 4c7d47f..3e604a7 100644
--- a/pkg/openwrt/Makefile
+++ b/pkg/openwrt/Makefile
@@ -6,7 +6,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ustreamer
-PKG_VERSION:=5.4
+PKG_VERSION:=5.38
 PKG_RELEASE:=1
 PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
 
diff --git a/python/setup.py b/python/setup.py
index 38a48cc..694a5b8 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -5,10 +5,19 @@ from setuptools import setup
 
 
 # =====
+def _find_sources(suffix: str) -> list[str]:
+    sources: list[str] = []
+    for (root_path, _, names) in os.walk("src"):
+        for name in names:
+            if name.endswith(suffix):
+                sources.append(os.path.join(root_path, name))
+    return sources
+
+
 if __name__ == "__main__":
     setup(
         name="ustreamer",
-        version="5.4",
+        version="5.38",
         description="uStreamer tools",
         author="Maxim Devaev",
         author_email="mdevaev@gmail.com",
@@ -17,9 +26,10 @@ if __name__ == "__main__":
             Extension(
                 "ustreamer",
                 libraries=["rt", "m", "pthread"],
+                extra_compile_args=["-std=c17", "-D_GNU_SOURCE"],
                 undef_macros=["NDEBUG"],
-                sources=["src/" + name for name in os.listdir("src") if name.endswith(".c")],
-                depends=["src/" + name for name in os.listdir("src") if name.endswith(".h")],
+                sources=_find_sources(".c"),
+                depends=_find_sources(".h"),
             ),
         ],
     )
diff --git a/python/src/frame.c b/python/src/frame.c
deleted file mode 120000
index 3bc454d..0000000
--- a/python/src/frame.c
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/frame.c
\ No newline at end of file
diff --git a/python/src/frame.h b/python/src/frame.h
deleted file mode 120000
index 73ed450..0000000
--- a/python/src/frame.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/frame.h
\ No newline at end of file
diff --git a/python/src/memsinksh.h b/python/src/memsinksh.h
deleted file mode 120000
index 52d4728..0000000
--- a/python/src/memsinksh.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/memsinksh.h
\ No newline at end of file
diff --git a/python/src/tools.h b/python/src/tools.h
deleted file mode 120000
index c2a07b7..0000000
--- a/python/src/tools.h
+++ /dev/null
@@ -1 +0,0 @@
-../../src/libs/tools.h
\ No newline at end of file
diff --git a/python/src/uslibs/frame.c b/python/src/uslibs/frame.c
new file mode 120000
index 0000000..456f8ec
--- /dev/null
+++ b/python/src/uslibs/frame.c
@@ -0,0 +1 @@
+../../../src/libs/frame.c
\ No newline at end of file
diff --git a/python/src/uslibs/frame.h b/python/src/uslibs/frame.h
new file mode 120000
index 0000000..0e86d5e
--- /dev/null
+++ b/python/src/uslibs/frame.h
@@ -0,0 +1 @@
+../../../src/libs/frame.h
\ No newline at end of file
diff --git a/python/src/uslibs/memsinksh.h b/python/src/uslibs/memsinksh.h
new file mode 120000
index 0000000..6fe82d0
--- /dev/null
+++ b/python/src/uslibs/memsinksh.h
@@ -0,0 +1 @@
+../../../src/libs/memsinksh.h
\ No newline at end of file
diff --git a/python/src/uslibs/tools.h b/python/src/uslibs/tools.h
new file mode 120000
index 0000000..f2b790a
--- /dev/null
+++ b/python/src/uslibs/tools.h
@@ -0,0 +1 @@
+../../../src/libs/tools.h
\ No newline at end of file
diff --git a/python/src/ustreamer.c b/python/src/ustreamer.c
index a17129b..f1d27f0 100644
--- a/python/src/ustreamer.c
+++ b/python/src/ustreamer.c
@@ -13,9 +13,9 @@
 
 #include <Python.h>
 
-#include "tools.h"
-#include "frame.h"
-#include "memsinksh.h"
+#include "uslibs/tools.h"
+#include "uslibs/frame.h"
+#include "uslibs/memsinksh.h"
 
 
 typedef struct {
@@ -27,34 +27,34 @@ typedef struct {
 	double	drop_same_frames;
 
 	int					fd;
-	memsink_shared_s	*mem;
+	us_memsink_shared_s	*mem;
 
-	uint64_t	frame_id;
-	long double	frame_ts;
-	frame_s		*frame;
-} MemsinkObject;
+	uint64_t		frame_id;
+	long double		frame_ts;
+	us_frame_s		*frame;
+} _MemsinkObject;
 
 
-#define MEM(_next)		self->mem->_next
-#define FRAME(_next)	self->frame->_next
+#define _MEM(x_next)	self->mem->x_next
+#define _FRAME(x_next)	self->frame->x_next
 
 
-static void MemsinkObject_destroy_internals(MemsinkObject *self) {
+static void _MemsinkObject_destroy_internals(_MemsinkObject *self) {
 	if (self->mem != NULL) {
-		memsink_shared_unmap(self->mem);
+		us_memsink_shared_unmap(self->mem);
 		self->mem = NULL;
 	}
-	if (self->fd > 0) {
+	if (self->fd >= 0) {
 		close(self->fd);
 		self->fd = -1;
 	}
-	if (self->frame) {
-		frame_destroy(self->frame);
+	if (self->frame != NULL) {
+		us_frame_destroy(self->frame);
 		self->frame = NULL;
 	}
 }
 
-static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwargs) {
+static int _MemsinkObject_init(_MemsinkObject *self, PyObject *args, PyObject *kwargs) {
 	self->lock_timeout = 1;
 	self->wait_timeout = 1;
 
@@ -78,14 +78,14 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
 
 #	undef SET_DOUBLE
 
-	self->frame = frame_init();
+	self->frame = us_frame_init();
 
 	if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
 		PyErr_SetFromErrno(PyExc_OSError);
 		goto error;
 	}
 
-	if ((self->mem = memsink_shared_map(self->fd)) == NULL) {
+	if ((self->mem = us_memsink_shared_map(self->fd)) == NULL) {
 		PyErr_SetFromErrno(PyExc_OSError);
 		goto error;
 	}
@@ -93,37 +93,37 @@ static int MemsinkObject_init(MemsinkObject *self, PyObject *args, PyObject *kwa
 	return 0;
 
 	error:
-		MemsinkObject_destroy_internals(self);
+		_MemsinkObject_destroy_internals(self);
 		return -1;
 }
 
-static PyObject *MemsinkObject_repr(MemsinkObject *self) {
+static PyObject *_MemsinkObject_repr(_MemsinkObject *self) {
 	char repr[1024];
 	snprintf(repr, 1023, "<Memsink(%s)>", self->obj);
 	return Py_BuildValue("s", repr);
 }
 
-static void MemsinkObject_dealloc(MemsinkObject *self) {
-	MemsinkObject_destroy_internals(self);
+static void _MemsinkObject_dealloc(_MemsinkObject *self) {
+	_MemsinkObject_destroy_internals(self);
 	PyObject_Del(self);
 }
 
-static PyObject *MemsinkObject_close(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
-	MemsinkObject_destroy_internals(self);
+static PyObject *_MemsinkObject_close(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
+	_MemsinkObject_destroy_internals(self);
 	Py_RETURN_NONE;
 }
 
-static PyObject *MemsinkObject_enter(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
+static PyObject *_MemsinkObject_enter(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
 	Py_INCREF(self);
 	return (PyObject *)self;
 }
 
-static PyObject *MemsinkObject_exit(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
+static PyObject *_MemsinkObject_exit(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
 	return PyObject_CallMethod((PyObject *)self, "close", "");
 }
 
-static int wait_frame(MemsinkObject *self) {
-	long double deadline_ts = get_now_monotonic() + self->wait_timeout;
+static int _wait_frame(_MemsinkObject *self) {
+	const long double deadline_ts = us_get_now_monotonic() + self->wait_timeout;
 
 #	define RETURN_OS_ERROR { \
 			Py_BLOCK_THREADS \
@@ -135,21 +135,21 @@ static int wait_frame(MemsinkObject *self) {
 	do {
 		Py_BEGIN_ALLOW_THREADS
 
-		int retval = flock_timedwait_monotonic(self->fd, self->lock_timeout);
-		now = get_now_monotonic();
+		const int retval = us_flock_timedwait_monotonic(self->fd, self->lock_timeout);
+		now = us_get_now_monotonic();
 
 		if (retval < 0 && errno != EWOULDBLOCK) {
 			RETURN_OS_ERROR;
 
 		} else if (retval == 0) {
-			if (MEM(magic) == MEMSINK_MAGIC && MEM(version) == MEMSINK_VERSION && MEM(id) != self->frame_id) {
+			if (_MEM(magic) == US_MEMSINK_MAGIC && _MEM(version) == US_MEMSINK_VERSION && _MEM(id) != self->frame_id) {
 				if (self->drop_same_frames > 0) {
 					if (
-						FRAME_COMPARE_META_USED_NOTS(self->mem, self->frame)
+						US_FRAME_COMPARE_META_USED_NOTS(self->mem, self->frame)
 						&& (self->frame_ts + self->drop_same_frames > now)
-						&& !memcmp(FRAME(data), MEM(data), MEM(used))
+						&& !memcmp(_FRAME(data), _MEM(data), _MEM(used))
 					) {
-						self->frame_id = MEM(id);
+						self->frame_id = _MEM(id);
 						goto drop;
 					}
 				}
@@ -181,23 +181,32 @@ static int wait_frame(MemsinkObject *self) {
 	return -2;
 }
 
-static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
+static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args, PyObject *kwargs) {
 	if (self->mem == NULL || self->fd <= 0) {
 		PyErr_SetString(PyExc_RuntimeError, "Closed");
 		return NULL;
 	}
 
-	switch (wait_frame(self)) {
+	bool key_required = false;
+	static char *kws[] = {"key_required", NULL};
+	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", kws, &key_required)) {
+		return NULL;
+	}
+
+	switch (_wait_frame(self)) {
 		case 0: break;
 		case -2: Py_RETURN_NONE;
 		default: return NULL;
 	}
 
-	frame_set_data(self->frame, MEM(data), MEM(used));
-	FRAME_COPY_META(self->mem, self->frame);
-	self->frame_id = MEM(id);
-	self->frame_ts = get_now_monotonic();
-	MEM(last_client_ts) = self->frame_ts;
+	us_frame_set_data(self->frame, _MEM(data), _MEM(used));
+	US_FRAME_COPY_META(self->mem, self->frame);
+	self->frame_id = _MEM(id);
+	self->frame_ts = us_get_now_monotonic();
+	_MEM(last_client_ts) = self->frame_ts;
+	if (key_required) {
+		_MEM(key_requested) = true;
+	}
 
 	if (flock(self->fd, LOCK_UN) < 0) {
 		return PyErr_SetFromErrno(PyExc_OSError);
@@ -219,7 +228,7 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
 			} \
 			Py_DECREF(_tmp); \
 		}
-#	define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(FRAME(_key)))
+#	define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(_FRAME(_key)))
 
 	SET_NUMBER(width, Long, Long);
 	SET_NUMBER(height, Long, Long);
@@ -227,10 +236,11 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
 	SET_NUMBER(stride, Long, Long);
 	SET_NUMBER(online, Long, Bool);
 	SET_NUMBER(key, Long, Bool);
+	SET_NUMBER(gop, Long, Long);
 	SET_NUMBER(grab_ts, Double, Float);
 	SET_NUMBER(encode_begin_ts, Double, Float);
 	SET_NUMBER(encode_end_ts, Double, Float);
-	SET_VALUE("data", PyBytes_FromStringAndSize((const char *)FRAME(data), FRAME(used)));
+	SET_VALUE("data", PyBytes_FromStringAndSize((const char *)_FRAME(data), _FRAME(used)));
 
 #	undef SET_NUMBER
 #	undef SET_VALUE
@@ -238,12 +248,12 @@ static PyObject *MemsinkObject_wait_frame(MemsinkObject *self, PyObject *Py_UNUS
 	return dict_frame;
 }
 
-static PyObject *MemsinkObject_is_opened(MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
+static PyObject *_MemsinkObject_is_opened(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
 	return PyBool_FromLong(self->mem != NULL && self->fd > 0);
 }
 
 #define FIELD_GETTER(_field, _from, _to) \
-	static PyObject *MemsinkObject_getter_##_field(MemsinkObject *self, void *Py_UNUSED(closure)) { \
+	static PyObject *_MemsinkObject_getter_##_field(_MemsinkObject *self, void *Py_UNUSED(closure)) { \
 		return Py##_to##_From##_from(self->_field); \
 	}
 
@@ -254,20 +264,20 @@ FIELD_GETTER(drop_same_frames, Double, Float)
 
 #undef FIELD_GETTER
 
-static PyMethodDef MemsinkObject_methods[] = {
+static PyMethodDef _MemsinkObject_methods[] = {
 #	define ADD_METHOD(_name, _method, _flags) \
-		{.ml_name = _name, .ml_meth = (PyCFunction)MemsinkObject_##_method, .ml_flags = (_flags)}
+		{.ml_name = _name, .ml_meth = (PyCFunction)_MemsinkObject_##_method, .ml_flags = (_flags)}
 	ADD_METHOD("close", close, METH_NOARGS),
 	ADD_METHOD("__enter__", enter, METH_NOARGS),
 	ADD_METHOD("__exit__", exit, METH_VARARGS),
-	ADD_METHOD("wait_frame", wait_frame, METH_NOARGS),
+	ADD_METHOD("wait_frame", wait_frame, METH_VARARGS | METH_KEYWORDS),
 	ADD_METHOD("is_opened", is_opened, METH_NOARGS),
 	{},
 #	undef ADD_METHOD
 };
 
-static PyGetSetDef MemsinkObject_getsets[] = {
-#	define ADD_GETTER(_field) {.name = #_field, .get = (getter)MemsinkObject_getter_##_field}
+static PyGetSetDef _MemsinkObject_getsets[] = {
+#	define ADD_GETTER(_field) {.name = #_field, .get = (getter)_MemsinkObject_getter_##_field}
 	ADD_GETTER(obj),
 	ADD_GETTER(lock_timeout),
 	ADD_GETTER(wait_timeout),
@@ -276,43 +286,40 @@ static PyGetSetDef MemsinkObject_getsets[] = {
 #	undef ADD_GETTER
 };
 
-static PyTypeObject MemsinkType = {
+static PyTypeObject _MemsinkType = {
 	PyVarObject_HEAD_INIT(NULL, 0)
 	.tp_name		= "ustreamer.Memsink",
-	.tp_basicsize	= sizeof(MemsinkObject),
+	.tp_basicsize	= sizeof(_MemsinkObject),
 	.tp_flags		= Py_TPFLAGS_DEFAULT,
 	.tp_new			= PyType_GenericNew,
-	.tp_init		= (initproc)MemsinkObject_init,
-	.tp_dealloc		= (destructor)MemsinkObject_dealloc,
-	.tp_repr		= (reprfunc)MemsinkObject_repr,
-	.tp_methods		= MemsinkObject_methods,
-	.tp_getset		= MemsinkObject_getsets,
+	.tp_init		= (initproc)_MemsinkObject_init,
+	.tp_dealloc		= (destructor)_MemsinkObject_dealloc,
+	.tp_repr		= (reprfunc)_MemsinkObject_repr,
+	.tp_methods		= _MemsinkObject_methods,
+	.tp_getset		= _MemsinkObject_getsets,
 };
 
-static PyModuleDef ustreamer_Module = {
+static PyModuleDef _Module = {
 	PyModuleDef_HEAD_INIT,
 	.m_name = "ustreamer",
 	.m_size = -1,
 };
 
 PyMODINIT_FUNC PyInit_ustreamer(void) { // cppcheck-suppress unusedFunction
-	PyObject *module = PyModule_Create(&ustreamer_Module);
+	PyObject *module = PyModule_Create(&_Module);
 	if (module == NULL) {
 		return NULL;
 	}
 
-	if (PyType_Ready(&MemsinkType) < 0) {
+	if (PyType_Ready(&_MemsinkType) < 0) {
 		return NULL;
 	}
 
-	Py_INCREF(&MemsinkType);
+	Py_INCREF(&_MemsinkType);
 
-	if (PyModule_AddObject(module, "Memsink", (PyObject *)&MemsinkType) < 0) {
+	if (PyModule_AddObject(module, "Memsink", (PyObject *)&_MemsinkType) < 0) {
 		return NULL;
 	}
 
 	return module;
 }
-
-#undef FRAME
-#undef MEM
diff --git a/src/Makefile b/src/Makefile
index 9f9e9d1..ac5897a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -10,7 +10,7 @@ LDFLAGS ?=
 _USTR = ustreamer.bin
 _DUMP = ustreamer-dump.bin
 
-_CFLAGS = -MD -c -std=c11 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
+_CFLAGS = -MD -c -std=c17 -Wall -Wextra -D_GNU_SOURCE $(CFLAGS)
 _LDFLAGS = $(LDFLAGS)
 
 _COMMON_LIBS = -lm -ljpeg -pthread -lrt
@@ -23,7 +23,7 @@ _USTR_SRCS = $(shell ls \
 	ustreamer/data/*.c \
 	ustreamer/encoders/cpu/*.c \
 	ustreamer/encoders/hw/*.c \
-	ustreamer/h264/*.c \
+	ustreamer/*.c \
 )
 
 _DUMP_LIBS = $(_COMMON_LIBS)
diff --git a/src/dump/file.c b/src/dump/file.c
index 8213859..9a30094 100644
--- a/src/dump/file.c
+++ b/src/dump/file.c
@@ -23,17 +23,17 @@
 #include "file.h"
 
 
-output_file_s *output_file_init(const char *path, bool json) {
-	output_file_s *output;
-	A_CALLOC(output, 1);
+us_output_file_s *us_output_file_init(const char *path, bool json) {
+	us_output_file_s *output;
+	US_CALLOC(output, 1);
 
 	if (!strcmp(path, "-")) {
-		LOG_INFO("Using output: <stdout>");
+		US_LOG_INFO("Using output: <stdout>");
 		output->fp = stdout;
 	} else {
-		LOG_INFO("Using output: %s", path);
+		US_LOG_INFO("Using output: %s", path);
 		if ((output->fp = fopen(path, "wb")) == NULL) {
-			LOG_PERROR("Can't open output file");
+			US_LOG_PERROR("Can't open output file");
 			goto error;
 		}
 	}
@@ -42,21 +42,21 @@ output_file_s *output_file_init(const char *path, bool json) {
 	return output;
 
 	error:
-		output_file_destroy(output);
+		us_output_file_destroy(output);
 		return NULL;
 }
 
-void output_file_write(void *v_output, const frame_s *frame) {
-	output_file_s *output = (output_file_s *)v_output;
+void us_output_file_write(void *v_output, const us_frame_s *frame) {
+	us_output_file_s *output = (us_output_file_s *)v_output;
 	if (output->json) {
-		base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
+		us_base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
 		fprintf(output->fp,
 			"{\"size\": %zu, \"width\": %u, \"height\": %u,"
-			" \"format\": %u, \"stride\": %u, \"online\": %u,"
+			" \"format\": %u, \"stride\": %u, \"online\": %u, \"key\": %u, \"gop\": %u,"
 			" \"grab_ts\": %.3Lf, \"encode_begin_ts\": %.3Lf, \"encode_end_ts\": %.3Lf,"
 			" \"data\": \"%s\"}\n",
 			frame->used, frame->width, frame->height,
-			frame->format, frame->stride, frame->online,
+			frame->format, frame->stride, frame->online, frame->key, frame->gop,
 			frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts,
 			output->base64_data);
 	} else {
@@ -65,14 +65,12 @@ void output_file_write(void *v_output, const frame_s *frame) {
 	fflush(output->fp);
 }
 
-void output_file_destroy(void *v_output) {
-	output_file_s *output = (output_file_s *)v_output;
-	if (output->base64_data) {
-		free(output->base64_data);
-	}
+void us_output_file_destroy(void *v_output) {
+	us_output_file_s *output = (us_output_file_s *)v_output;
+	US_DELETE(output->base64_data, free);
 	if (output->fp && output->fp != stdout) {
 		if (fclose(output->fp) < 0) {
-			LOG_PERROR("Can't close output file");
+			US_LOG_PERROR("Can't close output file");
 		}
 	}
 	free(output);
diff --git a/src/dump/file.h b/src/dump/file.h
index 55975e1..a90f5f4 100644
--- a/src/dump/file.h
+++ b/src/dump/file.h
@@ -41,9 +41,9 @@ typedef struct {
 	FILE		*fp;
 	char		*base64_data;
 	size_t		base64_allocated;
-} output_file_s;
+} us_output_file_s;
 
 
-output_file_s *output_file_init(const char *path, bool json);
-void output_file_write(void *v_output, const frame_s *frame);
-void output_file_destroy(void *v_output);
+us_output_file_s *us_output_file_init(const char *path, bool json);
+void us_output_file_write(void *v_output, const us_frame_s *frame);
+void us_output_file_destroy(void *v_output);
diff --git a/src/dump/main.c b/src/dump/main.c
index b71b0ca..4788d97 100644
--- a/src/dump/main.c
+++ b/src/dump/main.c
@@ -31,7 +31,7 @@
 #include <errno.h>
 #include <assert.h>
 
-#include "../libs/config.h"
+#include "../libs/const.h"
 #include "../libs/tools.h"
 #include "../libs/logging.h"
 #include "../libs/frame.h"
@@ -48,6 +48,7 @@ enum _OPT_VALUES {
 	_O_OUTPUT_JSON = 'j',
 	_O_COUNT = 'c',
 	_O_INTERVAL = 'i',
+	_O_KEY_REQUIRED = 'k',
 
 	_O_HELP = 'h',
 	_O_VERSION = 'v',
@@ -67,6 +68,7 @@ static const struct option _LONG_OPTS[] = {
 	{"output-json",			no_argument,		NULL,	_O_OUTPUT_JSON},
 	{"count",				required_argument,	NULL,	_O_COUNT},
 	{"interval",			required_argument,	NULL,	_O_INTERVAL},
+	{"key-required",		no_argument,		NULL,	_O_KEY_REQUIRED},
 
 	{"log-level",			required_argument,	NULL,	_O_LOG_LEVEL},
 	{"perf",				no_argument,		NULL,	_O_PERF},
@@ -82,12 +84,12 @@ static const struct option _LONG_OPTS[] = {
 };
 
 
-volatile bool global_stop = false;
+volatile bool _g_stop = false;
 
 
 typedef struct {
 	void *v_output;
-	void (*write)(void *v_output, const frame_s *frame);
+	void (*write)(void *v_output, const us_frame_s *frame);
 	void (*destroy)(void *v_output);
 } _output_context_s;
 
@@ -98,14 +100,15 @@ static void _install_signal_handlers(void);
 static int _dump_sink(
 	const char *sink_name, unsigned sink_timeout,
 	long long count, long double interval,
+	bool key_required,
 	_output_context_s *ctx);
 
 static void _help(FILE *fp);
 
 
 int main(int argc, char *argv[]) {
-	LOGGING_INIT;
-	A_THREAD_RENAME("main");
+	US_LOGGING_INIT;
+	US_THREAD_RENAME("main");
 
 	char *sink_name = NULL;
 	unsigned sink_timeout = 1;
@@ -113,6 +116,7 @@ int main(int argc, char *argv[]) {
 	bool output_json = false;
 	long long count = 0;
 	long double interval = 0;
+	bool key_required = false;
 
 #	define OPT_SET(_dest, _value) { \
 			_dest = _value; \
@@ -140,7 +144,7 @@ int main(int argc, char *argv[]) {
 		}
 
 	char short_opts[128];
-	build_short_options(_LONG_OPTS, short_opts, 128);
+	us_build_short_options(_LONG_OPTS, short_opts, 128);
 
 	for (int ch; (ch = getopt_long(argc, argv, short_opts, _LONG_OPTS, NULL)) >= 0;) {
 		switch (ch) {
@@ -150,16 +154,17 @@ int main(int argc, char *argv[]) {
 			case _O_OUTPUT_JSON:	OPT_SET(output_json, true);
 			case _O_COUNT:			OPT_NUMBER("--count", count, 0, LLONG_MAX, 0);
 			case _O_INTERVAL:		OPT_LDOUBLE("--interval", interval, 0, 60);
+			case _O_KEY_REQUIRED:	OPT_SET(key_required, true);
 
-			case _O_LOG_LEVEL:			OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
-			case _O_PERF:				OPT_SET(us_log_level, LOG_LEVEL_PERF);
-			case _O_VERBOSE:			OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
-			case _O_DEBUG:				OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
-			case _O_FORCE_LOG_COLORS:	OPT_SET(us_log_colored, true);
-			case _O_NO_LOG_COLORS:		OPT_SET(us_log_colored, false);
+			case _O_LOG_LEVEL:			OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
+			case _O_PERF:				OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
+			case _O_VERBOSE:			OPT_SET(us_g_log_level, US_LOG_LEVEL_VERBOSE);
+			case _O_DEBUG:				OPT_SET(us_g_log_level, US_LOG_LEVEL_DEBUG);
+			case _O_FORCE_LOG_COLORS:	OPT_SET(us_g_log_colored, true);
+			case _O_NO_LOG_COLORS:		OPT_SET(us_g_log_colored, false);
 
 			case _O_HELP:		_help(stdout); return 0;
-			case _O_VERSION:	puts(VERSION); return 0;
+			case _O_VERSION:	puts(US_VERSION); return 0;
 
 			case 0:		break;
 			default:	return 1;
@@ -178,15 +183,15 @@ int main(int argc, char *argv[]) {
 	_output_context_s ctx = {0};
 
 	if (output_path && output_path[0] != '\0') {
-		if ((ctx.v_output = (void *)output_file_init(output_path, output_json)) == NULL) {
+		if ((ctx.v_output = (void *)us_output_file_init(output_path, output_json)) == NULL) {
 			return 1;
 		}
-		ctx.write = output_file_write;
-		ctx.destroy = output_file_destroy;
+		ctx.write = us_output_file_write;
+		ctx.destroy = us_output_file_destroy;
 	}
 
 	_install_signal_handlers();
-	int retval = abs(_dump_sink(sink_name, sink_timeout, count, interval, &ctx));
+	const int retval = abs(_dump_sink(sink_name, sink_timeout, count, interval, key_required, &ctx));
 	if (ctx.v_output && ctx.destroy) {
 		ctx.destroy(ctx.v_output);
 	}
@@ -195,13 +200,10 @@ int main(int argc, char *argv[]) {
 
 
 static void _signal_handler(int signum) {
-	switch (signum) {
-		case SIGTERM:	LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
-		case SIGINT:	LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
-		case SIGPIPE:	LOG_INFO_NOLOCK("===== Stopping by SIGPIPE ====="); break;
-		default:		LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
-	}
-	global_stop = true;
+	char *const name = us_signum_to_string(signum);
+	US_LOG_INFO_NOLOCK("===== Stopping by %s =====", name);
+	free(name);
+	_g_stop = true;
 }
 
 static void _install_signal_handlers(void) {
@@ -213,31 +215,32 @@ static void _install_signal_handlers(void) {
 	assert(!sigaddset(&sig_act.sa_mask, SIGTERM));
 	assert(!sigaddset(&sig_act.sa_mask, SIGPIPE));
 
-	LOG_DEBUG("Installing SIGINT handler ...");
+	US_LOG_DEBUG("Installing SIGINT handler ...");
 	assert(!sigaction(SIGINT, &sig_act, NULL));
 
-	LOG_DEBUG("Installing SIGTERM handler ...");
+	US_LOG_DEBUG("Installing SIGTERM handler ...");
 	assert(!sigaction(SIGTERM, &sig_act, NULL));
 
-	LOG_DEBUG("Installing SIGTERM handler ...");
+	US_LOG_DEBUG("Installing SIGTERM handler ...");
 	assert(!sigaction(SIGPIPE, &sig_act, NULL));
 }
 
 static int _dump_sink(
 	const char *sink_name, unsigned sink_timeout,
 	long long count, long double interval,
+	bool key_required,
 	_output_context_s *ctx) {
 
 	if (count == 0) {
 		count = -1;
 	}
 
-	useconds_t interval_us = interval * 1000000;
+	const useconds_t interval_us = interval * 1000000;
 
-	frame_s *frame = frame_init();
-	memsink_s *sink = NULL;
+	us_frame_s *frame = us_frame_init();
+	us_memsink_s *sink = NULL;
 
-	if ((sink = memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
+	if ((sink = us_memsink_init("input", sink_name, false, 0, false, 0, sink_timeout)) == NULL) {
 		goto error;
 	}
 
@@ -247,32 +250,36 @@ static int _dump_sink(
 
 	long double last_ts = 0;
 
-	while (!global_stop) {
-		int error = memsink_client_get(sink, frame);
+	while (!_g_stop) {
+		bool key_requested;
+		const int error = us_memsink_client_get(sink, frame, &key_requested, key_required);
 		if (error == 0) {
-			const long double now = get_now_monotonic();
-			const long long now_second = floor_ms(now);
+			key_required = false;
+
+			const long double now = us_get_now_monotonic();
+			const long long now_second = us_floor_ms(now);
 
 			char fourcc_str[8];
-			LOG_VERBOSE("Frame: size=%zu, res=%ux%u, fourcc=%s, stride=%u, online=%d, key=%d, latency=%.3Lf, diff=%.3Lf",
-				frame->used, frame->width, frame->height,
-				fourcc_to_string(frame->format, fourcc_str, 8),
-				frame->stride, frame->online, frame->key,
-				now - frame->grab_ts, (last_ts ? now - last_ts : 0));
+			US_LOG_VERBOSE("Frame: %s - %ux%u -- online=%d, key=%d, kr=%d, gop=%u, latency=%.3Lf, backlog=%.3Lf, size=%zu",
+				us_fourcc_to_string(frame->format, fourcc_str, 8),
+				frame->width, frame->height,
+				frame->online, frame->key, key_requested, frame->gop,
+				now - frame->grab_ts, (last_ts ? now - last_ts : 0),
+				frame->used);
 			last_ts = now;
 
-			LOG_DEBUG("       grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
-				frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
+			US_LOG_DEBUG("       stride=%u, grab_ts=%.3Lf, encode_begin_ts=%.3Lf, encode_end_ts=%.3Lf",
+				frame->stride, frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts);
 
 			if (now_second != fps_second) {
 				fps = fps_accum;
 				fps_accum = 0;
 				fps_second = now_second;
-				LOG_PERF_FPS("A new second has come; captured_fps=%u", fps);
+				US_LOG_PERF_FPS("A new second has come; captured_fps=%u", fps);
 			}
 			fps_accum += 1;
 
-			if (ctx->v_output) {
+			if (ctx->v_output != NULL) {
 				ctx->write(ctx->v_output, frame);
 			}
 
@@ -300,12 +307,10 @@ static int _dump_sink(
 		retval = -1;
 
 	ok:
-		if (sink) {
-			memsink_destroy(sink);
-		}
-		frame_destroy(frame);
+		US_DELETE(sink, us_memsink_destroy);
+		us_frame_destroy(frame);
 
-		LOG_INFO("Bye-bye");
+		US_LOG_INFO("Bye-bye");
 		return retval;
 }
 
@@ -313,7 +318,7 @@ static void _help(FILE *fp) {
 #	define SAY(_msg, ...) fprintf(fp, _msg "\n", ##__VA_ARGS__)
 	SAY("\nuStreamer-dump - Dump uStreamer's memory sink to file");
 	SAY("═════════════════════════════════════════════════════");
-	SAY("Version: %s; license: GPLv3", VERSION);
+	SAY("Version: %s; license: GPLv3", US_VERSION);
 	SAY("Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com>\n");
 	SAY("Example:");
 	SAY("════════");
@@ -327,12 +332,13 @@ static void _help(FILE *fp) {
 	SAY("    -j|--output-json  ──────── Format output as JSON. Required option --output. Default: disabled.\n");
 	SAY("    -c|--count  <N>  ───────── Limit the number of frames. Default: 0 (infinite).\n");
 	SAY("    -i|--interval <sec>  ───── Delay between reading frames (float). Default: 0.\n");
+	SAY("    -k|--key-required  ─────── Request keyframe from the sink. Default: disabled.\n");
 	SAY("Logging options:");
 	SAY("════════════════");
 	SAY("    --log-level <N>  ──── Verbosity level of messages from 0 (info) to 3 (debug).");
 	SAY("                          Enabling debugging messages can slow down the program.");
 	SAY("                          Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
-	SAY("                          Default: %d.\n", us_log_level);
+	SAY("                          Default: %d.\n", us_g_log_level);
 	SAY("    --perf  ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
 	SAY("    --verbose  ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
 	SAY("    --debug  ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
diff --git a/src/libs/array.h b/src/libs/array.h
new file mode 100644
index 0000000..4f10a65
--- /dev/null
+++ b/src/libs/array.h
@@ -0,0 +1,37 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <assert.h>
+
+
+#define US_ARRAY_LEN(x_array) (sizeof(x_array) / sizeof((x_array)[0]))
+
+#define US_ARRAY_ITERATE(x_array, x_start, x_item_ptr, ...) { \
+		const int m_len = US_ARRAY_LEN(x_array); \
+		assert(x_start <= m_len); \
+		for (int m_index = x_start; m_index < m_len; ++m_index) { \
+			__typeof__((x_array)[0]) *const x_item_ptr = &x_array[m_index]; \
+			__VA_ARGS__ \
+		} \
+	}
diff --git a/src/libs/base64.c b/src/libs/base64.c
index 928d11b..22ad42c 100644
--- a/src/libs/base64.c
+++ b/src/libs/base64.c
@@ -37,11 +37,11 @@ static const char _ENCODING_TABLE[] = {
 static const unsigned _MOD_TABLE[] = {0, 2, 1};
 
 
-void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated) {
+void us_base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated) {
 	const size_t encoded_size = 4 * ((size + 2) / 3) + 1; // +1 for '\0'
 
 	if (*encoded == NULL || (allocated && *allocated < encoded_size)) {
-		A_REALLOC(*encoded, encoded_size);
+		US_REALLOC(*encoded, encoded_size);
 		if (allocated) {
 			*allocated = encoded_size;
 		}
@@ -54,7 +54,7 @@ void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *all
 		OCTET(octet_c);
 #		undef OCTET
 
-		unsigned triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
+		const unsigned triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
 
 #		define ENCODE(_offset) (*encoded)[encoded_index++] = _ENCODING_TABLE[(triple >> _offset * 6) & 0x3F]
 		ENCODE(3);
diff --git a/src/libs/base64.h b/src/libs/base64.h
index 99cdfdd..f9f076a 100644
--- a/src/libs/base64.h
+++ b/src/libs/base64.h
@@ -31,4 +31,4 @@
 #include "tools.h"
 
 
-void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated);
+void us_base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated);
diff --git a/src/libs/const.h b/src/libs/const.h
new file mode 100644
index 0000000..4ae9c34
--- /dev/null
+++ b/src/libs/const.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#define US_VERSION_MAJOR 5
+#define US_VERSION_MINOR 38
+
+#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
+#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
+#define US_VERSION US_MAKE_VERSION1(US_VERSION_MAJOR, US_VERSION_MINOR)
+
+#define US_VERSION_U ((unsigned)(US_VERSION_MAJOR * 1000 + US_VERSION_MINOR))
diff --git a/src/libs/frame.c b/src/libs/frame.c
index e4076d1..a5c58e2 100644
--- a/src/libs/frame.c
+++ b/src/libs/frame.c
@@ -23,55 +23,53 @@
 #include "frame.h"
 
 
-frame_s *frame_init(void) {
-	frame_s *frame;
-	A_CALLOC(frame, 1);
-	frame_realloc_data(frame, 512 * 1024);
+us_frame_s *us_frame_init(void) {
+	us_frame_s *frame;
+	US_CALLOC(frame, 1);
+	us_frame_realloc_data(frame, 512 * 1024);
 	frame->dma_fd = -1;
 	return frame;
 }
 
-void frame_destroy(frame_s *frame) {
-	if (frame->data) {
-		free(frame->data);
-	}
+void us_frame_destroy(us_frame_s *frame) {
+	US_DELETE(frame->data, free);
 	free(frame);
 }
 
-void frame_realloc_data(frame_s *frame, size_t size) {
+void us_frame_realloc_data(us_frame_s *frame, size_t size) {
 	if (frame->allocated < size) {
-		A_REALLOC(frame->data, size);
+		US_REALLOC(frame->data, size);
 		frame->allocated = size;
 	}
 }
 
-void frame_set_data(frame_s *frame, const uint8_t *data, size_t size) {
-	frame_realloc_data(frame, size);
+void us_frame_set_data(us_frame_s *frame, const uint8_t *data, size_t size) {
+	us_frame_realloc_data(frame, size);
 	memcpy(frame->data, data, size);
 	frame->used = size;
 }
 
-void frame_append_data(frame_s *frame, const uint8_t *data, size_t size) {
-	size_t new_used = frame->used + size;
-	frame_realloc_data(frame, new_used);
+void us_frame_append_data(us_frame_s *frame, const uint8_t *data, size_t size) {
+	const size_t new_used = frame->used + size;
+	us_frame_realloc_data(frame, new_used);
 	memcpy(frame->data + frame->used, data, size);
 	frame->used = new_used;
 }
 
-void frame_copy(const frame_s *src, frame_s *dest) {
-	frame_set_data(dest, src->data, src->used);
-	FRAME_COPY_META(src, dest);
+void us_frame_copy(const us_frame_s *src, us_frame_s *dest) {
+	us_frame_set_data(dest, src->data, src->used);
+	US_FRAME_COPY_META(src, dest);
 }
 
-bool frame_compare(const frame_s *a, const frame_s *b) {
+bool us_frame_compare(const us_frame_s *a, const us_frame_s *b) {
 	return (
 		a->allocated && b->allocated
-		&& FRAME_COMPARE_META_USED_NOTS(a, b)
+		&& US_FRAME_COMPARE_META_USED_NOTS(a, b)
 		&& !memcmp(a->data, b->data, b->used)
 	);
 }
 
-unsigned frame_get_padding(const frame_s *frame) {
+unsigned us_frame_get_padding(const us_frame_s *frame) {
 	unsigned bytes_per_pixel = 0;
 	switch (frame->format) {
 		case V4L2_PIX_FMT_YUYV:
@@ -89,7 +87,7 @@ unsigned frame_get_padding(const frame_s *frame) {
 	return 0;
 }
 
-const char *fourcc_to_string(unsigned format, char *buf, size_t size) {
+const char *us_fourcc_to_string(unsigned format, char *buf, size_t size) {
 	assert(size >= 8);
 	buf[0] = format & 0x7F;
 	buf[1] = (format >> 8) & 0x7F;
diff --git a/src/libs/frame.h b/src/libs/frame.h
index a9d444c..952dd55 100644
--- a/src/libs/frame.h
+++ b/src/libs/frame.h
@@ -35,84 +35,87 @@
 
 
 typedef struct {
-	uint8_t	*data;
-	size_t	used;
-	size_t	allocated;
-	int		dma_fd;
-
-	unsigned width;
-	unsigned height;
-	unsigned format;
-	unsigned stride;
+	uint8_t		*data;
+	size_t		used;
+	size_t		allocated;
+	int			dma_fd;
+
+	unsigned	width;
+	unsigned	height;
+	unsigned	format;
+	unsigned	stride;
 	// 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;
+	bool		online;
+	bool		key;
+	unsigned	gop;
 
 	long double	grab_ts;
 	long double	encode_begin_ts;
 	long double	encode_end_ts;
-} frame_s;
-
-
-#define FRAME_COPY_META(_src, _dest) { \
-		_dest->width = _src->width; \
-		_dest->height = _src->height; \
-		_dest->format = _src->format; \
-		_dest->stride = _src->stride; \
-		_dest->online = _src->online; \
-		_dest->key = _src->key; \
-		_dest->grab_ts = _src->grab_ts; \
-		_dest->encode_begin_ts = _src->encode_begin_ts; \
-		_dest->encode_end_ts = _src->encode_end_ts; \
+} us_frame_s;
+
+
+#define US_FRAME_COPY_META(x_src, x_dest) { \
+		x_dest->width = x_src->width; \
+		x_dest->height = x_src->height; \
+		x_dest->format = x_src->format; \
+		x_dest->stride = x_src->stride; \
+		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 frame_copy_meta(const frame_s *src, frame_s *dest) {
-	FRAME_COPY_META(src, dest);
+static inline void us_frame_copy_meta(const us_frame_s *src, us_frame_s *dest) {
+	US_FRAME_COPY_META(src, dest);
 }
 
-#define FRAME_COMPARE_META_USED_NOTS(_a, _b) ( \
-		_a->used == _b->used \
-		&& _a->width == _b->width \
-		&& _a->height == _b->height \
-		&& _a->format == _b->format \
-		&& _a->stride == _b->stride \
-		&& _a->online == _b->online \
-		&& _a->key == _b->key \
+#define US_FRAME_COMPARE_META_USED_NOTS(x_a, x_b) ( \
+		x_a->used == x_b->used \
+		&& x_a->width == x_b->width \
+		&& x_a->height == x_b->height \
+		&& x_a->format == x_b->format \
+		&& x_a->stride == x_b->stride \
+		&& x_a->online == x_b->online \
+		&& x_a->key == x_b->key \
+		&& x_a->gop == x_b->gop \
 	)
 
 
-static inline void frame_encoding_begin(const frame_s *src, frame_s *dest, unsigned format) {
+static inline void us_frame_encoding_begin(const us_frame_s *src, us_frame_s *dest, unsigned format) {
 	assert(src->used > 0);
-	frame_copy_meta(src, dest);
-	dest->encode_begin_ts = get_now_monotonic();
+	us_frame_copy_meta(src, dest);
+	dest->encode_begin_ts = us_get_now_monotonic();
 	dest->format = format;
 	dest->stride = 0;
 	dest->used = 0;
 }
 
-static inline void frame_encoding_end(frame_s *dest) {
+static inline void us_frame_encoding_end(us_frame_s *dest) {
 	assert(dest->used > 0);
-	dest->encode_end_ts = get_now_monotonic();
+	dest->encode_end_ts = us_get_now_monotonic();
 }
 
 
-frame_s *frame_init(void);
-void frame_destroy(frame_s *frame);
+us_frame_s *us_frame_init(void);
+void us_frame_destroy(us_frame_s *frame);
 
-void frame_realloc_data(frame_s *frame, size_t size);
-void frame_set_data(frame_s *frame, const uint8_t *data, size_t size);
-void frame_append_data(frame_s *frame, const uint8_t *data, size_t size);
+void us_frame_realloc_data(us_frame_s *frame, size_t size);
+void us_frame_set_data(us_frame_s *frame, const uint8_t *data, size_t size);
+void us_frame_append_data(us_frame_s *frame, const uint8_t *data, size_t size);
 
-void frame_copy(const frame_s *src, frame_s *dest);
-bool frame_compare(const frame_s *a, const frame_s *b);
+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);
 
-unsigned frame_get_padding(const frame_s *frame);
+unsigned us_frame_get_padding(const us_frame_s *frame);
 
-const char *fourcc_to_string(unsigned format, char *buf, size_t size);
+const char *us_fourcc_to_string(unsigned format, char *buf, size_t size);
 
-static inline bool is_jpeg(unsigned format) {
+static inline bool us_is_jpeg(unsigned format) {
 	return (format == V4L2_PIX_FMT_JPEG || format == V4L2_PIX_FMT_MJPEG);
 }
diff --git a/src/libs/list.h b/src/libs/list.h
index 8174d67..6e6556c 100644
--- a/src/libs/list.h
+++ b/src/libs/list.h
@@ -25,47 +25,47 @@
 #include <assert.h>
 
 
-#define LIST_STRUCT(...) \
+#define US_LIST_STRUCT(...) \
 	__VA_ARGS__ *prev; \
 	__VA_ARGS__ *next;
 
-#define LIST_ITERATE(_first, _item, ...) { \
-		for (__typeof__(_first) _item = _first; _item;) { \
-			__typeof__(_first) _next = _item->next; \
+#define US_LIST_ITERATE(x_first, x_item, ...) { \
+		for (__typeof__(x_first) x_item = x_first; x_item;) { \
+			__typeof__(x_first) m_next = x_item->next; \
 			__VA_ARGS__ \
-			_item = _next; \
+			x_item = m_next; \
 		} \
 	}
 
-#define LIST_APPEND(_first, _item) { \
-		if (_first == NULL) { \
-			_first = _item; \
+#define US_LIST_APPEND(x_first, x_item) { \
+		if (x_first == NULL) { \
+			x_first = x_item; \
 		} else { \
-			__typeof__(_first) _last = _first; \
-			for (; _last->next; _last = _last->next); \
-			_item->prev = _last; \
-			_last->next = _item; \
+			__typeof__(x_first) m_last = x_first; \
+			for (; m_last->next; m_last = m_last->next); \
+			x_item->prev = m_last; \
+			m_last->next = x_item; \
 		} \
 	}
 
-#define LIST_APPEND_C(_first, _item, _count) { \
-		LIST_APPEND(_first, _item); \
-		++(_count); \
+#define US_LIST_APPEND_C(x_first, x_item, x_count) { \
+		US_LIST_APPEND(x_first, x_item); \
+		++(x_count); \
 	}
 
-#define LIST_REMOVE(_first, _item) { \
-		if (_item->prev == NULL) { \
-			_first = _item->next; \
+#define US_LIST_REMOVE(x_first, x_item) { \
+		if (x_item->prev == NULL) { \
+			x_first = x_item->next; \
 		} else { \
-			_item->prev->next = _item->next; \
+			x_item->prev->next = x_item->next; \
 		} \
-		if (_item->next != NULL) { \
-			_item->next->prev = _item->prev; \
+		if (x_item->next != NULL) { \
+			x_item->next->prev = x_item->prev; \
 		} \
 	}
 
-#define LIST_REMOVE_C(_first, _item, _count) { \
-		LIST_REMOVE(_first, _item); \
-		assert((_count) >= 1); \
-		--(_count); \
+#define US_LIST_REMOVE_C(x_first, x_item, x_count) { \
+		US_LIST_REMOVE(x_first, x_item); \
+		assert((x_count) >= 1); \
+		--(x_count); \
 	}
diff --git a/src/libs/logging.c b/src/libs/logging.c
index b3c013b..9250625 100644
--- a/src/libs/logging.c
+++ b/src/libs/logging.c
@@ -23,8 +23,8 @@
 #include "logging.h"
 
 
-enum log_level_t us_log_level;
+enum us_log_level_t us_g_log_level;
 
-bool us_log_colored;
+bool us_g_log_colored;
 
-pthread_mutex_t us_log_mutex;
+pthread_mutex_t us_g_log_mutex;
diff --git a/src/libs/logging.h b/src/libs/logging.h
index 253bc42..de0cf54 100644
--- a/src/libs/logging.h
+++ b/src/libs/logging.h
@@ -37,126 +37,126 @@
 #include "threading.h"
 
 
-enum log_level_t {
-    LOG_LEVEL_INFO,
-    LOG_LEVEL_PERF,
-    LOG_LEVEL_VERBOSE,
-    LOG_LEVEL_DEBUG,
+enum us_log_level_t {
+    US_LOG_LEVEL_INFO,
+    US_LOG_LEVEL_PERF,
+    US_LOG_LEVEL_VERBOSE,
+    US_LOG_LEVEL_DEBUG,
 };
 
 
-extern enum log_level_t us_log_level;
+extern enum us_log_level_t us_g_log_level;
 
-extern bool us_log_colored;
+extern bool us_g_log_colored;
 
-extern pthread_mutex_t us_log_mutex;
+extern pthread_mutex_t us_g_log_mutex;
 
 
-#define LOGGING_INIT { \
-		us_log_level = LOG_LEVEL_INFO; \
-		us_log_colored = isatty(2); \
-		A_MUTEX_INIT(&us_log_mutex); \
+#define US_LOGGING_INIT { \
+		us_g_log_level = US_LOG_LEVEL_INFO; \
+		us_g_log_colored = isatty(2); \
+		US_MUTEX_INIT(us_g_log_mutex); \
 	}
 
-#define LOGGING_DESTROY A_MUTEX_DESTROY(&us_log_mutex)
+#define US_LOGGING_DESTROY	US_MUTEX_DESTROY(us_g_log_mutex)
 
-#define LOGGING_LOCK	A_MUTEX_LOCK(&us_log_mutex)
-#define LOGGING_UNLOCK	A_MUTEX_UNLOCK(&us_log_mutex)
+#define US_LOGGING_LOCK		US_MUTEX_LOCK(us_g_log_mutex)
+#define US_LOGGING_UNLOCK	US_MUTEX_UNLOCK(us_g_log_mutex)
 
 
-#define COLOR_GRAY		"\x1b[30;1m"
-#define COLOR_RED		"\x1b[31;1m"
-#define COLOR_GREEN		"\x1b[32;1m"
-#define COLOR_YELLOW	"\x1b[33;1m"
-#define COLOR_BLUE		"\x1b[34;1m"
-#define COLOR_CYAN		"\x1b[36;1m"
-#define COLOR_RESET		"\x1b[0m"
+#define US_COLOR_GRAY		"\x1b[30;1m"
+#define US_COLOR_RED		"\x1b[31;1m"
+#define US_COLOR_GREEN		"\x1b[32;1m"
+#define US_COLOR_YELLOW		"\x1b[33;1m"
+#define US_COLOR_BLUE		"\x1b[34;1m"
+#define US_COLOR_CYAN		"\x1b[36;1m"
+#define US_COLOR_RESET		"\x1b[0m"
 
 
-#define SEP_INFO(_ch) { \
-		LOGGING_LOCK; \
-		for (int _i = 0; _i < 80; ++_i) { \
-			fputc(_ch, stderr); \
+#define US_SEP_INFO(x_ch) { \
+		US_LOGGING_LOCK; \
+		for (int m_count = 0; m_count < 80; ++m_count) { \
+			fputc((x_ch), stderr); \
 		} \
 		fputc('\n', stderr); \
 		fflush(stderr); \
-		LOGGING_UNLOCK; \
+		US_LOGGING_UNLOCK; \
 	}
 
-#define SEP_DEBUG(_ch) { \
-		if (us_log_level >= LOG_LEVEL_DEBUG) { \
-			SEP_INFO(_ch); \
+#define US_SEP_DEBUG(x_ch) { \
+		if (us_g_log_level >= US_LOG_LEVEL_DEBUG) { \
+			US_SEP_INFO(x_ch); \
 		} \
 	}
 
 
-#define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \
-		char _tname_buf[MAX_THREAD_NAME] = {0}; \
-		thread_get_name(_tname_buf); \
-		if (us_log_colored) { \
-			fprintf(stderr, COLOR_GRAY "-- " _label_color _label COLOR_GRAY \
-				" [%.03Lf %9s]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \
-				get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
+#define US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ...) { \
+		char m_tname_buf[US_MAX_THREAD_NAME] = {0}; \
+		us_thread_get_name(m_tname_buf); \
+		if (us_g_log_colored) { \
+			fprintf(stderr, US_COLOR_GRAY "-- " x_label_color x_label US_COLOR_GRAY \
+				" [%.03Lf %9s]" " -- " US_COLOR_RESET x_msg_color x_msg US_COLOR_RESET, \
+				us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
 		} else { \
-			fprintf(stderr, "-- " _label " [%.03Lf %9s] -- " _msg, \
-				get_now_monotonic(), _tname_buf, ##__VA_ARGS__); \
+			fprintf(stderr, "-- " x_label " [%.03Lf %9s] -- " x_msg, \
+				us_get_now_monotonic(), m_tname_buf, ##__VA_ARGS__); \
 		} \
 		fputc('\n', stderr); \
 		fflush(stderr); \
 	}
 
-#define LOG_PRINTF(_label_color, _label, _msg_color, _msg, ...) { \
-		LOGGING_LOCK; \
-		LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ##__VA_ARGS__); \
-		LOGGING_UNLOCK; \
+#define US_LOG_PRINTF(x_label_color, x_label, x_msg_color, x_msg, ...) { \
+		US_LOGGING_LOCK; \
+		US_LOG_PRINTF_NOLOCK(x_label_color, x_label, x_msg_color, x_msg, ##__VA_ARGS__); \
+		US_LOGGING_UNLOCK; \
 	}
 
-#define LOG_ERROR(_msg, ...) { \
-		LOG_PRINTF(COLOR_RED, "ERROR", COLOR_RED, _msg, ##__VA_ARGS__); \
+#define US_LOG_ERROR(x_msg, ...) { \
+		US_LOG_PRINTF(US_COLOR_RED, "ERROR", US_COLOR_RED, x_msg, ##__VA_ARGS__); \
 	}
 
-#define LOG_PERROR(_msg, ...) { \
-		char _perror_buf[1024] = {0}; \
-		char *_perror_ptr = errno_to_string(errno, _perror_buf, 1024); \
-		LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _perror_ptr); \
+#define US_LOG_PERROR(x_msg, ...) { \
+		char *const m_perror_str = us_errno_to_string(errno); \
+		US_LOG_ERROR(x_msg ": %s", ##__VA_ARGS__, m_perror_str); \
+		free(m_perror_str); \
 	}
 
-#define LOG_INFO(_msg, ...) { \
-		LOG_PRINTF(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
+#define US_LOG_INFO(x_msg, ...) { \
+		US_LOG_PRINTF(US_COLOR_GREEN, "INFO ", "", x_msg, ##__VA_ARGS__); \
 	}
 
-#define LOG_INFO_NOLOCK(_msg, ...) { \
-		LOG_PRINTF_NOLOCK(COLOR_GREEN, "INFO ", "", _msg, ##__VA_ARGS__); \
+#define US_LOG_INFO_NOLOCK(x_msg, ...) { \
+		US_LOG_PRINTF_NOLOCK(US_COLOR_GREEN, "INFO ", "", x_msg, ##__VA_ARGS__); \
 	}
 
-#define LOG_PERF(_msg, ...) { \
-		if (us_log_level >= LOG_LEVEL_PERF) { \
-			LOG_PRINTF(COLOR_CYAN, "PERF ", COLOR_CYAN, _msg, ##__VA_ARGS__); \
+#define US_LOG_PERF(x_msg, ...) { \
+		if (us_g_log_level >= US_LOG_LEVEL_PERF) { \
+			US_LOG_PRINTF(US_COLOR_CYAN, "PERF ", US_COLOR_CYAN, x_msg, ##__VA_ARGS__); \
 		} \
 	}
 
-#define LOG_PERF_FPS(_msg, ...) { \
-		if (us_log_level >= LOG_LEVEL_PERF) { \
-			LOG_PRINTF(COLOR_YELLOW, "PERF ", COLOR_YELLOW, _msg, ##__VA_ARGS__); \
+#define US_LOG_PERF_FPS(x_msg, ...) { \
+		if (us_g_log_level >= US_LOG_LEVEL_PERF) { \
+			US_LOG_PRINTF(US_COLOR_YELLOW, "PERF ", US_COLOR_YELLOW, x_msg, ##__VA_ARGS__); \
 		} \
 	}
 
-#define LOG_VERBOSE(_msg, ...) { \
-		if (us_log_level >= LOG_LEVEL_VERBOSE) { \
-			LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg, ##__VA_ARGS__); \
+#define US_LOG_VERBOSE(x_msg, ...) { \
+		if (us_g_log_level >= US_LOG_LEVEL_VERBOSE) { \
+			US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg, ##__VA_ARGS__); \
 		} \
 	}
 
-#define LOG_VERBOSE_PERROR(_msg, ...) { \
-		if (us_log_level >= LOG_LEVEL_VERBOSE) { \
-			char _perror_buf[1024] = {0}; \
-			char *_perror_ptr = errno_to_string(errno, _perror_buf, 1023); \
-			LOG_PRINTF(COLOR_BLUE, "VERB ", COLOR_BLUE, _msg ": %s", ##__VA_ARGS__, _perror_ptr); \
+#define US_LOG_VERBOSE_PERROR(x_msg, ...) { \
+		if (us_g_log_level >= US_LOG_LEVEL_VERBOSE) { \
+			char *m_perror_str = us_errno_to_string(errno); \
+			US_LOG_PRINTF(US_COLOR_BLUE, "VERB ", US_COLOR_BLUE, x_msg ": %s", ##__VA_ARGS__, m_perror_str); \
+			free(m_perror_str); \
 		} \
 	}
 
-#define LOG_DEBUG(_msg, ...) { \
-		if (us_log_level >= LOG_LEVEL_DEBUG) { \
-			LOG_PRINTF(COLOR_GRAY, "DEBUG", COLOR_GRAY, _msg, ##__VA_ARGS__); \
+#define US_LOG_DEBUG(x_msg, ...) { \
+		if (us_g_log_level >= US_LOG_LEVEL_DEBUG) { \
+			US_LOG_PRINTF(US_COLOR_GRAY, "DEBUG", US_COLOR_GRAY, x_msg, ##__VA_ARGS__); \
 		} \
 	}
diff --git a/src/libs/memsink.c b/src/libs/memsink.c
index 2be2671..c35cfaa 100644
--- a/src/libs/memsink.c
+++ b/src/libs/memsink.c
@@ -23,12 +23,12 @@
 #include "memsink.h"
 
 
-memsink_s *memsink_init(
+us_memsink_s *us_memsink_init(
 	const char *name, const char *obj, bool server,
 	mode_t mode, bool rm, unsigned client_ttl, unsigned timeout) {
 
-	memsink_s *sink;
-	A_CALLOC(sink, 1);
+	us_memsink_s *sink;
+	US_CALLOC(sink, 1);
 	sink->name = name;
 	sink->obj = obj;
 	sink->server = server;
@@ -36,57 +36,56 @@ memsink_s *memsink_init(
 	sink->client_ttl = client_ttl;
 	sink->timeout = timeout;
 	sink->fd = -1;
-	sink->mem = MAP_FAILED;
 	atomic_init(&sink->has_clients, false);
 
-	LOG_INFO("Using %s-sink: %s", name, obj);
+	US_LOG_INFO("Using %s-sink: %s", name, obj);
 
-	mode_t mask = umask(0);
+	const mode_t mask = umask(0);
 	sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
 	umask(mask);
 	if (sink->fd == -1) {
 		umask(mask);
-		LOG_PERROR("%s-sink: Can't open shared memory", name);
+		US_LOG_PERROR("%s-sink: Can't open shared memory", name);
 		goto error;
 	}
 
-	if (sink->server && ftruncate(sink->fd, sizeof(memsink_shared_s)) < 0) {
-		LOG_PERROR("%s-sink: Can't truncate shared memory", name);
+	if (sink->server && ftruncate(sink->fd, sizeof(us_memsink_shared_s)) < 0) {
+		US_LOG_PERROR("%s-sink: Can't truncate shared memory", name);
 		goto error;
 	}
 
-	if ((sink->mem = memsink_shared_map(sink->fd)) == NULL) {
-		LOG_PERROR("%s-sink: Can't mmap shared memory", name);
+	if ((sink->mem = us_memsink_shared_map(sink->fd)) == NULL) {
+		US_LOG_PERROR("%s-sink: Can't mmap shared memory", name);
 		goto error;
 	}
 
 	return sink;
 
 	error:
-		memsink_destroy(sink);
+		us_memsink_destroy(sink);
 		return NULL;
 }
 
-void memsink_destroy(memsink_s *sink) {
-	if (sink->mem != MAP_FAILED) {
-		if (memsink_shared_unmap(sink->mem) < 0) {
-			LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
+void us_memsink_destroy(us_memsink_s *sink) {
+	if (sink->mem != NULL) {
+		if (us_memsink_shared_unmap(sink->mem) < 0) {
+			US_LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
 		}
 	}
 	if (sink->fd >= 0) {
 		if (close(sink->fd) < 0) {
-			LOG_PERROR("%s-sink: Can't close shared memory fd", sink->name);
+			US_LOG_PERROR("%s-sink: Can't close shared memory fd", sink->name);
 		}
 		if (sink->rm && shm_unlink(sink->obj) < 0) {
 			if (errno != ENOENT) {
-				LOG_PERROR("%s-sink: Can't remove shared memory", sink->name);
+				US_LOG_PERROR("%s-sink: Can't remove shared memory", sink->name);
 			}
 		}
 	}
 	free(sink);
 }
 
-bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
+bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame) {
 	// Return true (the need to write to memsink) on any of these conditions:
 	//   - EWOULDBLOCK - we have an active client;
 	//   - Incorrect magic or version - need to first write;
@@ -100,97 +99,106 @@ bool memsink_server_check(memsink_s *sink, const frame_s *frame) {
 			atomic_store(&sink->has_clients, true);
 			return true;
 		}
-		LOG_PERROR("%s-sink: Can't lock memory", sink->name);
+		US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
 		return false;
 	}
 
-	if (sink->mem->magic != MEMSINK_MAGIC || sink->mem->version != MEMSINK_VERSION) {
+	if (sink->mem->magic != US_MEMSINK_MAGIC || sink->mem->version != US_MEMSINK_VERSION) {
 		return true;
 	}
 
-	bool has_clients = (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic());
+	const bool has_clients = (sink->mem->last_client_ts + sink->client_ttl > us_get_now_monotonic());
 	atomic_store(&sink->has_clients, has_clients);
 
 	if (flock(sink->fd, LOCK_UN) < 0) {
-		LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
+		US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
 		return false;
 	}
-	return (has_clients || !FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
+	return (has_clients || !US_FRAME_COMPARE_META_USED_NOTS(sink->mem, frame));;
 }
 
-int memsink_server_put(memsink_s *sink, const frame_s *frame) {
+int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *const key_requested) {
 	assert(sink->server);
 
-	const long double now = get_now_monotonic();
+	const long double now = us_get_now_monotonic();
 
-	if (frame->used > MEMSINK_MAX_DATA) {
-		LOG_ERROR("%s-sink: Can't put frame: is too big (%zu > %zu)",
-			sink->name, frame->used, MEMSINK_MAX_DATA);
+	if (frame->used > US_MEMSINK_MAX_DATA) {
+		US_LOG_ERROR("%s-sink: Can't put frame: is too big (%zu > %zu)",
+			sink->name, frame->used, US_MEMSINK_MAX_DATA);
 		return 0; // -2
 	}
 
-	if (flock_timedwait_monotonic(sink->fd, 1) == 0) {
-		LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
+	if (us_flock_timedwait_monotonic(sink->fd, 1) == 0) {
+		US_LOG_VERBOSE("%s-sink: >>>>> Exposing new frame ...", sink->name);
 
-		sink->last_id = get_now_id();
+		sink->last_id = us_get_now_id();
 		sink->mem->id = sink->last_id;
+		if (sink->mem->key_requested && frame->key) {
+			sink->mem->key_requested = false;
+		}
+		*key_requested = sink->mem->key_requested;
 
 		memcpy(sink->mem->data, frame->data, frame->used);
 		sink->mem->used = frame->used;
-		FRAME_COPY_META(frame, sink->mem);
+		US_FRAME_COPY_META(frame, sink->mem);
+
+		sink->mem->magic = US_MEMSINK_MAGIC;
+		sink->mem->version = US_MEMSINK_VERSION;
 
-		sink->mem->magic = MEMSINK_MAGIC;
-		sink->mem->version = MEMSINK_VERSION;
-		atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > get_now_monotonic()));
+		atomic_store(&sink->has_clients, (sink->mem->last_client_ts + sink->client_ttl > us_get_now_monotonic()));
 
 		if (flock(sink->fd, LOCK_UN) < 0) {
-			LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
+			US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
 			return -1;
 		}
-		LOG_VERBOSE("%s-sink: Exposed new frame; full exposition time = %.3Lf",
-			sink->name, get_now_monotonic() - now);
+		US_LOG_VERBOSE("%s-sink: Exposed new frame; full exposition time = %.3Lf",
+			sink->name, us_get_now_monotonic() - now);
 
 	} else if (errno == EWOULDBLOCK) {
-		LOG_VERBOSE("%s-sink: ===== Shared memory is busy now; frame skipped", sink->name);
+		US_LOG_VERBOSE("%s-sink: ===== Shared memory is busy now; frame skipped", sink->name);
 
 	} else {
-		LOG_PERROR("%s-sink: Can't lock memory", sink->name);
+		US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
 		return -1;
 	}
 	return 0;
 }
 
-int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress unusedFunction
+int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *const key_requested, bool key_required) { // cppcheck-suppress unusedFunction
 	assert(!sink->server); // Client only
 
-	if (flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
+	if (us_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
 		if (errno == EWOULDBLOCK) {
 			return -2;
 		}
-		LOG_PERROR("%s-sink: Can't lock memory", sink->name);
+		US_LOG_PERROR("%s-sink: Can't lock memory", sink->name);
 		return -1;
 	}
 
 	int retval = -2; // Not updated
-	if (sink->mem->magic == MEMSINK_MAGIC) {
-		if (sink->mem->version != MEMSINK_VERSION) {
-			LOG_ERROR("%s-sink: Protocol version mismatch: sink=%u, required=%u",
-				sink->name, sink->mem->version, MEMSINK_VERSION);
+	if (sink->mem->magic == US_MEMSINK_MAGIC) {
+		if (sink->mem->version != US_MEMSINK_VERSION) {
+			US_LOG_ERROR("%s-sink: Protocol version mismatch: sink=%u, required=%u",
+				sink->name, sink->mem->version, US_MEMSINK_VERSION);
 			retval = -1;
 			goto done;
 		}
 		if (sink->mem->id != sink->last_id) { // When updated
 			sink->last_id = sink->mem->id;
-			frame_set_data(frame, sink->mem->data, sink->mem->used);
-			FRAME_COPY_META(sink->mem, frame);
+			us_frame_set_data(frame, sink->mem->data, sink->mem->used);
+			US_FRAME_COPY_META(sink->mem, frame);
+			*key_requested = sink->mem->key_requested;
 			retval = 0;
 		}
-		sink->mem->last_client_ts = get_now_monotonic();
+		sink->mem->last_client_ts = us_get_now_monotonic();
+		if (key_required) {
+			sink->mem->key_requested = true;
+		}
 	}
 
 	done:
 		if (flock(sink->fd, LOCK_UN) < 0) {
-			LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
+			US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
 			return -1;
 		}
 		return retval;
diff --git a/src/libs/memsink.h b/src/libs/memsink.h
index d09de9e..16d0aaf 100644
--- a/src/libs/memsink.h
+++ b/src/libs/memsink.h
@@ -42,27 +42,27 @@
 
 
 typedef struct {
-	const char	*name;
-	const char	*obj;
-	bool		server;
-	bool		rm;
-	unsigned	client_ttl; // Only for server
-	unsigned	timeout;
+	const char			*name;
+	const char			*obj;
+	bool				server;
+	bool				rm;
+	unsigned			client_ttl; // Only for server
+	unsigned			timeout;
 
 	int					fd;
-	memsink_shared_s	*mem;
+	us_memsink_shared_s	*mem;
 	uint64_t			last_id;
 	atomic_bool			has_clients; // Only for server
-} memsink_s;
+} us_memsink_s;
 
 
-memsink_s *memsink_init(
+us_memsink_s *us_memsink_init(
 	const char *name, const char *obj, bool server,
 	mode_t mode, bool rm, unsigned client_ttl, unsigned timeout);
 
-void memsink_destroy(memsink_s *sink);
+void us_memsink_destroy(us_memsink_s *sink);
 
-bool memsink_server_check(memsink_s *sink, const frame_s *frame);
-int memsink_server_put(memsink_s *sink, const frame_s *frame);
+bool us_memsink_server_check(us_memsink_s *sink, const us_frame_s *frame);
+int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *const key_requested);
 
-int memsink_client_get(memsink_s *sink, frame_s *frame);
+int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *const key_requested, bool key_required);
diff --git a/src/libs/memsinksh.h b/src/libs/memsinksh.h
index 73b0da3..0ad2452 100644
--- a/src/libs/memsinksh.h
+++ b/src/libs/memsinksh.h
@@ -29,13 +29,13 @@
 #include <sys/mman.h>
 
 
-#define MEMSINK_MAGIC	((uint64_t)0xCAFEBABECAFEBABE)
-#define MEMSINK_VERSION	((uint32_t)2)
+#define US_MEMSINK_MAGIC	((uint64_t)0xCAFEBABECAFEBABE)
+#define US_MEMSINK_VERSION	((uint32_t)4)
 
-#ifndef CFG_MEMSINK_MAX_DATA
-#	define CFG_MEMSINK_MAX_DATA 33554432
+#ifndef US_CFG_MEMSINK_MAX_DATA
+#	define US_CFG_MEMSINK_MAX_DATA 33554432
 #endif
-#define MEMSINK_MAX_DATA ((size_t)(CFG_MEMSINK_MAX_DATA))
+#define US_MEMSINK_MAX_DATA ((size_t)(US_CFG_MEMSINK_MAX_DATA))
 
 
 typedef struct {
@@ -51,21 +51,23 @@ typedef struct {
 	unsigned	stride;
 	bool		online;
 	bool		key;
+	unsigned	gop;
 
 	long double	grab_ts;
 	long double	encode_begin_ts;
 	long double	encode_end_ts;
 
 	long double	last_client_ts;
+	bool		key_requested;
 
-	uint8_t		data[MEMSINK_MAX_DATA];
-} memsink_shared_s;
+	uint8_t		data[US_MEMSINK_MAX_DATA];
+} us_memsink_shared_s;
 
 
-INLINE memsink_shared_s *memsink_shared_map(int fd) {
-	memsink_shared_s *mem = mmap(
+INLINE us_memsink_shared_s *us_memsink_shared_map(int fd) {
+	us_memsink_shared_s *mem = mmap(
 		NULL,
-		sizeof(memsink_shared_s),
+		sizeof(us_memsink_shared_s),
 		PROT_READ | PROT_WRITE,
 		MAP_SHARED,
 		fd,
@@ -78,7 +80,7 @@ INLINE memsink_shared_s *memsink_shared_map(int fd) {
 	return mem;
 }
 
-INLINE int memsink_shared_unmap(memsink_shared_s *mem) {
+INLINE int us_memsink_shared_unmap(us_memsink_shared_s *mem) {
 	assert(mem != NULL);
-	return munmap(mem, sizeof(memsink_shared_s));
+	return munmap(mem, sizeof(us_memsink_shared_s));
 }
diff --git a/src/libs/options.c b/src/libs/options.c
index f58beb8..c3d42e1 100644
--- a/src/libs/options.c
+++ b/src/libs/options.c
@@ -23,7 +23,7 @@
 #include "options.h"
 
 
-void build_short_options(const struct option opts[], char *short_opts, size_t size) {
+void us_build_short_options(const struct option opts[], char *short_opts, size_t size) {
 	memset(short_opts, 0, size);
     for (unsigned short_index = 0, opt_index = 0; opts[opt_index].name != NULL; ++opt_index) {
 		assert(short_index < size - 3);
diff --git a/src/libs/options.h b/src/libs/options.h
index c83a53e..9901caa 100644
--- a/src/libs/options.h
+++ b/src/libs/options.h
@@ -30,4 +30,4 @@
 #include <sys/types.h>
 
 
-void build_short_options(const struct option opts[], char *short_opts, size_t size);
+void us_build_short_options(const struct option opts[], char *short_opts, size_t size);
diff --git a/src/libs/process.h b/src/libs/process.h
index 854b3ec..edb280a 100644
--- a/src/libs/process.h
+++ b/src/libs/process.h
@@ -72,26 +72,25 @@ extern char **environ;
 
 
 #ifdef HAS_PDEATHSIG
-INLINE int process_track_parent_death(void) {
-	pid_t parent = getppid();
+INLINE int us_process_track_parent_death(void) {
+	const pid_t parent = getppid();
 	int signum = SIGTERM;
 #	if defined(__linux__)
-	int retval = prctl(PR_SET_PDEATHSIG, signum);
+	const int retval = prctl(PR_SET_PDEATHSIG, signum);
 #	elif defined(__FreeBSD__)
-	int retval = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
+	const int retval = procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signum);
 #	else
 #		error WTF?
 #	endif
 	if (retval < 0) {
-		LOG_PERROR("Can't set to receive SIGTERM on parent process death");
+		US_LOG_PERROR("Can't set to receive SIGTERM on parent process death");
 		return -1;
 	}
 
 	if (kill(parent, 0) < 0) {
-		LOG_PERROR("The parent process %d is already dead", parent);
+		US_LOG_PERROR("The parent process %d is already dead", parent);
 		return -1;
 	}
-
 	return 0;
 }
 #endif
@@ -99,21 +98,21 @@ INLINE int process_track_parent_death(void) {
 #ifdef WITH_SETPROCTITLE
 #	pragma GCC diagnostic ignored "-Wunused-parameter"
 #	pragma GCC diagnostic push
-INLINE void process_set_name_prefix(int argc, char *argv[], const char *prefix) {
+INLINE void us_process_set_name_prefix(int argc, char *argv[], const char *prefix) {
 #	pragma GCC diagnostic pop
 
 	char *cmdline = NULL;
 	size_t allocated = 2048;
 	size_t used = 0;
 
-	A_REALLOC(cmdline, allocated);
+	US_REALLOC(cmdline, allocated);
 	cmdline[0] = '\0';
 
 	for (int index = 0; index < argc; ++index) {
 		size_t arg_len = strlen(argv[index]);
 		if (used + arg_len + 16 >= allocated) {
 			allocated += arg_len + 2048;
-			A_REALLOC(cmdline, allocated); // cppcheck-suppress memleakOnRealloc // False-positive (ok with assert)
+			US_REALLOC(cmdline, allocated); // cppcheck-suppress memleakOnRealloc // False-positive (ok with assert)
 		}
 
 		strcat(cmdline, " ");
@@ -130,18 +129,16 @@ INLINE void process_set_name_prefix(int argc, char *argv[], const char *prefix)
 }
 #endif
 
-INLINE void process_notify_parent(void) {
-	pid_t parent = getppid();
-
+INLINE void us_process_notify_parent(void) {
+	const pid_t parent = getppid();
 	if (kill(parent, SIGUSR2) < 0) {
-		LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent);
+		US_LOG_PERROR("Can't send SIGUSR2 to the parent process %d", parent);
 	}
 }
 
-INLINE void process_suicide(void) {
-	pid_t pid = getpid();
-
+INLINE void us_process_suicide(void) {
+	const pid_t pid = getpid();
 	if (kill(pid, SIGTERM) < 0) {
-		LOG_PERROR("Can't send SIGTERM to own pid %d", pid);
+		US_LOG_PERROR("Can't send SIGTERM to own pid %d", pid);
 	}
 }
diff --git a/src/libs/threading.h b/src/libs/threading.h
index a283ee2..b61c623 100644
--- a/src/libs/threading.h
+++ b/src/libs/threading.h
@@ -41,37 +41,38 @@
 
 
 #ifdef PTHREAD_MAX_NAMELEN_NP
-#	define MAX_THREAD_NAME ((size_t)(PTHREAD_MAX_NAMELEN_NP))
+#	define US_MAX_THREAD_NAME ((size_t)(PTHREAD_MAX_NAMELEN_NP))
 #else
-#	define MAX_THREAD_NAME ((size_t)16)
+#	define US_MAX_THREAD_NAME ((size_t)16)
 #endif
 
-#define A_THREAD_CREATE(_tid, _func, _arg)	assert(!pthread_create(_tid, NULL, _func, _arg))
-#define A_THREAD_JOIN(_tid)					assert(!pthread_join(_tid, NULL))
+#define US_THREAD_CREATE(x_tid, x_func, x_arg)	assert(!pthread_create(&(x_tid), NULL, (x_func), (x_arg)))
+#define US_THREAD_JOIN(x_tid)					assert(!pthread_join((x_tid), NULL))
 
 #ifdef WITH_PTHREAD_NP
-#	define A_THREAD_RENAME(_fmt, ...) { \
-			char _new_tname_buf[MAX_THREAD_NAME] = {0}; \
-			assert(snprintf(_new_tname_buf, MAX_THREAD_NAME, _fmt, ##__VA_ARGS__) > 0); \
-			thread_set_name(_new_tname_buf); \
+#	define US_THREAD_RENAME(x_fmt, ...) { \
+			char m_new_tname_buf[US_MAX_THREAD_NAME] = {0}; \
+			assert(snprintf(m_new_tname_buf, US_MAX_THREAD_NAME, (x_fmt), ##__VA_ARGS__) > 0); \
+			us_thread_set_name(m_new_tname_buf); \
 		}
 #else
-#	define A_THREAD_RENAME(_fmt, ...)
+#	define US_THREAD_RENAME(_fmt, ...)
 #endif
 
-#define A_MUTEX_INIT(_mutex)	assert(!pthread_mutex_init(_mutex, NULL))
-#define A_MUTEX_DESTROY(_mutex)	assert(!pthread_mutex_destroy(_mutex))
-#define A_MUTEX_LOCK(_mutex)	assert(!pthread_mutex_lock(_mutex))
-#define A_MUTEX_UNLOCK(_mutex)	assert(!pthread_mutex_unlock(_mutex))
+#define US_MUTEX_INIT(x_mutex)		assert(!pthread_mutex_init(&(x_mutex), NULL))
+#define US_MUTEX_DESTROY(x_mutex)	assert(!pthread_mutex_destroy(&(x_mutex)))
+#define US_MUTEX_LOCK(x_mutex)		assert(!pthread_mutex_lock(&(x_mutex)))
+#define US_MUTEX_UNLOCK(x_mutex)	assert(!pthread_mutex_unlock(&(x_mutex)))
 
-#define A_COND_INIT(_cond)		assert(!pthread_cond_init(_cond, NULL))
-#define A_COND_DESTROY(_cond)	assert(!pthread_cond_destroy(_cond))
-#define A_COND_SIGNAL(...)		assert(!pthread_cond_signal(__VA_ARGS__))
-#define A_COND_WAIT_TRUE(_var, _cond, _mutex) { while(!_var) assert(!pthread_cond_wait(_cond, _mutex)); }
+#define US_COND_INIT(x_cond)		assert(!pthread_cond_init(&(x_cond), NULL))
+#define US_COND_DESTROY(x_cond)		assert(!pthread_cond_destroy(&(x_cond)))
+#define US_COND_SIGNAL(x_cond)		assert(!pthread_cond_signal(&(x_cond)))
+#define US_COND_BROADCAST(x_cond)	assert(!pthread_cond_broadcast(&(x_cond)))
+#define US_COND_WAIT_FOR(x_var, x_cond, x_mutex) { while(!(x_var)) assert(!pthread_cond_wait(&(x_cond), &(x_mutex))); }
 
 
 #ifdef WITH_PTHREAD_NP
-INLINE void thread_set_name(const char *name) {
+INLINE void us_thread_set_name(const char *name) {
 #	if defined(__linux__)
 	pthread_setname_np(pthread_self(), name);
 #	elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
@@ -79,45 +80,45 @@ INLINE void thread_set_name(const char *name) {
 #	elif defined(__NetBSD__)
 	pthread_setname_np(pthread_self(), "%s", (void *)name);
 #	else
-#		error thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
+#		error us_thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
 #	endif
 }
 #endif
 
-INLINE void thread_get_name(char *name) { // Always required for logging
+INLINE void us_thread_get_name(char *name) { // Always required for logging
 #ifdef WITH_PTHREAD_NP
 	int retval = -1;
 #	if defined(__linux__) || defined (__NetBSD__)
-	retval = pthread_getname_np(pthread_self(), name, MAX_THREAD_NAME);
+	retval = pthread_getname_np(pthread_self(), name, US_MAX_THREAD_NAME);
 #	elif \
 		(defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1103500) \
 		|| (defined(__OpenBSD__) && defined(OpenBSD) && OpenBSD >= 201905) \
 		|| defined(__DragonFly__)
-	pthread_get_name_np(pthread_self(), name, MAX_THREAD_NAME);
+	pthread_get_name_np(pthread_self(), name, US_MAX_THREAD_NAME);
 	if (name[0] != '\0') {
 		retval = 0;
 	}
 #	else
-#		error thread_get_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
+#		error us_thread_get_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
 #	endif
 	if (retval < 0) {
 #endif
 
 #if defined(__linux__)
-		pid_t tid = syscall(SYS_gettid);
+		const pid_t tid = syscall(SYS_gettid);
 #elif defined(__FreeBSD__)
-		pid_t tid = syscall(SYS_thr_self);
+		const pid_t tid = syscall(SYS_thr_self);
 #elif defined(__OpenBSD__)
-		pid_t tid = syscall(SYS_getthrid);
+		const pid_t tid = syscall(SYS_getthrid);
 #elif defined(__NetBSD__)
-		pid_t tid = syscall(SYS__lwp_self);
+		const pid_t tid = syscall(SYS__lwp_self);
 #elif defined(__DragonFly__)
-		pid_t tid = syscall(SYS_lwp_gettid);
+		const pid_t tid = syscall(SYS_lwp_gettid);
 #else
-		pid_t tid = 0; // Makes cppcheck happy
+		const pid_t tid = 0; // Makes cppcheck happy
 #	warning gettid() not implemented
 #endif
-		assert(snprintf(name, MAX_THREAD_NAME, "tid=%d", tid) > 0);
+		assert(snprintf(name, US_MAX_THREAD_NAME, "tid=%d", tid) > 0);
 
 #ifdef WITH_PTHREAD_NP
 	}
diff --git a/src/libs/tools.h b/src/libs/tools.h
index b4f1be9..94facf2 100644
--- a/src/libs/tools.h
+++ b/src/libs/tools.h
@@ -35,8 +35,15 @@
 #include <time.h>
 #include <assert.h>
 
+#include <sys/types.h>
 #include <sys/file.h>
 
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 32
+#	define HAS_SIGABBREV_NP
+#else
+#	include <signal.h>
+#endif
+
 
 #ifdef NDEBUG
 #	error WTF dude? Asserts are good things!
@@ -52,36 +59,41 @@
 #define INLINE inline __attribute__((always_inline))
 #define UNUSED __attribute__((unused))
 
-#define A_CALLOC(_dest, _nmemb)		assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
-#define A_REALLOC(_dest, _nmemb)	assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
-#define MEMSET_ZERO(_obj)			memset(&(_obj), 0, sizeof(_obj))
+#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); } }
+#define US_MEMSET_ZERO(x_obj)			memset(&(x_obj), 0, sizeof(x_obj))
 
-#define A_ASPRINTF(_dest, _fmt, ...) assert(asprintf(&(_dest), _fmt, ##__VA_ARGS__) >= 0)
+#define US_ASPRINTF(x_dest, x_fmt, ...) assert(asprintf(&(x_dest), (x_fmt), ##__VA_ARGS__) >= 0)
 
-#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))
 
+INLINE char *us_strdup(const char *str) {
+	char *const new = strdup(str);
+	assert(new != NULL);
+	return new;
+}
 
-INLINE const char *bool_to_string(bool flag) {
+INLINE const char *us_bool_to_string(bool flag) {
 	return (flag ? "true" : "false");
 }
 
-INLINE size_t align_size(size_t size, size_t to) {
+INLINE size_t us_align_size(size_t size, size_t to) {
 	return ((size + (to - 1)) & ~(to - 1));
 }
 
-INLINE unsigned min_u(unsigned a, unsigned b) {
+INLINE unsigned us_min_u(unsigned a, unsigned b) {
 	return (a < b ? a : b);
 }
 
-INLINE unsigned max_u(unsigned a, unsigned b) {
+INLINE unsigned us_max_u(unsigned a, unsigned b) {
 	return (a > b ? a : b);
 }
 
-INLINE long long floor_ms(long double now) {
+INLINE long long us_floor_ms(long double now) {
 	return (long long)now - (now < (long long)now); // floor()
 }
 
-INLINE uint32_t triple_u32(uint32_t x) {
+INLINE uint32_t us_triple_u32(uint32_t x) {
 	// https://nullprogram.com/blog/2018/07/31/
 	x ^= x >> 17;
 	x *= UINT32_C(0xED5AD4BB);
@@ -93,7 +105,7 @@ INLINE uint32_t triple_u32(uint32_t x) {
 	return x;
 }
 
-INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
+INLINE void us_get_now(clockid_t clk_id, time_t *sec, long *msec) {
 	struct timespec ts;
 	assert(!clock_gettime(clk_id, &ts));
 	*sec = ts.tv_sec;
@@ -106,53 +118,66 @@ INLINE void get_now(clockid_t clk_id, time_t *sec, long *msec) {
 }
 
 #if defined(CLOCK_MONOTONIC_RAW)
-#	define X_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW
+#	define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC_RAW
 #elif defined(CLOCK_MONOTONIC_FAST)
-#	define X_CLOCK_MONOTONIC CLOCK_MONOTONIC_FAST
+#	define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC_FAST
 #else
-#	define X_CLOCK_MONOTONIC CLOCK_MONOTONIC
+#	define _X_CLOCK_MONOTONIC CLOCK_MONOTONIC
 #endif
 
-INLINE long double get_now_monotonic(void) {
+INLINE long double us_get_now_monotonic(void) {
 	time_t sec;
 	long msec;
-	get_now(X_CLOCK_MONOTONIC, &sec, &msec);
+	us_get_now(_X_CLOCK_MONOTONIC, &sec, &msec);
 	return (long double)sec + ((long double)msec) / 1000;
 }
 
-INLINE uint64_t get_now_monotonic_u64(void) {
+INLINE uint64_t us_get_now_monotonic_u64(void) {
 	struct timespec ts;
-	assert(!clock_gettime(X_CLOCK_MONOTONIC, &ts));
+	assert(!clock_gettime(_X_CLOCK_MONOTONIC, &ts));
 	return (uint64_t)(ts.tv_nsec / 1000) + (uint64_t)ts.tv_sec * 1000000;
 }
 
-INLINE uint64_t get_now_id(void) {
-	uint64_t now = get_now_monotonic_u64();
-	return (uint64_t)triple_u32(now) | ((uint64_t)triple_u32(now + 12345) << 32);
-}
+#undef _X_CLOCK_MONOTONIC
 
-#undef X_CLOCK_MONOTONIC
+INLINE uint64_t us_get_now_id(void) {
+	const uint64_t now = us_get_now_monotonic_u64();
+	return (uint64_t)us_triple_u32(now) | ((uint64_t)us_triple_u32(now + 12345) << 32);
+}
 
-INLINE long double get_now_real(void) {
+INLINE long double us_get_now_real(void) {
 	time_t sec;
 	long msec;
-	get_now(CLOCK_REALTIME, &sec, &msec);
+	us_get_now(CLOCK_REALTIME, &sec, &msec);
 	return (long double)sec + ((long double)msec) / 1000;
 }
 
-INLINE unsigned get_cores_available(void) {
+INLINE unsigned us_get_cores_available(void) {
 	long cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
 	cores_sysconf = (cores_sysconf < 0 ? 0 : cores_sysconf);
-	return max_u(min_u(cores_sysconf, 4), 1);
+	return us_max_u(us_min_u(cores_sysconf, 4), 1);
 }
 
-INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
-	long double deadline_ts = get_now_monotonic() + timeout;
+INLINE void us_ld_to_timespec(long double ld, struct timespec *ts) {
+	ts->tv_sec = (long)ld;
+	ts->tv_nsec = (ld - ts->tv_sec) * 1000000000L;
+	if (ts->tv_nsec > 999999999L) {
+		ts->tv_sec += 1;
+		ts->tv_nsec = 0;
+	}
+}
+
+INLINE long double us_timespec_to_ld(const struct timespec *ts) {
+	return ts->tv_sec + ((long double)ts->tv_nsec) / 1000000000;
+}
+
+INLINE int us_flock_timedwait_monotonic(int fd, long double timeout) {
+	const long double deadline_ts = us_get_now_monotonic() + timeout;
 	int retval = -1;
 
 	while (true) {
 		retval = flock(fd, LOCK_EX | LOCK_NB);
-		if (retval == 0 || errno != EWOULDBLOCK || get_now_monotonic() > deadline_ts) {
+		if (retval == 0 || errno != EWOULDBLOCK || us_get_now_monotonic() > deadline_ts) {
 			break;
 		}
 		if (usleep(1000) < 0) {
@@ -162,15 +187,34 @@ INLINE int flock_timedwait_monotonic(int fd, long double timeout) {
 	return retval;
 }
 
-INLINE char *errno_to_string(int error, char *buf, size_t size) {
-	assert(buf);
-	assert(size > 0);
+INLINE char *us_errno_to_string(int error) {
 	locale_t locale = newlocale(LC_MESSAGES_MASK, "C", NULL);
-	char *str = "!!! newlocale() error !!!";
-	strncpy(buf, (locale ? strerror_l(error, locale) : str), size - 1);
-	buf[size - 1] = '\0';
+	char *buf;
 	if (locale) {
+		buf = us_strdup(strerror_l(error, locale));
 		freelocale(locale);
+	} else {
+		buf = us_strdup("!!! newlocale() error !!!");
+	}
+	return buf;
+}
+
+INLINE char *us_signum_to_string(int signum) {
+#	ifdef HAS_SIGABBREV_NP
+	const char *const name = sigabbrev_np(signum);
+#	else
+	const char *const name = (
+		signum == SIGTERM ? "TERM" :
+		signum == SIGINT ? "INT" :
+		signum == SIGPIPE ? "PIPE" :
+		NULL
+	);
+#	endif
+	char *buf;
+	if (name != NULL) {
+		US_ASPRINTF(buf, "SIG%s", name);
+	} else {
+		US_ASPRINTF(buf, "SIG[%d]", signum);
 	}
 	return buf;
 }
diff --git a/src/libs/unjpeg.c b/src/libs/unjpeg.c
index 3a37dd9..9cd7fad 100644
--- a/src/libs/unjpeg.c
+++ b/src/libs/unjpeg.c
@@ -26,15 +26,15 @@
 typedef struct {
 	struct jpeg_error_mgr	mgr; // Default manager
 	jmp_buf					jmp;
-	const frame_s			*frame;
+	const us_frame_s		*frame;
 } _jpeg_error_manager_s;
 
 
 static void _jpeg_error_handler(j_common_ptr jpeg);
 
 
-int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
-	assert(is_jpeg(src->format));
+int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode) {
+	assert(us_is_jpeg(src->format));
 
 	volatile int retval = 0;
 
@@ -57,7 +57,7 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
 
 	jpeg_start_decompress(&jpeg);
 
-	frame_copy_meta(src, dest);
+	us_frame_copy_meta(src, dest);
 	dest->format = V4L2_PIX_FMT_RGB24;
 	dest->width = jpeg.output_width;
 	dest->height = jpeg.output_height;
@@ -68,10 +68,10 @@ int unjpeg(const frame_s *src, frame_s *dest, bool decode) {
 		JSAMPARRAY scanlines;
 		scanlines = (*jpeg.mem->alloc_sarray)((j_common_ptr) &jpeg, JPOOL_IMAGE, dest->stride, 1);
 
-		frame_realloc_data(dest, ((dest->width * dest->height) << 1) * 2);
+		us_frame_realloc_data(dest, ((dest->width * dest->height) << 1) * 2);
 		while (jpeg.output_scanline < jpeg.output_height) {
 			jpeg_read_scanlines(&jpeg, scanlines, 1);
-			frame_append_data(dest, scanlines[0], dest->stride);
+			us_frame_append_data(dest, scanlines[0], dest->stride);
 		}
 
 		jpeg_finish_decompress(&jpeg);
@@ -87,6 +87,6 @@ static void _jpeg_error_handler(j_common_ptr jpeg) {
 	char msg[JMSG_LENGTH_MAX];
 
 	(*jpeg_error->mgr.format_message)(jpeg, msg);
-	LOG_ERROR("Can't decompress JPEG: %s", msg);
+	US_LOG_ERROR("Can't decompress JPEG: %s", msg);
 	longjmp(jpeg_error->jmp, -1);
 }
diff --git a/src/libs/unjpeg.h b/src/libs/unjpeg.h
index 0696516..90c8df5 100644
--- a/src/libs/unjpeg.h
+++ b/src/libs/unjpeg.h
@@ -37,4 +37,4 @@
 #include "frame.h"
 
 
-int unjpeg(const frame_s *src, frame_s *dest, bool decode);
+int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode);
diff --git a/src/ustreamer/xioctl.h b/src/libs/xioctl.h
similarity index 81%
rename from src/ustreamer/xioctl.h
rename to src/libs/xioctl.h
index 31e82ed..b9691a6 100644
--- a/src/ustreamer/xioctl.h
+++ b/src/libs/xioctl.h
@@ -26,18 +26,15 @@
 
 #include <sys/ioctl.h>
 
-#include "../libs/tools.h"
-#include "../libs/logging.h"
 
-
-#ifndef CFG_XIOCTL_RETRIES
-#	define CFG_XIOCTL_RETRIES 4
+#ifndef US_CFG_XIOCTL_RETRIES
+#	define US_CFG_XIOCTL_RETRIES 4
 #endif
-#define XIOCTL_RETRIES ((unsigned)(CFG_XIOCTL_RETRIES))
+#define _XIOCTL_RETRIES ((unsigned)(US_CFG_XIOCTL_RETRIES))
 
 
-INLINE int xioctl(int fd, int request, void *arg) {
-	int retries = XIOCTL_RETRIES;
+INLINE int us_xioctl(int fd, int request, void *arg) {
+	int retries = _XIOCTL_RETRIES;
 	int retval = -1;
 
 	do {
@@ -51,10 +48,5 @@ INLINE int xioctl(int fd, int request, void *arg) {
 			|| errno == ETIMEDOUT
 		)
 	);
-
-	// cppcheck-suppress knownConditionTrueFalse
-	if (retval && retries <= 0) {
-		LOG_PERROR("ioctl(%d) retried %u times; giving up", request, XIOCTL_RETRIES);
-	}
 	return retval;
 }
diff --git a/src/ustreamer/blank.c b/src/ustreamer/blank.c
index d91d593..eec5818 100644
--- a/src/ustreamer/blank.c
+++ b/src/ustreamer/blank.c
@@ -23,85 +23,83 @@
 #include "blank.h"
 
 
-static frame_s *_init_internal(void);
-static frame_s *_init_external(const char *path);
+static us_frame_s *_init_internal(void);
+static us_frame_s *_init_external(const char *path);
 
 
-frame_s *blank_frame_init(const char *path) {
-	frame_s *blank = NULL;
+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) {
-		LOG_INFO("Using external blank placeholder: %s", path);
+	if (blank != NULL) {
+		US_LOG_INFO("Using external blank placeholder: %s", path);
 	} else {
 		blank = _init_internal();
-		LOG_INFO("Using internal blank placeholder");
+		US_LOG_INFO("Using internal blank placeholder");
 	}
 	return blank;
 }
 
-static frame_s *_init_internal(void) {
-	frame_s *blank = frame_init();
-	frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE);
-	blank->width = BLANK_JPEG_WIDTH;
-	blank->height = BLANK_JPEG_HEIGHT;
+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;
 }
 
-static frame_s *_init_external(const char *path) {
+static us_frame_s *_init_external(const char *path) {
 	FILE *fp = NULL;
 
-	frame_s *blank = frame_init();
+	us_frame_s *blank = us_frame_init();
 	blank->format = V4L2_PIX_FMT_JPEG;
 
 	if ((fp = fopen(path, "rb")) == NULL) {
-		LOG_PERROR("Can't open blank placeholder '%s'", path);
+		US_LOG_PERROR("Can't open blank placeholder '%s'", path);
 		goto error;
 	}
 
 #	define CHUNK_SIZE ((size_t)(100 * 1024))
 	while (true) {
 		if (blank->used + CHUNK_SIZE >= blank->allocated) {
-			frame_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
+			us_frame_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
 		}
 
-		size_t readed = fread(blank->data + blank->used, 1, CHUNK_SIZE, fp);
+		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 {
-				LOG_PERROR("Can't read blank placeholder");
+				US_LOG_PERROR("Can't read blank placeholder");
 				goto error;
 			}
 		}
 	}
 #	undef CHUNK_SIZE
 
-	frame_s *decoded = frame_init();
-	if (unjpeg(blank, decoded, false) < 0) {
-		frame_destroy(decoded);
+	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;
-	frame_destroy(decoded);
+	us_frame_destroy(decoded);
 
 	goto ok;
 
 	error:
-		frame_destroy(blank);
+		us_frame_destroy(blank);
 		blank = NULL;
 
 	ok:
-		if (fp) {
-			fclose(fp);
-		}
+		US_DELETE(fp, fclose);
 
 	return blank;
 }
diff --git a/src/ustreamer/blank.h b/src/ustreamer/blank.h
index 0fbb035..74e80fb 100644
--- a/src/ustreamer/blank.h
+++ b/src/ustreamer/blank.h
@@ -35,4 +35,4 @@
 #include "data/blank_jpeg.h"
 
 
-frame_s *blank_frame_init(const char *path);
+us_frame_s *us_blank_frame_init(const char *path);
diff --git a/src/ustreamer/data/blank_jpeg.c b/src/ustreamer/data/blank_jpeg.c
index c4aaf5e..9ebb2cc 100644
--- a/src/ustreamer/data/blank_jpeg.c
+++ b/src/ustreamer/data/blank_jpeg.c
@@ -22,11 +22,11 @@
 #include "blank_jpeg.h"
 
 
-const unsigned BLANK_JPEG_WIDTH = 640;
-const unsigned BLANK_JPEG_HEIGHT = 480;
+const unsigned US_BLANK_JPEG_WIDTH = 640;
+const unsigned US_BLANK_JPEG_HEIGHT = 480;
 
-const size_t BLANK_JPEG_DATA_SIZE = 13845;
-const uint8_t BLANK_JPEG_DATA[] = {
+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,
diff --git a/src/ustreamer/data/blank_jpeg.h b/src/ustreamer/data/blank_jpeg.h
index 2babf84..26744eb 100644
--- a/src/ustreamer/data/blank_jpeg.h
+++ b/src/ustreamer/data/blank_jpeg.h
@@ -27,8 +27,8 @@
 #include <sys/types.h>
 
 
-extern const unsigned BLANK_JPEG_WIDTH;
-extern const unsigned BLANK_JPEG_HEIGHT;
+extern const unsigned US_BLANK_JPEG_WIDTH;
+extern const unsigned US_BLANK_JPEG_HEIGHT;
 
-extern const size_t BLANK_JPEG_DATA_SIZE;
-extern const uint8_t BLANK_JPEG_DATA[];
+extern const size_t US_BLANK_JPEG_DATA_SIZE;
+extern const uint8_t US_BLANK_JPEG_DATA[];
diff --git a/src/ustreamer/data/favicon.ico b/src/ustreamer/data/favicon.ico
new file mode 100644
index 0000000..26af37a
Binary files /dev/null and b/src/ustreamer/data/favicon.ico differ
diff --git a/src/ustreamer/data/favicon_ico.c b/src/ustreamer/data/favicon_ico.c
new file mode 100644
index 0000000..639a83a
--- /dev/null
+++ b/src/ustreamer/data/favicon_ico.c
@@ -0,0 +1,782 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 "favicon_ico.h"
+
+
+const size_t US_FAVICON_ICO_DATA_SIZE = 15086;
+const uint8_t US_FAVICON_ICO_DATA[] = {
+	0x00, 0x00, 0x01, 0x00, 0x03, 0x00, 0x30, 0x30, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xA8, 0x25, 0x00, 0x00, 0x36, 0x00,
+	0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0xA8, 0x10, 0x00, 0x00, 0xDE, 0x25, 0x00, 0x00, 0x10, 0x10,
+	0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x68, 0x04, 0x00, 0x00, 0x86, 0x36, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x30, 0x00,
+	0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
+	0x00, 0x3A, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x2A, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x19, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x1A, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00,
+	0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFE, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00,
+	0x00, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x50, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00,
+	0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00,
+	0x00, 0xE3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xE2, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00,
+	0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x28, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x16, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
+	0x00, 0x3B, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00,
+	0x00, 0xDA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+	0x00, 0x91, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00,
+	0x00, 0xE2, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x0C, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xC7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x52, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
+	0x00, 0xF9, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xA4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x8D, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x7B, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x52, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+	0x00, 0xC4, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00,
+	0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x30, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xF5, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xB8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x2A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00,
+	0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00,
+	0x00, 0xAE, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00,
+	0x00, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xA2, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00,
+	0x00, 0x46, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00,
+	0x00, 0x39, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00,
+	0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00,
+	0x00, 0x6C, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00,
+	0x00, 0x8B, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFC, 0x01, 0xFF, 0x00, 0x00, 0xF0, 0x0F, 0xFF, 0xF8, 0x00, 0x7F, 0x00, 0x00, 0xF0, 0x07,
+	0xFF, 0xF0, 0x00, 0x1F, 0x00, 0x00, 0xF0, 0x03, 0xFF, 0xE0, 0x00, 0x0F, 0x00, 0x00, 0xE0, 0x03, 0xFF, 0xE0, 0x00, 0x07,
+	0x00, 0x00, 0xF0, 0x01, 0xFF, 0xC0, 0x00, 0x07, 0x00, 0x00, 0xF0, 0x01, 0xFF, 0xC0, 0x00, 0x03, 0x00, 0x00, 0xF8, 0x01,
+	0xFF, 0xC0, 0x00, 0x03, 0x00, 0x00, 0xFC, 0x00, 0xFF, 0xC0, 0x01, 0xC1, 0x00, 0x00, 0xFE, 0x00, 0xFF, 0xC0, 0x07, 0xE1,
+	0x00, 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0x07, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0x0F, 0xFD, 0x00, 0x00, 0xFF, 0x80,
+	0xFF, 0xC0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xC0, 0x7F, 0xC0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xC0, 0x7F, 0xC0, 0x0F, 0xFF,
+	0x00, 0x00, 0xFF, 0xE0, 0x7F, 0xC0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xE0, 0x7F, 0xE0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xE0,
+	0x7F, 0xE0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF0, 0x7F, 0xE0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF0, 0x3F, 0xE0, 0x0F, 0xFF,
+	0x00, 0x00, 0xFF, 0xF0, 0x3F, 0xF0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x3F, 0xF0, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF8,
+	0x3F, 0xF8, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x3F, 0xF8, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0xF8, 0x3F, 0xF8, 0x0F, 0xFF,
+	0x00, 0x00, 0xFF, 0xF8, 0x1F, 0xFC, 0x0F, 0xFF, 0x00, 0x00, 0xBF, 0xFC, 0x1F, 0xFC, 0x07, 0xFF, 0x00, 0x00, 0x1F, 0xFC,
+	0x1F, 0xFC, 0x07, 0xFF, 0x00, 0x00, 0x0F, 0xFC, 0x1F, 0xFE, 0x07, 0xFF, 0x00, 0x00, 0x03, 0xFC, 0x1F, 0xFE, 0x07, 0xFF,
+	0x00, 0x00, 0x80, 0x0C, 0x0F, 0xFE, 0x03, 0xFF, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x00,
+	0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x03,
+	0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xF8, 0x00,
+	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81,
+	0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x20, 0x00,
+	0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+	0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00,
+	0x00, 0x77, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00,
+	0x00, 0xFA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xCD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x87, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x9C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x15, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xA4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xCA, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x7B, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x02, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00,
+	0x00, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x7C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+	0x00, 0xF6, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x1A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
+	0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xAE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00,
+	0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x7D, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
+	0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
+	0x00, 0xE8, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xE2, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x8A, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+	0x00, 0x67, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00,
+	0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
+	0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
+	0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00,
+	0x00, 0xF3, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x06, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF,
+	0xFF, 0xFF, 0xE0, 0xFF, 0xF0, 0x1F, 0xC0, 0x7F, 0xC0, 0x07, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x3F, 0xC0, 0x03, 0xE0, 0x3F,
+	0x80, 0x01, 0xF0, 0x3F, 0x80, 0x71, 0xF8, 0x1F, 0x80, 0x78, 0xFC, 0x1F, 0x80, 0xFF, 0xFC, 0x1F, 0x80, 0xFF, 0xFE, 0x1F,
+	0xC0, 0xFF, 0xFE, 0x1F, 0xC0, 0xFF, 0xFF, 0x0F, 0xC0, 0xFF, 0xFF, 0x0F, 0xC0, 0xFF, 0xFF, 0x0F, 0xE0, 0xFF, 0xFF, 0x0F,
+	0xE0, 0xFF, 0xFF, 0x8F, 0xE0, 0xFF, 0xFF, 0x8F, 0xF0, 0xFF, 0x7F, 0x87, 0xF0, 0xFF, 0x1F, 0x87, 0xF8, 0x7F, 0x07, 0xC7,
+	0xF8, 0x7F, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x01, 0xE0, 0x00,
+	0x00, 0x01, 0xFC, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x28, 0x00,
+	0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00,
+	0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x1F, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x05, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xBC, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0xD5, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xBD, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00,
+	0x00, 0xA2, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
+	0x00, 0x78, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00,
+	0x00, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00,
+	0x00, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+	0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
+	0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
+	0x00, 0x78, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xDF, 0xCF, 0x00, 0x00, 0x87, 0x81, 0x00, 0x00, 0x87, 0x81, 0x00, 0x00, 0xC7, 0x84,
+	0x00, 0x00, 0xE7, 0x8F, 0x00, 0x00, 0xE3, 0x8F, 0x00, 0x00, 0xF3, 0x8F, 0x00, 0x00, 0xF3, 0x8F, 0x00, 0x00, 0xF3, 0xCF,
+	0x00, 0x00, 0x7B, 0xCF, 0x00, 0x00, 0x01, 0xC7, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xF8,
+	0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
+};
diff --git a/src/libs/config.h b/src/ustreamer/data/favicon_ico.h
similarity index 84%
rename from src/libs/config.h
rename to src/ustreamer/data/favicon_ico.h
index b944ba5..aa4435f 100644
--- a/src/libs/config.h
+++ b/src/ustreamer/data/favicon_ico.h
@@ -22,11 +22,10 @@
 
 #pragma once
 
-#define VERSION_MAJOR 5
-#define VERSION_MINOR 4
+#include <stdint.h>
 
-#define MAKE_VERSION2(_major, _minor) #_major "." #_minor
-#define MAKE_VERSION1(_major, _minor) MAKE_VERSION2(_major, _minor)
-#define VERSION MAKE_VERSION1(VERSION_MAJOR, VERSION_MINOR)
+#include <sys/types.h>
 
-#define VERSION_U ((unsigned)(VERSION_MAJOR * 1000 + VERSION_MINOR))
+
+extern const size_t US_FAVICON_ICO_DATA_SIZE;
+extern const uint8_t US_FAVICON_ICO_DATA[];
diff --git a/src/ustreamer/data/index.html b/src/ustreamer/data/index.html
index fc700f8..9afd0c0 100644
--- a/src/ustreamer/data/index.html
+++ b/src/ustreamer/data/index.html
@@ -3,7 +3,7 @@
 <html>
 <head>
 	<meta charset="utf-8" />
-	<title>uStreamer</title>
+	<title>μStreamer</title>
 	<style>body {font-family: monospace;}</style>
 </head>
 
diff --git a/src/ustreamer/data/index_html.c b/src/ustreamer/data/index_html.c
index cdb4d38..12409c0 100644
--- a/src/ustreamer/data/index_html.c
+++ b/src/ustreamer/data/index_html.c
@@ -22,18 +22,18 @@
 #include "index_html.h"
 
 
-const char *const HTML_INDEX_PAGE = " \
+const char *const US_HTML_INDEX_PAGE = " \
 	<!DOCTYPE html> \
 	\
 	<html> \
 	<head> \
 		<meta charset=\"utf-8\" /> \
-		<title>uStreamer</title> \
+		<title>μStreamer</title> \
 		<style>body {font-family: monospace;}</style> \
 	</head> \
 	\
 	<body> \
-		<h3>&micro;Streamer v" VERSION "</h3> \
+		<h3>&micro;Streamer v" US_VERSION "</h3> \
 		<hr> \
 		<ul> \
 			<li> \
diff --git a/src/ustreamer/data/index_html.h b/src/ustreamer/data/index_html.h
index 45b2716..99dbe5a 100644
--- a/src/ustreamer/data/index_html.h
+++ b/src/ustreamer/data/index_html.h
@@ -24,7 +24,7 @@
 
 #include <sys/types.h>
 
-#include "../../libs/config.h"
+#include "../../libs/const.h"
 
 
-extern const char *const HTML_INDEX_PAGE;
+extern const char *const US_HTML_INDEX_PAGE;
diff --git a/src/ustreamer/device.c b/src/ustreamer/device.c
index 1f9f801..427d999 100644
--- a/src/ustreamer/device.c
+++ b/src/ustreamer/device.c
@@ -34,8 +34,8 @@ static const struct {
 };
 
 static const struct {
-	const char *name;
-	const unsigned format;
+	const char *name; // cppcheck-suppress unusedStructMember
+	const unsigned format; // cppcheck-suppress unusedStructMember
 } _FORMATS[] = {
 	{"YUYV",	V4L2_PIX_FMT_YUYV},
 	{"UYVY",	V4L2_PIX_FMT_UYVY},
@@ -46,32 +46,32 @@ static const struct {
 };
 
 static const struct {
-	const char *name;
-	const enum v4l2_memory io_method;
+	const char *name; // cppcheck-suppress unusedStructMember
+	const enum v4l2_memory io_method; // cppcheck-suppress unusedStructMember
 } _IO_METHODS[] = {
 	{"MMAP",	V4L2_MEMORY_MMAP},
 	{"USERPTR",	V4L2_MEMORY_USERPTR},
 };
 
 
-static int _device_open_check_cap(device_s *dev);
-static int _device_open_dv_timings(device_s *dev);
-static int _device_apply_dv_timings(device_s *dev);
-static int _device_open_format(device_s *dev, bool first);
-static void _device_open_hw_fps(device_s *dev);
-static void _device_open_jpeg_quality(device_s *dev);
-static int _device_open_io_method(device_s *dev);
-static int _device_open_io_method_mmap(device_s *dev);
-static int _device_open_io_method_userptr(device_s *dev);
-static int _device_open_queue_buffers(device_s *dev);
-static int _device_apply_resolution(device_s *dev, unsigned width, unsigned height);
+static int _device_open_check_cap(us_device_s *dev);
+static int _device_open_dv_timings(us_device_s *dev);
+static int _device_apply_dv_timings(us_device_s *dev);
+static int _device_open_format(us_device_s *dev, bool first);
+static void _device_open_hw_fps(us_device_s *dev);
+static void _device_open_jpeg_quality(us_device_s *dev);
+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_apply_resolution(us_device_s *dev, unsigned width, unsigned height);
 
-static void _device_apply_controls(device_s *dev);
+static void _device_apply_controls(us_device_s *dev);
 static int _device_query_control(
-	device_s *dev, struct v4l2_queryctrl *query,
+	us_device_s *dev, struct v4l2_queryctrl *query,
 	const char *name, unsigned cid, bool quiet);
 static void _device_set_control(
-	device_s *dev, struct v4l2_queryctrl *query,
+	us_device_s *dev, struct v4l2_queryctrl *query,
 	const char *name, unsigned cid, int value, bool quiet);
 
 static const char *_format_to_string_nullable(unsigned format);
@@ -80,16 +80,17 @@ static const char *_standard_to_string(v4l2_std_id standard);
 static const char *_io_method_to_string_supported(enum v4l2_memory io_method);
 
 
-#define RUN(_next) dev->run->_next
+#define _RUN(x_next)	dev->run->x_next
+#define _D_XIOCTL(...)	us_xioctl(_RUN(fd), __VA_ARGS__)
 
 
-device_s *device_init(void) {
-	device_runtime_s *run;
-	A_CALLOC(run, 1);
+us_device_s *us_device_init(void) {
+	us_device_runtime_s *run;
+	US_CALLOC(run, 1);
 	run->fd = -1;
 
-	device_s *dev;
-	A_CALLOC(dev, 1);
+	us_device_s *dev;
+	US_CALLOC(dev, 1);
 	dev->path = "/dev/video0";
 	dev->width = 640;
 	dev->height = 480;
@@ -97,51 +98,51 @@ device_s *device_init(void) {
 	dev->jpeg_quality = 80;
 	dev->standard = V4L2_STD_UNKNOWN;
 	dev->io_method = V4L2_MEMORY_MMAP;
-	dev->n_bufs = get_cores_available() + 1;
+	dev->n_bufs = us_get_cores_available() + 1;
 	dev->min_frame_size = 128;
 	dev->timeout = 1;
 	dev->run = run;
 	return dev;
 }
 
-void device_destroy(device_s *dev) {
+void us_device_destroy(us_device_s *dev) {
 	free(dev->run);
 	free(dev);
 }
 
-int device_parse_format(const char *str) {
-	for (unsigned index = 0; index < ARRAY_LEN(_FORMATS); ++index) {
-		if (!strcasecmp(str, _FORMATS[index].name)) {
-			return _FORMATS[index].format;
+int us_device_parse_format(const char *str) {
+	US_ARRAY_ITERATE(_FORMATS, 0, item, {
+		if (!strcasecmp(item->name, str)) {
+			return item->format;
 		}
-	}
-	return FORMAT_UNKNOWN;
+	});
+	return US_FORMAT_UNKNOWN;
 }
 
-v4l2_std_id device_parse_standard(const char *str) {
-	for (unsigned index = 1; index < ARRAY_LEN(_STANDARDS); ++index) {
-		if (!strcasecmp(str, _STANDARDS[index].name)) {
-			return _STANDARDS[index].standard;
+v4l2_std_id us_device_parse_standard(const char *str) {
+	US_ARRAY_ITERATE(_STANDARDS, 1, item, {
+		if (!strcasecmp(item->name, str)) {
+			return item->standard;
 		}
-	}
-	return STANDARD_UNKNOWN;
+	});
+	return US_STANDARD_UNKNOWN;
 }
 
-int device_parse_io_method(const char *str) {
-	for (unsigned index = 0; index < ARRAY_LEN(_IO_METHODS); ++index) {
-		if (!strcasecmp(str, _IO_METHODS[index].name)) {
-			return _IO_METHODS[index].io_method;
+int us_device_parse_io_method(const char *str) {
+	US_ARRAY_ITERATE(_IO_METHODS, 0, item, {
+		if (!strcasecmp(item->name, str)) {
+			return item->io_method;
 		}
-	}
-	return IO_METHOD_UNKNOWN;
+	});
+	return US_IO_METHOD_UNKNOWN;
 }
 
-int device_open(device_s *dev) {
-	if ((RUN(fd) = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
-		LOG_PERROR("Can't open device");
+int us_device_open(us_device_s *dev) {
+	if ((_RUN(fd) = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
+		US_LOG_PERROR("Can't open device");
 		goto error;
 	}
-	LOG_INFO("Device fd=%d opened", RUN(fd));
+	US_LOG_INFO("Device fd=%d opened", _RUN(fd));
 
 	if (_device_open_check_cap(dev) < 0) {
 		goto error;
@@ -162,21 +163,21 @@ int device_open(device_s *dev) {
 	}
 	_device_apply_controls(dev);
 
-	LOG_DEBUG("Device fd=%d initialized", RUN(fd));
+	US_LOG_DEBUG("Device fd=%d initialized", _RUN(fd));
 	return 0;
 
 	error:
-		device_close(dev);
+		us_device_close(dev);
 		return -1;
 }
 
-void device_close(device_s *dev) {
-	RUN(persistent_timeout_reported) = false;
+void us_device_close(us_device_s *dev) {
+	_RUN(persistent_timeout_reported) = false;
 
-	if (RUN(hw_bufs)) {
-		LOG_DEBUG("Releasing device buffers ...");
-		for (unsigned index = 0; index < RUN(n_bufs); ++index) {
-#			define HW(_next) RUN(hw_bufs)[index]._next
+	if (_RUN(hw_bufs) != NULL) {
+		US_LOG_DEBUG("Releasing device buffers ...");
+		for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
+#			define HW(x_next) _RUN(hw_bufs)[index].x_next
 
 			if (HW(dma_fd) >= 0) {
 				close(HW(dma_fd));
@@ -184,46 +185,44 @@ void device_close(device_s *dev) {
 			}
 
 			if (dev->io_method == V4L2_MEMORY_MMAP) {
-				if (HW(raw.allocated) > 0 && HW(raw.data) != MAP_FAILED) {
+				if (HW(raw.allocated) > 0 && HW(raw.data) != NULL) {
 					if (munmap(HW(raw.data), HW(raw.allocated)) < 0) {
-						LOG_PERROR("Can't unmap device buffer=%u", index);
+						US_LOG_PERROR("Can't unmap device buffer=%u", index);
 					}
 				}
 			} else { // V4L2_MEMORY_USERPTR
-				if (HW(raw.data)) {
-					free(HW(raw.data));
-				}
+				US_DELETE(HW(raw.data), free);
 			}
 
 #			undef HW
 		}
-		RUN(n_bufs) = 0;
-		free(RUN(hw_bufs));
-		RUN(hw_bufs) = NULL;
+		_RUN(n_bufs) = 0;
+		free(_RUN(hw_bufs));
+		_RUN(hw_bufs) = NULL;
 	}
 
-	if (RUN(fd) >= 0) {
-		LOG_DEBUG("Closing device ...");
-		if (close(RUN(fd)) < 0) {
-			LOG_PERROR("Can't close device fd=%d", RUN(fd));
+	if (_RUN(fd) >= 0) {
+		US_LOG_DEBUG("Closing device ...");
+		if (close(_RUN(fd)) < 0) {
+			US_LOG_PERROR("Can't close device fd=%d", _RUN(fd));
 		} else {
-			LOG_INFO("Device fd=%d closed", RUN(fd));
+			US_LOG_INFO("Device fd=%d closed", _RUN(fd));
 		}
-		RUN(fd) = -1;
+		_RUN(fd) = -1;
 	}
 }
 
-int device_export_to_dma(device_s *dev) {
-#	define DMA_FD		RUN(hw_bufs[index].dma_fd)
+int us_device_export_to_dma(us_device_s *dev) {
+#	define DMA_FD		_RUN(hw_bufs[index].dma_fd)
 
-	for (unsigned index = 0; index < RUN(n_bufs); ++index) {
+	for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
 		struct v4l2_exportbuffer exp = {0};
 		exp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 		exp.index = index;
 
-		LOG_DEBUG("Exporting device buffer=%u to DMA ...", index);
-		if (xioctl(RUN(fd), VIDIOC_EXPBUF, &exp) < 0) {
-			LOG_PERROR("Can't export device buffer=%u to DMA", index);
+		US_LOG_DEBUG("Exporting device buffer=%u to DMA ...", index);
+		if (_D_XIOCTL(VIDIOC_EXPBUF, &exp) < 0) {
+			US_LOG_PERROR("Can't export device buffer=%u to DMA", index);
 			goto error;
 		}
 		DMA_FD = exp.fd;
@@ -232,7 +231,7 @@ int device_export_to_dma(device_s *dev) {
 	return 0;
 
 	error:
-		for (unsigned index = 0; index < RUN(n_bufs); ++index) {
+		for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
 			if (DMA_FD >= 0) {
 				close(DMA_FD);
 				DMA_FD = -1;
@@ -243,29 +242,29 @@ int device_export_to_dma(device_s *dev) {
 #	undef DMA_FD
 }
 
-int device_switch_capturing(device_s *dev, bool enable) {
-	if (enable != RUN(capturing)) {
+int us_device_switch_capturing(us_device_s *dev, bool enable) {
+	if (enable != _RUN(capturing)) {
 		enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
-		LOG_DEBUG("%s device capturing ...", (enable ? "Starting" : "Stopping"));
-		if (xioctl(RUN(fd), (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
-			LOG_PERROR("Can't %s capturing", (enable ? "start" : "stop"));
+		US_LOG_DEBUG("%s device capturing ...", (enable ? "Starting" : "Stopping"));
+		if (_D_XIOCTL((enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
+			US_LOG_PERROR("Can't %s capturing", (enable ? "start" : "stop"));
 			if (enable) {
 				return -1;
 			}
 		}
 
-		RUN(capturing) = enable;
-		LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
+		_RUN(capturing) = enable;
+		US_LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
 	}
 	return 0;
 }
 
-int device_select(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_write, bool *has_error) {
 	int retval;
 
-#	define INIT_FD_SET(_set) \
-		fd_set _set; FD_ZERO(&_set); FD_SET(RUN(fd), &_set);
+#	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);
@@ -277,27 +276,27 @@ int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_erro
 	timeout.tv_sec = dev->timeout;
 	timeout.tv_usec = 0;
 
-	LOG_DEBUG("Calling select() on video device ...");
+	US_LOG_DEBUG("Calling select() on video device ...");
 
-	retval = select(RUN(fd) + 1, &read_fds, &write_fds, &error_fds, &timeout);
+	retval = select(_RUN(fd) + 1, &read_fds, &write_fds, &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);
+		*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;
 	}
-	LOG_DEBUG("Device select() --> %d", retval);
+	US_LOG_DEBUG("Device select() --> %d", retval);
 
 	if (retval > 0) {
-		RUN(persistent_timeout_reported) = false;
+		_RUN(persistent_timeout_reported) = false;
 	} else if (retval == 0) {
 		if (dev->persistent) {
-			if (!RUN(persistent_timeout_reported)) {
-				LOG_ERROR("Persistent device timeout (unplugged)");
-				RUN(persistent_timeout_reported) = true;
+			if (!_RUN(persistent_timeout_reported)) {
+				US_LOG_ERROR("Persistent device timeout (unplugged)");
+				_RUN(persistent_timeout_reported) = true;
 			}
 		} else {
 			// Если устройство не персистентное, то таймаут является ошибкой
@@ -307,23 +306,23 @@ int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_erro
 	return retval;
 }
 
-int device_grab_buffer(device_s *dev, hw_buffer_s **hw) {
+int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
 	*hw = NULL;
 
 	struct v4l2_buffer buf = {0};
 	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	buf.memory = dev->io_method;
 
-	LOG_DEBUG("Grabbing device buffer ...");
-	if (xioctl(RUN(fd), VIDIOC_DQBUF, &buf) < 0) {
-		LOG_PERROR("Can't grab device buffer");
+	US_LOG_DEBUG("Grabbing device buffer ...");
+	if (_D_XIOCTL(VIDIOC_DQBUF, &buf) < 0) {
+		US_LOG_PERROR("Can't grab device buffer");
 		return -1;
 	}
 
-	LOG_DEBUG("Grabbed new frame: buffer=%u, bytesused=%u", buf.index, buf.bytesused);
+	US_LOG_DEBUG("Grabbed new frame: buffer=%u, bytesused=%u", buf.index, buf.bytesused);
 
-	if (buf.index >= RUN(n_bufs)) {
-		LOG_ERROR("V4L2 error: grabbed invalid device buffer=%u, n_bufs=%u", buf.index, RUN(n_bufs));
+	if (buf.index >= _RUN(n_bufs)) {
+		US_LOG_ERROR("V4L2 error: grabbed invalid device buffer=%u, n_bufs=%u", buf.index, _RUN(n_bufs));
 		return -1;
 	}
 
@@ -333,112 +332,112 @@ int device_grab_buffer(device_s *dev, hw_buffer_s **hw) {
 	// For example a VGA (640x480) webcam frame is normally >= 8kByte large,
 	// corrupted frames are smaller.
 	if (buf.bytesused < dev->min_frame_size) {
-		LOG_DEBUG("Dropped too small frame, assuming it was broken: buffer=%u, bytesused=%u",
+		US_LOG_DEBUG("Dropped too small frame, assuming it was broken: buffer=%u, bytesused=%u",
 			buf.index, buf.bytesused);
-		LOG_DEBUG("Releasing device buffer=%u (broken frame) ...", buf.index);
-		if (xioctl(RUN(fd), VIDIOC_QBUF, &buf) < 0) {
-			LOG_PERROR("Can't release device buffer=%u (broken frame)", buf.index);
+		US_LOG_DEBUG("Releasing device buffer=%u (broken frame) ...", buf.index);
+		if (_D_XIOCTL(VIDIOC_QBUF, &buf) < 0) {
+			US_LOG_PERROR("Can't release device buffer=%u (broken frame)", buf.index);
 			return -1;
 		}
 		return -2;
 	}
 
-#	define HW(_next) RUN(hw_bufs)[buf.index]._next
+#	define HW(x_next) _RUN(hw_bufs)[buf.index].x_next
 
 	if (HW(grabbed)) {
-		LOG_ERROR("V4L2 error: grabbed device buffer=%u is already used", buf.index);
+		US_LOG_ERROR("V4L2 error: grabbed device buffer=%u is already used", buf.index);
 		return -1;
 	}
 	HW(grabbed) = true;
 
 	HW(raw.dma_fd) = HW(dma_fd);
 	HW(raw.used) = buf.bytesused;
-	HW(raw.width) = RUN(width);
-	HW(raw.height) = RUN(height);
-	HW(raw.format) = RUN(format);
-	HW(raw.stride) = RUN(stride);
+	HW(raw.width) = _RUN(width);
+	HW(raw.height) = _RUN(height);
+	HW(raw.format) = _RUN(format);
+	HW(raw.stride) = _RUN(stride);
 	HW(raw.online) = true;
 	memcpy(&HW(buf), &buf, sizeof(struct v4l2_buffer));
-	HW(raw.grab_ts) = get_now_monotonic();
+	HW(raw.grab_ts) = us_get_now_monotonic();
 
 #	undef HW
-	*hw = &RUN(hw_bufs[buf.index]);
+	*hw = &_RUN(hw_bufs[buf.index]);
 	return buf.index;
 }
 
-int device_release_buffer(device_s *dev, hw_buffer_s *hw) {
+int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw) {
 	const unsigned index = hw->buf.index;
-	LOG_DEBUG("Releasing device buffer=%u ...", index);
+	US_LOG_DEBUG("Releasing device buffer=%u ...", index);
 
-	if (xioctl(RUN(fd), VIDIOC_QBUF, &hw->buf) < 0) {
-		LOG_PERROR("Can't release device buffer=%u", index);
+	if (_D_XIOCTL(VIDIOC_QBUF, &hw->buf) < 0) {
+		US_LOG_PERROR("Can't release device buffer=%u", index);
 		return -1;
 	}
 	hw->grabbed = false;
 	return 0;
 }
 
-int device_consume_event(device_s *dev) {
+int us_device_consume_event(us_device_s *dev) {
 	struct v4l2_event event;
 
-	LOG_DEBUG("Consuming V4L2 event ...");
-	if (xioctl(RUN(fd), VIDIOC_DQEVENT, &event) == 0) {
+	US_LOG_DEBUG("Consuming V4L2 event ...");
+	if (_D_XIOCTL(VIDIOC_DQEVENT, &event) == 0) {
 		switch (event.type) {
 			case V4L2_EVENT_SOURCE_CHANGE:
-				LOG_INFO("Got V4L2_EVENT_SOURCE_CHANGE: source changed");
+				US_LOG_INFO("Got V4L2_EVENT_SOURCE_CHANGE: source changed");
 				return -1;
 			case V4L2_EVENT_EOS:
-				LOG_INFO("Got V4L2_EVENT_EOS: end of stream (ignored)");
+				US_LOG_INFO("Got V4L2_EVENT_EOS: end of stream (ignored)");
 				return 0;
 		}
 	} else {
-		LOG_PERROR("Got some V4L2 device event, but where is it? ");
+		US_LOG_PERROR("Got some V4L2 device event, but where is it? ");
 	}
 	return 0;
 }
 
-static int _device_open_check_cap(device_s *dev) {
+static int _device_open_check_cap(us_device_s *dev) {
 	struct v4l2_capability cap = {0};
 
-	LOG_DEBUG("Querying device capabilities ...");
-	if (xioctl(RUN(fd), VIDIOC_QUERYCAP, &cap) < 0) {
-		LOG_PERROR("Can't query device capabilities");
+	US_LOG_DEBUG("Querying device capabilities ...");
+	if (_D_XIOCTL(VIDIOC_QUERYCAP, &cap) < 0) {
+		US_LOG_PERROR("Can't query device capabilities");
 		return -1;
 	}
 
 	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
-		LOG_ERROR("Video capture is not supported by device");
+		US_LOG_ERROR("Video capture is not supported by device");
 		return -1;
 	}
 
 	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
-		LOG_ERROR("Device doesn't support streaming IO");
+		US_LOG_ERROR("Device doesn't support streaming IO");
 		return -1;
 	}
 
 	int input = dev->input; // Needs a pointer to int for ioctl()
-	LOG_INFO("Using input channel: %d", input);
-	if (xioctl(RUN(fd), VIDIOC_S_INPUT, &input) < 0) {
-		LOG_ERROR("Can't set input channel");
+	US_LOG_INFO("Using input channel: %d", input);
+	if (_D_XIOCTL(VIDIOC_S_INPUT, &input) < 0) {
+		US_LOG_ERROR("Can't set input channel");
 		return -1;
 	}
 
 	if (dev->standard != V4L2_STD_UNKNOWN) {
-		LOG_INFO("Using TV standard: %s", _standard_to_string(dev->standard));
-		if (xioctl(RUN(fd), VIDIOC_S_STD, &dev->standard) < 0) {
-			LOG_ERROR("Can't set video standard");
+		US_LOG_INFO("Using TV standard: %s", _standard_to_string(dev->standard));
+		if (_D_XIOCTL(VIDIOC_S_STD, &dev->standard) < 0) {
+			US_LOG_ERROR("Can't set video standard");
 			return -1;
 		}
 	} else {
-		LOG_DEBUG("Using TV standard: DEFAULT");
+		US_LOG_DEBUG("Using TV standard: DEFAULT");
 	}
 	return 0;
 }
 
-static int _device_open_dv_timings(device_s *dev) {
+static int _device_open_dv_timings(us_device_s *dev) {
 	_device_apply_resolution(dev, dev->width, dev->height);
 	if (dev->dv_timings) {
-		LOG_DEBUG("Using DV-timings");
+		US_LOG_DEBUG("Using DV-timings");
 
 		if (_device_apply_dv_timings(dev) < 0) {
 			return -1;
@@ -447,40 +446,37 @@ static int _device_open_dv_timings(device_s *dev) {
 		struct v4l2_event_subscription sub = {0};
 		sub.type = V4L2_EVENT_SOURCE_CHANGE;
 
-		LOG_DEBUG("Subscribing to DV-timings events ...")
-		if (xioctl(RUN(fd), VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) {
-			LOG_PERROR("Can't subscribe to DV-timings events");
+		US_LOG_DEBUG("Subscribing to DV-timings events ...")
+		if (_D_XIOCTL(VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) {
+			US_LOG_PERROR("Can't subscribe to DV-timings events");
 			return -1;
 		}
 	}
 	return 0;
 }
 
-static int _device_apply_dv_timings(device_s *dev) {
+static int _device_apply_dv_timings(us_device_s *dev) {
 	struct v4l2_dv_timings dv = {0};
 
-	LOG_DEBUG("Calling ioctl(VIDIOC_QUERY_DV_TIMINGS) ...");
-	if (xioctl(RUN(fd), VIDIOC_QUERY_DV_TIMINGS, &dv) == 0) {
+	US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERY_DV_TIMINGS) ...");
+	if (_D_XIOCTL(VIDIOC_QUERY_DV_TIMINGS, &dv) == 0) {
 		if (dv.type == V4L2_DV_BT_656_1120) {
 			// See v4l2_print_dv_timings() in the kernel
-			unsigned htot = V4L2_DV_BT_FRAME_WIDTH(&dv.bt);
-			unsigned vtot = V4L2_DV_BT_FRAME_HEIGHT(&dv.bt);
-			if (dv.bt.interlaced) {
-				vtot /= 2;
-			}
-			unsigned fps = ((htot * vtot) > 0 ? ((100 * (uint64_t)dv.bt.pixelclock)) / (htot * vtot) : 0);
-			LOG_INFO("Got new DV-timings: %ux%u%s%u.%02u, pixclk=%llu, vsync=%u, hsync=%u",
+			const unsigned htot = V4L2_DV_BT_FRAME_WIDTH(&dv.bt);
+			const unsigned vtot = V4L2_DV_BT_FRAME_HEIGHT(&dv.bt) / (dv.bt.interlaced ? 2 : 1);
+			const unsigned fps = ((htot * vtot) > 0 ? ((100 * (uint64_t)dv.bt.pixelclock)) / (htot * vtot) : 0);
+			US_LOG_INFO("Got new DV-timings: %ux%u%s%u.%02u, pixclk=%llu, vsync=%u, hsync=%u",
 				dv.bt.width, dv.bt.height, (dv.bt.interlaced ? "i" : "p"), fps / 100, fps % 100,
 				(unsigned long long)dv.bt.pixelclock, dv.bt.vsync, dv.bt.hsync); // See #11 about %llu
 		} else {
-			LOG_INFO("Got new DV-timings: %ux%u, pixclk=%llu, vsync=%u, hsync=%u",
+			US_LOG_INFO("Got new DV-timings: %ux%u, pixclk=%llu, vsync=%u, hsync=%u",
 				dv.bt.width, dv.bt.height,
 				(unsigned long long)dv.bt.pixelclock, dv.bt.vsync, dv.bt.hsync);
 		}
 
-		LOG_DEBUG("Calling ioctl(VIDIOC_S_DV_TIMINGS) ...");
-		if (xioctl(RUN(fd), VIDIOC_S_DV_TIMINGS, &dv) < 0) {
-			LOG_PERROR("Failed to set DV-timings");
+		US_LOG_DEBUG("Calling us_xioctl(VIDIOC_S_DV_TIMINGS) ...");
+		if (_D_XIOCTL(VIDIOC_S_DV_TIMINGS, &dv) < 0) {
+			US_LOG_PERROR("Failed to set DV-timings");
 			return -1;
 		}
 
@@ -489,11 +485,11 @@ static int _device_apply_dv_timings(device_s *dev) {
 		}
 
 	} else {
-		LOG_DEBUG("Calling ioctl(VIDIOC_QUERYSTD) ...");
-		if (xioctl(RUN(fd), VIDIOC_QUERYSTD, &dev->standard) == 0) {
-			LOG_INFO("Applying the new VIDIOC_S_STD: %s ...", _standard_to_string(dev->standard));
-			if (xioctl(RUN(fd), VIDIOC_S_STD, &dev->standard) < 0) {
-				LOG_PERROR("Can't set video standard");
+		US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERYSTD) ...");
+		if (_D_XIOCTL(VIDIOC_QUERYSTD, &dev->standard) == 0) {
+			US_LOG_INFO("Applying the new VIDIOC_S_STD: %s ...", _standard_to_string(dev->standard));
+			if (_D_XIOCTL(VIDIOC_S_STD, &dev->standard) < 0) {
+				US_LOG_PERROR("Can't set video standard");
 				return -1;
 			}
 		}
@@ -501,29 +497,29 @@ static int _device_apply_dv_timings(device_s *dev) {
 	return 0;
 }
 
-static int _device_open_format(device_s *dev, bool first) {
-	const unsigned stride = align_size(RUN(width), 32) << 1;
+static int _device_open_format(us_device_s *dev, bool first) {
+	const unsigned stride = us_align_size(_RUN(width), 32) << 1;
 
 	struct v4l2_format fmt = {0};
 	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	fmt.fmt.pix.width = RUN(width);
-	fmt.fmt.pix.height = RUN(height);
+	fmt.fmt.pix.width = _RUN(width);
+	fmt.fmt.pix.height = _RUN(height);
 	fmt.fmt.pix.pixelformat = dev->format;
 	fmt.fmt.pix.field = V4L2_FIELD_ANY;
 	fmt.fmt.pix.bytesperline = stride;
 
 	// Set format
-	LOG_DEBUG("Probing device format=%s, stride=%u, resolution=%ux%u ...",
-		_format_to_string_supported(dev->format), stride, RUN(width), RUN(height));
-	if (xioctl(RUN(fd), VIDIOC_S_FMT, &fmt) < 0) {
-		LOG_PERROR("Can't set device format");
+	US_LOG_DEBUG("Probing device format=%s, stride=%u, resolution=%ux%u ...",
+		_format_to_string_supported(dev->format), stride, _RUN(width), _RUN(height));
+	if (_D_XIOCTL(VIDIOC_S_FMT, &fmt) < 0) {
+		US_LOG_PERROR("Can't set device format");
 		return -1;
 	}
 
 	// Check resolution
 	bool retry = false;
-	if (fmt.fmt.pix.width != RUN(width) || fmt.fmt.pix.height != RUN(height)) {
-		LOG_ERROR("Requested resolution=%ux%u is unavailable", RUN(width), RUN(height));
+	if (fmt.fmt.pix.width != _RUN(width) || fmt.fmt.pix.height != _RUN(height)) {
+		US_LOG_ERROR("Requested resolution=%ux%u is unavailable", _RUN(width), _RUN(height));
 		retry = true;
 	}
 	if (_device_apply_resolution(dev, fmt.fmt.pix.width, fmt.fmt.pix.height) < 0) {
@@ -532,109 +528,109 @@ static int _device_open_format(device_s *dev, bool first) {
 	if (first && retry) {
 		return _device_open_format(dev, false);
 	}
-	LOG_INFO("Using resolution: %ux%u", RUN(width), RUN(height));
+	US_LOG_INFO("Using resolution: %ux%u", _RUN(width), _RUN(height));
 
 	// Check format
 	if (fmt.fmt.pix.pixelformat != dev->format) {
-		LOG_ERROR("Could not obtain the requested format=%s; driver gave us %s",
+		US_LOG_ERROR("Could not obtain the requested format=%s; driver gave us %s",
 			_format_to_string_supported(dev->format),
 			_format_to_string_supported(fmt.fmt.pix.pixelformat));
 
 		char *format_str;
 		if ((format_str = (char *)_format_to_string_nullable(fmt.fmt.pix.pixelformat)) != NULL) {
-			LOG_INFO("Falling back to format=%s", format_str);
+			US_LOG_INFO("Falling back to format=%s", format_str);
 		} else {
 			char fourcc_str[8];
-			LOG_ERROR("Unsupported format=%s (fourcc)",
-				fourcc_to_string(fmt.fmt.pix.pixelformat, fourcc_str, 8));
+			US_LOG_ERROR("Unsupported format=%s (fourcc)",
+				us_fourcc_to_string(fmt.fmt.pix.pixelformat, fourcc_str, 8));
 			return -1;
 		}
 	}
 
-	RUN(format) = fmt.fmt.pix.pixelformat;
-	LOG_INFO("Using format: %s", _format_to_string_supported(RUN(format)));
+	_RUN(format) = fmt.fmt.pix.pixelformat;
+	US_LOG_INFO("Using format: %s", _format_to_string_supported(_RUN(format)));
 
-	RUN(stride) = fmt.fmt.pix.bytesperline;
-	RUN(raw_size) = fmt.fmt.pix.sizeimage; // Only for userptr
+	_RUN(stride) = fmt.fmt.pix.bytesperline;
+	_RUN(raw_size) = fmt.fmt.pix.sizeimage; // Only for userptr
 	return 0;
 }
 
-static void _device_open_hw_fps(device_s *dev) {
-	RUN(hw_fps) = 0;
+static void _device_open_hw_fps(us_device_s *dev) {
+	_RUN(hw_fps) = 0;
 
 	struct v4l2_streamparm setfps = {0};
 	setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 
-	LOG_DEBUG("Querying HW FPS ...");
-	if (xioctl(RUN(fd), VIDIOC_G_PARM, &setfps) < 0) {
+	US_LOG_DEBUG("Querying HW FPS ...");
+	if (_D_XIOCTL(VIDIOC_G_PARM, &setfps) < 0) {
 		if (errno == ENOTTY) { // Quiet message for TC358743
-			LOG_INFO("Querying HW FPS changing is not supported");
+			US_LOG_INFO("Querying HW FPS changing is not supported");
 		} else {
-			LOG_PERROR("Can't query HW FPS changing");
+			US_LOG_PERROR("Can't query HW FPS changing");
 		}
 		return;
 	}
 
 	if (!(setfps.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
-		LOG_INFO("Changing HW FPS is not supported");
+		US_LOG_INFO("Changing HW FPS is not supported");
 		return;
 	}
 
-#	define SETFPS_TPF(_next) setfps.parm.capture.timeperframe._next
+#	define SETFPS_TPF(x_next) setfps.parm.capture.timeperframe.x_next
 
-	MEMSET_ZERO(setfps);
+	US_MEMSET_ZERO(setfps);
 	setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	SETFPS_TPF(numerator) = 1;
 	SETFPS_TPF(denominator) = (dev->desired_fps == 0 ? 255 : dev->desired_fps);
 
-	if (xioctl(RUN(fd), VIDIOC_S_PARM, &setfps) < 0) {
-		LOG_PERROR("Can't set HW FPS");
+	if (_D_XIOCTL(VIDIOC_S_PARM, &setfps) < 0) {
+		US_LOG_PERROR("Can't set HW FPS");
 		return;
 	}
 
 	if (SETFPS_TPF(numerator) != 1) {
-		LOG_ERROR("Invalid HW FPS numerator: %u != 1", SETFPS_TPF(numerator));
+		US_LOG_ERROR("Invalid HW FPS numerator: %u != 1", SETFPS_TPF(numerator));
 		return;
 	}
 
 	if (SETFPS_TPF(denominator) == 0) { // Не знаю, бывает ли так, но пускай на всякий случай
-		LOG_ERROR("Invalid HW FPS denominator: 0");
+		US_LOG_ERROR("Invalid HW FPS denominator: 0");
 		return;
 	}
 
-	RUN(hw_fps) = SETFPS_TPF(denominator);
-	if (dev->desired_fps != RUN(hw_fps)) {
-		LOG_INFO("Using HW FPS: %u -> %u (coerced)", dev->desired_fps, RUN(hw_fps));
+	_RUN(hw_fps) = SETFPS_TPF(denominator);
+	if (dev->desired_fps != _RUN(hw_fps)) {
+		US_LOG_INFO("Using HW FPS: %u -> %u (coerced)", dev->desired_fps, _RUN(hw_fps));
 	} else {
-		LOG_INFO("Using HW FPS: %u", RUN(hw_fps));
+		US_LOG_INFO("Using HW FPS: %u", _RUN(hw_fps));
 	}
 
 #	undef SETFPS_TPF
 }
 
-static void _device_open_jpeg_quality(device_s *dev) {
+static void _device_open_jpeg_quality(us_device_s *dev) {
 	unsigned quality = 0;
 
-	if (is_jpeg(RUN(format))) {
+	if (us_is_jpeg(_RUN(format))) {
 		struct v4l2_jpegcompression comp = {0};
 
-		if (xioctl(RUN(fd), VIDIOC_G_JPEGCOMP, &comp) < 0) {
-			LOG_ERROR("Device doesn't support setting of HW encoding quality parameters");
+		if (_D_XIOCTL(VIDIOC_G_JPEGCOMP, &comp) < 0) {
+			US_LOG_ERROR("Device doesn't support setting of HW encoding quality parameters");
 		} else {
 			comp.quality = dev->jpeg_quality;
-			if (xioctl(RUN(fd), VIDIOC_S_JPEGCOMP, &comp) < 0) {
-				LOG_ERROR("Can't change MJPEG quality for JPEG source with HW pass-through encoder");
+			if (_D_XIOCTL(VIDIOC_S_JPEGCOMP, &comp) < 0) {
+				US_LOG_ERROR("Can't change MJPEG quality for JPEG source with HW pass-through encoder");
 			} else {
 				quality = dev->jpeg_quality;
 			}
 		}
 	}
 
-	RUN(jpeg_quality) = quality;
+	_RUN(jpeg_quality) = quality;
 }
 
-static int _device_open_io_method(device_s *dev) {
-	LOG_INFO("Using IO method: %s", _io_method_to_string_supported(dev->io_method));
+static int _device_open_io_method(us_device_s *dev) {
+	US_LOG_INFO("Using IO method: %s", _io_method_to_string_supported(dev->io_method));
 	switch (dev->io_method) {
 		case V4L2_MEMORY_MMAP: return _device_open_io_method_mmap(dev);
 		case V4L2_MEMORY_USERPTR: return _device_open_io_method_userptr(dev);
@@ -643,56 +639,57 @@ static int _device_open_io_method(device_s *dev) {
 	return -1;
 }
 
-static int _device_open_io_method_mmap(device_s *dev) {
+static int _device_open_io_method_mmap(us_device_s *dev) {
 	struct v4l2_requestbuffers req = {0};
 	req.count = dev->n_bufs;
 	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	req.memory = V4L2_MEMORY_MMAP;
 
-	LOG_DEBUG("Requesting %u device buffers for MMAP ...", req.count);
-	if (xioctl(RUN(fd), VIDIOC_REQBUFS, &req) < 0) {
-		LOG_PERROR("Device '%s' doesn't support MMAP method", dev->path);
+	US_LOG_DEBUG("Requesting %u device buffers for MMAP ...", req.count);
+	if (_D_XIOCTL(VIDIOC_REQBUFS, &req) < 0) {
+		US_LOG_PERROR("Device '%s' doesn't support MMAP method", dev->path);
 		return -1;
 	}
 
 	if (req.count < 1) {
-		LOG_ERROR("Insufficient buffer memory: %u", req.count);
+		US_LOG_ERROR("Insufficient buffer memory: %u", req.count);
 		return -1;
 	} else {
-		LOG_INFO("Requested %u device buffers, got %u", dev->n_bufs, req.count);
+		US_LOG_INFO("Requested %u device buffers, got %u", dev->n_bufs, req.count);
 	}
 
-	LOG_DEBUG("Allocating device buffers ...");
+	US_LOG_DEBUG("Allocating device buffers ...");
 
-	A_CALLOC(RUN(hw_bufs), req.count);
-	for (RUN(n_bufs) = 0; RUN(n_bufs) < req.count; ++RUN(n_bufs)) {
+	US_CALLOC(_RUN(hw_bufs), req.count);
+	for (_RUN(n_bufs) = 0; _RUN(n_bufs) < req.count; ++_RUN(n_bufs)) {
 		struct v4l2_buffer buf = {0};
 		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 		buf.memory = V4L2_MEMORY_MMAP;
-		buf.index = RUN(n_bufs);
+		buf.index = _RUN(n_bufs);
 
-		LOG_DEBUG("Calling ioctl(VIDIOC_QUERYBUF) for device buffer=%u ...", RUN(n_bufs));
-		if (xioctl(RUN(fd), VIDIOC_QUERYBUF, &buf) < 0) {
-			LOG_PERROR("Can't VIDIOC_QUERYBUF");
+		US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERYBUF) for device buffer=%u ...", _RUN(n_bufs));
+		if (_D_XIOCTL(VIDIOC_QUERYBUF, &buf) < 0) {
+			US_LOG_PERROR("Can't VIDIOC_QUERYBUF");
 			return -1;
 		}
 
-#		define HW(_next) RUN(hw_bufs)[RUN(n_bufs)]._next
+#		define HW(x_next) _RUN(hw_bufs)[_RUN(n_bufs)].x_next
 
 		HW(dma_fd) = -1;
 
-		LOG_DEBUG("Mapping device buffer=%u ...", RUN(n_bufs));
+		US_LOG_DEBUG("Mapping device buffer=%u ...", _RUN(n_bufs));
 		if ((HW(raw.data) = mmap(
 			NULL,
 			buf.length,
 			PROT_READ | PROT_WRITE,
 			MAP_SHARED,
-			RUN(fd),
+			_RUN(fd),
 			buf.m.offset
 		)) == MAP_FAILED) {
-			LOG_PERROR("Can't map device buffer=%u", RUN(n_bufs));
+			US_LOG_PERROR("Can't map device buffer=%u", _RUN(n_bufs));
 			return -1;
 		}
+		assert(HW(raw.data) != NULL);
 		HW(raw.allocated) = buf.length;
 
 #		undef HW
@@ -700,35 +697,35 @@ static int _device_open_io_method_mmap(device_s *dev) {
 	return 0;
 }
 
-static int _device_open_io_method_userptr(device_s *dev) {
+static int _device_open_io_method_userptr(us_device_s *dev) {
 	struct v4l2_requestbuffers req = {0};
 	req.count = dev->n_bufs;
 	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 	req.memory = V4L2_MEMORY_USERPTR;
 
-	LOG_DEBUG("Requesting %u device buffers for USERPTR ...", req.count);
-	if (xioctl(RUN(fd), VIDIOC_REQBUFS, &req) < 0) {
-		LOG_PERROR("Device '%s' doesn't support USERPTR method", dev->path);
+	US_LOG_DEBUG("Requesting %u device buffers for USERPTR ...", req.count);
+	if (_D_XIOCTL(VIDIOC_REQBUFS, &req) < 0) {
+		US_LOG_PERROR("Device '%s' doesn't support USERPTR method", dev->path);
 		return -1;
 	}
 
 	if (req.count < 1) {
-		LOG_ERROR("Insufficient buffer memory: %u", req.count);
+		US_LOG_ERROR("Insufficient buffer memory: %u", req.count);
 		return -1;
 	} else {
-		LOG_INFO("Requested %u device buffers, got %u", dev->n_bufs, req.count);
+		US_LOG_INFO("Requested %u device buffers, got %u", dev->n_bufs, req.count);
 	}
 
-	LOG_DEBUG("Allocating device buffers ...");
+	US_LOG_DEBUG("Allocating device buffers ...");
 
-	A_CALLOC(RUN(hw_bufs), req.count);
+	US_CALLOC(_RUN(hw_bufs), req.count);
 
 	const unsigned page_size = getpagesize();
-	const unsigned buf_size = align_size(RUN(raw_size), page_size);
+	const unsigned buf_size = us_align_size(_RUN(raw_size), page_size);
 
-	for (RUN(n_bufs) = 0; RUN(n_bufs) < req.count; ++RUN(n_bufs)) {
-#       define HW(_next) RUN(hw_bufs)[RUN(n_bufs)]._next
-		assert(HW(raw.data) = aligned_alloc(page_size, buf_size));
+	for (_RUN(n_bufs) = 0; _RUN(n_bufs) < req.count; ++_RUN(n_bufs)) {
+#       define HW(x_next) _RUN(hw_bufs)[_RUN(n_bufs)].x_next
+		assert((HW(raw.data) = aligned_alloc(page_size, buf_size)) != NULL);
 		memset(HW(raw.data), 0, buf_size);
 		HW(raw.allocated) = buf_size;
 #		undef HW
@@ -736,75 +733,75 @@ static int _device_open_io_method_userptr(device_s *dev) {
 	return 0;
 }
 
-static int _device_open_queue_buffers(device_s *dev) {
-	for (unsigned index = 0; index < RUN(n_bufs); ++index) {
+static int _device_open_queue_buffers(us_device_s *dev) {
+	for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
 		struct v4l2_buffer buf = {0};
 		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 		buf.memory = dev->io_method;
 		buf.index = index;
 		if (dev->io_method == V4L2_MEMORY_USERPTR) {
-			buf.m.userptr = (unsigned long)RUN(hw_bufs)[index].raw.data;
-			buf.length = RUN(hw_bufs)[index].raw.allocated;
+			buf.m.userptr = (unsigned long)_RUN(hw_bufs)[index].raw.data;
+			buf.length = _RUN(hw_bufs)[index].raw.allocated;
 		}
 
-		LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) for buffer=%u ...", index);
-		if (xioctl(RUN(fd), VIDIOC_QBUF, &buf) < 0) {
-			LOG_PERROR("Can't VIDIOC_QBUF");
+		US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QBUF) for buffer=%u ...", index);
+		if (_D_XIOCTL(VIDIOC_QBUF, &buf) < 0) {
+			US_LOG_PERROR("Can't VIDIOC_QBUF");
 			return -1;
 		}
 	}
 	return 0;
 }
 
-static int _device_apply_resolution(device_s *dev, unsigned width, unsigned height) {
+static int _device_apply_resolution(us_device_s *dev, unsigned width, unsigned height) {
 	// Тут VIDEO_MIN_* не используются из-за странностей минимального разрешения при отсутствии сигнала
 	// у некоторых устройств, например TC358743
 	if (
-		width == 0 || width > VIDEO_MAX_WIDTH
-		|| height == 0 || height > VIDEO_MAX_HEIGHT
+		width == 0 || width > US_VIDEO_MAX_WIDTH
+		|| height == 0 || height > US_VIDEO_MAX_HEIGHT
 	) {
-		LOG_ERROR("Requested forbidden resolution=%ux%u: min=1x1, max=%ux%u",
-			width, height, VIDEO_MAX_WIDTH, VIDEO_MAX_HEIGHT);
+		US_LOG_ERROR("Requested forbidden resolution=%ux%u: min=1x1, max=%ux%u",
+			width, height, US_VIDEO_MAX_WIDTH, US_VIDEO_MAX_HEIGHT);
 		return -1;
 	}
-	RUN(width) = width;
-	RUN(height) = height;
+	_RUN(width) = width;
+	_RUN(height) = height;
 	return 0;
 }
 
-static void _device_apply_controls(device_s *dev) {
-#	define SET_CID_VALUE(_cid, _field, _value, _quiet) { \
-			struct v4l2_queryctrl query; \
-			if (_device_query_control(dev, &query, #_field, _cid, _quiet) == 0) { \
-				_device_set_control(dev, &query, #_field, _cid, _value, _quiet); \
+static void _device_apply_controls(us_device_s *dev) {
+#	define SET_CID_VALUE(x_cid, x_field, x_value, x_quiet) { \
+			struct v4l2_queryctrl m_query; \
+			if (_device_query_control(dev, &m_query, #x_field, x_cid, x_quiet) == 0) { \
+				_device_set_control(dev, &m_query, #x_field, x_cid, x_value, x_quiet); \
 			} \
 		}
 
-#	define SET_CID_DEFAULT(_cid, _field, _quiet) { \
-			struct v4l2_queryctrl query; \
-			if (_device_query_control(dev, &query, #_field, _cid, _quiet) == 0) { \
-				_device_set_control(dev, &query, #_field, _cid, query.default_value, _quiet); \
+#	define SET_CID_DEFAULT(x_cid, x_field, x_quiet) { \
+			struct v4l2_queryctrl m_query; \
+			if (_device_query_control(dev, &m_query, #x_field, x_cid, x_quiet) == 0) { \
+				_device_set_control(dev, &m_query, #x_field, x_cid, m_query.default_value, x_quiet); \
 			} \
 		}
 
-#	define CONTROL_MANUAL_CID(_cid, _field) { \
-			if (dev->ctl._field.mode == CTL_MODE_VALUE) { \
-				SET_CID_VALUE(_cid, _field, dev->ctl._field.value, false); \
-			} else if (dev->ctl._field.mode == CTL_MODE_DEFAULT) { \
-				SET_CID_DEFAULT(_cid, _field, false); \
+#	define CONTROL_MANUAL_CID(x_cid, x_field) { \
+			if (dev->ctl.x_field.mode == CTL_MODE_VALUE) { \
+				SET_CID_VALUE(x_cid, x_field, dev->ctl.x_field.value, false); \
+			} else if (dev->ctl.x_field.mode == CTL_MODE_DEFAULT) { \
+				SET_CID_DEFAULT(x_cid, x_field, false); \
 			} \
 		}
 
-#	define CONTROL_AUTO_CID(_cid_auto, _cid_manual, _field) { \
-			if (dev->ctl._field.mode == CTL_MODE_VALUE) { \
-				SET_CID_VALUE(_cid_auto, _field##_auto, 0, true); \
-				SET_CID_VALUE(_cid_manual, _field, dev->ctl._field.value, false); \
-			} else if (dev->ctl._field.mode == CTL_MODE_AUTO) { \
-				SET_CID_VALUE(_cid_auto, _field##_auto, 1, false); \
-			} else if (dev->ctl._field.mode == CTL_MODE_DEFAULT) { \
-				SET_CID_VALUE(_cid_auto, _field##_auto, 0, true); /* Reset inactive flag */ \
-				SET_CID_DEFAULT(_cid_manual, _field, false); \
-				SET_CID_DEFAULT(_cid_auto, _field##_auto, false); \
+#	define CONTROL_AUTO_CID(x_cid_auto, x_cid_manual, x_field) { \
+			if (dev->ctl.x_field.mode == CTL_MODE_VALUE) { \
+				SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); \
+				SET_CID_VALUE(x_cid_manual, x_field, dev->ctl.x_field.value, false); \
+			} else if (dev->ctl.x_field.mode == CTL_MODE_AUTO) { \
+				SET_CID_VALUE(x_cid_auto, x_field##_auto, 1, false); \
+			} else if (dev->ctl.x_field.mode == CTL_MODE_DEFAULT) { \
+				SET_CID_VALUE(x_cid_auto, x_field##_auto, 0, true); /* Reset inactive flag */ \
+				SET_CID_DEFAULT(x_cid_manual, x_field, false); \
+				SET_CID_DEFAULT(x_cid_auto, x_field##_auto, false); \
 			} \
 		}
 
@@ -829,16 +826,16 @@ static void _device_apply_controls(device_s *dev) {
 }
 
 static int _device_query_control(
-	device_s *dev, struct v4l2_queryctrl *query,
+	us_device_s *dev, struct v4l2_queryctrl *query,
 	const char *name, unsigned cid, bool quiet) {
 
 	// cppcheck-suppress redundantPointerOp
-	MEMSET_ZERO(*query);
+	US_MEMSET_ZERO(*query);
 	query->id = cid;
 
-	if (xioctl(RUN(fd), VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) {
+	if (_D_XIOCTL(VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) {
 		if (!quiet) {
-			LOG_ERROR("Changing control %s is unsupported", name);
+			US_LOG_ERROR("Changing control %s is unsupported", name);
 		}
 		return -1;
 	}
@@ -846,12 +843,12 @@ static int _device_query_control(
 }
 
 static void _device_set_control(
-	device_s *dev, struct v4l2_queryctrl *query,
+	us_device_s *dev, struct v4l2_queryctrl *query,
 	const char *name, unsigned cid, int value, bool quiet) {
 
 	if (value < query->minimum || value > query->maximum || value % query->step != 0) {
 		if (!quiet) {
-			LOG_ERROR("Invalid value %d of control %s: min=%d, max=%d, default=%d, step=%u",
+			US_LOG_ERROR("Invalid value %d of control %s: min=%d, max=%d, default=%d, step=%u",
 				value, name, query->minimum, query->maximum, query->default_value, query->step);
 		}
 		return;
@@ -861,45 +858,43 @@ static void _device_set_control(
 	ctl.id = cid;
 	ctl.value = value;
 
-	if (xioctl(RUN(fd), VIDIOC_S_CTRL, &ctl) < 0) {
+	if (_D_XIOCTL(VIDIOC_S_CTRL, &ctl) < 0) {
 		if (!quiet) {
-			LOG_PERROR("Can't set control %s", name);
+			US_LOG_PERROR("Can't set control %s", name);
 		}
 	} else if (!quiet) {
-		LOG_INFO("Applying control %s: %d", name, ctl.value);
+		US_LOG_INFO("Applying control %s: %d", name, ctl.value);
 	}
 }
 
 static const char *_format_to_string_nullable(unsigned format) {
-	for (unsigned index = 0; index < ARRAY_LEN(_FORMATS); ++index) {
-		if (format == _FORMATS[index].format) {
-			return _FORMATS[index].name;
+	US_ARRAY_ITERATE(_FORMATS, 0, item, {
+		if (item->format == format) {
+			return item->name;
 		}
-	}
+	});
 	return NULL;
 }
 
 static const char *_format_to_string_supported(unsigned format) {
-	const char *format_str = _format_to_string_nullable(format);
+	const char *const format_str = _format_to_string_nullable(format);
 	return (format_str == NULL ? "unsupported" : format_str);
 }
 
 static const char *_standard_to_string(v4l2_std_id standard) {
-	for (unsigned index = 0; index < ARRAY_LEN(_STANDARDS); ++index) {
-		if (standard == _STANDARDS[index].standard) {
-			return _STANDARDS[index].name;
+	US_ARRAY_ITERATE(_STANDARDS, 0, item, {
+		if (item->standard == standard) {
+			return item->name;
 		}
-	}
+	});
 	return _STANDARDS[0].name;
 }
 
 static const char *_io_method_to_string_supported(enum v4l2_memory io_method) {
-	for (unsigned index = 0; index < ARRAY_LEN(_IO_METHODS); ++index) {
-		if (io_method == _IO_METHODS[index].io_method) {
-			return _IO_METHODS[index].name;
+	US_ARRAY_ITERATE(_IO_METHODS, 0, item, {
+		if (item->io_method == io_method) {
+			return item->name;
 		}
-	}
+	});
 	return "unsupported";
 }
-
-#	undef RUN
diff --git a/src/ustreamer/device.h b/src/ustreamer/device.h
index 63a8bc9..252ba23 100644
--- a/src/ustreamer/device.h
+++ b/src/ustreamer/device.h
@@ -42,80 +42,80 @@
 #include <linux/v4l2-controls.h>
 
 #include "../libs/tools.h"
+#include "../libs/array.h"
 #include "../libs/logging.h"
 #include "../libs/threading.h"
 #include "../libs/frame.h"
+#include "../libs/xioctl.h"
 
-#include "xioctl.h"
 
+#define US_VIDEO_MIN_WIDTH		((unsigned)160)
+#define US_VIDEO_MAX_WIDTH		((unsigned)10240)
 
-#define VIDEO_MIN_WIDTH		((unsigned)160)
-#define VIDEO_MAX_WIDTH		((unsigned)10240)
+#define US_VIDEO_MIN_HEIGHT		((unsigned)120)
+#define US_VIDEO_MAX_HEIGHT		((unsigned)4320)
 
-#define VIDEO_MIN_HEIGHT	((unsigned)120)
-#define VIDEO_MAX_HEIGHT	((unsigned)4320)
+#define US_VIDEO_MAX_FPS		((unsigned)120)
 
-#define VIDEO_MAX_FPS		((unsigned)120)
+#define US_STANDARD_UNKNOWN		V4L2_STD_UNKNOWN
+#define US_STANDARDS_STR		"PAL, NTSC, SECAM"
 
-#define STANDARD_UNKNOWN	V4L2_STD_UNKNOWN
-#define STANDARDS_STR		"PAL, NTSC, SECAM"
+#define US_FORMAT_UNKNOWN		-1
+#define US_FORMATS_STR			"YUYV, UYVY, RGB565, RGB24, MJPEG, JPEG"
 
-#define FORMAT_UNKNOWN	-1
-#define FORMATS_STR		"YUYV, UYVY, RGB565, RGB24, MJPEG, JPEG"
-
-#define IO_METHOD_UNKNOWN	-1
-#define IO_METHODS_STR		"MMAP, USERPTR"
+#define US_IO_METHOD_UNKNOWN	-1
+#define US_IO_METHODS_STR		"MMAP, USERPTR"
 
 
 typedef struct {
-	frame_s				raw;
+	us_frame_s			raw;
 	struct v4l2_buffer	buf;
 	int					dma_fd;
 	bool				grabbed;
-} hw_buffer_s;
+} us_hw_buffer_s;
 
 typedef struct {
-	int			fd;
-	unsigned	width;
-	unsigned	height;
-	unsigned	format;
-	unsigned	stride;
-	unsigned	hw_fps;
-	unsigned	jpeg_quality;
-	size_t		raw_size;
-	unsigned	n_bufs;
-	hw_buffer_s	*hw_bufs;
-	bool		capturing;
-	bool		persistent_timeout_reported;
-} device_runtime_s;
+	int				fd;
+	unsigned		width;
+	unsigned		height;
+	unsigned		format;
+	unsigned		stride;
+	unsigned		hw_fps;
+	unsigned		jpeg_quality;
+	size_t			raw_size;
+	unsigned		n_bufs;
+	us_hw_buffer_s	*hw_bufs;
+	bool			capturing;
+	bool			persistent_timeout_reported;
+} us_device_runtime_s;
 
 typedef enum {
 	CTL_MODE_NONE = 0,
 	CTL_MODE_VALUE,
 	CTL_MODE_AUTO,
 	CTL_MODE_DEFAULT,
-} control_mode_e;
+} us_control_mode_e;
 
 typedef struct {
-	control_mode_e	mode;
-	int				value;
-} control_s;
+	us_control_mode_e	mode;
+	int					value;
+} us_control_s;
 
 typedef struct {
-	control_s brightness;
-	control_s contrast;
-	control_s saturation;
-	control_s hue;
-	control_s gamma;
-	control_s sharpness;
-	control_s backlight_compensation;
-	control_s white_balance;
-	control_s gain;
-	control_s color_effect;
-	control_s rotate;
-	control_s flip_vertical;
-	control_s flip_horizontal;
-} controls_s;
+	us_control_s	brightness;
+	us_control_s	contrast;
+	us_control_s	saturation;
+	us_control_s	hue;
+	us_control_s	gamma;
+	us_control_s	sharpness;
+	us_control_s	backlight_compensation;
+	us_control_s	white_balance;
+	us_control_s	gain;
+	us_control_s	color_effect;
+	us_control_s	rotate;
+	us_control_s	flip_vertical;
+	us_control_s	flip_horizontal;
+} us_controls_s;
 
 typedef struct {
 	char				*path;
@@ -133,25 +133,25 @@ typedef struct {
 	bool				persistent;
 	unsigned			timeout;
 
-	controls_s ctl;
+	us_controls_s 		ctl;
 
-	device_runtime_s *run;
-} device_s;
+	us_device_runtime_s *run;
+} us_device_s;
 
 
-device_s *device_init(void);
-void device_destroy(device_s *dev);
+us_device_s *us_device_init(void);
+void us_device_destroy(us_device_s *dev);
 
-int device_parse_format(const char *str);
-v4l2_std_id device_parse_standard(const char *str);
-int device_parse_io_method(const char *str);
+int us_device_parse_format(const char *str);
+v4l2_std_id us_device_parse_standard(const char *str);
+int us_device_parse_io_method(const char *str);
 
-int device_open(device_s *dev);
-void device_close(device_s *dev);
+int us_device_open(us_device_s *dev);
+void us_device_close(us_device_s *dev);
 
-int device_export_to_dma(device_s *dev);
-int device_switch_capturing(device_s *dev, bool enable);
-int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_error);
-int device_grab_buffer(device_s *dev, hw_buffer_s **hw);
-int device_release_buffer(device_s *dev, hw_buffer_s *hw);
-int device_consume_event(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_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);
diff --git a/src/ustreamer/encoder.c b/src/ustreamer/encoder.c
index f01b9a2..9b5b993 100644
--- a/src/ustreamer/encoder.c
+++ b/src/ustreamer/encoder.c
@@ -25,112 +25,110 @@
 
 static const struct {
 	const char *name;
-	const encoder_type_e type;
+	const us_encoder_type_e type; // cppcheck-suppress unusedStructMember
 } _ENCODER_TYPES[] = {
-	{"CPU",			ENCODER_TYPE_CPU},
-	{"HW",			ENCODER_TYPE_HW},
-	{"M2M-VIDEO",	ENCODER_TYPE_M2M_VIDEO},
-	{"M2M-IMAGE",	ENCODER_TYPE_M2M_IMAGE},
-	{"M2M-MJPEG",	ENCODER_TYPE_M2M_VIDEO},
-	{"M2M-JPEG",	ENCODER_TYPE_M2M_IMAGE},
-	{"OMX",			ENCODER_TYPE_M2M_IMAGE},
-	{"NOOP",		ENCODER_TYPE_NOOP},
+	{"CPU",			US_ENCODER_TYPE_CPU},
+	{"HW",			US_ENCODER_TYPE_HW},
+	{"M2M-VIDEO",	US_ENCODER_TYPE_M2M_VIDEO},
+	{"M2M-IMAGE",	US_ENCODER_TYPE_M2M_IMAGE},
+	{"M2M-MJPEG",	US_ENCODER_TYPE_M2M_VIDEO},
+	{"M2M-JPEG",	US_ENCODER_TYPE_M2M_IMAGE},
+	{"OMX",			US_ENCODER_TYPE_M2M_IMAGE},
+	{"NOOP",		US_ENCODER_TYPE_NOOP},
 };
 
 
 static void *_worker_job_init(void *v_enc);
 static void _worker_job_destroy(void *v_job);
-static bool _worker_run_job(worker_s *wr);
+static bool _worker_run_job(us_worker_s *wr);
 
 
-#define ER(_next)	enc->run->_next
+#define _ER(x_next)	enc->run->x_next
 
 
-encoder_s *encoder_init(void) {
-	encoder_runtime_s *run;
-	A_CALLOC(run, 1);
-	run->type = ENCODER_TYPE_CPU;
+us_encoder_s *us_encoder_init(void) {
+	us_encoder_runtime_s *run;
+	US_CALLOC(run, 1);
+	run->type = US_ENCODER_TYPE_CPU;
 	run->quality = 80;
-	A_MUTEX_INIT(&run->mutex);
+	US_MUTEX_INIT(run->mutex);
 
-	encoder_s *enc;
-	A_CALLOC(enc, 1);
+	us_encoder_s *enc;
+	US_CALLOC(enc, 1);
 	enc->type = run->type;
-	enc->n_workers = get_cores_available();
+	enc->n_workers = us_get_cores_available();
 	enc->run = run;
 	return enc;
 }
 
-void encoder_destroy(encoder_s *enc) {
-	if (ER(m2ms)) {
-		for (unsigned index = 0; index < ER(n_m2ms); ++index) {
-			if (ER(m2ms[index])) {
-				m2m_encoder_destroy(ER(m2ms[index]));
-			}
+void us_encoder_destroy(us_encoder_s *enc) {
+	if (_ER(m2ms) != NULL) {
+		for (unsigned index = 0; index < _ER(n_m2ms); ++index) {
+			US_DELETE(_ER(m2ms[index]), us_m2m_encoder_destroy)
 		}
-		free(ER(m2ms));
+		free(_ER(m2ms));
 	}
-	A_MUTEX_DESTROY(&ER(mutex));
+	US_MUTEX_DESTROY(_ER(mutex));
 	free(enc->run);
 	free(enc);
 }
 
-encoder_type_e encoder_parse_type(const char *str) {
-	for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
-		if (!strcasecmp(str, _ENCODER_TYPES[index].name)) {
-			return _ENCODER_TYPES[index].type;
+us_encoder_type_e us_encoder_parse_type(const char *str) {
+	US_ARRAY_ITERATE(_ENCODER_TYPES, 0, item, {
+		if (!strcasecmp(item->name, str)) {
+			return item->type;
 		}
-	}
-	return ENCODER_TYPE_UNKNOWN;
+	});
+	return US_ENCODER_TYPE_UNKNOWN;
 }
 
-const char *encoder_type_to_string(encoder_type_e type) {
-	for (unsigned index = 0; index < ARRAY_LEN(_ENCODER_TYPES); ++index) {
-		if (_ENCODER_TYPES[index].type == type) {
-			return _ENCODER_TYPES[index].name;
+const char *us_encoder_type_to_string(us_encoder_type_e type) {
+	US_ARRAY_ITERATE(_ENCODER_TYPES, 0, item, {
+		if (item->type == type) {
+			return item->name;
 		}
-	}
+	});
 	return _ENCODER_TYPES[0].name;
 }
 
-workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
-#	define DR(_next) dev->run->_next
+us_workers_pool_s *us_encoder_workers_pool_init(us_encoder_s *enc, us_device_s *dev) {
+#	define DR(x_next) dev->run->x_next
 
-	encoder_type_e type = (ER(cpu_forced) ? ENCODER_TYPE_CPU : enc->type);
+	us_encoder_type_e type = (_ER(cpu_forced) ? US_ENCODER_TYPE_CPU : enc->type);
 	unsigned quality = dev->jpeg_quality;
-	unsigned n_workers = min_u(enc->n_workers, DR(n_bufs));
+	unsigned n_workers = us_min_u(enc->n_workers, DR(n_bufs));
 	bool cpu_forced = false;
 
-	if (is_jpeg(DR(format)) && type != ENCODER_TYPE_HW) {
-		LOG_INFO("Switching to HW encoder: the input is (M)JPEG ...");
-		type = ENCODER_TYPE_HW;
+	if (us_is_jpeg(DR(format)) && type != US_ENCODER_TYPE_HW) {
+		US_LOG_INFO("Switching to HW encoder: the input is (M)JPEG ...");
+		type = US_ENCODER_TYPE_HW;
 	}
 
-	if (type == ENCODER_TYPE_HW) {
-		if (!is_jpeg(DR(format))) {
-			LOG_INFO("Switching to CPU encoder: the input format is not (M)JPEG ...");
+	if (type == US_ENCODER_TYPE_HW) {
+		if (!us_is_jpeg(DR(format))) {
+			US_LOG_INFO("Switching to CPU encoder: the input format is not (M)JPEG ...");
 			goto use_cpu;
 		}
 		quality = DR(jpeg_quality);
 		n_workers = 1;
 
-	} else if (type == ENCODER_TYPE_M2M_VIDEO || type == ENCODER_TYPE_M2M_IMAGE) {
-		LOG_DEBUG("Preparing M2M-%s encoder ...", (type == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"));
-		if (ER(m2ms) == NULL) {
-			A_CALLOC(ER(m2ms), n_workers);
+	} else if (type == US_ENCODER_TYPE_M2M_VIDEO || type == US_ENCODER_TYPE_M2M_IMAGE) {
+		US_LOG_DEBUG("Preparing M2M-%s encoder ...", (type == US_ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"));
+		if (_ER(m2ms) == NULL) {
+			US_CALLOC(_ER(m2ms), n_workers);
 		}
-		for (; ER(n_m2ms) < n_workers; ++ER(n_m2ms)) {
+		for (; _ER(n_m2ms) < n_workers; ++_ER(n_m2ms)) {
 			// Начинаем с нуля и доинициализируем на следующих заходах при необходимости
 			char name[32];
-			snprintf(name, 32, "JPEG-%u", ER(n_m2ms));
-			if (type == ENCODER_TYPE_M2M_VIDEO) {
-				ER(m2ms[ER(n_m2ms)]) = m2m_mjpeg_encoder_init(name, enc->m2m_path, quality);
+			snprintf(name, 32, "JPEG-%u", _ER(n_m2ms));
+			if (type == US_ENCODER_TYPE_M2M_VIDEO) {
+				_ER(m2ms[_ER(n_m2ms)]) = us_m2m_mjpeg_encoder_init(name, enc->m2m_path, quality);
 			} else {
-				ER(m2ms[ER(n_m2ms)]) = m2m_jpeg_encoder_init(name, enc->m2m_path, quality);
+				_ER(m2ms[_ER(n_m2ms)]) = us_m2m_jpeg_encoder_init(name, enc->m2m_path, quality);
 			}
 		}
 
-	} else if (type == ENCODER_TYPE_NOOP) {
+	} else if (type == US_ENCODER_TYPE_NOOP) {
 		n_workers = 1;
 		quality = 0;
 	}
@@ -138,32 +136,33 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
 	goto ok;
 
 	use_cpu:
-		type = ENCODER_TYPE_CPU;
+		type = US_ENCODER_TYPE_CPU;
 		quality = dev->jpeg_quality;
 
 	ok:
-		if (type == ENCODER_TYPE_NOOP) {
-			LOG_INFO("Using JPEG NOOP encoder");
+		if (type == US_ENCODER_TYPE_NOOP) {
+			US_LOG_INFO("Using JPEG NOOP encoder");
 		} else if (quality == 0) {
-			LOG_INFO("Using JPEG quality: encoder default");
+			US_LOG_INFO("Using JPEG quality: encoder default");
 		} else {
-			LOG_INFO("Using JPEG quality: %u%%", quality);
+			US_LOG_INFO("Using JPEG quality: %u%%", quality);
 		}
 
-		A_MUTEX_LOCK(&ER(mutex));
-		ER(type) = type;
-		ER(quality) = quality;
+		US_MUTEX_LOCK(_ER(mutex));
+		_ER(type) = type;
+		_ER(quality) = quality;
 		if (cpu_forced) {
-			ER(cpu_forced) = true;
+			_ER(cpu_forced) = true;
 		}
-		A_MUTEX_UNLOCK(&ER(mutex));
+		US_MUTEX_UNLOCK(_ER(mutex));
 
-		long double desired_interval = 0;
-		if (dev->desired_fps > 0 && (dev->desired_fps < dev->run->hw_fps || dev->run->hw_fps == 0)) {
-			desired_interval = (long double)1 / dev->desired_fps;
-		}
+		const long double desired_interval = (
+			dev->desired_fps > 0 && (dev->desired_fps < dev->run->hw_fps || dev->run->hw_fps == 0)
+			? (long double)1 / dev->desired_fps
+			: 0
+		);
 
-		return workers_pool_init(
+		return us_workers_pool_init(
 			"JPEG", "jw", n_workers, desired_interval,
 			_worker_job_init, (void *)enc,
 			_worker_job_destroy,
@@ -172,63 +171,61 @@ workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev) {
 #	undef DR
 }
 
-void encoder_get_runtime_params(encoder_s *enc, encoder_type_e *type, unsigned *quality) {
-	A_MUTEX_LOCK(&ER(mutex));
-	*type = ER(type);
-	*quality = ER(quality);
-	A_MUTEX_UNLOCK(&ER(mutex));
+void us_encoder_get_runtime_params(us_encoder_s *enc, us_encoder_type_e *type, unsigned *quality) {
+	US_MUTEX_LOCK(_ER(mutex));
+	*type = _ER(type);
+	*quality = _ER(quality);
+	US_MUTEX_UNLOCK(_ER(mutex));
 }
 
 static void *_worker_job_init(void *v_enc) {
-	encoder_job_s *job;
-	A_CALLOC(job, 1);
-	job->enc = (encoder_s *)v_enc;
-	job->dest = frame_init();
+	us_encoder_job_s *job;
+	US_CALLOC(job, 1);
+	job->enc = (us_encoder_s *)v_enc;
+	job->dest = us_frame_init();
 	return (void *)job;
 }
 
 static void _worker_job_destroy(void *v_job) {
-	encoder_job_s *job = (encoder_job_s *)v_job;
-	frame_destroy(job->dest);
+	us_encoder_job_s *job = (us_encoder_job_s *)v_job;
+	us_frame_destroy(job->dest);
 	free(job);
 }
 
-#undef ER
+static bool _worker_run_job(us_worker_s *wr) {
+	us_encoder_job_s *job = (us_encoder_job_s *)wr->job;
+	us_encoder_s *enc = job->enc; // Just for _ER()
+	us_frame_s *src = &job->hw->raw;
+	us_frame_s *dest = job->dest;
 
-static bool _worker_run_job(worker_s *wr) {
-	encoder_job_s *job = (encoder_job_s *)wr->job;
-	frame_s *src = &job->hw->raw;
-	frame_s *dest = job->dest;
+	assert(_ER(type) != US_ENCODER_TYPE_UNKNOWN);
 
-#	define ER(_next) job->enc->run->_next
-
-	assert(ER(type) != ENCODER_TYPE_UNKNOWN);
-
-	if (ER(type) == ENCODER_TYPE_CPU) {
-		LOG_VERBOSE("Compressing JPEG using CPU: worker=%s, buffer=%u",
+	if (_ER(type) == US_ENCODER_TYPE_CPU) {
+		US_LOG_VERBOSE("Compressing JPEG using CPU: worker=%s, buffer=%u",
 			wr->name, job->hw->buf.index);
-		cpu_encoder_compress(src, dest, ER(quality));
+		us_cpu_encoder_compress(src, dest, _ER(quality));
 
-	} else if (ER(type) == ENCODER_TYPE_HW) {
-		LOG_VERBOSE("Compressing JPEG using HW (just copying): worker=%s, buffer=%u",
+	} else if (_ER(type) == US_ENCODER_TYPE_HW) {
+		US_LOG_VERBOSE("Compressing JPEG using HW (just copying): worker=%s, buffer=%u",
 			wr->name, job->hw->buf.index);
-		hw_encoder_compress(src, dest);
+		us_hw_encoder_compress(src, dest);
 
-	} else if (ER(type) == ENCODER_TYPE_M2M_VIDEO || ER(type) == ENCODER_TYPE_M2M_IMAGE) {
-		LOG_VERBOSE("Compressing JPEG using M2M-%s: worker=%s, buffer=%u",
-			(ER(type) == ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"), wr->name, job->hw->buf.index);
-		if (m2m_encoder_compress(ER(m2ms[wr->number]), src, dest, false) < 0) {
+	} else if (_ER(type) == US_ENCODER_TYPE_M2M_VIDEO || _ER(type) == US_ENCODER_TYPE_M2M_IMAGE) {
+		US_LOG_VERBOSE("Compressing JPEG using M2M-%s: worker=%s, buffer=%u",
+			(_ER(type) == US_ENCODER_TYPE_M2M_VIDEO ? "VIDEO" : "IMAGE"), wr->name, job->hw->buf.index);
+		if (us_m2m_encoder_compress(_ER(m2ms[wr->number]), src, dest, false) < 0) {
 			goto error;
 		}
 
-	} else if (ER(type) == ENCODER_TYPE_NOOP) {
-		LOG_VERBOSE("Compressing JPEG using NOOP (do nothing): worker=%s, buffer=%u",
+	} else if (_ER(type) == US_ENCODER_TYPE_NOOP) {
+		US_LOG_VERBOSE("Compressing JPEG using NOOP (do nothing): worker=%s, buffer=%u",
 			wr->name, job->hw->buf.index);
+		us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
 		usleep(5000); // Просто чтобы работала логика desired_fps
-		dest->encode_end_ts = get_now_monotonic();
+		dest->encode_end_ts = us_get_now_monotonic(); // us_frame_encoding_end()
 	}
 
-	LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
+	US_LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
 		job->dest->used,
 		job->dest->encode_end_ts - job->dest->encode_begin_ts,
 		wr->name,
@@ -237,12 +234,10 @@ static bool _worker_run_job(worker_s *wr) {
 	return true;
 
 	error:
-		LOG_ERROR("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf.index);
-		LOG_ERROR("Error while compressing buffer, falling back to CPU");
-		A_MUTEX_LOCK(&ER(mutex));
-		ER(cpu_forced) = true;
-		A_MUTEX_UNLOCK(&ER(mutex));
+		US_LOG_ERROR("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf.index);
+		US_LOG_ERROR("Error while compressing buffer, falling back to CPU");
+		US_MUTEX_LOCK(_ER(mutex));
+		_ER(cpu_forced) = true;
+		US_MUTEX_UNLOCK(_ER(mutex));
 		return false;
-
-#	undef ER
 }
diff --git a/src/ustreamer/encoder.h b/src/ustreamer/encoder.h
index 934e4fe..4750520 100644
--- a/src/ustreamer/encoder.h
+++ b/src/ustreamer/encoder.h
@@ -31,6 +31,7 @@
 #include <linux/videodev2.h>
 
 #include "../libs/tools.h"
+#include "../libs/array.h"
 #include "../libs/threading.h"
 #include "../libs/logging.h"
 #include "../libs/frame.h"
@@ -46,46 +47,46 @@
 #define ENCODER_TYPES_STR "CPU, HW, M2M-VIDEO, M2M-IMAGE, NOOP"
 
 typedef enum {
-	ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
-	ENCODER_TYPE_CPU,
-	ENCODER_TYPE_HW,
-	ENCODER_TYPE_M2M_VIDEO,
-	ENCODER_TYPE_M2M_IMAGE,
-	ENCODER_TYPE_NOOP,
-} encoder_type_e;
+	US_ENCODER_TYPE_UNKNOWN, // Only for us_encoder_parse_type() and main()
+	US_ENCODER_TYPE_CPU,
+	US_ENCODER_TYPE_HW,
+	US_ENCODER_TYPE_M2M_VIDEO,
+	US_ENCODER_TYPE_M2M_IMAGE,
+	US_ENCODER_TYPE_NOOP,
+} us_encoder_type_e;
 
 typedef struct {
-	encoder_type_e	type;
-	unsigned		quality;
-	bool			cpu_forced;
-	pthread_mutex_t	mutex;
+	us_encoder_type_e	type;
+	unsigned			quality;
+	bool				cpu_forced;
+	pthread_mutex_t		mutex;
 
-	unsigned		n_m2ms;
-	m2m_encoder_s	**m2ms;
-} encoder_runtime_s;
+	unsigned			n_m2ms;
+	us_m2m_encoder_s	**m2ms;
+} us_encoder_runtime_s;
 
 typedef struct {
-	encoder_type_e	type;
-	unsigned		n_workers;
-	char			*m2m_path;
+	us_encoder_type_e	type;
+	unsigned			n_workers;
+	char				*m2m_path;
 
-	encoder_runtime_s *run;
-} encoder_s;
+	us_encoder_runtime_s *run;
+} us_encoder_s;
 
 typedef struct {
-	encoder_s	*enc;
-	hw_buffer_s	*hw;
-	frame_s		*dest;
-} encoder_job_s;
+	us_encoder_s	*enc;
+	us_hw_buffer_s	*hw;
+	us_frame_s		*dest;
+} us_encoder_job_s;
 
 
-encoder_s *encoder_init(void);
-void encoder_destroy(encoder_s *enc);
+us_encoder_s *us_encoder_init(void);
+void us_encoder_destroy(us_encoder_s *enc);
 
-encoder_type_e encoder_parse_type(const char *str);
-const char *encoder_type_to_string(encoder_type_e type);
+us_encoder_type_e us_encoder_parse_type(const char *str);
+const char *us_encoder_type_to_string(us_encoder_type_e type);
 
-workers_pool_s *encoder_workers_pool_init(encoder_s *enc, device_s *dev);
-void encoder_get_runtime_params(encoder_s *enc, encoder_type_e *type, unsigned *quality);
+us_workers_pool_s *us_encoder_workers_pool_init(us_encoder_s *enc, us_device_s *dev);
+void us_encoder_get_runtime_params(us_encoder_s *enc, us_encoder_type_e *type, unsigned *quality);
 
-int encoder_compress(encoder_s *enc, unsigned worker_number, frame_s *src, frame_s *dest);
+int us_encoder_compress(us_encoder_s *enc, unsigned worker_number, us_frame_s *src, us_frame_s *dest);
diff --git a/src/ustreamer/encoders/cpu/encoder.c b/src/ustreamer/encoders/cpu/encoder.c
index 9aeedd8..1c20e6b 100644
--- a/src/ustreamer/encoders/cpu/encoder.c
+++ b/src/ustreamer/encoders/cpu/encoder.c
@@ -31,26 +31,26 @@
 typedef struct {
 	struct	jpeg_destination_mgr mgr; // Default manager
 	JOCTET	*buf; // Start of buffer
-	frame_s	*frame;
+	us_frame_s	*frame;
 } _jpeg_dest_manager_s;
 
 
-static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame);
+static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame);
 
-static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame);
-static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame);
-static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame);
-static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame);
+static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
+static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
+static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
+static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame);
 
 static void _jpeg_init_destination(j_compress_ptr jpeg);
 static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
 static void _jpeg_term_destination(j_compress_ptr jpeg);
 
 
-void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
+void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned quality) {
 	// This function based on compress_image_to_jpeg() from mjpg-streamer
 
-	frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
+	us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
 
 	struct jpeg_compress_struct jpeg;
 	struct jpeg_error_mgr jpeg_error;
@@ -70,8 +70,8 @@ void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
 
 	jpeg_start_compress(&jpeg, TRUE);
 
-#	define WRITE_SCANLINES(_format, _func) \
-		case _format: { _func(&jpeg, src); break; }
+#	define WRITE_SCANLINES(x_format, x_func) \
+		case x_format: { x_func(&jpeg, src); break; }
 
 	switch (src->format) {
 		// https://www.fourcc.org/yuv.php
@@ -87,17 +87,17 @@ void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality) {
 	jpeg_finish_compress(&jpeg);
 	jpeg_destroy_compress(&jpeg);
 
-	frame_encoding_end(dest);
+	us_frame_encoding_end(dest);
 }
 
-static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
+static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
 	if (jpeg->dest == NULL) {
 		assert((jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
 			(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
-		)));
+		)) != NULL);
 	}
 
-	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
+	_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
 	dest->mgr.init_destination = _jpeg_init_destination;
 	dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
 	dest->mgr.term_destination = _jpeg_term_destination;
@@ -111,11 +111,11 @@ static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
 #define YUV_B(_y, _u, _)	(((_y) + (454 * (_u))) >> 8)
 #define NORM_COMPONENT(_x)	(((_x) > 255) ? 255 : (((_x) < 0) ? 0 : (_x)))
 
-static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
+static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
 	uint8_t *line_buf;
-	A_CALLOC(line_buf, frame->width * 3);
+	US_CALLOC(line_buf, frame->width * 3);
 
-	const unsigned padding = frame_get_padding(frame);
+	const unsigned padding = us_frame_get_padding(frame);
 	const uint8_t *data = frame->data;
 	unsigned z = 0;
 
@@ -123,13 +123,13 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const
 		uint8_t *ptr = line_buf;
 
 		for (unsigned x = 0; x < frame->width; ++x) {
-			int y = (!z ? data[0] << 8 : data[2] << 8);
-			int u = data[1] - 128;
-			int v = data[3] - 128;
+			const int y = (!z ? data[0] << 8 : data[2] << 8);
+			const int u = data[1] - 128;
+			const int v = data[3] - 128;
 
-			int r = YUV_R(y, u, v);
-			int g = YUV_G(y, u, v);
-			int b = YUV_B(y, u, v);
+			const int r = YUV_R(y, u, v);
+			const int g = YUV_G(y, u, v);
+			const int b = YUV_B(y, u, v);
 
 			*(ptr++) = NORM_COMPONENT(r);
 			*(ptr++) = NORM_COMPONENT(g);
@@ -149,11 +149,11 @@ static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const
 	free(line_buf);
 }
 
-static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
+static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
 	uint8_t *line_buf;
-	A_CALLOC(line_buf, frame->width * 3);
+	US_CALLOC(line_buf, frame->width * 3);
 
-	const unsigned padding = frame_get_padding(frame);
+	const unsigned padding = us_frame_get_padding(frame);
 	const uint8_t *data = frame->data;
 	unsigned z = 0;
 
@@ -161,13 +161,13 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const
 		uint8_t *ptr = line_buf;
 
 		for (unsigned x = 0; x < frame->width; ++x) {
-			int y = (!z ? data[1] << 8 : data[3] << 8);
-			int u = data[0] - 128;
-			int v = data[2] - 128;
+			const int y = (!z ? data[1] << 8 : data[3] << 8);
+			const int u = data[0] - 128;
+			const int v = data[2] - 128;
 
-			int r = YUV_R(y, u, v);
-			int g = YUV_G(y, u, v);
-			int b = YUV_B(y, u, v);
+			const int r = YUV_R(y, u, v);
+			const int g = YUV_G(y, u, v);
+			const int b = YUV_B(y, u, v);
 
 			*(ptr++) = NORM_COMPONENT(r);
 			*(ptr++) = NORM_COMPONENT(g);
@@ -192,18 +192,18 @@ static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const
 #undef YUV_G
 #undef YUV_R
 
-static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
+static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
 	uint8_t *line_buf;
-	A_CALLOC(line_buf, frame->width * 3);
+	US_CALLOC(line_buf, frame->width * 3);
 
-	const unsigned padding = frame_get_padding(frame);
+	const unsigned padding = us_frame_get_padding(frame);
 	const uint8_t *data = frame->data;
 
 	while (jpeg->next_scanline < frame->height) {
 		uint8_t *ptr = line_buf;
 
 		for (unsigned x = 0; x < frame->width; ++x) {
-			unsigned int two_byte = (data[1] << 8) + data[0];
+			const unsigned int two_byte = (data[1] << 8) + data[0];
 
 			*(ptr++) = data[1] & 248; // Red
 			*(ptr++) = (uint8_t)((two_byte & 2016) >> 3); // Green
@@ -220,27 +220,27 @@ static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, cons
 	free(line_buf);
 }
 
-static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
-	const unsigned padding = frame_get_padding(frame);
+static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const us_frame_s *frame) {
+	const unsigned padding = us_frame_get_padding(frame);
 	uint8_t *data = frame->data;
 
 	while (jpeg->next_scanline < frame->height) {
 		JSAMPROW scanlines[1] = {data};
 		jpeg_write_scanlines(jpeg, scanlines, 1);
 
-		data += (jpeg->next_scanline * frame->width * 3) + padding;
+		data += (frame->width * 3) + padding;
 	}
 }
 
 #define JPEG_OUTPUT_BUFFER_SIZE ((size_t)4096)
 
 static void _jpeg_init_destination(j_compress_ptr jpeg) {
-	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
+	_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
 
 	// Allocate the output buffer - it will be released when done with image
 	assert((dest->buf = (JOCTET *)(*jpeg->mem->alloc_small)(
 		(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
-	)));
+	)) != NULL);
 
 	dest->mgr.next_output_byte = dest->buf;
 	dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
@@ -249,9 +249,9 @@ static void _jpeg_init_destination(j_compress_ptr jpeg) {
 static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
 	// Called whenever local jpeg buffer fills up
 
-	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
+	_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
 
-	frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
+	us_frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
 
 	dest->mgr.next_output_byte = dest->buf;
 	dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
@@ -263,11 +263,11 @@ static void _jpeg_term_destination(j_compress_ptr jpeg) {
 	// Called by jpeg_finish_compress after all data has been written.
 	// Usually needs to flush buffer.
 
-	_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
-	size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
+	_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
+	const size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
 
 	// Write any data remaining in the buffer.
-	frame_append_data(dest->frame, dest->buf, final);
+	us_frame_append_data(dest->frame, dest->buf, final);
 }
 
 #undef JPEG_OUTPUT_BUFFER_SIZE
diff --git a/src/ustreamer/encoders/cpu/encoder.h b/src/ustreamer/encoders/cpu/encoder.h
index 7aaf1b1..ab30f2c 100644
--- a/src/ustreamer/encoders/cpu/encoder.h
+++ b/src/ustreamer/encoders/cpu/encoder.h
@@ -35,4 +35,4 @@
 #include "../../../libs/frame.h"
 
 
-void cpu_encoder_compress(const frame_s *src, frame_s *dest, unsigned quality);
+void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned quality);
diff --git a/src/ustreamer/encoders/hw/encoder.c b/src/ustreamer/encoders/hw/encoder.c
index 901e32a..38330f3 100644
--- a/src/ustreamer/encoders/hw/encoder.c
+++ b/src/ustreamer/encoders/hw/encoder.c
@@ -28,21 +28,21 @@
 #include "encoder.h"
 
 
-void _copy_plus_huffman(const frame_s *src, frame_s *dest);
+void _copy_plus_huffman(const us_frame_s *src, us_frame_s *dest);
 static bool _is_huffman(const uint8_t *data);
 
 
-void hw_encoder_compress(const frame_s *src, frame_s *dest) {
-	assert(is_jpeg(src->format));
+void us_hw_encoder_compress(const us_frame_s *src, us_frame_s *dest) {
+	assert(us_is_jpeg(src->format));
 	_copy_plus_huffman(src, dest);
 }
 
-void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
-	frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
+void _copy_plus_huffman(const us_frame_s *src, us_frame_s *dest) {
+	us_frame_encoding_begin(src, dest, V4L2_PIX_FMT_JPEG);
 
 	if (!_is_huffman(src->data)) {
 		const uint8_t *src_ptr = src->data;
-		const uint8_t *src_end = src->data + src->used;
+		const uint8_t *const src_end = src->data + src->used;
 
 		while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) {
 			src_ptr += 1;
@@ -54,15 +54,15 @@ void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
 
 		const size_t paste = src_ptr - src->data;
 
-		frame_set_data(dest, src->data, paste);
-		frame_append_data(dest, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE));
-		frame_append_data(dest, src_ptr, src->used - paste);
+		us_frame_set_data(dest, src->data, paste);
+		us_frame_append_data(dest, US_HUFFMAN_TABLE, sizeof(US_HUFFMAN_TABLE));
+		us_frame_append_data(dest, src_ptr, src->used - paste);
 
 	} else {
-		frame_set_data(dest, src->data, src->used);
+		us_frame_set_data(dest, src->data, src->used);
 	}
 
-	frame_encoding_end(dest);
+	us_frame_encoding_end(dest);
 }
 
 static bool _is_huffman(const uint8_t *data) {
diff --git a/src/ustreamer/encoders/hw/encoder.h b/src/ustreamer/encoders/hw/encoder.h
index c81af6c..c9c8bd3 100644
--- a/src/ustreamer/encoders/hw/encoder.h
+++ b/src/ustreamer/encoders/hw/encoder.h
@@ -34,4 +34,4 @@
 #include "huffman.h"
 
 
-void hw_encoder_compress(const frame_s *src, frame_s *dest);
+void us_hw_encoder_compress(const us_frame_s *src, us_frame_s *dest);
diff --git a/src/ustreamer/encoders/hw/huffman.h b/src/ustreamer/encoders/hw/huffman.h
index 2de8d34..c2ab132 100644
--- a/src/ustreamer/encoders/hw/huffman.h
+++ b/src/ustreamer/encoders/hw/huffman.h
@@ -30,7 +30,7 @@
 #include <stdint.h>
 
 
-static const uint8_t HUFFMAN_TABLE[] = {
+static const uint8_t US_HUFFMAN_TABLE[] = {
 	0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
 	0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
 	0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03,
diff --git a/src/ustreamer/gpio/gpio.c b/src/ustreamer/gpio/gpio.c
index 54e41ed..5036d11 100644
--- a/src/ustreamer/gpio/gpio.c
+++ b/src/ustreamer/gpio/gpio.c
@@ -23,13 +23,13 @@
 #include "gpio.h"
 
 
-gpio_s us_gpio = {
+us_gpio_s us_g_gpio = {
 	.path = "/dev/gpiochip0",
 	.consumer_prefix = "ustreamer",
 
-#	define MAKE_OUTPUT(_role) { \
+#	define MAKE_OUTPUT(x_role) { \
 		.pin = -1, \
-		.role = _role, \
+		.role = x_role, \
 		.consumer = NULL, \
 		.line = NULL, \
 		.state = false \
@@ -46,82 +46,82 @@ gpio_s us_gpio = {
 };
 
 
-static void _gpio_output_init(gpio_output_s *output);
-static void _gpio_output_destroy(gpio_output_s *output);
+static void _gpio_output_init(us_gpio_output_s *output);
+static void _gpio_output_destroy(us_gpio_output_s *output);
 
 
-void gpio_init(void) {
-	assert(us_gpio.chip == NULL);
+void us_gpio_init(void) {
+	assert(us_g_gpio.chip == NULL);
 	if (
-		us_gpio.prog_running.pin >= 0
-		|| us_gpio.stream_online.pin >= 0
-		|| us_gpio.has_http_clients.pin >= 0
+		us_g_gpio.prog_running.pin >= 0
+		|| us_g_gpio.stream_online.pin >= 0
+		|| us_g_gpio.has_http_clients.pin >= 0
 	) {
-		A_MUTEX_INIT(&us_gpio.mutex);
-		LOG_INFO("GPIO: Using chip device: %s", us_gpio.path);
-		if ((us_gpio.chip = gpiod_chip_open(us_gpio.path)) != NULL) {
-			_gpio_output_init(&us_gpio.prog_running);
-			_gpio_output_init(&us_gpio.stream_online);
-			_gpio_output_init(&us_gpio.has_http_clients);
+		US_MUTEX_INIT(us_g_gpio.mutex);
+		US_LOG_INFO("GPIO: Using chip device: %s", us_g_gpio.path);
+		if ((us_g_gpio.chip = gpiod_chip_open(us_g_gpio.path)) != NULL) {
+			_gpio_output_init(&us_g_gpio.prog_running);
+			_gpio_output_init(&us_g_gpio.stream_online);
+			_gpio_output_init(&us_g_gpio.has_http_clients);
 		} else {
-			LOG_PERROR("GPIO: Can't initialize chip device %s", us_gpio.path);
+			US_LOG_PERROR("GPIO: Can't initialize chip device %s", us_g_gpio.path);
 		}
 	}
 }
 
-void gpio_destroy(void) {
-	_gpio_output_destroy(&us_gpio.prog_running);
-	_gpio_output_destroy(&us_gpio.stream_online);
-	_gpio_output_destroy(&us_gpio.has_http_clients);
-	if (us_gpio.chip) {
-		gpiod_chip_close(us_gpio.chip);
-		us_gpio.chip = NULL;
-		A_MUTEX_DESTROY(&us_gpio.mutex);
+void us_gpio_destroy(void) {
+	_gpio_output_destroy(&us_g_gpio.prog_running);
+	_gpio_output_destroy(&us_g_gpio.stream_online);
+	_gpio_output_destroy(&us_g_gpio.has_http_clients);
+	if (us_g_gpio.chip != NULL) {
+		gpiod_chip_close(us_g_gpio.chip);
+		us_g_gpio.chip = NULL;
+		US_MUTEX_DESTROY(us_g_gpio.mutex);
 	}
 }
 
-int gpio_inner_set(gpio_output_s *output, bool state) {
+int us_gpio_inner_set(us_gpio_output_s *output, bool state) {
 	int retval = 0;
 
-	assert(us_gpio.chip);
-	assert(output->line);
+	assert(us_g_gpio.chip != NULL);
+	assert(output->line != NULL);
 	assert(output->state != state); // Must be checked in macro for the performance
-	A_MUTEX_LOCK(&us_gpio.mutex);
+	US_MUTEX_LOCK(us_g_gpio.mutex);
 
 	if (gpiod_line_set_value(output->line, (int)state) < 0) { \
-		LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
+		US_LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
 		_gpio_output_destroy(output);
 		retval = -1;
 	}
 
-	A_MUTEX_UNLOCK(&us_gpio.mutex);
+	US_MUTEX_UNLOCK(us_g_gpio.mutex);
 	return retval;
 }
 
-static void _gpio_output_init(gpio_output_s *output) {
-	assert(us_gpio.chip);
+static void _gpio_output_init(us_gpio_output_s *output) {
+	assert(us_g_gpio.chip != NULL);
 	assert(output->line == NULL);
 
-	A_ASPRINTF(output->consumer, "%s::%s", us_gpio.consumer_prefix, output->role);
+	US_ASPRINTF(output->consumer, "%s::%s", us_g_gpio.consumer_prefix, output->role);
 
 	if (output->pin >= 0) {
-		if ((output->line = gpiod_chip_get_line(us_gpio.chip, output->pin)) != NULL) {
+		if ((output->line = gpiod_chip_get_line(us_g_gpio.chip, output->pin)) != NULL) {
 			if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
-				LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
+				US_LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
 				_gpio_output_destroy(output);
 			}
 		} else {
-			LOG_PERROR("GPIO: Can't get pin=%d as %s", output->pin, output->consumer);
+			US_LOG_PERROR("GPIO: Can't get pin=%d as %s", output->pin, output->consumer);
 		}
 	}
 }
 
-static void _gpio_output_destroy(gpio_output_s *output) {
-	if (output->line) {
+static void _gpio_output_destroy(us_gpio_output_s *output) {
+	if (output->line != NULL) {
 		gpiod_line_release(output->line);
 		output->line = NULL;
 	}
-	if (output->consumer) {
+	if (output->consumer != NULL) {
 		free(output->consumer);
 		output->consumer = NULL;
 	}
diff --git a/src/ustreamer/gpio/gpio.h b/src/ustreamer/gpio/gpio.h
index 523bec2..8d48929 100644
--- a/src/ustreamer/gpio/gpio.h
+++ b/src/ustreamer/gpio/gpio.h
@@ -41,47 +41,47 @@ typedef struct {
 	char 				*consumer;
 	struct gpiod_line	*line;
 	bool				state;
-} gpio_output_s;
+} us_gpio_output_s;
 
 typedef struct {
-	char *path;
-	char *consumer_prefix;
+	char	*path;
+	char	*consumer_prefix;
 
-	gpio_output_s prog_running;
-	gpio_output_s stream_online;
-	gpio_output_s has_http_clients;
+	us_gpio_output_s	prog_running;
+	us_gpio_output_s	stream_online;
+	us_gpio_output_s	has_http_clients;
 
 	pthread_mutex_t		mutex;
 	struct gpiod_chip	*chip;
-} gpio_s;
+} us_gpio_s;
 
 
-extern gpio_s us_gpio;
+extern us_gpio_s us_g_gpio;
 
 
-void gpio_init(void);
-void gpio_destroy(void);
-int gpio_inner_set(gpio_output_s *output, bool state);
+void us_gpio_init(void);
+void us_gpio_destroy(void);
+int us_gpio_inner_set(us_gpio_output_s *output, bool state);
 
 
-#define SET_STATE(_output, _state) { \
-		if (_output.line && _output.state != _state) { \
-			if (!gpio_inner_set(&_output, _state)) { \
-				_output.state = _state; \
+#define SET_STATE(x_output, x_state) { \
+		if (x_output.line && x_output.state != x_state) { \
+			if (!us_gpio_inner_set(&x_output, x_state)) { \
+				x_output.state = x_state; \
 			} \
 		} \
 	}
 
-INLINE void gpio_set_prog_running(bool state) {
-	SET_STATE(us_gpio.prog_running, state);
+INLINE void us_gpio_set_prog_running(bool state) {
+	SET_STATE(us_g_gpio.prog_running, state);
 }
 
-INLINE void gpio_set_stream_online(bool state) {
-	SET_STATE(us_gpio.stream_online, state);
+INLINE void us_gpio_set_stream_online(bool state) {
+	SET_STATE(us_g_gpio.stream_online, state);
 }
 
-INLINE void gpio_set_has_http_clients(bool state) {
-	SET_STATE(us_gpio.has_http_clients, state);
+INLINE void us_gpio_set_has_http_clients(bool state) {
+	SET_STATE(us_g_gpio.has_http_clients, state);
 }
 
 #undef SET_STATE
diff --git a/src/ustreamer/h264/stream.c b/src/ustreamer/h264.c
similarity index 60%
rename from src/ustreamer/h264/stream.c
rename to src/ustreamer/h264.c
index 2444595..d4fc47b 100644
--- a/src/ustreamer/h264/stream.c
+++ b/src/ustreamer/h264.c
@@ -20,45 +20,51 @@
 *****************************************************************************/
 
 
-#include "stream.h"
+#include "h264.h"
 
 
-h264_stream_s *h264_stream_init(memsink_s *sink, const char *path, unsigned bitrate, unsigned gop) {
-	h264_stream_s *h264;
-	A_CALLOC(h264, 1);
+us_h264_stream_s *us_h264_stream_init(us_memsink_s *sink, const char *path, unsigned bitrate, unsigned gop) {
+	us_h264_stream_s *h264;
+	US_CALLOC(h264, 1);
 	h264->sink = sink;
-	h264->tmp_src = frame_init();
-	h264->dest = frame_init();
+	h264->tmp_src = us_frame_init();
+	h264->dest = us_frame_init();
 	atomic_init(&h264->online, false);
-	h264->enc = m2m_h264_encoder_init("H264", path, bitrate, gop);
+	h264->enc = us_m2m_h264_encoder_init("H264", path, bitrate, gop);
 	return h264;
 }
 
-void h264_stream_destroy(h264_stream_s *h264) {
-	m2m_encoder_destroy(h264->enc);
-	frame_destroy(h264->dest);
-	frame_destroy(h264->tmp_src);
+void us_h264_stream_destroy(us_h264_stream_s *h264) {
+	us_m2m_encoder_destroy(h264->enc);
+	us_frame_destroy(h264->dest);
+	us_frame_destroy(h264->tmp_src);
 	free(h264);
 }
 
-void h264_stream_process(h264_stream_s *h264, const frame_s *frame, bool force_key) {
-	if (!memsink_server_check(h264->sink, frame)) {
+void us_h264_stream_process(us_h264_stream_s *h264, const us_frame_s *frame, bool force_key) {
+	if (!us_memsink_server_check(h264->sink, frame)) {
 		return;
 	}
 
-	if (is_jpeg(frame->format)) {
-		long double now = get_now_monotonic();
-		LOG_DEBUG("H264: Input frame is JPEG; decoding ...");
-		if (unjpeg(frame, h264->tmp_src, true) < 0) {
+	if (us_is_jpeg(frame->format)) {
+		const long double now = us_get_now_monotonic();
+		US_LOG_DEBUG("H264: Input frame is JPEG; decoding ...");
+		if (us_unjpeg(frame, h264->tmp_src, true) < 0) {
 			return;
 		}
 		frame = h264->tmp_src;
-		LOG_VERBOSE("H264: JPEG decoded; time=%.3Lf", get_now_monotonic() - now);
+		US_LOG_VERBOSE("H264: JPEG decoded; time=%.3Lf", us_get_now_monotonic() - now);
+	}
+
+	if (h264->key_requested) {
+		US_LOG_INFO("H264: Requested keyframe by a sink client");
+		h264->key_requested = false;
+		force_key = true;
 	}
 
 	bool online = false;
-	if (!m2m_encoder_compress(h264->enc, frame, h264->dest, force_key)) {
-		online = !memsink_server_put(h264->sink, h264->dest);
+	if (!us_m2m_encoder_compress(h264->enc, frame, h264->dest, force_key)) {
+		online = !us_memsink_server_put(h264->sink, h264->dest, &h264->key_requested);
 	}
 	atomic_store(&h264->online, online);
 }
diff --git a/src/ustreamer/h264.h b/src/ustreamer/h264.h
new file mode 100644
index 0000000..2729228
--- /dev/null
+++ b/src/ustreamer/h264.h
@@ -0,0 +1,49 @@
+/*****************************************************************************
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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/>.  #
+#                                                                            #
+*****************************************************************************/
+
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdatomic.h>
+#include <assert.h>
+
+#include "../libs/tools.h"
+#include "../libs/logging.h"
+#include "../libs/frame.h"
+#include "../libs/memsink.h"
+#include "../libs/unjpeg.h"
+#include "m2m.h"
+
+
+typedef struct {
+	us_memsink_s		*sink;
+	bool				key_requested;
+	us_frame_s			*tmp_src;
+	us_frame_s			*dest;
+	us_m2m_encoder_s	*enc;
+	atomic_bool			online;
+} us_h264_stream_s;
+
+
+us_h264_stream_s *us_h264_stream_init(us_memsink_s *sink, const char *path, unsigned bitrate, unsigned gop);
+void us_h264_stream_destroy(us_h264_stream_s *h264);
+void us_h264_stream_process(us_h264_stream_s *h264, const us_frame_s *frame, bool force_key);
diff --git a/src/ustreamer/http/bev.c b/src/ustreamer/http/bev.c
index 9ca9a10..5bbf0c6 100644
--- a/src/ustreamer/http/bev.c
+++ b/src/ustreamer/http/bev.c
@@ -23,25 +23,26 @@
 #include "bev.h"
 
 
-char *bufferevent_my_format_reason(short what) {
+char *us_bufferevent_format_reason(short what) {
 	char *reason;
-	A_CALLOC(reason, 2048);
+	US_CALLOC(reason, 2048);
 
-	char perror_buf[1024] = {0};
-	char *perror_ptr = errno_to_string(EVUTIL_SOCKET_ERROR(), perror_buf, 1024); // evutil_socket_error_to_string() is not thread-safe
+	// evutil_socket_error_to_string() is not thread-safe
+	char *const perror_str = us_errno_to_string(EVUTIL_SOCKET_ERROR());
 	bool first = true;
 
-	strcat(reason, perror_ptr);
+	strcat(reason, perror_str);
+	free(perror_str);
 	strcat(reason, " (");
 
-#	define FILL_REASON(_bev, _name) { \
-			if (what & _bev) { \
+#	define FILL_REASON(x_bev, x_name) { \
+			if (what & x_bev) { \
 				if (first) { \
 					first = false; \
 				} else { \
 					strcat(reason, ","); \
 				} \
-				strcat(reason, _name); \
+				strcat(reason, x_name); \
 			} \
 		}
 
diff --git a/src/ustreamer/http/bev.h b/src/ustreamer/http/bev.h
index 4ccdd35..e74bc2d 100644
--- a/src/ustreamer/http/bev.h
+++ b/src/ustreamer/http/bev.h
@@ -32,4 +32,4 @@
 #include "../../libs/logging.h"
 
 
-char *bufferevent_my_format_reason(short what);
+char *us_bufferevent_format_reason(short what);
diff --git a/src/ustreamer/http/mime.c b/src/ustreamer/http/mime.c
index e1909ba..cf61eba 100644
--- a/src/ustreamer/http/mime.c
+++ b/src/ustreamer/http/mime.c
@@ -24,8 +24,8 @@
 
 
 static const struct {
-	const char *ext;
-	const char *mime;
+	const char *ext; // cppcheck-suppress unusedStructMember
+	const char *mime; // cppcheck-suppress unusedStructMember
 } _MIME_TYPES[] = {
 	{"html",	"text/html"},
 	{"htm",		"text/html"},
@@ -46,19 +46,19 @@ static const struct {
 };
 
 
-const char *guess_mime_type(const char *path) {
+const char *us_guess_mime_type(const char *path) {
 	// FIXME: false-positive cppcheck
-	char *dot = strrchr(path, '.'); // cppcheck-suppress ctunullpointer
+	const char *dot = strrchr(path, '.'); // cppcheck-suppress ctunullpointer
 	if (dot == NULL || strchr(dot, '/') != NULL) {
 		goto misc;
 	}
 
-	char *ext = dot + 1;
-	for (unsigned index = 0; index < ARRAY_LEN(_MIME_TYPES); ++index) {
-		if (!evutil_ascii_strcasecmp(ext, _MIME_TYPES[index].ext)) {
-			return _MIME_TYPES[index].mime;
+	const char *ext = dot + 1;
+	US_ARRAY_ITERATE(_MIME_TYPES, 0, item, {
+		if (!evutil_ascii_strcasecmp(item->ext, ext)) {
+			return item->mime;
 		}
-	}
+	});
 
 	misc:
 		return "application/misc";
diff --git a/src/ustreamer/http/mime.h b/src/ustreamer/http/mime.h
index ed6e17f..4a708a3 100644
--- a/src/ustreamer/http/mime.h
+++ b/src/ustreamer/http/mime.h
@@ -27,6 +27,7 @@
 #include <event2/util.h>
 
 #include "../../libs/tools.h"
+#include "../../libs/array.h"
 
 
-const char *guess_mime_type(const char *str);
+const char *us_guess_mime_type(const char *str);
diff --git a/src/ustreamer/http/path.c b/src/ustreamer/http/path.c
index a590cfe..47fc525 100644
--- a/src/ustreamer/http/path.c
+++ b/src/ustreamer/http/path.c
@@ -23,7 +23,7 @@
 #include "path.h"
 
 
-char *simplify_request_path(const char *str) {
+char *us_simplify_request_path(const char *str) {
 	// Based on Lighttpd sources:
 	//   - https://github.com/lighttpd/lighttpd1.4/blob/b31e7840d5403bc640579135b7004793b9ccd6c0/src/buffer.c#L840
 	//   - https://github.com/lighttpd/lighttpd1.4/blob/77c01f981725512653c01cde5ca74c11633dfec4/src/t/test_buffer.c
@@ -36,7 +36,7 @@ char *simplify_request_path(const char *str) {
 	char *out;
 	char *slash;
 
-	A_CALLOC(simplified, strlen(str) + 1);
+	US_CALLOC(simplified, strlen(str) + 1);
 
 	if (str[0] == '\0') {
 		simplified[0] = '\0';
@@ -74,7 +74,7 @@ char *simplify_request_path(const char *str) {
 		// (out <= str) still true; also now (slash < out)
 
 		if (ch == '/' || ch == '\0') {
-			size_t toklen = out - slash;
+			const size_t toklen = out - slash;
 
 			if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') {
 				// "/../" or ("/.." at end of string)
@@ -112,7 +112,7 @@ char *simplify_request_path(const char *str) {
 #ifdef TEST_HTTP_PATH
 
 int test_simplify_request_path(const char *sample, const char *expected) {
-	char *result = simplify_request_path(sample);
+	char *result = us_simplify_request_path(sample);
 	int retval = -!!strcmp(result, expected);
 
 	printf("Testing '%s' -> '%s' ... ", sample, expected);
diff --git a/src/ustreamer/http/path.h b/src/ustreamer/http/path.h
index 394ddfd..1ee1dcd 100644
--- a/src/ustreamer/http/path.h
+++ b/src/ustreamer/http/path.h
@@ -31,4 +31,4 @@
 #include "../../libs/tools.h"
 
 
-char *simplify_request_path(const char *str);
+char *us_simplify_request_path(const char *str);
diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c
index db8f499..fab9ae4 100644
--- a/src/ustreamer/http/server.c
+++ b/src/ustreamer/http/server.c
@@ -23,11 +23,12 @@
 #include "server.h"
 
 
-static int _http_preprocess_request(struct evhttp_request *request, server_s *server);
+static int _http_preprocess_request(struct evhttp_request *request, us_server_s *server);
 
 static int _http_check_run_compat_action(struct evhttp_request *request, void *v_server);
 
 static void _http_callback_root(struct evhttp_request *request, void *v_server);
+static void _http_callback_favicon(struct evhttp_request *request, void *v_server);
 static void _http_callback_static(struct evhttp_request *request, void *v_server);
 static void _http_callback_state(struct evhttp_request *request, void *v_server);
 static void _http_callback_snapshot(struct evhttp_request *request, void *v_server);
@@ -38,31 +39,36 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
 
 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(server_s *server, bool stream_updated, bool frame_updated);
+static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated);
 
-static bool _expose_new_frame(server_s *server);
+static bool _expose_new_frame(us_server_s *server);
 
+static const char *_http_get_header(struct evhttp_request *request, const char *key);
 static char *_http_get_client_hostport(struct evhttp_request *request);
 
 
-#define RUN(_next)		server->run->_next
-#define STREAM(_next)	RUN(stream->_next)
-#define VID(_next)		STREAM(run->video->_next)
-#define EX(_next)		RUN(exposed->_next)
+#define _A_EVBUFFER_NEW(x_buf)						assert((x_buf = evbuffer_new()) != NULL)
+#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 _RUN(x_next)	server->run->x_next
+#define _STREAM(x_next)	_RUN(stream->x_next)
+#define _VID(x_next)	_STREAM(run->video->x_next)
+#define _EX(x_next)		_RUN(exposed->x_next)
 
-server_s *server_init(stream_s *stream) {
-	exposed_s *exposed;
-	A_CALLOC(exposed, 1);
-	exposed->frame = frame_init();
 
-	server_runtime_s *run;
-	A_CALLOC(run, 1);
+us_server_s *us_server_init(us_stream_s *stream) {
+	us_exposed_s *exposed;
+	US_CALLOC(exposed, 1);
+	exposed->frame = us_frame_init();
+
+	us_server_runtime_s *run;
+	US_CALLOC(run, 1);
 	run->stream = stream;
 	run->exposed = exposed;
 
-	server_s *server;
-	A_CALLOC(server, 1);
+	us_server_s *server;
+	US_CALLOC(server, 1);
 	server->host = "127.0.0.1";
 	server->port = 8080;
 	server->unix_path = "";
@@ -70,160 +76,156 @@ server_s *server_init(stream_s *stream) {
 	server->passwd = "";
 	server->static_path = "";
 	server->allow_origin = "";
+	server->instance_id = "";
 	server->timeout = 10;
 	server->run = run;
 
 	assert(!evthread_use_pthreads());
-	assert((run->base = event_base_new()));
-	assert((run->http = evhttp_new(run->base)));
+	assert((run->base = event_base_new()) != NULL);
+	assert((run->http = evhttp_new(run->base)) != NULL);
 	evhttp_set_allowed_methods(run->http, EVHTTP_REQ_GET|EVHTTP_REQ_HEAD|EVHTTP_REQ_OPTIONS);
 	return server;
 }
 
-void server_destroy(server_s *server) {
-	if (RUN(refresher)) {
-		event_del(RUN(refresher));
-		event_free(RUN(refresher));
+void us_server_destroy(us_server_s *server) {
+	if (_RUN(refresher) != NULL) {
+		event_del(_RUN(refresher));
+		event_free(_RUN(refresher));
 	}
 
-	if (RUN(request_watcher)) {
-		event_del(RUN(request_watcher));
-		event_free(RUN(request_watcher));
+	if (_RUN(request_watcher) != NULL) {
+		event_del(_RUN(request_watcher));
+		event_free(_RUN(request_watcher));
 	}
 
-	evhttp_free(RUN(http));
-	if (RUN(ext_fd)) {
-		close(RUN(ext_fd));
+	evhttp_free(_RUN(http));
+	if (_RUN(ext_fd) >= 0) {
+		close(_RUN(ext_fd));
 	}
-	event_base_free(RUN(base));
+	event_base_free(_RUN(base));
 
 #	if LIBEVENT_VERSION_NUMBER >= 0x02010100
 	libevent_global_shutdown();
 #	endif
 
-	LIST_ITERATE(RUN(stream_clients), client, {
+	US_LIST_ITERATE(_RUN(stream_clients), client, {
 		free(client->key);
 		free(client->hostport);
 		free(client);
 	});
 
-	if (RUN(auth_token)) {
-		free(RUN(auth_token));
-	}
+	US_DELETE(_RUN(auth_token), free);
 
-	frame_destroy(EX(frame));
-	free(RUN(exposed));
+	us_frame_destroy(_EX(frame));
+	free(_RUN(exposed));
 	free(server->run);
 	free(server);
 }
 
-int server_listen(server_s *server) {
+int us_server_listen(us_server_s *server) {
 	{
 		if (server->static_path[0] != '\0') {
-			LOG_INFO("Enabling HTTP file server: %s", server->static_path);
-			evhttp_set_gencb(RUN(http), _http_callback_static, (void *)server);
+			US_LOG_INFO("Enabling HTTP file server: %s", server->static_path);
+			evhttp_set_gencb(_RUN(http), _http_callback_static, (void *)server);
 		} else {
-			assert(!evhttp_set_cb(RUN(http), "/", _http_callback_root, (void *)server));
+			assert(!evhttp_set_cb(_RUN(http), "/", _http_callback_root, (void *)server));
+			assert(!evhttp_set_cb(_RUN(http), "/favicon.ico", _http_callback_favicon, (void *)server));
 		}
-		assert(!evhttp_set_cb(RUN(http), "/state", _http_callback_state, (void *)server));
-		assert(!evhttp_set_cb(RUN(http), "/snapshot", _http_callback_snapshot, (void *)server));
-		assert(!evhttp_set_cb(RUN(http), "/stream", _http_callback_stream, (void *)server));
+		assert(!evhttp_set_cb(_RUN(http), "/state", _http_callback_state, (void *)server));
+		assert(!evhttp_set_cb(_RUN(http), "/snapshot", _http_callback_snapshot, (void *)server));
+		assert(!evhttp_set_cb(_RUN(http), "/stream", _http_callback_stream, (void *)server));
 	}
 
-	frame_copy(STREAM(blank), EX(frame));
-	EX(notify_last_width) = EX(frame->width);
-	EX(notify_last_height) = EX(frame->height);
+	us_frame_copy(_STREAM(blank), _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) = get_now_monotonic();
+		_RUN(last_request_ts) = us_get_now_monotonic();
 		struct timeval interval = {0};
 		interval.tv_usec = 100000;
-		assert((RUN(request_watcher) = event_new(RUN(base), -1, EV_PERSIST, _http_request_watcher, server)));
-		assert(!event_add(RUN(request_watcher), &interval));
+		assert((_RUN(request_watcher) = event_new(_RUN(base), -1, EV_PERSIST, _http_request_watcher, server)) != NULL);
+		assert(!event_add(_RUN(request_watcher), &interval));
 	}
 
 	{
 		struct timeval interval = {0};
-		if (STREAM(dev->desired_fps) > 0) {
-			interval.tv_usec = 1000000 / (STREAM(dev->desired_fps) * 2);
+		if (_STREAM(dev->desired_fps) > 0) {
+			interval.tv_usec = 1000000 / (_STREAM(dev->desired_fps) * 2);
 		} else {
 			interval.tv_usec = 16000; // ~60fps
 		}
-		assert((RUN(refresher) = event_new(RUN(base), -1, EV_PERSIST, _http_refresher, server)));
-		assert(!event_add(RUN(refresher), &interval));
+		assert((_RUN(refresher) = event_new(_RUN(base), -1, EV_PERSIST, _http_refresher, server)) != NULL);
+		assert(!event_add(_RUN(refresher), &interval));
 	}
 
-	evhttp_set_timeout(RUN(http), server->timeout);
+	evhttp_set_timeout(_RUN(http), server->timeout);
 
 	if (server->user[0] != '\0') {
 		char *encoded_token = NULL;
 
 		char *raw_token;
-		A_ASPRINTF(raw_token, "%s:%s", server->user, server->passwd);
-		base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL);
+		US_ASPRINTF(raw_token, "%s:%s", server->user, server->passwd);
+		us_base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL);
 		free(raw_token);
 
-		A_ASPRINTF(RUN(auth_token), "Basic %s", encoded_token);
+		US_ASPRINTF(_RUN(auth_token), "Basic %s", encoded_token);
 		free(encoded_token);
 
-		LOG_INFO("Using HTTP basic auth");
+		US_LOG_INFO("Using HTTP basic auth");
 	}
 
 	if (server->unix_path[0] != '\0') {
-		LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
-		if ((RUN(ext_fd) = evhttp_my_bind_unix(
-			RUN(http),
+		US_LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
+		if ((_RUN(ext_fd) = us_evhttp_bind_unix(
+			_RUN(http),
 			server->unix_path,
 			server->unix_rm,
 			server->unix_mode)) < 0
 		) {
 			return -1;
 		}
-		LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
+		US_LOG_INFO("Listening HTTP on UNIX socket '%s'", server->unix_path);
 
 #	ifdef WITH_SYSTEMD
 	} else if (server->systemd) {
-		LOG_DEBUG("Binding HTTP to systemd socket ...");
-		if ((RUN(ext_fd) = evhttp_my_bind_systemd(RUN(http))) < 0) {
+		US_LOG_DEBUG("Binding HTTP to systemd socket ...");
+		if ((_RUN(ext_fd) = us_evhttp_bind_systemd(_RUN(http))) < 0) {
 			return -1;
 		}
-		LOG_INFO("Listening systemd socket ...");
+		US_LOG_INFO("Listening systemd socket ...");
 #	endif
 
 	} else {
-		LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
-		if (evhttp_bind_socket(RUN(http), server->host, server->port) < 0) {
-			LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
+		US_LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
+		if (evhttp_bind_socket(_RUN(http), server->host, server->port) < 0) {
+			US_LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
 			return -1;
 		}
-		LOG_INFO("Listening HTTP on [%s]:%u", server->host, server->port);
+		US_LOG_INFO("Listening HTTP on [%s]:%u", server->host, server->port);
 	}
 
 	return 0;
 }
 
-void server_loop(server_s *server) {
-	LOG_INFO("Starting HTTP eventloop ...");
-	event_base_dispatch(RUN(base));
-	LOG_INFO("HTTP eventloop stopped");
+void us_server_loop(us_server_s *server) {
+	US_LOG_INFO("Starting HTTP eventloop ...");
+	event_base_dispatch(_RUN(base));
+	US_LOG_INFO("HTTP eventloop stopped");
 }
 
-void server_loop_break(server_s *server) {
-	event_base_loopbreak(RUN(base));
+void us_server_loop_break(us_server_s *server) {
+	event_base_loopbreak(_RUN(base));
 }
 
-#define GET_HEADER(_key) \
-	evhttp_find_header(evhttp_request_get_input_headers(request), _key)
+#define ADD_HEADER(x_key, x_value) assert(!evhttp_add_header(evhttp_request_get_output_headers(request), x_key, x_value))
 
-#define ADD_HEADER(_key, _value) \
-	assert(!evhttp_add_header(evhttp_request_get_output_headers(request), _key, _value))
-
-static int _http_preprocess_request(struct evhttp_request *request, server_s *server) {
-	RUN(last_request_ts) = get_now_monotonic();
+static int _http_preprocess_request(struct evhttp_request *request, us_server_s *server) {
+	_RUN(last_request_ts) = us_get_now_monotonic();
 
 	if (server->allow_origin[0] != '\0') {
-		const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
-		const char *cors_method = GET_HEADER("Access-Control-Request-Method");
+		const char *const cors_headers = _http_get_header(request, "Access-Control-Request-Headers");
+		const char *const cors_method = _http_get_header(request, "Access-Control-Request-Method");
 
 		ADD_HEADER("Access-Control-Allow-Origin", server->allow_origin);
 		ADD_HEADER("Access-Control-Allow-Credentials", "true");
@@ -240,10 +242,10 @@ static int _http_preprocess_request(struct evhttp_request *request, server_s *se
 		return -1;
 	}
 
-	if (RUN(auth_token)) {
-		const char *token = GET_HEADER("Authorization");
+	if (_RUN(auth_token) != NULL) {
+		const char *const token = _http_get_header(request, "Authorization");
 
-		if (token == NULL || strcmp(token, RUN(auth_token)) != 0) {
+		if (token == NULL || strcmp(token, _RUN(auth_token)) != 0) {
 			ADD_HEADER("WWW-Authenticate", "Basic realm=\"Restricted area\"");
 			evhttp_send_reply(request, 401, "Unauthorized", NULL);
 			return -1;
@@ -271,7 +273,7 @@ static int _http_check_run_compat_action(struct evhttp_request *request, void *v
 	int error = 0;
 
 	evhttp_parse_query(evhttp_request_get_uri(request), &params);
-	const char *action = evhttp_find_header(&params, "action");
+	const char *const action = evhttp_find_header(&params, "action");
 
 	if (action && !strcmp(action, "snapshot")) {
 		_http_callback_snapshot(request, v_server);
@@ -294,23 +296,36 @@ static int _http_check_run_compat_action(struct evhttp_request *request, void *v
 	}
 
 static void _http_callback_root(struct evhttp_request *request, void *v_server) {
-	server_s *server = (server_s *)v_server;
+	us_server_s *const server = (us_server_s *)v_server;
 
 	PREPROCESS_REQUEST;
 	COMPAT_REQUEST;
 
 	struct evbuffer *buf;
-
-	assert((buf = evbuffer_new()));
-	assert(evbuffer_add_printf(buf, "%s", HTML_INDEX_PAGE));
+	_A_EVBUFFER_NEW(buf);
+	_A_EVBUFFER_ADD_PRINTF(buf, "%s", US_HTML_INDEX_PAGE);
 	ADD_HEADER("Content-Type", "text/html");
 	evhttp_send_reply(request, HTTP_OK, "OK", buf);
 
 	evbuffer_free(buf);
 }
 
+static void _http_callback_favicon(struct evhttp_request *request, void *v_server) {
+	us_server_s *const server = (us_server_s *)v_server;
+
+	PREPROCESS_REQUEST;
+
+	struct evbuffer *buf;
+	_A_EVBUFFER_NEW(buf);
+	_A_EVBUFFER_ADD(buf, (const void *)US_FAVICON_ICO_DATA, US_FAVICON_ICO_DATA_SIZE);
+	ADD_HEADER("Content-Type", "image/x-icon");
+	evhttp_send_reply(request, HTTP_OK, "OK", buf);
+
+	evbuffer_free(buf);
+}
+
 static void _http_callback_static(struct evhttp_request *request, void *v_server) {
-	server_s *server = (server_s *)v_server;
+	us_server_s *const server = (us_server_s *)v_server;
 
 	PREPROCESS_REQUEST;
 	COMPAT_REQUEST;
@@ -322,7 +337,7 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
 	int fd = -1;
 
 	{
-		char *uri_path;
+		const char *uri_path;
 
 		if ((uri = evhttp_uri_parse(evhttp_request_get_uri(request))) == NULL) {
 			goto bad_request;
@@ -336,14 +351,14 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
 		}
 	}
 
-	assert((buf = evbuffer_new()));
+	_A_EVBUFFER_NEW(buf);
 
-	if ((static_path = find_static_file_path(server->static_path, decoded_path)) == NULL) {
+	if ((static_path = us_find_static_file_path(server->static_path, decoded_path)) == NULL) {
 		goto not_found;
 	}
 
 	if ((fd = open(static_path, O_RDONLY)) < 0) {
-		LOG_PERROR("HTTP: Can't open found static file %s", static_path);
+		US_LOG_PERROR("HTTP: Can't open found static file %s", static_path);
 		goto not_found;
 	}
 
@@ -351,11 +366,11 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
 		struct stat st;
 
 		if (fstat(fd, &st) < 0) {
-			LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
+			US_LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
 			goto not_found;
 		}
 		if (st.st_size > 0 && evbuffer_add_file(buf, fd, 0, st.st_size) < 0) {
-			LOG_ERROR("HTTP: Can't serve static file %s", static_path);
+			US_LOG_ERROR("HTTP: Can't serve static file %s", static_path);
 			goto not_found;
 		}
 
@@ -363,7 +378,7 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
 		// and will close it when finished transferring data
 		fd = -1;
 
-		ADD_HEADER("Content-Type", guess_mime_type(static_path));
+		ADD_HEADER("Content-Type", us_guess_mime_type(static_path));
 		evhttp_send_reply(request, HTTP_OK, "OK", buf);
 		goto cleanup;
 	}
@@ -380,97 +395,91 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
 		if (fd >= 0) {
 			close(fd);
 		}
-		if (static_path) {
-			free(static_path);
-		}
-		if (buf) {
-			evbuffer_free(buf);
-		}
-		if (decoded_path) {
-			free(decoded_path);
-		}
-		if (uri) {
-			evhttp_uri_free(uri);
-		}
+		US_DELETE(static_path, free);
+		US_DELETE(buf, evbuffer_free);
+		US_DELETE(decoded_path, free);
+		US_DELETE(uri, evhttp_uri_free);
 }
 
 #undef COMPAT_REQUEST
 
 static void _http_callback_state(struct evhttp_request *request, void *v_server) {
-	server_s *server = (server_s *)v_server;
+	us_server_s *const server = (us_server_s *)v_server;
 
 	PREPROCESS_REQUEST;
 
-	encoder_type_e enc_type;
+	us_encoder_type_e enc_type;
 	unsigned enc_quality;
-	encoder_get_runtime_params(STREAM(enc), &enc_type, &enc_quality);
+	us_encoder_get_runtime_params(_STREAM(enc), &enc_type, &enc_quality);
 
 	struct evbuffer *buf;
-	assert((buf = evbuffer_new()));
+	_A_EVBUFFER_NEW(buf);
 
-	assert(evbuffer_add_printf(buf,
+	_A_EVBUFFER_ADD_PRINTF(buf,
 		"{\"ok\": true, \"result\": {"
+		" \"instance_id\": \"%s\","
 		" \"encoder\": {\"type\": \"%s\", \"quality\": %u},",
-		encoder_type_to_string(enc_type),
+		server->instance_id,
+		us_encoder_type_to_string(enc_type),
 		enc_quality
-	));
+	);
 
-	if (STREAM(run->h264)) {
-		assert(evbuffer_add_printf(buf,
+	if (_STREAM(run->h264) != NULL) {
+		_A_EVBUFFER_ADD_PRINTF(buf,
 			" \"h264\": {\"bitrate\": %u, \"gop\": %u, \"online\": %s},",
-			STREAM(h264_bitrate),
-			STREAM(h264_gop),
-			bool_to_string(atomic_load(&STREAM(run->h264->online)))
-		));
+			_STREAM(h264_bitrate),
+			_STREAM(h264_gop),
+			us_bool_to_string(atomic_load(&_STREAM(run->h264->online)))
+		);
 	}
 
-	if (STREAM(sink) || STREAM(h264_sink)) {
-		assert(evbuffer_add_printf(buf, " \"sinks\": {"));
-		if (STREAM(sink)) {
-			assert(evbuffer_add_printf(buf,
+	if (_STREAM(sink) != NULL || _STREAM(h264_sink) != NULL) {
+		_A_EVBUFFER_ADD_PRINTF(buf, " \"sinks\": {");
+		if (_STREAM(sink) != NULL) {
+			_A_EVBUFFER_ADD_PRINTF(buf,
 				"\"jpeg\": {\"has_clients\": %s}",
-				bool_to_string(atomic_load(&STREAM(sink->has_clients)))
-			));
+				us_bool_to_string(atomic_load(&_STREAM(sink->has_clients)))
+			);
 		}
-		if (STREAM(h264_sink)) {
-			assert(evbuffer_add_printf(buf,
+		if (_STREAM(h264_sink) != NULL) {
+			_A_EVBUFFER_ADD_PRINTF(buf,
 				"%s\"h264\": {\"has_clients\": %s}",
-				(STREAM(sink) ? ", " : ""),
-				bool_to_string(atomic_load(&STREAM(h264_sink->has_clients)))
-			));
+				(_STREAM(sink) ? ", " : ""),
+				us_bool_to_string(atomic_load(&_STREAM(h264_sink->has_clients)))
+			);
 		}
-		assert(evbuffer_add_printf(buf, "},"));
+		_A_EVBUFFER_ADD_PRINTF(buf, "},");
 	}
 
-	assert(evbuffer_add_printf(buf,
+	_A_EVBUFFER_ADD_PRINTF(buf,
 		" \"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)),
-		bool_to_string(EX(frame->online)),
-		STREAM(dev->desired_fps),
-		EX(captured_fps),
-		EX(queued_fps),
-		RUN(stream_clients_count)
-	));
-
-	LIST_ITERATE(RUN(stream_clients), client, {
-		assert(evbuffer_add_printf(buf,
+		(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),
+		_RUN(stream_clients_count)
+	);
+
+	US_LIST_ITERATE(_RUN(stream_clients), client, {
+		_A_EVBUFFER_ADD_PRINTF(buf,
 			"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
 			" \"dual_final_frames\": %s, \"zero_data\": %s, \"key\": \"%s\"}%s",
 			client->id,
 			client->fps,
-			bool_to_string(client->extra_headers),
-			bool_to_string(client->advance_headers),
-			bool_to_string(client->dual_final_frames),
-			bool_to_string(client->zero_data),
+			us_bool_to_string(client->extra_headers),
+			us_bool_to_string(client->advance_headers),
+			us_bool_to_string(client->dual_final_frames),
+			us_bool_to_string(client->zero_data),
 			(client->key != NULL ? client->key : "0"),
 			(client->next ? ", " : "")
-		));
+		);
 	});
 
-	assert(evbuffer_add_printf(buf, "}}}}"));
+	_A_EVBUFFER_ADD_PRINTF(buf, "}}}}");
 
 	ADD_HEADER("Content-Type", "application/json");
 	evhttp_send_reply(request, HTTP_OK, "OK", buf);
@@ -478,13 +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) {
-	server_s *server = (server_s *)v_server;
+	us_server_s *const server = (us_server_s *)v_server;
 
 	PREPROCESS_REQUEST;
 
 	struct evbuffer *buf;
-	assert((buf = evbuffer_new()));
-	assert(!evbuffer_add(buf, (const void *)EX(frame->data), EX(frame->used)));
+	_A_EVBUFFER_NEW(buf);
+	_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");
@@ -492,29 +501,29 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
 
 	char header_buf[256];
 
-#	define ADD_TIME_HEADER(_key, _value) { \
-			snprintf(header_buf, 255, "%.06Lf", _value); \
-			ADD_HEADER(_key, header_buf); \
+#	define ADD_TIME_HEADER(x_key, x_value) { \
+			snprintf(header_buf, 255, "%.06Lf", x_value); \
+			ADD_HEADER(x_key, header_buf); \
 		}
 
-#	define ADD_UNSIGNED_HEADER(_key, _value) { \
-			snprintf(header_buf, 255, "%u", _value); \
-			ADD_HEADER(_key, header_buf); \
+#	define ADD_UNSIGNED_HEADER(x_key, x_value) { \
+			snprintf(header_buf, 255, "%u", x_value); \
+			ADD_HEADER(x_key, header_buf); \
 		}
 
-	ADD_TIME_HEADER("X-Timestamp", get_now_real());
+	ADD_TIME_HEADER("X-Timestamp", us_get_now_real());
 
-	ADD_HEADER("X-UStreamer-Online",						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",			get_now_monotonic());
+	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
 #	undef ADD_TIME_HEADER
@@ -534,16 +543,14 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
 	// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L791
 	// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L1458
 
-	server_s *server = (server_s *)v_server;
+	us_server_s *const server = (us_server_s *)v_server;
 
 	PREPROCESS_REQUEST;
 
-	struct evhttp_connection *conn;
-	conn = evhttp_request_get_connection(request);
-
-	if (conn) {
-		stream_client_s *client;
-		A_CALLOC(client, 1);
+	struct evhttp_connection *const conn = evhttp_request_get_connection(request);
+	if (conn != NULL) {
+		us_stream_client_s *client;
+		US_CALLOC(client, 1);
 		client->server = server;
 		client->request = request;
 		client->need_initial = true;
@@ -551,7 +558,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
 
 		struct evkeyvalq params;
 		evhttp_parse_query(evhttp_request_get_uri(request), &params);
-#		define PARSE_PARAM(_type, _name) client->_name = uri_get_##_type(&params, #_name)
+#		define PARSE_PARAM(x_type, x_name) client->x_name = us_uri_get_##x_type(&params, #x_name)
 		PARSE_PARAM(string, key);
 		PARSE_PARAM(true, extra_headers);
 		PARSE_PARAM(true, advance_headers);
@@ -561,29 +568,28 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
 		evhttp_clear_headers(&params);
 
 		client->hostport = _http_get_client_hostport(request);
-		client->id = get_now_id();
+		client->id = us_get_now_id();
 
-		LIST_APPEND_C(RUN(stream_clients), client, RUN(stream_clients_count));
+		US_LIST_APPEND_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
 
-		if (RUN(stream_clients_count) == 1) {
-			atomic_store(&VID(has_clients), true);
+		if (_RUN(stream_clients_count) == 1) {
+			atomic_store(&_VID(has_clients), true);
 #			ifdef WITH_GPIO
-			gpio_set_has_http_clients(true);
+			us_gpio_set_has_http_clients(true);
 #			endif
 		}
 
-		LOG_INFO("HTTP: Registered client: %s, id=%" PRIx64 "; clients now: %u",
-			client->hostport, client->id, RUN(stream_clients_count));
+		US_LOG_INFO("HTTP: NEW client (now=%u): %s, id=%" PRIx64,
+			_RUN(stream_clients_count), client->hostport, client->id);
 
-		struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
-		if (server->tcp_nodelay && !RUN(ext_fd)) {
-			evutil_socket_t fd;
+		struct bufferevent *const buf_event = evhttp_connection_get_bufferevent(conn);
+		if (server->tcp_nodelay && !_RUN(ext_fd)) {
+			US_LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client %s ...", client->hostport);
+			const evutil_socket_t fd = bufferevent_getfd(buf_event);
+			assert(fd >= 0);
 			int on = 1;
-
-			LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client %s ...", client->hostport);
-			assert((fd = bufferevent_getfd(buf_event)) >= 0);
 			if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) != 0) {
-				LOG_PERROR("HTTP: Can't set TCP_NODELAY to the client %s", client->hostport);
+				US_LOG_PERROR("HTTP: Can't set TCP_NODELAY to the client %s", client->hostport);
 			}
 		}
 		bufferevent_setcb(buf_event, NULL, NULL, _http_callback_stream_error, (void *)client);
@@ -598,12 +604,11 @@ 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) {
 #	define BOUNDARY "boundarydonotcross"
 
-	stream_client_s *client = (stream_client_s *)v_client;
-	struct evhttp_request *request = client->request; // for GET_HEADER
-	server_s *server = client->server;
+	us_stream_client_s *const client = (us_stream_client_s *)v_client;
+	us_server_s *const server = client->server;
 
-	long double now = get_now_monotonic();
-	long long now_second = floor_ms(now);
+	const long double now = us_get_now_monotonic();
+	const long long now_second = us_floor_ms(now);
 
 	if (now_second != client->fps_accum_second) {
 		client->fps = client->fps_accum;
@@ -613,7 +618,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
 	client->fps_accum += 1;
 
 	struct evbuffer *buf;
-	assert((buf = evbuffer_new()));
+	_A_EVBUFFER_NEW(buf);
 
 	// В хроме и его производных есть фундаментальный баг: он отрисовывает
 	// фрейм с задержкой на один, как только ему придут заголовки следующего.
@@ -635,30 +640,30 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
 	// по тем же причинам, по которым у нас нет Content-Length.
 
 #	define ADD_ADVANCE_HEADERS \
-		assert(evbuffer_add_printf(buf, \
-			"Content-Type: image/jpeg" RN "X-Timestamp: %.06Lf" RN RN, get_now_real()))
+		_A_EVBUFFER_ADD_PRINTF(buf, \
+			"Content-Type: image/jpeg" RN "X-Timestamp: %.06Lf" RN RN, us_get_now_real())
 
 	if (client->need_initial) {
-		assert(evbuffer_add_printf(buf, "HTTP/1.0 200 OK" RN));
+		_A_EVBUFFER_ADD_PRINTF(buf, "HTTP/1.0 200 OK" RN);
 		
 		if (client->server->allow_origin[0] != '\0') {
-			const char *cors_headers = GET_HEADER("Access-Control-Request-Headers");
-			const char *cors_method = GET_HEADER("Access-Control-Request-Method");
+			const char *const cors_headers = _http_get_header(client->request, "Access-Control-Request-Headers");
+			const char *const cors_method = _http_get_header(client->request, "Access-Control-Request-Method");
 
-			assert(evbuffer_add_printf(buf,
+			_A_EVBUFFER_ADD_PRINTF(buf,
 				"Access-Control-Allow-Origin: %s" RN
 				"Access-Control-Allow-Credentials: true" RN,
 				client->server->allow_origin				
-			));
+			);
 			if (cors_headers != NULL) {
-				assert(evbuffer_add_printf(buf, "Access-Control-Allow-Headers: %s" RN, cors_headers));
+				_A_EVBUFFER_ADD_PRINTF(buf, "Access-Control-Allow-Headers: %s" RN, cors_headers);
 			}
 			if (cors_method != NULL) {
-				assert(evbuffer_add_printf(buf, "Access-Control-Allow-Methods: %s" RN, cors_method));
+				_A_EVBUFFER_ADD_PRINTF(buf, "Access-Control-Allow-Methods: %s" RN, cors_method);
 			}
 		}
 
-		assert(evbuffer_add_printf(buf,
+		_A_EVBUFFER_ADD_PRINTF(buf,
 			"Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0" RN
 			"Pragma: no-cache" RN
 			"Expires: Mon, 3 Jan 2000 12:34:56 GMT" RN
@@ -668,7 +673,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
 			"--" BOUNDARY RN,
 			(client->key != NULL ? client->key : "0"),
 			client->id
-		));
+		);
 
 		if (client->advance_headers) {
 			ADD_ADVANCE_HEADERS;
@@ -679,17 +684,17 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
 	}
 
 	if (!client->advance_headers) {
-		assert(evbuffer_add_printf(buf,
+		_A_EVBUFFER_ADD_PRINTF(buf,
 			"Content-Type: image/jpeg" RN
 			"Content-Length: %zu" RN
 			"X-Timestamp: %.06Lf" RN
 			"%s",
-			(!client->zero_data ? EX(frame->used) : 0),
-			get_now_real(),
+			(!client->zero_data ? _EX(frame->used) : 0),
+			us_get_now_real(),
 			(client->extra_headers ? "" : RN)
-		));
+		);
 		if (client->extra_headers) {
-			assert(evbuffer_add_printf(buf,
+			_A_EVBUFFER_ADD_PRINTF(buf,
 				"X-UStreamer-Online: %s" RN
 				"X-UStreamer-Dropped: %u" RN
 				"X-UStreamer-Width: %u" RN
@@ -704,27 +709,27 @@ 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,
-				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) {
-		assert(!evbuffer_add(buf, (void *)EX(frame->data), EX(frame->used)));
+		_A_EVBUFFER_ADD(buf, (void *)_EX(frame->data), _EX(frame->used));
 	}
-	assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN));
+	_A_EVBUFFER_ADD_PRINTF(buf, RN "--" BOUNDARY RN);
 
 	if (client->advance_headers) {
 		ADD_ADVANCE_HEADERS;
@@ -741,47 +746,45 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
 }
 
 static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UNUSED short what, void *v_client) {
-	stream_client_s *client = (stream_client_s *)v_client;
-	server_s *server = client->server;
+	us_stream_client_s *const client = (us_stream_client_s *)v_client;
+	us_server_s *const server = client->server;
 
-	LIST_REMOVE_C(RUN(stream_clients), client, RUN(stream_clients_count));
+	US_LIST_REMOVE_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
 
-	if (RUN(stream_clients_count) == 0) {
-		atomic_store(&VID(has_clients), false);
+	if (_RUN(stream_clients_count) == 0) {
+		atomic_store(&_VID(has_clients), false);
 #		ifdef WITH_GPIO
-		gpio_set_has_http_clients(false);
+		us_gpio_set_has_http_clients(false);
 #		endif
 	}
 
-	char *reason = bufferevent_my_format_reason(what);
-	LOG_INFO("HTTP: Disconnected client: %s, id=%" PRIx64 ", %s; clients now: %u",
-		client->hostport, client->id, reason, RUN(stream_clients_count));
+	char *const reason = us_bufferevent_format_reason(what);
+	US_LOG_INFO("HTTP: DEL client (now=%u): %s, id=%" PRIx64 ", %s",
+		_RUN(stream_clients_count), client->hostport, client->id, reason);
 	free(reason);
 
-	struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
-	if (conn) {
-		evhttp_connection_free(conn);
-	}
+	struct evhttp_connection *const conn = evhttp_request_get_connection(client->request);
+	US_DELETE(conn, evhttp_connection_free);
 
 	free(client->key);
 	free(client->hostport);
 	free(client);
 }
 
-static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated) {
+static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated) {
 	bool has_clients = false;
 	bool queued = false;
 
-	LIST_ITERATE(RUN(stream_clients), client, {
-		struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
-		if (conn) {
+	US_LIST_ITERATE(_RUN(stream_clients), client, {
+		struct evhttp_connection *const conn = evhttp_request_get_connection(client->request);
+		if (conn != NULL) {
 			// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
 			// WebKit отрисовывает последний фрейм в серии с некоторой задержкой,
 			// и нужно послать два фрейма, чтобы серия была вовремя завершена.
 			// Это похоже на баг Blink (см. _http_callback_stream_write() и advance_headers),
 			// но фикс для него не лечит проблему вебкита. Такие дела.
 
-			bool dual_update = (
+			const bool dual_update = (
 				server->drop_same_frames
 				&& client->dual_final_frames
 				&& stream_updated
@@ -790,7 +793,7 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
 			);
 
 			if (dual_update || frame_updated || client->need_first_frame) {
-				struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
+				struct bufferevent *const buf_event = evhttp_connection_get_bufferevent(conn);
 				bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
 				bufferevent_enable(buf_event, EV_READ|EV_WRITE);
 
@@ -808,45 +811,45 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
 	if (queued) {
 		static unsigned queued_fps_accum = 0;
 		static long long queued_fps_second = 0;
-		long long now = floor_ms(get_now_monotonic());
+		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;
 	}
 }
 
 static void _http_request_watcher(UNUSED int fd, UNUSED short what, void *v_server) {
-	server_s *server = (server_s *)v_server;
-	const long double now = get_now_monotonic();
+	us_server_s *server = (us_server_s *)v_server;
+	const long double now = us_get_now_monotonic();
 
-	if (stream_has_clients(RUN(stream))) {
-		RUN(last_request_ts) = now;
-	} else if (RUN(last_request_ts) + server->exit_on_no_clients < now) {
-		LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...",
+	if (us_stream_has_clients(_RUN(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 ...",
 			server->exit_on_no_clients);
-		process_suicide();
-		RUN(last_request_ts) = now;
+		us_process_suicide();
+		_RUN(last_request_ts) = now;
 	}
 }
 
 static void _http_refresher(UNUSED int fd, UNUSED short what, void *v_server) {
-	server_s *server = (server_s *)v_server;
+	us_server_s *server = (us_server_s *)v_server;
 	bool stream_updated = false;
 	bool frame_updated = false;
 
-	if (atomic_load(&VID(updated))) {
+	if (atomic_load(&_VID(updated))) {
 		frame_updated = _expose_new_frame(server);
 		stream_updated = true;
-	} else if (EX(expose_end_ts) + 1 < get_now_monotonic()) {
-		LOG_DEBUG("HTTP: Repeating exposed ...");
-		EX(expose_begin_ts) = get_now_monotonic();
-		EX(expose_cmp_ts) = EX(expose_begin_ts);
-		EX(expose_end_ts) = EX(expose_begin_ts);
+	} 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);
 		frame_updated = true;
 		stream_updated = true;
 	}
@@ -857,85 +860,82 @@ static void _http_refresher(UNUSED int fd, UNUSED 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);
-		process_notify_parent();
+		_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(server_s *server) {
+static bool _expose_new_frame(us_server_s *server) {
 	bool updated = false;
 
-	A_MUTEX_LOCK(&VID(mutex));
+	US_MUTEX_LOCK(_VID(mutex));
 
-	LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", VID(frame->online));
+	US_LOG_DEBUG("HTTP: Updating exposed frame (online=%d) ...", _VID(frame->online));
 
-	EX(captured_fps) = VID(captured_fps);
-	EX(expose_begin_ts) = get_now_monotonic();
+	_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 && _VID(frame->online)) {
 		bool need_drop = false;
 		bool maybe_same = false;
 		if (
-			(need_drop = (EX(dropped) < server->drop_same_frames))
-			&& (maybe_same = frame_compare(EX(frame), VID(frame)))
+			(need_drop = (_EX(dropped) < server->drop_same_frames))
+			&& (maybe_same = us_frame_compare(_EX(frame), _VID(frame)))
 		) {
-			EX(expose_cmp_ts) = get_now_monotonic();
-			EX(expose_end_ts) = EX(expose_cmp_ts);
-			LOG_VERBOSE("HTTP: Dropped same frame number %u; cmp_time=%.06Lf",
-				EX(dropped), EX(expose_cmp_ts) - EX(expose_begin_ts));
-			EX(dropped) += 1;
+			_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;
 		} else {
-			EX(expose_cmp_ts) = get_now_monotonic();
-			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)));
+			_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)));
 		}
 	}
 
-	frame_copy(VID(frame), EX(frame));
+	us_frame_copy(_VID(frame), _EX(frame));
 
-	EX(dropped) = 0;
-	EX(expose_cmp_ts) = EX(expose_begin_ts);
-	EX(expose_end_ts) = get_now_monotonic();
+	_EX(dropped) = 0;
+	_EX(expose_cmp_ts) = _EX(expose_begin_ts);
+	_EX(expose_end_ts) = us_get_now_monotonic();
 
-	LOG_VERBOSE("HTTP: Exposed frame: online=%d, exp_time=%.06Lf",
-		 EX(frame->online), EX(expose_end_ts) - EX(expose_begin_ts));
+	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);
-		A_MUTEX_UNLOCK(&VID(mutex));
+		atomic_store(&_VID(updated), false);
+		US_MUTEX_UNLOCK(_VID(mutex));
 		return updated;
 }
 
-#undef EX
-#undef VID
-#undef STREAM
-#undef RUN
+static const char *_http_get_header(struct evhttp_request *request, const char *key) {
+	return evhttp_find_header(evhttp_request_get_input_headers(request), key);
+}
 
 static char *_http_get_client_hostport(struct evhttp_request *request) {
 	char *addr = NULL;
 	unsigned short port = 0;
 	struct evhttp_connection *conn = evhttp_request_get_connection(request);
-	if (conn) {
+	if (conn != NULL) {
 		char *peer;
 		evhttp_connection_get_peer(conn, &peer, &port);
-		assert(addr = strdup(peer));
+		addr = us_strdup(peer);
 	}
 
-	const char *xff = GET_HEADER("X-Forwarded-For");
-	if (xff) {
-		if (addr) {
-			free(addr);
-		}
-		assert(addr = strndup(xff, 1024));
+	const char *xff = _http_get_header(request, "X-Forwarded-For");
+	if (xff != NULL) {
+		US_DELETE(addr, free);
+		assert((addr = strndup(xff, 1024)) != NULL);
 		for (unsigned index = 0; addr[index]; ++index) {
 			if (addr[index] == ',') {
 				addr[index] = '\0';
@@ -945,13 +945,11 @@ static char *_http_get_client_hostport(struct evhttp_request *request) {
 	}
 
 	if (addr == NULL) {
-		assert(addr = strdup("???"));
+		addr = us_strdup("???");
 	}
 
 	char *hostport;
-	A_ASPRINTF(hostport, "[%s]:%u", addr, port);
+	US_ASPRINTF(hostport, "[%s]:%u", addr, port);
 	free(addr);
 	return hostport;
 }
-
-#undef GET_HEADER
diff --git a/src/ustreamer/http/server.h b/src/ustreamer/http/server.h
index 9b122ae..4c7f141 100644
--- a/src/ustreamer/http/server.h
+++ b/src/ustreamer/http/server.h
@@ -60,6 +60,7 @@
 #include "../../libs/base64.h"
 #include "../../libs/list.h"
 #include "../data/index_html.h"
+#include "../data/favicon_ico.h"
 #include "../encoder.h"
 #include "../stream.h"
 #ifdef WITH_GPIO
@@ -76,30 +77,30 @@
 #endif
 
 
-typedef struct stream_client_sx {
-	struct server_sx *server;
-	struct evhttp_request *request;
+typedef struct us_stream_client_sx {
+	struct us_server_sx		*server;
+	struct evhttp_request	*request;
 
-	char			*key;
-	bool			extra_headers;
-	bool			advance_headers;
-	bool			dual_final_frames;
-	bool			zero_data;
+	char		*key;
+	bool		extra_headers;
+	bool		advance_headers;
+	bool		dual_final_frames;
+	bool		zero_data;
 
-	char			*hostport;
-	uint64_t		id;
-	bool			need_initial;
-	bool			need_first_frame;
-	bool			updated_prev;
-	unsigned		fps;
-	unsigned		fps_accum;
-	long long		fps_accum_second;
+	char		*hostport;
+	uint64_t	id;
+	bool		need_initial;
+	bool		need_first_frame;
+	bool		updated_prev;
+	unsigned	fps;
+	unsigned	fps_accum;
+	long long	fps_accum_second;
 
-	LIST_STRUCT(struct stream_client_sx);
-} stream_client_s;
+	US_LIST_STRUCT(struct us_stream_client_sx);
+} us_stream_client_s;
 
 typedef struct {
-	frame_s			*frame;
+	us_frame_s		*frame;
 	unsigned		captured_fps;
 	unsigned		queued_fps;
 	unsigned		dropped;
@@ -107,10 +108,10 @@ typedef struct {
 	long double		expose_cmp_ts;
 	long double		expose_end_ts;
 
-	bool		notify_last_online;
-	unsigned	notify_last_width;
-	unsigned	notify_last_height;
-} exposed_s;
+	bool			notify_last_online;
+	unsigned		notify_last_width;
+	unsigned		notify_last_height;
+} us_exposed_s;
 
 typedef struct {
 	struct event_base	*base;
@@ -123,14 +124,14 @@ typedef struct {
 	long double			last_request_ts;
 
 	struct event		*refresher;
-	stream_s			*stream;
-	exposed_s			*exposed;
+	us_stream_s			*stream;
+	us_exposed_s			*exposed;
 
-	stream_client_s		*stream_clients;
+	us_stream_client_s	*stream_clients;
 	unsigned			stream_clients_count;
-} server_runtime_s;
+} us_server_runtime_s;
 
-typedef struct server_sx {
+typedef struct us_server_sx {
 	char		*host;
 	unsigned	port;
 
@@ -149,6 +150,7 @@ typedef struct server_sx {
 	char		*passwd;
 	char		*static_path;
 	char		*allow_origin;
+	char		*instance_id;
 
 	unsigned	drop_same_frames;
 	unsigned	fake_width;
@@ -157,13 +159,13 @@ typedef struct server_sx {
 	bool		notify_parent;
 	unsigned	exit_on_no_clients;
 
-	server_runtime_s *run;
-} server_s;
+	us_server_runtime_s *run;
+} us_server_s;
 
 
-server_s *server_init(stream_s *stream);
-void server_destroy(server_s *server);
+us_server_s *us_server_init(us_stream_s *stream);
+void us_server_destroy(us_server_s *server);
 
-int server_listen(server_s *server);
-void server_loop(server_s *server);
-void server_loop_break(server_s *server);
+int us_server_listen(us_server_s *server);
+void us_server_loop(us_server_s *server);
+void us_server_loop_break(us_server_s *server);
diff --git a/src/ustreamer/http/static.c b/src/ustreamer/http/static.c
index 7902f1e..75c718a 100644
--- a/src/ustreamer/http/static.c
+++ b/src/ustreamer/http/static.c
@@ -23,29 +23,29 @@
 #include "static.h"
 
 
-char *find_static_file_path(const char *root_path, const char *request_path) {
+char *us_find_static_file_path(const char *root_path, const char *request_path) {
 	char *path = NULL;
 
-	char *simplified_path = simplify_request_path(request_path);
+	char *const simplified_path = us_simplify_request_path(request_path);
 	if (simplified_path[0] == '\0') {
-		LOG_VERBOSE("HTTP: Invalid request path %s to static", request_path);
+		US_LOG_VERBOSE("HTTP: Invalid request path %s to static", request_path);
 		goto error;
 	}
 
-	A_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 16); // + reserved for /index.html
+	US_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 16); // + reserved for /index.html
 	sprintf(path, "%s/%s", root_path, simplified_path);
 
 	struct stat st;
 #	define LOAD_STAT { \
 			if (lstat(path, &st) < 0) { \
-				LOG_VERBOSE_PERROR("HTTP: Can't stat() static path %s", path); \
+				US_LOG_VERBOSE_PERROR("HTTP: Can't stat() static path %s", path); \
 				goto error; \
 			} \
 		}
 
 	LOAD_STAT;
 	if (S_ISDIR(st.st_mode)) {
-		LOG_VERBOSE("HTTP: Requested static path %s is a directory, trying %s/index.html", path, path);
+		US_LOG_VERBOSE("HTTP: Requested static path %s is a directory, trying %s/index.html", path, path);
 		strcat(path, "/index.html");
 		LOAD_STAT;
 	}
@@ -53,21 +53,19 @@ char *find_static_file_path(const char *root_path, const char *request_path) {
 #	undef LOAD_STAT
 
 	if (!S_ISREG(st.st_mode)) {
-		LOG_VERBOSE("HTTP: Not a regular file: %s", path);
+		US_LOG_VERBOSE("HTTP: Not a regular file: %s", path);
 		goto error;
 	}
 
 	if (access(path, R_OK) < 0) {
-		LOG_VERBOSE_PERROR("HTTP: Can't access() R_OK file %s", path);
+		US_LOG_VERBOSE_PERROR("HTTP: Can't access() R_OK file %s", path);
 		goto error;
 	}
 
 	goto ok;
 
 	error:
-		if (path) {
-			free(path);
-		}
+		US_DELETE(path, free);
 		path = NULL;
 
 	ok:
diff --git a/src/ustreamer/http/static.h b/src/ustreamer/http/static.h
index c6b7734..62ed72c 100644
--- a/src/ustreamer/http/static.h
+++ b/src/ustreamer/http/static.h
@@ -35,4 +35,4 @@
 #include "path.h"
 
 
-char *find_static_file_path(const char *root_path, const char *request_path);
+char *us_find_static_file_path(const char *root_path, const char *request_path);
diff --git a/src/ustreamer/http/systemd/systemd.c b/src/ustreamer/http/systemd/systemd.c
index d006ff5..40a8429 100644
--- a/src/ustreamer/http/systemd/systemd.c
+++ b/src/ustreamer/http/systemd/systemd.c
@@ -23,10 +23,10 @@
 #include "systemd.h"
 
 
-evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http) {
-	int fds = sd_listen_fds(1);
+evutil_socket_t us_evhttp_bind_systemd(struct evhttp *http) {
+	const int fds = sd_listen_fds(1);
 	if (fds < 1) {
-		LOG_ERROR("No available systemd sockets");
+		US_LOG_ERROR("No available systemd sockets");
 		return -1;
 	}
 
@@ -39,7 +39,7 @@ evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http) {
 	assert(!evutil_make_socket_nonblocking(fd));
 
 	if (evhttp_accept_socket(http, fd) < 0) {
-		LOG_PERROR("Can't evhttp_accept_socket() systemd socket");
+		US_LOG_PERROR("Can't evhttp_accept_socket() systemd socket");
 		return -1;
 	}
 	return fd;
diff --git a/src/ustreamer/http/systemd/systemd.h b/src/ustreamer/http/systemd/systemd.h
index 6494cb3..60a02b5 100644
--- a/src/ustreamer/http/systemd/systemd.h
+++ b/src/ustreamer/http/systemd/systemd.h
@@ -34,4 +34,4 @@
 #include "../../../libs/logging.h"
 
 
-evutil_socket_t evhttp_my_bind_systemd(struct evhttp *http);
+evutil_socket_t us_evhttp_bind_systemd(struct evhttp *http);
diff --git a/src/ustreamer/http/unix.c b/src/ustreamer/http/unix.c
index fa855f8..d480e10 100644
--- a/src/ustreamer/http/unix.c
+++ b/src/ustreamer/http/unix.c
@@ -23,13 +23,13 @@
 #include "unix.h"
 
 
-evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
+evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
 	struct sockaddr_un addr = {0};
 
 #	define MAX_SUN_PATH (sizeof(addr.sun_path) - 1)
 
 	if (strlen(path) > MAX_SUN_PATH) {
-		LOG_ERROR("UNIX socket path is too long; max=%zu", MAX_SUN_PATH);
+		US_LOG_ERROR("UNIX socket path is too long; max=%zu", MAX_SUN_PATH);
 		return -1;
 	}
 
@@ -38,30 +38,30 @@ evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool
 
 #	undef MAX_SUN_PATH
 
-	evutil_socket_t fd = -1;
-	assert((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
+	const evutil_socket_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
+	assert(fd >= 0);
 	assert(!evutil_make_socket_nonblocking(fd));
 
 	if (rm && unlink(path) < 0) {
 		if (errno != ENOENT) {
-			LOG_PERROR("Can't remove old UNIX socket '%s'", path);
+			US_LOG_PERROR("Can't remove old UNIX socket '%s'", path);
 			return -1;
 		}
 	}
 	if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
-		LOG_PERROR("Can't bind HTTP to UNIX socket '%s'", path);
+		US_LOG_PERROR("Can't bind HTTP to UNIX socket '%s'", path);
 		return -1;
 	}
 	if (mode && chmod(path, mode) < 0) {
-		LOG_PERROR("Can't set permissions %o to UNIX socket '%s'", mode, path);
+		US_LOG_PERROR("Can't set permissions %o to UNIX socket '%s'", mode, path);
 		return -1;
 	}
 	if (listen(fd, 128) < 0) {
-		LOG_PERROR("Can't listen UNIX socket '%s'", path);
+		US_LOG_PERROR("Can't listen UNIX socket '%s'", path);
 		return -1;
 	}
 	if (evhttp_accept_socket(http, fd) < 0) {
-		LOG_PERROR("Can't evhttp_accept_socket() UNIX socket '%s'", path);
+		US_LOG_PERROR("Can't evhttp_accept_socket() UNIX socket '%s'", path);
 		return -1;
 	}
 	return fd;
diff --git a/src/ustreamer/http/unix.h b/src/ustreamer/http/unix.h
index 29f9ec2..9a3ae34 100644
--- a/src/ustreamer/http/unix.h
+++ b/src/ustreamer/http/unix.h
@@ -39,4 +39,4 @@
 #include "../../libs/logging.h"
 
 
-evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode);
+evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode);
diff --git a/src/ustreamer/http/uri.c b/src/ustreamer/http/uri.c
index 24ed0c4..027e3fb 100644
--- a/src/ustreamer/http/uri.c
+++ b/src/ustreamer/http/uri.c
@@ -23,7 +23,7 @@
 #include "uri.h"
 
 
-bool uri_get_true(struct evkeyvalq *params, const char *key) {
+bool us_uri_get_true(struct evkeyvalq *params, const char *key) {
 	const char *value_str = evhttp_find_header(params, key);
 	if (value_str != NULL) {
 		if (
@@ -37,8 +37,8 @@ bool uri_get_true(struct evkeyvalq *params, const char *key) {
 	return false;
 }
 
-char *uri_get_string(struct evkeyvalq *params, const char *key) {
-	const char *value_str = evhttp_find_header(params, key);
+char *us_uri_get_string(struct evkeyvalq *params, const char *key) {
+	const char *const value_str = evhttp_find_header(params, key);
 	if (value_str != NULL) {
 		return evhttp_encode_uri(value_str);
 	}
diff --git a/src/ustreamer/http/uri.h b/src/ustreamer/http/uri.h
index 1fe225d..f559622 100644
--- a/src/ustreamer/http/uri.h
+++ b/src/ustreamer/http/uri.h
@@ -29,5 +29,5 @@
 #include <event2/keyvalq_struct.h>
 
 
-bool uri_get_true(struct evkeyvalq *params, const char *key);
-char *uri_get_string(struct evkeyvalq *params, const char *key);
+bool us_uri_get_true(struct evkeyvalq *params, const char *key);
+char *us_uri_get_string(struct evkeyvalq *params, const char *key);
diff --git a/src/ustreamer/m2m.c b/src/ustreamer/m2m.c
index 66d5893..0add7c1 100644
--- a/src/ustreamer/m2m.c
+++ b/src/ustreamer/m2m.c
@@ -23,52 +23,39 @@
 #include "m2m.h"
 
 
-static m2m_encoder_s *_m2m_encoder_init(
+static us_m2m_encoder_s *_m2m_encoder_init(
 	const char *name, const char *path, unsigned output_format,
-	unsigned fps, bool allow_dma, m2m_option_s *options);
+	unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma);
 
-static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame);
+static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame);
 
 static int _m2m_encoder_init_buffers(
-	m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
-	m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
+	us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
+	us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma);
 
-static void _m2m_encoder_cleanup(m2m_encoder_s *enc);
+static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc);
 
-static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key);
+static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key);
 
 
-#define E_LOG_ERROR(_msg, ...)		LOG_ERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
-#define E_LOG_PERROR(_msg, ...)		LOG_PERROR("%s: " _msg, enc->name, ##__VA_ARGS__)
-#define E_LOG_INFO(_msg, ...)		LOG_INFO("%s: " _msg, enc->name, ##__VA_ARGS__)
-#define E_LOG_VERBOSE(_msg, ...)	LOG_VERBOSE("%s: " _msg, enc->name, ##__VA_ARGS__)
-#define E_LOG_DEBUG(_msg, ...)		LOG_DEBUG("%s: " _msg, enc->name, ##__VA_ARGS__)
+#define _E_LOG_ERROR(x_msg, ...)	US_LOG_ERROR("%s: " x_msg, enc->name, ##__VA_ARGS__)
+#define _E_LOG_PERROR(x_msg, ...)	US_LOG_PERROR("%s: " x_msg, enc->name, ##__VA_ARGS__)
+#define _E_LOG_INFO(x_msg, ...)		US_LOG_INFO("%s: " x_msg, enc->name, ##__VA_ARGS__)
+#define _E_LOG_VERBOSE(x_msg, ...)	US_LOG_VERBOSE("%s: " x_msg, enc->name, ##__VA_ARGS__)
+#define _E_LOG_DEBUG(x_msg, ...)	US_LOG_DEBUG("%s: " x_msg, enc->name, ##__VA_ARGS__)
 
+#define _RUN(x_next) enc->run->x_next
 
-m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) {
-#	define OPTION(_key, _value) {#_key, V4L2_CID_MPEG_VIDEO_##_key, _value}
-
-	m2m_option_s options[] = {
-		OPTION(BITRATE, bitrate * 1000),
-		// OPTION(BITRATE_PEAK, bitrate * 1000),
-		OPTION(H264_I_PERIOD, gop),
-		OPTION(H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE),
-		OPTION(H264_LEVEL, V4L2_MPEG_VIDEO_H264_LEVEL_4_0),
-		OPTION(REPEAT_SEQ_HEADER, 1),
-		OPTION(H264_MIN_QP, 16),
-		OPTION(H264_MAX_QP, 32),
-		{NULL, 0, 0},
-	};
-
-#	undef OPTION
 
+us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop) {
 	// FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L2100
 	// По логике вещей правильно 0, но почему-то на низких разрешениях типа 640x480
 	// енкодер через несколько секунд перестает производить корректные фреймы.
-	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_H264, 30, true, options);
+	bitrate *= 1000; // From Kbps
+	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_H264, 30, bitrate, gop, 0, true);
 }
 
-m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
+us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality) {
 	const double b_min = 25;
 	const double b_max = 20000;
 	const double step = 25;
@@ -76,169 +63,175 @@ m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsign
 	bitrate = step * round(bitrate / step);
 	bitrate *= 1000; // From Kbps
 	assert(bitrate > 0);
-
-	m2m_option_s options[] = {
-		{"BITRATE", V4L2_CID_MPEG_VIDEO_BITRATE, bitrate},
-		{NULL, 0, 0},
-	};
-
 	// FIXME: То же самое про 30 or 0, но еще даже не проверено на низких разрешениях
-	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, 30, true, options);
+	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_MJPEG, 30, bitrate, 0, 0, true);
 }
 
-m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) {
-	m2m_option_s options[] = {
-		{"QUALITY", V4L2_CID_JPEG_COMPRESSION_QUALITY, quality},
-		{NULL, 0, 0},
-	};
-
+us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality) {
 	// FIXME: DMA не работает
-	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_JPEG, 30, false, options);
+	return _m2m_encoder_init(name, path, V4L2_PIX_FMT_JPEG, 30, 0, 0, quality, false);
 }
 
-void m2m_encoder_destroy(m2m_encoder_s *enc) {
-	E_LOG_INFO("Destroying encoder ...");
+void us_m2m_encoder_destroy(us_m2m_encoder_s *enc) {
+	_E_LOG_INFO("Destroying encoder ...");
 	_m2m_encoder_cleanup(enc);
-	free(enc->options);
 	free(enc->path);
 	free(enc->name);
 	free(enc);
 }
 
-#define RUN(_next) enc->run->_next
-
-int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key) {
-	frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
+int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
+	us_frame_encoding_begin(src, dest, (enc->output_format == V4L2_PIX_FMT_MJPEG ? V4L2_PIX_FMT_JPEG : enc->output_format));
 
 	if (
-		RUN(width) != src->width
-		|| RUN(height) != src->height
-		|| RUN(input_format) != src->format
-		|| RUN(stride) != src->stride
-		|| RUN(dma) != (enc->allow_dma && src->dma_fd >= 0)
+		_RUN(width) != src->width
+		|| _RUN(height) != src->height
+		|| _RUN(input_format) != src->format
+		|| _RUN(stride) != src->stride
+		|| _RUN(dma) != (enc->allow_dma && src->dma_fd >= 0)
 	) {
-		if (_m2m_encoder_prepare(enc, src) < 0) {
-			return -1;
-		}
+		_m2m_encoder_prepare(enc, src);
+	}
+	if (!_RUN(ready)) { // Already prepared but failed
+		return -1;
 	}
 
-	force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || RUN(last_online) != src->online));
+	force_key = (enc->output_format == V4L2_PIX_FMT_H264 && (force_key || _RUN(last_online) != src->online));
 
 	if (_m2m_encoder_compress_raw(enc, src, dest, force_key) < 0) {
 		_m2m_encoder_cleanup(enc);
-		E_LOG_ERROR("Encoder destroyed due an error (compress)");
+		_E_LOG_ERROR("Encoder destroyed due an error (compress)");
 		return -1;
 	}
 
-	frame_encoding_end(dest);
+	us_frame_encoding_end(dest);
 
-	E_LOG_VERBOSE("Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
+	_E_LOG_VERBOSE("Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
 		dest->used, dest->encode_end_ts - dest->encode_begin_ts, force_key);
 
-	RUN(last_online) = src->online;
+	_RUN(last_online) = src->online;
 	return 0;
 }
 
-static m2m_encoder_s *_m2m_encoder_init(
+static us_m2m_encoder_s *_m2m_encoder_init(
 	const char *name, const char *path, unsigned output_format,
-	unsigned fps, bool allow_dma, m2m_option_s *options) {
+	unsigned fps, unsigned bitrate, unsigned gop, unsigned quality, bool allow_dma) {
 
-	LOG_INFO("%s: Initializing encoder ...", name);
+	US_LOG_INFO("%s: Initializing encoder ...", name);
 
-	m2m_encoder_runtime_s *run;
-	A_CALLOC(run, 1);
+	us_m2m_encoder_runtime_s *run;
+	US_CALLOC(run, 1);
 	run->last_online = -1;
 	run->fd = -1;
 
-	m2m_encoder_s *enc;
-	A_CALLOC(enc, 1);
-	assert(enc->name = strdup(name));
+	us_m2m_encoder_s *enc;
+	US_CALLOC(enc, 1);
+	enc->name = us_strdup(name);
 	if (path == NULL) {
-		assert(enc->path = strdup(output_format == V4L2_PIX_FMT_JPEG ? "/dev/video31" : "/dev/video11"));
+		enc->path = us_strdup(output_format == V4L2_PIX_FMT_JPEG ? "/dev/video31" : "/dev/video11");
 	} else {
-		assert(enc->path = strdup(path));
+		enc->path = us_strdup(path);
 	}
 	enc->output_format = output_format;
 	enc->fps = fps;
+	enc->bitrate = bitrate;
+	enc->gop = gop;
+	enc->quality = quality;
 	enc->allow_dma = allow_dma;
 	enc->run = run;
-
-	unsigned count = 0;
-	for (; options[count].name != NULL; ++count);
-	++count;
-	A_CALLOC(enc->options, count);
-	memcpy(enc->options, options, sizeof(m2m_option_s) * count);
-
 	return enc;
 }
 
-#define E_XIOCTL(_request, _value, _msg, ...) { \
-		if (xioctl(RUN(fd), _request, _value) < 0) { \
-			E_LOG_PERROR(_msg, ##__VA_ARGS__); \
+#define _E_XIOCTL(x_request, x_value, x_msg, ...) { \
+		if (us_xioctl(_RUN(fd), x_request, x_value) < 0) { \
+			_E_LOG_PERROR(x_msg, ##__VA_ARGS__); \
 			goto error; \
 		} \
 	}
 
-static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
-	bool dma = (enc->allow_dma && frame->dma_fd >= 0);
+static void _m2m_encoder_prepare(us_m2m_encoder_s *enc, const us_frame_s *frame) {
+	const bool dma = (enc->allow_dma && frame->dma_fd >= 0);
 
-	E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
+	_E_LOG_INFO("Configuring encoder: DMA=%d ...", dma);
 
 	_m2m_encoder_cleanup(enc);
 
-	RUN(width) = frame->width;
-	RUN(height) = frame->height;
-	RUN(input_format) = frame->format;
-	RUN(stride) = frame->stride;
-	RUN(dma) = dma;
+	_RUN(width) = frame->width;
+	_RUN(height) = frame->height;
+	_RUN(input_format) = frame->format;
+	_RUN(stride) = frame->stride;
+	_RUN(dma) = dma;
 
-	if ((RUN(fd) = open(enc->path, O_RDWR)) < 0) {
-		E_LOG_PERROR("Can't open encoder device");
+	if ((_RUN(fd) = open(enc->path, O_RDWR)) < 0) {
+		_E_LOG_PERROR("Can't open encoder device");
 		goto error;
 	}
-	E_LOG_DEBUG("Encoder device fd=%d opened", RUN(fd));
-
-	for (m2m_option_s *option = enc->options; option->name != NULL; ++option) {
-		struct v4l2_control ctl = {0};
-		ctl.id = option->id;
-		ctl.value = option->value;
+	_E_LOG_DEBUG("Encoder device fd=%d opened", _RUN(fd));
+
+#	define SET_OPTION(x_cid, x_value) { \
+			struct v4l2_control m_ctl = {0}; \
+			m_ctl.id = x_cid; \
+			m_ctl.value = x_value; \
+			_E_LOG_DEBUG("Configuring option " #x_cid " ..."); \
+			_E_XIOCTL(VIDIOC_S_CTRL, &m_ctl, "Can't set option " #x_cid); \
+		}
 
-		E_LOG_DEBUG("Configuring option %s ...", option->name);
-		E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't set option %s", option->name);
+	if (enc->output_format == V4L2_PIX_FMT_H264) {
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE,				enc->bitrate);
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_I_PERIOD,		enc->gop);
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_PROFILE,		V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE);
+		if (_RUN(width) * _RUN(height) <= 1920 * 1080) { // https://forums.raspberrypi.com/viewtopic.php?t=291447#p1762296
+			SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL,		V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+		} else {
+			SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_LEVEL,		V4L2_MPEG_VIDEO_H264_LEVEL_5_1);
+		}
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER,	1);
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_MIN_QP,			16);
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_H264_MAX_QP,			32);
+	} else if (enc->output_format == V4L2_PIX_FMT_MJPEG) {
+		SET_OPTION(V4L2_CID_MPEG_VIDEO_BITRATE,				enc->bitrate);
+	} else if (enc->output_format == V4L2_PIX_FMT_JPEG) {
+		SET_OPTION(V4L2_CID_JPEG_COMPRESSION_QUALITY,		enc->quality);
 	}
 
+#	undef SET_OPTION
+
 	{
 		struct v4l2_format fmt = {0};
 		fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-		fmt.fmt.pix_mp.width = RUN(width);
-		fmt.fmt.pix_mp.height = RUN(height);
-		fmt.fmt.pix_mp.pixelformat = RUN(input_format);
+		fmt.fmt.pix_mp.width = _RUN(width);
+		fmt.fmt.pix_mp.height = _RUN(height);
+		fmt.fmt.pix_mp.pixelformat = _RUN(input_format);
 		fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
 		fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_JPEG; // libcamera currently has no means to request the right colour space
 		fmt.fmt.pix_mp.num_planes = 1;
-		// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = RUN(stride);
-		E_LOG_DEBUG("Configuring INPUT format ...");
-		E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
+		// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = _RUN(stride);
+		_E_LOG_DEBUG("Configuring INPUT format ...");
+		_E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set INPUT format");
 	}
 
 	{
 		struct v4l2_format fmt = {0};
 		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-		fmt.fmt.pix_mp.width = RUN(width);
-		fmt.fmt.pix_mp.height = RUN(height);
+		fmt.fmt.pix_mp.width = _RUN(width);
+		fmt.fmt.pix_mp.height = _RUN(height);
 		fmt.fmt.pix_mp.pixelformat = enc->output_format;
 		fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
 		fmt.fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
 		fmt.fmt.pix_mp.num_planes = 1;
 		// fmt.fmt.pix_mp.plane_fmt[0].bytesperline = 0;
-		// fmt.fmt.pix_mp.plane_fmt[0].sizeimage = 512 << 10;
-		E_LOG_DEBUG("Configuring OUTPUT format ...");
-		E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
+		if (enc->output_format == V4L2_PIX_FMT_H264) {
+			// https://github.com/pikvm/ustreamer/issues/169
+			// https://github.com/raspberrypi/linux/pull/5232
+			fmt.fmt.pix_mp.plane_fmt[0].sizeimage = (1024 + 512) << 10; // 1.5Mb
+		}
+		_E_LOG_DEBUG("Configuring OUTPUT format ...");
+		_E_XIOCTL(VIDIOC_S_FMT, &fmt, "Can't set OUTPUT format");
 		if (fmt.fmt.pix_mp.pixelformat != enc->output_format) {
 			char fourcc_str[8];
-			E_LOG_ERROR("The OUTPUT format can't be configured as %s",
-				fourcc_to_string(enc->output_format, fourcc_str, 8));
-			E_LOG_ERROR("In case of Raspberry Pi, try to append 'start_x=1' to /boot/config.txt");
+			_E_LOG_ERROR("The OUTPUT format can't be configured as %s",
+				us_fourcc_to_string(enc->output_format, fourcc_str, 8));
+			_E_LOG_ERROR("In case of Raspberry Pi, try to append 'start_x=1' to /boot/config.txt");
 			goto error;
 		}
 	}
@@ -248,62 +241,61 @@ static int _m2m_encoder_prepare(m2m_encoder_s *enc, const frame_s *frame) {
 		setfps.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
 		setfps.parm.output.timeperframe.numerator = 1;
 		setfps.parm.output.timeperframe.denominator = enc->fps;
-		E_LOG_DEBUG("Configuring INPUT FPS ...");
-		E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
+		_E_LOG_DEBUG("Configuring INPUT FPS ...");
+		_E_XIOCTL(VIDIOC_S_PARM, &setfps, "Can't set INPUT FPS");
 	}
 
 	if (_m2m_encoder_init_buffers(enc, (dma ? "INPUT-DMA" : "INPUT"), V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
-		&RUN(input_bufs), &RUN(n_input_bufs), dma) < 0) {
+		&_RUN(input_bufs), &_RUN(n_input_bufs), dma) < 0) {
 		goto error;
 	}
 	if (_m2m_encoder_init_buffers(enc, "OUTPUT", V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
-		&RUN(output_bufs), &RUN(n_output_bufs), false) < 0) {
+		&_RUN(output_bufs), &_RUN(n_output_bufs), false) < 0) {
 		goto error;
 	}
 
 	{
 		enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-		E_LOG_DEBUG("Starting INPUT ...");
-		E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start INPUT");
+		_E_LOG_DEBUG("Starting INPUT ...");
+		_E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start INPUT");
 
 		type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-		E_LOG_DEBUG("Starting OUTPUT ...");
-		E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
+		_E_LOG_DEBUG("Starting OUTPUT ...");
+		_E_XIOCTL(VIDIOC_STREAMON, &type, "Can't start OUTPUT");
 	}
 
-	RUN(ready) = true;
-	E_LOG_DEBUG("Encoder state: *** READY ***");
-	return 0;
+	_RUN(ready) = true;
+	_E_LOG_DEBUG("Encoder state: *** READY ***");
+	return;
 
 	error:
 		_m2m_encoder_cleanup(enc);
-		E_LOG_ERROR("Encoder destroyed due an error (prepare)");
-		return -1;
+		_E_LOG_ERROR("Encoder destroyed due an error (prepare)");
 }
 
 static int _m2m_encoder_init_buffers(
-	m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
-	m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
+	us_m2m_encoder_s *enc, const char *name, enum v4l2_buf_type type,
+	us_m2m_buffer_s **bufs_ptr, unsigned *n_bufs_ptr, bool dma) {
 
-	E_LOG_DEBUG("Initializing %s buffers ...", name);
+	_E_LOG_DEBUG("Initializing %s buffers ...", name);
 
 	struct v4l2_requestbuffers req = {0};
 	req.count = 1;
 	req.type = type;
 	req.memory = (dma ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP);
 
-	E_LOG_DEBUG("Requesting %u %s buffers ...", req.count, name);
-	E_XIOCTL(VIDIOC_REQBUFS, &req, "Can't request %s buffers", name);
+	_E_LOG_DEBUG("Requesting %u %s buffers ...", req.count, name);
+	_E_XIOCTL(VIDIOC_REQBUFS, &req, "Can't request %s buffers", name);
 	if (req.count < 1) {
-		E_LOG_ERROR("Insufficient %s buffer memory: %u", name, req.count);
+		_E_LOG_ERROR("Insufficient %s buffer memory: %u", name, req.count);
 		goto error;
 	}
-	E_LOG_DEBUG("Got %u %s buffers", req.count, name);
+	_E_LOG_DEBUG("Got %u %s buffers", req.count, name);
 
 	if (dma) {
 		*n_bufs_ptr = req.count;
 	} else {
-		A_CALLOC(*bufs_ptr, req.count);
+		US_CALLOC(*bufs_ptr, req.count);
 		for (*n_bufs_ptr = 0; *n_bufs_ptr < req.count; ++(*n_bufs_ptr)) {
 			struct v4l2_buffer buf = {0};
 			struct v4l2_plane plane = {0};
@@ -313,25 +305,26 @@ static int _m2m_encoder_init_buffers(
 			buf.length = 1;
 			buf.m.planes = &plane;
 
-			E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
-			E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
+			_E_LOG_DEBUG("Querying %s buffer=%u ...", name, *n_bufs_ptr);
+			_E_XIOCTL(VIDIOC_QUERYBUF, &buf, "Can't query %s buffer=%u", name, *n_bufs_ptr);
 
-			E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
+			_E_LOG_DEBUG("Mapping %s buffer=%u ...", name, *n_bufs_ptr);
 			if (((*bufs_ptr)[*n_bufs_ptr].data = mmap(
 				NULL,
 				plane.length,
 				PROT_READ | PROT_WRITE,
 				MAP_SHARED,
-				RUN(fd),
+				_RUN(fd),
 				plane.m.mem_offset
 			)) == MAP_FAILED) {
-				E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
+				_E_LOG_PERROR("Can't map %s buffer=%u", name, *n_bufs_ptr);
 				goto error;
 			}
+			assert((*bufs_ptr)[*n_bufs_ptr].data != NULL);
 			(*bufs_ptr)[*n_bufs_ptr].allocated = plane.length;
 
-			E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
-			E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
+			_E_LOG_DEBUG("Queuing %s buffer=%u ...", name, *n_bufs_ptr);
+			_E_XIOCTL(VIDIOC_QBUF, &buf, "Can't queue %s buffer=%u", name, *n_bufs_ptr);
 		}
 	}
 
@@ -340,13 +333,13 @@ static int _m2m_encoder_init_buffers(
 		return -1;
 }
 
-static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
-	if (RUN(ready)) {
-#		define STOP_STREAM(_name, _type) { \
-				enum v4l2_buf_type _type_var = _type; \
-				E_LOG_DEBUG("Stopping %s ...", _name); \
-				if (xioctl(RUN(fd), VIDIOC_STREAMOFF, &_type_var) < 0) { \
-					E_LOG_PERROR("Can't stop %s", _name); \
+static void _m2m_encoder_cleanup(us_m2m_encoder_s *enc) {
+	if (_RUN(ready)) {
+#		define STOP_STREAM(x_name, x_type) { \
+				enum v4l2_buf_type m_type_var = x_type; \
+				_E_LOG_DEBUG("Stopping %s ...", x_name); \
+				if (us_xioctl(_RUN(fd), VIDIOC_STREAMOFF, &m_type_var) < 0) { \
+					_E_LOG_PERROR("Can't stop %s", x_name); \
 				} \
 			}
 
@@ -356,19 +349,19 @@ static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
 #		undef STOP_STREAM
 	}
 
-#	define DESTROY_BUFFERS(_name, _target) { \
-		if (RUN(_target##_bufs)) { \
-			for (unsigned index = 0; index < RUN(n_##_target##_bufs); ++index) { \
-				if (RUN(_target##_bufs[index].allocated) > 0 && RUN(_target##_bufs[index].data) != MAP_FAILED) { \
-					if (munmap(RUN(_target##_bufs[index].data), RUN(_target##_bufs[index].allocated)) < 0) { \
-						E_LOG_PERROR("Can't unmap %s buffer=%u", #_name, index); \
+#	define DESTROY_BUFFERS(x_name, x_target) { \
+		if (_RUN(x_target##_bufs) != NULL) { \
+			for (unsigned m_index = 0; m_index < _RUN(n_##x_target##_bufs); ++m_index) { \
+				if (_RUN(x_target##_bufs[m_index].allocated) > 0 && _RUN(x_target##_bufs[m_index].data) != NULL) { \
+					if (munmap(_RUN(x_target##_bufs[m_index].data), _RUN(x_target##_bufs[m_index].allocated)) < 0) { \
+						_E_LOG_PERROR("Can't unmap %s buffer=%u", #x_name, m_index); \
 					} \
 				} \
 			} \
-			free(RUN(_target##_bufs)); \
-			RUN(_target##_bufs) = NULL; \
+			free(_RUN(x_target##_bufs)); \
+			_RUN(x_target##_bufs) = NULL; \
 		} \
-		RUN(n_##_target##_bufs) = 0; \
+		_RUN(n_##x_target##_bufs) = 0; \
 	}
 
 	DESTROY_BUFFERS("OUTPUT", output);
@@ -376,30 +369,30 @@ static void _m2m_encoder_cleanup(m2m_encoder_s *enc) {
 
 #	undef DESTROY_BUFFERS
 
-	if (RUN(fd) >= 0) {
-		if (close(RUN(fd)) < 0) {
-			E_LOG_PERROR("Can't close encoder device");
+	if (_RUN(fd) >= 0) {
+		if (close(_RUN(fd)) < 0) {
+			_E_LOG_PERROR("Can't close encoder device");
 		}
-		RUN(fd) = -1;
+		_RUN(fd) = -1;
 	}
 
-	RUN(last_online) = -1;
-	RUN(ready) = false;
+	_RUN(last_online) = -1;
+	_RUN(ready) = false;
 
-	E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
+	_E_LOG_DEBUG("Encoder state: ~~~ NOT READY ~~~");
 }
 
-static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key) {
-	assert(RUN(ready));
+static int _m2m_encoder_compress_raw(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key) {
+	assert(_RUN(ready));
 
-	E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
+	_E_LOG_DEBUG("Compressing new frame; force_key=%d ...", force_key);
 
 	if (force_key) {
 		struct v4l2_control ctl = {0};
 		ctl.id = V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME;
 		ctl.value = 1;
-		E_LOG_DEBUG("Forcing keyframe ...")
-		E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
+		_E_LOG_DEBUG("Forcing keyframe ...")
+		_E_XIOCTL(VIDIOC_S_CTRL, &ctl, "Can't force keyframe");
 	}
 
 	struct v4l2_buffer input_buf = {0};
@@ -408,25 +401,25 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
 	input_buf.length = 1;
 	input_buf.m.planes = &input_plane;
 
-	if (RUN(dma)) {
+	if (_RUN(dma)) {
 		input_buf.index = 0;
 		input_buf.memory = V4L2_MEMORY_DMABUF;
 		input_buf.field = V4L2_FIELD_NONE;
 		input_plane.m.fd = src->dma_fd;
-		E_LOG_DEBUG("Using INPUT-DMA buffer=%u", input_buf.index);
+		_E_LOG_DEBUG("Using INPUT-DMA buffer=%u", input_buf.index);
 	} else {
 		input_buf.memory = V4L2_MEMORY_MMAP;
-		E_LOG_DEBUG("Grabbing INPUT buffer ...");
-		E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
-		if (input_buf.index >= RUN(n_input_bufs)) {
-			E_LOG_ERROR("V4L2 error: grabbed invalid INPUT: buffer=%u, n_bufs=%u",
-				input_buf.index, RUN(n_input_bufs));
+		_E_LOG_DEBUG("Grabbing INPUT buffer ...");
+		_E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't grab INPUT buffer");
+		if (input_buf.index >= _RUN(n_input_bufs)) {
+			_E_LOG_ERROR("V4L2 error: grabbed invalid INPUT: buffer=%u, n_bufs=%u",
+				input_buf.index, _RUN(n_input_bufs));
 			goto error;
 		}
-		E_LOG_DEBUG("Grabbed INPUT buffer=%u", input_buf.index);
+		_E_LOG_DEBUG("Grabbed INPUT buffer=%u", input_buf.index);
 	}
 
-	uint64_t now = get_now_monotonic_u64();
+	const uint64_t now = us_get_now_monotonic_u64();
 	struct timeval ts = {
 		.tv_sec = now / 1000000,
 		.tv_usec = now % 1000000,
@@ -436,31 +429,31 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
 	input_buf.timestamp.tv_usec = ts.tv_usec;
 	input_plane.bytesused = src->used;
 	input_plane.length = src->used;
-	if (!RUN(dma)) {
-		memcpy(RUN(input_bufs[input_buf.index].data), src->data, src->used);
+	if (!_RUN(dma)) {
+		memcpy(_RUN(input_bufs[input_buf.index].data), src->data, src->used);
 	}
 
-	const char *input_name = (RUN(dma) ? "INPUT-DMA" : "INPUT");
+	const char *input_name = (_RUN(dma) ? "INPUT-DMA" : "INPUT");
 
-	E_LOG_DEBUG("Sending%s %s buffer ...", (!RUN(dma) ? " (releasing)" : ""), input_name);
-	E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
+	_E_LOG_DEBUG("Sending%s %s buffer ...", (!_RUN(dma) ? " (releasing)" : ""), input_name);
+	_E_XIOCTL(VIDIOC_QBUF, &input_buf, "Can't send %s buffer", input_name);
 
 	// Для не-DMA отправка буфера по факту являтся освобождением этого буфера
-	bool input_released = !RUN(dma);
+	bool input_released = !_RUN(dma);
 
 	while (true) {
-		struct pollfd enc_poll = {RUN(fd), POLLIN, 0};
+		struct pollfd enc_poll = {_RUN(fd), POLLIN, 0};
 
-		E_LOG_DEBUG("Polling encoder ...");
+		_E_LOG_DEBUG("Polling encoder ...");
 		if (poll(&enc_poll, 1, 1000) < 0 && errno != EINTR) {
-			E_LOG_PERROR("Can't poll encoder");
+			_E_LOG_PERROR("Can't poll encoder");
 			goto error;
 		}
 
 		if (enc_poll.revents & POLLIN) {
 			if (!input_released) {
-				E_LOG_DEBUG("Releasing %s buffer=%u ...", input_name, input_buf.index);
-				E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't release %s buffer=%u",
+				_E_LOG_DEBUG("Releasing %s buffer=%u ...", input_name, input_buf.index);
+				_E_XIOCTL(VIDIOC_DQBUF, &input_buf, "Can't release %s buffer=%u",
 					input_name, input_buf.index);
 				input_released = true;
 			}
@@ -471,23 +464,24 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
 			output_buf.memory = V4L2_MEMORY_MMAP;
 			output_buf.length = 1;
 			output_buf.m.planes = &output_plane;
-			E_LOG_DEBUG("Fetching OUTPUT buffer ...");
-			E_XIOCTL(VIDIOC_DQBUF, &output_buf, "Can't fetch OUTPUT buffer");
+			_E_LOG_DEBUG("Fetching OUTPUT buffer ...");
+			_E_XIOCTL(VIDIOC_DQBUF, &output_buf, "Can't fetch OUTPUT buffer");
 
 			bool done = false;
 			if (ts.tv_sec != output_buf.timestamp.tv_sec || ts.tv_usec != output_buf.timestamp.tv_usec) {
 				// Енкодер первый раз может выдать буфер с мусором и нулевым таймстампом,
 				// так что нужно убедиться, что мы читаем выходной буфер, соответствующий
 				// входному (с тем же таймстампом).
-				E_LOG_DEBUG("Need to retry OUTPUT buffer due timestamp mismatch");
+				_E_LOG_DEBUG("Need to retry OUTPUT buffer due timestamp mismatch");
 			} else {
-				frame_set_data(dest, RUN(output_bufs[output_buf.index].data), output_plane.bytesused);
+				us_frame_set_data(dest, _RUN(output_bufs[output_buf.index].data), output_plane.bytesused);
 				dest->key = output_buf.flags & V4L2_BUF_FLAG_KEYFRAME;
+				dest->gop = enc->gop;
 				done = true;
 			}
 
-			E_LOG_DEBUG("Releasing OUTPUT buffer=%u ...", output_buf.index);
-			E_XIOCTL(VIDIOC_QBUF, &output_buf, "Can't release OUTPUT buffer=%u", output_buf.index);
+			_E_LOG_DEBUG("Releasing OUTPUT buffer=%u ...", output_buf.index);
+			_E_XIOCTL(VIDIOC_QBUF, &output_buf, "Can't release OUTPUT buffer=%u", output_buf.index);
 
 			if (done) {
 				break;
@@ -500,12 +494,4 @@ static int _m2m_encoder_compress_raw(m2m_encoder_s *enc, const frame_s *src, fra
 		return -1;
 }
 
-#undef E_XIOCTL
-
-#undef RUN
-
-#undef E_LOG_DEBUG
-#undef E_LOG_VERBOSE
-#undef E_LOG_INFO
-#undef E_LOG_PERROR
-#undef E_LOG_ERROR
+#undef _E_XIOCTL
diff --git a/src/ustreamer/m2m.h b/src/ustreamer/m2m.h
index d8577fb..b1e6eda 100644
--- a/src/ustreamer/m2m.h
+++ b/src/ustreamer/m2m.h
@@ -39,53 +39,48 @@
 #include "../libs/tools.h"
 #include "../libs/logging.h"
 #include "../libs/frame.h"
-
-#include "xioctl.h"
+#include "../libs/xioctl.h"
 
 
 typedef struct {
 	uint8_t	*data;
 	size_t	allocated;
-} m2m_buffer_s;
-
-typedef struct {
-	char		*name;
-	uint32_t	id;
-	int32_t		value;
-} m2m_option_s;
+} us_m2m_buffer_s;
 
 typedef struct {
 	int				fd;
-	m2m_buffer_s	*input_bufs;
+	us_m2m_buffer_s	*input_bufs;
 	unsigned		n_input_bufs;
-	m2m_buffer_s	*output_bufs;
+	us_m2m_buffer_s	*output_bufs;
 	unsigned		n_output_bufs;
 
-	unsigned	width;
-	unsigned	height;
-	unsigned	input_format;
-	unsigned	stride;
-	bool		dma;
-	bool		ready;
+	unsigned		width;
+	unsigned		height;
+	unsigned		input_format;
+	unsigned		stride;
+	bool			dma;
+	bool			ready;
 
-	int last_online;
-} m2m_encoder_runtime_s;
+	int				last_online;
+} us_m2m_encoder_runtime_s;
 
 typedef struct {
 	char			*name;
 	char			*path;
 	unsigned		output_format;
 	unsigned		fps;
+	unsigned		bitrate;
+	unsigned		gop;
+	unsigned		quality;
 	bool			allow_dma;
-	m2m_option_s	*options;
 
-	m2m_encoder_runtime_s *run;
-} m2m_encoder_s;
+	us_m2m_encoder_runtime_s *run;
+} us_m2m_encoder_s;
 
 
-m2m_encoder_s *m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop);
-m2m_encoder_s *m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality);
-m2m_encoder_s *m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality);
-void m2m_encoder_destroy(m2m_encoder_s *enc);
+us_m2m_encoder_s *us_m2m_h264_encoder_init(const char *name, const char *path, unsigned bitrate, unsigned gop);
+us_m2m_encoder_s *us_m2m_mjpeg_encoder_init(const char *name, const char *path, unsigned quality);
+us_m2m_encoder_s *us_m2m_jpeg_encoder_init(const char *name, const char *path, unsigned quality);
+void us_m2m_encoder_destroy(us_m2m_encoder_s *enc);
 
-int m2m_encoder_compress(m2m_encoder_s *enc, const frame_s *src, frame_s *dest, bool force_key);
+int us_m2m_encoder_compress(us_m2m_encoder_s *enc, const us_frame_s *src, us_frame_s *dest, bool force_key);
diff --git a/src/ustreamer/main.c b/src/ustreamer/main.c
index 1039194..c0406e7 100644
--- a/src/ustreamer/main.c
+++ b/src/ustreamer/main.c
@@ -40,12 +40,9 @@
 #endif
 
 
-typedef struct {
-	stream_s *stream;
-	server_s *server;
-} _main_context_s;
+static us_stream_s	*_g_stream = NULL;
+static us_server_s	*_g_server = NULL;
 
-static _main_context_s *_ctx;
 
 static void _block_thread_signals(void) {
 	sigset_t mask;
@@ -56,27 +53,25 @@ static void _block_thread_signals(void) {
 }
 
 static void *_stream_loop_thread(UNUSED void *arg) {
-	A_THREAD_RENAME("stream");
+	US_THREAD_RENAME("stream");
 	_block_thread_signals();
-	stream_loop(_ctx->stream);
+	us_stream_loop(_g_stream);
 	return NULL;
 }
 
 static void *_server_loop_thread(UNUSED void *arg) {
-	A_THREAD_RENAME("http");
+	US_THREAD_RENAME("http");
 	_block_thread_signals();
-	server_loop(_ctx->server);
+	us_server_loop(_g_server);
 	return NULL;
 }
 
 static void _signal_handler(int signum) {
-	switch (signum) {
-		case SIGTERM:	LOG_INFO_NOLOCK("===== Stopping by SIGTERM ====="); break;
-		case SIGINT:	LOG_INFO_NOLOCK("===== Stopping by SIGINT ====="); break;
-		default:		LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
-	}
-	stream_loop_break(_ctx->stream);
-	server_loop_break(_ctx->server);
+	char *const name = us_signum_to_string(signum);
+	US_LOG_INFO_NOLOCK("===== Stopping by %s =====", name);
+	free(name);
+	us_stream_loop_break(_g_stream);
+	us_server_loop_break(_g_server);
 }
 
 static void _install_signal_handlers(void) {
@@ -87,13 +82,13 @@ static void _install_signal_handlers(void) {
 	assert(!sigaddset(&sig_act.sa_mask, SIGINT));
 	assert(!sigaddset(&sig_act.sa_mask, SIGTERM));
 
-	LOG_DEBUG("Installing SIGINT handler ...");
+	US_LOG_DEBUG("Installing SIGINT handler ...");
 	assert(!sigaction(SIGINT, &sig_act, NULL));
 
-	LOG_DEBUG("Installing SIGTERM handler ...");
+	US_LOG_DEBUG("Installing SIGTERM handler ...");
 	assert(!sigaction(SIGTERM, &sig_act, NULL));
 
-	LOG_DEBUG("Ignoring SIGPIPE ...");
+	US_LOG_DEBUG("Ignoring SIGPIPE ...");
 	assert(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
 }
 
@@ -101,55 +96,50 @@ int main(int argc, char *argv[]) {
 	assert(argc >= 0);
 	int exit_code = 0;
 
-	LOGGING_INIT;
-	A_THREAD_RENAME("main");
+	US_LOGGING_INIT;
+	US_THREAD_RENAME("main");
 
-	options_s *options = options_init(argc, argv);
-	device_s *dev = device_init();
-	encoder_s *enc = encoder_init();
-	stream_s *stream = stream_init(dev, enc);
-	server_s *server = server_init(stream);
+	us_options_s *options = us_options_init(argc, argv);
+	us_device_s *dev = us_device_init();
+	us_encoder_s *enc = us_encoder_init();
+	_g_stream = us_stream_init(dev, enc);
+	_g_server = us_server_init(_g_stream);
 
-	if ((exit_code = options_parse(options, dev, enc, stream, server)) == 0) {
+	if ((exit_code = options_parse(options, dev, enc, _g_stream, _g_server)) == 0) {
 #		ifdef WITH_GPIO
-		gpio_init();
+		us_gpio_init();
 #		endif
 
 		_install_signal_handlers();
 
-		_main_context_s ctx;
-		ctx.stream = stream;
-		ctx.server = server;
-		_ctx = &ctx;
-
-		if ((exit_code = server_listen(server)) == 0) {
+		if ((exit_code = us_server_listen(_g_server)) == 0) {
 #			ifdef WITH_GPIO
-			gpio_set_prog_running(true);
+			us_gpio_set_prog_running(true);
 #			endif
 
 			pthread_t stream_loop_tid;
 			pthread_t server_loop_tid;
-			A_THREAD_CREATE(&stream_loop_tid, _stream_loop_thread, NULL);
-			A_THREAD_CREATE(&server_loop_tid, _server_loop_thread, NULL);
-			A_THREAD_JOIN(server_loop_tid);
-			A_THREAD_JOIN(stream_loop_tid);
+			US_THREAD_CREATE(stream_loop_tid, _stream_loop_thread, NULL);
+			US_THREAD_CREATE(server_loop_tid, _server_loop_thread, NULL);
+			US_THREAD_JOIN(server_loop_tid);
+			US_THREAD_JOIN(stream_loop_tid);
 		}
 
 #		ifdef WITH_GPIO
-		gpio_set_prog_running(false);
-		gpio_destroy();
+		us_gpio_set_prog_running(false);
+		us_gpio_destroy();
 #		endif
 	}
 
-	server_destroy(server);
-	stream_destroy(stream);
-	encoder_destroy(enc);
-	device_destroy(dev);
-	options_destroy(options);
+	us_server_destroy(_g_server);
+	us_stream_destroy(_g_stream);
+	us_encoder_destroy(enc);
+	us_device_destroy(dev);
+	us_options_destroy(options);
 
 	if (exit_code == 0) {
-		LOG_INFO("Bye-bye");
+		US_LOG_INFO("Bye-bye");
 	}
-	LOGGING_DESTROY;
+	US_LOGGING_DESTROY;
 	return (exit_code < 0 ? 1 : 0);
 }
diff --git a/src/ustreamer/options.c b/src/ustreamer/options.c
index 4109b56..2d41e2a 100644
--- a/src/ustreamer/options.c
+++ b/src/ustreamer/options.c
@@ -23,7 +23,7 @@
 #include "options.h"
 
 
-enum _OPT_VALUES {
+enum _US_OPT_VALUES {
 	_O_DEVICE = 'd',
 	_O_INPUT = 'i',
 	_O_RESOLUTION = 'r',
@@ -82,15 +82,16 @@ enum _OPT_VALUES {
 	_O_PASSWD,
 	_O_STATIC,
 	_O_ALLOW_ORIGIN,
+	_O_INSTANCE_ID,
 	_O_TCP_NODELAY,
 	_O_SERVER_TIMEOUT,
 
-#	define ADD_SINK(_prefix) \
-		_O_##_prefix, \
-		_O_##_prefix##_MODE, \
-		_O_##_prefix##_RM, \
-		_O_##_prefix##_CLIENT_TTL, \
-		_O_##_prefix##_TIMEOUT,
+#	define ADD_SINK(x_prefix) \
+		_O_##x_prefix, \
+		_O_##x_prefix##_MODE, \
+		_O_##x_prefix##_RM, \
+		_O_##x_prefix##_CLIENT_TTL, \
+		_O_##x_prefix##_TIMEOUT,
 	ADD_SINK(SINK)
 	ADD_SINK(RAW_SINK)
 	ADD_SINK(H264_SINK)
@@ -177,16 +178,17 @@ static const struct option _LONG_OPTS[] = {
 	{"static",					required_argument,	NULL,	_O_STATIC},
 	{"drop-same-frames",		required_argument,	NULL,	_O_DROP_SAME_FRAMES},
 	{"allow-origin",			required_argument,	NULL,	_O_ALLOW_ORIGIN},
+	{"instance-id",				required_argument,	NULL,	_O_INSTANCE_ID},
 	{"fake-resolution",			required_argument,	NULL,	_O_FAKE_RESOLUTION},
 	{"tcp-nodelay",				no_argument,		NULL,	_O_TCP_NODELAY},
 	{"server-timeout",			required_argument,	NULL,	_O_SERVER_TIMEOUT},
 
-#	define ADD_SINK(_opt, _prefix) \
-		{_opt "sink",				required_argument,	NULL,	_O_##_prefix}, \
-		{_opt "sink-mode",			required_argument,	NULL,	_O_##_prefix##_MODE}, \
-		{_opt "sink-rm",			no_argument,		NULL,	_O_##_prefix##_RM}, \
-		{_opt "sink-client-ttl",	required_argument,	NULL,	_O_##_prefix##_CLIENT_TTL}, \
-		{_opt "sink-timeout",		required_argument,	NULL,	_O_##_prefix##_TIMEOUT},
+#	define ADD_SINK(x_opt, x_prefix) \
+		{x_opt "sink",				required_argument,	NULL,	_O_##x_prefix}, \
+		{x_opt "sink-mode",			required_argument,	NULL,	_O_##x_prefix##_MODE}, \
+		{x_opt "sink-rm",			no_argument,		NULL,	_O_##x_prefix##_RM}, \
+		{x_opt "sink-client-ttl",	required_argument,	NULL,	_O_##x_prefix##_CLIENT_TTL}, \
+		{x_opt "sink-timeout",		required_argument,	NULL,	_O_##x_prefix##_TIMEOUT},
 	ADD_SINK("", SINK)
 	ADD_SINK("raw-", RAW_SINK)
 	ADD_SINK("h264-", H264_SINK)
@@ -228,38 +230,31 @@ static const struct option _LONG_OPTS[] = {
 
 
 static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited);
+static int _check_instance_id(const char *str);
 
 static void _features(void);
-static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server);
+static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server);
 
 
-options_s *options_init(unsigned argc, char *argv[]) {
-	options_s *options;
-	A_CALLOC(options, 1);
+us_options_s *us_options_init(unsigned argc, char *argv[]) {
+	us_options_s *options;
+	US_CALLOC(options, 1);
 	options->argc = argc;
 	options->argv = argv;
 
-	A_CALLOC(options->argv_copy, argc);
+	US_CALLOC(options->argv_copy, argc);
 	for (unsigned index = 0; index < argc; ++index) {
-		assert(options->argv_copy[index] = strdup(argv[index]));
+		options->argv_copy[index] = us_strdup(argv[index]);
 	}
 	return options;
 }
 
-void options_destroy(options_s *options) {
-#	define ADD_SINK(_prefix) { \
-			if (options->_prefix) { \
-				memsink_destroy(options->_prefix); \
-			} \
-		}
-	ADD_SINK(sink);
-	ADD_SINK(raw_sink);
-	ADD_SINK(h264_sink);
-#	undef ADD_SINK
+void us_options_destroy(us_options_s *options) {
+	US_DELETE(options->sink, us_memsink_destroy);
+	US_DELETE(options->raw_sink, us_memsink_destroy);
+	US_DELETE(options->h264_sink, us_memsink_destroy);
 
-	if (options->blank) {
-		frame_destroy(options->blank);
-	}
+	US_DELETE(options->blank, us_frame_destroy);
 
 	for (unsigned index = 0; index < options->argc; ++index) {
 		free(options->argv_copy[index]);
@@ -270,32 +265,32 @@ void options_destroy(options_s *options) {
 }
 
 
-int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server) {
-#	define OPT_SET(_dest, _value) { \
-			_dest = _value; \
+int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server) {
+#	define OPT_SET(x_dest, x_value) { \
+			x_dest = x_value; \
 			break; \
 		}
 
-#	define OPT_NUMBER(_name, _dest, _min, _max, _base) { \
-			errno = 0; char *_end = NULL; long long _tmp = strtoll(optarg, &_end, _base); \
-			if (errno || *_end || _tmp < _min || _tmp > _max) { \
-				printf("Invalid value for '%s=%s': min=%lld, max=%lld\n", _name, optarg, (long long)_min, (long long)_max); \
+#	define OPT_NUMBER(x_name, x_dest, x_min, x_max, x_base) { \
+			errno = 0; char *m_end = NULL; const long long m_tmp = strtoll(optarg, &m_end, x_base); \
+			if (errno || *m_end || m_tmp < x_min || m_tmp > x_max) { \
+				printf("Invalid value for '%s=%s': min=%lld, max=%lld\n", x_name, optarg, (long long)x_min, (long long)x_max); \
 				return -1; \
 			} \
-			_dest = _tmp; \
+			x_dest = m_tmp; \
 			break; \
 		}
 
-#	define OPT_RESOLUTION(_name, _dest_width, _dest_height, _limited) { \
-			switch (_parse_resolution(optarg, &_dest_width, &_dest_height, _limited)) { \
+#	define OPT_RESOLUTION(x_name, x_dest_width, x_dest_height, x_limited) { \
+			switch (_parse_resolution(optarg, &x_dest_width, &x_dest_height, x_limited)) { \
 				case -1: \
-					printf("Invalid resolution format for '%s=%s'\n", _name, optarg); \
+					printf("Invalid resolution format for '%s=%s'\n", x_name, optarg); \
 					return -1; \
 				case -2: \
-					printf("Invalid width of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_WIDTH, VIDEO_MAX_WIDTH); \
+					printf("Invalid width of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_WIDTH, US_VIDEO_MAX_WIDTH); \
 					return -1; \
 				case -3: \
-					printf("Invalid height of '%s=%s': min=%u, max=%u\n", _name, optarg, VIDEO_MIN_HEIGHT, VIDEO_MAX_HEIGHT); \
+					printf("Invalid height of '%s=%s': min=%u, max=%u\n", x_name, optarg, US_VIDEO_MIN_HEIGHT, US_VIDEO_MAX_HEIGHT); \
 					return -1; \
 				case 0: break; \
 				default: assert(0 && "Unknown error"); \
@@ -303,48 +298,48 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 			break; \
 		}
 
-#	define OPT_PARSE(_name, _dest, _func, _invalid, _available) { \
-			if ((_dest = _func(optarg)) == _invalid) { \
-				printf("Unknown " _name ": %s; available: %s\n", optarg, _available); \
+#	define OPT_PARSE(x_name, x_dest, x_func, x_invalid, x_available) { \
+			if ((x_dest = x_func(optarg)) == x_invalid) { \
+				printf("Unknown " x_name ": %s; available: %s\n", optarg, x_available); \
 				return -1; \
 			} \
 			break; \
 		}
 
-#	define OPT_CTL_DEFAULT_NOBREAK(_dest) { \
-			dev->ctl._dest.mode = CTL_MODE_DEFAULT; \
+#	define OPT_CTL_DEFAULT_NOBREAK(x_dest) { \
+			dev->ctl.x_dest.mode = CTL_MODE_DEFAULT; \
 		}
 
-#	define OPT_CTL_MANUAL(_dest) { \
+#	define OPT_CTL_MANUAL(x_dest) { \
 			if (!strcasecmp(optarg, "default")) { \
-				OPT_CTL_DEFAULT_NOBREAK(_dest); \
+				OPT_CTL_DEFAULT_NOBREAK(x_dest); \
 			} else { \
-				dev->ctl._dest.mode = CTL_MODE_VALUE; \
-				OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
+				dev->ctl.x_dest.mode = CTL_MODE_VALUE; \
+				OPT_NUMBER("--"#x_dest, dev->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
 			} \
 			break; \
 		}
 
-#	define OPT_CTL_AUTO(_dest) { \
+#	define OPT_CTL_AUTO(x_dest) { \
 			if (!strcasecmp(optarg, "default")) { \
-				OPT_CTL_DEFAULT_NOBREAK(_dest); \
+				OPT_CTL_DEFAULT_NOBREAK(x_dest); \
 			} else if (!strcasecmp(optarg, "auto")) { \
-				dev->ctl._dest.mode = CTL_MODE_AUTO; \
+				dev->ctl.x_dest.mode = CTL_MODE_AUTO; \
 			} else { \
-				dev->ctl._dest.mode = CTL_MODE_VALUE; \
-				OPT_NUMBER("--"#_dest, dev->ctl._dest.value, INT_MIN, INT_MAX, 0); \
+				dev->ctl.x_dest.mode = CTL_MODE_VALUE; \
+				OPT_NUMBER("--"#x_dest, dev->ctl.x_dest.value, INT_MIN, INT_MAX, 0); \
 			} \
 			break; \
 		}
 
 	char *blank_path = NULL;
 
-#	define ADD_SINK(_prefix) \
-		char *_prefix##_name = NULL; \
-		mode_t _prefix##_mode = 0660; \
-		bool _prefix##_rm = false; \
-		unsigned _prefix##_client_ttl = 10; \
-		unsigned _prefix##_timeout = 1;
+#	define ADD_SINK(x_prefix) \
+		char *x_prefix##_name = NULL; \
+		mode_t x_prefix##_mode = 0660; \
+		bool x_prefix##_rm = false; \
+		unsigned x_prefix##_client_ttl = 10; \
+		unsigned x_prefix##_timeout = 1;
 	ADD_SINK(sink);
 	ADD_SINK(raw_sink);
 	ADD_SINK(h264_sink);
@@ -355,27 +350,27 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 #	endif
 
 	char short_opts[128];
-	build_short_options(_LONG_OPTS, short_opts, 128);
+	us_build_short_options(_LONG_OPTS, short_opts, 128);
 
 	for (int ch; (ch = getopt_long(options->argc, options->argv_copy, short_opts, _LONG_OPTS, NULL)) >= 0;) {
 		switch (ch) {
-			case _O_DEVICE:			OPT_SET(dev->path, optarg);
-			case _O_INPUT:			OPT_NUMBER("--input", dev->input, 0, 128, 0);
-			case _O_RESOLUTION:		OPT_RESOLUTION("--resolution", dev->width, dev->height, true);
+			case _O_DEVICE:				OPT_SET(dev->path, optarg);
+			case _O_INPUT:				OPT_NUMBER("--input", dev->input, 0, 128, 0);
+			case _O_RESOLUTION:			OPT_RESOLUTION("--resolution", dev->width, dev->height, true);
 #			pragma GCC diagnostic ignored "-Wsign-compare"
 #			pragma GCC diagnostic push
-			case _O_FORMAT:			OPT_PARSE("pixel format", dev->format, device_parse_format, FORMAT_UNKNOWN, FORMATS_STR);
+			case _O_FORMAT:				OPT_PARSE("pixel format", dev->format, us_device_parse_format, US_FORMAT_UNKNOWN, US_FORMATS_STR);
 #			pragma GCC diagnostic pop
-			case _O_TV_STANDARD:	OPT_PARSE("TV standard", dev->standard, device_parse_standard, STANDARD_UNKNOWN, STANDARDS_STR);
-			case _O_IO_METHOD:		OPT_PARSE("IO method", dev->io_method, device_parse_io_method, IO_METHOD_UNKNOWN, IO_METHODS_STR);
-			case _O_DESIRED_FPS:	OPT_NUMBER("--desired-fps", dev->desired_fps, 0, VIDEO_MAX_FPS, 0);
-			case _O_MIN_FRAME_SIZE:	OPT_NUMBER("--min-frame-size", dev->min_frame_size, 1, 8192, 0);
-			case _O_PERSISTENT:		OPT_SET(dev->persistent, true);
-			case _O_DV_TIMINGS:		OPT_SET(dev->dv_timings, true);
-			case _O_BUFFERS:		OPT_NUMBER("--buffers", dev->n_bufs, 1, 32, 0);
-			case _O_WORKERS:		OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
-			case _O_QUALITY:		OPT_NUMBER("--quality", dev->jpeg_quality, 1, 100, 0);
-			case _O_ENCODER:		OPT_PARSE("encoder type", enc->type, encoder_parse_type, ENCODER_TYPE_UNKNOWN, ENCODER_TYPES_STR);
+			case _O_TV_STANDARD:		OPT_PARSE("TV standard", dev->standard, us_device_parse_standard, US_STANDARD_UNKNOWN, US_STANDARDS_STR);
+			case _O_IO_METHOD:			OPT_PARSE("IO method", dev->io_method, us_device_parse_io_method, US_IO_METHOD_UNKNOWN, US_IO_METHODS_STR);
+			case _O_DESIRED_FPS:		OPT_NUMBER("--desired-fps", dev->desired_fps, 0, US_VIDEO_MAX_FPS, 0);
+			case _O_MIN_FRAME_SIZE:		OPT_NUMBER("--min-frame-size", dev->min_frame_size, 1, 8192, 0);
+			case _O_PERSISTENT:			OPT_SET(dev->persistent, true);
+			case _O_DV_TIMINGS:			OPT_SET(dev->dv_timings, true);
+			case _O_BUFFERS:			OPT_NUMBER("--buffers", dev->n_bufs, 1, 32, 0);
+			case _O_WORKERS:			OPT_NUMBER("--workers", enc->n_workers, 1, 32, 0);
+			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_LAST_AS_BLANK:		OPT_NUMBER("--last-as-blank", stream->last_as_blank, 0, 86400, 0);
@@ -409,7 +404,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 			case _O_WHITE_BALANCE:			OPT_CTL_AUTO(white_balance);
 			case _O_GAIN:					OPT_CTL_AUTO(gain);
 			case _O_COLOR_EFFECT:			OPT_CTL_MANUAL(color_effect);
-			case _O_ROTATE:			 	OPT_CTL_MANUAL(rotate);
+			case _O_ROTATE:				 	OPT_CTL_MANUAL(rotate);
 			case _O_FLIP_VERTICAL:			OPT_CTL_MANUAL(flip_vertical);
 			case _O_FLIP_HORIZONTAL:		OPT_CTL_MANUAL(flip_horizontal);
 
@@ -424,37 +419,44 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 			case _O_USER:				OPT_SET(server->user, optarg);
 			case _O_PASSWD:				OPT_SET(server->passwd, optarg);
 			case _O_STATIC:				OPT_SET(server->static_path, optarg);
-			case _O_DROP_SAME_FRAMES:	OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, VIDEO_MAX_FPS, 0);
+			case _O_DROP_SAME_FRAMES:	OPT_NUMBER("--drop-same-frames", server->drop_same_frames, 0, US_VIDEO_MAX_FPS, 0);
 			case _O_FAKE_RESOLUTION:	OPT_RESOLUTION("--fake-resolution", server->fake_width, server->fake_height, false);
 			case _O_ALLOW_ORIGIN:		OPT_SET(server->allow_origin, optarg);
+			case _O_INSTANCE_ID:
+				if (_check_instance_id(optarg) != 0) {
+					printf("Invalid instance ID, it should be like: ^[a-zA-Z0-9\\./+_-]*$\n");
+					return -1;
+				}
+				server->instance_id = optarg;
+				break;
 			case _O_TCP_NODELAY:		OPT_SET(server->tcp_nodelay, true);
 			case _O_SERVER_TIMEOUT:		OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
 
-#			define ADD_SINK(_opt, _lp, _up) \
-				case _O_##_up:				OPT_SET(_lp##_name, optarg); \
-				case _O_##_up##_MODE:		OPT_NUMBER("--" #_opt "sink-mode", _lp##_mode, INT_MIN, INT_MAX, 8); \
-				case _O_##_up##_RM:			OPT_SET(_lp##_rm, true); \
-				case _O_##_up##_CLIENT_TTL:	OPT_NUMBER("--" #_opt "sink-client-ttl", _lp##_client_ttl, 1, 60, 0); \
-				case _O_##_up##_TIMEOUT:	OPT_NUMBER("--" #_opt "sink-timeout", _lp##_timeout, 1, 60, 0);
+#			define ADD_SINK(x_opt, x_lp, x_up) \
+				case _O_##x_up:					OPT_SET(x_lp##_name, optarg); \
+				case _O_##x_up##_MODE:			OPT_NUMBER("--" #x_opt "sink-mode", x_lp##_mode, INT_MIN, INT_MAX, 8); \
+				case _O_##x_up##_RM:			OPT_SET(x_lp##_rm, true); \
+				case _O_##x_up##_CLIENT_TTL:	OPT_NUMBER("--" #x_opt "sink-client-ttl", x_lp##_client_ttl, 1, 60, 0); \
+				case _O_##x_up##_TIMEOUT:		OPT_NUMBER("--" #x_opt "sink-timeout", x_lp##_timeout, 1, 60, 0);
 			ADD_SINK("", sink, SINK)
 			ADD_SINK("raw-", raw_sink, RAW_SINK)
 			ADD_SINK("h264-", h264_sink, H264_SINK)
-			case _O_H264_BITRATE:		OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
-			case _O_H264_GOP:			OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
-			case _O_H264_M2M_DEVICE:	OPT_SET(stream->h264_m2m_path, optarg);
+			case _O_H264_BITRATE:			OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
+			case _O_H264_GOP:				OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
+			case _O_H264_M2M_DEVICE:		OPT_SET(stream->h264_m2m_path, optarg);
 #			undef ADD_SINK
 
 #			ifdef WITH_GPIO
-			case _O_GPIO_DEVICE:			OPT_SET(us_gpio.path, optarg);
-			case _O_GPIO_CONSUMER_PREFIX:	OPT_SET(us_gpio.consumer_prefix, optarg);
-			case _O_GPIO_PROG_RUNNING:		OPT_NUMBER("--gpio-prog-running", us_gpio.prog_running.pin, 0, 256, 0);
-			case _O_GPIO_STREAM_ONLINE:		OPT_NUMBER("--gpio-stream-online", us_gpio.stream_online.pin, 0, 256, 0);
-			case _O_GPIO_HAS_HTTP_CLIENTS:	OPT_NUMBER("--gpio-has-http-clients", us_gpio.has_http_clients.pin, 0, 256, 0);
+			case _O_GPIO_DEVICE:			OPT_SET(us_g_gpio.path, optarg);
+			case _O_GPIO_CONSUMER_PREFIX:	OPT_SET(us_g_gpio.consumer_prefix, optarg);
+			case _O_GPIO_PROG_RUNNING:		OPT_NUMBER("--gpio-prog-running", us_g_gpio.prog_running.pin, 0, 256, 0);
+			case _O_GPIO_STREAM_ONLINE:		OPT_NUMBER("--gpio-stream-online", us_g_gpio.stream_online.pin, 0, 256, 0);
+			case _O_GPIO_HAS_HTTP_CLIENTS:	OPT_NUMBER("--gpio-has-http-clients", us_g_gpio.has_http_clients.pin, 0, 256, 0);
 #			endif
 
 #			ifdef HAS_PDEATHSIG
 			case _O_EXIT_ON_PARENT_DEATH:
-				if (process_track_parent_death() < 0) {
+				if (us_process_track_parent_death() < 0) {
 					return -1;
 				};
 				break;
@@ -465,15 +467,15 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 #			endif
 			case _O_NOTIFY_PARENT:			OPT_SET(server->notify_parent, true);
 
-			case _O_LOG_LEVEL:			OPT_NUMBER("--log-level", us_log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0);
-			case _O_PERF:				OPT_SET(us_log_level, LOG_LEVEL_PERF);
-			case _O_VERBOSE:			OPT_SET(us_log_level, LOG_LEVEL_VERBOSE);
-			case _O_DEBUG:				OPT_SET(us_log_level, LOG_LEVEL_DEBUG);
-			case _O_FORCE_LOG_COLORS:	OPT_SET(us_log_colored, true);
-			case _O_NO_LOG_COLORS:		OPT_SET(us_log_colored, false);
+			case _O_LOG_LEVEL:			OPT_NUMBER("--log-level", us_g_log_level, US_LOG_LEVEL_INFO, US_LOG_LEVEL_DEBUG, 0);
+			case _O_PERF:				OPT_SET(us_g_log_level, US_LOG_LEVEL_PERF);
+			case _O_VERBOSE:			OPT_SET(us_g_log_level, US_LOG_LEVEL_VERBOSE);
+			case _O_DEBUG:				OPT_SET(us_g_log_level, US_LOG_LEVEL_DEBUG);
+			case _O_FORCE_LOG_COLORS:	OPT_SET(us_g_log_colored, true);
+			case _O_NO_LOG_COLORS:		OPT_SET(us_g_log_colored, false);
 
 			case _O_HELP:		_help(stdout, dev, enc, stream, server); return 1;
-			case _O_VERSION:	puts(VERSION); return 1;
+			case _O_VERSION:	puts(US_VERSION); return 1;
 			case _O_FEATURES:	_features(); return 1;
 
 			case 0:		break;
@@ -481,22 +483,24 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 		}
 	}
 
-	options->blank = blank_frame_init(blank_path);
+	US_LOG_INFO("Starting PiKVM uStreamer %s ...", US_VERSION);
+
+	options->blank = us_blank_frame_init(blank_path);
 	stream->blank = options->blank;
 
-#	define ADD_SINK(_label, _prefix) { \
-			if (_prefix##_name && _prefix##_name[0] != '\0') { \
-				options->_prefix = memsink_init( \
-					_label, \
-					_prefix##_name, \
+#	define ADD_SINK(x_label, x_prefix) { \
+			if (x_prefix##_name && x_prefix##_name[0] != '\0') { \
+				options->x_prefix = us_memsink_init( \
+					x_label, \
+					x_prefix##_name, \
 					true, \
-					_prefix##_mode, \
-					_prefix##_rm, \
-					_prefix##_client_ttl, \
-					_prefix##_timeout \
+					x_prefix##_mode, \
+					x_prefix##_rm, \
+					x_prefix##_client_ttl, \
+					x_prefix##_timeout \
 				); \
 			} \
-			stream->_prefix = options->_prefix; \
+			stream->x_prefix = options->x_prefix; \
 		}
 	ADD_SINK("JPEG", sink);
 	ADD_SINK("RAW", raw_sink);
@@ -505,7 +509,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *s
 
 #	ifdef WITH_SETPROCTITLE
 	if (process_name_prefix != NULL) {
-		process_set_name_prefix(options->argc, options->argv, process_name_prefix);
+		us_process_set_name_prefix(options->argc, options->argv, process_name_prefix);
 	}
 #	endif
 
@@ -526,10 +530,10 @@ static int _parse_resolution(const char *str, unsigned *width, unsigned *height,
 		return -1;
 	}
 	if (limited) {
-		if (tmp_width < VIDEO_MIN_WIDTH || tmp_width > VIDEO_MAX_WIDTH) {
+		if (tmp_width < US_VIDEO_MIN_WIDTH || tmp_width > US_VIDEO_MAX_WIDTH) {
 			return -2;
 		}
-		if (tmp_height < VIDEO_MIN_HEIGHT || tmp_height > VIDEO_MAX_HEIGHT) {
+		if (tmp_height < US_VIDEO_MIN_HEIGHT || tmp_height > US_VIDEO_MAX_HEIGHT) {
 			return -3;
 		}
 	}
@@ -538,6 +542,18 @@ static int _parse_resolution(const char *str, unsigned *width, unsigned *height,
 	return 0;
 }
 
+static int _check_instance_id(const char *str) {
+	for (const char *ptr = str; *ptr; ++ptr) {
+		if (!(isascii(*ptr) && (
+			isalpha(*ptr) || isdigit(*ptr)
+			|| *ptr == '.' || *ptr == '/' || *ptr == '+' || *ptr == '_' || *ptr == '-'
+		))) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
 static void _features(void) {
 #	ifdef WITH_GPIO
 	puts("+ WITH_GPIO");
@@ -570,11 +586,11 @@ static void _features(void) {
 #	endif
 }
 
-static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server) {
-#	define SAY(_msg, ...) fprintf(fp, _msg "\n", ##__VA_ARGS__)
+static void _help(FILE *fp, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server) {
+#	define SAY(x_msg, ...) fprintf(fp, x_msg "\n", ##__VA_ARGS__)
 	SAY("\nuStreamer - Lightweight and fast MJPEG-HTTP streamer");
 	SAY("═══════════════════════════════════════════════════");
-	SAY("Version: %s; license: GPLv3", VERSION);
+	SAY("Version: %s; license: GPLv3", US_VERSION);
 	SAY("Copyright (C) 2018-2022 Maxim Devaev <mdevaev@gmail.com>\n");
 	SAY("Capturing options:");
 	SAY("══════════════════");
@@ -582,12 +598,12 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
 	SAY("    -i|--input <N>  ────────────────────── Input channel. Default: %u.\n", dev->input);
 	SAY("    -r|--resolution <WxH>  ─────────────── Initial image resolution. Default: %ux%u.\n", dev->width, dev->height);
 	SAY("    -m|--format <fmt>  ─────────────────── Image format.");
-	SAY("                                           Available: %s; default: YUYV.\n", FORMATS_STR);
+	SAY("                                           Available: %s; default: YUYV.\n", US_FORMATS_STR);
 	SAY("    -a|--tv-standard <std>  ────────────── Force TV standard.");
-	SAY("                                           Available: %s; default: disabled.\n", STANDARDS_STR);
+	SAY("                                           Available: %s; default: disabled.\n", US_STANDARDS_STR);
 	SAY("    -I|--io-method <method>  ───────────── Set V4L2 IO method (see kernel documentation).");
 	SAY("                                           Changing of this parameter may increase the performance. Or not.");
-	SAY("                                           Available: %s; default: MMAP.\n", IO_METHODS_STR);
+	SAY("                                           Available: %s; default: MMAP.\n", US_IO_METHODS_STR);
 	SAY("    -f|--desired-fps <N>  ──────────────── Desired FPS. Default: maximum possible.\n");
 	SAY("    -z|--min-frame-size <N>  ───────────── Drop frames smaller then this limit. Useful if the device");
 	SAY("                                           produces small-sized garbage frames. Default: %zu bytes.\n", dev->min_frame_size);
@@ -665,15 +681,17 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
 	SAY("    --tcp-nodelay  ────────────── Set TCP_NODELAY flag to the client /stream socket. Only for TCP socket.");
 	SAY("                                  Default: disabled.\n");
 	SAY("    --allow-origin <str>  ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n");
+	SAY("    --instance-id <str>  ──────── A short string identifier to be displayed in the /state handle.");
+	SAY("                                  It must satisfy regexp ^[a-zA-Z0-9\\./+_-]*$. Default: an empty string.\n");
 	SAY("    --server-timeout <sec>  ───── Timeout for client connections. Default: %u.\n", server->timeout);
-#	define ADD_SINK(_name, _opt) \
-		SAY(_name " sink options:"); \
+#	define ADD_SINK(x_name, x_opt) \
+		SAY(x_name " sink options:"); \
 		SAY("══════════════════"); \
-		SAY("    --" _opt "sink <name>  ──────────── Use the shared memory to sink " _name " frames. Default: disabled.\n"); \
-		SAY("    --" _opt "sink-mode <mode>  ─────── Set " _name " sink permissions (like 777). Default: 660.\n"); \
-		SAY("    --" _opt "sink-rm  ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
-		SAY("    --" _opt "sink-client-ttl <sec>  ── Client TTL. Default: 10.\n"); \
-		SAY("    --" _opt "sink-timeout <sec>  ───── Timeout for lock. Default: 1.\n");
+		SAY("    --" x_opt "sink <name>  ──────────── Use the shared memory to sink " x_name " frames. Default: disabled.\n"); \
+		SAY("    --" x_opt "sink-mode <mode>  ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
+		SAY("    --" x_opt "sink-rm  ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
+		SAY("    --" x_opt "sink-client-ttl <sec>  ── Client TTL. Default: 10.\n"); \
+		SAY("    --" x_opt "sink-timeout <sec>  ───── Timeout for lock. Default: 1.\n");
 	ADD_SINK("JPEG", "")
 	ADD_SINK("RAW", "raw-")
 	ADD_SINK("H264", "h264-")
@@ -684,8 +702,8 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
 #	ifdef WITH_GPIO
 	SAY("GPIO options:");
 	SAY("═════════════");
-	SAY("    --gpio-device </dev/path>  ───── Path to GPIO character device. Default: %s.\n", us_gpio.path);
-	SAY("    --gpio-consumer-prefix <str>  ── Consumer prefix for GPIO outputs. Default: %s.\n", us_gpio.consumer_prefix);
+	SAY("    --gpio-device </dev/path>  ───── Path to GPIO character device. Default: %s.\n", us_g_gpio.path);
+	SAY("    --gpio-consumer-prefix <str>  ── Consumer prefix for GPIO outputs. Default: %s.\n", us_g_gpio.consumer_prefix);
 	SAY("    --gpio-prog-running <pin>  ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n");
 	SAY("    --gpio-stream-online <pin>  ──── Set 1 while streaming. Default: disabled.\n");
 	SAY("    --gpio-has-http-clients <pin>  ─ Set 1 while stream has at least one client. Default: disabled.\n");
@@ -710,7 +728,7 @@ static void _help(FILE *fp, device_s *dev, encoder_s *enc, stream_s *stream, ser
 	SAY("    --log-level <N>  ──── Verbosity level of messages from 0 (info) to 3 (debug).");
 	SAY("                          Enabling debugging messages can slow down the program.");
 	SAY("                          Available levels: 0 (info), 1 (performance), 2 (verbose), 3 (debug).");
-	SAY("                          Default: %d.\n", us_log_level);
+	SAY("                          Default: %d.\n", us_g_log_level);
 	SAY("    --perf  ───────────── Enable performance messages (same as --log-level=1). Default: disabled.\n");
 	SAY("    --verbose  ────────── Enable verbose messages and lower (same as --log-level=2). Default: disabled.\n");
 	SAY("    --debug  ──────────── Enable debug messages and lower (same as --log-level=3). Default: disabled.\n");
diff --git a/src/ustreamer/options.h b/src/ustreamer/options.h
index af2c590..8cc98ad 100644
--- a/src/ustreamer/options.h
+++ b/src/ustreamer/options.h
@@ -32,7 +32,7 @@
 #include <errno.h>
 #include <assert.h>
 
-#include "../libs/config.h"
+#include "../libs/const.h"
 #include "../libs/logging.h"
 #include "../libs/process.h"
 #include "../libs/frame.h"
@@ -50,17 +50,17 @@
 
 
 typedef struct {
-	unsigned	argc;
-	char		**argv;
-	char		**argv_copy;
-	frame_s		*blank;
-	memsink_s	*sink;
-	memsink_s	*raw_sink;
-	memsink_s	*h264_sink;
-} options_s;
+	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;
+} us_options_s;
 
 
-options_s *options_init(unsigned argc, char *argv[]);
-void options_destroy(options_s *options);
+us_options_s *us_options_init(unsigned argc, char *argv[]);
+void us_options_destroy(us_options_s *options);
 
-int options_parse(options_s *options, device_s *dev, encoder_s *enc, stream_s *stream, server_s *server);
+int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us_stream_s *stream, us_server_s *server);
diff --git a/src/ustreamer/stream.c b/src/ustreamer/stream.c
index 0816f07..94a5279 100644
--- a/src/ustreamer/stream.c
+++ b/src/ustreamer/stream.c
@@ -23,41 +23,42 @@
 #include "stream.h"
 
 
-static workers_pool_s *_stream_init_loop(stream_s *stream);
-static workers_pool_s *_stream_init_one(stream_s *stream);
-static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps);
+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);
 
 
-#define RUN(_next) stream->run->_next
+#define _RUN(x_next) stream->run->x_next
 
-#define SINK_PUT(_sink, _frame) { \
-		if (stream->_sink && memsink_server_check(stream->_sink, _frame)) {\
-			memsink_server_put(stream->_sink, _frame); \
+#define _SINK_PUT(x_sink, x_frame) { \
+		if (stream->x_sink && us_memsink_server_check(stream->x_sink, x_frame)) {\
+			bool m_key_requested; /* Unused */ \
+			us_memsink_server_put(stream->x_sink, x_frame, &m_key_requested); \
 		} \
 	}
 
-#define H264_PUT(_frame, _force_key) { \
-		if (RUN(h264)) { \
-			h264_stream_process(RUN(h264), _frame, _force_key); \
+#define _H264_PUT(x_frame, x_force_key) { \
+		if (_RUN(h264)) { \
+			us_h264_stream_process(_RUN(h264), x_frame, x_force_key); \
 		} \
 	}
 
 
-stream_s *stream_init(device_s *dev, encoder_s *enc) {
-	stream_runtime_s *run;
-	A_CALLOC(run, 1);
+us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
+	us_stream_runtime_s *run;
+	US_CALLOC(run, 1);
 	atomic_init(&run->stop, false);
 
-	video_s *video;
-	A_CALLOC(video, 1);
-	video->frame = frame_init();
+	us_video_s *video;
+	US_CALLOC(video, 1);
+	video->frame = us_frame_init();
 	atomic_init(&video->updated, false);
-	A_MUTEX_INIT(&video->mutex);
+	US_MUTEX_INIT(video->mutex);
 	atomic_init(&video->has_clients, false);
 	run->video = video;
 
-	stream_s *stream;
-	A_CALLOC(stream, 1);
+	us_stream_s *stream;
+	US_CALLOC(stream, 1);
 	stream->dev = dev;
 	stream->enc = enc;
 	stream->last_as_blank = -1;
@@ -68,42 +69,42 @@ stream_s *stream_init(device_s *dev, encoder_s *enc) {
 	return stream;
 }
 
-void stream_destroy(stream_s *stream) {
-	A_MUTEX_DESTROY(&RUN(video->mutex));
-	frame_destroy(RUN(video->frame));
-	free(RUN(video));
+void us_stream_destroy(us_stream_s *stream) {
+	US_MUTEX_DESTROY(_RUN(video->mutex));
+	us_frame_destroy(_RUN(video->frame));
+	free(_RUN(video));
 	free(stream->run);
 	free(stream);
 }
 
-void stream_loop(stream_s *stream) {
-	assert(stream->blank);
+void us_stream_loop(us_stream_s *stream) {
+	assert(stream->blank != NULL);
 
-	LOG_INFO("Using V4L2 device: %s", stream->dev->path);
-	LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
+	US_LOG_INFO("Using V4L2 device: %s", stream->dev->path);
+	US_LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
 
-	if (stream->h264_sink) {
-		RUN(h264) = h264_stream_init(stream->h264_sink, stream->h264_m2m_path, stream->h264_bitrate, stream->h264_gop);
+	if (stream->h264_sink != NULL) {
+		_RUN(h264) = us_h264_stream_init(stream->h264_sink, stream->h264_m2m_path, stream->h264_bitrate, stream->h264_gop);
 	}
 
-	for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
+	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;
 
-		LOG_INFO("Capturing ...");
+		US_LOG_INFO("Capturing ...");
 
-		while (!atomic_load(&RUN(stop))) {
-			SEP_DEBUG('-');
-			LOG_DEBUG("Waiting for worker ...");
+		while (!atomic_load(&_RUN(stop))) {
+			US_SEP_DEBUG('-');
+			US_LOG_DEBUG("Waiting for worker ...");
 
-			worker_s *ready_wr = workers_pool_wait(pool);
-			encoder_job_s *ready_job = (encoder_job_s *)(ready_wr->job);
+			us_worker_s *const ready_wr = us_workers_pool_wait(pool);
+			us_encoder_job_s *const ready_job = (us_encoder_job_s *)(ready_wr->job);
 
-			if (ready_job->hw) {
-				if (device_release_buffer(stream->dev, ready_job->hw) < 0) {
+			if (ready_job->hw != NULL) {
+				if (us_device_release_buffer(stream->dev, ready_job->hw) < 0) {
 					ready_wr->job_failed = true;
 				}
 				ready_job->hw = NULL;
@@ -111,9 +112,9 @@ void stream_loop(stream_s *stream) {
 				if (!ready_wr->job_failed) {
 					if (ready_wr->job_timely) {
 						_stream_expose_frame(stream, ready_job->dest, captured_fps);
-						LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
+						US_LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
 					} else {
-						LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
+						US_LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
 					}
 				} else {
 					break;
@@ -123,51 +124,51 @@ void stream_loop(stream_s *stream) {
 			bool h264_force_key = false;
 			if (stream->slowdown) {
 				unsigned slc = 0;
-				for (; slc < 10 && !atomic_load(&RUN(stop)) && !stream_has_clients(stream); ++slc) {
+				for (; slc < 10 && !atomic_load(&_RUN(stop)) && !us_stream_has_clients(stream); ++slc) {
 					usleep(100000);
 					++slc;
 				}
 				h264_force_key = (slc == 10);
 			}
 
-			if (atomic_load(&RUN(stop))) {
+			if (atomic_load(&_RUN(stop))) {
 				break;
 			}
 
 			bool has_read;
 			bool has_write;
 			bool has_error;
-			int selected = device_select(stream->dev, &has_read, &has_write, &has_error);
+			const int selected = us_device_select(stream->dev, &has_read, &has_write, &has_error);
 
 			if (selected < 0) {
 				if (errno != EINTR) {
-					LOG_PERROR("Mainloop select() error");
+					US_LOG_PERROR("Mainloop select() error");
 					break;
 				}
 			} else if (selected == 0) { // Persistent timeout
 #				ifdef WITH_GPIO
-				gpio_set_stream_online(false);
+				us_gpio_set_stream_online(false);
 #				endif
 			} else {
 				if (has_read) {
-					LOG_DEBUG("Frame is ready");
+					US_LOG_DEBUG("Frame is ready");
 
 #					ifdef WITH_GPIO
-					gpio_set_stream_online(true);
+					us_gpio_set_stream_online(true);
 #					endif
 
-					const long double now = get_now_monotonic();
-					const long long now_second = floor_ms(now);
+					const long double now = us_get_now_monotonic();
+					const long long now_second = us_floor_ms(now);
 
-					hw_buffer_s *hw;
-					int buf_index = device_grab_buffer(stream->dev, &hw);
+					us_hw_buffer_s *hw;
+					const int buf_index = us_device_grab_buffer(stream->dev, &hw);
 
 					if (buf_index >= 0) {
 						if (now < grab_after) {
 							fluency_passed += 1;
-							LOG_VERBOSE("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf",
+							US_LOG_VERBOSE("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf",
 								fluency_passed, now, grab_after);
-							if (device_release_buffer(stream->dev, hw) < 0) {
+							if (us_device_release_buffer(stream->dev, hw) < 0) {
 								break;
 							}
 						} else {
@@ -177,20 +178,20 @@ void stream_loop(stream_s *stream) {
 								captured_fps = captured_fps_accum;
 								captured_fps_accum = 0;
 								captured_fps_second = now_second;
-								LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
+								US_LOG_PERF_FPS("A new second has come; captured_fps=%u", captured_fps);
 							}
 							captured_fps_accum += 1;
 
-							const long double fluency_delay = workers_pool_get_fluency_delay(pool, ready_wr);
+							const long double fluency_delay = us_workers_pool_get_fluency_delay(pool, ready_wr);
 							grab_after = now + fluency_delay;
-							LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
+							US_LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
 
 							ready_job->hw = hw;
-							workers_pool_assign(pool, ready_wr);
-							LOG_DEBUG("Assigned new frame in buffer=%d to worker=%s", buf_index, ready_wr->name);
+							us_workers_pool_assign(pool, ready_wr);
+							US_LOG_DEBUG("Assigned new frame in buffer=%d to worker=%s", buf_index, ready_wr->name);
 
-							SINK_PUT(raw_sink, &hw->raw);
-							H264_PUT(&hw->raw, h264_force_key);
+							_SINK_PUT(raw_sink, &hw->raw);
+							_H264_PUT(&hw->raw, h264_force_key);
 						}
 					} else if (buf_index != -2) { // -2 for broken frame
 						break;
@@ -198,72 +199,70 @@ void stream_loop(stream_s *stream) {
 				}
 
 				if (has_write) {
-					LOG_ERROR("Got unexpected writing event, seems device was disconnected");
+					US_LOG_ERROR("Got unexpected writing event, seems device was disconnected");
 					break;
 				}
 
 				if (has_error) {
-					LOG_INFO("Got V4L2 event");
-					if (device_consume_event(stream->dev) < 0) {
+					US_LOG_INFO("Got V4L2 event");
+					if (us_device_consume_event(stream->dev) < 0) {
 						break;
 					}
 				}
 			}
 		}
 
-		workers_pool_destroy(pool);
-		device_switch_capturing(stream->dev, false);
-		device_close(stream->dev);
+		us_workers_pool_destroy(pool);
+		us_device_switch_capturing(stream->dev, false);
+		us_device_close(stream->dev);
 
 #		ifdef WITH_GPIO
-		gpio_set_stream_online(false);
+		us_gpio_set_stream_online(false);
 #		endif
 	}
 
-	if (RUN(h264)) {
-		h264_stream_destroy(RUN(h264));
-	}
+	US_DELETE(_RUN(h264), us_h264_stream_destroy);
 }
 
-void stream_loop_break(stream_s *stream) {
-	atomic_store(&RUN(stop), true);
+void us_stream_loop_break(us_stream_s *stream) {
+	atomic_store(&_RUN(stop), true);
 }
 
-bool stream_has_clients(stream_s *stream) {
+bool us_stream_has_clients(us_stream_s *stream) {
 	return (
-		atomic_load(&RUN(video->has_clients))
+		atomic_load(&_RUN(video->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)))
+		|| (_RUN(h264) != NULL && /*_RUN(h264->sink) == NULL ||*/ atomic_load(&_RUN(h264->sink->has_clients)))
 	);
 }
 
-static workers_pool_s *_stream_init_loop(stream_s *stream) {
+static us_workers_pool_s *_stream_init_loop(us_stream_s *stream) {
 
-	workers_pool_s *pool = NULL;
+	us_workers_pool_s *pool = NULL;
 	int access_error = 0;
 
-	LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&RUN(stop)));
+	US_LOG_DEBUG("%s: stream->run->stop=%d", __FUNCTION__, atomic_load(&_RUN(stop)));
 
-	while (!atomic_load(&RUN(stop))) {
+	while (!atomic_load(&_RUN(stop))) {
 		_stream_expose_frame(stream, NULL, 0);
 
 		if (access(stream->dev->path, R_OK|W_OK) < 0) {
 			if (access_error != errno) {
-				SEP_INFO('=');
-				LOG_PERROR("Can't access device");
-				LOG_INFO("Waiting for the device access ...");
+				US_SEP_INFO('=');
+				US_LOG_PERROR("Can't access device");
+				US_LOG_INFO("Waiting for the device access ...");
 				access_error = errno;
 			}
 			sleep(stream->error_delay);
 			continue;
 		} else {
-			SEP_INFO('=');
+			US_SEP_INFO('=');
 			access_error = 0;
 		}
 
 		if ((pool = _stream_init_one(stream)) == NULL) {
-			LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
+			US_LOG_INFO("Sleeping %u seconds before new stream init ...", stream->error_delay);
 			sleep(stream->error_delay);
 		} else {
 			break;
@@ -272,90 +271,86 @@ static workers_pool_s *_stream_init_loop(stream_s *stream) {
 	return pool;
 }
 
-static workers_pool_s *_stream_init_one(stream_s *stream) {
-	if (device_open(stream->dev) < 0) {
+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 == ENCODER_TYPE_M2M_VIDEO
-		|| stream->enc->type == ENCODER_TYPE_M2M_IMAGE
-		|| (RUN(h264) && !is_jpeg(stream->dev->run->format))
+		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))
 	) {
-		device_export_to_dma(stream->dev);
+		us_device_export_to_dma(stream->dev);
 	}
-	if (device_switch_capturing(stream->dev, true) < 0) {
+	if (us_device_switch_capturing(stream->dev, true) < 0) {
 		goto error;
 	}
-	return encoder_workers_pool_init(stream->enc, stream->dev);
+	return us_encoder_workers_pool_init(stream->enc, stream->dev);
 	error:
-		device_close(stream->dev);
+		us_device_close(stream->dev);
 		return NULL;
 }
 
-static void _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps) {
-#	define VID(_next) RUN(video->_next)
+static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame, unsigned captured_fps) {
+#	define VID(x_next) _RUN(video->x_next)
 
-	frame_s *new = NULL;
+	us_frame_s *new = NULL;
 
-	A_MUTEX_LOCK(&VID(mutex));
+	US_MUTEX_LOCK(VID(mutex));
 
-	if (frame) {
+	if (frame != NULL) {
 		new = frame;
-		RUN(last_as_blank_ts) = 0; // Останавливаем таймер
-		LOG_DEBUG("Exposed ALIVE video 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;
+			_RUN(last_as_blank_ts) = 0;
 
 		} else if (VID(frame->online)) { // Если переходим из online в offline
 			if (stream->last_as_blank < 0) { // Если last_as_blank выключен, просто покажем старую картинку
 				new = stream->blank;
-				LOG_INFO("Changed video frame to BLANK");
+				US_LOG_INFO("Changed video frame to BLANK");
 			} else if (stream->last_as_blank > 0) { // // Если нужен таймер - запустим
-				RUN(last_as_blank_ts) = get_now_monotonic() + stream->last_as_blank;
-				LOG_INFO("Freezed last ALIVE video frame for %d seconds", stream->last_as_blank);
+				_RUN(last_as_blank_ts) = us_get_now_monotonic() + stream->last_as_blank;
+				US_LOG_INFO("Freezed last ALIVE video frame for %d seconds", stream->last_as_blank);
 			} else {  // last_as_blank == 0 - показываем последний фрейм вечно
-				LOG_INFO("Freezed last ALIVE video frame forever");
+				US_LOG_INFO("Freezed last ALIVE video frame forever");
 			}
 
 		} else if (stream->last_as_blank < 0) {
 			new = stream->blank;
-			// LOG_INFO("Changed video frame to BLANK");
+			// US_LOG_INFO("Changed video frame to BLANK");
 		}
 
 		if ( // Если уже оффлайн, включена фича last_as_blank с таймером и он запущен
 			stream->last_as_blank > 0
-			&& RUN(last_as_blank_ts) != 0
-			&& RUN(last_as_blank_ts) < get_now_monotonic()
+			&& _RUN(last_as_blank_ts) != 0
+			&& _RUN(last_as_blank_ts) < us_get_now_monotonic()
 		) {
 			new = stream->blank;
-			RUN(last_as_blank_ts) = 0; // // Останавливаем таймер
-			LOG_INFO("Changed last ALIVE video frame to BLANK");
+			_RUN(last_as_blank_ts) = 0; // // Останавливаем таймер
+			US_LOG_INFO("Changed last ALIVE video frame to BLANK");
 		}
 	}
 
-	if (new) {
-		frame_copy(new, VID(frame));
+	if (new != NULL) {
+		us_frame_copy(new, VID(frame));
 	}
-	VID(frame->online) = (bool)frame;
+	VID(frame->online) = (frame != NULL);
 	VID(captured_fps) = captured_fps;
 	atomic_store(&VID(updated), true);
 
-	A_MUTEX_UNLOCK(&VID(mutex));
+	US_MUTEX_UNLOCK(VID(mutex));
 
 	new = (frame ? frame : stream->blank);
-	SINK_PUT(sink, new);
+	_SINK_PUT(sink, new);
 
 	if (frame == NULL) {
-		SINK_PUT(raw_sink, stream->blank);
-		H264_PUT(stream->blank, false);
+		_SINK_PUT(raw_sink, stream->blank);
+		_H264_PUT(stream->blank, false);
 	}
 
 #	undef VID
 }
-
-#undef H264_PUT
-#undef SINK_PUT
-#undef RUN
diff --git a/src/ustreamer/stream.h b/src/ustreamer/stream.h
index d0f4aa1..02501d2 100644
--- a/src/ustreamer/stream.h
+++ b/src/ustreamer/stream.h
@@ -42,55 +42,55 @@
 #include "device.h"
 #include "encoder.h"
 #include "workers.h"
-#include "h264/stream.h"
+#include "h264.h"
 #ifdef WITH_GPIO
 #	include "gpio/gpio.h"
 #endif
 
 
 typedef struct {
-	frame_s			*frame;
+	us_frame_s		*frame;
 	unsigned		captured_fps;
 	atomic_bool		updated;
 	pthread_mutex_t	mutex;
 
 	atomic_bool		has_clients; // For slowdown
-} video_s;
+} us_video_s;
 
 typedef struct {
-	video_s		*video;
-	long double	last_as_blank_ts;
+	us_video_s		*video;
+	long double		last_as_blank_ts;
 
-	h264_stream_s *h264;
+	us_h264_stream_s	*h264;
 
-	atomic_bool stop;
-} stream_runtime_s;
+	atomic_bool		stop;
+} us_stream_runtime_s;
 
 typedef struct {
-	device_s	*dev;
-	encoder_s	*enc;
+	us_device_s		*dev;
+	us_encoder_s	*enc;
 
-	frame_s		*blank;
-	int			last_as_blank;
-	bool		slowdown;
-	unsigned	error_delay;
+	us_frame_s		*blank;
+	int				last_as_blank;
+	bool			slowdown;
+	unsigned		error_delay;
 
-	memsink_s	*sink;
-	memsink_s	*raw_sink;
+	us_memsink_s	*sink;
+	us_memsink_s	*raw_sink;
 
-	memsink_s	*h264_sink;
-	unsigned	h264_bitrate;
-	unsigned	h264_gop;
-	char		*h264_m2m_path;
+	us_memsink_s	*h264_sink;
+	unsigned		h264_bitrate;
+	unsigned		h264_gop;
+	char			*h264_m2m_path;
 
-	stream_runtime_s *run;
-} stream_s;
+	us_stream_runtime_s	*run;
+} us_stream_s;
 
 
-stream_s *stream_init(device_s *dev, encoder_s *enc);
-void stream_destroy(stream_s *stream);
+us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc);
+void us_stream_destroy(us_stream_s *stream);
 
-void stream_loop(stream_s *stream);
-void stream_loop_break(stream_s *stream);
+void us_stream_loop(us_stream_s *stream);
+void us_stream_loop_break(us_stream_s *stream);
 
-bool stream_has_clients(stream_s *stream);
+bool us_stream_has_clients(us_stream_s *stream);
diff --git a/src/ustreamer/workers.c b/src/ustreamer/workers.c
index dfcf919..8343db1 100644
--- a/src/ustreamer/workers.c
+++ b/src/ustreamer/workers.c
@@ -26,16 +26,16 @@
 static void *_worker_thread(void *v_worker);
 
 
-workers_pool_s *workers_pool_init(
+us_workers_pool_s *us_workers_pool_init(
 	const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
-	workers_pool_job_init_f job_init, void *job_init_arg,
-	workers_pool_job_destroy_f job_destroy,
-	workers_pool_run_job_f run_job) {
+	us_workers_pool_job_init_f job_init, void *job_init_arg,
+	us_workers_pool_job_destroy_f job_destroy,
+	us_workers_pool_run_job_f run_job) {
 
-	LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
+	US_LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
 
-	workers_pool_s *pool;
-	A_CALLOC(pool, 1);
+	us_workers_pool_s *pool;
+	US_CALLOC(pool, 1);
 	pool->name = name;
 	pool->desired_interval = desired_interval;
 	pool->job_destroy = job_destroy;
@@ -44,25 +44,25 @@ workers_pool_s *workers_pool_init(
 	atomic_init(&pool->stop, false);
 
 	pool->n_workers = n_workers;
-	A_CALLOC(pool->workers, pool->n_workers);
+	US_CALLOC(pool->workers, pool->n_workers);
 
-	A_MUTEX_INIT(&pool->free_workers_mutex);
-	A_COND_INIT(&pool->free_workers_cond);
+	US_MUTEX_INIT(pool->free_workers_mutex);
+	US_COND_INIT(pool->free_workers_cond);
 
 	for (unsigned number = 0; number < pool->n_workers; ++number) {
-#		define WR(_next) pool->workers[number]._next
+#		define WR(x_next) pool->workers[number].x_next
 
 		WR(number) = number;
-		A_ASPRINTF(WR(name), "%s-%u", wr_prefix, number);
+		US_ASPRINTF(WR(name), "%s-%u", wr_prefix, number);
 
-		A_MUTEX_INIT(&WR(has_job_mutex));
+		US_MUTEX_INIT(WR(has_job_mutex));
 		atomic_init(&WR(has_job), false);
-		A_COND_INIT(&WR(has_job_cond));
+		US_COND_INIT(WR(has_job_cond));
 
 		WR(pool) = pool;
 		WR(job) = job_init(job_init_arg);
 
-		A_THREAD_CREATE(&WR(tid), _worker_thread, (void *)&(pool->workers[number]));
+		US_THREAD_CREATE(WR(tid), _worker_thread, (void *)&(pool->workers[number]));
 		pool->free_workers += 1;
 
 #		undef WR
@@ -70,21 +70,21 @@ workers_pool_s *workers_pool_init(
 	return pool;
 }
 
-void workers_pool_destroy(workers_pool_s *pool) {
-	LOG_INFO("Destroying workers pool %s ...", pool->name);
+void us_workers_pool_destroy(us_workers_pool_s *pool) {
+	US_LOG_INFO("Destroying workers pool %s ...", pool->name);
 
 	atomic_store(&pool->stop, true);
 	for (unsigned number = 0; number < pool->n_workers; ++number) {
-#		define WR(_next) pool->workers[number]._next
+#		define WR(x_next) pool->workers[number].x_next
 
-		A_MUTEX_LOCK(&WR(has_job_mutex));
+		US_MUTEX_LOCK(WR(has_job_mutex));
 		atomic_store(&WR(has_job), true); // Final job: die
-		A_MUTEX_UNLOCK(&WR(has_job_mutex));
-		A_COND_SIGNAL(&WR(has_job_cond));
+		US_MUTEX_UNLOCK(WR(has_job_mutex));
+		US_COND_SIGNAL(WR(has_job_cond));
 
-		A_THREAD_JOIN(WR(tid));
-		A_MUTEX_DESTROY(&WR(has_job_mutex));
-		A_COND_DESTROY(&WR(has_job_cond));
+		US_THREAD_JOIN(WR(tid));
+		US_MUTEX_DESTROY(WR(has_job_mutex));
+		US_COND_DESTROY(WR(has_job_cond));
 
 		free(WR(name));
 
@@ -93,19 +93,19 @@ void workers_pool_destroy(workers_pool_s *pool) {
 #		undef WR
 	}
 
-	A_MUTEX_DESTROY(&pool->free_workers_mutex);
-	A_COND_DESTROY(&pool->free_workers_cond);
+	US_MUTEX_DESTROY(pool->free_workers_mutex);
+	US_COND_DESTROY(pool->free_workers_cond);
 
 	free(pool->workers);
 	free(pool);
 }
 
-worker_s *workers_pool_wait(workers_pool_s *pool) {
-	worker_s *ready_wr = NULL;
+us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool) {
+	us_worker_s *ready_wr = NULL;
 
-	A_MUTEX_LOCK(&pool->free_workers_mutex);
-	A_COND_WAIT_TRUE(pool->free_workers, &pool->free_workers_cond, &pool->free_workers_mutex);
-	A_MUTEX_UNLOCK(&pool->free_workers_mutex);
+	US_MUTEX_LOCK(pool->free_workers_mutex);
+	US_COND_WAIT_FOR(pool->free_workers, pool->free_workers_cond, pool->free_workers_mutex);
+	US_MUTEX_UNLOCK(pool->free_workers_mutex);
 
 	if (pool->oldest_wr && !atomic_load(&pool->oldest_wr->has_job)) {
 		ready_wr = pool->oldest_wr;
@@ -129,15 +129,15 @@ worker_s *workers_pool_wait(workers_pool_s *pool) {
 	return ready_wr;
 }
 
-void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/) {
+void us_workers_pool_assign(us_workers_pool_s *pool, us_worker_s *ready_wr/*, void *job*/) {
 	if (pool->oldest_wr == NULL) {
 		pool->oldest_wr = ready_wr;
 		pool->latest_wr = pool->oldest_wr;
 	} else {
-		if (ready_wr->next_wr) {
+		if (ready_wr->next_wr != NULL) {
 			ready_wr->next_wr->prev_wr = ready_wr->prev_wr;
 		}
-		if (ready_wr->prev_wr) {
+		if (ready_wr->prev_wr != NULL) {
 			ready_wr->prev_wr->next_wr = ready_wr->next_wr;
 		}
 		ready_wr->prev_wr = pool->latest_wr;
@@ -146,21 +146,21 @@ void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/
 	}
 	pool->latest_wr->next_wr = NULL;
 
-	A_MUTEX_LOCK(&ready_wr->has_job_mutex);
+	US_MUTEX_LOCK(ready_wr->has_job_mutex);
 	//ready_wr->job = job;
 	atomic_store(&ready_wr->has_job, true);
-	A_MUTEX_UNLOCK(&ready_wr->has_job_mutex);
-	A_COND_SIGNAL(&ready_wr->has_job_cond);
+	US_MUTEX_UNLOCK(ready_wr->has_job_mutex);
+	US_COND_SIGNAL(ready_wr->has_job_cond);
 
-	A_MUTEX_LOCK(&pool->free_workers_mutex);
+	US_MUTEX_LOCK(pool->free_workers_mutex);
 	pool->free_workers -= 1;
-	A_MUTEX_UNLOCK(&pool->free_workers_mutex);
+	US_MUTEX_UNLOCK(pool->free_workers_mutex);
 }
 
-long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready_wr) {
+long double us_workers_pool_get_fluency_delay(us_workers_pool_s *pool, us_worker_s *ready_wr) {
 	const long double approx_job_time = pool->approx_job_time * 0.9 + ready_wr->last_job_time * 0.1;
 
-	LOG_VERBOSE("Correcting pool's %s approx_job_time: %.3Lf -> %.3Lf (last_job_time=%.3Lf)",
+	US_LOG_VERBOSE("Correcting pool's %s approx_job_time: %.3Lf -> %.3Lf (last_job_time=%.3Lf)",
 		pool->name, pool->approx_job_time, approx_job_time, ready_wr->last_job_time);
 
 	pool->approx_job_time = approx_job_time;
@@ -176,35 +176,35 @@ long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready
 }
 
 static void *_worker_thread(void *v_worker) {
-	worker_s *wr = (worker_s *)v_worker;
+	us_worker_s *wr = (us_worker_s *)v_worker;
 
-	A_THREAD_RENAME("%s", wr->name);
-	LOG_DEBUG("Hello! I am a worker %s ^_^", wr->name);
+	US_THREAD_RENAME("%s", wr->name);
+	US_LOG_DEBUG("Hello! I am a worker %s ^_^", wr->name);
 
 	while (!atomic_load(&wr->pool->stop)) {
-		LOG_DEBUG("Worker %s waiting for a new job ...", wr->name);
+		US_LOG_DEBUG("Worker %s waiting for a new job ...", wr->name);
 
-		A_MUTEX_LOCK(&wr->has_job_mutex);
-		A_COND_WAIT_TRUE(atomic_load(&wr->has_job), &wr->has_job_cond, &wr->has_job_mutex);
-		A_MUTEX_UNLOCK(&wr->has_job_mutex);
+		US_MUTEX_LOCK(wr->has_job_mutex);
+		US_COND_WAIT_FOR(atomic_load(&wr->has_job), wr->has_job_cond, wr->has_job_mutex);
+		US_MUTEX_UNLOCK(wr->has_job_mutex);
 
 		if (!atomic_load(&wr->pool->stop)) {
-			long double job_start_ts = get_now_monotonic();
+			const long double job_start_ts = us_get_now_monotonic();
 			wr->job_failed = !wr->pool->run_job(wr);
 			if (!wr->job_failed) {
 				wr->job_start_ts = job_start_ts;
-				wr->last_job_time = get_now_monotonic() - wr->job_start_ts;
+				wr->last_job_time = us_get_now_monotonic() - wr->job_start_ts;
 			}
 			//wr->job = NULL;
 			atomic_store(&wr->has_job, false);
 		}
 
-		A_MUTEX_LOCK(&wr->pool->free_workers_mutex);
+		US_MUTEX_LOCK(wr->pool->free_workers_mutex);
 		wr->pool->free_workers += 1;
-		A_MUTEX_UNLOCK(&wr->pool->free_workers_mutex);
-		A_COND_SIGNAL(&wr->pool->free_workers_cond);
+		US_MUTEX_UNLOCK(wr->pool->free_workers_mutex);
+		US_COND_SIGNAL(wr->pool->free_workers_cond);
 	}
 
-	LOG_DEBUG("Bye-bye (worker %s)", wr->name);
+	US_LOG_DEBUG("Bye-bye (worker %s)", wr->name);
 	return NULL;
 }
diff --git a/src/ustreamer/workers.h b/src/ustreamer/workers.h
index 91ab8b1..c3fa69e 100644
--- a/src/ustreamer/workers.h
+++ b/src/ustreamer/workers.h
@@ -34,7 +34,7 @@
 #include "../libs/logging.h"
 
 
-typedef struct worker_sx {
+typedef struct us_worker_sx {
 	pthread_t		tid;
 	unsigned		number;
 	char			*name;
@@ -49,27 +49,27 @@ typedef struct worker_sx {
 	long double		job_start_ts;
 	pthread_cond_t	has_job_cond;
 
-	struct worker_sx *prev_wr;
-	struct worker_sx *next_wr;
+	struct us_worker_sx		*prev_wr;
+	struct us_worker_sx		*next_wr;
 
-	struct workers_pool_sx *pool;
-} worker_s;
+	struct us_workers_pool_sx	*pool;
+} us_worker_s;
 
-typedef void *(*workers_pool_job_init_f)(void *arg);
-typedef void (*workers_pool_job_destroy_f)(void *job);
-typedef bool (*workers_pool_run_job_f)(worker_s *wr);
+typedef void *(*us_workers_pool_job_init_f)(void *arg);
+typedef void (*us_workers_pool_job_destroy_f)(void *job);
+typedef bool (*us_workers_pool_run_job_f)(us_worker_s *wr);
 
-typedef struct workers_pool_sx {
+typedef struct us_workers_pool_sx {
 	const char		*name;
 	long double		desired_interval;
 
-	workers_pool_job_destroy_f	job_destroy;
-	workers_pool_run_job_f		run_job;
+	us_workers_pool_job_destroy_f	job_destroy;
+	us_workers_pool_run_job_f		run_job;
 
 	unsigned		n_workers;
-	worker_s		*workers;
-	worker_s		*oldest_wr;
-	worker_s		*latest_wr;
+	us_worker_s		*workers;
+	us_worker_s		*oldest_wr;
+	us_worker_s		*latest_wr;
 
 	long double		approx_job_time;
 
@@ -78,18 +78,18 @@ typedef struct workers_pool_sx {
 	pthread_cond_t	free_workers_cond;
 
 	atomic_bool		stop;
-} workers_pool_s;
+} us_workers_pool_s;
 
 
-workers_pool_s *workers_pool_init(
+us_workers_pool_s *us_workers_pool_init(
 	const char *name, const char *wr_prefix, unsigned n_workers, long double desired_interval,
-	workers_pool_job_init_f job_init, void *job_init_arg,
-	workers_pool_job_destroy_f job_destroy,
-	workers_pool_run_job_f run_job);
+	us_workers_pool_job_init_f job_init, void *job_init_arg,
+	us_workers_pool_job_destroy_f job_destroy,
+	us_workers_pool_run_job_f run_job);
 
-void workers_pool_destroy(workers_pool_s *pool);
+void us_workers_pool_destroy(us_workers_pool_s *pool);
 
-worker_s *workers_pool_wait(workers_pool_s *pool);
-void workers_pool_assign(workers_pool_s *pool, worker_s *ready_wr/*, void *job*/);
+us_worker_s *us_workers_pool_wait(us_workers_pool_s *pool);
+void us_workers_pool_assign(us_workers_pool_s *pool, us_worker_s *ready_wr/*, void *job*/);
 
-long double workers_pool_get_fluency_delay(workers_pool_s *pool, worker_s *ready_wr);
+long double us_workers_pool_get_fluency_delay(us_workers_pool_s *pool, us_worker_s *ready_wr);
diff --git a/tools/common.py b/tools/common.py
index eceafc8..18502aa 100644
--- a/tools/common.py
+++ b/tools/common.py
@@ -26,26 +26,24 @@ import textwrap
 
 # =====
 C_PREPEND = textwrap.dedent("""
-        /*****************************************************************************
-        #                                                                            #
-        #    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
-        #                                                                            #
-        #    Copyright (C) 2018-2022  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/>.  #
-        #                                                                            #
-        *****************************************************************************/
-
-
-    """).strip() + "\n"
+    /*****************************************************************************
+    #                                                                            #
+    #    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+    #                                                                            #
+    #    Copyright (C) 2018-2022  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/>.  #
+    #                                                                            #
+    *****************************************************************************/
+""").strip() + "\n"
diff --git a/tools/make-html-h.py b/tools/make-html-h.py
index 2220744..5dad39a 100755
--- a/tools/make-html-h.py
+++ b/tools/make-html-h.py
@@ -36,12 +36,12 @@ def main() -> None:
     h_path = os.path.basename(c_path[:-2] + ".h")
     name = sys.argv[3]
 
-    with open(html_path, "r") as html_file:
-        html = html_file.read()
+    with open(html_path, "r") as file:
+        html = file.read()
 
     html = html.strip()
     html = html.replace("\"", "\\\"")
-    html = html.replace("%VERSION%", "\" VERSION \"")
+    html = html.replace("%VERSION%", "\" US_VERSION \"")
     html = textwrap.indent(html, "\t", (lambda line: True))
     html = "\n".join(
         (f"{line} \\" if line.strip() else f"{line}\\")
@@ -49,10 +49,10 @@ def main() -> None:
     )
 
     text = f"{common.C_PREPEND}\n#include \"{h_path}\"\n\n\n"
-    text += f"const char *const HTML_{name}_PAGE = \" \\\n{html}\n\";\n"
+    text += f"const char *const US_HTML_{name}_PAGE = \" \\\n{html}\n\";\n"
 
-    with open(c_path, "w") as c_file:
-        c_file.write(text)
+    with open(c_path, "w") as file:
+        file.write(text)
 
 
 # =====
diff --git a/tools/make-ico-h.py b/tools/make-ico-h.py
new file mode 100755
index 0000000..a8d192b
--- /dev/null
+++ b/tools/make-ico-h.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env -S python3 -B
+# ========================================================================== #
+#                                                                            #
+#    uStreamer - Lightweight and fast MJPEG-HTTP streamer.                   #
+#                                                                            #
+#    Copyright (C) 2018-2022  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 common
+
+
+# =====
+def main() -> None:
+    assert len(sys.argv) == 4, f"{sys.argv[0]} <file.ico> <file.h> <name>"
+    ico_path = sys.argv[1]
+    c_path = sys.argv[2]
+    h_path = os.path.basename(c_path[:-2]) + ".h"
+    name = sys.argv[3]
+
+    with open(ico_path, "rb") as file:
+        data = file.read()
+
+    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 size_t US_{name}_ICO_DATA_SIZE = {len(data)};\n"
+    text += f"const uint8_t US_{name}_ICO_DATA[] = {data_text};\n"
+
+    with open(c_path, "w") as file:
+        file.write(text)
+
+
+# =====
+if __name__ == "__main__":
+    main()
diff --git a/tools/make-jpeg-h.py b/tools/make-jpeg-h.py
index b66c2e9..e521be5 100755
--- a/tools/make-jpeg-h.py
+++ b/tools/make-jpeg-h.py
@@ -26,13 +26,11 @@ import os
 import io
 import struct
 
-from typing import Tuple
-
 import common
 
 
 # =====
-def _get_jpeg_size(data: bytes) -> Tuple[int, int]:
+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)
@@ -67,28 +65,28 @@ def main() -> None:
     h_path = os.path.basename(c_path[:-2]) + ".h"
     name = sys.argv[3]
 
-    with open(jpeg_path, "rb") as jpeg_file:
-        jpeg_data = jpeg_file.read()
+    with open(jpeg_path, "rb") as file:
+        data = file.read()
 
-    (width, height) = _get_jpeg_size(jpeg_data)
+    (width, height) = _get_jpeg_size(data)
 
-    jpeg_data_text = "{\n\t" + ",\n\t".join(
+    data_text = "{\n\t" + ",\n\t".join(
         ", ".join(
             f"0x{ch:02X}"
-            for ch in jpeg_data[index:index + 20]
+            for ch in data[index:index + 20]
         )
-        for index in range(0, len(jpeg_data), 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 {name}_JPEG_WIDTH = {width};\n"
-    text += f"const unsigned {name}_JPEG_HEIGHT = {height};\n\n"
-    text += f"const size_t {name}_JPEG_DATA_SIZE = {len(jpeg_data)};\n"
-    text += f"const uint8_t {name}_JPEG_DATA[] = {jpeg_data_text};\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 c_file:
-        c_file.write(text)
+    with open(c_path, "w") as file:
+        file.write(text)
 
 
 # =====

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/debug/.build-id/77/def4d4cdfe1e43732f7815bdc5c7862e18f1b8.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/91/5a515278c05d60d599285037d9df9d78ddd4ff.debug

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/debug/.build-id/52/dfa8110b809bcade196ae4cd84f324e5b7e6d9.debug
-rw-r--r--  root/root   /usr/lib/debug/.build-id/71/ec455b4e5972af0a2b5e0febb0e1ebceba0155.debug

No differences were encountered between the control files of package ustreamer

Control files of package ustreamer-dbgsym: lines which differ (wdiff format)

  • Build-Ids: 52dfa8110b809bcade196ae4cd84f324e5b7e6d9 71ec455b4e5972af0a2b5e0febb0e1ebceba0155 77def4d4cdfe1e43732f7815bdc5c7862e18f1b8 915a515278c05d60d599285037d9df9d78ddd4ff

More details

Full run details