了解并加快addCallbackBuffer

时间:2015-09-28 14:41:28

标签: java android android-camera

我想了解传递给addCallbackBuffer的预览缓冲区如何与通过byte[]传递的onPreviewFrame数组相关联,这会引发以下相关问题。

Q1。我猜测addCallbackBuffer中传递的缓冲区用于存储新的相机帧,并且在调用onPreviewFrame之前,该缓冲区被复制到通过onPreviewFrame传递的数据缓冲区中。如果是这种情况,这意味着我可以在输入addCallbackBuffer后立即通过调用onPreviewFrame来重用我的预览帧缓冲区,并在完成处理返回的缓冲区后在函数末尾注意按onPreviewFrame。这是对的吗?

Q2。我也不清楚使用两个预览帧缓冲区的机制。假设我在初始化期间添加了两个私有byte[]预览缓冲区,如下所示:

addCallbackBuffer(mPreviewBuffer1);
addCallbackBuffer(mPreviewBuffer2);

当我在onPreviewFrame时,如何知道使用了哪个预览缓冲区,以便我可以再次使用addCallbackBuffer重新添加正确的预览帧缓冲区?

private byte[] mPreviewBuffer1;
private byte[] mPreviewBuffer1;
...
public void onPreviewFrame(byte[] camera, Camera c) {
  ...
  // how do I decide which buffer to re-add?
  //c.addCallbackBuffer(mPreviewBuffer1);
  //c.addCallbackBuffer(mPreviewBuffer2);
  ...
}

Q3。我是否正确理解另一个线程负责获取帧缓冲区,即只要预览缓冲区在队列中,我们将在onPreviewFrame执行时捕获帧?如果情况并非如此,那么拥有两个回调缓冲区对速度没有帮助,是吗?

2 个答案:

答案 0 :(得分:3)

Q1 是的,如果您不关心其内容,可以提前将缓冲区返回到摄像机。您可以在致电addCallbackBuffer()之后读取此缓冲区,或者您可以阅读,但像素数据将会出错。

Q2 你可以简单地将你在回调时收到的缓冲区返回相机,即

@Override public void onPreviewFrame(byte[] data, Camera camera) {
  …
  camera.addCallbackBuffer(data);
}

在这种情况下,您不关心data == mPreviewBuffer1data == mPreviewBuffer2。但是下面的代码也没关系:

private byte[][] mPrevieBuffers = new byte[4][];
@Override public void onPreviewFrame(byte[] data, Camera camera) {
  for (int i=0; i<mPreviewBuffers.length; i++) {
    if (data == mPreviewBuffers[i]) {
       processData(i);
    }
  }
}

Q3 正确,onPreviewFrame()与填充其他缓冲区并行执行,但所有onPreviewFrame()回调都在同一个线程上调用。如果要处理尽可能多的预览帧,则应 a)将处理委派给工作线程(尤其是在多核设备上)。请注意,您可以从其他线程安全地调用addCallbackBuffer(); b) start the camera on a separate LooperonPreviewFrame()与UI线程分离。

答案 1 :(得分:1)

这对我有用:

public void surfaceChanged(SurfaceHolder holder, int format, int ww, int hh)
{
    if (mCamera == null) return;
    mCamera.stopPreview();
    mCamera.setPreviewCallback(null);
    //get camera parameters
    prepareSizeScreen();

    mCamera.setParameters(mMyCameraParameters.makeParameters(mCamera.getParameters(), mSizeScreen));
    try
    {
        mCamera.setPreviewDisplay(mHolder);
    } catch (Exception e)
    {
    }
    Camera.Size setSize = mCamera.getParameters().getPreviewSize();
    int bufferSize = setSize.width * setSize.height
            * ImageFormat.getBitsPerPixel(mCamera.getParameters().getPreviewFormat()) / 8;

    setupCallback(bufferSize);//this is what you are looking for


    mCamera.startPreview();
}

您可以选择更多回调次数,您可以更多地使用预览帧。

static final private int NUM_BUFFERS = 5;
private void setupCallback(int bufferSize)
{
    mCamera.setPreviewCallbackWithBuffer(this);
    for (int i = 0; i <= NUM_BUFFERS; ++i)
    {
        byte[] cameraBuffer = new byte[bufferSize];
        mCamera.addCallbackBuffer(cameraBuffer);
    }
}

当你收到回电时,你应该使用与设置另一个回调相同的数据。

@Override
public void onPreviewFrame(byte[] data, Camera camera)
{//data has NV21 format        
    processData(data);
    camera.addCallbackBuffer(data);//same data is sent for another callback. So you will be managing NUM_BUFFERS at all times.
}

不要忘记onDestroy:     public void surfaceDestroyed(SurfaceHolder holder)     {         if(mCamera!= null)         {             mCamera.setPreviewCallback(NULL);             mCamera.stopPreview();             mCamera.release();         }         mCamera = null;     }