理解和加速 addCallbackBuffer

Understanding and speeding up addCallbackBuffer

想了解传递给addCallbackBuffer的预览缓冲区与onPreviewFrame传递的byte[]数组有什么关系,从而提示以下相关问题。

Q1。我猜测传入 addCallbackBuffer 的缓冲区用于存储新的相机帧,并且在调用 onPreviewFrame 之前,该缓冲区被复制到通过 onPreviewFrame 传递的数据缓冲区中。如果是这样,那意味着我可以通过在输入 onPreviewFrame 后立即调用 addCallbackBuffer 来重用我的预览帧缓冲区,并在处理完返回的缓冲区时在函数末尾注明通过 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 执行时捕获帧?如果不是这种情况,那么拥有两个回调缓冲区将无助于提高速度,对吗?

这对我有用:

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();
}

您可以选择回调的数量,回调越多,您将获得 onPreviewFrame。

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) { 如果(mCamera!= null) { mCamera.setPreviewCallback(空); mCamera.stopPreview(); mCamera.release(); } mCamera = null; }

Q1 是的,如果你不关心它的内容,你可以 return 尽早将缓冲区缓存到相机。为它调用 addCallbackBuffer() 后,您可能无法从该缓冲区读取,或者您可以读取,但像素数据将是错误的。


Q2 您可以简单地 return 拍摄您在回调中收到的缓冲区,即

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

在这种情况下,您不关心 data == mPreviewBuffer1 还是 data == 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 Looper 使 onPreviewFrame() 与 UI 线程分离。