Несовместимое время выборки / время представления во время декодирования видео

Я пишу приложение, которое может кодировать видео по входу камеры и обрабатывать видео с помощью шагов декодирования-редактирования-кодирования. Для камеры я использую класс Camera, а не Intent для настройки параметров камеры. Затем я передаю кадры камеры кодировщику (MediaCodec в API 16) и мультиплексору (я использую мультиплексор ffmpeg, поскольку хочу работать на устройствах 4.1).

Я измеряю временной код кадров фотоаппарата по времени системы nano и выбираю подмножество кадров, чтобы соответствовать желаемому FPS (в настоящее время 15). Есть некоторые небольшие "шумы" в значениях времени, например (в мс): 0, 60718, 135246, 201049, ... вместо 0, 66000, 133000, 200000, ... .

После некоторых попыток правильно настроить мультиплексор (как этот вопрос), я могу создать видео (с кодеком AVC), которое может воспроизводиться видеопроигрывателем на устройствах. Скорость воспроизведения правильная, поэтому я думаю, что видео должно иметь правильную информацию о времени кадров.

Однако у меня возникла проблема, когда я пытаюсь декодировать видео для выполнения процесса редактирования видео. Я использую стандартные шаги извлечения / декодирования видео в качестве этих примеров, например:

int decode_input_index = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (decode_input_index >= 0)
{
    ByteBuffer decoder_input_buffer = decode_input_buffers[decode_input_index];
    int sample_size = extractor.readSampleData(decoder_input_buffer, 0);
    if (sample_size < 0)
    {
        decoder.queueInputBuffer(decode_input_index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
        is_decode_input_done = true;
    }
    else
    {
        long sample_time = extractor.getSampleTime();
        decoder.queueInputBuffer(decode_input_index, 0, sample_size, sample_time, 0);
        extractor.advance();
    }
}
else
{
    Log.v(TAG, "Decoder dequeueInputBuffer timed out! Try again later");
}

Время выборки из getSampleTime() имеет правильное значение при кодировании видео. (например, они точно 0, 60718, 135246, 201049,... у нас). Это также время представления на входе decoder.queueInputBuffer(). Когда декодер продолжает декодировать этот кадр, я получаю время кадра следующим образом:

int decode_output_index = decoder.dequeueOutputBuffer(decode_buffer_info, TIMEOUT_USEC);
switch (decode_output_index)
{
    ....
    (some negative-value flags in MediaCodec)
    ....
    default:
    {
        ByteBuffer decode_output_buffer = decode_output_buffers[decode_output_index];
        long ptime_us = decode_buffer_info.presentationTimeUs;
        boolean is_decode_EOS = ((decode_buffer_info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0);

        ....
    }
}

Я ожидаю установить ту же временную последовательность, что и на входе декодера, но я получаю много нулей из BufferInfo на выходе декодера. Содержимое декодированного кадра кажется правильным, но большинство значений времени представления равно 0. Только последние несколько кадров имеют правильное время представления.

Я тестирую весь тот же процесс на устройстве с Android 4.3 (даже с тем же мультиплексором ffmpeg, а не MediaMuxer в API 18), и все выглядит хорошо. На устройствах 4.1/4.2, если я снимаю видео с помощью встроенной камеры приложения на устройстве, а затем декодирую видео, тогда время представления также является правильным, хотя значения времени также имеют шумы из-за задержки камеры.

Что не так с видео или процессом декодирования, когда видео можно воспроизводить и декодировать нормально, но с правильным временем выборки и плохим временем представления? Возможно, мне придется использовать обходной путь, чтобы измерить время представления по времени выборки (это легко с помощью очереди), но я хочу выяснить, есть ли какая-либо пропущенная часть в моей работе.

1 ответ

Решение

Там нет никакой гарантии, что MediaCodec правильно обрабатывает метки времени представления перед Android 4.3. Это связано с тем, что тесты CTS, подтверждающие поведение PTS, не были добавлены до тех пор.

Напомню, что были проблемы с обработкой меток времени в кодеках AVC от некоторых поставщиков. Я не помню детали от случая к случаю, но если вы запустите тесты "буфер-буфер" и "буфер-поверхность" из EncodeDecodeTest на различных устройствах 4.1/4.2, вы обнаружите некоторые сбои. (Вам, конечно, нужно будет удалить тесты с поверхности на поверхность.)

Ваш код обработки метки времени выглядит нормально. Временная метка не является частью потока H.264, поэтому на самом деле она просто передается через кодек в виде метаданных, и вы, похоже, собираете ее и пересылаете в нужных местах. Суть в том, что если вы передаете действительные значения PTS и получаете хорошее видео, но мусорные значения PTS, что-то в кодеке неправильно с ними работает.

Вам нужно будет обойти это, передав значения отдельно, или - если частота кадров ввода всегда регулярна - просто вычислить его. Теоретически кодировщик может переупорядочивать кадры, поэтому порядок передачи меток времени в кодировщик может отличаться от того, в каком порядке они появляются... но поскольку вы знаете, что при создании фильма метки времени были возрастающими, вам следует быть в состоянии просто отсортировать их, если это было проблемой на практике.

С другой стороны, задержки в системе вызовут "колебания", которые вы видите в значениях меток времени, если вы захватите System.nanoTime() когда кадр прибывает в приложение. Вы можете сделать немного лучше в Android 4.3 с помощью Surface, потому что SurfaceTexture содержит метку времени, которая установлена ​​намного ближе к моменту захвата кадра. (Я знаю, что это бесполезно для ваших нынешних усилий, но хотел дать надежду на будущее.)

Другие вопросы по тегам