From f7f55ac2fb0d63cfeb3345ebe721b21584568305 Mon Sep 17 00:00:00 2001 From: Compot Developer Date: Tue, 13 Oct 2026 18:26:47 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20today's=20features=20in=20the=20READMEs?= =?UTF-8?q?=20(EN/RU/ZH)=20and=20LABORATORY=20=E2=80=94=20S/Q,=20MJPEG=20i?= =?UTF-8?q?n=20multi-view=20and=20over=20HTTP,=20damaged=20UDP=20frames,?= =?UTF-8?q?=20decoder=20warnings,=20Info=20switch,=20menu=20colours;=20scr?= =?UTF-8?q?eenshots=20follow=20in=20#151?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- LABORATORY.md | 5 ++++- README.md | 27 ++++++++++++++++++--------- README.ru.md | 25 +++++++++++++++++-------- README.zh-CN.md | 25 +++++++++++++++++-------- 4 files changed, 56 insertions(+), 26 deletions(-) diff --git a/LABORATORY.md b/LABORATORY.md index da99bac7..60ddd2e0 100644 --- a/LABORATORY.md +++ b/LABORATORY.md @@ -55,7 +55,9 @@ Common to both: | Android | 10 (API 29), `armeabi-v7a` (32-bit userspace on a 64-bit CPU) | | RAM / storage | 2 GB / `/data` 11 GB | | Screen | 1280×720 (16:9), 60 Hz, 160 dpi, `television` UI mode, no touch | -| HW video decoder | `OMX.allwinner.video.decoder.avc`; H.265 is decoded as `video/hevc` | +| HW video decoder | `OMX.allwinner.video.decoder.avc` (16 instances), `…hevc` (**4** instances); beyond that, key-frame cells use `c2.android.*` software decoders (#138) | +| JPEG decoder | `OMX.allwinner.video.decoder.mjpeg` is declared in `/vendor/etc/media_codecs.xml` but not listed by Android, and creating it fails (`0xfffffffe`): MJPEG always runs on the software path here | +| Decoder hang | Once (2026-10-13, after a long test session) every cell received but nothing decoded until `adb reboot`; the app now says so (#145, #147). In tests: all cells `0.0 fps` with `in_fps` > 0 → reboot the box first | | Quirks | The vendor firmware sometimes shows its own "Update Alert!!" dialog after screen off/on; unrelated to the app | Check the firmware of a box: `$ADB shell getprop ro.build.description`. If it changes, update the table above. @@ -111,6 +113,7 @@ Only for filling grid cells (Quad 2×2, 3×3) and screenshots; not used in strea ### URLs - OpenIPC cameras: `rtsp://:@/stream=0` (main), `/stream=1` (substream). +- MJPEG (Majestic): RTSP `rtsp://:@/stream=2` (RTP/JPEG, `.133`: 1280×720, 5 fps), HTTP `http://:@/mjpeg` (`multipart/x-mixed-replace`, chunked, Basic auth). - XM: `rtsp://:554/user=_password=_channel=1_stream=0.sdp?real_stream`. The password is in the path, so it also appears in the `Content-Base` header; the app redacts it in logs (#55). ### 3×3 layout (README screenshots, 8-slot tests) diff --git a/README.md b/README.md index eca868de..4700ac87 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ A beta channel is available on [Google Play](https://play.google.com/apps/intern ## Features -- **Video:** RTSP playback, hardware H.264/H.265 decoding via MediaCodec, MJPEG over RTSP (RTP/JPEG, single view), automatic decoder recovery, picture fitted to any screen shape +- **Video:** RTSP playback, hardware H.264/H.265 decoding via MediaCodec, MJPEG over RTSP (RTP/JPEG) and over HTTP (`multipart/x-mixed-replace`) in the single view and in multi-view, automatic decoder recovery, a lossy UDP stream shown with concealed damage instead of a black picture, a warning when the device's video decoder stops responding, picture fitted to any screen shape - **Audio:** AAC (RFC 3640 `mpeg4-generic`, ADTS, MP4A-LATM), Opus, G.711 μ-law/A-law, L16 PCM (mono/stereo); in multi-view, the sound of the selected cell -- **Display:** single camera, or multi-view (Quad) with up to 8 cameras plus the U cell (a camera that pushes RTP) in a 2×2 or 3×3 grid; adaptive decoding (full rate or key frames per cell, learnt per device); slides (S): every camera on key frames only +- **Display:** single camera, or multi-view (Quad) with up to 8 cameras plus the U cell (a camera that pushes RTP) in a 2×2 or 3×3 grid; adaptive decoding (full rate or key frames per cell, learnt per device); S: key frames only, in multi-view and in the single view - **Controls:** pinch-to-zoom, pan, double-tap to reset, Android TV remote (Leanback launcher) - **Network:** RTSP over TCP or UDP (Auto, the default, falls back to TCP when UDP is refused, brings no video or keeps losing key frames), the transport in use shown on connect, RTSP multicast, plain RTP pushed over UDP by the camera (unicast or multicast), Basic and Digest authentication, RTSP keepalive, automatic reconnection with exponential back-off, connection-quality indicator - **WebUI:** built-in browser for the camera's web interface @@ -61,13 +61,15 @@ A beta channel is available on [Google Play](https://play.google.com/apps/intern |--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **1**–**8** | Switch to a camera slot. The active slot's number shows the connection quality: green under 100 ms of jitter, yellow 100–300 ms, red above 300 ms. A grey number is an empty slot. | | **U** | The U cell full-screen: the camera that pushes RTP over UDP to port 5600 | -| **D** | Text in the corner of every multi-view cell (resolution, codec, decoded/received fps, key frames, status), or in none | -| **Q** | Turn multi-view (Quad) on or off | -| **S** | Slides: multi-view with every camera on key frames only (the picture updates once per key frame interval), the lowest decoder load; S or Q goes back to the adaptive grid | +| **D** | Text in the corner of every multi-view cell (resolution, codec, decoded/received fps, key frames, status), or in none | +| **Q** | The view: multi-view (Quad) or the single camera. Q does not change the playback mode (S) | +| **S** | The playback mode: every frame, or key frames only (the picture updates once per key frame interval, MJPEG once a second), the lowest decoder load. Works in multi-view and in the single view and never changes the view | | **Settings** | Edit the URL and the transport of the active slot (single-camera mode only) | | **WebUI** | Open the camera's web interface; credentials come from the RTSP URL (single-camera mode only) | | **Exit** | Close the app. The small text next to it is the app version and build hash. | +Items that are on — D, Q, S, U and the active slot — have an orange fill; the item the remote is on has a light blue frame. + ### Gestures and keys | Input | Action | @@ -80,7 +82,7 @@ A beta channel is available on [Google Play](https://play.google.com/apps/intern ### Opening a camera from another app -The app accepts plain-text shares (`ACTION_SEND`, `text/plain`). Sharing an `rtsp://` or `udp://` URL from another app, for example OpenIPC Network, puts it into the active slot. An optional `transport` extra (`tcp`, `udp`, `auto`, `multicast`) sets the slot's transport: +The app accepts plain-text shares (`ACTION_SEND`, `text/plain`). Sharing an `rtsp://`, `udp://` or `http://` URL from another app, for example OpenIPC Network, puts it into the active slot. An optional `transport` extra (`tcp`, `udp`, `auto`, `multicast`) sets the slot's transport: ```sh adb shell am start -a android.intent.action.SEND -t text/plain \ @@ -90,7 +92,7 @@ adb shell am start -a android.intent.action.SEND -t text/plain \ ### Multi-view (Quad) -Shows the configured slots in a 2×2 grid, or in a 3×3 grid once any of slots 5–8 is used. The ninth cell of the 3×3 grid, the **U** cell, plays a camera that pushes RTP over UDP to port 5600 of the device (`udp://@:5600`, see [udp:// slots](#udp-slots-majestic-outgoing)); until a stream arrives it shows every address of the device to send to. **U** in the menu opens it full-screen. Other ports or multicast groups go into a slot as a `udp://` URL. Each cell has its own connection and decoder, and the device's video decoder has a fixed budget: the AG-216 test box, for example, decodes about two 1080p30 streams at full rate. So the grid adapts: cells start on key frames only, then decode every frame in priority order (the U cell while it receives a stream, the cell whose sound plays, then slot order) while the decoder keeps up. Cells beyond that stay on key frames, with the picture updated once per key frame interval (`key frames` in the corner text, menu **D**). The limit is learnt on each device by measurement. Substreams of the cameras, where available, let more cells play at full rate. A cell over the decoder's instance limit shows `Decoder limit: N × H.265`. Some cameras accept only one RTSP client at a time. Long-press a cell to play its audio (long-press it again to mute); the selected cell shows an AUDIO badge and is remembered. Tap any cell to open the menu. +Shows the configured slots in a 2×2 grid, or in a 3×3 grid once any of slots 5–8 is used. The ninth cell of the 3×3 grid, the **U** cell, plays a camera that pushes RTP over UDP to port 5600 of the device (`udp://@:5600`, see [udp:// slots](#udp-slots-majestic-outgoing)); until a stream arrives it shows every address of the device to send to. **U** in the menu opens it full-screen. Other ports or multicast groups go into a slot as a `udp://` URL. Each cell has its own connection and decoder, and the device's video decoder has a fixed budget: the AG-216 test box, for example, decodes about two 1080p30 streams at full rate. So the grid adapts: cells start on key frames only, then decode every frame in priority order (the U cell while it receives a stream, the cell whose sound plays, then slot order) while the decoder keeps up. Cells beyond that stay on key frames, with the picture updated once per key frame interval (`key frames` in the corner text, menu **D**). The limit is learnt on each device by measurement. Substreams of the cameras, where available, let more cells play at full rate. A key-frame cell over the hardware decoder's instance limit (the AG-216 has 4 × H.265) uses a software decoder instead (`· software` in the corner); only beyond that budget too does it show `Decoder limit: N × H.265`. When even key frames are too much, the lowest-priority cell is paused (`Paused: decoder overloaded`) and tried again after a quiet period; losses in the first seconds after the grid starts, when every decoder starts at once, do not count. A cell whose UDP stream loses a piece of every key frame shows the frames with the damage concealed (`· damaged`) rather than a black picture. Some cameras accept only one RTSP client at a time. Long-press a cell to play its audio (long-press it again to mute); the selected cell shows an AUDIO badge and is remembered. Tap any cell to open the menu. ## Transports @@ -132,11 +134,15 @@ outgoing: `/api/v1/outgoing.json` on the camera shows whether the destination is live. Majestic can also send to a multicast group (`udp://239.1.2.3:5600`), so several devices can watch one camera. On Wi‑Fi the app holds a multicast lock while it is in a group. +### HTTP MJPEG slots + +A slot also takes an HTTP MJPEG URL, such as Majestic's `http://user:password@/mjpeg`: a `multipart/x-mixed-replace` stream with one JPEG per part, chunked or not. Basic credentials are sent with the request, Digest after the camera asks for it; the stream reconnects like RTSP. It plays in the single view and in multi-view; the transport shows as `HTTP`. There is no audio on this path. + ## Debug page (port 8085) While the app runs it serves its debug information over HTTP on port **8085**, for a browser or `curl` on the same network: -- `http://:8085/` — the snapshot fills the page, with each cell's camera, size, fps and losses (in red) drawn over it; the header shows the version, the device time and uptime; +- `http://:8085/` — the snapshot fills the page, with each cell's camera, size, fps and losses (in red) drawn over it; the header shows the version, the device time and uptime. **Info On / Off** in the header hides the text over the cells (and the list below them on a narrow screen), leaving only the picture; the choice is remembered in the browser. A red note in the header says when the device's video decoder stopped responding (`decoder_stuck`); - `http://:8085/snapshot.jpg` (`?labels=0`: frames only) — a picture laid out like the screen, at the screen's resolution (at most 1920×1080), made from the frames the app shows right now; a cell without video is black with its status. Taken only when asked for; the page refreshes it every 3, 5 or 10 s while the tab is visible; - `http://:8085/stats.json` — the same as JSON, for scripts; - `http://:8085/log` — the last 500 lines of the app's log. @@ -156,7 +162,8 @@ Only clients on the local network are answered (loopback, 10/8, 172.16/12, 192.1 > > - **RTSP multicast needs Android 8.0+** and a server that offers it; OpenIPC cameras do not — [#69](https://github.com/OpenIPC/decoder/issues/69) > - **G.726 audio and MP4A-LATM with in-band configuration (`cpresent=1`) are not supported.** OpenIPC Majestic does not use them, and cameras that offer G.726 can usually send G.711 or AAC instead — [#14](https://github.com/OpenIPC/decoder/issues/14) -> - **MJPEG plays in the single view only and is decoded in software**; hardware decoding and multi-view — [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **MJPEG is decoded in software on devices without a usable JPEG decoder** (the AG-216 lists one but cannot create it); hardware decoding is used where the device offers `video/jpeg` — [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **The video decoder of the AG-216 can stop responding until the device is restarted**: frames arrive, none is decoded; the app says so on the screen and on the debug page, the cause is not known yet — [#145](https://github.com/OpenIPC/decoder/issues/145) > - **Switching between multi-view and the single view can freeze the picture for seconds** on some TV boxes (seen on the AG-216) — [#76](https://github.com/OpenIPC/decoder/issues/76) > - **Credentials are sent in clear text** over plain RTSP/HTTP (Basic auth unless the camera asks for Digest); RTSPS is not supported and not planned — [#16](https://github.com/OpenIPC/decoder/issues/16) > @@ -204,8 +211,10 @@ Debug and release builds are signed with different keys. To switch from one to t | `RtspAuth`, `Sdp` | Basic and Digest authentication; parsing of the camera's session description | | `Transport`, `UdpReceiver`, `RtpReorder` | Transport negotiation; 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` | Rebuilds H.264/H.265 NAL units from RTP packets, drops units damaged by packet loss | | `JpegAssembler` | MJPEG: rebuilds whole JPEG frames from RTP/JPEG (RFC 2435) | +| `JpegCodec`, `JpegRenderer` | MJPEG on screen: the device's JPEG decoder where there is one, otherwise BitmapFactory | | `DecodePlanner` | Multi-view: which cells decode every frame and which only key frames, within the learnt decoder capacity | | `MediaCodecManager`, `DecoderRetry` | Hardware video decoder lifecycle and recovery after decoder errors | | `AudioManager`, `AudioConfig` | Audio pipeline: G.711/L16 conversion, AAC/Opus decoding, AudioTrack playback | diff --git a/README.ru.md b/README.ru.md index 5ad578a8..6921d4f6 100644 --- a/README.ru.md +++ b/README.ru.md @@ -28,9 +28,9 @@ ## Возможности -- **Видео:** воспроизведение RTSP, аппаратное декодирование H.264/H.265 через MediaCodec, MJPEG по RTSP (RTP/JPEG, в одиночном режиме), автоматическое восстановление декодера, картинка вписывается в экран любой формы +- **Видео:** воспроизведение RTSP, аппаратное декодирование H.264/H.265 через MediaCodec, MJPEG по RTSP (RTP/JPEG) и по HTTP (`multipart/x-mixed-replace`) в одиночном режиме и в мультивиде, автоматическое восстановление декодера, поток UDP с потерями показывается с замаскированными повреждениями вместо чёрного экрана, предупреждение, если видеодекодер устройства перестал отвечать, картинка вписывается в экран любой формы - **Звук:** AAC (RFC 3640 `mpeg4-generic`, ADTS, MP4A-LATM), Opus, G.711 μ-law/A-law, L16 PCM (моно и стерео); в мультивиде — звук выбранной ячейки -- **Режимы:** одна камера или мультивид (Quad) — до 8 камер и ячейка U (камера, которая сама шлёт RTP) сеткой 2×2 или 3×3; адаптивное декодирование (все кадры или только ключевые для каждой ячейки, предел определяется на устройстве); слайды (S): все камеры только на ключевых кадрах +- **Режимы:** одна камера или мультивид (Quad) — до 8 камер и ячейка U (камера, которая сама шлёт RTP) сеткой 2×2 или 3×3; адаптивное декодирование (все кадры или только ключевые для каждой ячейки, предел определяется на устройстве); S: только ключевые кадры — и в мультивиде, и в одиночном режиме - **Управление:** масштаб двумя пальцами, перемещение, сброс двойным касанием, пульт Android TV (Leanback) - **Сеть:** RTSP поверх TCP или UDP (Auto, режим по умолчанию, переходит на TCP, если UDP отклонён, видео не приходит или ключевые кадры теряются), фактический транспорт показывается при подключении, RTSP multicast, приём RTP, который камера сама шлёт по UDP (unicast или multicast), авторизация Basic и Digest, RTSP keepalive, автоматическое переподключение с растущей задержкой, индикатор качества связи - **WebUI:** встроенный браузер для веб-интерфейса камеры @@ -62,12 +62,14 @@ | **1**–**8** | Переключиться на слот камеры. Цвет номера активного слота показывает качество связи: зелёный — джиттер меньше 100 мс, жёлтый — 100–300 мс, красный — больше 300 мс. Серый номер — пустой слот. | | **U** | Ячейка U на весь экран: камера, которая сама шлёт RTP по UDP на порт 5600 | | **D** | Текст в углу каждой ячейки мультивида (разрешение, кодек, декодировано/получено кадр/с, ключевые кадры, статус) — или ни в одной | -| **Q** | Включить или выключить мультивид (Quad) | -| **S** | Слайды: мультивид, где все камеры только на ключевых кадрах (картинка обновляется раз в интервал ключевых кадров), минимальная нагрузка на декодер; S или Q — обратно к адаптивной сетке | +| **Q** | Вид: мультивид (Quad) или одна камера. Q не меняет режим воспроизведения (S) | +| **S** | Режим воспроизведения: все кадры или только ключевые (картинка обновляется раз в интервал ключевых кадров, MJPEG — раз в секунду), минимальная нагрузка на декодер. Работает и в мультивиде, и в одиночном режиме, вид не меняет | | **Settings** | Изменить адрес и транспорт активного слота (только в режиме одной камеры) | | **WebUI** | Открыть веб-интерфейс камеры; логин и пароль берутся из RTSP-адреса (только в режиме одной камеры) | | **Exit** | Закрыть приложение. Мелкий текст рядом — версия приложения и хеш сборки. | +Включённые пункты — D, Q, S, U и активный слот — залиты оранжевым; пункт, на котором стоит пульт, обведён голубой рамкой. + ### Жесты и кнопки | Ввод | Действие | @@ -80,7 +82,7 @@ ### Открытие камеры из другого приложения -Приложение принимает текст через «Поделиться» (`ACTION_SEND`, `text/plain`). Если поделиться ссылкой `rtsp://` или `udp://` из другого приложения, например из OpenIPC Network, она запишется в активный слот. Необязательный параметр `transport` (`tcp`, `udp`, `auto`, `multicast`) задаёт транспорт слота: +Приложение принимает текст через «Поделиться» (`ACTION_SEND`, `text/plain`). Если поделиться ссылкой `rtsp://`, `udp://` или `http://` из другого приложения, например из OpenIPC Network, она запишется в активный слот. Необязательный параметр `transport` (`tcp`, `udp`, `auto`, `multicast`) задаёт транспорт слота: ```sh adb shell am start -a android.intent.action.SEND -t text/plain \ @@ -90,7 +92,7 @@ adb shell am start -a android.intent.action.SEND -t text/plain \ ### Мультивид (Quad) -Показывает настроенные слоты сеткой 2×2, а если занят хотя бы один из слотов 5–8 — сеткой 3×3. Девятая ячейка сетки 3×3, ячейка **U**, показывает камеру, которая сама отправляет RTP по UDP на порт 5600 устройства (`udp://@:5600`, см. [слоты udp://](#слоты-udp-majestic-outgoing)); пока поток не пришёл, в ней перечислены все адреса устройства, куда его слать. Кнопка **U** в меню открывает её на весь экран. Другие порты и multicast-группы указываются в слоте адресом `udp://`. У каждой ячейки своё подключение и свой декодер, а у видеодекодера устройства постоянный бюджет: тестовая приставка AG-216, например, декодирует в полном темпе примерно два потока 1080p30. Поэтому сетка подстраивается: ячейки стартуют на ключевых кадрах, затем по приоритету (ячейка U, пока в неё идёт поток, ячейка со звуком, потом по номеру слота) переходят на все кадры, пока декодер успевает. Остальные остаются на ключевых кадрах — картинка обновляется раз в интервал между ключевыми кадрами (в тексте в углу — `key frames`, меню **D**). Предел определяется замером на каждом устройстве. Дополнительные потоки камер (substream), если они есть, позволяют показать в полном темпе больше ячеек. Ячейка сверх лимита экземпляров декодера показывает `Decoder limit: N × H.265`. Некоторые камеры допускают только одно RTSP-подключение одновременно. Долгое нажатие на ячейку включает её звук (повторное — выключает); у выбранной ячейки виден значок AUDIO, выбор запоминается. Коснитесь любой ячейки, чтобы открыть меню. +Показывает настроенные слоты сеткой 2×2, а если занят хотя бы один из слотов 5–8 — сеткой 3×3. Девятая ячейка сетки 3×3, ячейка **U**, показывает камеру, которая сама отправляет RTP по UDP на порт 5600 устройства (`udp://@:5600`, см. [слоты udp://](#слоты-udp-majestic-outgoing)); пока поток не пришёл, в ней перечислены все адреса устройства, куда его слать. Кнопка **U** в меню открывает её на весь экран. Другие порты и multicast-группы указываются в слоте адресом `udp://`. У каждой ячейки своё подключение и свой декодер, а у видеодекодера устройства постоянный бюджет: тестовая приставка AG-216, например, декодирует в полном темпе примерно два потока 1080p30. Поэтому сетка подстраивается: ячейки стартуют на ключевых кадрах, затем по приоритету (ячейка U, пока в неё идёт поток, ячейка со звуком, потом по номеру слота) переходят на все кадры, пока декодер успевает. Остальные остаются на ключевых кадрах — картинка обновляется раз в интервал между ключевыми кадрами (в тексте в углу — `key frames`, меню **D**). Предел определяется замером на каждом устройстве. Дополнительные потоки камер (substream), если они есть, позволяют показать в полном темпе больше ячеек. Ячейка на ключевых кадрах сверх лимита экземпляров аппаратного декодера (у AG-216 — 4 × H.265) работает на программном декодере (`· software` в углу); только сверх и этого бюджета она показывает `Decoder limit: N × H.265`. Если не справляются даже ключевые кадры, ячейка с наименьшим приоритетом ставится на паузу (`Paused: decoder overloaded`) и после спокойного периода пробуется снова; потери в первые секунды после запуска сетки, когда все декодеры стартуют разом, не учитываются. Ячейка, у которой поток UDP теряет кусок каждого ключевого кадра, показывает кадры с замаскированными повреждениями (`· damaged`), а не чёрный экран. Некоторые камеры допускают только одно RTSP-подключение одновременно. Долгое нажатие на ячейку включает её звук (повторное — выключает); у выбранной ячейки виден значок AUDIO, выбор запоминается. Коснитесь любой ячейки, чтобы открыть меню. ## Транспорты @@ -132,11 +134,15 @@ outgoing: `/api/v1/outgoing.json` на камере показывает, идёт ли отправка. Majestic умеет слать и в multicast-группу (`udp://239.1.2.3:5600`), тогда одну камеру могут смотреть несколько устройств. В Wi‑Fi приложение держит multicast lock, пока состоит в группе. +### Слоты HTTP MJPEG + +Слот принимает и адрес HTTP MJPEG, например у Majestic `http://user:password@<камера>/mjpeg`: поток `multipart/x-mixed-replace`, по одному JPEG в каждой части, с chunked-кодированием или без. Учётные данные Basic отправляются сразу, Digest — после запроса камеры; поток переподключается так же, как RTSP. Работает в одиночном режиме и в мультивиде, транспорт показывается как `HTTP`. Звука в этом режиме нет. + ## Отладочная страница (порт 8085) Пока приложение работает, оно отдаёт отладочную информацию по HTTP на порту **8085** — для браузера или `curl` из той же сети: -- `http://<приставка>:8085/` — снимок на всю страницу, поверх каждой ячейки камера, размер, fps и потери (красным); в шапке версия, время приставки и аптайм; +- `http://<приставка>:8085/` — снимок на всю страницу, поверх каждой ячейки камера, размер, fps и потери (красным); в шапке версия, время приставки и аптайм. Переключатель **Info On / Off** в шапке убирает текст поверх ячеек (и список под ними на узком экране), остаётся только картинка; выбор запоминается в браузере. Красная пометка в шапке сообщает, что видеодекодер устройства перестал отвечать (`decoder_stuck`); - `http://<приставка>:8085/snapshot.jpg` (`?labels=0` — только кадры) — картинка с раскладкой экрана в разрешении экрана (не больше 1920×1080) из кадров, которые приложение показывает сейчас; ячейка без видео — чёрная с её статусом. Снимается только по запросу; страница обновляет её раз в 3, 5 или 10 с, пока вкладка видна; - `http://<приставка>:8085/stats.json` — то же в JSON, для скриптов; - `http://<приставка>:8085/log` — последние 500 строк лога приложения. @@ -156,7 +162,8 @@ outgoing: > > - **RTSP multicast требует Android 8.0+** и сервера, который его поддерживает; камеры OpenIPC его не поддерживают — [#69](https://github.com/OpenIPC/decoder/issues/69) > - **Не поддерживаются звук G.726 и MP4A-LATM с конфигурацией внутри потока (`cpresent=1`).** OpenIPC Majestic их не использует, а камеры с G.726 обычно умеют отдавать G.711 или AAC — [#14](https://github.com/OpenIPC/decoder/issues/14) -> - **MJPEG воспроизводится только в одиночном режиме и декодируется программно**; аппаратное декодирование и мультивид — [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **MJPEG декодируется программно на устройствах без рабочего JPEG-декодера** (AG-216 его объявляет, но создать не может); аппаратное декодирование используется, если устройство предоставляет `video/jpeg` — [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **Видеодекодер AG-216 может перестать отвечать до перезагрузки устройства**: кадры приходят, но ни один не декодируется; приложение сообщает об этом на экране и на отладочной странице, причина пока неизвестна — [#145](https://github.com/OpenIPC/decoder/issues/145) > - **Переключение между мультивидом и одиночным режимом может на несколько секунд заморозить картинку** на некоторых ТВ-приставках (замечено на AG-216) — [#76](https://github.com/OpenIPC/decoder/issues/76) > - **Логин и пароль передаются в открытом виде** по RTSP/HTTP без шифрования (Basic, если камера не запросила Digest); RTSPS не поддерживается и не планируется — [#16](https://github.com/OpenIPC/decoder/issues/16) > @@ -204,8 +211,10 @@ export KEY_PASS=your_key_password | `RtspAuth`, `Sdp` | Авторизация Basic и Digest; разбор описания сессии камеры | | `Transport`, `UdpReceiver`, `RtpReorder` | Согласование транспорта; приём по UDP и multicast с фильтром отправителя; восстановление порядка пакетов | | `RtpListener` | Слоты `udp://`: RTP, который камера отправляет сама | +| `HttpMjpeg` | Слоты `http://`: MJPEG по HTTP (`multipart/x-mixed-replace`, chunked или нет) | | `NalAssembler` | Сборка NAL-блоков H.264/H.265 из RTP-пакетов, отбрасывание блоков, повреждённых потерями | | `JpegAssembler` | MJPEG: сборка целых JPEG-кадров из RTP/JPEG (RFC 2435) | +| `JpegCodec`, `JpegRenderer` | MJPEG на экране: JPEG-декодер устройства, если он есть, иначе BitmapFactory | | `DecodePlanner` | Мультивид: какие ячейки декодируют все кадры, а какие только ключевые, в пределах выученной ёмкости декодера | | `MediaCodecManager`, `DecoderRetry` | Жизненный цикл аппаратного видеодекодера и восстановление после ошибок | | `AudioManager`, `AudioConfig` | Звук: преобразование G.711/L16, декодирование AAC/Opus, вывод через AudioTrack | diff --git a/README.zh-CN.md b/README.zh-CN.md index 258f346f..63903723 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -28,9 +28,9 @@ ## 功能 -- **视频:** RTSP 播放,通过 MediaCodec 进行 H.264/H.265 硬件解码,支持 RTSP 上的 MJPEG(RTP/JPEG,仅单画面),解码器出错后自动恢复,画面适配任意屏幕比例 +- **视频:** RTSP 播放,通过 MediaCodec 进行 H.264/H.265 硬件解码,支持 RTSP 上的 MJPEG(RTP/JPEG)和 HTTP 上的 MJPEG(`multipart/x-mixed-replace`),单画面和多画面均可,解码器出错后自动恢复,有丢包的 UDP 流以掩盖损坏的画面显示而不是黑屏,设备视频解码器停止响应时给出提示,画面适配任意屏幕比例 - **音频:** AAC(RFC 3640 `mpeg4-generic`、ADTS、MP4A-LATM)、Opus、G.711 μ-law/A-law、L16 PCM(单声道/立体声);多画面模式下播放所选画面的声音 -- **显示:** 单路画面,或多画面模式(Quad),以 2×2 或 3×3 网格最多显示 8 路摄像机外加 U 画面(主动推送 RTP 的摄像机);自适应解码(每个画面全帧率或仅关键帧,上限按设备实测);幻灯片(S):所有摄像机仅解码关键帧 +- **显示:** 单路画面,或多画面模式(Quad),以 2×2 或 3×3 网格最多显示 8 路摄像机外加 U 画面(主动推送 RTP 的摄像机);自适应解码(每个画面全帧率或仅关键帧,上限按设备实测);S:仅解码关键帧,多画面和单画面均适用 - **操作:** 双指缩放、拖动平移、双击复位,支持 Android TV 遥控器(Leanback 启动器) - **网络:** RTSP over TCP 或 UDP(默认 Auto:UDP 被拒绝、无视频或关键帧持续丢失时回退到 TCP),连接时显示实际使用的传输方式、RTSP 组播、接收摄像机通过 UDP 主动推送的 RTP(单播或组播),支持 Basic 和 Digest 认证、RTSP 保活,断线后自动重连(指数退避),连接质量指示 - **WebUI:** 内置浏览器,可打开摄像机的网页管理界面 @@ -62,12 +62,14 @@ | **1**–**8** | 切换摄像机槽位。当前槽位编号的颜色表示连接质量:绿色为抖动低于 100 毫秒,黄色为 100–300 毫秒,红色为高于 300 毫秒。灰色编号表示该槽位未配置。 | | **U** | 全屏显示 U 画面:通过 UDP 向 5600 端口推送 RTP 的摄像机 | | **D** | 在每个多画面格子的角落显示文字(分辨率、编码、已解码/已接收帧率、关键帧、状态),或全部不显示 | -| **Q** | 开启或关闭多画面模式(Quad) | -| **S** | 幻灯片:所有摄像机只解码关键帧的多画面模式(画面按关键帧间隔更新),解码器负载最低;再按 S 或 Q 返回自适应网格 | +| **Q** | 视图:多画面(Quad)或单路画面。Q 不改变播放模式(S) | +| **S** | 播放模式:全部帧或仅关键帧(画面按关键帧间隔更新,MJPEG 每秒一帧),解码器负载最低。多画面和单画面均适用,不切换视图 | | **Settings** | 编辑当前槽位的地址和传输方式(仅单路模式) | | **WebUI** | 打开摄像机网页界面,用户名和密码取自 RTSP 地址(仅单路模式) | | **Exit** | 退出应用。旁边的小字是应用版本号和构建哈希。 | +已开启的项目(D、Q、S、U 和当前槽位)以橙色填充;遥控器焦点所在的项目带浅蓝色边框。 + ### 手势与按键 | 输入 | 功能 | @@ -80,7 +82,7 @@ ### 从其他应用打开摄像机 -应用可接收纯文本分享(`ACTION_SEND`,`text/plain`)。从其他应用(例如 OpenIPC Network)分享 `rtsp://` 或 `udp://` 地址后,该地址会写入当前槽位。可选参数 `transport`(`tcp`、`udp`、`auto`、`multicast`)用于设置该槽位的传输方式: +应用可接收纯文本分享(`ACTION_SEND`,`text/plain`)。从其他应用(例如 OpenIPC Network)分享 `rtsp://`、`udp://` 或 `http://` 地址后,该地址会写入当前槽位。可选参数 `transport`(`tcp`、`udp`、`auto`、`multicast`)用于设置该槽位的传输方式: ```sh adb shell am start -a android.intent.action.SEND -t text/plain \ @@ -90,7 +92,7 @@ adb shell am start -a android.intent.action.SEND -t text/plain \ ### 多画面模式(Quad) -以 2×2 网格显示已配置的槽位;只要使用了第 5–8 个槽位中的任意一个,就改为 3×3 网格。3×3 网格的第九格即 **U** 画面,播放通过 UDP 向本设备 5600 端口主动推送 RTP 的摄像机(`udp://@:5600`,参见 [udp:// 槽位](#udp-槽位-majestic-outgoing));收到流之前,其中列出本设备可用于推流的所有地址。菜单中的 **U** 可将其全屏显示。其他端口或组播组请在槽位中填写 `udp://` 地址。每个画面都有独立的连接和解码器,而设备视频解码器的处理能力是固定的:例如测试用的 AG-216 盒子以全帧率大约只能解码两路 1080p30。因此网格会自适应:各画面先只解码关键帧,然后按优先级(正在接收流的 U 画面,其次是正在播放声音的画面,再按槽位顺序)在解码器跟得上时切换为全帧率。其余画面保持只解码关键帧,画面按关键帧间隔更新(角落文字显示 `key frames`,菜单 **D**)。上限在每台设备上通过实测确定。如摄像机提供子码流,可让更多画面以全帧率播放。超出解码器实例上限的画面显示 `Decoder limit: N × H.265`。部分摄像机同一时间只允许一个 RTSP 客户端。长按某个画面即可播放其声音(再次长按则静音);被选中的画面显示 AUDIO 标记,选择会被记住。点击任意画面即可打开菜单。 +以 2×2 网格显示已配置的槽位;只要使用了第 5–8 个槽位中的任意一个,就改为 3×3 网格。3×3 网格的第九格即 **U** 画面,播放通过 UDP 向本设备 5600 端口主动推送 RTP 的摄像机(`udp://@:5600`,参见 [udp:// 槽位](#udp-槽位-majestic-outgoing));收到流之前,其中列出本设备可用于推流的所有地址。菜单中的 **U** 可将其全屏显示。其他端口或组播组请在槽位中填写 `udp://` 地址。每个画面都有独立的连接和解码器,而设备视频解码器的处理能力是固定的:例如测试用的 AG-216 盒子以全帧率大约只能解码两路 1080p30。因此网格会自适应:各画面先只解码关键帧,然后按优先级(正在接收流的 U 画面,其次是正在播放声音的画面,再按槽位顺序)在解码器跟得上时切换为全帧率。其余画面保持只解码关键帧,画面按关键帧间隔更新(角落文字显示 `key frames`,菜单 **D**)。上限在每台设备上通过实测确定。如摄像机提供子码流,可让更多画面以全帧率播放。超出硬件解码器实例上限(AG-216 为 4 × H.265)的仅关键帧画面改用软件解码器(角落显示 `· software`);只有连这一预算也超出时才显示 `Decoder limit: N × H.265`。如果连关键帧都处理不过来,优先级最低的画面会被暂停(`Paused: decoder overloaded`),平稳一段时间后再重试;网格启动后最初几秒(所有解码器同时启动)的丢帧不计入。若某画面的 UDP 流每个关键帧都丢失一部分,会显示掩盖了损坏的画面(`· damaged`),而不是黑屏。部分摄像机同一时间只允许一个 RTSP 客户端。长按某个画面即可播放其声音(再次长按则静音);被选中的画面显示 AUDIO 标记,选择会被记住。点击任意画面即可打开菜单。 ## 传输方式 @@ -132,11 +134,15 @@ outgoing: 摄像机上的 `/api/v1/outgoing.json` 可查看推流是否正常。Majestic 也可以推送到组播组(`udp://239.1.2.3:5600`),这样多台设备可以同时观看同一台摄像机。在 Wi‑Fi 下,应用加入组播组期间会持有 multicast lock。 +### HTTP MJPEG 槽位 + +槽位也可以填写 HTTP MJPEG 地址,例如 Majestic 的 `http://user:password@<摄像机>/mjpeg`:`multipart/x-mixed-replace` 流,每个部分是一帧 JPEG,可以是分块传输(chunked)。Basic 凭据随请求发送,摄像机要求时使用 Digest;断线后与 RTSP 一样自动重连。单画面和多画面均可播放,传输方式显示为 `HTTP`。此方式没有音频。 + ## 调试页面(端口 8085) 应用运行时会在 **8085** 端口通过 HTTP 提供调试信息,可在同一网络中用浏览器或 `curl` 访问: -- `http://<设备>:8085/` — 快照铺满页面,每个画面上叠加摄像机、分辨率、帧率和丢帧(红色);页头显示版本、设备时间和运行时长; +- `http://<设备>:8085/` — 快照铺满页面,每个画面上叠加摄像机、分辨率、帧率和丢帧(红色);页头显示版本、设备时间和运行时长。页头的 **Info On / Off** 开关可隐藏画面上的文字(以及窄屏时下方的列表),只保留画面;选择会保存在浏览器中。设备视频解码器停止响应时,页头会出现红色提示(`decoder_stuck`); - `http://<设备>:8085/snapshot.jpg`(`?labels=0`:仅画面)— 按屏幕布局拼成的图片,分辨率与屏幕相同(最大 1920×1080),取自应用当前显示的画面;没有视频的画面为黑色并显示其状态。仅在请求时截取;页面在标签页可见时每 3、5 或 10 秒刷新一次; - `http://<设备>:8085/stats.json` — 相同内容的 JSON,便于脚本使用; - `http://<设备>:8085/log` — 应用日志的最后 500 行。 @@ -156,7 +162,8 @@ outgoing: > > - **RTSP 组播需要 Android 8.0+**,且服务器须支持;OpenIPC 摄像机不支持 — [#69](https://github.com/OpenIPC/decoder/issues/69) > - **不支持 G.726 音频,也不支持流内配置的 MP4A-LATM(`cpresent=1`)。** OpenIPC Majestic 不使用这些格式,支持 G.726 的摄像机通常也能输出 G.711 或 AAC — [#14](https://github.com/OpenIPC/decoder/issues/14) -> - **MJPEG 仅支持单画面且为软件解码**;硬件解码和多画面见 [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **在没有可用 JPEG 解码器的设备上 MJPEG 为软件解码**(AG-216 声明了 JPEG 解码器但无法创建);设备提供 `video/jpeg` 时使用硬件解码 — [#132](https://github.com/OpenIPC/decoder/issues/132) +> - **AG-216 的视频解码器可能停止响应,直到重启设备**:帧照常到达但无法解码;应用会在屏幕和调试页面上提示,原因尚不清楚 — [#145](https://github.com/OpenIPC/decoder/issues/145) > - **在多画面与单画面之间切换时,部分电视盒子(如 AG-216)的画面可能会冻结数秒** — [#76](https://github.com/OpenIPC/decoder/issues/76) > - **用户名和密码以明文传输**(未加密的 RTSP/HTTP;摄像机未要求 Digest 时发送 Basic),不支持 RTSPS,也不计划支持 — [#16](https://github.com/OpenIPC/decoder/issues/16) > @@ -204,8 +211,10 @@ export KEY_PASS=your_key_password | `RtspAuth`、`Sdp` | Basic 与 Digest 认证;解析摄像机的会话描述 | | `Transport`、`UdpReceiver`、`RtpReorder` | 传输方式协商;带发送端过滤的 UDP 与组播接收;数据包重排序 | | `RtpListener` | `udp://` 槽位:接收摄像机主动推送的 RTP | +| `HttpMjpeg` | `http://` 槽位:HTTP 上的 MJPEG(`multipart/x-mixed-replace`) | | `NalAssembler` | 将 RTP 包重组为 H.264/H.265 NAL 单元,丢弃因丢包而损坏的单元 | | `JpegAssembler` | MJPEG:由 RTP/JPEG(RFC 2435)重组完整的 JPEG 帧 | +| `JpegCodec`、`JpegRenderer` | MJPEG 显示:有设备 JPEG 解码器时使用它,否则使用 BitmapFactory | | `DecodePlanner` | 多画面:在解码器实测能力内决定哪些画面全帧率、哪些仅解码关键帧 | | `MediaCodecManager`、`DecoderRetry` | 硬件视频解码器的生命周期管理与出错后的恢复 | | `AudioManager`、`AudioConfig` | 音频处理:G.711/L16 转换、AAC/Opus 解码、通过 AudioTrack 播放 |