Вопрос или проблема
У меня есть программа захвата видео и программа потоковой передачи видео, которые работают вместе как rpicam-vid ... | go2rtc
. Моя проблема в том, что go2rtc
читает данные из трубы только тогда, когда кто-то открывает видеопоток, но мне нужно, чтобы rpicam-vid
работал непрерывно, даже когда поток не активен. Есть ли способ просто отбрасывать данные, когда нет читателя в трубе?
Вы можете вставить что-то между ripcam-vid
и go2rtc
, что будет пересылать данные с выходом в неблокирующем режиме, чтобы, когда труба полная, потому что никто из нее не читает, данные отбрасывались (записи write()
в трубу просто не удались с EAGAIN
(Ресурс временно недоступен)¹):
rpicam-vid ... | {
dd count=0 oflag=nonblock status=none &&
PERLIO=:unix perl -pe 'BEGIN{$/ = \8192}'
} | go2rtc
Здесь используется dd
(предположительно, реализация dd
от GNU), чтобы изменить stdout в неблокирующий режим, и perl
, чтобы передавать данные порциями до 8КиБ.
По умолчанию в Linux размер буфера трубы составляет 64КиБ, вы можете увеличить его, чтобы уменьшить вероятность отброса данных в обычном режиме, используя функцию F_SETPIPE_SZ
. Например, делая и неблокирующий режим, и увеличение емкости трубы с помощью perl
:
rpicam-vid ... |
PERLIO=:unix perl -MFcntl=F_GETFL,F_SETFL,O_NONBLOCK,F_SETPIPE_SZ -pe '
BEGIN{
$flags = fcntl(STDOUT, F_GETFL, 0) || die "getfl: $!\n";
fcntl(STDOUT, F_SETFL, $flags | O_NONBLOCK) || die "setfl: $!\n";
fcntl(STDOUT, F_SETPIPE_SZ, 1048576) || die "setpipesz: $!\n";
$/ = \8192;
}' | go2rtc
Максимальное значение размера трубы, которое можно использовать, определяется через /proc/sys/fs/pipe-max-size
(sysctl fs/pipe-max-size
), по умолчанию 1МиБ, которое вы можете увеличить как root
с помощью sysctl -w fs/pipe-max-size="$newvalue"
, если это необходимо.
Но я не уверен, что отбрасывать куски видео таким образом — хорошая идея.
¹ Неблокирующий режим также может означать короткие записи, то есть при записи в почти полную трубу будет записано только столько, сколько помещается в трубу, всё остальное отбросится, если не будет повторной попытки.
Ответ или решение
Как поддерживать открытую трубу и отбрасывать данные при отсутствии читателя
Недавно возникла проблема, связанная с совместной работой программы захвата изображения на камеру и программы потокового видео. В вашем случае, когда используется команда rpicam-vid ... | go2rtc
, go2rtc
начинает считывать данные из трубы только тогда, когда кто-то открывает видеопоток. Однако необходимо, чтобы rpicam-vid
продолжал работать непрерывно, даже когда поток не запущен. В данной статье мы рассмотрим, как можно решить эту проблему, эффективно отбрасывая данные, когда нет читателя на трубе.
Вставка промежуточного элемента
Одним из наиболее практичных решений является использование промежуточного элемента, который будет пересылать данные из rpicam-vid
в go2rtc
в неблокирующем режиме. Это позволит избежать заполнения очереди на трубе и тем самым избежать зависания программы. Когда труба заполнена, попытки записи завершатся с ошибкой EAGAIN
(Ресурс временно недоступен), что укажет на отсутствие читателя.
Пример использования dd
и perl
Вы можете воспользоваться утилитой dd
и языком Perl для достижения этой цели. В следующем коде используется команда dd
для настройки стандартного вывода в неблокирующем режиме, а Perl для пересылки данных в чанках, например, по 8 КБ:
rpicam-vid ... | {
dd count=0 oflag=nonblock status=none &&
PERLIO=:unix perl -pe 'BEGIN{$/ = \8192}'
} | go2rtc
Настройка размера трубы
По умолчанию размер буфера трубы в Linux составляет 64 КБ. Вы можете увеличить его, чтобы снизить вероятность потери данных в процессе нормальной работы. Это можно сделать с помощью функции F_SETPIPE_SZ
. Вот пример, как это сделать с использованием Perl:
rpicam-vid ... |
PERLIO=:unix perl -MFcntl=F_GETFL,F_SETFL,O_NONBLOCK,F_SETPIPE_SZ -pe '
BEGIN{
$flags = fcntl(STDOUT, F_GETFL, 0) || die "getfl: $!\n";
fcntl(STDOUT, F_SETFL, $flags | O_NONBLOCK) || die "setfl: $!\n";
fcntl(STDOUT, F_SETPIPE_SZ, 1048576) || die "setpipesz: $!\n";
$/ = \8192;
}' | go2rtc
В данном случае мы устанавливаем максимальный размер трубы в 1 МБ. Обратите внимание, что максимальное значение определено параметром /proc/sys/fs/pipe-max-size
и может по умолчанию составлять 1 МБ. Это значение можно изменить с помощью команды:
sysctl -w fs/pipe-max-size="$newvalue"
Обеспечение корректного отбора данных
Следует учитывать, что отбрасывание видеочастей в неблокирующем режиме может не быть оптимальным решением. Вы можете потерять важные данные, если в момент записи в трубу никто не читает поток. Это необходимо учитывать при настройке вашего приложения. Для этого рекомендуется внедрять дополнительные механизмы, которые могли бы определять момент, когда поток начинает установку, и в этот момент повышать приоритет для обработки данных.
Заключение
Таким образом, поддержание открытой трубы с отбрасыванием данных в отсутствие читателя можно реализовать с использованием неблокирующего режима dd
и устанавливая размер трубы с помощью Perl. Это обеспечивает стабильную работу rpicam-vid
и минимизирует потери данных при условии правильной настройки системы. Помните, что важно тщательно взвешивать риски потери важной информации, и по возможности избегать неблокирующего режима в критически важных приложениях.