mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-02 12:02:09 +02:00
Fix Markdown-mangling artifacts the free web engine (yandex) produced in the
committed docs/ru, and regenerate the whole tree cleanly (0 fallbacks):
- Sentinel format @@N@@ -> {N}. MT engines are trained to preserve curly
format-string placeholders, so {N} survives code-heavy lines where @@N@@ (and
ZZZ…ZZZ, which also duplicated its Z) were split/moved — e.g. the stray
"@0@@" in the FAQ and "load balancerZ" in the README are gone.
- Possessive: a trailing English `'s` is consumed INTO the masked span and
dropped on restore. Every sentinel format breaks when a bare `'s` sits right
after it, and Russian has no possessive `'s`.
- Validate + retry: after restore, any leftover brace fragment triggers a retry
(the engine is non-deterministic); after a few failures the line stays English
so a broken token is never emitted.
- Glossary += KeyDB, yt-dlp, Ubuntu, iptables, MAGSCAN.
Regenerated docs/ru (37 files, translators/yandex): no residual sentinels,
mkdocs build --strict clean.
497 lines
28 KiB
Markdown
497 lines
28 KiB
Markdown
# Подсистема потоковой передачи
|
||
|
||
Подсистема потоковой передачи обрабатывает доставку в реальном времени, VOD и timeshift.
|
||
Это быстрый путь (~10-100 тыс. запросов в минуту, <50 мс p99), и он использует отдельный облегченный bootstrap, чтобы избежать загрузки всего стека администратора.
|
||
|
||
---
|
||
|
||
## Поток запросов
|
||
|
||
```text
|
||
client request
|
||
|
|
||
nginx rewrite (/auth/{token} -> /stream/live.php?token={token})
|
||
|
|
||
StreamingRequestBootstrap::init()
|
||
|
|
||
StreamingBootstrap::bootstrap()
|
||
|
|
||
LegacyInitializer::initStreaming()
|
||
|
|
||
endpoint logic (live.php / vod.php / timeshift.php)
|
||
|
|
||
ShutdownHandler::handle()
|
||
```
|
||
|
||
nginx переписывает все URL-адреса потоковой передачи на PHP точки входа в соответствии с `www/stream/`:
|
||
|
||
|Шаблон URL-адреса|Точка входа|Цель|
|
||
| --- | --- | --- |
|
||
| `/auth/{token}` | `live.php` |Прямая трансляция|
|
||
| `/vauth/{token}` | `vod.php` |Доставка видео по запросу|
|
||
| `/tsauth/{token}` | `timeshift.php` |Архив/timeshift воспроизведение|
|
||
| `/hls/{token}` | `segment.php` |HLS сегментная доставка|
|
||
| `/key/{token}` | `key.php` |Ключ шифрования AES-128|
|
||
| `/subauth/{token}` | `subtitle.php` |Передача субтитров|
|
||
|
||
---
|
||
|
||
## Расположение каталога
|
||
|
||
```
|
||
src/Streaming/
|
||
├── StreamingBootstrap.php
|
||
├── AsyncFileOperations.php
|
||
├── TimeshiftClient.php
|
||
├── Auth/
|
||
│ ├── StreamAuth.php
|
||
│ └── StreamAuthMiddleware.php
|
||
├── Balancer/
|
||
│ └── ProxySelector.php
|
||
├── Codec/
|
||
│ ├── FFmpegCommand.php
|
||
│ ├── FfmpegPaths.php
|
||
│ └── FFprobeRunner.php
|
||
├── Delivery/
|
||
│ ├── HLSGenerator.php
|
||
│ ├── OffAirHandler.php
|
||
│ └── StreamRedirector.php
|
||
├── Health/
|
||
│ └── ProcessChecker.php
|
||
├── Lifecycle/
|
||
│ └── ShutdownHandler.php
|
||
└── Protection/
|
||
└── ConnectionLimiter.php
|
||
|
||
src/www/stream/
|
||
├── init.php # Legacy bootstrap shim (deprecated)
|
||
├── auth.php # Token validation gateway
|
||
├── live.php # Live streaming delivery
|
||
├── vod.php # VOD delivery
|
||
├── timeshift.php # Archive/timeshift playback
|
||
├── segment.php # HLS segment delivery
|
||
├── key.php # Encryption key delivery
|
||
├── subtitle.php # Subtitle delivery
|
||
├── thumb.php # Thumbnail delivery
|
||
└── rtmp.php # RTMP publishing endpoint
|
||
```
|
||
|
||
---
|
||
|
||
## Конвейер начальной загрузки
|
||
|
||
### 1. StreamingRequestBootstrap::init()
|
||
|
||
Файл: `src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php`
|
||
|
||
Действия в порядке:
|
||
|
||
1. Загружайте коды ошибок, обработчик, пути, конфигурацию, двоичные файлы.
|
||
2. Защита от наводнений (только HTTP): проверьте наличие `FLOOD_TMP_PATH . 'block_' . $rIP`.
|
||
3. Загрузите настройки из файлового кэша (`CACHE_TMP_PATH . 'settings'`).
|
||
4. Проверка хоста (только HTTP): проверка на соответствие `allowed_domains`.
|
||
5. Инициализируйте регистратор.
|
||
6. Аварийно закрытый шлюз: возвращает 404, если настройки отсутствуют (кроме `/status`).
|
||
7. Вызовите `StreamingBootstrap::bootstrap()`.
|
||
|
||
### 2. Потоковый загрузчик::bootstrap()
|
||
|
||
Файл: `src/Streaming/StreamingBootstrap.php`
|
||
|
||
```php
|
||
public static function bootstrap($rFilename, $rSettings)
|
||
```
|
||
|
||
Классифицирует конечную точку:
|
||
|
||
- **Конечные точки зондирования:** `probe`, `player_api` (небольшая нагрузка)
|
||
- **Конечные точки по умолчанию:** `live`, `thumb`, `subtitle`, `timeshift`, `vod`, `status`
|
||
- **Привилегированные конечные точки:** `rtmp`, `portal`
|
||
|
||
Загружает `AsyncFileOperations.php` и `DatabaseHandler.php`, сохраняет настройки в `$GLOBALS['rSettings']` и получает доступ к данным в `$GLOBALS['rAccess']`, затем вызывает `LegacyInitializer::initStreaming()`.
|
||
|
||
Возвращает экземпляр базы данных `$db` (используемый устаревшими точками входа).
|
||
|
||
### 3. LegacyInitializer::Инициализация потока()
|
||
|
||
Файл: `src/Core/Init/LegacyInitializer.php`
|
||
|
||
Заполняет глобальные переменные из кэша:
|
||
|
||
- `$GLOBALS['rSettings']`, `$GLOBALS['rServers']`, `$GLOBALS['rBouquets']`
|
||
- `$GLOBALS['rBlockedUA']`, `$GLOBALS['rBlockedISP']`, `$GLOBALS['rBlockedIPs']`
|
||
- `$GLOBALS['rAllowedIPs']`, `$GLOBALS['rProxies']`, `$GLOBALS['rSegmentSettings']`
|
||
- `$GLOBALS['rFFMPEG_CPU']`, `$GLOBALS['rFFMPEG_GPU']`, `$GLOBALS['rFFPROBE']`
|
||
|
||
Подключается к базе данных/Redis на основе `$rSettings['redis_handler']`.
|
||
|
||
> **Важно:** Путь к потоковой передаче считывается исключительно из файлового кэша. При обычной работе программа не запрашивает настройки в базе данных или запросы пользователей.
|
||
|
||
---
|
||
|
||
## Аутентификация по токену
|
||
|
||
Файл: `src/Streaming/Auth/StreamAuthMiddleware.php`
|
||
|
||
```php
|
||
StreamAuthMiddleware::decryptToken($rToken, $rSettings, $rServers, $rIP): array
|
||
```
|
||
|
||
Содержимое токена:
|
||
|
||
|Поле|Описание|
|
||
| --- | --- |
|
||
| `username` |Имя пользователя строки|
|
||
| `password` |Пароль к строке|
|
||
| `stream_id` |Идентификатор целевого потока|
|
||
| `expires` |Временная метка истечения срока действия токена|
|
||
| `channel_info` |Потоковые метаданные (on_demand, прокси, pid)|
|
||
| `user_info` |Разрешения пользователя (max_connections, is_restreamer)|
|
||
| `country_code` |GeoIP код страны|
|
||
| `video_codec` |Запрашиваемый видеокодек|
|
||
|
||
Утверждение:
|
||
|
||
1. Расшифруйте токен, используя `live_streaming_pass`.
|
||
2. Проверьте истечение срока действия: `$rTokenData['expires'] < time() - $rServers[SERVER_ID]['time_offset']`.
|
||
3. Возвращает проанализированные данные токена или вызывает ошибку.
|
||
|
||
Заголовки ответов задаются через `StreamAuthMiddleware::sendStreamHeaders()`:
|
||
|
||
```text
|
||
Access-Control-Allow-Origin: *
|
||
X-XSS-Protection: 0
|
||
X-Content-Type-Options: nosniff
|
||
Alt-Svc: h3-29, h3-T051, h3-Q050 (HTTP/3 hints)
|
||
```
|
||
|
||
---
|
||
|
||
## Потоковая доставка
|
||
|
||
### Жить (live.php)
|
||
|
||
Основная конечная точка доставки (~650 строк):
|
||
|
||
1. Расшифруйте токен с помощью `StreamAuthMiddleware::decryptToken()`.
|
||
2. Разрешить использование сервера/прокси-сервера: `StreamAuth::checkAccess()` + `ProxySelector::availableProxy()`.
|
||
3. Установите ограничения на подключение: `StreamAuth::validateConnections()`.
|
||
4. Создайте запись о подключении: `ConnectionTracker::createConnection()`.
|
||
5. Hand delivery to the **`xc_fanout` daemon** (see below): PHP emits an
|
||
`X-Accel-Redirect` и завершает байтовый путь — nginx передает байты в потоковом режиме.
|
||
- **ТС:** `X-Accel-Redirect: /xc_fanout/<id>?c=<uuid>&prebuffer=N` (nginx
|
||
перезаписывается в файл демона `/live/<id>`).
|
||
- **HLS:** список воспроизведения указывает на выделенные сегменты; `segment.php` транслируется в прямом эфире
|
||
сегментирует только через демон (`/xc_fanout_hls/<id>_<seq>`), иначе `404`.
|
||
6. При выходе: `ShutdownHandler::handle()` → закрыть запись о подключении.
|
||
|
||
### VOD (vod.php)
|
||
|
||
Тот же процесс аутентификации, что и в live. Считывается из `VOD_PATH` вместо `STREAMS_PATH`.
|
||
|
||
### Временной сдвиг (timeshift.php)
|
||
|
||
Обслуживает архивные сегменты. Использует `TimeshiftClient` для разрешения архивного файла.
|
||
|
||
### Доставка демона — `xc_fanout`
|
||
|
||
Live client delivery (TS **and** HLS) is **daemon-only**: PHP authorizes the
|
||
средство просмотра, а затем полностью покидает байтовый путь, так что средство просмотра больше не закрепляет
|
||
PHP-FPM работник, отвечающий за жизнедеятельность потока.
|
||
|
||
- **Fan-out.** `xc_fanout` (a bundled Go daemon) pulls each source **once** and
|
||
предоставляет его каждому пользователю через сокет unix с помощью встроенного в оперативную память сегментатора HLS.
|
||
PHP не соответствует байтовому пути для каждого зрителя; старый цикл поиска и чтения
|
||
(`AsyncFileOperations::awaitFileExists()`) и `HLSGenerator::generateHLS()`
|
||
сервировочные дорожки были удалены при разделке.
|
||
- **Два сокета.** Клиентский сокет (ориентированный на nginx) обслуживает `/live/<id>` и
|
||
`/hls/...`; управляющий сокет, предназначенный только для PHP, регистрирует источники
|
||
(`PUT /streams/<id>` / `/ingest/<id>`), отвечает на вопросы о статусе выхода в эфир
|
||
(`GET /streams/<id>`, `GET /probe/<id>`) и предоставляет доступ к телеметрии.
|
||
- **Telemetry / reconciliation.** `fanout_sync` polls `GET /rates` (per-uuid
|
||
КБИТ/с → `lines_divergence`) и `GET /connections` (согласовывает `lines_live`
|
||
строк, поскольку PHP не может видеть разъединение в `X-Accel`).
|
||
- **Отключен.** Если демон сообщает об отсутствии данных (`has_data=false` / устаревшие), PHP
|
||
показывает страницу "не в эфире" вместо того, чтобы позволить зрителю зависнуть.
|
||
- **Сохранено на диске HLS** только для timeshift / миниатюр / `.analyse` /
|
||
`MonitorCommand` — не для доставки клиенту.
|
||
|
||
#### Наложение отправленного сообщения
|
||
|
||
Действие администратора "Отправить сообщение" отображает текстовый баннер на видео **одного** зрителя.
|
||
PHP отправляет его в сокет управления демоном
|
||
(`FanoutClient::sendSignal` → `POST /signal/<uuid>`), и демон применяет
|
||
ffmpeg `drawtext` наложение на следующий HLS сегмент этого просмотра (или короткий ~5-секундный фрагмент
|
||
окно), однократный запуск, максимальное усилие - сигнал никогда не прерывает воспроизведение. Демон должен
|
||
быть запущенным с помощью ffmpeg, который на самом деле имеет фильтр `drawtext`, так что
|
||
`service` программа запуска выбирает сборку с поддержкой drawtext.
|
||
|
||
---
|
||
|
||
## Управление подключениями
|
||
|
||
### Средство отслеживания подключений
|
||
|
||
Управляет текущим состоянием соединения. Серверная часть выбрана с помощью `$rSettings['redis_handler']`:
|
||
|
||
**Redis (предпочтительно для масштабирования):**
|
||
|
||
- Соединения, хранящиеся в отсортированных наборах:
|
||
- `LINE#{identity}` — подключения для пользователя
|
||
- `STREAM#{stream_id}` — соединения для потока
|
||
- `SERVER#{server_id}` — соединения на сервере
|
||
|
||
**MySQL (резервный вариант):**
|
||
|
||
- Таблица: `lines_live` с полями: `activity_id`, `user_id`, `stream_id`, `server_id`, `uuid`, `pid`, `hls_end`
|
||
|
||
Ключевые методы:
|
||
|
||
```php
|
||
ConnectionTracker::createConnection($data)
|
||
ConnectionTracker::updateConnection($connection, $changes, 'open'|'close')
|
||
ConnectionTracker::getConnection($uuid)
|
||
ConnectionTracker::getLineConnections($user_id)
|
||
ConnectionTracker::getCapacity()
|
||
```
|
||
|
||
### Ограничитель подключения
|
||
|
||
Файл: `src/Streaming/Protection/ConnectionLimiter.php`
|
||
|
||
Устанавливает ограничения на подключение для каждого пользователя при превышении значения `max_connections`:
|
||
|
||
|Приоритет|Критерий|Действие|
|
||
| --- | --- | --- |
|
||
|2|Тот же IP + тот же пользовательский агент|Убей первым|
|
||
|1|Тот же IP-адрес (любой UA)|Убей следующего|
|
||
|0|Какая-либо связь|Убить в качестве запасного варианта|
|
||
|
||
Настройки:
|
||
|
||
- `disallow_2nd_ip_con` — принудительно использовать один IP-адрес для каждого пользователя
|
||
- `ip_subnet_match` — соответствует подсети /24 вместо точного IP-адреса
|
||
- `restrict_same_ip` — возвращает ошибку при несоответствии IP-адресов вместо уничтожения
|
||
|
||
### Устройство для выключения
|
||
|
||
Файл: `src/Streaming/Lifecycle/ShutdownHandler.php`
|
||
|
||
Зарегистрирован с помощью `register_shutdown_function()`. При завершении процесса PHP:
|
||
|
||
1. Закройте запись о соединении в `lines_live` или Redis.
|
||
2. Удалите tmp-файлы со значением `CONS_TMP_PATH . $uuid`.
|
||
3. Удалите поток по требованию из очереди, если это применимо.
|
||
|
||
---
|
||
|
||
## балансировка нагрузки
|
||
|
||
### Выбор сервера (StreamAuth::checkAccess)
|
||
|
||
Файл: `src/Streaming/Auth/StreamAuth.php`
|
||
|
||
```php
|
||
public static function checkAccess($rUserInfo, $rUserIP, $rCountryCode, $rUserISP = ''): int|false
|
||
```
|
||
|
||
Алгоритм:
|
||
|
||
1. Получите доступные серверы: `server_online == true`, `server_type == 0`, `online_clients < total_clients`.
|
||
2. Сортировка по вместимости (по возрастанию) — сначала загружается наименее загруженный.
|
||
3. Применить маршрутизацию GeoIP (если `enable_geoip == 1`):
|
||
- Точное соответствие стране → выберите немедленно.
|
||
- `geoip_type == 'strict'` → исключить несоответствия.
|
||
- В противном случае → присвоить приоритетный вес.
|
||
4. Примените маршрутизацию через интернет-провайдера (если `enable_isp == 1`): та же логика, что и GeoIP.
|
||
5. Верните сервер с наименьшей пропускной способностью из группы с наивысшим приоритетом.
|
||
|
||
### Выбор прокси-сервера (ProxySelector::Доступный прокси)
|
||
|
||
Файл: `src/Streaming/Balancer/ProxySelector.php`
|
||
|
||
```php
|
||
public static function availableProxy($rProxies, $rCountryCode, $rUserISP = ''): int|null
|
||
```
|
||
|
||
Тот же алгоритм, что и `StreamAuth::checkAccess()`, но примененный к списку прокси-серверов.
|
||
|
||
---
|
||
|
||
## Ограничение скорости и защита от наводнений
|
||
|
||
Три слоя:
|
||
|
||
### 1. nginx (уровень подключения)
|
||
|
||
```nginx
|
||
limit_req_zone $binary_remote_addr zone=one:30m rate=20r/s;
|
||
limit_req zone=one burst=8;
|
||
```
|
||
|
||
20 запросов в секунду на IP-адрес с пакетом из 8 запросов. 30-минутное скользящее окно.
|
||
|
||
### 2. StreamingRequestBootstrap (IP-блокировка)
|
||
|
||
```php
|
||
if (file_exists(FLOOD_TMP_PATH . 'block_' . $rIP)) {
|
||
http_response_code(403);
|
||
exit();
|
||
}
|
||
```
|
||
|
||
IP-блокировка на основе файлов. Файлы блоков создаются с помощью вышестоящей логики обнаружения наводнений.
|
||
|
||
### 3. Ограничитель подключений (для каждого пользователя)
|
||
|
||
Применяется после проверки токена. Ограничивает одновременные потоки для каждого пользователя на основе `max_connections`.
|
||
|
||
---
|
||
|
||
## HLS Шифрование
|
||
|
||
Файл: `src/Streaming/Delivery/HLSGenerator.php`
|
||
|
||
```php
|
||
public static function generateHLS($rSettings, $rM3U8, $rUsername, $rPassword,
|
||
$rStreamID, $rUUID, $rIP, ...): string|false
|
||
```
|
||
|
||
Когда `encrypt_hls == true`:
|
||
|
||
1. Сгенерируйте ключевой токен AES-128 из IP + StreamID + salt.
|
||
2. Замените IV содержимым из `STREAMS_PATH . $rStreamID . '_.iv'`.
|
||
3. Encrypt each segment reference: `IP/StreamID/Segment/UUID/SERVER_ID/VideoCodec/OnDemand`.
|
||
4. Замените названия сегментов на `/hls/{encrypted_token}`.
|
||
|
||
Доставка ключей происходит через `key.php` с использованием того же механизма токенов.
|
||
|
||
---
|
||
|
||
## Представление
|
||
|
||
Ключевые проектные решения, касающиеся пропускной способности и задержки:
|
||
|
||
|Особенность|Механизм|
|
||
| --- | --- |
|
||
|Ожидание неблокирующего файла|`AsyncFileOperations::awaitFileExists()` использует inotify (Linux) или оптимизированный опрос|
|
||
|Нулевой режим работы процессора|`time_nanosleep()` через `AsyncFileOperations::efficientSleep()`|
|
||
|nginx буферизация|128 буферов по 32 КБАЙТ на запрос|
|
||
|Объединение подключений в пул|Redis (предпочтительно) или постоянный MySQL|
|
||
|Чтение только из кэша|Настройки и пользовательские данные считываются из файлового кэша без запросов к базе данных|
|
||
|Ранний выход|Отслеживает `connection_status()` каждые 5 секунд для обнаружения отключения клиента|
|
||
|Обновление настроек|Каждые 5 минут (300 секунд) для отслеживания изменений конфигурации без перезапуска|
|
||
|
||
---
|
||
|
||
## Пути к файловой системе
|
||
|
||
```text
|
||
STREAMS_PATH = /home/xc_vm/www/stream/
|
||
CONS_TMP_PATH = /home/xc_vm/tmp/
|
||
CACHE_TMP_PATH = /home/xc_vm/tmp/cache/
|
||
FLOOD_TMP_PATH = /home/xc_vm/tmp/flood/
|
||
SIGNALS_PATH = /home/xc_vm/tmp/signals/
|
||
VIDEO_PATH = /home/xc_vm/www/video/
|
||
ARCHIVE_PATH = /home/xc_vm/www/archive/
|
||
VOD_PATH = /home/xc_vm/www/vod/
|
||
```
|
||
|
||
---
|
||
|
||
## Диагностика и оснастка
|
||
|
||
Два инструмента проверяют правильность доставки потока — что сегменты поступают по порядку
|
||
и очередь на доставку не прерывается.
|
||
|
||
### `tools/stream_queue_check.py` (Только Python, stdlib)
|
||
|
||
Автономный монитор **целостности сегмента/очереди пакетов** с дополнительным ** функцией live
|
||
панель управления буфером**. Автоматически определяет HLS по сравнению с MPEG-TS.
|
||
|
||
```bash
|
||
python3 tools/stream_queue_check.py "<url>" --duration 30 # batch check
|
||
python3 tools/stream_queue_check.py "<url>" --json # cron / monitoring
|
||
python3 tools/stream_queue_check.py "<url>" --live --duration 0 # live dashboard
|
||
```
|
||
|
||
Что означает "неповрежденная очередь" для каждого типа потока:
|
||
|
||
|Течение|Проверка очереди|
|
||
| --- | --- |
|
||
|HLS (`.m3u8`)|`EXT-X-MEDIA-SEQUENCE` монотонный и непрерывный (никаких удаленных или перемотанных сегментов), нет `EXT-X-DISCONTINUITY`, каждый вновь появляющийся сегмент доступен для загрузки. Основные плейлисты отображаются в их первом варианте.|
|
||
|MPEG-TS (`.ts`, `/play/<token>/ts`)|per-PID `continuity_counter` (потерянные / дублированные / переупорядоченные пакеты = разрыв очереди), потеря байта синхронизации, индикатор транспортной ошибки и задержка доставки.|
|
||
|
||
Основные параметры:
|
||
|
||
|Флаг|Цель|
|
||
| --- | --- |
|
||
| `--duration N` |секунды для наблюдения (`0` = до нажатия Ctrl-C в `--live`)|
|
||
| `--tolerance N` |разрешить N временных разрывов очереди, прежде чем сообщать о `BROKEN` (игнорируются редкие сбои источника, переданные `-c copy`)|
|
||
| `--stall-timeout S` |перерыв в доставке засчитывается как задержка; не превышайте продолжительность сегмента (по умолчанию 15).|
|
||
| `--live` |цветная приборная панель TUI (внизу)|
|
||
|`--prebuffer S` / `--buffer-target S`|live: предварительный буфер для виртуального игрока и масштаб буферного графика|
|
||
|`--json` / `--no-color`|машинный вывод / отключение ANSI|
|
||
|
||
Код выхода: `0` исправен, `2` проблема с очередью или задержка, `1` использование.
|
||
|
||
#### Оперативная панель мониторинга (`--live`)
|
||
|
||
Моделирует виртуального проигрывателя: проигрыватель перемещается со скоростью настенных часов, в то время как содержимое
|
||
"получено". Для **TS** полученная временная шкала берется из **PCR** (часы потока).;
|
||
для **HLS** из длительностей сегментов `EXTINF`. Буферизованное время воспроизведения ("кэш") =
|
||
получено − воспроизведено; если значение достигает нуля, то начало воспроизведения зависает (событие отмены буферизации).
|
||
|
||
```text
|
||
STREAM QUEUE / BUFFER MONITOR TS up 00:22
|
||
cache buffer (s), last 60s:
|
||
▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▄▄▄▇▇▇▆▆▆▅▅▅▄▄▇▇▇▆▆▆▅ <- burst-then-drain = delivery sawtooth
|
||
IN CACHE : [█████████████████░░░░░░░░░░░░░] 11.6s / 20s
|
||
PLAYING : PLAYING head 00:18 received 00:29
|
||
rate 1000 kbit/s received 4.1 MB last data 7.0s ago
|
||
QUEUE OK cc:0 sync:0 gaps:0 disc:0 rebuffers:0
|
||
```
|
||
|
||
График буфера и индикатор окрашены в зеленый (исправный) / желтый (низкий) / красный цвета
|
||
(голодает). Для HLS строка блоков показывает сегменты, которые все еще находятся в кэше перед началом
|
||
плейхед.
|
||
|
||
### `console.php stream:check` (PHP, компаньон)
|
||
|
||
Проверяет URL-адрес источника и с помощью `--decode` извлекает и декодирует медиафайл для перехвата
|
||
поврежденные сегменты. HLS проверяется посегментно; сообщение об ошибке с одним сокетом
|
||
конечная точка фиксируется с помощью cURL и декодируется в автономном режиме (ffmpeg в режиме реального времени `-i` зависает на
|
||
it). Источник: `src/Cli/Commands/StreamCheckCommand.php`.
|
||
|
||
```bash
|
||
console.php stream:check "<url>" # metadata probe (type, codecs)
|
||
console.php stream:check "<url>" --decode=30 --json
|
||
```
|
||
|
||
> **Примечание — темп доставки.** Цикл доставки TS в реальном времени в `live.php` истощает
|
||
> доступные данные без регулирования и приостанавливаются только при достижении значения ffmpeg
|
||
> заголовок записи. Более ранняя версия отключалась на одну секунду после каждого чтения, ограничивая
|
||
> пропускная способность составляет `read_buffer_size` в секунду, а клиенты голодают;
|
||
> `stream_queue_check.py --live` визуализирует результирующее поведение буфера.
|
||
|
||
---
|
||
|
||
## Связанные файлы
|
||
|
||
|Файл|Цель|
|
||
| --- | --- |
|
||
| `src/Streaming/StreamingBootstrap.php` |основной загрузчик потоковой передачи|
|
||
| `src/Infrastructure/Bootstrap/StreamingRequestBootstrap.php` |Инициализация на уровне HTTP|
|
||
| `src/Streaming/Auth/StreamAuth.php` |выбор сервера и проверка подключения|
|
||
| `src/Streaming/Auth/StreamAuthMiddleware.php` |расшифровка токенов и заголовки ответов|
|
||
| `src/Streaming/Balancer/ProxySelector.php` |выбор прокси-сервера|
|
||
| `src/Streaming/Protection/ConnectionLimiter.php` |ограничения на подключение для каждого пользователя|
|
||
| `src/Streaming/Delivery/HLSGenerator.php` |Генерация плейлиста M3U8|
|
||
| `src/Streaming/Delivery/StreamRedirector.php` |доступность потока и маршрутизация сервера|
|
||
| `src/Streaming/AsyncFileOperations.php` |неблокирующие утилиты для файловой системы|
|
||
| `src/Streaming/Lifecycle/ShutdownHandler.php` |очистка соединения при выходе|
|
||
| `src/Domain/Stream/ConnectionTracker.php` |состояние соединения в Redis/MySQL|
|
||
| `src/Core/Init/LegacyInitializer.php` |настройка глобальной переменной для потоковой передачи|
|
||
| `src/Cli/Commands/StreamCheckCommand.php` |`stream:check` — проверка/декодирование потока на наличие фрагментарных сегментов|
|
||
| `tools/stream_queue_check.py` |мониторинг целостности очереди + панель мониторинга динамического буфера|
|