Android MediaCodec: кодирование не работает, потому что нет кадров синхронизации для видеодорожки

Я планирую преобразовать видеофайл в другой видеофайл с другим битрейтом, частотой кадров и т. д.

В основном я следую примерам в http://bigflake.com/mediacodec.

Однако журнал показывает ошибку отсутствия кадров синхронизации для видеодорожки:

submitted frame 5 to dec, size=47398
no output from encoder available
decoder output format changed: {height=1080, what=1869968451, color-format=2141391875, slice-height=1088, crop-left=0, width=1920, crop-bottom=1079, crop-top=0, mime=video/raw, stride=1920, crop-right=1919}
no output from encoder available
surface decoder given buffer 0 (size=3137536)
awaiting frame
E/MPEG4Writer(3464): There are no sync frames for video track
W/MPEG4Writer(3464): 0-duration samples found: 1
Stopping Video track

Затем программа выходит.

Я искал в Интернете. Фадден говорит "Убедитесь, что вы передаете все значения MediaCodec.BufferInfo в MediaMuxer - это где находятся флаги. Кадры синхронизации будут иметь установленный флаг BUFFER_FLAG_SYNC_FRAME."

Однако в примере с http://bigflake.com/mediacodec используется:

outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); 

Похоже, это означает, что кодер будет определять, какой кадр будет назначен ключевому кадру.

Кажется, в Интернете не так много связанной информации по этой проблеме. О, хотелось бы, чтобы на bigflake.com было больше примеров, связанных с некоторыми проблемами, которые интересуют разработчиков (например, изменение параметров формата одного существующего видеофайла)

==[Обновление]== Вот код, который я использую:

MediaFormat outputFormat = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
outputFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
        MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE,5000000);
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE,30);
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,5); 
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
inputSurface = new InputSurface(encoder.createInputSurface());
inputSurface.makeCurrent();
encoder.start();
...
try {
    mMuxer = new MediaMuxer(ouVideoFileName, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException ioe) {
    throw new RuntimeException("MediaMuxer creation failed", ioe);
}
...
// now that we have the Magic Goodies, start the muxer
mTrackIndex = mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted = true;
...
mMuxer.writeSampleData(mTrackIndex, encodedData, info_encoder);

Тогда где мне не передать параметры в mMuxer? Кажется, я передал все необходимые параметры.

==[Обновление 2]== В:

int encoderStatus = encoder.dequeueOutputBuffer(info_encoder, TIMEOUT_USEC);

Я выхожу из системы info_encoder.flags: с 0 по 5 кадры, флаги = 0. Это не флаги для ключевого кадра. Входной видеофайл — короткий, записанный устройством, .mp4, и воспроизводится корректно. После кадра 5 MPEG4Write жалуется: «Нет кадров синхронизации для видеодорожки».

==[Обновление 3]== Кстати, я обнаружил, что часть кодирования между DecodeEditEncodeTest.java и EncodeDecodeTest.java отличается. Часть кодирования в EncodeDecodeTest.java включает encoder.dequeueInputBuffer, а в DecodeEditEncodeTest.java вообще нет строк, связанных с encoder.dequeueInputBuffer. Как вы думаете, это будет проблемой? Но, во-первых, почему они разные в двух примерах?

==[Обновление 4]== Я копирую код в класс ExtractMpegFramesTest Затем в моем основном действии у меня есть кнопка. После того, как я нажму кнопку, он вызовет:

// test:
ExtractMpegFramesTest mTest = new ExtractMpegFramesTest();
try {
    mTest.testExtractMpegFrames();
} catch (Throwable e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

Ошибка:

E/ACodec(11342): [OMX.qcom.video.decoder.avc] storeMetaDataInBuffers failed w/ err -2147483648
java.lang.RuntimeException: frame wait timed out
ExtractMpegFramesTest$CodecOutputSurface.awaitNewImage(ExtractMpegFramesTest.java:496)

person user1914692    schedule 03.03.2014    source источник
comment
Кодер решает, куда поместить кадры синхронизации на основе установленных вами параметров. Вам нужно убедиться, что вы передаете их MediaMuxer — это должно быть простым делом взять все, что вы получите от декодера, и передать его writeSampleData().   -  person fadden    schedule 03.03.2014
comment
... кроме того, если вы получаете изменение FPS за счет пропуска кадров, убедитесь, что вы не пропускаете кадры синхронизации. Например, первый кадр, выходящий из кодера AVC (после одного или нескольких фрагментов CODEC_CONFIG), должен быть кадром синхронизации.   -  person fadden    schedule 03.03.2014
comment
@fadden: Спасибо. Я сделал это: outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10); Я также меняю значение на другие числа, например меньше 5. Весь остальной код следует примеру. Позвольте мне добавить больше кода в мой исходный пост.   -  person user1914692    schedule 03.03.2014
comment
Параметры просто определяют, как часто они встречаются. Сообщение об ошибке указывает, что ничего не появляется, что должно быть невозможно, поскольку самый первый кадр, выходящий из кодировщика, должен быть кадром синхронизации. (Декодер ничего не может сделать, если в потоке просто куча кадров P/B и нет I-кадров, поэтому он жалуется.)   -  person fadden    schedule 03.03.2014
comment
Я отлаживаю код. In: int encoderStatus = encoder.dequeueOutputBuffer(info_encoder, TIMEOUT_USEC); Я выхожу из системы info_encoder.flags: с 0 по 5 кадры, флаги = 0. Это не флаги для ключевого кадра. Входной видеофайл — короткий, записанный устройством, .mp4, и воспроизводится корректно.   -  person user1914692    schedule 03.03.2014
comment
Кстати, я обнаружил, что часть кодирования между DecodeEditEncodeTest.java и EncodeDecodeTest.java отличается. Часть кодирования в EncodeDecodeTest.java включает encoder.dequeueInputBuffer, а в DecodeEditEncodeTest.java вообще нет строк, связанных с encoder.dequeueInputBuffer. Как вы думаете, это будет проблемой? Но, во-первых, почему они разные в двух примерах?   -  person user1914692    schedule 03.03.2014
comment
В одном примере используются входные данные из буферов YUV, в другом — входные данные из Surface. В EncodeDecodeTest вы обнаружите, что в doEncodeDecodeVideoFromSurfaceToSurface() нет encoder.dequeueInputBuffer(). Может быть полезно увидеть полный цикл декодирования/кодирования. Вы можете вставлять большие фрагменты кода на gist.github.com .   -  person fadden    schedule 04.03.2014
comment
@fadden, спасибо за объяснение разницы. ОК, выложу на gishub. Прямо сейчас я пытаюсь вывести данные в необработанный файл (избегая мультиплексора); тоже не получается! Затем я пытаюсь запустить исходный код в примере ExtractMpegFramesTest. Тоже не получается! Информация об ошибке E/ACodec(11342): [OMX.qcom.video.decoder.avc] storeMetaDataInBuffers не удалось с ошибкой -2147483648. Значит, что-то должно быть не так в другом месте. Смотрите мое новое обновление в исходном посте.   -  person user1914692    schedule 04.03.2014
comment
Игнорируйте жалобу storeMetaDataInBuffers, она появляется даже при успешных запусках. Ваша проблема заключается в том, что время ожидания кадра истекло, что произошло бы, если бы вы не сохранили ExtractMpegFramesWrapper (который запускает все в отдельном потоке). FWIW, аналогичный вопрос: stackoverflow.com/questions/21750790/   -  person fadden    schedule 04.03.2014
comment
@fadden: Вы сказали, что произойдет, если вы не сохраните ExtractMpegFramesWrapper. Я создаю объект для ExtractMpegFramesTest и запускаю его в основной активности приложения для Android. Разве этого недостаточно? Тогда как это сделать? Любой пример? // Я прочитал указанную вами ссылку. Он указывает, где проблема; но я не знаю, где это решить. Позвольте мне поработать над этим еще... Большое спасибо!   -  person user1914692    schedule 04.03.2014
comment
давайте продолжим это обсуждение в чате   -  person user1914692    schedule 05.03.2014
comment
истекло время ожидания повторного кадра. насколько я помню нужно начинать обработку в отдельном потоке   -  person Marlon    schedule 05.03.2014
comment
@Марлон, спасибо. Существует ExtractMpegFramesWrapper, который служит функцией для запуска extractMpegFrames в отдельном потоке. На самом деле класс ExtractMpegFramesWrapper уже демонстрирует, как его использовать (есть тестовая точка входа: testExtractMpegFrames)   -  person user1914692    schedule 05.03.2014
comment
работает ли тест testExtractMpegFrames? не из вашего приложения, а просто тест?   -  person Marlon    schedule 05.03.2014
comment
@Марлон, спасибо. Вы имели в виду тест CTS testExtractMpegFrames? Я этого не делал. Я просто проверяю руководство CTS, в котором говорится: «Сбросьте заводские данные на устройстве» («Настройки» > «Хранилище» > «Сброс заводских данных»). Предупреждение: это приведет к удалению всех пользовательских данных с устройства. Я не решаюсь сделать это. Кстати, мое устройство: Nexus 4. Android 4.4.2. Я предполагаю, что это должно быть совместимо. Или любой другой лучший способ протестировать testExtractMpegFrames?   -  person user1914692    schedule 05.03.2014
comment
ExtractMpegFramesTest — это общий тестовый класс, расширяющий AndroidTestCase. У вас есть опыт запуска тестов на Android? Нет необходимости выполнять какой-либо сброс данных или другие изменения конфигурации устройства, когда вы хотите запустить тест. Просто создайте тестовый проект с правильными настройками, поместите туда этот тест + необходимое видео и запустите на устройстве   -  person Marlon    schedule 06.03.2014
comment
stackoverflow.com/questions/20878546 / - краткое обсуждение запуска медиа-тестов   -  person Marlon    schedule 06.03.2014