【问题标题】:Imageformat set in surfaceChanged changes to something else in onPreviewFramesurfaceChanged 中设置的图像格式更改为 onPreviewFrame 中的其他内容
【发布时间】:2014-03-10 23:05:41
【问题描述】:

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

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

甚至图片格式从 NV21(surfaceChanged 中的 17)变为 JP​​EG(onPreviewFrame 中的 256)。

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

如果我能够在 onPreviewFrame 中成功获得 NV21 的预览格式,我该如何将其转换为 ARGB 格式?我已经尝试过在 stackoverflow 中发布的方法,但是由于索引超出范围,传递给 onPreviewFrame 的数据失败,这导致我首先检查图像格式。如果有人尝试过类似的事情,请让我知道我在创建过程中缺少什么导致这个混乱:(。

我试图通过最初从传递给 onPreviewFrame 的 byte[] 创建 YuvImage 来创建位图,这给了我绿色的后期图像(有时都是绿色或垃圾)!

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

【问题讨论】:

    标签: android android-camera


    【解决方案1】:

    好的,在我仔细阅读了 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;
        }
        }
    }
    

    【讨论】:

    • 以本机方法进行解码而不是使用 YuvImage 使 FPS 翻倍。
    猜你喜欢
    • 2012-10-20
    • 1970-01-01
    • 2019-03-27
    • 2014-06-21
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多