Исходные драйверы¶
A исходный драйвер позволяет модулю добавлять своего рода живой исходный код, который ffmpeg не может прочитать, например, DASH с помощью DRM и запустите для него свой собственный двигатель. Движок использует ffmpeg-код. место только для этих источников. Поток остается обычным потоком XC_VM: тем же самым потоком идентификатор, категории, букеты, мониторинг, запуск/остановка/перезапуск, подключения, архив и эскизы.
Поток может содержать оба вида. С основным в вашем движке и резервным на обычном HTTP, поток переключается между ними без использования дополнительного кода.
Статусе
Интерфейс драйвера, регистрация, выбор производителя, распознавание процесса, проверка исходного кода и проверка с экономией времени находятся в ядре, а вкладка модуля - в потоке доступны форма и вид импорта модуля (Вкладки потоковой формы, Виды импорта). Выпуски HLS все еще планируются; смотрите Текущие ограничения.
Основные сведения о модуле (макет, манифест, класс модуля) см. в разделе Разработка модуля; другие хуки см. в разделе Точки расширения модуля.
Как это работает¶
stream_source: ["acmedash://prov1/demo-001", "http://backup.example/live.ts"]
│ │
SourceDriverRegistry::for(url) core: ffmpeg or native remux
│ │
driver->buildArgv($ctx) buildLive() / buildNativeLive()
│ │
└───────────── sources[] ─────────┘
│
the supervisor (xc_fanout) or the PHP monitor
runs one entry and fails over to the next on exit
- Ваш модуль объявляет класс драйвера в
module.json. - Драйвер запрашивает одну или несколько схем URL-адресов (
acmedash://…). - Когда запускается поток, core просматривает каждый URL-адрес источника. Для URL-адреса с вашей схемой это
запрашивает у водителя, доступен ли источник (
available()) и работает ли двигатель командная строка (buildArgv()). - Core запускает эту команду точно так же, как производитель ffmpeg, и контролирует ее, перезапускает его и отображает его выходные данные.
| Срок | Значение |
|---|---|
| Producer | Процесс, который генерирует байты прямого потока: ffmpeg, xc_fanout remux, прокси-сервер, рабочие команды LLOD/delay/loopback или ваш движок. |
| Source driver | Ваш класс: он утверждает схемы URL-адресов и создает для них команду producer. |
| Supervisor | Демон xc_fanout, который запускается, наблюдает, перезапускает и принимает производителей. Когда контроль отключен, эту работу выполняет PHP monitor (console.php monitor). |
Откуда берется реальный URL-адрес потока¶
Ядро никогда не узнает исходный URL-адрес источника драйвера. Оно хранит URL-адрес оператора
вводится в список источников потока, например, acmedash://prov1/demo-001, и передает, что
строка для вашего драйвера не изменилась:
- в
available($streamId, $url), как$url; - в
buildArgv($ctx), как$ctx['url'].
Превратить его во что-то, что может извлечь ваш движок, - это полностью задача вашего модуля. Два шаблоны работают.
Шаблон A: идентификатор, который определяет ваш модуль¶
URL-адрес указывает канал, а не местоположение: acmedash://<provider>/<channel-id>. Реальный
URL-адрес манифеста, заголовки и ключи просматриваются когда начнется трансляция, а не сохраняются в
ядро:
operator / import → stream_source = acmedash://prov1/demo-001
stream start → core calls buildArgv(['url' => 'acmedash://prov1/demo-001', …])
your engine → runs the provider script: manifest id=demo-001
→ gets manifest_url, manifest_headers, media_headers
→ runs the provider script: cdm id=demo-001 (keys, if needed)
→ starts fetching; re-signs the URL when it expires
Используйте этот шаблон, когда исходный URL-адрес является недолговечным, подписан или нуждается в ключах. Ничего
значение "секретно" или "срок действия истекает" указывается в streams.stream_source, в основных журналах или в ps. Сохраняйте
определения провайдера (какой скрипт, какие настройки) в собственных таблицах вашего модуля с ключом
по <provider> части URL-адреса.
Шаблон B: исходящий URL-адрес, включенный в вашу схему¶
URL-адрес содержит само местоположение, и меняется только схема:
acmedash://cdn.example.com/live/ch1/manifest.mpd. Ваш водитель отображает его обратно:
Используйте этот шаблон, когда у оператора уже есть стабильный общедоступный URL-адрес манифеста и только
нужен ваш движок, чтобы прочитать его. Все, что находится после ://, принадлежит вам, включая строку запроса,
таким образом, исходный URL-адрес остается неизменным.
Как URL-адрес попадает в поток¶
- Вручную: оператор вводит его в качестве источника на обычной странице добавления/редактирования потока. Ядро принимает там любую схему.
- Путем импорта: зарегистрируйте тип импорта в нем перечислены каналы вашего провайдера с вашими URL-адресами. Администратор выбирает его на странице Страница импорта и просмотра и импортирует каналы как обычные потоки.
- В качестве резервной копии: укажите свой URL-адрес в любом месте списка источников. Основной/резервный порядок работает то же самое, что и для основных источников.
Быстрый старт¶
В этом пошаговом руководстве создается модуль с именем acme-dash и схемой acmedash://.
Замените оба варианта вашими собственными именами.
1. Расположение¶
src/Modules/acme-dash_9f1c0/
├── module.json
├── AcmeDashModule.php # the module class
├── AcmeDashDriver.php # the source driver
├── bin/
│ └── xcvm-acmedash # your engine, executable (chmod 755)
└── migrations/
├── 1.0.0.up.sql
└── 1.0.0.down.sql
Загрузите движок в каталог модуля, чтобы при удалении модуля он был удален.
Не устанавливайте его под /home/xc_vm/bin/, который принадлежит core.
2. модуль.json¶
{
"name": "acme-dash",
"hash_id": "9f1c0b7e4d2a6538c1e0a4b7d6f39e21",
"description": "DASH sources through the Acme engine",
"version": "1.0.0",
"requires_core": ">=2.5",
"environment": "main",
"source_drivers": ["XcVm\\Module\\AcmeDash\\AcmeDashDriver"],
"start_timeout": 60
}
| Ключ | Значение |
|---|---|
source_drivers |
Полные имена классов, реализующие XcVm\Core\Module\SourceDriverInterface. Модуль может объявлять несколько таких классов. |
start_timeout |
Необязательно, в секундах. Как долго core ожидает ваш первый плейлист, прежде чем посчитает запуск неудачным. Оставьте значение по умолчанию для core (20-30 секунд). Установите его, если холодный запуск включает получение ключа. |
environment |
Должно быть main. Ваш движок работает только на главном сервере; см. Только на главном сервере. |
3. Класс водителя¶
<?php
namespace XcVm\Module\AcmeDash;
use XcVm\Core\Module\SourceDriverInterface;
use XcVm\Infrastructure\Database\DatabaseAware;
final class AcmeDashDriver implements SourceDriverInterface {
use DatabaseAware;
public function schemes(): array {
return ['acmedash'];
}
public function binary(): string {
return 'xcvm-acmedash';
}
public function buildArgv(array $ctx): array {
$argv = [
__DIR__ . '/bin/xcvm-acmedash',
'--source', $ctx['url'],
'--config', $this->writeConfig($ctx),
'--playlist', $ctx['hls']['dir'] . $ctx['hls']['playlist'],
'--segment', $ctx['hls']['dir'] . $ctx['hls']['segment_pattern'],
'--hls-time', (string) $ctx['hls']['seg_time'],
'--hls-list-size', (string) $ctx['hls']['list_size'],
'--hls-delete-threshold', (string) $ctx['hls']['delete_threshold'],
'--progress', $ctx['progress_path'],
];
if ($ctx['ingest'] !== null) {
array_push($argv, '--ingest', $ctx['ingest']);
}
return $argv;
}
public function available(int $streamId, string $url): bool {
// Cheap and local: is the provider known and enabled? No manifest fetch here.
$provider = (string) parse_url($url, PHP_URL_HOST);
return (bool) self::db()->fetchValue('SELECT `enabled` FROM `acmedash_providers` WHERE `name` = ?', $provider);
}
/** Network settings and credentials go to a private file, never into argv. */
private function writeConfig(array $ctx): string {
$dir = TMP_PATH . 'acmedash/';
if (!is_dir($dir)) {
mkdir($dir, 0700, true);
}
$path = $dir . intval($ctx['stream_id']) . '.json';
file_put_contents($path, json_encode(['fetch' => $ctx['fetch']]));
chmod($path, 0600);
return $path;
}
}
Что ожидает ядро от этого класса:
- Никаких аргументов конструктора и никаких контейнерных сервисов. Ядро загружает драйверы без загрузочные модули, поскольку запрос зрителя может запустить поток из облегченного точка входа в потоковую передачу.
- Database access only through
DatabaseAware+self::db(). schemes()иbinary()должны быть мгновенными: нет сети и нет подпроцессов.available()необходимо ответить в течение нескольких секунд. Ядро вызывает его перед запуском и прежде чем переключиться на этот источник, вместо запуска ffprobe по URL-адресу.- Исключения безопасны. Если запускается какой-либо метод, ядро пропускает этот источник и записывает сообщение в журнал потока.
4. Класс модуля¶
Сохраняйте настройки для каждого потока в своих собственных таблицах и очищайте их при запуске потоков. удаленный:
<?php
namespace XcVm\Module\AcmeDash;
use XcVm\Core\Events\ListensTo;
use XcVm\Core\Events\Stream\StreamsDeletedEvent;
use XcVm\Core\Module\BaseModule;
use XcVm\Infrastructure\Database\DatabaseAware;
class AcmeDashModule extends BaseModule {
use DatabaseAware;
public function getName(): string {
return 'acme-dash';
}
public function getVersion(): string {
return '1.0.0';
}
#[ListensTo(StreamsDeletedEvent::class)]
public function onStreamsDeleted(StreamsDeletedEvent $event): void {
$ids = array_map('intval', $event->streamIds);
if ($ids !== []) {
self::db()->delete('acmedash_streams', '`stream_id` IN (' . implode(',', $ids) . ')');
}
}
}
Никогда не добавляйте внешний ключ к streams. При сохранении потока его строка перезаписывается
(REPLACE INTO), так что ON DELETE CASCADE будет удалять ваши данные при каждой правке.
5. Двигатель¶
Ваш движок - это обычный исполняемый файл, который следует за контракт с продюсером. Короткая версия:
- Он выполняется на переднем плане, по одному процессу на поток.
- Он записывает HLS на диск по указанным путям.
- Когда задано значение
--ingest, он также записывает MPEG-файлы в этот unix-сокет. - Он добавляет блоки прогресса.
- Он выживает
SIGKILL, не оставляя после себя детей.
6. Попробовать это¶
- Установите модуль и запустите
console.php status. При этом будут установлены входящие в комплект модули и запускает ваши миграции. - Создайте прямую трансляцию с источником
acmedash://prov1/demo-001, без перекодирования, отложите выключение. Сохраните и запустите его. - Watch the stream's log:
/home/xc_vm/content/streams/<id>.errors. Every decision основные сведения о вашем источнике записаны там с префиксом[panel](см. Устранение неполадок). - Убедитесь, что в списке потоков указан значок производителя модуль. Это означает, что ядро распознает ваш двигатель как работающий производитель.
- Остановите поток и убедитесь, что процесс запуска завершен. Затем заглушите двигатель с помощью
kill -9и подтвердите, что поток перезапускается.
Ссылка¶
Исходный интерфейс драйвера¶
namespace XcVm\Core\Module;
interface SourceDriverInterface {
/** @return string[] URL schemes this driver owns, lowercase, without "://". */
public function schemes(): array;
/** Basename of the engine executable; stable across upgrades, unique among producers. */
public function binary(): string;
/**
* The producer's argv. Element 0 is the absolute path of binary(); one element
* must contain the full playlist path (hls.dir + hls.playlist). Core escapes
* every element and adds the redirections and pid handling itself.
*
* @return string[]
*/
public function buildArgv(array $ctx): array;
/** Whether the source is reachable now; called instead of ffprobe on the URL. */
public function available(int $streamId, string $url): bool;
}
Ядро отклоняет argv, элемент которого 0 не является абсолютным путем, заканчивающимся на binary(), или
при этом список воспроизведения не называется. Затем он пропускает источник и записывает причину.
$ctx¶
| Ключ | Тип | Значение |
|---|---|---|
stream_id |
инт | Идентификатор потока. |
url |
строка | Исходный URL-адрес, указанный вашим водителем, не изменился. |
label |
строка | Индекс источника в списке источников потока (0, 1, ...), для журналов. |
fetch |
массив | Параметры сети stream: user_agent, proxy, cookie, headers. Используются только значения, заданные оператором; значение, равное глобальному значению по умолчанию, не учитывается, поэтому оно никогда не отменяет значения, заданные вашим провайдером. |
hls.dir |
строка | Каталог для HLS на диске, с завершающей косой чертой. |
hls.playlist |
строка | Имя файла списка воспроизведения: <id>_.m3u8. |
hls.segment_pattern |
строка | Шаблон имени файла сегмента: <id>_%d.ts. |
hls.seg_time |
инт | Длительность целевого сегмента в секундах. |
hls.list_size |
инт | Сегменты, перечисленные в списке воспроизведения. |
hls.delete_threshold |
инт | Дополнительные фрагменты следует сохранить на диске после того, как они покинут список воспроизведения. |
ingest |
?строка | Путь к сокету Unix для входа в разветвление или null, когда разветвление отключено или не работает. |
progress_path |
строка | Куда добавлять блоки выполнения: <id>_.progress. |
errors_path |
строка | Лог потока, <id>.errors. Туда будет отправлен ваш стандартный запрос. |
supervised |
тип bool | true когда супервизор xc_fanout запускает программу producer, false для монитора PHP. |
URL-адреса источников¶
- Схема. Каждый модуль выбирает свой собственный, и один модуль может владеть несколькими.
- Формат -
[a-z][a-z0-9+.-]*. - Схемы чтения ядра (
http,https,rtmp,rtsp,udp,rtp,srt,file, ...) не может быть заявлено. - Схема, заявленная двумя установленными модулями, не используется ни в одном из них, и оба имени регистрируются в журнале.
- Предпочитайте имя, привязанное к вашему модулю (
acmedash, а неdash). - Все, что находится после
://, принадлежит вам. Ядро передает его драйверу без изменений. Проверьте его вavailable()(возвратfalse) или вbuildArgv()(бросок). - URL-адрес - это идентификатор источника, например
acmedash://<provider>/<channel-id>. Держи идентификатор в URL-адресе, а не в таблице для каждого потока, поскольку каждый источник резервного копирования может укажите на другой канал или провайдера. - URL-адрес должен быть доступен для печати, без пробелов или управляющих символов. Он хранится
в
stream_sourceи отображается на панели.
Контракт с продюсером¶
Процесс¶
- Один процесс переднего плана на поток. Без демонизации, без двойной вилки: pid-ядро записи должны стать вашим двигателем.
- В командной строке будет указан список воспроизведения. Остановка, уничтожение, резервное копирование pid cron и
принятие после перезапуска супервизора найдите производителя по
<dir>/<id>_.m3u8в его командная строка. Ядро применяет это к argv. - Stop is
SIGKILL. - Супервизор уничтожает всю группу процессов; PHP-монитор уничтожает только pid.
- Не сохраняйте состояния, которое необходимо сбросить при выходе.
- Дайте каждому дочернему процессу (скриптам провайдера, помощникам) сигнал о смерти родителя, чтобы он
умирает вместе с двигателем на обоих путях. В Go:
SysProcAttr{Pdeathsig: syscall.SIGKILL}. - Коды выхода. Любой выход означает, что работа производителя завершена, и ядро перезапускает его или завершается сбоем. в соответствии с настройками трансляции. Не используйте:
3: зарезервировано ("не поддерживается" основным ремультиплексором);4: зарезервировано для будущего "презентация завершена, не перезапускайте". Это обрабатывается как на данный момент есть любой другой выход.- стандартный переходит к логу потока. Пишите короткие, удобочитаемые сообщения в одну строку.
HLS диска (всегда)¶
Архив, миниатюры, монитор, cron статистики и путь доставки без разветвления - все это
считывает данные HLS с диска, поэтому записывает их, даже если установлено значение ingest.
- Нумерация сегментов. Сегменты равны
<id>_<N>.ts, где N начинается с 0 на каждом запускает и не имеет пробелов во время выполнения, как это делает ffmpeg. - Ядро удаляет
<id>_*при остановке и при явном запуске. - После сбоя супервизор перезапускает вашу очистку без. Переписывать существующие файлы; никогда не продолжайте использовать сохраненный счетчик.
- Формат списка воспроизведения. В нем перечислены базовые имена с
#EXTINFдля каждого сегмента, и#EXT-X-MEDIA-SEQUENCEравно первому из перечисленных N. - Атомарные записи. Запишите каждый сегмент и список воспроизведения атомарно: запишите во временный файл
в том же каталоге, тогда
rename(). Сегменты являются обычными MPEG-TS; core шифрует на муха там, где она настроена. - Удержание. Сохраняйте
hls.delete_thresholdдополнительные сегменты на диске после того, как они покинут список воспроизведения. - Timing.
- The first playlist must appear within the start timeout:
start_timeout, or the core по умолчанию. - После этого список воспроизведения должен меняться по крайней мере каждые
6 × seg_timeсекунд, иначе поток считается остановленным и перезапускается.
Принимать (когда ingest не равно null)¶
- Соединение. Подключитесь к сокету unix и запишите MPEG-TS, а затем повторно подключитесь, если соединение прерывается. При использовании супервизора сокет всегда передается, и супервизор подтверждает начало по поступающим на него байтам.
- Packet format.
- 188-байтовые пакеты, выровненные по первому байту.
- ПЦР по ПЦР-идентификатору PMT.
random_access_indicatorустанавливается для ключевых кадров видео; сегменты HLS вырезаются только там.- Аудио
stream_type. Используйте стандартное значение: AAC0x0F/0x11, MP2/MP30x03/0x04, AC-30x81, E-AC-30x87. Аудиосигнал передается как личные данные (0x06) отключает проверку на потерю звука. - Никакой проверки. Супервизор обрабатывает любые байты как оперативный сигнал и не проверяет ТС, так что его правильность зависит от вас.
Прогресс¶
- Добавьте блоки в стиле ffmpeg к строкам
progress_path:key=value, заканчивающимся наprogress=continueилиprogress=endна чистой поверхности. - Откройте файл с помощью
O_APPEND, потому что core обрезает его между чтениями. - Полезными ключами являются
bitrate(например,bitrate=4200.5kbits/s),speedиout_time_us. - Опустить
fpsесли вы не декодируете кадры. Пропущенный кадр или нольfpsприводит к снижению частоты кадров в секунду. сторожевой таймер пропускает проверку. Не вводите значение-заполнитель.
Что core делает для вас¶
| Ситуация | Основное поведение |
|---|---|
| Начало трансляции | Запрашивает available(). Когда он возвращает true, запускайте свой argv в качестве источника; в противном случае попробуйте следующий исходный код. |
| Отказоустойчивость | Переходит к следующему источнику в списке при завершении работы вашего движка. Этот источник может быть вашим или core. |
| Приоритетное возвращение | Каждые 5 минут запрашивает у available() источники с более высоким приоритетом и переключается обратно, когда кто-то отвечает. Монитор PHP вызывает его напрямую; супервизор запускает console.php source:probe <id> <base64 url>, который вызывает его. |
| Проверка источников | Кнопка проверки в форме потоковой передачи, проверка средствами потоковой передачи и сканер по запросу сообщают о вашем источнике как о "доступном / недоступном источнике модуля" с available(), поскольку в нем нет кодеков, которые можно было бы отобразить до запуска вашего движка. |
| Сохранение потока | Отклоняет прямой источник, Direct Proxy и LLOD v2 в потоке с вашим источником, а также балансировщик нагрузки, загружаемый непосредственно из источника, а не как дочерний элемент main. Те же проверки применяются к импорту и массовым правкам. |
| Принудительный переключатель источника | Запрашивает available() перед переключением на ваш источник. |
| Остановка / перезапуск | Завершает работу процесса, найденного с помощью pid и списка воспроизведения, а затем сбрасывает значение <id>_*. |
| Перезапуск супервизора | Повторно использует ваш запущенный движок по пути к списку воспроизведения вместо запуска второго. |
| Статус панели | В списке потоков производитель указан как модуль, с процессором и памятью из вашего процесса. |
| Информация о кодеке | Считайте из ваших выходных данных с помощью stats cron. До этого на панели будет отображаться заполнитель (h264/aac). |
Отказы¶
Ядро само отказывает вашему источнику, когда поток использует настройку, которую может применить только ffmpeg, потому что ваш двигатель бесшумно сбросил бы эту настройку:
| Настройка потока | Сообщение журнала |
|---|---|
| Тип потока, отличный от прямых трансляций | not a live channel (type …) |
| Перекодирование, включая наложение логотипа | transcoding is enabled |
| Пользовательская команда ffmpeg | the stream has a custom ffmpeg command |
| Пользовательская карта треков | the stream maps specific tracks |
| Выход RTMP | RTMP (FLV) output is enabled |
| Внешний толчок | the stream is pushed to an external server |
| Аудиокодек с принудительным вводом | an input audio codec is forced |
| Задержка | a source driver cannot feed a delayed stream |
Отклоненный источник пропускается, и ядро перепробует следующий. Если источника не осталось, поток не удается запустить. Core никогда не запускает ffmpeg по URL-адресу с вашей схемой. На узле, где ваша модуль не установлен, ваши исходные тексты отклоняются с сообщением "нет исходного драйвера для acmedash:// на этом узле`.
Только главный сервер¶
Модули установлены только на главном сервере, поэтому ваш движок работает там. Для обслуживания поток драйверов из подсистемы балансировки нагрузки, присвойте LB значение дочерний элемент главного сервера. Затем LB извлекает поток из main через loopback, как и любой другой дочерний поток, и никогда не видит ваш URL-адрес.
Скрипты и секреты провайдера¶
Если ваш модуль запускает скрипты, предоставляемые оператором, это означает выполнение кода от имени пользователя xc_vm.
- Исполнение. Запустите их как подпроцесс со списком argv и без оболочки, используя JSON в аут, жесткий тайм-аут и абсолютное ограничение на stdout и stderr.
- Где живут скрипты. Храните скрипты в виде файлов на диске в каталоге вашего модуля. Пусть
Пользовательский интерфейс выбирает из этого списка и показывает отпечаток SHA-256 каждого скрипта, а не принимает
загружаемые файлы. Если вы добавляете загружаемые файлы, регистрируйте их без их собственного разрешения, а не
edit_stream. - В argv нет секретов. Core записывает командную строку на диск и
psпоказывает ее пользователю. каждому пользователю. Передайте учетные данные и сетевые настройки через файл0600, поскольку примерный водитель делает это или загружает их в двигатель.
Средства массовой информации¶
- Треков. Треки в вашем TS (видео, аудио, DVB субтитры) доходят до клиентов TS и HLS неизменившийся. VLC, Kodi и TiviMate показывают субтитры в формате DVB; браузерные проигрыватели (hls.js, video.js) не.
- Выдачи. Для раздельного воспроизведения аудио и субтитров WebVTT требуется Представления о ЗОЖ, который ядро еще не поддерживает. Когда это произойдет, контракт с производителем получит выходные данные для каждого исполнения.
Диагностика¶
Каждое решение, которое core принимает относительно вашего источника, записывается в
/home/xc_vm/content/streams/<id>.errors с префиксом [panel]:
| Сообщение | Значение |
|---|---|
source #N skipped: <refusal> |
Настройка потока отказывает драйверам; см. Отказы. |
source #N skipped: argv[0] must be the absolute path of … |
buildArgv() элемент 0 не является /…/<binary()>. |
source #N skipped: the command must name …_.m3u8 |
Ни один элемент argv не содержит путь к списку воспроизведения. |
source driver reports <url> unavailable |
available() вернул false. |
source <url> skipped: <exception message> |
Метод драйвера выброшен. |
no source driver for …:// on this node |
Модуль здесь не установлен или не включен, либо его схема была оспорена. |
Если схема оспаривается или недействительна, поищите в журнале ошибок PHP
SourceDriverRegistry: строк. В них указаны названия задействованных модулей.
Если поток продолжает перезапускаться, в то время как движок выглядит исправным, сначала проверьте это:
- путь к списку воспроизведения указан в командной строке движка;
- базовое имя исполняемого файла равно
binary(); - список воспроизведения меняется по крайней мере каждые
6 × seg_timeсекунд.
Текущие ограничения¶
Эти части планируются как отдельные пункты расширения и пока не входят в состав ядра:
- соблюдение кода выхода
4иSIGTERMс отсрочкой доSIGKILL; - Воспроизведение в формате HLS (несколько звуковых дорожек, WebVTT).
Контрольный список перед отправкой¶
- [ ]
binary()равно базовому имени исполняемого файла движка и является уникальным. - [ ]
buildArgv()элемент 0 - это абсолютный путь к движку; путь к списку воспроизведения указан в argv. - [ ] В argv нет учетных данных или ключей.
- [ ] При новом запуске сначала записывается
<id>_0.tsбез пробелов; плейлист заменяется атомарно. - [ ] При установке
ingestTS выравнивается и имеет значения PCR и RAI; двигатель снова подключается после закрытия разъема. - [ ] После
kill -9 <engine pid>ни один дочерний процесс не остается запущенным. - [ ] Блоки выполнения добавляются и заканчиваются на
progress=. - [ ]
available()является локальным и быстрым. - [ ] У ваших таблиц нет внешнего ключа для
streams;StreamsDeletedEventочищает их. - [ ]
environmentравноmain.