Как уменьшить задержку при декодировании видео / avc MediaCodec

Я выполнил несколько простых моментов MoviePlayer.java в примере примера Grafika MediaCodec, работающем на Nexus 5. Я поставил оператор журнала в следующих местах:

На линии 203 непосредственно перед

decoder.queueInputBuffer 

На линии 244 после

 decoder.dequeueOutputBuffer 

Я сопоставил логические операторы с помощью presentationTimeUs .

Вот выдержка из logcat:

 01-29 10:56:43.295: I/Grafika(21286): queueInputBuffer index/pts, 2,0 01-29 10:56:43.305: I/Grafika(21286): queueInputBuffer index/pts, 0,33100 01-29 10:56:43.315: I/Grafika(21286): queueInputBuffer index/pts, 3,66466 01-29 10:56:43.325: I/Grafika(21286): queueInputBuffer index/pts, 1,99833 01-29 10:56:43.325: I/Grafika(21286): queueInputBuffer index/pts, 2,133200 01-29 10:56:43.335: I/Grafika(21286): queueInputBuffer index/pts, 0,166566 01-29 10:56:43.345: I/ATSParser(21286): discontinuity on stream pid 0x1011 01-29 10:56:43.345: I/ATSParser(21286): discontinuity on stream pid 0x1100 01-29 10:56:43.345: I/Grafika(21286): queueInputBuffer index/pts, 3,199933 01-29 10:56:43.345: I/Grafika(21286): dequeueOutputBuffer index/pts, 7,0 01-29 10:56:43.345: I/Grafika(21286): queueInputBuffer index/pts, 1,300033 01-29 10:56:43.355: I/Grafika(21286): dequeueOutputBuffer index/pts, 6,33100 01-29 10:56:43.385: I/Grafika(21286): queueInputBuffer index/pts, 2,333400 01-29 10:56:43.385: I/Grafika(21286): dequeueOutputBuffer index/pts, 5,66466 01-29 10:56:43.415: I/Grafika(21286): queueInputBuffer index/pts, 0,366766 01-29 10:56:43.415: I/Grafika(21286): dequeueOutputBuffer index/pts, 4,99833 01-29 10:56:43.445: I/Grafika(21286): queueInputBuffer index/pts, 3,400133 01-29 10:56:43.445: I/Grafika(21286): dequeueOutputBuffer index/pts, 3,133200 

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

Есть ли способ уменьшить эту задержку?

Я думаю, что вы видите некоторые эффекты, уникальные для первого кадра. Я повторил ваш эксперимент с дальнейшим добавлением принудительного doRender = false вокруг строки 244, чтобы избежать вызовов сна, используемых для управления частотой выходных кадров. Я вижу:

 01-29 14:05:36.552 9115 9224 I Grafika : queueInputBuffer index/pts, 2,0 01-29 14:05:36.562 9115 9224 I Grafika : queueInputBuffer index/pts, 0,66655 01-29 14:05:36.572 9115 9224 I Grafika : queueInputBuffer index/pts, 3,133288 01-29 14:05:36.582 9115 9224 I Grafika : queueInputBuffer index/pts, 1,199955 01-29 14:05:36.602 9115 9224 I Grafika : dequeueOutputBuffer index/pts, 4,0 01-29 14:05:36.602 9115 9224 I Grafika : dequeueOutputBuffer index/pts, 3,66655 01-29 14:05:36.602 9115 9224 I Grafika : dequeueOutputBuffer index/pts, 2,133288 01-29 14:05:36.612 9115 9224 I Grafika : dequeueOutputBuffer index/pts, 4,199955 

(Посторонние линии удалены для ясности.) Это подтверждает ваши результаты. Заметим, что, несмотря на задержку 50 мс между входом и выходом для pts = 0, последующие выходные кадры были доступны почти мгновенно. Видео, которое я использовал, было «camera-test.mp4» (выход камеры 720p).

Чтобы понять, почему это происходит, взгляните на другой материал в журнале и где он появится. Начиная с первой queueInputBuffer журнала очереди queueInputBuffer , подсчитайте количество журналов, которые появляются между этой и первой dequeueOutputBuffer . Я считаю около 60 строк вывода от OMX-VDEC-1080P на шахте. Теперь подсчитайте количество строк OMX-VDEC после появления выходных буферов. Я не вижу ничего, пока видео не закончится.

Видеодекодер явно откладывает некоторую дорогостоящую инициализацию до тех пор, пока не будут доступны данные. Итак, следующий вопрос: сколько данных нужно? Я добавил спальный сон 500 мс после отправки второго кадра (pts == 66633). Результат: представлены два кадра, пауза 500 мс, два кадра, большая стопка журналов OMX-VDEC. Похоже, что декодер хочет несколько кадров, прежде чем он начнет.

Это говорит о том, что мы можем уменьшить задержку запуска, быстро загрузив первые несколько кадров. Чтобы проверить это, я изменил TIMEOUT_USEC на ноль, чтобы он быстро реагировал, но записывал CPU. Новый выход журнала (ваши журналы, без сна, без рендеринга):

 01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 0,0 01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 2,66633 01-29 14:29:04.542 10560 10599 I Grafika : queueInputBuffer index/pts, 3,133288 ... 01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 4,0 01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 3,66633 01-29 14:29:04.572 10560 10599 I Grafika : dequeueOutputBuffer index/pts, 2,133288 

Благодаря быстрой загрузке исходных кадров мы сократили начальную задержку с 50 мс до 30 мс.

(Обратите внимание, как все метки времени заканчиваются на «2»? Таймер, используемый для времени журнала, кажется, округляется до ближайших 10 мс, поэтому фактическая временная дельта может немного отличаться.)

Причина, по которой мы медленно загружаем исходные кадры, заключается в том, что мы пытаемся вывести выходной сигнал из декодера после отправки каждого входного буфера, ожидая 10 мс для вывода, который никогда не появляется. Моя первоначальная мысль заключается в том, что мы хотим, чтобы вы dequeueInputBuffer() тайм-аута либо для dequeueInputBuffer() либо для dequeueOutputBuffer() , но не для обоих – возможно, сначала используйте тайм-аут на входе и быстрый опрос для вывода, затем переключитесь на таймаут на Когда заканчивается вход в канал. (В этом случае начальный тайм-аут для ввода может быть -1, поскольку мы знаем, что ничего не произойдет до тех пор, пока первый буфер ввода не будет поставлен в очередь.)

Я не знаю, есть ли способ уменьшить латентность дальше.