Files
XC_VM/tools/test-install/STREAMTEST.md
T
Divarion_D 175198a902 refactor(stream-check): merge checker and grapher into one stream_check.py
Consolidate the two stream-check tools into a single master script with
three subcommands:

- check <url>          verify one stream (or --live dashboard)
- playlist <path|url>  batch an .m3u list -> aggregate JSON (+ per-stream files)
- graph <inputs...>    render the JSON as SVG charts

Shared helpers (HTTP, slugify, m3u/JSON handling) are now defined once.
The old --playlist flag becomes the `playlist` subcommand and the bare-URL
form becomes `check <url>`; the streamtest harness is updated accordingly.

Also fix the per-stream SVG rendering black in viewers that do not support
8-digit #rrggbbaa hex: the bitrate area fill and not-PLAYING bands now use
6-digit hex plus a separate fill-opacity attribute.

Docs (English + tools READMEs + STREAMTEST) updated to the new invocation.
Removes stream_queue_check.py and stream_graph.py.
2026-09-06 11:11:10 +03:00

307 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Стрим-тест панели (e2e)
Сквозная проверка стриминга: локальный **генератор** отдаёт видеопоток →
**панель** его рестримит → `stream_check.py` проверяет **выход панели**
(каждый канал ~2 минуты) и пишет JSON-лог по каждому потоку.
Панель настраивается **один раз вручную** через админку, конфигурация
сохраняется в **дамп БД**, а прогон теста восстанавливает дамп и запускает
checker — повторяемо. Прямых SQL-запросов к БД нет: тест только снимает и
восстанавливает бэкап (`mysqldump` / импорт).
```text
генератор (127.0.0.1:8088) ──► панель (рестрим/LLOD) ──► get.php m3u ──► stream_check.py
stream_server.py xc_fanout / monitor выход панели JSON-график
```
## Команды `test_release.sh`
| Команда | Что делает |
|---|---|
| `streamtest-gen` | поднять генератор в контейнере (для ручной настройки панели) + напечатать URL источников и админки |
| `streamtest-gen-stop` | остановить генератор |
| `streamtest-backup` | снять дамп настроенной БД в `fixtures/streamtest-db.sql.gz` |
| `streamtest` | восстановить дамп → прогреть кэш → генератор → забрать m3u линии → прогнать checker (e2e) |
Плюс отдельный инструмент `tools/stream-check/stream_check.py` с подкомандами
`check` (checker), `playlist` (пакетный прогон) и `graph` (SVG-графики).
## Требования
- Контейнер установлен: `./tools/test-install/test_release.sh install`
- `tools/test-stream-generator/sample.mp4` — короткий H.264/AAC .mp4 (в git его нет)
- Внутри контейнера уже есть `python3`, встроенный `ffmpeg`, `mariadb`
## 0. Порты и доступ
- Админка: `http://localhost:8880/<код-доступа>/` — **код обязателен**, голый
`http://localhost:8880/` отдаёт **404**. Код — в конце `./test_release.sh logs`
(строка вида `http://…/PjmpGpxM/`); `streamtest-gen` тоже печатает готовую ссылку.
- Генератор: `http://127.0.0.1:8088/` (внутри контейнера), проброшен на хост как
`http://localhost:8088/`.
## 1. Завершить миграции БД
Свежая установка может показывать «Database Incomplete». Выполнить **от root**
в контейнере:
```bash
docker exec xcvm-test-install /home/xc_vm/bin/php/bin/php /home/xc_vm/console.php status
```
> Известный баг: миграция `010_add_shared_mount_prefixes.sql` не идемпотентна —
> колонка уже есть в базовой схеме `database.sql`, поэтому на свежей установке
> она падает («Duplicate column») и статус остаётся «Incomplete». Если это
> мешает — эту миграцию нужно поправить (guard `IF NOT EXISTS` / убрать из
> базовой схемы) в `src/migrations/010_add_shared_mount_prefixes.sql`.
## 2. Запустить генератор (для ручной настройки)
```bash
./tools/test-install/test_release.sh streamtest-gen
```
Команда поднимает генератор в контейнере и печатает URL источников и готовую
ссылку на админку (с кодом доступа):
- MPEG-TS (LLOD): `http://127.0.0.1:8088/stream.ts`
- HLS: `http://127.0.0.1:8088/stream.m3u8`
Оставьте его запущенным, пока настраиваете панель (остановить —
`./test_release.sh streamtest-gen-stop`).
## 3. Настроить панель вручную (админка)
Цель — чтобы **оба потока реально пошли ONLINE** (панель должна успешно
рестримить источник от генератора). Порядок:
1. **Категория** (Streams → Categories): создать live-категорию, напр. `XC_VM Test Live`.
2. **Поток 1 — MPEG-TS** (Streams → Add):
- Source: `http://127.0.0.1:8088/stream.ts`
- Категория: созданная выше
- **Сервер**: назначить **Main Server** как *источник* (узел «source» в дереве
серверов — поток читает свой `stream_source` напрямую).
- Режим запуска: on-demand (LLOD) либо always-on — на ваш выбор; для
on-demand поток стартует при первом запросе checker'а.
3. **Поток 2 — HLS**: то же, source `http://127.0.0.1:8088/stream.m3u8`.
4. **Проверить, что потоки ONLINE**: в списке стримов у обоих должен появиться
битрейт/статус «онлайн» (панель подключилась к генератору и рестримит). Это
ключевая проверка — без неё выход панели будет отдавать заглушку «not on air».
5. **Bouquet** (Bouquets → Add): создать бэкет, добавить оба потока.
6. **Линия** (Lines → Add):
- Username / Password: **`xcvmtest` / `xcvmtest`** (значения по умолчанию для
теста; можно другие — тогда задайте `STREAMTEST_USER` / `STREAMTEST_PASS`).
- Bouquet: выбрать созданный бэкет.
- Allowed Output Formats: включить как минимум **MPEG-TS**.
- Exp date — в будущем, Max connections ≥ 1, линия активна.
7. **Проверка выхода панели** (в браузере или curl):
```
http://localhost:8880/get.php?username=xcvmtest&password=xcvmtest&type=m3u_plus&output=ts
```
Должен вернуться m3u со ссылками на выход панели (`/play/<token>/ts`), и они
должны **проигрываться** (в плеере/ffprobe), а не отдавать заглушку.
## 4. Снять бэкап настроенной БД
Когда потоки идут ONLINE и выход панели проигрывается:
```bash
./tools/test-install/test_release.sh streamtest-backup
```
Дамп сохранится в `tools/test-install/fixtures/streamtest-db.sql.gz` (в git не
коммитится — он привязан к текущей установке: креды, IP сервера, коды доступа).
## 5. Прогонять тест (повторяемо)
```bash
./tools/test-install/test_release.sh streamtest
```
Что делает: восстанавливает дамп БД → пересобирает кэши (`cron:cache`,
`cron:cache_engine`) → поднимает генератор → забирает m3u линии с выхода панели →
запускает `stream_check.py playlist` (по умолчанию 120 c на канал).
Результат:
- `tools/test-install/out/streamtest-<ts>.json` — сводный отчёт (все каналы + summary);
- `tools/test-install/out/streamtest-<ts>/` — **отдельный JSON-лог на каждый канал**
(`<NN>-<имя>.json`), пишется по мере прохождения каждого потока;
- краткий pass/fail в консоль.
Код возврата `0` — все каналы healthy, `2` — есть проблемные.
## 6. Итерации
Если с первого раза потоки не проходят:
1. `./test_release.sh streamtest` — восстановит **известное** состояние БД из
бэкапа (сбрасывает любые накопившиеся изменения) и прогонит checker.
2. По JSON-логу видно, что не так (`healthy=false`, `errors`, счётчики очереди,
`samples` — тайм-серия kbit/s и буфера).
3. Поправьте настройку в админке (или запустите генератор
`streamtest-gen` и донастройте), убедитесь, что потоки снова ONLINE.
4. Снимите новый бэкап `streamtest-backup` (перезапишет фикстуру).
5. Повторяйте `streamtest`, пока не будет зелёным.
## Переменные окружения
| Переменная | По умолчанию | Назначение |
|---|---|---|
| `STREAMTEST_USER` / `STREAMTEST_PASS` | `xcvmtest` | креды тест-линии (должны совпадать с настроенной) |
| `STREAMTEST_DURATION` | `120` | секунд на каждый канал |
| `STREAMTEST_TOLERANCE` | `0` | сколько транзиентных разрывов очереди (CC/sync) допускать; у on-demand рестрима на старте обычно 1–2 CC-разрыва — поставьте `2`–`5`, чтобы их не считать за провал |
| `STREAMTEST_STALL_TIMEOUT` | `15` | секунд без новых данных = сталл — **только для непрерывного TS**. HLS этим не валится: длина HLS-сегментов часто намеренно рваная (рандомизация против DPI провайдера), поэтому HLS судится по реальному опустошению буфера (`rebuffers`), а не по паузе доставки |
| `STREAMTEST_WARMUP` | `8` | пауза после старта генератора до забора m3u |
| `STREAMTEST_OUT_DIR` | `/tmp/xcvm-streamlogs` | каталог per-stream логов в контейнере (хост копирует их в `out/streamtest-<ts>/`) |
| `XCVM_STREAM_PORT` | `8088` | порт генератора (хост→контейнер) |
Пример: быстрый прогон по 20 с на канал —
`STREAMTEST_DURATION=20 ./test_release.sh streamtest`.
## Формат JSON-отчёта
```json
{
"generated_at": "2026-…Z",
"duration_per_stream": 120,
"streams": [
{
"name": "XC_VM Test TS", "url": "http://…/play/…/ts", "mode": "ts",
"healthy": true,
"details": { "kbit_s": 3372, "cc_errors": 0, "sync_errors": 0,
"hls_gaps": 0, "hls_disc": 0, "received_s": 120.0,
"rebuffers": 0, "max_data_gap_s": 1.2, "stalled": false },
"samples": [ { "t": 0.0, "kbit_s": 0, "buffer_s": 0.0, "received_s": 0.0,
"state": "PREBUFFER", "cc_errors": 0, "sync_errors": 0,
"gaps": 0, "disc": 0 }, "… по одному в секунду …" ],
"errors": []
}
],
"summary": { "total": 2, "healthy": 2, "failed": 0 }
}
```
`samples` — это «график» живого дашборда (`stream_check.py check --live`),
сериализованный по секундам: пропускная способность, буфер, накопленные секунды
и счётчики разрывов очереди (CC/sync для TS, gaps/disc для HLS).
**Правило `healthy`:** получены данные, `queue_breaks ≤ tolerance`, нет ошибок и —
по режиму — **TS**: не было сталла (пауза доставки > `stall-timeout`); **HLS**: не
было ребуферов (буфер не опустошался). Длина HLS-сегментов часто намеренно рваная
(анти-DPI), поэтому одна лишь длинная пауза доставки HLS не валит — важно, реально
ли опустел буфер.
## Графики (SVG) для сравнения
`stream_check.py graph` рисует из JSON статические **SVG-графики** (чистый
stdlib, без зависимостей) — их удобно сравнивать как картинки и вставлять в
PR/доки.
На **каждый поток** — один SVG: пропускная способность (kbit/s) во времени
с заливкой, кривая буфера на второй оси, амбровые полосы там, где виртуальный
плеер не в состоянии PLAYING, красные штрихи разрывов очереди
(CC/sync/gaps/disc), а в шапке — имя, `mode`, бейдж **OK/FAIL** и строка цифр
(`avg kbit/s · recv · cc/sync/gaps/disc · stall`). Поток без сэмплов (упавший на
открытии) тоже даёт картинку — с вердиктом и текстом ошибки.
### Вход
Позиционные аргументы — **любое сочетание файлов и каталогов** (`nargs="+"`):
- **вся папка** — сканируется по `*.json` (не рекурсивно);
- **отдельные файлы** — по одному или списком/glob (`logs/0*.json`);
- **смесь** папок и файлов в одной команде;
Один и тот же файл, попавший и через папку, и как отдельный аргумент, читается
**один раз** (дедуп по `realpath`). Каждый JSON — либо per-stream запись (есть
`samples`), либо сводный отчёт (`"streams": [...]`, разворачивается в потоки).
```bash
# вся папка per-stream логов + сводный comparison.svg
python3 tools/stream-check/stream_check.py graph tools/test-install/out/streamtest-<ts>/ --combined --out-dir graphs/
# сводный отчёт одного прогона
python3 tools/stream-check/stream_check.py graph tools/test-install/out/streamtest-<ts>.json
# отдельные файлы / glob
python3 tools/stream-check/stream_check.py graph logs/01-MPEG-TS.json logs/02-*.json
# смесь: папка + отдельный файл (без дублей)
python3 tools/stream-check/stream_check.py graph logs/ other-run/07-HLS.json --combined
```
### Опции
| Опция | По умолчанию | Назначение |
|---|---|---|
| `--out-dir DIR` | `graphs` | каталог для SVG |
| `--combined` | выкл. | доп. `comparison.svg` — наложение битрейта всех потоков с легендой и вердиктами |
| `--width PX` | `920` | ширина графика |
| `--height PX` | `460` | высота графика |
Результат — по `graphs/<NN>-<имя>.svg` на поток (+ `comparison.svg` при
`--combined`). SVG открывается в браузере/IDE и при необходимости конвертируется
в PNG (`rsvg-convert`, `inkscape` и т.п.).
## Диагностика
- **Пустой m3u / панель отдаёт 404 на `get.php`** — в восстановленном бэкапе нет
линии с такими кредами (частый случай: линия названа не `xcvmtest`). Передайте
реальное имя: `STREAMTEST_USER=<line> STREAMTEST_PASS=<pass> ./test_release.sh streamtest`.
Также проверьте, что линия активна и бэкет не пуст (шаг 3.6–3.7).
- **`healthy=false`, `stalled=true`, ~100 КБ и тишина** — панель отдаёт заглушку
«not on air»: поток НЕ идёт ONLINE. Причины: поток не назначен на Main Server
как источник; источник (генератор) недоступен; источник не стартовал. Убедитесь
на шаге 3.4, что в админке у потоков есть битрейт.
- **HLS-поток FAIL с `rebuffers > 0`** — буфер реально опустел посреди
воспроизведения. Длина HLS-сегментов намеренно рваная (анти-DPI), и checker это
учитывает — HLS судится по `rebuffers`, а не по паузе доставки; так что
`rebuffers > 0` означает, что буфер слишком мелкий, чтобы поглотить длинный
сегмент. Увеличьте буфер/окно на панели (`client_prebuffer` /
`request_prebuffer` / `seg_list_size`) — это чаще тонкий буфер выбранного
LLOD-режима, а не поломка нарезки. (`stalled=true` при `rebuffers=0` у HLS —
информативно, но за провал не считается.)
- **LLOD-поток не стартует**, в логах панели «Undefined array key 0» в
`StreamProcess.php` (`startLLOD`) — у этого потока пустой/невалидный
`stream_source`. Впишите валидный источник в поле Source (шаг 3.2); панель
обрабатывает llod 2 и 3 одинаково, «режим v3» тут ни при чём.
- **Генератор не запросили** — в логе генератора
(`docker exec xcvm-test-install cat /tmp/xcvm-streamgen.log`) нет `GET /stream.ts`:
панель не пытается тянуть источник (см. предыдущий пункт).
- **Бэкап не подходит после переустановки** — фикстура привязана к конкретной
установке (креды/IP). После `clean` + `install` повторите ручную настройку и
`streamtest-backup`.
## Одиночная проверка (без панели)
`stream_check.py` работает и как одиночный чекер:
```bash
python3 tools/stream-check/stream_check.py check 'http://host/stream.ts' --duration 30 --json
python3 tools/stream-check/stream_check.py check 'http://host/stream.m3u8' --live # ANSI-дашборд
python3 tools/stream-check/stream_check.py playlist 'path/or/url.m3u' # batch → сводный JSON в stdout
python3 tools/stream-check/stream_check.py playlist 'path/or/url.m3u' --out-dir logs/ # + отдельный файл на каждый стрим
```
`--out-dir DIR` (только для `playlist`) дополнительно пишет по одному файлу
`<NN>-<имя>.json` на каждый поток — по мере его прохождения.
Сводный вывод зависит от того, куда идёт stdout: в **интерактивном терминале** —
короткая сводка (без простыни JSON), при **перенаправлении/пайпе** (в файл, в
`streamtest`) — полный сводный JSON. То есть `… playlist url` в терминале не
засоряет консоль, а `… playlist url > all.json` даёт машинный JSON.
> **Берите URL в кавычки.** Если в ссылке есть `&` (напр.
> `.../playlist/TestLine/TestLine/m3u?output=hls&key=live`), без кавычек bash
> посчитает `&` концом команды и уведёт её в фон, а `key=live` выполнит отдельно.
> Одинарные кавычки экранируют `&`, `?`, `=` — весь URL уйдёт в `playlist`:
>
> ```bash
> python3 tools/stream-check/stream_check.py playlist 'http://172.18.0.2:80/playlist/TestLine/TestLine/m3u?output=hls&key=live'
> ```
>
> Через `test_release.sh streamtest` это не нужно — там URL уже передаётся в кавычках.