Камера историй ВКонтакте
15 августа на VK Tech Talks | Android разработчик Олег Смирнов рассказал о видеоисториях, а именно о создании видеозаписи с эффектом Ping Pong — двухсекундным видео без звука, которое проигрывается вперёд-обратно несколько раз подряд.
Запись видео
1. Записываем простое видео
Чтобы разобрать что-то сложное, нужно начать с чего-то простого. Как же реализовать запись видео?
В Android есть три класса, позволяющие работать с видео: Camera, SurfaceView и MediaRecorder.
При помощи SurfaceView создаём основной блок, затем создаем камеру и сообщаем ей, чтобы она рисовала в SurfaceView. Теперь мы видим изображение.
Далее создаём MediaRecorder, показываем ему камеру и задаем настройки: запись звука будет идти прямо с микрофона, а видео — с камеры. Далее указываем, в какой файл будет вестись непосредственно запись.
Осталось настроить качество видео. Из настроек Android возьмём 720p и начнём запись.
Готово! Видео сделано, и его можно вставлять куда душе угодно. Но что делать с функциональностью такого рода?
В этой ситуации при помощи нейросети на лицо накладывается виртуальная маска. В разобранном примере мы обращались непосредственно к системе, а тут нужно найти лицо, что-то обработать. Одним словом — сложно.
Наша задача — создать Ping Pong видео.
Реализация Ping Pong
2. Создаём видеоэффект Ping Pong
Вначале получим видеокадры.
Задаём размер выходных данных, вычисляемый как 1280 × 720 × 12 / 8.
Если с 1280 × 720 всё понятно — задаётся качество 720p, то что же означает 12 × 8? Олег подробно объяснил.
Существует такой формат хранения пикселей в памяти — NV21, один из подформатов YUV. Почему же используется YUV, а не привычный всем RGB?
Как известно, пиксель на экране состоит из трёх цветов: красного, зелёного и синего. При смешивании они создают любой другой цвет.
Глаз работает так, что яркостную составляющую он воспринимает намного лучше, чем цветовую. При помощи формул RGB разбили на яркостную и два цветоразностных компонента.
В формате RGB каждый цвет занимает 1 байт, то есть чтобы сохранить пиксель, нам потребуется 3 байта. А в формате YUV пиксели группируются по 4, и храним две общие цветоразностные составляющие, отдельно от каждого из них их яркостную составляющую. Все форматы видео работают с форматом YUV и это никак не влияет на качество.
Проведём подсчёты: один пиксель в RGB формате займёт 24 бита, а один пиксель формата YUV — 12 бит. Только основываясь на том, что яркость человеческий глаз воспринимает лучше, чем цвет, мы экономим половину памяти.
После всего этого мы выделяем callback камере, куда будут присылаться фреймы.
Камере выставляется превью callback, куда в массив ByteArray попадает сырой кадр.
На этом этапе этапе с полученной информацией можно уже что-то сделать: распознать QR-код, наложить маску, проделать множество других действий, однако нас интересует только сохранение кадров.
Хранение кадров
Чтобы создать эффект пинг-понга, мы воспользуемся классом MemoryFile. Благодаря ему все данные по возможности сохраняются в памяти, а если её не хватает он скидывает их на диск. Работает он в памяти системы.
3. Запись кадров в файл
Файлы нужно сжать при помощи кодека. Для справки: 12 секунд несжатого видео будут занимать 400 Мб памяти, а сжатого — 4 Мб.
Для работы с кодеком операционная система предоставляет нам два класса — MediaCodec и MediaMuxer.
MediaCodec — интерфейс для всех кодеков, которые есть в операционной системе. С его помощью можно создать любой кодек для сжатия видео или аудио.
MediaMuxer нужен для того, чтобы сжатые кадры записались в файл.
Напишем энкодер. Работать будем с кодеком H.264.
Далее настроим качество видео, битрейт и фреймрейт, настроим цифровой формат: YUV — это важно, тут мы работаем в том же цифровом формате, что и с камерой.
Затем создаём кодек с перечисленными настройками.
Следующим шагом создаём MediaMuxer.
Также нужно добавить один трек, так как пинг-понг — видео без звука. Если бы мы работали со звуком, мы бы добавили второй трек.
Формат выходного контейнера — MP4.
Теперь мы подошли к основному циклу.
Вначале запускаем энкодер, затем происходит операция кодирования, а в конце мы должны остановить энкодер и муксер.
Разберём более детально сам цикл. Он повторяется три раза. Ровно столько повторений у пинг-понга.
Создаём условие: в какую сторону мы идём? Если в прямом направлении — то индексы увеличиваются, а если в обратную — индексы уменьшаются, и в любом случае берём из массива один кадр.
Дальше подключаем функцию dequeInputBuffer, которая синхронно возвращает буфер, но она может вернуть отрицательный индекс в том случае, когда энкодер чем-то занят.
Получаем индекс и буфер.
Далее копируем все кадры из YUV, вычисляем время.
Одной из главных функций является сжатие кадра. Этим займётся энкодер.
Затем нужно дождаться когда энкодер вернёт сжатый кадр. В этом поможет функция dequeOutputBuffer.
Далее сообщаем муксеру, чтобы тот записал сжатые данные в файл и освобождаем энкодер. Выполняем цикл трижды. Готово!
Здесь опущено много ситуаций, что может пойти не так на каждом девайсе, но задачей спикера было предоставить информацию о том, как работать с кодеками и как стартовать.
Спасибо за прочтение! Запись выступления можно посмотреть ниже.
The Brown Room — независимое интернет-издание про социальные сети и современные технологии
Автор: Илья Филиппов
Корректор: Арсений Метелев
Слайды и данные: VK Tech / Олег Смирнов
