Android相机,onPictureTaken(byte [] imgData,相机相机)方法& PictureCallback从未调用过

时间:2013-10-21 21:16:00

标签: java android camera android-camera

我有一个自定义相机应用程序,它预览SurfaceView上的相机视频输出并尝试拍照,图片应该由“xzing”扫描仪API处理,以解码图像中的任何条形码。

我的应用程序预览正确,并没有抛出任何错误或期望,然而我的onPictureTaken(byte [] imgData,相机相机)方法& PictureCallback永远不会被调用,因此我无法获得图像&继续进一步扫描。

以下是处理Camera逻辑的活动的实际代码。 onPictureTaken(byte [] imgData,Camera camera)方法& PictureCallback在这个类中(ScanVinFromBarcodeActivity.java)看看打击:

package com.ty.tyownerspoc.barcode;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource; 
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ty.tyownerspoc.R;

public class ScanVinFromBarcodeActivity extends Activity {

    private Camera globalCamera;
    private int cameraId = 0;
    private TextView VINtext = null;
    private View scanButton = null;
    // bitmap from camera
    private Bitmap bmpOfTheImageFromCamera = null;
    // global flag whether a camera has been detected
    private boolean isThereACamera = false;
    // surfaceView for preview object
    private FrameLayout frameLayoutBarcodeScanner = null;
    private CameraPreview newCameraPreview = null;
    private SurfaceView surfaceViewBarcodeScanner = null;
    private int counter = 0; 
    private volatile boolean finishedPictureTask  = false;
    /*
     * This method , finds FEATURE_CAMERA, opens the camera, set parameters ,
     * add CameraPreview to layout, set camera surface holder, start preview
     */
    private void initializeGlobalCamera() {
        try {
            if (!getPackageManager().hasSystemFeature(
                    PackageManager.FEATURE_CAMERA)) {
                Toast.makeText(this, "No camera on this device",
                        Toast.LENGTH_LONG).show();
            } else { // check for front camera ,and get the ID
                cameraId = findFrontFacingCamera();
                if (cameraId < 0) {

                    Toast.makeText(this, "No front facing camera found.",
                            Toast.LENGTH_LONG).show();
                } else {

                    Log.d("ClassScanViewBarcodeActivity",
                            "camera was found , ID: " + cameraId);
                    // camera was found , set global camera flag to true
                    isThereACamera = true;
                    // OPEN
                    globalCamera = Camera.open(cameraId);

                    // pass surfaceView to CameraPreview
                    newCameraPreview = new CameraPreview(this, globalCamera);
                    // pass CameraPreview to Layout
                    frameLayoutBarcodeScanner.addView(newCameraPreview);
                    try {
                        globalCamera
                                .setPreviewDisplay(surfaceViewBarcodeScanner
                                        .getHolder());
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // PREVIEW
                    globalCamera.startPreview();

                    Log.d("ClassScanViewBarcodeActivity",
                            "camera opened & previewing");
                }
            }// end else ,check for front camera
        }// end try
        catch (Exception exc) {

            // in case of exception release resources & cleanup
            if (globalCamera != null) {
                globalCamera.stopPreview();
                globalCamera.setPreviewCallback(null);
                globalCamera.release();
                globalCamera = null;

            }
            Log.d("ClassScanViewBarcodeActivity initializeGlobalCamera() exception:",
                    exc.getMessage());
        }// end catch
    }

    // onCreate, instantiates layouts & surfaceView used for video preview
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_barcode_vin_scanner);
        Log.d("ClassScanViewBarcodeActivity", "onCreate ");

        // create surfaceView for previewing of camera image
        frameLayoutBarcodeScanner = (FrameLayout) findViewById(R.id.FrameLayoutForPreview);
        surfaceViewBarcodeScanner = (SurfaceView) findViewById(R.id.surfaceViewBarcodeScanner);

        initializeGlobalCamera();

        // create text area & scan button
        VINtext = (TextView) findViewById(R.id.mytext);
        scanButton = findViewById(R.id.webbutton);
        // on click listener, onClick take a picture
        scanButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {

                    // if true take a picture
                    if (isThereACamera) {
                        Log.d("ClassScanViewBarcodeActivity",
                                "setOnClickListener() isThereACamera: "+ isThereACamera);

                        //set picture format to JPEG, everytime makesure JPEg callback is called 
                         Parameters parameters = globalCamera.getParameters();              
                         parameters.setPictureFormat(ImageFormat.JPEG);
                         globalCamera.setParameters(parameters);                         
                         //take pic , should call Callback 
                        globalCamera.takePicture(null, null, jpegCallback);
                        // wait 1 sec , than start preview again
                        Thread.sleep(1000);
                        //STOP
                        globalCamera.stopPreview();
                        //start previewing again onthe SurfaceView in case use wants to take another pic/scan
                        globalCamera.startPreview();

                    }

                }// end try
                catch (Exception exc) {

                    // in case of exception release resources & cleanup
                    if (globalCamera != null) {
                        globalCamera.stopPreview();
                        globalCamera.setPreviewCallback(null);
                        globalCamera.release();
                        globalCamera = null;

                    }
                    Log.d("ClassScanViewBarcodeActivity setOnClickListener() exceprtion:",
                            exc.getMessage());
                }// end catch

            }// end on Click
        });// end OnClickListener() implementation

    }// end onCreate

    @Override
    protected void onResume() {

        Log.d("ClassScanViewBarcodeActivity, onResume() globalCamera:",
                String.valueOf(globalCamera));

        if (globalCamera != null) {
            // START PREVIEW
            globalCamera.startPreview();
        } else {
            initializeGlobalCamera();
        }

        super.onResume();
    }

    @Override
    protected void onStop() {

        if (globalCamera != null) {
            globalCamera.stopPreview();
            globalCamera.setPreviewCallback(null);
            globalCamera.release();
            globalCamera = null;
        }
        super.onStop();
    }

    //callback used by takePicture()
    PictureCallback jpegCallback = new PictureCallback() {



        public void onPictureTaken(byte[] imgData, Camera camera) {
            BinaryBitmap bitmap = null;
            try {



                Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");

                //save image to sd card 
                savePicture(imgData);

                // get the bitmap from camera imageData
                bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
                        imgData, 0, imgData.length);


                if (bmpOfTheImageFromCamera != null) {
                    //Galaxy S3 , incorrect rotation issue rotate to correct rotation
                    Matrix matrix = new Matrix();
                    matrix.postRotate(90);
                    Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);
                    // convert bitmap to binary bitmap
                    bitmap = cameraBytesToBinaryBitmap(rotatedBitmap);


                    if (bitmap != null) {
                        // decode the VIN
                        String VIN = decodeBitmapToString(bitmap);
                        Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
                                VIN);
                        VINtext.setText(VIN);
                    } else {
                        Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));

                    }
                } else {
                    Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
                            String.valueOf(bmpOfTheImageFromCamera));

                }
            }// end try

            catch (Exception exc) {
                exc.getMessage();

                Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
                        exc.getMessage());
            }

        }// end onPictureTaken()
    };// jpegCallback implementation


    /*
     * created savePicture(byte [] data) for testing 
     */
    public void savePicture(byte [] data)
    {
        Log.d( "ScanVinFromBarcodeActivity " , "savePicture(byte [] data)");
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
        String date = dateFormat.format(new Date());
        String photoFile = "Picture_"+counter+"_"+ date + ".jpg";

        File sdDir = Environment
                  .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);


        String filename =sdDir + File.separator + photoFile;

        File pictureFile = new File(filename);

        try {
          FileOutputStream fos = new FileOutputStream(pictureFile);
          fos.write(data);
          fos.close();

          Toast.makeText(this, "New Image saved:" + photoFile,
              Toast.LENGTH_LONG).show();

        } catch (Exception error) {
            Log.d( "File not saved: " , error.getMessage());
          Toast.makeText(this, "Image could not be saved.",
              Toast.LENGTH_LONG).show();
        }
        counter++;
      }



    private int findFrontFacingCamera() {
        int cameraId = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            CameraInfo info = new CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
                Log.d("ClassScanViewBarcodeActivity , findFrontFacingCamera(): ",
                        "Camera found");
                cameraId = i;
                break;
            }
        }
        return cameraId;
    }// end findFrontFacingCamera()

    @Override
    protected void onPause() {
        if (globalCamera != null) {
            globalCamera.stopPreview();
            globalCamera.setPreviewCallback(null);
            globalCamera.release();
            globalCamera = null;
        }
        super.onPause();
    }// end onPause()

    public String decodeBitmapToString(BinaryBitmap bitmap) {
        Reader reader = null;
        Result result = null;
        String textResult = null;
        try {

            reader = new MultiFormatReader();
            if (bitmap != null) {
                result = reader.decode(bitmap);
                if (result != null) {
                    textResult = result.getText();
                } else {
                    Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): result = ",
                            String.valueOf(result));
                }
            } else {
                Log.d("ClassScanViewBarcodeActivity , String decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
                        String.valueOf(bitmap));
            }
            /*
             * byte[] rawBytes = result.getRawBytes(); BarcodeFormat format =
             * result.getBarcodeFormat(); ResultPoint[] points =
             * result.getResultPoints();
             */

        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ChecksumException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (FormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }

        return textResult;
    }// end decodeBitmapToString (BinaryBitmap bitmap)

    public BinaryBitmap cameraBytesToBinaryBitmap(Bitmap bitmap) {
        BinaryBitmap binaryBitmap = null;
        if (bitmap != null) {

            int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
            bitmap.getPixels(pixels, 0, 0, bitmap.getWidth() - 1,
                    bitmap.getHeight() - 1, bitmap.getWidth(),
                    bitmap.getHeight());

            RGBLuminanceSource source = new RGBLuminanceSource(
                    bitmap.getWidth(), bitmap.getHeight(), pixels);

            HybridBinarizer bh = new HybridBinarizer(source);
            binaryBitmap = new BinaryBitmap(bh);
        } else {
            Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap): bitmap = ",
                    String.valueOf(bitmap));
        }

        return binaryBitmap;
    }

    @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    /*
     * The method getScreenOrientation() return screen orientation either
     * landscape or portrait. IF width < height , than orientation = portrait,
     * ELSE landscape For backwards compatibility we use to methods to detect
     * the orientation. The first method is for API versions prior to 13 or
     * HONEYCOMB.
     */
    public int getScreenOrientation() {

        int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        // if API version less than 13
        Display getOrient = getWindowManager().getDefaultDisplay();
        int orientation = Configuration.ORIENTATION_UNDEFINED;

        if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
            // Do something for API version less than HONEYCOMB

            if (getOrient.getWidth() == getOrient.getHeight()) {
                orientation = Configuration.ORIENTATION_SQUARE;
            } else {
                if (getOrient.getWidth() < getOrient.getHeight()) {
                    orientation = Configuration.ORIENTATION_PORTRAIT;
                } else {
                    orientation = Configuration.ORIENTATION_LANDSCAPE;
                }
            }
        } else {
            // Do something for API version greater or equal to HONEYCOMB

            Point size = new Point();
            this.getWindowManager().getDefaultDisplay().getSize(size);
            int width = size.x;
            int height = size.y;

            if (width < height) {
                orientation = Configuration.ORIENTATION_PORTRAIT;
            } else {
                orientation = Configuration.ORIENTATION_LANDSCAPE;
            }
        }

        return orientation;

    }// end getScreenOrientation()
}// end class ScanVinFromBarcodeActivity

用于在SurfaceView上显示实时相机的预览类(CameraPreview.java):

package com.ty.tyownerspoc.barcode;

import java.io.IOException;
import java.util.List;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.hardware.Camera.CameraInfo;

/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
        SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private Context context;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        this.context = context;

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the
        // preview.
        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {

        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty. Take care of releasing the Camera preview in your activity.
    }

    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) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }
                Camera.Parameters p = mCamera.getParameters();

        // get width & height of the SurfaceView
        int SurfaceViewWidth = this.getWidth();
        int SurfaceViewHeight = this.getHeight();

        List<Size> sizes = p.getSupportedPreviewSizes();
         Size optimalSize = getOptimalPreviewSize(sizes, SurfaceViewWidth, SurfaceViewHeight);


        // set parameters
        p.setPreviewSize(optimalSize.width, optimalSize.height);

        /*rotate the image by 90 degrees clockwise , in order to correctly displayed the image , images seem to be -90 degrees (counter clockwise) rotated 
         * I even tried setting it to  p.setRotation(0); , but still no effect.
         */
        mCamera.setDisplayOrientation(90);

        mCamera.setParameters(p);

        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();

        } catch (Exception e) {
            Log.d("CameraPreview ,  surfaceCreated() , orientation: ",
                    String.valueOf(e.getMessage()));
        }
    }// end surfaceChanged()
     static Size getOptimalPreviewSize(List <Camera.Size>sizes, int w, int h) {
            final double ASPECT_TOLERANCE = 0.1;
            final double MAX_DOWNSIZE = 1.5;

            double targetRatio = (double) w / h;
            if (sizes == null) return null;

            Size optimalSize = null;
            double minDiff = Double.MAX_VALUE;

            int targetHeight = h;

            // Try to find an size match aspect ratio and size
            for (Camera.Size size : sizes) {
              double ratio = (double) size.width / size.height;
              double downsize = (double) size.width / w;
              if (downsize > MAX_DOWNSIZE) {
                //if the preview is a lot larger than our display surface ignore it
                //reason - on some phones there is not enough heap available to show the larger preview sizes
                continue;
              }
              if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
              if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
              }
            }
            // Cannot find the one match the aspect ratio, ignore the requirement
            //keep the max_downsize requirement
            if (optimalSize == null) {
              minDiff = Double.MAX_VALUE;
              for (Size size : sizes) {
                double downsize = (double) size.width / w;
                if (downsize > MAX_DOWNSIZE) {
                  continue;
                }
                if (Math.abs(size.height - targetHeight) < minDiff) {
                  optimalSize = size;
                  minDiff = Math.abs(size.height - targetHeight);
                }
              }
            }
            //everything else failed, just take the closest match
            if (optimalSize == null) {
              minDiff = Double.MAX_VALUE;
              for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                  optimalSize = size;
                  minDiff = Math.abs(size.height - targetHeight);
                }
              }
            }
            return optimalSize;
          }

}//end Preview class

“ScanVinFromBarcodeActivity”活动的布局(activity_barcode_vin_scanner.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="20dip" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:padding="20dip"
        android:text="@string/decode_label"
        android:textColor="@color/mbackground1" />

    <TextView
        android:id="@+id/mytext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/mbackground2"
        android:gravity="center_horizontal"
        android:padding="20dip"
        android:textColor="@color/mytextcolor" />

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:padding="20dip"
        android:text="@string/continue_label"
        android:textColor="@color/mytextcolor" />

    <Button
        android:id="@+id/webbutton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/web_button"
        android:textColor="@color/mytextcolor" />

    <FrameLayout
    android:id="@+id/FrameLayoutForPreview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1">

        <SurfaceView
            android:id="@+id/surfaceViewBarcodeScanner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </FrameLayout>
</LinearLayout>

任何帮助将不胜感激。

由于

Updated & working onTakePicturen method, the that works , the PictureCallback returns (stopPreview & StartPreview were moved to inside this method in its finally block).


    public void onPictureTaken(byte[] imgData, Camera camera) {
        BinaryBitmap bitmap;
        try {



            Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");

            //save image to sd card 
            savePicture(imgData);

            // get the bitmap from camera imageData

            BitmapFactory.Options options = new BitmapFactory.Options();
              options.inSampleSize = 8; 
              //down sample
            bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
                    imgData, 0, imgData.length,options);


            if (bmpOfTheImageFromCamera != null) {

                Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): bmpOfTheImageFromCamera getByteCount(): ",
                        String.valueOf(bmpOfTheImageFromCamera.getByteCount()));
                //Galaxy S3 , incorrect rotation issue rotate to correct rotation
                /*Matrix matrix = new Matrix();
                matrix.postRotate(90);
                Bitmap rotatedBitmap = Bitmap.createBitmap(bmpOfTheImageFromCamera, 0, 0, bmpOfTheImageFromCamera.getWidth(), bmpOfTheImageFromCamera.getHeight(), matrix, true);*/
                // convert bitmap to binary bitmap
                bitmap = cameraBytesToBinaryBitmap(bmpOfTheImageFromCamera);


                if (bitmap != null) {
                    // decode the VIN
                    String VIN = decodeBitmapToString(bitmap);
                    Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
                            VIN);
                    VINtext.setText(VIN);
                } else {
                    Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));

                }
            } else {
                Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
                        String.valueOf(bmpOfTheImageFromCamera));

            }


        }// end try

        catch (Exception exc) {
            exc.getMessage();

            Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
                    exc.getMessage());
        }

        finally
        {
            globalCamera.stopPreview();
            //start previewing again onthe SurfaceView in case use wants to take another pic/scan
            globalCamera.startPreview();
        }

    }// end onPictureTaken()
};// jpegCallback implementation

1 个答案:

答案 0 :(得分:5)

我唯一能找到的可能是罪魁祸首是你在jpegCallback返回之前再次开始预览。根据{{​​3}},这是不允许的:

  

调用此方法后,在返回JPEG回调之前,不得调用startPreview()或拍摄另一张照片。

回调实际上放在UI线程队列中 - 它将暂停在Thread.sleep()中,同一个线程在实际进入回调之前会调用stopPreview()startPreview()。一般来说,如果你在UI线程上调用Thread.sleep() - 你做错了。所以,希望如果您删除sleep()并将其后面的内容放在jpegCallback中,那么您的问题就应该解决了。