Все слышали про "эпилепсию", иногда натыкались на видео с пометкой "epilepsy warning". И вроде бы понятно, что это что-то связанное с резкими мерцаниями и переменами цветов, но так ли все просто на самом деле? На True Tech Hack одной из задач учатников стала "Адаптация фильмов для людей с особыми потребностями", а одной из таких потребностей стала возможность фильтровать или изменять сцены, которые могли бы вызвать эпилептические припадки. Именно о решении мною этой задачи я и хочу рассказать далее.
Попытка 0.5: А что вообще такое эта ваша "эпилепсия"
Для начала я решил разобраться с какой проблемой я вообще имею дело, поскольку опыта взаимодействия с людьми у которых есть такая проблема у меня не было, равно как и опыта взаимодействия с книжками по медицине. Первое что я узнал - эпилеписия бывает разная, очень разная, настолько разная, что в данном случае мы затрагиваем лишь одну ее вариацию - Photosensitive epilepsy (PSE), или же Фотосенситивная эпилепсия . По своей сути PSE характеризуется резкими приступами, вызываемыми зрительными стимулами, к которым относятся мигающий свет, регулярные узоры, или регулярные движущиеся модели. Именно прочитав такое описание мне и пришла в голову первая идея решения проблемы.
Попытки 1 и 2: А давайте резать все? Ну или почти все?
Первой мыслью стало порезать вообще все места в видео, где кадры рядом очень сильно отличаются друг от друга по цвету. Поначалу звучало вполне себе хорошо, но потом я решил посмотреть фильм... И понял, что таким методом я скорее сокращу хронометраж до 0, чем исправлю хоть какую-то проблему. Быстро решение перетекло в то, чтобы считать какую-то количественную "разницу" между соседними кадрами, и вырезать их только если она переходит через какую-то границу много раз подряд. Теперь это уже было похоже на то, что предложили некоторые из финалистов хакатона, однако мне захотелось пойти дальше.
Попытка 3: А есть же ffmpeg
Спустя какое-то время после начала работы над хакатоном, мой сонный мозг внезапно решил вспомнить про существование такой прекрасной утилиты, как ffmpeg. Как пишут сами разработчики - это "A complete, cross-platform solution to record, convert and stream audio and video", то есть по сути просто "комбайн" для перегонки одних видео в другие. Проведя небольшой поиск по сайту документации ffmpeg я обнаруживаю в ней такой фильтр как photosensivity. Собственно сам фильтр и был создан для того, чтобы убирать резкие изменения цвета в видео. По сути он берет и сглаживает кривую изменения цвета на каждом отдельном пикселе, в случае, если яркость изменяется со слишком высокой частотой, что в итоге дает плавный переход цвета на всем видео (например здесь). Спустя несколько минут подгонки команд у меня получилось что-то вроде вот такого:
ffmpeg -i video.mp4 -vf photosensitivity=30:0.6:20 out.mp4
Однако уже тогда я понимал, что такой простой командой все не ограничится, ведь на горизонте уже поджидал VoD контент.
VoD, MPEG-DASH и все-все-все: как помешать фильтровать контент нормально
Осознав, что использовать ffmpeg мне придется где-то между сервером и клиентом, я начал гуглить о том, как вообще работают плееры видео в современном мире. В итоге я узнал про VoD, или же Video on demand, что по сути своей является чем-то вроде парадигмы - пользователи получают видео, запрашивая его из каталога. Звучит просто, но вот реализации данной парадигмы бывают достаточно сложны. Думаю многие смотрят видео на различных видео хостингах, но задумывались ли вы, как получатеся, что вы можете смотреть видео не скачивая его полностью или даже сразу начав из середины? Относительно быстро в голову приходит идея сегментировать видео и отправлять его на клиента по частям, однако здесь можно столкнуться со сложностью: как сохранять максимально маленький объем сегментов? Одим из решений является технология MPEG-DASH, которая сейчас используется в огромном количестве видео плееров. Суть ее в том, что мы можем вынести все метаданные и описания формата в отдельный файл, а далее просто отправлять по сети непосредственно контент сегментов, чтобы склеивать их с форматом уже на клиенте.
Именно использование сегментированного видео и не позволяет применить решение на ffmpeg написанное выше, однако его можно модифицировать - ffmpeg умеет принимать на вход и выдавать на выход формат MPEG-DASH. После некоторых экспериметнов у меня получилась вот такая команда:
ffmpeg -filter_complex photosensitivity=30:0.6:20 -re -i "$URL" \
-f dash -seg_duration 1 -adaptation_sets id=0,streams=v id=1,streams=a \
-ldash 1 -preset veryfast file.mpd
Так я смог принимать на вход видео с ссылки на его mpd файл и выдавать на выход новое сегментированное видео, которое генерировалось "на лету". Последней проблемой оставалась необходимость как-то отдавать это видео клиенту.
Давайте уже начнем разрабатывать уже
По сути оставалось лишь разработать решение, которое могло бы быть неким прокси между клиентом, который хочет посмотреть видео, и сервером, у которого это видео есть. Решено было писать на Go, поскольку это мой основной язык и он дает хорошую производительность как раз в таких задачах. Сама архитектура решения не является чем-то особенным - принимаем на вход ссылку на видео, создаем поток под его обработку, отдаем ссылку на новое видео клиенту. В итоге взаимодействие между клиентом и прокси выглядело как-то так:
// Запрос
{
"stream_url": "https://dash.akamaized.net/dash264/TestCasesHD/2b..."
}
// Ответ
{
"new_url": "/stream/b1642bb4-e7fd-42ac-93dd-9334b4b74e35/file.mpd"
}
Потом был разработан еще и фронтенд, но он по сути был еще проще, пара ползунков и несколько полей ввода рядом с плеером:
Затем для деплоя и тестов решение было упаковано в docker-контейнеры и обвешано docker-compose и kubernetes манифестами. Была написана мнимальная документация для запуска и тестирования.
Немного итогов
Мы стали финалистами хакатона, сходили на конференцию, на которой немного запороли защиту. Никаких призов, кроме минимального набора мерча, нам не дали, но в целом сами виноваты. Хакатон дал мне много опыта и новых знаний, которые помогают мне развиваться дальше. Собственно ссылка на решение, если кто-то захочет его потыкать.
solderman
В заголовке описка? Или так задумано :)