Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用

Posted

技术标签:

【中文标题】Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用【英文标题】:Android camera , onPictureTaken(byte[] imgData, Camera camera) method & PictureCallback never called 【发布时间】:2013-10-30 13:22:42 【问题描述】:

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

我的应用可以正确预览并且不会抛出任何错误或期望,但是我的 onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未被调用,因此我无法获取图像并继续进一步扫描。

以下是负责相机逻辑的活动的实际代码。 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_
    android:layout_
    android:orientation="vertical"
    android:padding="20dip" >

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

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

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

    <Button
        android:id="@+id/webbutton"
        android:layout_
        android:layout_
        android:text="@string/web_button"
        android:textColor="@color/mytextcolor" />

    <FrameLayout
    android:id="@+id/FrameLayoutForPreview"
    android:layout_
    android:layout_
    android:layout_weight="1">

        <SurfaceView
            android:id="@+id/surfaceViewBarcodeScanner"
            android:layout_
            android:layout_ />

    </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】:

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

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

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

【讨论】:

另外,在 takePicture() 之后不需要stopPreview()。拍照后系统会停止预览。 @Pescis 你绝对写我把 stopPreview() 和 startPreview() 移到了 takePictiure 方法的末尾,这些方法现在可以工作了,新代码在上面。 请回答这个问题***.com/questions/39061991/…

以上是关于Android camera , onPictureTaken(byte[] imgData, Camera camera) 方法和 PictureCallback 从未调用的主要内容,如果未能解决你的问题,请参考以下文章

android中的camera和camera2权限

问题支持Android相机的API和camera2 API问题,怎么解决

Android Camera2 拍照入门学习

android camera:camera模组CMM介绍

每次“Camera camera = Camera.open();”时,Android都会抛出错误叫做

Android Camera2 教程 · 第一章 · 概览