【问题标题】:Can Android MediaCodec decodes to two Surfaces at the same time?Android MediaCodec 可以同时解码到两个 Surface 吗?
【发布时间】:2016-08-19 02:29:15
【问题描述】:

我正在尝试改进我们应用的帧提取。基本上我所做的是将 Grafika 的MoviePlayer 的解决方案用于前向搜索和 BigFlake 的ExtractMpegFramesTest 以提取帧。对于提取,我回溯到前一个关键帧,然后向前解码并只保存最后一帧。像这样的(更完整的解释见我的previous question):

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 是如何工作的,一开始会很困惑。我必须重新阅读每个时尚的评论以更好地理解问题(this 是给我那个“啊哈”时刻的人)。

简而言之,解码器喜欢在输出任何表示缓冲区之前消耗多个缓冲区。所以当前显示的那个和当前extractor.getSampleTime()位置的那个是不一样的。所以显示和提取之间要同步的正确值应该是输出缓冲区的presentationTime,如下所示:

mCurrentSampleTime = mBufferInfo.presentationTimeUs;

理解这一点有助于解决许多神秘的问题(例如为什么第一帧不在 0 位置?)。希望这会对某人有所帮助。

【问题讨论】:

    标签: android android-mediacodec mediaextractor grafika


    【解决方案1】:

    不是我问题的具体答案,但我确实找到了一种方法来改善帧提取时间。基本上,如果您对PNG 的格式没有任何严格要求,那么只需将输出图像压缩为jpeg,如下所示:

    outputBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);

    这将使用硬件加速而不是像PNG 那样的纯软件压缩,而且速度明显更快。整个操作大约需要 600 毫秒,压缩位大约需要 200 毫秒。与之前使用 PNG 压缩的 5 秒相比,这是一个非常巨大的改进。

    理论上,如果您不关心透明度,则将Bitmap.Config.RGB_565 用于输出图像而不是Bitmap.Config.ARGB_8888 可以获得更高的性能。但是,在实践中,我遇到了 2 个阻止我这样做的问题:

    • 输出图像的颜色混乱。
    • 实际上提取图像需要更长的时间。

    【讨论】:

      猜你喜欢
      • 2014-03-19
      • 2015-12-03
      • 2017-09-16
      • 2016-09-06
      • 2017-08-03
      • 2013-04-25
      • 1970-01-01
      • 1970-01-01
      • 2013-10-15
      相关资源
      最近更新 更多