在surfaceChanged中设置的imageformat更改为onPreviewFrame中的其他内容

时间:2014-03-10 23:05:42

标签: android android-camera

下面是我正在尝试的代码。启动器活动正在使用CamPreview类。在实现PreviewCallback之前,我能够获得预览。当我通过实现onPreviewFrame尝试PreviewCallback时,我完全感到困惑它是如何在内部工作的。以下是让我困惑的事情。请澄清一下。

1)虽然我设置了像ImageFormat和Previewsize这样的相机参数,但它们似乎一直持续到方法onPreviewFrame的调用。例如,surfaceChanged方法中的Log.i语句(根据我的理解,在surfaceCreated之后立即调用一次)将预览大小打印为1056x864。但是,onPreviewFrame报告预览大小为1920x1080。

即使图片格式也从NV21(在surfaceChanged中为17)变为JPEG(在onPreviewFrame中为256)。

我已经验证并确认传递给onPreviewFrame的Camera实例与CamPreview类中声明的成员变量mCamera相同。

如果我能够在onPreviewFrame中成功获得NV21的预览格式,我该如何将其转换为ARGB格式?我已经尝试了stackoverflow中发布的方法,但传递给onPreviewFrame的数据由于索引越界而失败,这导致我首先检查图像格式。如果有人尝试过类似的东西,请告诉我在创作过程中我错过了什么导致了这个混乱:(。

我试图通过最初从传递给onPreviewFrame的byte []创建YuvImage来创建位图,这给了我绿色的latern图像(全部为绿色或垃圾)!

2)你可以看到我在第(1)点中提到的其他Log.i stmts。它们在方法surfaceChanged和onPreviewFrame中打印出每个像素的位数和每个像素的字节数信息。结果分别为12和1。这怎么可能呢 ?同样,这可能是(1)

中发生的事情的副作用
public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {

    private static final String TAG = "CamPreview";

    private SurfaceHolder mHolder;
    private Camera mCamera;
    private byte[] mVideoSource;
    private Bitmap mBackBuffer;
    private Paint mPaint;
    private Context mContext;

    public CamPreview(Context context) {
    super(context);
    mContext = context;
    mCamera = getCameraInstance();
    mHolder = getHolder();
    mHolder.addCallback(this);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
        mCamera.setDisplayOrientation(90);
        mCamera.setPreviewDisplay(null);
        mCamera.setPreviewCallbackWithBuffer(this);
        this.setWillNotDraw(false);
        Log.i(TAG, "@SurfaceCreated: initilization finished");
    } catch (IOException eIOException) {
        Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
        throw new IllegalStateException();
    }
    }

    private Size findBestResolution(int pWidth, int pHeight) {
    List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
    Size lSelectedSize = mCamera.new Size(0, 0);
    for (Size lSize : lSizes) {
        if ((lSize.width <= pWidth)
         && (lSize.height <= pHeight)
         && (lSize.width >= lSelectedSize.width)
         && (lSize.height >= lSelectedSize.height)) {
            lSelectedSize = lSize;
        }
    }
    if ((lSelectedSize.width == 0)
                    || (lSelectedSize.height == 0)) {
        lSelectedSize = lSizes.get(0);
    }
    return lSelectedSize;
    }

    private void createBuffers(String caller, Size prefSize) {
        Camera.Parameters camParams = mCamera.getParameters();

    int previewWidth    = prefSize.width;
    int previewHeight   = prefSize.height;
    mBackBuffer         = Bitmap.createBitmap(previewWidth,
                                            previewHeight,
                                            Bitmap.Config.ARGB_8888);

    Log.i(TAG,"@"+caller+": Piture Width " + Integer.toString(previewWidth));
    Log.i(TAG,"@"+caller+": Piture Height " + Integer.toString(previewHeight));
    Log.i(TAG,"@"+caller+": Piture format " + Integer.toString(ImageFormat.NV21));        

    camParams.setPreviewSize(previewWidth,previewHeight);
    camParams.setPreviewFormat(ImageFormat.NV21);
    mCamera.setParameters(camParams);

    PixelFormat pxlFrmt = new PixelFormat();
    PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
    Log.i(TAG,"@"+caller+": Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
    Log.i(TAG,"@"+caller+": Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));
    int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;        
    mVideoSource = new byte[sz];
    mCamera.addCallbackBuffer(mVideoSource);

    Log.i(TAG, "@"+caller+": backbuffer initilization finished");
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
        Log.i(TAG, "@SurfaceCreated: preview started");
    } catch (Exception e){
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // If your preview can change or rotate, take care of those events here.
    // Make sure to stop the preview before resizing or reformatting it.
        if (mHolder.getSurface() == null) {
            Log.i(TAG,"No proper holder");
        return;
    }
    try {
        mCamera.stopPreview();
    } catch (Exception e){
        Log.i(TAG,"tried to stop a non-existent preview");
        return;
    }

    createBuffers("surfaceChanged",findBestResolution(w, h));        
    }

    public void onPreviewFrame(byte[] data, Camera camera) {
        Log.i(TAG,"@onPreviewFrame: Invoked");
        Camera.Parameters params = camera.getParameters();
    Camera.Size camSize = params.getPictureSize();
    int w = camSize.width;
    int h = camSize.height;

    Log.i(TAG,"@onPreviewFrame: Piture Width " + Integer.toString(w));
    Log.i(TAG,"@onPreviewFrame: Piture Height " + Integer.toString(h));
    Log.i(TAG,"@onPreviewFrame: Piture format " + Integer.toString(params.getPictureFormat()));
    PixelFormat pxlFrmt = new PixelFormat();
    PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);
    Log.i(TAG,"@onPreviewFrame: Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
    Log.i(TAG,"@onPreviewFrame: Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));

    mBackBuffer     = BitmapFactory.decodeByteArray(data, 0, data.length);        

    Log.i(TAG,"@onPreviewFrame: Back buffer set.");
    invalidate();
    }

    @Override
    protected void onDraw(Canvas pCanvas) {
        super.onDraw(pCanvas);
        Log.i(TAG,"@onDraw: Invoked");
    if (mCamera != null) {
        Log.i(TAG,"@onDraw: Bbefore draw call to canvas");
        pCanvas.drawBitmap(mBackBuffer, 0, 0, mPaint);
        mCamera.addCallbackBuffer(mVideoSource);
        Log.i(TAG,"@onDraw: Draw finished");
    }
    }    

    /** Check if this device has a camera */
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
        } else {
        // no camera on this device
        return false;
        }
    }

    /** A safe way to get an instance of the Camera object. */
    private Camera getCameraInstance(){
        Camera c = null;
        if(checkCameraHardware(mContext)) {
            try {
                Log.i(TAG, "Trying to open the camera");
            c = Camera.open(0);
            Log.i(TAG, "Camera opened successfully.");
            }
            catch (Exception e){
                Log.i(TAG, e.getMessage());
            }
        }
        return c;
    }

    private void releaseCamera(){
    if (mCamera != null){
        mCamera.release();        // release the camera for other applications
        mCamera = null;
    }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
        mCamera.stopPreview();
        releaseCamera();
        mVideoSource = null;
        mBackBuffer = null;
    }
    }
}

1 个答案:

答案 0 :(得分:0)

好的,在通过android文档仔细重复阅读之后想出了几件事。显然预览[尺寸|格式]与图片[尺寸/格式]完全不同,我认为它们之前是相同的。因此,这修复了由于不正确的数据格式导致的渲染问题和崩溃。它还自动澄清了我对相机参数变化的困惑。

整个例子现在正在运作。但是,我看到两层预览,一个是由相机直接渲染的,另一个是我渲染的onDraw。我不确定我是否应该看到他们两个。以下是固定代码。

如果有人可能花时间在此,请感谢您。现在,我将努力将整个onPreviewFrame逻辑移动到本机代码以加快速度! :)

public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {

    private static final String TAG = "CamPreview";
    private static final int mPreviewWidth = 1280;
    private static final int mPreviewHeight = 720;
    private static final int mFPSXOff = 72;
    private static final int mFPSYOff = 72;
    private static final int mFPSSize = 64;

    private float mTotalTime;
    private float mFrameCount;
    private String mFPS;
    private long  mStart;
    private Context mContext;
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private byte[] mVideoSource;
    private Bitmap mBackBuffer;
    private Paint  mPaint; 

    public CamPreview(Context context) {
    super(context);
    mContext = context;
    mHolder = getHolder();
    mCamera = getCameraInstance();
    mHolder.addCallback(this);
    mFrameCount = 0;
    mTotalTime = 0;
    mFPS = "0 FPS";
    mStart = 0;
    mPaint = new Paint();
    mPaint.setColor(0xFFFF0000);
    mPaint.setTextSize(mFPSSize);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
        mCamera.setPreviewDisplay(null);
        mCamera.setPreviewCallbackWithBuffer(this);
        this.setWillNotDraw(false);
    } catch (IOException eIOException) {
        Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
        throw new IllegalStateException();
    }
    }

    private Size findBestResolution(int pWidth, int pHeight) {
    List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
    Size lSelectedSize = mCamera.new Size(0, 0);
    for (Size lSize : lSizes) {
        if ((lSize.width <= pWidth)
         && (lSize.height <= pHeight)
         && (lSize.width >= lSelectedSize.width)
         && (lSize.height >= lSelectedSize.height)) {
            lSelectedSize = lSize;
        }
    }
    if ((lSelectedSize.width == 0)
                    || (lSelectedSize.height == 0)) {
        lSelectedSize = lSizes.get(0);
    }
    return lSelectedSize;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

        if (mHolder.getSurface() == null) {
            Log.i(TAG,"No proper holder");
        return;
    }
    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        Log.i(TAG,"tried to stop a non-existent preview");
        return;
    }
    PixelFormat pxlFrmt = new PixelFormat();
        Camera.Parameters camParams = mCamera.getParameters();
    Size previewSize    = findBestResolution(w, h);
    int previewWidth    = previewSize.width;
    int previewHeight   = previewSize.height;
    camParams.setPreviewSize(previewWidth,previewHeight);
    camParams.setPreviewFormat(ImageFormat.NV21);
    mCamera.setParameters(camParams);
    mCamera.setDisplayOrientation(90);
    mBackBuffer         = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
    PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
    int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;        
    mVideoSource = new byte[sz];
    mCamera.addCallbackBuffer(mVideoSource);        

    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
        Log.i(TAG, "@SurfaceChanged: preview started");
    } catch (Exception e){
        Log.d(TAG, "@SurfaceChanged:Error starting camera preview: " + e.getMessage());
    }
    mFrameCount = 0;
    mTotalTime = 0;
    mStart = SystemClock.elapsedRealtime();
    }

    public void onPreviewFrame(byte[] data, Camera camera) {
        Log.i(TAG,"@onPreviewFrame: Invoked");
        Camera.Parameters params = camera.getParameters();
    Camera.Size camSize = params.getPreviewSize();
    int w = camSize.width;
    int h = camSize.height;
    PixelFormat pxlFrmt = new PixelFormat();
    PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);

    try {
        YuvImage yuv = new YuvImage(data,ImageFormat.NV21,w,h,null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuv.compressToJpeg(new Rect(0,0,w,h), 100, baos);
        byte[] jpgData = baos.toByteArray();
        mBackBuffer = BitmapFactory.decodeByteArray(jpgData, 0, jpgData.length);
    } catch (Exception e) {
        ;
    }
    Log.i(TAG,"@onPreviewFrame: Backbuffer set.");
    postInvalidate();
    mFrameCount++;
    long end = SystemClock.elapsedRealtime();
    mTotalTime += (end-mStart);
    mStart = end;
    mFPS = Float.toString((1000*mFrameCount/mTotalTime))+" fps";
    }

    @Override
    protected void onDraw(Canvas pCanvas) {
        Log.i(TAG,"@onDraw: Invoked");
    if (mCamera != null) {
        if(mBackBuffer==null) {
            Log.i(TAG, "Back buffer is null :((((((( ");
        } else {
            pCanvas.drawBitmap(mBackBuffer, 0, 0, null);
            pCanvas.drawText(mFPS, mFPSXOff, mFPSYOff, mPaint);
            mCamera.addCallbackBuffer(mVideoSource);
        }
    }
    }    

    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        return true;
        } else {
        return false;
        }
    }

    private Camera getCameraInstance(){
        Camera c = null;
        if(checkCameraHardware(mContext)) {
            try {
            c = Camera.open(0);
            Log.i(TAG, "Camera opened successfully");
            Camera.Parameters params = c.getParameters();
            params.setPreviewFormat(ImageFormat.NV21);
            params.setPreviewSize(mPreviewWidth, mPreviewHeight);
            c.setParameters(params);
            Log.i(TAG, "NV21 format set to camera with resolution 1280x720");
            }
            catch (Exception e){
                Log.i(TAG, e.getMessage());
            }
        }
        return c;
    }

    private void releaseCamera(){
    if (mCamera != null){
        mCamera.release();
        Log.i(TAG,"@releaseCamera:");
        mCamera = null;
    }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
        mCamera.stopPreview();
        mCamera.setPreviewCallback(null);
        releaseCamera();
        mVideoSource = null;
        mBackBuffer = null;
    }
    }
}