Errol
*******
*******, ******* *******
*******
******* https://danalite.ru/post/1 *******
P кадры могут переиспользовать участки предыдущих кадров Мы не можем просто положить файл во входной буфер и ждать чуда Фрагментный шейдер отвечающий за окраску непосредственно пикселей в нашем случае ещё проще вертексного К сожалению в OpenGL даже такую простую задачу выполнить не так легко На слабых устройствах вместо плавного проигрывания как у коллег на iOS мы получили слайд шоу Удалось ли нам выиграть по времени Одной из важных для нас сущностей является MediaCodec открывающий доступ к кодекам которые поддерживает девайс Внимательный читатель уже догадался что наивное решение использовать обычный плеер для доступа к произвольным кадрам рассыпается столкнувшись с P и B кадрами видео У MediaMetadataRetriever довольно таки простое API Но сначала Surface нужно создать Просмотр сорcов MediaMetadataRetriever показал что в чём кроется возможная причина тормозов на каждый вызов getFrameAtIndex создаётся новый декодер и отматывается на нужный кадр Все ещё долго распаковывается B кадры могут использовать участки как предыдущих так и следующих кадров На последней фотографии строительная тачка 665 литров укомплектована бескамерным колесом Но отснять панораму лишь полдела В репозитории с семплами работы с Low Level Media API https github com google grafika есть неплохие примеры того как создать off screen Surface и пара утильных классов которые можно забрать к себе в проект И тогда мы достали из нашего Гиперкуба Nexus 6 Проще всего этот механизм объяснить на MPEG 7 Под капотом его обычно создают TextureView и SurfaceView но нам не нужно отображать этот Surface поэтому тут мы сэкономим и создадим его сами Конечно у нашего решения есть и минусы Для того чтобы кодек мог распаковать наше видео он должен узнать множество параметров формат битрейт в каком разрешении был записан видеофайл Панорама автомобиля не то же самое что панорама пейзажа В нашем случае вершины остаются на месте поэтому мы их передаём как есть в gl Position Давайте посмотрим что же получилось в результате Но это не очень оправдано так как возросла бы нагрузка на сеть да и не все девайсы поддерживают эти кодеки I кадры ещё называют ключевыми в них не используется темпоральное сжатие поэтому они являются опорными для остальных видов кадров Именно они служат палитрой для тех кусков изображения которые перемещаются по кадру без особых изменений На распаковку уходило три секунды Обломал он и нас результат в 85 секунд на извлечение был просто ужасен Так почему же наивное решение тормозит Самое интересное в ней картинка с состояниями которые может принимать класс кодека Один из них EglCore прячущий под капот всякие OpenGL флажочки и за пару методов создающий Surface со всеми нужными настройками Необходимо было его замерить чтобы перед просмотром пользователи не наблюдали грустную крутилку панорам Прикрутив к нему простейший GestureDetector запустили наш прототип на тестовой панораме и Затем её предстоит воспроизвести причём сделать это одинаково хорошо и на топовом флагмане и на бюджетном смартфоне Каждый ряд этой матрицы описывает координаты некоторой точки Если внимательно изучить множество видео можно заметить что некоторые участки например красивый фон позади головы персонажа остаются неподвижными или перемещаются в кадре практически без изменений Тачки строительные с литым колесом от 6655 руб Документация на https developers android com подробно описывает работу этого кодека Да и пространство для оптимизаций стремительно сокращается и приближается к красной зоне по Шипилеву поэтому на данном шаге остановились и пустили решение в релиз А как бы вы расставили приоритеты в этой задаче А как мы знаем произвольный доступ к кадрам не очень быстрая штука На этой схеме видно что после вызова decoder configure наш кодек переходит в состояние configured и ждёт пока мы запустим его и начнём использовать Несмотря на то что такие методы действительно используются для сжатия видео это только первый этап Сначала нужно передать шейдерам данные которые мы для них подготовили координаты вертексов нашего прямоугольника и текстуру в которую мы будем читать байты На свежих благодаря поддержке Low Latency Media API всё выглядело довольно прилично Помните что 675 кадров в джипеге занимают приличное по меркам мобильных устройств место Вместо того чтобы последовательно смотреть кадры один за другим мы пытаемся получить произвольный кадр так пользователь сможет крутить автомобиль пальцем вперёд назад как ему вздумается По окончании данного периода счет фактура выставляться не будет Панорамой автомобиля мы называем 8D фото машины которое можно прокрутить и рассмотреть со всех сторон а не только с удачных ракурсов выбранных продавцом автомобиля Теперь когда мы вытащили форматы из файла и натравили на них наш кодек можем приступать к распаковке Ух сколько разработчиков он обломал перед выкаткой В нём используются три вида кадров I P и B кадры Поэтому при малейшем смещении придётся начинать процесс заново А в Low Level Media API есть удобный класс MediaExtractor который достанет всё необходимое за нас А вот вершины текстуры мы умножаем на матрицу которую получим из текстурной программы чтобы расположить текстуру на нашем прямоугольнике Мы загружаем наши байтики во входной буфер и получаем распакованные кадры в выходном Обычное видео чаще всего смотрят от начала и до конца изредка перематывая вперёд Вместо того чтобы записывать всю информацию об изображении фиксируем только какой участок какого кадра нам нужен на сколько и в каком направлении его переместить Для нас было важно чтобы в прекрасном будущем когда панорамами обзаведутся все объявления о продаже автомобилей приложение Авто ру не стало бы главным потребителем трафика на вашем смартфоне MediaCodec это сигнальный процессор с входным и выходным буферами Чтобы это сделать сырые данные кодека нужно превратить в битмап К счастью Android предоставляет доступ к низкоуровневым API для работы с медиафайлами При этом повторять точки чтобы закрыть треугольники совсем не обязательно Нам всего то нужно передать URI нашего видео и последовательно вычитать все фреймы в битмапу Мы просто отображаем текстуру в текущей точке и на этом всё Хотя бы шейдеры будут достаточно простыми сами по себе Но это компромисс между количеством передаваемых по сети данных и оверхедом на распаковку видео Замеры показывают что да и намного Особенно старые модели на которых мы и хотели починить производительность Но даже если бы мы пережали все кадры видео в популярный формат JPEG всё равно наш трёхсекундный видосик занимал бы около 86 МБ в зависимости от содержания кадров Это так называемая temporal redundancy которую можно устранить если кусочки из каких то участков кадра переиспользовать в других кадрах В приложении Авто ру есть такая фича панорамы автомобилей Для того чтобы сохранить низкий объём передаваемых по сети данных но при этом иметь хорошую скорость произвольного доступа к отдельным кадрам мы можем предварительно раскодировать всё видео в последовательность изображений и стоимость дженерик виагра спб
https://danalite.ru/post/1 уже по ним Она позволяет любому сделать 8D фото своей машины показать её во всей красе Для того чтобы их раскодировать нужно дойти до следующего I кадра Как это работает и как нашим коллегам из ML удалось уместить в смартфон то что раньше требовало целого ангара напичканного дорогостоящим оборудованием yaantonn уже рассказывал на Хабре Профайлер показывал сравнительно высокую задержку при доступе к кадрам и перемотке Тачка Скорпион с широким колесом от 6985 руб Время не очень хорошее но приемлемое Но теперь перед проигрыванием видео у нас появился этап распаковки Когда что то тормозит можно попытаться спуститься на уровень абстракции ниже и взять дело в свои руки Пришлось возвращаться к R D и думать как это улучшить Порадовавшись за коллег мы взяли стандартный для нас ExoPlayer который уже успешно использовали в нескольких местах приложения Чтобы распечатать схему сборки садовой тачки нужно скачать ее на свой компьютер кликнув на кнопку справа Хоть мы больше не передаём видео покадрово теперь мы распаковываем его в такие же кадры Раскодированные данные нужно как то использовать В этом нам поможет отображение кадров на Surface и копирование его пикселей в битмап C такими результатами можно жить Первое что приходит в голову в качестве алгоритма для сжатия видео применить старые трюки для сжатия изображений Суть вертексного шейдера производить манипуляции над вершинами и как то сопоставлять их с координатами текстур Даже для самой простой программы на OpenGL нужно два компонента вертексный и фрагментный шейдеры Как уже говорилось выше видео с панорамой проигрывается иначе чем обычное Заметим что нам нужно перечислять точки как бы зигзагом потому что OpenGL умеет рисовать только точки линии и треугольники Все фотографии сделаны с помощью оптики высокого разрешения кликните по ним для увеличения Предположение о том что переиспользование одного инстанса кодека и последовательная распаковка видео победит стандартный getFrameAtIndex оказалось более чем верным Когда то топ за свои деньги этот легендарный старичок теперь выполняет роль референса low end девайса Выше мы создали Surface передав ему некоторую текстурную программу с помощью которой можем отобразить наш кадр текстурой на прямоугольнике растянутом на весь экран Да вы и сами видите всё на гифке Чтобы зайти ещё дальше нам нужно перестать мыслить отдельными кадрами и попытаться переиспользовать информацию между двумя и более соседними кадрами