Вместе с выходом Android 4.1 был представлен новый Меdia API Поначалу данное событие мною воспринялось как многообещающее, но после попыток раскурить его, энтузиазма поубавилось. И так попорядку.
Видео
На первых парах API выглядит достаточно простым и стройным. Полистав слайды, кажется, что демо можно накидать за пару часов. Это почти правда. Как выяснилось потом, на слайдах нет самых интересных кусков кода. Поэтому к некоторым вещам приходилось идти империческим путем т.е. методом проб и ошибок. Загуглив, я лишь нашел javadoc и один юнит тест :). Негусто. Качнул исходники Android 4.1 и "грепнул" их - толку тоже мало. В общем самым полезным из всего найденного оказался юнит тест.
Абстрактный декодер
Напишим три класса: абстрактный декодер и два наследника. Один для видео другой для аудио. В наших примерах мы будем играть либо видео, либо аудио. Позже я объясню почему. А сейчас код:Класс представляет собой тред, который "молотит" до момента пока в очереди есть декодированные данные(иначе говоря есть что отрисовывать/проигрывать). Важный момент - это инициализации входного/выходного буфера, и их заполнение/вычитка - методы dequeueInput()/dequeueOutput(). На начальном этапе трудности и вопросы возникает именно здесь.
Видео декодер
Класс выглядить достаточно просто. Нам нужно передать ссылку на канву и декодер отрисует все за нас. Порадовало то, что не нужно заморачиваться со всякого рода мелочами: формат пиксела и т.д. Собственно код: Второй параметр метода releaseOutputBuffer(..., true) "говорит", что мы рендерим картинку средствами декодера.
Аудио декодер
Кода на пару строк больше - создаем аудио трэк, а в качестве канвы передаем null(ибо для звука в ней нет необходимости).
Проблема номер один - синхронизация
Если честно, не нашел вменяемого способа, как синхронизировать аудио/видео в рамках данной задачи. Опробовав разные методы, пришел к очень примитивному, но в то же время простому - синхронизация по внешнему таймеру. Есть некий тред, который "посылает сигнал" декодеру, передавая текущую временную метку. Декодер получив уведомление, "проверяет" есть ли в очереди пакет PTS(presentation time stamp) которого равен либо меньше текущего значения метки. Если есть, то дергаем processDecodedData(). Повторюсь, способ убогий и не обеспечивает должной точности синхронизации, но он работает и для простенького демо сойдет. Пример синхронизации аудио(для видео абсолютно тоже самое за одним лишь отличием - вмето AudioTrackDecoder создать экземпляр VideoTrackDecoder с доп. параметром surface):
Проблема номер два - MediaExtractor
Как говорит дока - сие есть демультиплексор. Задача которого извлекать сырые данные. Если мы хотим работать по отдельности только с аудио, либо только с видео, то вопросов нет. А вот если одновременно, например, проиграть видео файл, то парочку есть. Перед чтением необходимо вызвать метод selectTrack, указать номер трэка в контейнере с которым мы собираемся работать. Допустим есть два трэда - один аудио декодер, другой видео декодер. Есть экземпляр медиа экстрактора расшаренный между этими тредами. При такой схеме у меня дико падала производительность, видео было куцее, звук прерывистый. Второй вариант - создать отдельный медиа экстрактор для каждого трэда. Показал гораздо лучшие результаты чем первый, но с точки зрения здравого смысла он не верный. Т.к. ресурс один, а соединений два(одно соединение для видео, другое - для аудио).
Выводы
Плюсы:- Pure java - не нужно париться с NDK
- Относительно простой API
- Нет примеров
No comments:
Post a Comment