0

我有一个扩展 SurfaceView 并实现 Camera.PreviewCallback 的类。在这个类中,我将相机设置为预览到提供的缓冲区(setPreviewCallbackWithBuffer)和几个缓冲区(addCallbackBuffer)。在我调用 startPreview 后,成功调用了 onPreviewFrame 回调。在 onPreviewFrame 中,我将工作交给另一个线程,该线程进行一些处理并最终将数据可视化。

但是,我注意到有时不再调用 onPreviewFrame 了。在探索这个问题的过程中,我观察到它最有可能发生在处理 1280x720 帧(相机支持)时,但它也发生在较低的分辨率上,但频率较低。我最终将代码简化为一个几乎为空的 onPreviewFrame(它只记录接收呼叫并再次调用 addCallbackBuffer;仅此而已)。处理线程未启动。可以观察到相同的行为。

在这种情况下,有 3 个缓冲区用于 1280x720 的预览,这将运行大约 20 分钟,然后不再调用 onPreviewFrame。Logcat 没有显示任何其他问题。这 20 分钟各不相同,有时是 5 分钟,有时不到一分钟。使用日志记录,我验证了每个 onPreviewFrame 调用的缓冲区序列是相当干净的(buffer1、buffer2、buffer3、buffer1、buffer2,...)。

我正在使用的设备是 Galaxy Tab 2(使用 Android 4.2.2 CyanogenMod);但我也在其他使用库存 rom 的设备上看到了这一点 - 所以我怀疑这是由于 rom 造成的。

所以我想这涉及到这些问题:

  1. 我需要提供多少缓冲区(给定一定的分辨率和一定的处理时间)?
  2. 为什么不再调用 onPreviewFrame (当时是空闲缓冲区)?

相关代码归结为:

private void startPreview()
{
    Camera.Parameters parameters = mCamera.getParameters();
    int width  = 1280;
    int height = 720;
    Resolution bestCameraResolution = getBestCameraResolutionMatch(width, height);
    width  = bestCameraResolution.width();
    height = bestCameraResolution.height();
    parameters.setPreviewSize(width, height);
    mCamera.setParameters(parameters);

    try {
        mCamera.setPreviewDisplay(mSurfaceHolder);
    } catch (IOException e) {
        Log.d(TAG, "Error setting camera preview: " + e.getMessage());
    }

    // calculate imageBufferSize here...

    mCamera.addCallbackBuffer(new byte[imageBufferSize]);
    mCamera.addCallbackBuffer(new byte[imageBufferSize]);
    mCamera.addCallbackBuffer(new byte[imageBufferSize]);
    mCamera.setPreviewCallbackWithBuffer(this);
    mCamera.startPreview();
    Log.d(TAG, "Start preview with " + width + "x" + height);
}

@Override
public void onPreviewFrame(byte[] frameData, Camera camera)
{
    // Log to a separate tag to allow better filtering
    Log.d(TAG + "Frame", "preview frame on buffer " + frameData.toString());

    // Give used buffer back for future grabbing
    if (mCamera != null) {
        mCamera.addCallbackBuffer(frameData);
    }
}
4

1 回答 1

0

如果代码更新为不使用虚拟 SurfaceView 并且我们将处理结果呈现给第二个 SurfaceView(位于顶部),则代码可以工作。我猜我们必须对同步更加小心,但它适用于我们迄今为止测试过的所有设备。

于 2013-05-14T06:38:11.900 回答