From a78891ec749858bfa87d147c1b887445552c9eab Mon Sep 17 00:00:00 2001
From: Compot Developer
Date: Fri, 16 Oct 2026 13:59:52 +0000
Subject: [PATCH] docs: align Markdown tables for reading as plain text
---
README.md | 44 +++++++++----------
README.ru.md | 42 +++++++++---------
README.zh-CN.md | 42 +++++++++---------
docs/architecture.md | 100 +++++++++++++++++++++----------------------
docs/devices.md | 84 ++++++++++++++++++------------------
docs/security.md | 20 ++++-----
docs/testing.md | 32 +++++++-------
docs/usage.md | 38 ++++++++--------
8 files changed, 201 insertions(+), 201 deletions(-)
diff --git a/README.md b/README.md
index 45111d09..56abfa69 100644
--- a/README.md
+++ b/README.md
@@ -40,10 +40,10 @@ A small Android app for watching RTSP streams from IP cameras, with hardware H.2
Ready-to-install builds are in the [**latest** release](https://github.com/OpenIPC/decoder/releases/tag/latest):
-| File | What it is |
-|---|---|
-| `decoder-v2.apk` | **Current version**, recommended |
-| `decoder.apk` | Legacy version from the `basic` branch |
+| File | What it is |
+|------------------|----------------------------------------|
+| `decoder-v2.apk` | **Current version**, recommended |
+| `decoder.apk` | Legacy version from the `basic` branch |
Every build is replaced in `latest` by the next one. Development is continuous, so there are no releases per version; every `v2` and `basic` build is also posted with its version and commit to the OpenIPC Telegram channel ([our channels](https://openipc.org/our-channels)).
@@ -74,14 +74,14 @@ A beta channel is available on [Google Play](https://play.google.com/apps/intern
[ Exit | Reset | Debug | UDP ]
```
-| Input | Action |
-|---|---|
-| Tap / **OK** | Open or close the menu |
-| **1**–**8** | Camera slot; the number's colour shows the connection quality |
-| **Grid** / **Slides** | Multi-view or one camera / every frame or key frames only |
-| **Debug** / **UDP** | Text in the multi-view cells / the camera that pushes RTP to port 5600 |
-| Pinch, double tap | Zoom and pan, reset the zoom (single view) |
-| Long press | Screenshot (single view) / play this cell's sound (multi-view) |
+| Input | Action |
+|-----------------------|------------------------------------------------------------------------|
+| Tap / **OK** | Open or close the menu |
+| **1**–**8** | Camera slot; the number's colour shows the connection quality |
+| **Grid** / **Slides** | Multi-view or one camera / every frame or key frames only |
+| **Debug** / **UDP** | Text in the multi-view cells / the camera that pushes RTP to port 5600 |
+| Pinch, double tap | Zoom and pan, reset the zoom (single view) |
+| Long press | Screenshot (single view) / play this cell's sound (multi-view) |
Every item, the remote, sharing a URL from another app and **Reset**: [Usage](docs/usage.md).
@@ -101,16 +101,16 @@ Every item, the remote, sharing a URL from another app and **Reset**: [Usage](do
## Documentation
-| Document | Contents |
-|---|---|
-| [Usage](docs/usage.md) | menu, gestures and keys, remote control, multi-view, Slides, the U cell, WebUI, sharing a URL from another app, screenshots, Reset |
-| [Transports](docs/transports.md) | URL formats and options: RTSP over TCP/UDP/Auto/multicast, `udp://` push (Majestic `outgoing`), HTTP MJPEG; codecs, authentication, what the servers support |
-| [Debug](docs/debug.md) | debug page on port 8085, `/stats.json` field reference, `/log`, snapshots, what to send with a bug report |
-| [Architecture](docs/architecture.md) | data path, classes, threads, the decode planner, SurfaceView rendering, repository layout |
-| [Testing and releases](docs/testing.md) | branches, CI, unit tests, versions and releases, branch builds, `mapping.txt`, building locally |
-| [Security](docs/security.md) | credential store, debug server (LAN only), log redaction, no encryption on the network |
-| [Devices](docs/devices.md) | verified and not working devices, hardware decoder notes, device and camera reports |
-| [Drones and Wi‑Fi toys](docs/toys.md) | how toys stream, FLOW-UFO (in testing), what to try with an unknown toy, what to send |
+| Document | Contents |
+|-----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [Usage](docs/usage.md) | menu, gestures and keys, remote control, multi-view, Slides, the U cell, WebUI, sharing a URL from another app, screenshots, Reset |
+| [Transports](docs/transports.md) | URL formats and options: RTSP over TCP/UDP/Auto/multicast, `udp://` push (Majestic `outgoing`), HTTP MJPEG; codecs, authentication, what the servers support |
+| [Debug](docs/debug.md) | debug page on port 8085, `/stats.json` field reference, `/log`, snapshots, what to send with a bug report |
+| [Architecture](docs/architecture.md) | data path, classes, threads, the decode planner, SurfaceView rendering, repository layout |
+| [Testing and releases](docs/testing.md) | branches, CI, unit tests, versions and releases, branch builds, `mapping.txt`, building locally |
+| [Security](docs/security.md) | credential store, debug server (LAN only), log redaction, no encryption on the network |
+| [Devices](docs/devices.md) | verified and not working devices, hardware decoder notes, device and camera reports |
+| [Drones and Wi‑Fi toys](docs/toys.md) | how toys stream, FLOW-UFO (in testing), what to try with an unknown toy, what to send |
The change history is the [merged pull requests](https://github.com/OpenIPC/decoder/pulls?q=is%3Apr+is%3Amerged+base%3Av2) and the [commits of v2](https://github.com/OpenIPC/decoder/commits/v2).
diff --git a/README.ru.md b/README.ru.md
index 54f41a82..6b47b53f 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -40,10 +40,10 @@
Готовые сборки лежат в [релизе **latest**](https://github.com/OpenIPC/decoder/releases/tag/latest):
-| Файл | Что это |
-|---|---|
+| Файл | Что это |
+|------------------|-----------------------------------|
| `decoder-v2.apk` | **Текущая версия**, рекомендуется |
-| `decoder.apk` | Старая версия из ветки `basic` |
+| `decoder.apk` | Старая версия из ветки `basic` |
Каждая новая сборка заменяет предыдущую в `latest`. Разработка идёт непрерывно, поэтому отдельных релизов по версиям нет; каждая сборка веток `v2` и `basic` с номером версии и коммитом публикуется в Telegram-канале OpenIPC ([наши каналы](https://openipc.org/our-channels)).
@@ -74,14 +74,14 @@
[ Exit | Reset | Debug | UDP ]
```
-| Действие | Результат |
-|---|---|
-| Касание / **OK** | Открыть или закрыть меню |
-| **1**–**8** | Слот камеры; цвет номера показывает качество связи |
-| **Grid** / **Slides** | Мультивид или одна камера / все кадры или только ключевые |
-| **Debug** / **UDP** | Текст в ячейках мультивида / камера, которая шлёт RTP на порт 5600 |
-| Два пальца, двойное касание | Масштаб и перемещение, сброс масштаба (одиночный режим) |
-| Долгое нажатие | Скриншот (одиночный режим) / звук этой ячейки (мультивид) |
+| Действие | Результат |
+|-----------------------------|--------------------------------------------------------------------|
+| Касание / **OK** | Открыть или закрыть меню |
+| **1**–**8** | Слот камеры; цвет номера показывает качество связи |
+| **Grid** / **Slides** | Мультивид или одна камера / все кадры или только ключевые |
+| **Debug** / **UDP** | Текст в ячейках мультивида / камера, которая шлёт RTP на порт 5600 |
+| Два пальца, двойное касание | Масштаб и перемещение, сброс масштаба (одиночный режим) |
+| Долгое нажатие | Скриншот (одиночный режим) / звук этой ячейки (мультивид) |
Все пункты меню, пульт, передача адреса из другого приложения и **Reset** — в [Usage](docs/usage.md) (на английском).
@@ -103,16 +103,16 @@
Документация написана на английском языке; ссылки ведут на английские документы.
-| Документ | Содержание |
-|---|---|
-| [Usage](docs/usage.md) | меню, жесты и клавиши, пульт, мультивид, Slides, ячейка U, WebUI, передача адреса из другого приложения, скриншоты, Reset |
-| [Transports](docs/transports.md) | форматы адресов и параметры: RTSP поверх TCP/UDP/Auto/multicast, приём `udp://` (Majestic `outgoing`), HTTP MJPEG; кодеки, авторизация, что умеют серверы |
-| [Debug](docs/debug.md) | отладочная страница на порту 8085, описание полей `/stats.json`, `/log`, снимки, что приложить к сообщению об ошибке |
-| [Architecture](docs/architecture.md) | путь данных, классы, потоки, планировщик декодирования, вывод через SurfaceView, структура репозитория |
-| [Testing and releases](docs/testing.md) | ветки, CI, юнит-тесты, версии и релизы, сборки веток, `mapping.txt`, локальная сборка |
-| [Security](docs/security.md) | хранилище учётных данных, отладочный сервер (только локальная сеть), скрытие паролей в логах, отсутствие шифрования в сети |
-| [Devices](docs/devices.md) | проверенные и неработающие устройства, особенности аппаратных декодеров, отчёты об устройствах и камерах |
-| [Drones and Wi‑Fi toys](docs/toys.md) | как передают видео игрушки, FLOW-UFO (в тестировании), что попробовать с неизвестной игрушкой, что прислать |
+| Документ | Содержание |
+|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [Usage](docs/usage.md) | меню, жесты и клавиши, пульт, мультивид, Slides, ячейка U, WebUI, передача адреса из другого приложения, скриншоты, Reset |
+| [Transports](docs/transports.md) | форматы адресов и параметры: RTSP поверх TCP/UDP/Auto/multicast, приём `udp://` (Majestic `outgoing`), HTTP MJPEG; кодеки, авторизация, что умеют серверы |
+| [Debug](docs/debug.md) | отладочная страница на порту 8085, описание полей `/stats.json`, `/log`, снимки, что приложить к сообщению об ошибке |
+| [Architecture](docs/architecture.md) | путь данных, классы, потоки, планировщик декодирования, вывод через SurfaceView, структура репозитория |
+| [Testing and releases](docs/testing.md) | ветки, CI, юнит-тесты, версии и релизы, сборки веток, `mapping.txt`, локальная сборка |
+| [Security](docs/security.md) | хранилище учётных данных, отладочный сервер (только локальная сеть), скрытие паролей в логах, отсутствие шифрования в сети |
+| [Devices](docs/devices.md) | проверенные и неработающие устройства, особенности аппаратных декодеров, отчёты об устройствах и камерах |
+| [Drones and Wi‑Fi toys](docs/toys.md) | как передают видео игрушки, FLOW-UFO (в тестировании), что попробовать с неизвестной игрушкой, что прислать |
История изменений — это [принятые pull request'ы](https://github.com/OpenIPC/decoder/pulls?q=is%3Apr+is%3Amerged+base%3Av2) и [коммиты ветки v2](https://github.com/OpenIPC/decoder/commits/v2).
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 1fea1b09..2b1c9b43 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -40,10 +40,10 @@
可直接安装的版本位于 [**latest** 发布页](https://github.com/OpenIPC/decoder/releases/tag/latest):
-| 文件 | 说明 |
-|---|---|
+| 文件 | 说明 |
+|------------------|------------------------|
| `decoder-v2.apk` | **当前版本**,推荐使用 |
-| `decoder.apk` | `basic` 分支的旧版本 |
+| `decoder.apk` | `basic` 分支的旧版本 |
每次新构建都会替换 `latest` 中的上一个构建。开发是持续进行的,因此不再按版本单独发布;`v2` 和 `basic` 分支的每个构建连同版本号和提交都会发布到 OpenIPC 的 Telegram 频道([我们的频道](https://openipc.org/our-channels))。
@@ -74,14 +74,14 @@
[ Exit | Reset | Debug | UDP ]
```
-| 输入 | 功能 |
-|---|---|
-| 点击 / **OK** | 打开或关闭菜单 |
-| **1**–**8** | 摄像机槽位;编号的颜色表示连接质量 |
-| **Grid** / **Slides** | 多画面或单路画面 / 解码全部帧或仅关键帧 |
-| **Debug** / **UDP** | 多画面中每个画面的文字 / 向端口 5600 推送 RTP 的摄像机 |
-| 双指捏合、双击 | 缩放和平移、恢复原始大小(单路画面) |
-| 长按 | 截图(单路画面)/ 播放该画面的声音(多画面) |
+| 输入 | 功能 |
+|-----------------------|--------------------------------------------------------|
+| 点击 / **OK** | 打开或关闭菜单 |
+| **1**–**8** | 摄像机槽位;编号的颜色表示连接质量 |
+| **Grid** / **Slides** | 多画面或单路画面 / 解码全部帧或仅关键帧 |
+| **Debug** / **UDP** | 多画面中每个画面的文字 / 向端口 5600 推送 RTP 的摄像机 |
+| 双指捏合、双击 | 缩放和平移、恢复原始大小(单路画面) |
+| 长按 | 截图(单路画面)/ 播放该画面的声音(多画面) |
菜单各项、遥控器、从其他应用分享地址以及 **Reset** 的说明见 [Usage](docs/usage.md)(英文)。
@@ -103,16 +103,16 @@
文档以英文撰写,以下链接均指向英文文档。
-| 文档 | 内容 |
-|---|---|
-| [Usage](docs/usage.md) | 菜单、手势与按键、遥控器、多画面、Slides、U 画面、WebUI、从其他应用分享地址、截图、Reset |
-| [Transports](docs/transports.md) | 地址格式与参数:RTSP over TCP/UDP/Auto/组播、`udp://` 推流接收(Majestic `outgoing`)、HTTP MJPEG;编码、认证、各服务器的支持情况 |
-| [Debug](docs/debug.md) | 端口 8085 上的调试页面、`/stats.json` 字段说明、`/log`、快照、提交错误报告时需附带的内容 |
-| [Architecture](docs/architecture.md) | 数据流程、类、线程、解码调度器、SurfaceView 渲染、仓库结构 |
-| [Testing and releases](docs/testing.md) | 分支、CI、单元测试、版本与发布、分支构建、`mapping.txt`、本地构建 |
-| [Security](docs/security.md) | 凭据存储、调试服务器(仅限局域网)、日志脱敏、网络传输未加密 |
-| [Devices](docs/devices.md) | 已验证和无法使用的设备、硬件解码器说明、设备与摄像机报告 |
-| [Drones and Wi‑Fi toys](docs/toys.md) | 玩具如何传输视频、FLOW-UFO(测试中)、遇到未知玩具时可以尝试什么、需要提交哪些信息 |
+| 文档 | 内容 |
+|-----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
+| [Usage](docs/usage.md) | 菜单、手势与按键、遥控器、多画面、Slides、U 画面、WebUI、从其他应用分享地址、截图、Reset |
+| [Transports](docs/transports.md) | 地址格式与参数:RTSP over TCP/UDP/Auto/组播、`udp://` 推流接收(Majestic `outgoing`)、HTTP MJPEG;编码、认证、各服务器的支持情况 |
+| [Debug](docs/debug.md) | 端口 8085 上的调试页面、`/stats.json` 字段说明、`/log`、快照、提交错误报告时需附带的内容 |
+| [Architecture](docs/architecture.md) | 数据流程、类、线程、解码调度器、SurfaceView 渲染、仓库结构 |
+| [Testing and releases](docs/testing.md) | 分支、CI、单元测试、版本与发布、分支构建、`mapping.txt`、本地构建 |
+| [Security](docs/security.md) | 凭据存储、调试服务器(仅限局域网)、日志脱敏、网络传输未加密 |
+| [Devices](docs/devices.md) | 已验证和无法使用的设备、硬件解码器说明、设备与摄像机报告 |
+| [Drones and Wi‑Fi toys](docs/toys.md) | 玩具如何传输视频、FLOW-UFO(测试中)、遇到未知玩具时可以尝试什么、需要提交哪些信息 |
更新历史即[已合并的 pull request](https://github.com/OpenIPC/decoder/pulls?q=is%3Apr+is%3Amerged+base%3Av2) 和 [v2 分支的提交](https://github.com/OpenIPC/decoder/commits/v2)。
diff --git a/docs/architecture.md b/docs/architecture.md
index 9300d32b..72746236 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -41,48 +41,48 @@ The single view runs one `RtspClient` (one `RtspSession`, assemblers, audio, con
## Classes
-| Class | Role |
-|---|---|
-| `Decoder` | Main activity: UI, lifecycle, settings, single-view decoding, multi-view grid, debug state |
-| `RtspClient` | Single-view stream: RTSP session, NAL assembly, audio, connection quality (jitter) |
-| `RtspSession` | One session over TCP, UDP or multicast (and the `udp://`, `http://` fronts): handshake, RTP receive, keepalive, watchdog, reconnect with back-off, Auto fallback |
-| `RtspAuth`, `Sdp` | Basic and Digest authentication; parsing of the session description |
-| `Transport` | Slot transport mode; the RTSP `Transport` header (request and reply) |
-| `UdpReceiver`, `RtpReorder` | UDP and multicast receive with a sender filter; packet reordering |
-| `RtpListener` | `udp://` slots: plain RTP pushed by the camera |
-| `HttpMjpeg` | `http://` slots: MJPEG over HTTP (`multipart/x-mixed-replace`, chunked or not) |
-| `NalAssembler` | H.264/H.265 NAL units from RTP; drops units damaged by packet loss, or passes them on cut short |
-| `JpegAssembler` | Whole JPEG frames from RTP/JPEG (RFC 2435) |
-| `JpegCodec`, `JpegRenderer` | MJPEG on screen: the device's JPEG decoder where there is one, otherwise BitmapFactory |
-| `MediaCodecManager`, `DecoderRetry` | Video decoder lifecycle, instance budget per MIME type, software fallback, recovery after errors |
-| `CodecInfo` | The device's video decoders and their declared limits (for `/stats.json`) |
-| `DecodePlanner` | Multi-view: which cells decode every frame and which only key frames |
-| `AudioManager`, `AudioConfig` | Audio: G.711/L16 conversion, AAC/Opus decoding, AudioTrack playback |
-| `QuadCell` | One multi-view cell: its own session, decoder, queue and optional audio |
-| `MenuManager` | Menu grid, URL dialog, reset dialog, WebUI browser |
-| `CredentialStore`, `KeystoreCipher` | Encrypted storage of camera URLs (Android Keystore) |
-| `FramePool`, `Frame` | Buffer pool that reduces garbage-collector load |
-| `DebugServer`, `LogBuffer`, `NetInfo`, `Snapshots` | Debug server on port 8085, redacted log ring, device addresses and the LAN rule, screen snapshots |
-| `StreamProbe`, `StreamProbes` | Short stream checks of the slots not on screen (single view, debug page in use) |
-| `App`, `Crash`, `Restart` | Uncaught-exception handler and crash screen; restart after Reset |
+| Class | Role |
+|----------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `Decoder` | Main activity: UI, lifecycle, settings, single-view decoding, multi-view grid, debug state |
+| `RtspClient` | Single-view stream: RTSP session, NAL assembly, audio, connection quality (jitter) |
+| `RtspSession` | One session over TCP, UDP or multicast (and the `udp://`, `http://` fronts): handshake, RTP receive, keepalive, watchdog, reconnect with back-off, Auto fallback |
+| `RtspAuth`, `Sdp` | Basic and Digest authentication; parsing of the session description |
+| `Transport` | Slot transport mode; the RTSP `Transport` header (request and reply) |
+| `UdpReceiver`, `RtpReorder` | UDP and multicast receive with a sender filter; packet reordering |
+| `RtpListener` | `udp://` slots: plain RTP pushed by the camera |
+| `HttpMjpeg` | `http://` slots: MJPEG over HTTP (`multipart/x-mixed-replace`, chunked or not) |
+| `NalAssembler` | H.264/H.265 NAL units from RTP; drops units damaged by packet loss, or passes them on cut short |
+| `JpegAssembler` | Whole JPEG frames from RTP/JPEG (RFC 2435) |
+| `JpegCodec`, `JpegRenderer` | MJPEG on screen: the device's JPEG decoder where there is one, otherwise BitmapFactory |
+| `MediaCodecManager`, `DecoderRetry` | Video decoder lifecycle, instance budget per MIME type, software fallback, recovery after errors |
+| `CodecInfo` | The device's video decoders and their declared limits (for `/stats.json`) |
+| `DecodePlanner` | Multi-view: which cells decode every frame and which only key frames |
+| `AudioManager`, `AudioConfig` | Audio: G.711/L16 conversion, AAC/Opus decoding, AudioTrack playback |
+| `QuadCell` | One multi-view cell: its own session, decoder, queue and optional audio |
+| `MenuManager` | Menu grid, URL dialog, reset dialog, WebUI browser |
+| `CredentialStore`, `KeystoreCipher` | Encrypted storage of camera URLs (Android Keystore) |
+| `FramePool`, `Frame` | Buffer pool that reduces garbage-collector load |
+| `DebugServer`, `LogBuffer`, `NetInfo`, `Snapshots` | Debug server on port 8085, redacted log ring, device addresses and the LAN rule, screen snapshots |
+| `StreamProbe`, `StreamProbes` | Short stream checks of the slots not on screen (single view, debug page in use) |
+| `App`, `Crash`, `Restart` | Uncaught-exception handler and crash screen; restart after Reset |
## Threads
Network, video decoding and audio run on separate threads, so a slow decoder never blocks the socket.
-| Thread | Work |
-|---|---|
-| UI (main) | layout, menu, planner step and stats once a second, debug state on request |
-| `-net` | one per session: connect, RTSP, receive RTP or HTTP, assemble, queue |
-| `-wd` | one per session: watchdog (no video for 8 s → reconnect) and RTSP keepalive |
-| single-view pool (2) | video decode from the queue; audio playback |
-| `Quad-N-dec` | one per multi-view cell: decode from the cell's queue |
-| `quad-audio` | audio of the selected multi-view cell |
-| `rtsp-aac-decode` | AAC/Opus output of the audio decoder |
-| `decoder-release` | short-lived: stops and releases a decoder off the UI thread |
-| `debug-http-*` | accept loop, two workers at background priority, request deadlines |
-| `probe-sched`, `probe` | stream checks: scheduler and two workers at background priority |
-| `codec-info` | lists the device's decoders once at start |
+| Thread | Work |
+|------------------------|-----------------------------------------------------------------------------|
+| UI (main) | layout, menu, planner step and stats once a second, debug state on request |
+| `-net` | one per session: connect, RTSP, receive RTP or HTTP, assemble, queue |
+| `-wd` | one per session: watchdog (no video for 8 s → reconnect) and RTSP keepalive |
+| single-view pool (2) | video decode from the queue; audio playback |
+| `Quad-N-dec` | one per multi-view cell: decode from the cell's queue |
+| `quad-audio` | audio of the selected multi-view cell |
+| `rtsp-aac-decode` | AAC/Opus output of the audio decoder |
+| `decoder-release` | short-lived: stops and releases a decoder off the UI thread |
+| `debug-http-*` | accept loop, two workers at background priority, request deadlines |
+| `probe-sched`, `probe` | stream checks: scheduler and two workers at background priority |
+| `codec-info` | lists the device's decoders once at start |
The queues between network and decoder are bounded (30 frames per cell); when a queue is full, the frame is dropped and counted as a decoder loss (`lost_fps`), not a network loss.
@@ -96,19 +96,19 @@ The queues between network and decoder are bounded (30 frames per cell); when a
**Priority:** the U cell while it receives a stream, the cell whose sound plays, then slot order.
-| Step | Rule |
-|---|---|
-| warm-up | the first 10 s after the grid starts: losses decide nothing, promotions that fit go on |
-| settle | after a change, 3 s before the next one |
-| loss signal | decoder losses (queue full, no input buffer): at full rate ≥ 2 frames/s and ≥ 10 % of the received rate; on key frames any loss. 3 lossy samples within 10 s trigger a demotion |
-| demote | the lowest-priority full-rate cell goes to key frames. The capacity drops to 90 % of the load, never below a proven load; that load is remembered as failed, and the next probe waits twice as long (up to 10 min) |
-| transient | a loss below a load that already played quietly is forgiven once: the cell is demoted, but the capacity and the failed load are kept. Forgiven again only after a load at least as high plays quietly again |
-| pause | when even key frames are too much, the lowest-priority cell is paused (never the last one playing); it is resumed on key frames after a quiet period that doubles with every pause |
-| promote | the highest-priority key-frame cell whose load is known, if the result fits within the capacity and stays below 90 % of a failed load. Beyond the capacity only after a quiet period (a probe, from 30 s). A lower cell may go first only if it surely fits |
-| proven | a load that plays 30 s without losses is proven; above the capacity it raises it. A failure at about that load is forgotten |
-| retry | a failed load is tried again one hour after the last demotion |
-| unknown | a playing cell whose size and rate are not known yet holds the lower cells for up to 10 s |
-| stall | a second in which nothing decodes at all (and nothing is lost) is skipped and does not count as quiet |
+| Step | Rule |
+|-------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| warm-up | the first 10 s after the grid starts: losses decide nothing, promotions that fit go on |
+| settle | after a change, 3 s before the next one |
+| loss signal | decoder losses (queue full, no input buffer): at full rate ≥ 2 frames/s and ≥ 10 % of the received rate; on key frames any loss. 3 lossy samples within 10 s trigger a demotion |
+| demote | the lowest-priority full-rate cell goes to key frames. The capacity drops to 90 % of the load, never below a proven load; that load is remembered as failed, and the next probe waits twice as long (up to 10 min) |
+| transient | a loss below a load that already played quietly is forgiven once: the cell is demoted, but the capacity and the failed load are kept. Forgiven again only after a load at least as high plays quietly again |
+| pause | when even key frames are too much, the lowest-priority cell is paused (never the last one playing); it is resumed on key frames after a quiet period that doubles with every pause |
+| promote | the highest-priority key-frame cell whose load is known, if the result fits within the capacity and stays below 90 % of a failed load. Beyond the capacity only after a quiet period (a probe, from 30 s). A lower cell may go first only if it surely fits |
+| proven | a load that plays 30 s without losses is proven; above the capacity it raises it. A failure at about that load is forgotten |
+| retry | a failed load is tried again one hour after the last demotion |
+| unknown | a playing cell whose size and rate are not known yet holds the lower cells for up to 10 s |
+| stall | a second in which nothing decodes at all (and nothing is lost) is skipped and does not count as quiet |
The learnt capacity and the failed load are stored per device (`decoder_capacity_v2`, `decoder_failed_load_v2`); stored values at or below the floor are not trusted. `/stats.json` shows them under `decoder` ([debug.md](debug.md#top-level)).
diff --git a/docs/devices.md b/docs/devices.md
index 6f36b6e6..74049b49 100644
--- a/docs/devices.md
+++ b/docs/devices.md
@@ -13,40 +13,40 @@ Tested the app on your device? Please [**submit a Device report**](https://githu
Most devices below were reported with the legacy 1.0x version (`basic` branch); the *App* column shows the version that was tested. New reports should include the app version.
-| Type | Device | Android | Kernel | App | Notes |
-|-------------|----------------------------------------|-----------------------------------|----------------|---------|--------------------------------------------------------------------------------------------|
-| Phone | Asus ZC553KL | 8.1 | 3.18.71 | 1.0x | |
-| Phone | Blackview BV4900Pro | 12 | 4.19.191 | 1.0x | |
-| Phone | Oppo 5X | ColorOS 15.0 | 5.15.149 | 1.0x | |
-| Phone | Oppo A17 | ColorOS 12.1 | 4.19.191 | 1.0x | |
-| Phone | Samsung Galaxy A51 | 13 | 4.14.113 | 1.0x | |
-| Phone | Samsung Galaxy M21 (SM-M215F/DSN) | 12 | 4.14.113 | 1.0x | |
-| Phone | Samsung Galaxy S25 | 15 | 6.6.30 | 1.0x | |
-| Phone | Samsung A55 5G | 14 | 6.1.93 | 1.0x | |
-| Phone | Samsung Galaxy J7 | 10 | 3.18.150 | 1.0x | |
-| Phone | Samsung S8 | 9 | 4.4.153 | 1.0x | |
-| Phone | Samsung S23 Ultra | 15 | 5.15.153 | 1.0x | |
+| Type | Device | Android | Kernel | App | Notes |
+|-------------|----------------------------------------|-----------------------------------|----------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Phone | Asus ZC553KL | 8.1 | 3.18.71 | 1.0x | |
+| Phone | Blackview BV4900Pro | 12 | 4.19.191 | 1.0x | |
+| Phone | Oppo 5X | ColorOS 15.0 | 5.15.149 | 1.0x | |
+| Phone | Oppo A17 | ColorOS 12.1 | 4.19.191 | 1.0x | |
+| Phone | Samsung Galaxy A51 | 13 | 4.14.113 | 1.0x | |
+| Phone | Samsung Galaxy M21 (SM-M215F/DSN) | 12 | 4.14.113 | 1.0x | |
+| Phone | Samsung Galaxy S25 | 15 | 6.6.30 | 1.0x | |
+| Phone | Samsung A55 5G | 14 | 6.1.93 | 1.0x | |
+| Phone | Samsung Galaxy J7 | 10 | 3.18.150 | 1.0x | |
+| Phone | Samsung S8 | 9 | 4.4.153 | 1.0x | |
+| Phone | Samsung S23 Ultra | 15 | 5.15.153 | 1.0x | |
| Phone | TECNO KM4 | 15 | 5.15.194 | v2 2.04 | Lab touch device: WebUI with the on-screen keyboard checked ([#159](https://github.com/OpenIPC/decoder/issues/159), [#160](https://github.com/OpenIPC/decoder/issues/160)); Close under the status bar ([#169](https://github.com/OpenIPC/decoder/issues/169)) |
-| Phone | Xiaomi Redmi 7A | 10 | 4.9.261 | 1.0x | |
-| Phone | Xiaomi Redmi Note 7 (M1901F7G) | 10 | 4.4.192 | 1.0x | |
-| Phone | Xiaomi Redmi Note 13 4G | 13 | 5.15.94 | 1.0x | |
-| Tablet | Lenovo TB-8504X | 8.1.0 | 3.18.71 | v2 2.00 | H.265 and multi-view work; WebUI input fixed in 2.02 ([#159](https://github.com/OpenIPC/decoder/issues/159)) |
-| Tablet | Lenovo TB-X304L | 8.1.0 | 3.18.71 | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
-| Tablet | Lenovo TB-X606F | 10 | — | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
-| Tablet | Lenovo Tab P11 | 11 | 4.19.157-perf+ | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
-| Tablet | Samsung Galaxy Tab A | 7.1.1 | 3.10.49 | 1.0x | |
-| Tablet | Samsung Galaxy Tab A | 11 | 4.9.227 | 1.0x | |
-| Tablet | Samsung Tab A7 (SM-T505) | 12 | 4.19.157-perf- | 1.0x | |
-| Set-top box | A95X F3 AIR | 9 (SlimBOXtv AOSP 9.14, no GAPPS) | 4.9.113 | 1.0x | |
-| Set-top box | IE X3 Air | 9 (SlimBOXtv AOSP 9.14, no GAPPS) | 4.9.113 | 1.0x | |
-| Set-top box | AG-216 | 10 | 4.9.170 | v2 2.00 | v2 test device; decoder budget measured at ≈ 1080p60 in total (two 1080p30 at full rate) |
-| Set-top box | SberBox SBDV-00001 | custom | 4.9.228 | 1.0x | |
-| Set-top box | X96Q (Allwinner H313) | 10 | 4.9.170 | 1.0x | |
-| Projector | SIBIRLINK HY300 / HY320 | 11 | 4.19.323 | v2 2.02 | 1080p H.264 / H.265 work; 2304×1296 and 2560×1440 do not start ([#165](https://github.com/OpenIPC/decoder/issues/165), [#166](https://github.com/OpenIPC/decoder/issues/166)) |
-| TV | Haier Candy Android TV 2K | 11 | 4.9.243+ | 1.0x | No icon in the device's app list |
-| TV | Yandex TV Novex NVX-55U169TSY | 7.1.1 | 4.4.3 | 1.0x | |
-| Car | Media Center JCAC10003 (ac8227/ac8229) | 12 | 3.18.79+ | 1.0x | |
-| Car | Mirror Z55 | 8.1.0 | 4.4.83 | 1.0x | |
+| Phone | Xiaomi Redmi 7A | 10 | 4.9.261 | 1.0x | |
+| Phone | Xiaomi Redmi Note 7 (M1901F7G) | 10 | 4.4.192 | 1.0x | |
+| Phone | Xiaomi Redmi Note 13 4G | 13 | 5.15.94 | 1.0x | |
+| Tablet | Lenovo TB-8504X | 8.1.0 | 3.18.71 | v2 2.00 | H.265 and multi-view work; WebUI input fixed in 2.02 ([#159](https://github.com/OpenIPC/decoder/issues/159)) |
+| Tablet | Lenovo TB-X304L | 8.1.0 | 3.18.71 | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
+| Tablet | Lenovo TB-X606F | 10 | — | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
+| Tablet | Lenovo Tab P11 | 11 | 4.19.157-perf+ | 1.0x | Picture cropped in 1.0x, fixed in v2 ([#17](https://github.com/OpenIPC/decoder/issues/17)) |
+| Tablet | Samsung Galaxy Tab A | 7.1.1 | 3.10.49 | 1.0x | |
+| Tablet | Samsung Galaxy Tab A | 11 | 4.9.227 | 1.0x | |
+| Tablet | Samsung Tab A7 (SM-T505) | 12 | 4.19.157-perf- | 1.0x | |
+| Set-top box | A95X F3 AIR | 9 (SlimBOXtv AOSP 9.14, no GAPPS) | 4.9.113 | 1.0x | |
+| Set-top box | IE X3 Air | 9 (SlimBOXtv AOSP 9.14, no GAPPS) | 4.9.113 | 1.0x | |
+| Set-top box | AG-216 | 10 | 4.9.170 | v2 2.00 | v2 test device; decoder budget measured at ≈ 1080p60 in total (two 1080p30 at full rate) |
+| Set-top box | SberBox SBDV-00001 | custom | 4.9.228 | 1.0x | |
+| Set-top box | X96Q (Allwinner H313) | 10 | 4.9.170 | 1.0x | |
+| Projector | SIBIRLINK HY300 / HY320 | 11 | 4.19.323 | v2 2.02 | 1080p H.264 / H.265 work; 2304×1296 and 2560×1440 do not start ([#165](https://github.com/OpenIPC/decoder/issues/165), [#166](https://github.com/OpenIPC/decoder/issues/166)) |
+| TV | Haier Candy Android TV 2K | 11 | 4.9.243+ | 1.0x | No icon in the device's app list |
+| TV | Yandex TV Novex NVX-55U169TSY | 7.1.1 | 4.4.3 | 1.0x | |
+| Car | Media Center JCAC10003 (ac8227/ac8229) | 12 | 3.18.79+ | 1.0x | |
+| Car | Mirror Z55 | 8.1.0 | 4.4.83 | 1.0x | |
## Not working
@@ -58,12 +58,12 @@ Most devices below were reported with the legacy 1.0x version (`basic` branch);
What the app has measured or met on real devices. The decoders of a device are listed in `/stats.json` (`decoders`: `name`, `type`, `hardware`, `adaptive`, `max_instances`, `fps_1080p`; see [debug.md](debug.md#decoders)).
-| Topic | Note |
-|---|---|
-| Decoder budget | The AG-216 (Allwinner H313/H616 family, Android 10) decodes about 1080p60 in total: two 1080p30 streams at full rate. Multi-view learns the budget of each device by measurement, see [architecture.md](architecture.md#decode-planner) |
-| Instance limit | The AG-216 allows 4 hardware H.265 decoder instances. Key-frame cells beyond that use a software decoder (`· software`); beyond that too, `Decoder limit: N × H.265` |
-| Stuck decoder | The AG-216's video decoder can stop responding until the device is restarted: frames arrive, none is decoded. The app says so on screen and in `/stats.json` (`decoder_stuck`). Cause unknown ([#145](https://github.com/OpenIPC/decoder/issues/145)) |
-| MJPEG | Hardware JPEG decoding is used where the device offers `video/jpeg` (also `video/mjpeg`, `video/x-motion-jpeg`). The AG-216 declares an Allwinner MJPEG decoder that Android does not list and that cannot be created; MJPEG is then decoded with BitmapFactory ([#132](https://github.com/OpenIPC/decoder/issues/132)) |
-| Window buffer stalls | On Mali TV boxes the app's window buffer stalled for seconds with every grid/single switch while the single view used a `TextureView`; since 2.09 the single view is a `SurfaceView` ([#76](https://github.com/OpenIPC/decoder/issues/76)) |
-| Large resolutions | The SIBIRLINK HY300 / HY320 projector plays 1080p H.264 / H.265, but 2304×1296 and 2560×1440 do not start ([#165](https://github.com/OpenIPC/decoder/issues/165), [#166](https://github.com/OpenIPC/decoder/issues/166)) |
-| Old Android | Android 5.0 is the minimum. Encrypted URL storage needs 6.0, multicast 8.0, screenshots and snapshots 7.0 |
+| Topic | Note |
+|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Decoder budget | The AG-216 (Allwinner H313/H616 family, Android 10) decodes about 1080p60 in total: two 1080p30 streams at full rate. Multi-view learns the budget of each device by measurement, see [architecture.md](architecture.md#decode-planner) |
+| Instance limit | The AG-216 allows 4 hardware H.265 decoder instances. Key-frame cells beyond that use a software decoder (`· software`); beyond that too, `Decoder limit: N × H.265` |
+| Stuck decoder | The AG-216's video decoder can stop responding until the device is restarted: frames arrive, none is decoded. The app says so on screen and in `/stats.json` (`decoder_stuck`). Cause unknown ([#145](https://github.com/OpenIPC/decoder/issues/145)) |
+| MJPEG | Hardware JPEG decoding is used where the device offers `video/jpeg` (also `video/mjpeg`, `video/x-motion-jpeg`). The AG-216 declares an Allwinner MJPEG decoder that Android does not list and that cannot be created; MJPEG is then decoded with BitmapFactory ([#132](https://github.com/OpenIPC/decoder/issues/132)) |
+| Window buffer stalls | On Mali TV boxes the app's window buffer stalled for seconds with every grid/single switch while the single view used a `TextureView`; since 2.09 the single view is a `SurfaceView` ([#76](https://github.com/OpenIPC/decoder/issues/76)) |
+| Large resolutions | The SIBIRLINK HY300 / HY320 projector plays 1080p H.264 / H.265, but 2304×1296 and 2560×1440 do not start ([#165](https://github.com/OpenIPC/decoder/issues/165), [#166](https://github.com/OpenIPC/decoder/issues/166)) |
+| Old Android | Android 5.0 is the minimum. Encrypted URL storage needs 6.0, multicast 8.0, screenshots and snapshots 7.0 |
diff --git a/docs/security.md b/docs/security.md
index d3b5d82c..fb5d7235 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -46,12 +46,12 @@ Anyone on the same LAN can see the camera pictures and hosts through it. On a ne
Applied to every line before it is kept for `/log` and to every string in `/stats.json`:
-| Pattern | Becomes |
-|---|---|
-| `rtsp://user:pass@`, `rtsps://`, `http://`, `https://` userinfo | `rtsp://***@` |
-| `pass=`, `password=`, `pwd=` parameters | `=***` |
-| `user=`, `username=`, `login=`, `usr=` parameters (debug server and `/log`) | `=***` |
-| `Authorization: ` | `Authorization: ***` |
+| Pattern | Becomes |
+|-----------------------------------------------------------------------------|-------------------------------|
+| `rtsp://user:pass@`, `rtsps://`, `http://`, `https://` userinfo | `rtsp://***@` |
+| `pass=`, `password=`, `pwd=` parameters | `=***` |
+| `user=`, `username=`, `login=`, `usr=` parameters (debug server and `/log`) | `=***` |
+| `Authorization: ` | `Authorization: ***` |
The app's own log calls redact URLs the same way, without the login rule ([#55](https://github.com/OpenIPC/decoder/issues/55)), and a shared link is logged by its host only: `adb logcat` shows no passwords, but may show a login that the camera expects in the URL path. Check reports before posting anyway.
@@ -61,10 +61,10 @@ The built-in browser opens the camera's web interface with JavaScript enabled. H
## App permissions
-| Permission | Why |
-|---|---|
-| `INTERNET` | streams, WebUI, the debug server |
-| `CHANGE_WIFI_MULTICAST_STATE` | the multicast lock for multicast RTP on Wi‑Fi |
+| Permission | Why |
+|------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|
+| `INTERNET` | streams, WebUI, the debug server |
+| `CHANGE_WIFI_MULTICAST_STATE` | the multicast lock for multicast RTP on Wi‑Fi |
| `WRITE_EXTERNAL_STORAGE` (Android ≤ 9), `READ_EXTERNAL_STORAGE` (≤ 12) | screenshots to `Pictures/OpenIPC` and reports to `Download/OpenIPC` on older Android versions |
No location, camera, microphone or account permissions. No analytics and no Google services.
diff --git a/docs/testing.md b/docs/testing.md
index a7588da7..79c73c5e 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -11,9 +11,9 @@
## Branches
-| Branch | Status |
-|---|---|
-| `v2` | **Main development branch** (default). Modular code in `android/`. Built as `decoder-v2.apk` |
+| Branch | Status |
+|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `v2` | **Main development branch** (default). Modular code in `android/`. Built as `decoder-v2.apk` |
| `basic` | Legacy single-file version (1.0x), kept for reference and older setups. Built as `decoder.apk`. Its version history is in the [`basic` README](https://github.com/OpenIPC/decoder/tree/basic#version-history) |
Base pull requests on `v2`.
@@ -22,18 +22,18 @@ Base pull requests on `v2`.
`.github/workflows/build.yml` runs on every push and pull request, and by hand (`workflow_dispatch`, optionally for a given commit). A newer push to the same branch cancels the older run, so `latest` always gets the build of the newest commit.
-| Job | Runs on | What it does |
-|---|---|---|
-| `test` | every push and pull request, forks included (no secrets) | `./gradlew testDebugUnitTest`, then `./gradlew lintDebug` against `app/lint-baseline.xml` (only new findings fail). Reports are uploaded when it fails |
-| `build` | pushes and manual runs, not pull requests | signed `assembleRelease` (R8 minify and resource shrinking), the R8 `mapping.txt` kept as a workflow artifact |
+| Job | Runs on | What it does |
+|---------|----------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `test` | every push and pull request, forks included (no secrets) | `./gradlew testDebugUnitTest`, then `./gradlew lintDebug` against `app/lint-baseline.xml` (only new findings fail). Reports are uploaded when it fails |
+| `build` | pushes and manual runs, not pull requests | signed `assembleRelease` (R8 minify and resource shrinking), the R8 `mapping.txt` kept as a workflow artifact |
What a `build` run publishes:
-| Branch | Output |
-|---|---|
-| `v2` | `decoder-v2.apk` and `decoder-v2-mapping.txt` in the [`latest`](https://github.com/OpenIPC/decoder/releases/tag/latest) release (replaced by every build); a message with the APK, version and commit to the OpenIPC Telegram channel |
-| `basic` | `decoder.apk` and its mapping in `latest`; Telegram |
-| any other | workflow artifacts `decoder-` (APK) and `…-mapping` on the run page |
+| Branch | Output |
+|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `v2` | `decoder-v2.apk` and `decoder-v2-mapping.txt` in the [`latest`](https://github.com/OpenIPC/decoder/releases/tag/latest) release (replaced by every build); a message with the APK, version and commit to the OpenIPC Telegram channel |
+| `basic` | `decoder.apk` and its mapping in `latest`; Telegram |
+| any other | workflow artifacts `decoder-` (APK) and `…-mapping` on the run page |
Every run, `v2` and `basic` included, also keeps its mapping as the artifact `…-mapping`. Artifacts expire after 90 days.
@@ -108,11 +108,11 @@ For every class, field and method it gives `original -> obfuscated`, with the li
A mapping fits **only the build it came from**. Use the one whose build matches the hash under **Exit** or `git` in `/stats.json`:
-| Build | Mapping |
-|---|---|
-| the current `v2` build | `decoder-v2-mapping.txt` in [`latest`](https://github.com/OpenIPC/decoder/releases/tag/latest) |
+| Build | Mapping |
+|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| the current `v2` build | `decoder-v2-mapping.txt` in [`latest`](https://github.com/OpenIPC/decoder/releases/tag/latest) |
| an older `v2` build, or any branch build | the artifact `decoder-v2-mapping` (or `decoder--mapping`) of the CI run that built it, kept 90 days. A push run is found by its commit (below). A manual run (*Run workflow* with `commit_hash`) is listed under the branch it was started on, not under the commit it built: `gh run list -R OpenIPC/decoder -w Build -e workflow_dispatch -b `, then pick the run by its time or ID |
-| builds before 2.08 | none: they were made before the mapping was kept (#181) |
+| builds before 2.08 | none: they were made before the mapping was kept (#181) |
```sh
RUN=$(gh run list -R OpenIPC/decoder -c -e push -w Build -L 1 --json databaseId -q '.[0].databaseId')
diff --git a/docs/usage.md b/docs/usage.md
index f09faeff..e1094a37 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -35,30 +35,30 @@ The menu is one grid: the eight slots on top, two rows of four below. Every word
-| Item | Action |
-|---|---|
-| **1**–**8** | Switch to a camera slot (single view). The number of the active slot shows the connection quality: green under 100 ms of jitter, yellow 100–300 ms, red above. A grey number is an empty slot |
-| **Settings** | URL and transport of the active slot. Single view only, grey elsewhere |
-| **WebUI** | The camera's web interface, see [WebUI](#webui). Single view only, grey elsewhere |
-| **Grid** | The view: [multi-view](#multi-view) (2×2 or 3×3) or the single camera. Does not change the playback mode |
-| **Slides** | The playback mode: every frame, or key frames only, see [Slides](#slides). Does not change the view |
-| **Exit** | Close the app. The small text under it is the version and build hash (`v2.10, `) |
-| **Reset** | Clear all settings after a confirmation, see [Reset](#reset) |
-| **Debug** | Text in the corner of every multi-view cell (resolution, codec, decoded/received fps, key frames, status), or none. **Long press** (remote: hold **OK**, or **Menu** on Debug): [save a report](debug.md#save-report) to `Download/OpenIPC` and share it |
-| **UDP** | [The U cell](#the-u-cell) full-screen |
+| Item | Action |
+|--------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **1**–**8** | Switch to a camera slot (single view). The number of the active slot shows the connection quality: green under 100 ms of jitter, yellow 100–300 ms, red above. A grey number is an empty slot |
+| **Settings** | URL and transport of the active slot. Single view only, grey elsewhere |
+| **WebUI** | The camera's web interface, see [WebUI](#webui). Single view only, grey elsewhere |
+| **Grid** | The view: [multi-view](#multi-view) (2×2 or 3×3) or the single camera. Does not change the playback mode |
+| **Slides** | The playback mode: every frame, or key frames only, see [Slides](#slides). Does not change the view |
+| **Exit** | Close the app. The small text under it is the version and build hash (`v2.10, `) |
+| **Reset** | Clear all settings after a confirmation, see [Reset](#reset) |
+| **Debug** | Text in the corner of every multi-view cell (resolution, codec, decoded/received fps, key frames, status), or none. **Long press** (remote: hold **OK**, or **Menu** on Debug): [save a report](debug.md#save-report) to `Download/OpenIPC` and share it |
+| **UDP** | [The U cell](#the-u-cell) full-screen |
Items that are on (Grid, Slides, Debug, UDP and the active slot) have an orange fill; the item the remote is on has a light blue frame. On a narrow screen or with a large font the labels are shortened with an ellipsis rather than clipped.
## Gestures and keys
-| Input | Action |
-|---|---|
-| Tap / **OK** / **Enter** (D-pad centre) | Open or close the menu |
-| **Back** / **Esc** | Close the menu or a dialog; without one, Back keeps its system action |
-| Pinch, then drag | Zoom (1× to 5×) and pan, single view |
-| Double tap | Reset the zoom |
-| Long press, single view | Save a screenshot, see [Screenshots](#screenshots) |
-| Long press, multi-view | Play this cell's sound; again to mute |
+| Input | Action |
+|-----------------------------------------|-----------------------------------------------------------------------|
+| Tap / **OK** / **Enter** (D-pad centre) | Open or close the menu |
+| **Back** / **Esc** | Close the menu or a dialog; without one, Back keeps its system action |
+| Pinch, then drag | Zoom (1× to 5×) and pan, single view |
+| Double tap | Reset the zoom |
+| Long press, single view | Save a screenshot, see [Screenshots](#screenshots) |
+| Long press, multi-view | Play this cell's sound; again to mute |
## Remote control