我正在尝试改进我们应用程序的帧提取。基本上我所做的是将 Grafika 的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 更好的检索帧的方法,我非常愿意尝试。
编辑:经过进一步测试,我可以匹配这extractor.getSampleTime()
两种方法,即使检索到的帧有时可能与显示帧不匹配。
编辑2:关于显示帧和提取帧之间的不匹配,它实际上非常简单,但如果你不知道它是如何MediaCodec
工作的,一开始会很混乱。我必须重新阅读每一个 fadden 的评论以更好地理解这个问题(这是给我那个“啊哈”时刻的那个)。
简而言之,解码器喜欢在吐出任何表示缓冲区之前消耗多个缓冲区。所以当前显示的那个和当前位置的那个是不一样的extractor.getSampleTime()
。所以显示和提取之间要同步的正确值应该是输出缓冲区的presentationTime,如下所示:
mCurrentSampleTime = mBufferInfo.presentationTimeUs;
理解这一点有助于解决许多神秘的问题(例如为什么第一帧不在 0 位置?)。希望这会对某人有所帮助。