Как записать "круглое" видео в мессенджере "CONCORD".
Пишу крутой облачный мессенджер (мини-соцсеть) под пилотным названием CONCORD. Недавно для него реализовал новую функцию отправки видеосообщений в форме "круглого" видео. Теперь любой пользователь может отправить своему собеседнику модное селфи без каких-либо специальных настроек. Это круто и весело, дополняет общение личной эмоцией.
Чтобы отправить такое видео, достаточно открыть чат и выбрать в верхнем выпадающем меню пункт "Селфи видео". Далее откроется небольшое диалоговое окошко, где необходимо нажать и удерживать кнопку "Hold" - после этого начнется процесс записи видео с фронтальной камеры смартфона.
Длительность записи ограничена по времени, чтобы не перегружать смартфон ресурсоемким процессом. После истечения лимита или как только пользователь отпустит кнопку, видео отправится на бэкенд для дальнейшей обработки.
Особенности видеосообщений:
Лимит времени: Максимальная длительность одного ролика составляет 10 секунд.
Формат: Видео автоматически обрезается в форме круга и записывается на фронтальную камеру.
Воспроизведение: В чате такие ролики проигрываются один раз при нажатии "Play".
Способ 1
Бэкенд приводит видео к "квадратному" формату с соотношением сторон 1:1 и возвращает ссылку на него обратно в чат собеседникам.
Ниже пример выполнения преобразования видео в "квадратный" формат с помощью "ffmpeg":
ffmpeg -i input.mp4 -vf "crop=480:480:0:(ih-480)/2" -c:v libx264 -crf 23 -c:a copy output.mp4
Чтобы отобразить "квадратное" видео в виде круга на Java, я создаю шаблон (ViewOutlineProvider). Он принудительно задает графическому элементу (View) форму идеального круга фиксированного размера:
private static final ViewOutlineProvider CIRCLE_PROVIDER = new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { int sizeInPx = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 270, view.getResources().getDisplayMetrics() ); outline.setOval(0, 0, sizeInPx, sizeInPx); } };
Затем в холдере применяю описанный выше шаблон к TextureView и ImageView:
static class CircleVideoViewHolder extends BaseViewHolder { TextureView texture; ImageView preview; VidCrViewHolder(View v) { super(v); texture = v.findViewById(R.id.video_texture); preview = v.findViewById(R.id.video_preview); texture.setOutlineProvider(CIRCLE_PROVIDER); preview.setOutlineProvider(CIRCLE_PROVIDER); texture.setClipToOutline(true); preview.setClipToOutline(true); } }
В результате выполнения этого кода видео примет "круглый" вид.
Способ 2
Как показала практика, кодирование видео в "квадратный" формат на бэкенде невыгодно, так как оно занимает время и нагружает сервер. Без хорошей видеокарты, которая на бэкенде часто отсутствует, работать с видео накладно. Поэтому я пошел другим путём.
Так как камера смартфона снимает видео не в "квадратном" формате, я воспользовался матрицей преобразования, центрировал кадр и обрезал лишние стороны.
//фиксированный размер видео с камеры смартфона: 480x640 float scaleY = h / (w * 480 / 640); Matrix matrix = new Matrix(); matrix.setScale(1.0f, scaleY, w / 2f, h / 2f); CircleVideoViewHolder vch = (CircleVideoViewHolder) holder; vch.texture.setTransform(matrix);
При таком подходе вся работа выполняется на стороне графического процессора (GPU) самого смартфона с максимальной скоростью. Это полностью избавляет от необходимости обрабатывать видео на бэкенде.
В результате получилась вот такая красота:

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


