ДИСКЛЕЙМЕР: Я не программист и не линуксоид, а лишь энтузиаст-копипастер, который делится тем, в чем смог разобраться. Поэтому, не исключено, что знающие специалисты некоторые моменты или формулировки могут счесть неправильными или нелепыми. Подача данного материала предполагает, что вы обладаете базовым уровнем знаний Liquidsoap и Nodejs. Версия Liquidsoap в этом примере 2.2.1. Как бы то ни было, следующие статьи и туториалы помогут вам разобраться: Создание 24/7 радио-стрима (Liquidsoap) Создание 24/7 радио-стрима ч.2 (Liquidsoap)
Благодаря дискуссиям на Github: https://github.com/savonet/liquidsoap/issues/3202 и https://github.com/savonet/liquidsoap/discussions/3402, наконец-то, понял как можно “легально” менять уже наложенные изображения/анимации на видеоисточник. Что в конечном итоге привело меня к добавлению на свой стрим всплывающего по таймеру анимационного баннера (за идею спасибо get_ked‘у). Но данный алгоритм вполне применим и для прочих случаев, вплоть до создания импровизированных “алертов” при условии, что есть бот, который ловит события о донате/подписке/рейде и так далее. Или для смены оверлея в зависимости от времени суток. В общем, этому приему можно найти массу применений.
Идея заключается в том, что на видеоисточник, с помощью оператора video.add_image
накладывается пустой прозрачный png-файл (не в буквальном смысле пустой файл, а все же в него надо добавить альфа-канал, т.е. прозрачный слой и размеры), который в определенный интервал времени подменяется другим файлом-баннером. Анимация проигрывает отведенное ей время и после чего video.add_image
переключается обратно на прозрачный png.
В качестве анимации можно использовать формат gif.
Чтобы автоматизировать переключение файлов и задать интервалы я решил использовать простейший js-скрипт для nodejs. Во-первых, потому что мне не хватает понимания как это можно реализовать внутри Liquidsoap (застопорился на моменте с тем как добавить таймаут выполнения функции), а во-вторых, как мне кажется, так проще редактировать интервалы срабатывания без перезапуска самого Liquidsoap (если не морочиться с interactive.values
).
Liquidsoap
В качестве основного видеоисточника будет выступать плейлист с видеофайлами – videos
. Понадобятся пустое изображение-заглушка – blank.png
и анимация – banner.gif
(предположим, длительностью в 10 секунд), у которых будет общая переменная banner_file
– она в качестве параметра назначается оператору video.add_image
.
Для banner_file
будет использоваться оператор ref
(“создает ссылку, т.е. значение, которое может быть изменено”). В качестве первоначального значения ref
устанавливается blank.png
, чтобы при запуске Liquidsoap на экране не болтался баннер.
videos = mksafe(playlist("/path/to/mp4"))
banner_file = ref("/path/to/blank.png")
videos = video.add_image(file=banner_file, x=0, y=0, height=100, width=1280, videos)
Ну а дальше, уже моя устоявшаяся практика: file.watch
для запуска функции и appendFile
😀
videos = mksafe(playlist("/path/to/mp4"))
banner_file = ref("/path/to/blank.png")
videos = video.add_image(file=banner_file, x=0, y=0, height=100, width=1280, videos)
file.watch("/path/to/banner_on", {banner_file.set("/path/to/banner.gif")})
file.watch("/path/to/banner_off", {banner_file.set("/path/to/blank.png")})
При изменениях в файле banner_on
– в banner_file
устанавливается путь к banner.gif
При изменениях в файле banner_off
– в banner_file
устанавливается путь к blank.png
NodeJS
Теперь остается создать js-скрипт и запустить его:
const fs = require('fs');
const banner_on = '/path/to/banner_on';
const banner_off = '/path/to/banner_on';
function banner_on_func() {
//дописываем в конец файла banner_on символ "i", тем самым включаем показ баннера
fs.appendFile(banner_on_func, `i`, (err) => {
if (err) {
console.log(err);
}
});
//пауза в выполнении скрипта, равна длительности анимации баннера (10 сек)
setTimeout(banner_off_func, 10000);
function banner_off_func () {
//дописываем в конец файла banner_off символ "x", тем самым выключаем показ баннера
fs.appendFile(banner_off, `x`, (err) => {
if (err) {
console.log(err);
}
});
}
}
//интервал показа баннера (каждые 10 минут)
setInterval(banner_on_func, 60000 * 10);
Пара нюансов
Я больше всего переживал за тайминги: насколько точно интервалы выполнения функции в js-скрипте будут совпадать с концом анимации. На удивление все работает очень четко, лишь иногда бывает, что анимация прерывается чуть раньше. Но, предполагаю, что если анимация будет отличаться по количеству кадров от стрима (например, стрим 30fps – анимация 20fps), то тут придется “примеряться” по таймингам, чтобы попасть в нужный интервал. Но никто не запрещает в конец анимации добавить лишнюю секунду с полной прозрачностью. Тогда целиться будет проще.
Из приятного: пока в video.add_image
находится путь к “пустышке”, то анимацию с баннером можно поменять на другой файл (но с тем же названием). Не уверен, что это будет работать в долгосрочной перспективе и с частой подменой, но в случае необходимости можно так делать не прибегая к перезапуску Liquidsoap.