Я пытаюсь улучшить извлечение кадров нашего приложения. По сути, я объединил решение из MoviePlayer
для прямого поиска и BigFlake ExtractMpegFramesTest
для извлечения кадра. Для извлечения я ищу предыдущий ключевой кадр, затем декодирую вперед и сохраняю только последний кадр. Что-то вроде этого (см. мой предыдущий вопрос для более полного объяснения):
decoder.releaseOutputBuffer(decoderStatus, doRender);
if (doRender) {
if (VERBOSE) Log.d(TAG, "awaiting decode of frame " + decodeCount);
outputSurface.awaitNewImage();
outputSurface.drawImage(false);
if(extractor.getSampleTime() == mPosition){
Log.d(TAG, "sampleTime: " + extractor.getSampleTime() + " mPosition: " + mPosition + "----- EXTRACTING FRAME");
long startWhen = System.currentTimeMillis();
outputSurface.saveFrame();
long frameSaveTime = System.currentTimeMillis() - startWhen;
Log.d(TAG, "sampleTime: frame saved in: " + frameSaveTime + " millisecond");
return;
}
decodeCount++;
}
Проблема иногда заключается в том, что шаг расчета, полученный из extractor.getSampleTime()
при поиске назад, а затем при декодировании вперед, кажется, не соответствует таковому при прямом поиске вперед.
Я включил журнал, чтобы было понятнее:
position is the seeking position in microsecond
sampleTime: 12112100 -- position: 12139000 ----- FORWARD
sampleTime: 12120441 -- position: 12139000 ----- FORWARD
sampleTime: 12128783 -- position: 12139000 ----- FORWARD
sampleTime: 12137125 -- position: 12139000 ----- FORWARD
sampleTime: 12012000 -- position: 12139000 ----- BACKWARD
sampleTime: 12020341 -- position: 12139000 ----- BACKWARD
sampleTime: 12028683 -- position: 12139000 ----- BACKWARD
sampleTime: 12037025 -- position: 12139000 ----- BACKWARD
sampleTime: 12045366 -- position: 12139000 ----- BACKWARD
sampleTime: 12053708 -- position: 12139000 ----- BACKWARD
sampleTime: 12062050 -- position: 12139000 ----- BACKWARD
sampleTime: 12070391 -- position: 12139000 ----- BACKWARD
sampleTime: 12078733 -- position: 12139000 ----- BACKWARD
sampleTime: 12087075 -- position: 12139000 ----- BACKWARD
sampleTime: 12095416 -- position: 12139000 ----- BACKWARD
sampleTime: 12103758 -- position: 12139000 ----- BACKWARD
sampleTime: 12112100 -- position: 12139000 ----- BACKWARD
sampleTime: 12120441 -- position: 12139000 ----- BACKWARD
sampleTime: 12128783 -- position: 12139000 ----- BACKWARD
Как вы можете видеть, при прямом поиске . Я не уверен, почему это происходит, но это приводит к несоответствию между кадром представления и извлеченным кадром. Также этот метод не очень эффективен, так как мне приходится настраивать extractor.getSampleTime()
может достичь позиции 12137125
, а при поиске назад, а затем при декодировании вперед, он может достичь только 12128783
EGLSurface
и декодировать его каждый раз, когда мне нужно извлечь кадр. В зависимости от того, насколько удален требуемый кадр от предыдущего ключевого кадра, эта операция может занять от 3 до 5 секунд, что определенно слишком долго для многократного извлечения.
Я хотел бы спросить, возможно ли сделать так, чтобы декодер декодировал обе поверхности (SurfaceView
для отображения и EGLSurface
для поиска кадров) одновременно, чтобы я мог потенциально решить обе эти проблемы с точностью и производительностью.
Я также пытался использовать FFmpeg для извлечения кадров раньше, производительность примерно такая же. Если есть лучший способ получить кадр, чем использование OpenGL, я очень хочу попробовать.
EDIT: После дальнейшего тестирования я могу сопоставить extractor.getSampleTime()
из обоих методов, даже несмотря на то, что полученный кадр иногда может не совпадать с кадром дисплея.
EDIT 2: Что касается несоответствия между отображаемым кадром и извлеченным кадром, на самом деле это очень просто, но поначалу это довольно запутанно, если вы не знаете, как работает MediaCodec
. Мне приходится перечитывать каждый комментарий Фаддена, чтобы лучше понять проблему (это тот, который вызывает у меня момент "ах-ха" ).
Короче говоря, декодер любит потреблять несколько буферов, прежде чем он выдаст какой-либо буфер представления. Таким образом, тот, который отображается в данный момент, не совпадает с тем, который находится в текущей позиции extractor.getSampleTime()
. Таким образом, правильным значением для синхронизации между отображением и извлечением должно быть PresentationTime выходного буфера, что-то вроде этого:
mCurrentSampleTime = mBufferInfo.presentationTimeUs;
Понимание этого помогает решить множество загадочных вопросов (например, почему первый кадр не находится в нулевой позиции?). Надеюсь, это поможет кому-то.