Arhn - архитектура программирования

MediaCodec KEY_FRAME_RATE, кажется, игнорируется

Я пытаюсь изменить источник для записи экрана в Android 4.4 и снизить захваченную частоту кадров, но независимо от того, какое значение я ввел:

format->setFloat("frame-rate", 5);

результат всегда один и тот же (очень высокая частота кадров)

Кодировщик игнорирует это свойство? как я могу контролировать частоту кадров?


  • Похоже, что это значение используется только для записи некоторой информации заголовка в результирующий файл mp4, а не для управления скоростью захвата. Для этого вам придется соответствующим образом изменить цикл кодировщика и отбросить кадры. 12.03.2014

Ответы:


1

Значение frame-rate не игнорируется, но оно не делает того, что вы хотите.

Комбинация frame-rate и i-frame-interval определяет, как часто I-кадры (также называемые «кадрами синхронизации») появляются в закодированном выводе. Значение частоты кадров также может играть роль в достижении цели bitrate на некоторых устройствах, но я не уверен в этом (см., например, этот пост).

Кодер MediaCodec не пропускает кадры. Если вы хотите уменьшить частоту кадров, вы должны сделать это, отправив на него меньше кадров.

Команда screenrecord не производит "выборку" экрана с фиксированной частотой кадров. Вместо этого каждый кадр, который он получает от компоновщика поверхности (SurfaceFlinger), отправляется кодировщику с соответствующей меткой времени. Если запись экрана получает 60 кадров в секунду, у вас будет вывод 60 кадров в секунду. Если он получает 10 кадров в быстрой последовательности, за которыми следует ничего в течение 5 секунд, а затем еще пару, вы получите именно это в выходном файле.

Вы можете изменить screenrecord для пропуска кадров, но вы должны быть немного осторожны. Если вы попытаетесь уменьшить максимальную частоту кадров с 60 кадров в секунду до 30 кадров в секунду, пропуская каждый второй кадр, вы рискуете, что в последовательности «кадр0 — кадр1 — длинная_пауза — кадр2» вы пропустите кадр1, а видео будет удерживаться на кадре0. вместо этого показывает не совсем полную анимацию. Таким образом, вам нужно буферизовать кадр, а затем закодировать или отбросить кадр N-1, если разница во времени представления между ним и кадром N составляет ~ 17 мс.

Хитрость заключается в том, что screenrecord в режиме работы по умолчанию направляет кадры в кодировщик, не касаясь их, поэтому все, что вы видите, это закодированный вывод. Вы не можете произвольно отбрасывать отдельные кадры закодированных данных, поэтому вы действительно хотите, чтобы кодировщик не видел их в первую очередь. Если вы используете источники screenrecord v1.1, вы можете использовать режим "наложения", используемый для --bugreport, чтобы пусть кадры проходят через screenrecord на пути к кодировщику.

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

Обновление: для примера того, как это сделать грубо, добавьте это в processFrame_l():

     int64_t droppedFrames = 0;
+    {
+        static int flipflop = 0;
+        flipflop = 1 - flipflop;
+        if (flipflop) {
+            printf("dropping frame %lld\n", frameNumber);
+            return;
+        }
+    }

     if (mLastFrameNumber > 0) {

Обратите внимание, что это происходит после updateTexImage(), который получает следующий буфер, и пропускает вызов swapBuffers(), который отправляет буфер видеокодеру.

12.03.2014
  • Я пытаюсь использовать screenrecord v 1.1 для установки частоты кадров, но безуспешно, если я просто пропускаю кадры в threadLoop, это не работает. как я могу успешно пропускать кадры? 26.03.2014
  • и если я добавляю сон в threadLoop, это замедляет работу устройства 26.03.2014
  • ваш патч работает отлично, но у него проблема с последним кадром. когда кадры не приходят. последний кадр не захвачен. могу ли я принудительно повторить последний кадр, если кадры не приходят? 26.03.2014
  • Это проблема, о которой я говорил в своем ответе (вы должны быть немного осторожны). Я не знаю простого способа сделать это правильно. Вы можете вызвать updateTexImage(), когда ничего нового не доступно, и он просто снова получит предыдущий кадр, поэтому вам может просто понадобиться отключить таймер, когда новые кадры не поступят, и отправить предыдущий кадр, если его не было. 26.03.2014
  • идея таймера звучит так, как будто она может работать, не могли бы вы помочь мне с примером кода? Кстати, здесь будет работать KEY_REPEAT_PREVIOUS_FRAME_AFTER? 26.03.2014
  • Могу ли я попросить производителя снова нажать на экран? 26.03.2014
  • Кодер увидит все, что было нарисовано до swapBuffers(). Код в processFrame_l() фиксирует новый кадр, визуализирует его и меняет местами. Вы можете разделить это, изменив основной цикл так, чтобы он вызывал updateTexImage() всякий раз, когда доступен новый кадр, но ничего не рисовал; затем каждые 33,3 мс вы просыпаетесь и выполняете рендеринг + своп. Это даст видео с постоянной частотой 30 кадров в секунду, повторно отправляя один и тот же кадр, когда система простаивает. Если вы этого не хотите, вы можете добавить проверку отправки этого кадра, которая сбрасывается при поступлении нового кадра. 26.03.2014
  • постоянная подача каждые 33 мс звучит хорошо, но я не совсем понимаю, что вы предложили. если новые кадры не поступают, и я вызываю swapBuffer, он останется с последним кадром? что именно делает swapbuffer? 27.03.2014
  • Думайте о swapBuffers() как о передаче всего, что я только что нарисовал, в видеокодер. Если вы просто вызовете swapBuffers(), ничего не нарисовав, вы будете несчастны. updateTexImage() означает взять все самое последнее, что мы получили от компоновщика, и поместить его в текстуру GLES. Итак, если вы посмотрите на processFrame_l(), вы увидите, что он захватывает новый кадр (или старый кадр, если ничего нового не появилось), отображает его с некоторым текстом, а затем отправляет его видеокодеру. 27.03.2014
  • как обычно ваше предложение отлично работает. Я добавил поток, который будит чрезмерно в соответствии с требуемой частотой кадров. таким образом я не теряю последний кадр. но я думал, что кодировщик улучшит вывод, если будет получать один и тот же кадр снова и снова (при использовании низкого битрейта), но это не так. любой способ сказать ему сделать это? 27.03.2014
  • Я не уверен, что вы подразумеваете под улучшением вывода. 27.03.2014
  • под улучшенным я подразумеваю менее пиксельный (как в x264, когда изменения между кадрами малы, кодировщик сверхурочно генерирует менее пиксельный вывод) 27.03.2014
  • Это зависит от кодера H.264. Я не знаю, на какое поведение вы можете положиться там. 27.03.2014
  • хорошо, вы знаете, как я могу отключить изменение поворота во время выполнения, то есть я хочу, чтобы захваченные кадры оставались прежними (собственная ориентация), даже если устройство повернуто 27.03.2014
  • Я не знаю, есть ли способ сделать это с виртуальным дисплеем — все проходит через проекцию дисплея, установленную setDisplayProjection(). Вы можете отключить автоматическое вращение устройства в настройках, но я не знаю, подойдет ли вам этот вариант. 27.03.2014
  • может быть, я могу вращать кадры в оверлее? увеличит ли это время обработки? Как я могу это сделать? 28.03.2014
  • Смена ориентации обнаруживается записью экрана через опрос (поэтому работает не совсем корректно). Вы можете увидеть вызов setDisplayProjection() в runEncoder(). Это должно быть простым вопросом передачи правильного набора аргументов в SurfaceComposerClient::setDisplayProjection(). Это недокументированный частный API, поэтому единственная информация об этом методе находится в заголовке: android.googlesource.com/platform/frameworks/native/+/ 28.03.2014
  • но setDisplayProjection не переключает высоту и ширину захвата? проблема в том, что я передаю закодированный вывод через rtp, и плеер не знает об изменении размеров. разве вращение кадров в оверлее не решит эту проблему? 28.03.2014
  • Попробуйте удалить изменения проекции дисплея в runEncoder() и посмотрите, что произойдет. Когда вы поворачиваете устройство, выход будет отключен. SurfaceFlinger делает композицию в новой ориентации, так что вы должны следовать. Вам необходимо обновить проекцию, чтобы охватить новые размеры виртуального дисплея. Поскольку вам уже нужно предоставить проекцию дисплея, вы также можете повернуть ее и там. 28.03.2014
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..