Используя MediaCodec, как я могу генерировать ключевые i-кадры с интервалом менее 1 секунды?
MediaFormat.KEY_I_FRAME_INTERVAL принимает только целочисленное значение, и я предполагаю, что именно это контролирует, как часто кодер генерирует I-кадр, верно? Так значит ли это, что если я использую MediaCodec, я не могу генерировать I-кадры чаще?
3 ответа
Я наконец нашел решение этой проблемы!
Вставьте следующий код до того, как потребуется ключевой кадр, и он сгенерирует ключевой кадр в следующем доступном кадре.
Bundle b = new Bundle();
b.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
encoder.setParameters(b);
Вы, вероятно, можете обойти это, масштабируя свои временные метки. Если вы, например, умножаете временные метки на 2 при вводе их в кодировщик, а затем делите на 2 временные метки, которые вы получаете в выходных буферах от кодировщика, вы сможете получить интервал I-кадра в полсекунды. Затем вам также нужно уменьшить битрейт (и частоту кадров) вдвое, чтобы он совпадал. Конечно, это не идеально, но должно позволить вам получить правильный эффект.
В документации сказано, что:
Нулевое значение означает, что запрашивается поток, содержащий все ключевые кадры.
Так что все, что вам нужно, это:
MediaFormat format = MediaFormat.createVideoFormat(...);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 0);
Он работает для некоторых устройств, но для некоторых устройств (например, Nexus 6p) выдает исключение:
E/ACodec: [OMX.qcom.video.encoder.avc] configureCodec returning error -1010
E/ACodec: signalError(omxError 0x80001001, internalError -1010)
E/MediaCodec: Codec reported err 0xfffffc0e, actionCode 0, while in state 3
E/MediaCodec: configure failed with err 0xfffffc0e, resetting...
KEY_I_FRAME_INTERVAL также может получать float, начиная с Android 7.1.
Поэтому теперь можно сделать что-то вроде этого:
val mediaFormat: MediaFormat = MediaFormat.createVideoFormat(VIDEO_MIME_TYPE, 480, 640)
// 30 Frames per second
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30)
// 1 second between key frames!
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1)
// 0.3 seconds between key frame!
mediaFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 0.3F)