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

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

Я измеряю временной код кадров камеры по системному нановремени и выбираю подмножество кадров, соответствующее желаемому 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, если я захватываю видео с помощью встроенного приложения камеры на устройстве, а затем декодирую видео, время презентации также является правильным, хотя значения времени также имеют шумы из-за задержки камеры.

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


person Mark    schedule 13.03.2014    source источник


Ответы (1)


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

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

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

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

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

person fadden    schedule 13.03.2014
comment
Спасибо за ответ. Что касается последней части, это правда, что системное время может быть нерегулярным, но я не могу получить более точное измерение времени по кадрам обратного вызова с камеры, поэтому я все еще использую системное время и разрабатываю простой алгоритм для фильтрации произвольных временных последовательностей. примерно до 15 кадров в секунду. - person Mark; 13.03.2014
comment
невозможно отправить измененные временные метки в ffmpeg, если следующая временная метка не больше предыдущей, она пропускается. какие временные метки вы отправляете в ffmpeg (я видел, что вы сказали, что они верны, но просто для перепроверки)? я помню, что это требует некоторых вычислений для pkt.pts на основе time_base.den - person Marlon; 13.03.2014
comment
Вы пытались запустить тест CTS на этих устройствах? декодер выводит время кадра, равное входу? - person Marlon; 13.03.2014
comment
Тест CTS корректно работает на видео, закодированном собственным приложением камеры. Временные метки всегда инкрементны, а ввод/вывод декодера согласован. Для видео, сделанного мной, извлеченный шаг расчета правильный и становится входным сигналом декодера, но выходные данные не согласуются. - person Mark; 13.03.2014
comment
@fadden, в Grafika в CameraCaptureActivity, когда вы записываете видео, то есть 25 секунд видео, а затем воспроизводите его, мы обнаружим проблемы с метками времени. файл будет 25-секундным видео, но кадры смещаются на более раннее время. поэтому видеокадры фактически останавливаются через 21 секунду, а затем только что отображается последний кадр. вам нужно запустить видео на реальном плеере, а не внутри приложение, чтобы наблюдать это. Есть идеи, как решить эту проблему? Может быть, updateTexImage () перед началом записи вызывает это смещение? Спасибо :) - person yarin; 14.02.2015
comment
@yarin Я не видел такого поведения. В системе нет ничего, что сжимало бы временные метки в источнике, которые основаны на системных монотонных часах. Фиксированное смещение не имеет значения, так как временные метки не обязаны начинаться с нуля в видеофайле. - person fadden; 14.02.2015
comment
@fadden, да, в этом разделе есть ошибка, мое видео сильно сжато. посмотрите на логарифм, с того момента, как я нажимаю на «Остановить запись» (при показе + захвате) активности камеры), приложение все еще записывает еще 9 ( !) секунд. Вот логи, которые демонстрируют, что: - person yarin; 03.03.2015
comment
@fadden 03-03 21:29:51.459: D/Grafika(23779): ОСТАНОВИТЬ запись 03-03 21:30:01.624: D/Grafika(23779): handleStopRecording 03-03 21:30:02.114: D/MPEG4Writer( 23779): Остановка видеодорожки 03-03 21:30:02.114: I/MPEG4Writer(23779): Всего получено буферов нулевой длины (476/0) и закодировано 476 кадров. - видео 03-03 21:30:02.119: D/MPEG4Writer(23779): Остановка источника видеодорожки 03-03 21:30:02.119: D/MPEG4Writer(23779): Видеодорожка остановлена ​​03-03 21:30:02.119: D/MPEG4Writer(23779): остановлен поток записи 03-03 21:30:02.119: D/MPEG4Writer(23779): в последнем пакете записано 0 фрагментов - person yarin; 03.03.2015
comment
03-03 21:30:02.119: D/MPEG4Writer(23779): Поток записи остановлен 03-03 21:30:02.119: I/MPEG4Writer(23779): Файл mp4 не будет доступен для потоковой передачи. 03-03 21:30:02.119: D/MPEG4Writer(23779): остановка видеодорожки - person yarin; 03.03.2015
comment
или с меньшим количеством строк: 03-03 21:29:51.459: D/Графика(23779): ОСТАНОВИТЬ запись 03-03 21:30:01.624: D/Графика(23779): handleStopRecording.it составляет 10 секунд между двумя строками. я использую галакси с3 - person yarin; 03.03.2015
comment
@yarin, вероятно, было бы лучше создать новый вопрос. - person fadden; 03.03.2015