【问题标题】:Understanding and speeding up addCallbackBuffer理解和加速 addCallbackBuffer
【发布时间】:2015-09-28 14:41:28
【问题描述】:

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

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

第二季度。我也不清楚使用两个预览帧缓冲区的机制。假设我在初始化期间添加了两个私有 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);
  ...
}

第三季度。我是否正确理解另一个线程负责获取帧缓冲区,即只要预览缓冲区在队列中,我们将在 onPreviewFrame 执行时捕获帧?如果不是这样,拥有两个回调缓冲区对速度没有帮助,不是吗?

【问题讨论】:

    标签: java android android-camera


    【解决方案1】:

    Q1 是的,如果您不关心缓冲区的内容,可以提前将缓冲区返回到相机。调用addCallbackBuffer() 后,可能无法从该缓冲区读取,或者您可以读取,但像素数据会出错。


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

    @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 线程分离。

    【讨论】:

    • 太棒了。你澄清了一切。特别是,意识到没有发生任何复制,并且我们从 onPreviewFrame 获得的数据指针正是使用 addCallbackBuffer 添加的预览缓冲区之一,这是关键!
    • 有一个 memcpy,但它对我们隐藏:这是当一帧从相机 HAL(系统进程)通过 Binder 接口到达您的(用户)进程时。复制到我们在addCallbackBuffer() 中提供的缓冲区的帧也用于显示实时预览。
    【解决方案2】:

    这对我有用:

    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(null); mCamera.stopPreview(); mCamera.release(); } mCamera = null; }

    【讨论】:

    • 如果您使用的是旧设备(比如说android 4.4 普通设备),这太慢了...
    猜你喜欢
    • 1970-01-01
    • 2013-06-21
    • 1970-01-01
    • 1970-01-01
    • 2011-12-16
    • 2012-12-25
    • 2018-05-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多