New streamtest-gen / -gen-stop / -backup / streamtest commands: configure the panel once by hand, snapshot the DB, then restore + run repeatably. The container runner rebuilds caches, launches the test-stream-generator, fetches the panel's output m3u for the test line, and runs the checker (aggregate JSON + per-stream logs copied out). STREAMTEST.md documents the flow; docker-compose mounts tools/ and exposes port 8088; ignore out/, fixtures/, graphs/.
20 KiB
Стрим-тест панели (e2e)
Сквозная проверка стриминга: локальный генератор отдаёт видеопоток →
панель его рестримит → stream_queue_check.py проверяет выход панели
(каждый канал ~2 минуты) и пишет JSON-лог по каждому потоку.
Панель настраивается один раз вручную через админку, конфигурация
сохраняется в дамп БД, а прогон теста восстанавливает дамп и запускает
checker — повторяемо. Прямых SQL-запросов к БД нет: тест только снимает и
восстанавливает бэкап (mysqldump / импорт).
генератор (127.0.0.1:8088) ──► панель (рестрим/LLOD) ──► get.php m3u ──► stream_queue_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_queue_check.py (checker) и
tools/stream-check/stream_graph.py (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 в контейнере:
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». Если это мешает — эту миграцию нужно поправить (guardIF NOT EXISTS/ убрать из базовой схемы) вsrc/migrations/010_add_shared_mount_prefixes.sql.
2. Запустить генератор (для ручной настройки)
./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 (панель должна успешно рестримить источник от генератора). Порядок:
-
Категория (Streams → Categories): создать live-категорию, напр.
XC_VM Test Live. -
Поток 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'а.
- Source:
-
Поток 2 — HLS: то же, source
http://127.0.0.1:8088/stream.m3u8. -
Проверить, что потоки ONLINE: в списке стримов у обоих должен появиться битрейт/статус «онлайн» (панель подключилась к генератору и рестримит). Это ключевая проверка — без неё выход панели будет отдавать заглушку «not on air».
-
Bouquet (Bouquets → Add): создать бэкет, добавить оба потока.
-
Линия (Lines → Add):
- Username / Password:
xcvmtest/xcvmtest(значения по умолчанию для теста; можно другие — тогда задайтеSTREAMTEST_USER/STREAMTEST_PASS). - Bouquet: выбрать созданный бэкет.
- Allowed Output Formats: включить как минимум MPEG-TS.
- Exp date — в будущем, Max connections ≥ 1, линия активна.
- Username / Password:
-
Проверка выхода панели (в браузере или curl):
http://localhost:8880/get.php?username=xcvmtest&password=xcvmtest&type=m3u_plus&output=tsДолжен вернуться m3u со ссылками на выход панели (
/play/<token>/ts), и они должны проигрываться (в плеере/ffprobe), а не отдавать заглушку.
4. Снять бэкап настроенной БД
Когда потоки идут ONLINE и выход панели проигрывается:
./tools/test-install/test_release.sh streamtest-backup
Дамп сохранится в tools/test-install/fixtures/streamtest-db.sql.gz (в git не
коммитится — он привязан к текущей установке: креды, IP сервера, коды доступа).
5. Прогонять тест (повторяемо)
./tools/test-install/test_release.sh streamtest
Что делает: восстанавливает дамп БД → пересобирает кэши (cron:cache,
cron:cache_engine) → поднимает генератор → забирает m3u линии с выхода панели →
запускает stream_queue_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. Итерации
Если с первого раза потоки не проходят:
./test_release.sh streamtest— восстановит известное состояние БД из бэкапа (сбрасывает любые накопившиеся изменения) и прогонит checker.- По JSON-логу видно, что не так (
healthy=false,errors, счётчики очереди,samples— тайм-серия kbit/s и буфера). - Поправьте настройку в админке (или запустите генератор
streamtest-genи донастройте), убедитесь, что потоки снова ONLINE. - Снимите новый бэкап
streamtest-backup(перезапишет фикстуру). - Повторяйте
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-отчёта
{
"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_queue_check.py --live),
сериализованный по секундам: пропускная способность, буфер, накопленные секунды
и счётчики разрывов очереди (CC/sync для TS, gaps/disc для HLS).
Правило healthy: получены данные, queue_breaks ≤ tolerance, нет ошибок и —
по режиму — TS: не было сталла (пауза доставки > stall-timeout); HLS: не
было ребуферов (буфер не опустошался). Длина HLS-сегментов часто намеренно рваная
(анти-DPI), поэтому одна лишь длинная пауза доставки HLS не валит — важно, реально
ли опустел буфер.
Графики (SVG) для сравнения
tools/stream-check/stream_graph.py рисует из 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": [...], разворачивается в потоки).
# вся папка per-stream логов + сводный comparison.svg
python3 tools/stream-check/stream_graph.py tools/test-install/out/streamtest-<ts>/ --combined --out-dir graphs/
# сводный отчёт одного прогона
python3 tools/stream-check/stream_graph.py tools/test-install/out/streamtest-<ts>.json
# отдельные файлы / glob
python3 tools/stream-check/stream_graph.py logs/01-MPEG-TS.json logs/02-*.json
# смесь: папка + отдельный файл (без дублей)
python3 tools/stream-check/stream_graph.py 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_queue_check.py работает и как одиночный чекер:
python3 tools/stream-check/stream_queue_check.py 'http://host/stream.ts' --duration 30 --json
python3 tools/stream-check/stream_queue_check.py 'http://host/stream.m3u8' --live # ANSI-дашборд
python3 tools/stream-check/stream_queue_check.py --playlist 'path/or/url.m3u' # batch → сводный JSON в stdout
python3 tools/stream-check/stream_queue_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:python3 tools/stream-check/stream_queue_check.py --playlist 'http://172.18.0.2:80/playlist/TestLine/TestLine/m3u?output=hls&key=live'Через
test_release.sh streamtestэто не нужно — там URL уже передаётся в кавычках.