Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время

Услуги
Веб-разработка
Разработка мобильных приложений
Автоматизация бизнеса
UX/UI дизайн
Техподдержка интернет-проектов 24/7 по SLA
Digital-продвижение
Обработка данных
Наша работа
Кейсы
Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время

Загрузка и обработка видеофайлов посредством minio, rest и ffmpeg

< Все публикации
ноябрь 2024 ~ 8 минИсточник Habr
Денис Павлов

Всем привет! Меня зовут Павлов Денис, я .NET backend разработчик в компании DD Planet.

В статье расскажу о реализации загрузки и обработки видеофайлов с использованием minio в качестве хранения и FFmpeg для работы с видео.

Предпосылки реализации видео или боль пользователей

Один из наших проектов прошел путь от стартапа до большой экосистемы со своими направлениями в разных сферах. Одно из таких направлений - social. Первой крупной фичей были чаты, позволяющие общаться не только P2P, но и со своими соседями, а позднее создавать собственные чаты с людьми со всей России. Второй фичей стали сообщества, начиная от домовых и заканчивая сообществами по интересам, объединяющие людей с общими темами. И третья составляющая этого направления - посты в ленте, которые представляли собой новости, поиск исполнителей или потерянных вещей, а также смешных постов с котиками.

С ростом аудитории и появлением новых фич все чаще пользователи сталкивались с проблемой видео файлов. Отправить текст в новости или приложить к ней картинку не составляет труда, но с видео файлами все было сложнее. Загрузив видео, пользователь не мог его посмотреть в нашем приложении, что заставляло его скачивать видео и смотреть его локально на своем устройстве. Постоянно скачивать, засорять память на устройстве не могло нравится пользователям, все чаще в чатах всплывали сообщения и обращения в поддержку с предложением реализовать проигрыватель внутри приложения. Мы прислушались к нашей аудитории и фича видео была взята в работу.

Готовые интеграционные решения

Начали мы с рассмотрения готовых решений для видео. Среди таких оказались следующие два: api.video и cloudinary.

Api.video предлагает полностью настраиваемый API для управления всеми аспектами видео, включая кодирование и доставку. Он поддерживает все качества видео, от 360p до 4K, и не взимает плату за кодирование, даже для 4K видео. Вы можете управлять несколькими ключами API в безопасном и централизованном окружении. Это решение подходит как для стриминга видео в реальном времени, так и для видео по запросу. Примерная стоимость использования данного ресурса 33$ в месяц за возможность хранить 1000 минут видео и 15000 минут просмотров.

Основные особенности Api.video:

  • Полностью настраиваемый API: Api.video предлагает гибкий и мощный инструмент для управления видео, позволяющий настраивать каждый аспект процесса, от кодирования до доставки.
  • Быстрая интеграция: С помощью Api.video вы можете добавить видео и прямые трансляции в любой раздел вашего сайта и приложения всего за несколько минут.
  • Возможность загрузки видео конечными пользователями: Api.video позволяет конечным пользователям загружать видео и начинать прямые трансляции всего за несколько кликов, что значительно упрощает процесс для пользователей.
  • Поддержка различных видов контента: Api.video подходит для создания онлайн-обучения, коротких видео, платформ электронной коммерции и многого другого, благодаря своей универсальности и гибкости.
  • Безопасность и надежность: Api.video гарантирует безопасность и надежность ваших видео, предлагая защиту от несанкционированного доступа и поддерживая высокое качество потоковой передачи.

Cloudinary предлагает мощный набор API, включающий видеоплеер, кодирование и хранение, CDN и видеоаналитику. Он также предоставляет SDK для популярных фреймворков, встроенный CDN класса предприятия и предварительно построенные интеграции с популярными приложениями, такими как CMS и системы электронной коммерции. Cloudinary поддерживает все современные форматы видео и кодеки, упрощая процесс интеграции видеопроигрывателей в ваши веб-сайты и мобильные приложения.

Основные возможности Cloudinary:

  • Автоматическая оптимизация: Cloudinary автоматически оптимизирует медиафайлы для различных устройств и сетевых условий, улучшая скорость загрузки и качество изображений и видео.
  • Резиновое масштабирование: Позволяет изображениям и видео масштабироваться без потери качества, сохраняя четкость даже при увеличении размера.
  • Управление медиаконтентом: Cloudinary предлагает инструменты для управления медиаконтентом, включая загрузку, категоризацию, поиск и управление версиями медиафайлов.
  • Доставка контента: Платформа обеспечивает быструю и надежную доставку медиаконтента через глобальные CDN.
  • Интеграция с другими сервисами: Cloudinary может быть интегрирован с популярными платформами и CMS, такими как WordPress, Shopify, Magento, Adobe Experience Manager и другими, благодаря поддержке API и SDK.

Преимущества использования Cloudinary:

  • Улучшенное качество и скорость: Автоматическая оптимизация и резиновое масштабирование улучшают качество и скорость загрузки медиаконтента.
  • Гибкость и масштабируемость: Облачная модель позволяет легко масштабировать ресурсы в зависимости от потребностей вашего бизнеса.
  • Управление контентом: Инструменты для управления медиаконтентом упрощают процесс организации и поиска медиафайлов.
  • Безопасность: Cloudinary предлагает сильные меры безопасности для защиты ваших медиафайлов и пользовательских данных.

Cloudinary предлагает бесплатный план, который позволяет начинающим пользователям и небольшим проектам испытать основные функции платформы без каких-либо затрат. Этот план обычно ограничен в отношении объема хранения, трафика и других ресурсов, но идеально подходит для тестирования и небольшого уровня использования. Платные тарифы начинаются от 89$ за версию Plus и 224$ за версию Advanced.

Ниже представлен краткий список того, что предоставляют платные версии:

  • Резервное копирование в собственную корзину S3
  • Поиск на основе автоматической маркировки
  • Доступ к бесплатным и платным дополнениям
  • Ускоренная поддержка
  • Доступ к активам из разрешенного/запрещенного списка
  • Увеличение пропускной способности видео 2:1
  • Многопользовательское администрирование на основе ролей
  • Поддержка индивидуального домена (CNAME)
  • Дополнительный сертификат HTTPS SSL
  • Параметры аутентификации

После рассмотрения этих решений были сформированы следующие требования, которым должны были соответствовать готовые решения:

  1. Загрузка видео чанками и сохранение итогового видео на сервере
  2. Гибкая настройка генерации различных разрешений видео (240p, 360p и т.д.)
  3. Генерация превью изображения и блюра для фона
  4. Мониторинг процесса загрузки видео и его обработки для отображения пользователю общего прогресса

После анализа соответствий интеграционных решений требованиям и споров, было принято решение отказаться от их использования и создать свое по следующим причинам.

Оба инструмента предоставляют возможность загрузки видео больших размеров чанками, однако для сохранения их в нашем хранилище придется либо сначала склеивать чанки, сохранять полученное видео на сервере и далее передавать для дополнительной обработки, либо загружать чанки сразу в api.video, а потом дополнительным запросом скачивать полученное видео. В любом из двух вариантов мы видим лишнее действие, которое нужно избегать для оптимизации работ с видео.

Второй причиной является отсутствие мониторинга прогресса обработки видео. Пользователь сможет увидеть только прогресс загрузки видео на сервер и в случае возникновения ошибок во время обработки видео, он не сможет об этом узнать и будет ждать завершения обработки.

И последней самой главной причиной стало желание заказчика разработать данный модуль полностью с нуля. Это позволит в дальнейшем дорабатывать его в зависимости от пожеланий бизнеса и обратной связи пользователей системы функциями, которые отсутствуют в коробочных решениях.

Знакомство с FFmpeg. Проблемы, лицензии и использование

Для работы с видео мы решили использовать библиотеку FFmpeg. Кратко пробежимся по ее особенностям использования.

FFmpeg это кроссплатформенное решение для обработки, сжатия, редактирования видео, которое объединяет в себе более 300 видео/аудио/графических кодеков, декодеров, муксеров, демуксеров и различных фильтров. Оно выступает ядром для почти всех современных инструментов для обработки видео, позволяя не погружаться в видеообработку, а написать прослойку для работы с FFmpeg и использовать готовые методы. Кроме того взаимодействовать с FFmpeg можно напрямую, используя обычную консоль.

FFmpeg имеет два вида лицензии: LGPL и GPL.

Разница между лицензией GNU Lesser General Public License (LGPL) и GNU General Public License (GPL) заключается в том, как они применяются к программному обеспечению, включающему их. В контексте FFmpeg, эта разница имеет значение для разработчиков, которые хотят интегрировать FFmpeg в свои проекты.

GNU Lesser General Public License (LGPL)

  • LGPL позволяет связывать программное обеспечение с библиотеками, лицензированными под LGPL, даже если сама программа не распространяется под LGPL. Это означает, что разработчики могут использовать FFmpeg в своем приложении, не обязываясь раскрывать исходный код своего приложения.
  • FFmpeg по умолчанию лицензирован под LGPL, что позволяет его использовать в большинстве коммерческих и закрытых проектов без необходимости открывать исходный код приложения.

GNU General Public License (GPL)

  • GPL, в отличие от LGPL, требует, чтобы все программное обеспечение, включая библиотеки, было свободно распространяемым и модифицируемым. Если часть FFmpeg, лицензированная под GPL, используется в проекте, весь проект должен быть лицензирован под GPL.
  • Некоторые части FFmpeg, например, кодеки x264 и x265, могут быть лицензированы под GPL. Если эти части используются, то вся версия FFmpeg, которую вы используете, должна быть лицензирована под GPL.

FFmpeg сам по себе лицензирован под LGPL, но включает в себя компоненты, лицензированные под GPL. Если эти компоненты используются, то FFmpeg становится GPL-совместимым. Разработчики должны внимательно изучить конфигурацию FFmpeg и убедиться, что они не включили в свою сборку компоненты, лицензированные под GPL, если они не готовы соблюдать условия GPL.

Выбор между LGPL и GPL зависит от того, какие требования вы ставите к вашему проекту. Если вы хотите использовать FFmpeg в своем приложении без обязательства раскрывать исходный код приложения, лучше выбрать версию, лицензированную под LGPL. Если же вы готовы соблюдать условия GPL, включая открытость исходного кода, вы можете использовать версию FFmpeg, включающую GPL-компоненты.

Развертывание FFmpeg

Установить и подготовить к использованию FFmpeg на любой платформе не сложная задача, но в нашем случае с ним должна была работать утилита, отвечающая за обработку с видео. А так как она разворачивается в docker, то необходимо было, чтобы FFmpeg был настроен в этом образе.

По умолчанию скачать и настроить FFmpeg при сборке образа нельзя. Docker не позволяет скачать и разархивировать архив, выдавая ошибку. Поэтому было принято решение положить архив с LGPL версией FFmpeg в корень проекта, что позволит разархивировать его и настроить следующим набором команд:

1.png

Способы использования FFmpeg в .NET

Существует несколько способов, как использовать FFmpeg в .NET приложениях.

Первый способ это использование готовых сторонних библиотек, например, FFmpeg.NET. Такие библиотеки предоставляют удобный API, что значительно упрощает работу с FFmpeg, но может иметь ограничения по лицензированию.

Пример кода работы с библиотекой FFmpeg.NET:

2.png

Более сложным способом, предоставляющий максимальный контроль, но требующий больше затрат, является использование P/Invoke для вызова нативных функций FFmpeg.

3.png

Дополнительно можно написать обертки для нативных функций и работать с ними. Однако при использовании данного подхода следует учитывать следующие моменты:

  • Все необходимые структуры и типы определены правильно
  • Вручную выполнять марширование структур после вызова функций
  • Очистка ресурсов после использования
  • Следует учитывать возможные изменения в именах dll и номерах версий

И последний путь, по которому мы решили пойти, это использование класса Process и запуск FFmpeg команд через процесс. Это позволяет легко запускать любую команду FFmpeg без необходимости писать сложный код с использованием P/Invoke, более гибко формировать команду и ее параметры, в зависимости от требований.

Обертка ProcessRunner над Process

Для работы с FFmpeg через Process был написан ProcessRunner, это обертка для вызова команд, позволяющая как запускать команды, так и логировать работу процесса и ошибок при выполнении.

ProcessRunner содержит единственный метод RunCommandAsync, принимающий два параметра: ProcessCommand и ProcessEventHandler.

ProcessCommand - это класс, содержащий в себе все необходимое для запуска команды, а также функцию для идентификации ошибки при выполнении команды. Код данного класса представлен ниже:

4.png

ProcessEventHandler - это класс, позволяющий имплементировать логику обработки выполняемой команды процессом. Например, отслеживать выполнение операции и обновлять у пользователя прогресс обработки видео.

5.png

Ниже представлен код класса ProcessRunner:

6.png

Использование Minio для хранения видео

Следующим этапом нужно было определиться с хранилищем для видео. на проекте уже использовался minio для хранения различных файлов, так что мы не стали придумывать ничего нового и начали использовать minio и для видео.

Minio является объектным хранилищем, которое предоставляет дополнительный слой абстракции над файловой системой и хостом и позволяет работать с файлами через API.

Объектное хранилище может помочь в кейсах, когда необходимо хранить файлы пользователей в ваших приложениях, складывать статику и предоставлять доступ к ней через Ingress или хранить кеши вашего CI.

Настраивается хранилище одной командой, но лучше потратить чуть больше времени, чтобы получить полноценное готовое решение с шифрованием, защитой от повреждения данных. С последним у minio все впорядке и он может позволить себе пережить потерю половины дисков. Более подробно про настройку можно почитать в Quick start guide.

Хранение видео предполагается сначала в виде чанков с последующим склеиванием в исходное видео. Под чанки выделяется отдельный бакет с дополнительной настройкой TTL для чанков. Это позволяет периодически автоматически очищать место и удалять уже ненужные чанки. В свою очередь под каждое видео выделялся свой бакет. Сделано это было для того, чтобы хранить дополнительные файла (такие как метаданные, видео различных форматов) отдельно от файлов, принадлежащим другим видео.

7.png

Загрузка исходного видео

Прежде чем обрабатывать видео, необходимо его загрузить от клиента на сервер. Данный процесс складывается из двух этапов: инициализация загрузки и загрузка видео на сервер.

На первом шаге мобильное приложение делает REST запрос на инициализацию загрузки видео со следующей request-моделью:

8.png

В ответе метода сервер возвращает следующую модель:

9.png

Данный шаг позволяет гибко настраивать на сервере дальнейший процесс загрузки чанков за счет параметров ChunkSizeInBytes и TotalChunksCount, влияющие на количество чанков, на которое будет разбито исходное видео и как следствие на количество запросов, необходимых для загрузки всех чанков на сервер для дальнейшей обработки. Дополнительным плюсом является варьирование параметра ProgressPollingInterval, отвечающего за интервал между запросами на сервер для обновления данных загрузки и отображения процента загрузки на клиенте.

Вторым шагом клиент начинает параллельную загрузку чанков на сервер. Сам чанк передается байтами в теле POST запроса, а дополнительные данные в хедерах (идентификатор файла и номер чанка). Контент прочитывается из запроса в MemoryStream и передается в VideoService, где проходит валидацию и сохраняется в Minio.

10.png

В параллельной загрузке чанков есть один нюанс. Заключается он в том, что при сохранении последнего чанка мы должны опубликовать в очередь идентификатор файла, который слушает консьюмер, отвечающий за обработку видео, может произойти дублирование публикации сообщения в очередь. Проблема кроется в параллельной обработке последних чанков и проверке количества загруженных чанков.

Для решения данной проблемы был использован Redis и его атомарные операции. Когда выполняется условие, по которому количество загруженных равно общему количеству чанков, то пытаемся получить fileId из Redis, если fileId is null, то значит это последний чанк и отправляется сообщение в очередь и сохраняем fileId в Redis. При повторном выполнении метода для другого параллельного чанка метод уже найдет данный fileId и не будет повторно публиковать сообщение, исключая таким образом дублирование.

11.png

Кэширование vs стриминг

На данном этапе нужно было определиться, как клиентское приложение будет получать видео с backend’а.

Первый подход - это кэширование видео на стороне клиента. Приложение должно полностью скачать видео и только затем разрешать запускать его в плеере. К плюсам данного подхода можно отнести: скачивание файла и дальнейшее его воспроизведение не только внутри приложения, но и другими сторонними плеерами, а также возможность без задержек перемотка видео на любой тайминг. К очевидным минусам относится необходимость полностью скачать файл, для дальнейшей работы с ним. В случае, если произойдет ошибка или разрыв соединения, то придется начинать скачивание заново, что приведет к негативным отзывам пользователей.

Второй подход - это стриминг. Заключается он в непрерывной передачи аудио- или видеофайлов с сервера на клиент. Клиент не скачивает файл полностью, а начинает запрашивать отдельные фрагменты, что значительно ускоряет получение видео, а также его воспроизведение, видео не сохраняется на устройстве, что также экономит место. В дополнение к этому медиаплееры загружают несколько секунд потока, чтобы видео могло продолжить воспроизведение, если произошла кратковременная потеря соединения с интернетом. Этот процесс называется буферизацией видео.

В результате сравнения двух подходов мы решили использовать стриминг. Это позволит клиентам быстрее получать небольшие фрагменты видео и воспроизводить их с возможностью буферизации видео без затрат места на диске.

Сравнение протоколов передачи данных DASH и HLS

Также необходимо было определиться с протоколом передачи данных, а именно DASH или HLS.

MPEG-DASH — это метод потоковой передачи. DASH означает «Динамическая адаптивная потоковая передача через HTTP». Поскольку он основан на HTTP, любой исходный сервер можно настроить для обслуживания потоков MPEG-DASH.

MPEG-DASH похож на HLS, другой протокол потоковой передачи, в том, что он разбивает видео на более мелкие фрагменты и кодирует эти фрагменты с разными уровнями качества. Это позволяет транслировать видео с разными уровнями качества и переключаться в середине видео с одного уровня качества на другой.

HLS — еще один протокол потоковой передачи, широко используемый сегодня. MPEG-DASH и HLS во многом схожи. Оба протокола работают через HTTP, используют TCP в качестве транспортного протокола, разбивают видео на сегменты с сопровождающим индексным файлом и предлагают потоковую передачу с адаптивным битрейтом.

Однако эти два протокола различают несколько ключевых отличий:

  • Форматы кодирования: MPEG-DASH позволяет использовать любой стандарт кодирования. HLS, с другой стороны, требует использования H.264 или H.265.
  • Поддержка устройств: HLS — единственный формат, поддерживаемый устройствами Apple. iPhone, MacBook и другие продукты Apple не могут воспроизводить видео, передаваемое через MPEG-DASH.
  • Длина сегмента: это была большая разница между протоколами до 2016 года, когда длина сегмента по умолчанию для HLS составляла 10 секунд. Сегодня продолжительность HLS по умолчанию составляет 6 секунд, хотя ее можно изменить по умолчанию. Сегменты MPEG-DASH обычно имеют длину от 2 до 10 секунд, хотя оптимальная длина составляет 2–4 секунды.
  • Стандартизация: MPEG-DASH является международным стандартом. HLS был разработан Apple и не был опубликован в качестве международного стандарта, хотя и имеет широкую поддержку.

Взвесив все за и против решили остановиться на HLS.

Обработка видео с помощью FFmpeg

После успешной загрузки видео запускается процесс генерации видео. За это отвечает отдельная утилита, которая прослушивает очередь. При поступлении сообщения утилита проходит по этапам генерации видео и в случае ошибки, сообщение попадает обратно в конец очереди. При повторных обработках уже пройденных этапов они пропускаются.

Всего этапов 6:

  1. склеивание чанков и сохранение оригинального видео
  2. сохранение метаданных видео
  3. генерация превью изображений
  4. генерация блюр фона
  5. генерация видео низкого разрешения
  6. генерация видео высокого разрешения

Для реализации всех этапов был использован паттерн “Шаблонный метод”. Шаблонный метод определяет алгоритм, некоторые этапы которого делегируются подклассам, что позволяет им переопределить этапы, не меняя структуру алгоритма.

Шаблонный метод выглядит следующим образом: сначала происходит проверка этапа, был ли он уже выполнен и нужно ли его выполнять повторно, затем создается точка прогресса, запускается команда FFmpeg, обновляются метаданные и сохраняется сгенерированный файл и сохраняется прогресс обработки видео.

Ниже приведены команды FFmpeg для генерации метаданных, превью и видео:

Генерация метаданных

12.png

Генерация превью

13.png

Генерация блюр

14.png

Генерация видео

15.png

Мониторинг прогресса обработки видео на сервер

Одним из требований заказчика была возможность мониторить загрузку и обработку видео. Под этим подразумевалось не только сохранение контрольных точек обработки, но и отрисовка лоадера клиенту для информирования о том, на каком этапе и какая часть видео была обработана.

Процесс загрузки и обработки делился на несколько этапом и представлял собой некое дерево процессов.

Первый этап: загрузка и предварительная обработка видео. Он включает в себя загрузку видео фрагментов на сервер, сохранение оригинального видео, предварительную подготовку HLS средствами ffmpeg, генерация превью, генерация metadata, генерация блюра для видео. Именно данный этап отображается пользователю в виде лоадера. В процентном соотношении 40% отводится под отображение процесса загрузки видео фрагментов на сервер, а оставшиеся 60% под предварительную обработку видео и генерацию HLS в низком качестве. Дополнительно для разработчиков есть разделение предварительной обработки на более мелкие этапы мониторинга для более детальной картины обработки видео.

Второй этап: пост обработка видео на сервере. Он логирует обработку HLS высокого качества. Информацию о данном этапе пользователь уже не видит, так как он уже получил доступ к просмотру видео в низком качестве, а более лучшее качество генерируется в фоновом процессе.

Итоги

В результате нам удалось реализовать обработку видео файлов с помощью ffmpeg и хранилищем minio. В дальнейшем мы планируем собирать обратную связь, внедрять аналитику в фичу, прорабатывать и улучшать нашу систему.

Всем спасибо за внимание!


Связаться с нами
Форматы: jpg, png, xls, xlsx, doc, docx, pdf
Размер до 5 МБ
Нажимая кнопку «Отправить», вы принимаете правилами обработки персональных данных
Заявка отправлена

Спасибо за проявленный интерес к нашей компании, специалист свяжется с вами в ближайшее время