mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-04 20:03:11 +02:00
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.
307 lines
20 KiB
Markdown
307 lines
20 KiB
Markdown
# Стрим-тест панели (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 уже передаётся в кавычках.
|