如何获取Android相机预览数据?

1''*_*1'' 15 android android-camera

我的相机应用程序在屏幕上显示相机预览,并在后台处理它.以下是相关代码,尽可能压缩(例如,没有显示错误处理或字段声明):

public final class CameraView extends SurfaceView implements
          SurfaceHolder.Callback, Runnable, PreviewCallback {

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

    void openCamera() {
        // Called from parent activity after setting content view to CameraView
        mCamera = Camera.open();
        mCamera.setPreviewCallbackWithBuffer(this);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start(); 

        // Set CameraView to the optimal camera preview size

        final Camera.Parameters params = mCamera.getParameters();
        final List<Camera.Size> sizes = params.getSupportedPreviewSizes();
        final int screenWidth = ((View) getParent()).getWidth();
        int minDiff = Integer.MAX_VALUE;
        Camera.Size bestSize = null;

        if (getResources().getConfiguration().orientation 
                == Configuration.ORIENTATION_LANDSCAPE) {
            // Find the camera preview width that best matches the
            // width of the surface.
            for (Camera.Size size : sizes) {
                final int diff = Math.abs(size.width - screenWidth);
                if (diff < minDiff) {
                    minDiff = diff;
                    bestSize = size;
                }
            }
        } else {
            // Find the camera preview HEIGHT that best matches the 
            // width of the surface, since the camera preview is rotated.
            mCamera.setDisplayOrientation(90);
            for (Camera.Size size : sizes) {
                final int diff = Math.abs(size.height - screenWidth);
                if (Math.abs(size.height - screenWidth) < minDiff) {
                    minDiff = diff;
                    bestSize = size;
                }
            }
        }

        final int previewWidth = bestSize.width;
        final int previewHeight = bestSize.height;

        ViewGroup.LayoutParams layoutParams = getLayoutParams();
        layoutParams.height = previewHeight;
        layoutParams.width = previewWidth;
        setLayoutParams(layoutParams);

        params.setPreviewFormat(ImageFormat.NV21);
        mCamera.setParameters(params);

        int size = previewWidth * previewHeight * 
            ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
        mBuffer = new byte[size];
        mCamera.addCallbackBuffer(mBuffer);

        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    }

    public void onPreviewFrame(byte[] data, Camera camera) {
        CameraView.this.notify();
    }

    public void run() {
        mThreadRun = true;
        while (mThreadRun) {
            synchronized (this) {
                this.wait();
                processFrame(mBuffer); // convert to RGB and rotate - not shown
            }
            // Request a new frame from the camera by putting 
            // the buffer back into the queue
            mCamera.addCallbackBuffer(mBuffer);
        }

        mHolder.removeCallback(this);
        mCamera.stopPreview();
        mCamera.setPreviewCallback(null);
        mCamera.release();
        mCamera = null;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        mThreadRun = false;
    }
}
Run Code Online (Sandbox Code Playgroud)

在所有设备上,相机预览正确显示,并且在大多数(模拟器,三星Galaxy S3等)上,存储的数据mBuffer也是正确的(当然,在NV21到RGB转换和旋转之后).但是,许多设备无法在onPreviewFrame中提供正确的数据.我确信数据在收到后正确转换为RGB,因此问题似乎出现在提供的原始数据中mBuffer.我注意到这个与YV12(别名YUV420p)相机预览格式有关的错误报告,但我使用的是旧的默认值NV21(别名YUV420sp),必须根据兼容性标准支持(见7.5.3.2,底部)第29页).

例如,对于此场景(此处显示在Samsung Galaxy Tab 2上的Camera Preview中):

在此输入图像描述

传递给mBufferTab 2 的数据如下所示:

在此输入图像描述

在摩托罗拉Droid 4上看起来像:

在此输入图像描述

在所有设备上获取Android相机预览数据的正确方法是什么?

编辑: for processFrame(),我使用OpenCV转换为RGB并旋转.看到这个答案这个答案.

1''*_*1'' 8

唯一的问题是我没有设置预览宽度和高度:

params.setPreviewSize(previewWidth, previewHeight);
mCamera.setParameters(params);
Run Code Online (Sandbox Code Playgroud)

这意味着,高度和宽度I为阵列分配(正比于previewWidth*previewHeight)趋向于比实际数据的大小被返回(正比于较大的很多默认预览宽度和预览高度).在某些手机上,默认设置与previewWidth和previewHeight的大小相同,因此没有问题.