Android Camera2 拍照流程

摄像头管理器。这是一个全新的系统管理器,专门用于检测系统摄像头、打开系统摄像头。另外,调用CameraManager的getCameraCharacteristics(String cameraId)方法即可获取指定摄像头的相关特性。

3.2 CameraCharacteristics:

摄像头特性。该对象通过CameraManager来获取,用于描述特定摄像头所支持的各种特性。类似与原来的CameraInfo 。

3.3 CameraDevice:

代表系统摄像头。该类的功能类似于早期的Camera类。而每个CameraDevice自己会负责创建CameraCaptureSession 以及建立 CaptureRequest。

3.4 CameraCaptureSession:

这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。为了监听CameraCaptureSession的创建过程,以及监听CameraCaptureSession的拍照过程,Camera-v2-API为CameraCaptureSession提供了StateCallback、CaptureCallback等内部类。

3.5 CameraRequest和CameraRequest.Builder:

当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。

四、相机预览与拍照流程


如果你看不太懂流程图,没关系,待会儿我们通过代码就可以更好的理解了。首先,Google官方推荐的Camera2控制拍照的步骤大致如下。

4.1 Camera2拍照的步骤

(1)用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。

(2)当摄像头被打开之后会回调接口mStateCallback.onOpened,程序即可获取CameraDevice —— 即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。

(3)不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。

(4)通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。

(5)调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。相机的预览与拍照流程我们基本了解了。

(6)预览时,是将mSurfaceHolder.getSurface()作为目标,使用setRepeatingRequest()方法,显示拍照结果时,是将mImageReader.getSurface()作为目标,使用capture()方法。

然后这里还有一个大招:Google官方Camera2拍照的demo的地址:点击跳转github

4.2 首先是我们的layout代码

首先权限不能忘:


uses-permission android:name=”android.permission.CAMERA” / 

uses-feature android:name=”android.hardware.camera” / 

uses-feature android:name=”android.hardware.camera.autofocus” /




<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">



    <TextureView

        android:id="@+id/texture"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:layout_weight="1"/>



    <LinearLayout

        android:orientation="horizontal"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:id="@+id/cancelButton"

            android:text="取消"

            android:visibility="invisible"/>

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:id="@+id/captureButton"

            android:text="拍照"

            android:visibility="visible"/>

        <Button

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:id="@+id/saveButton"

            android:text="保存"

            android:visibility="invisible"/>

    </LinearLayout>

</LinearLayout>



4.3 实现代码


public class customCarmeraActivity extends AppCompatActivity {



    private static final int REQUEST_CAMERA_PERMISSION = 1;



    private static final int STATE_PREVIEW = 1;

    private static final int STATE_WAITING_PRECAPTURE = 2;

    private int mState = STATE_PREVIEW;



    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();



    ///为了使照片竖直显示

    static {

        ORIENTATIONS.append(Surface.ROTATION_0, 90);

        ORIENTATIONS.append(Surface.ROTATION_90, 0);

        ORIENTATIONS.append(Surface.ROTATION_180, 270);

        ORIENTATIONS.append(Surface.ROTATION_270, 180);

    }



    private CameraManager mCameraManagerm;

    private CameraDevice mCameraDevice;

    private String mCameraId;

    private HandlerThread mBackgroundThread;

    private Handler mBackgroundHandler;



    private TextureView mTextureView;



    private ImageReader mImageReader;

    private File mFile;



    private CaptureRequest.Builder mPreviewBuilder;

    private CaptureRequest mPreviewRequest;

    private CameraCaptureSession mCaptureSession;





    private Semaphore mCameraOpenCloseLock = new Semaphore(1);



    /**

     * Starts a background thread and its {@link Handler}.

     */

    private void startBackgroundThread() {

        mBackgroundThread = new HandlerThread("CameraBackground");

        mBackgroundThread.start();

        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());

    }



    /**

     * Stops the background thread and its {@link Handler}.

     */

    private void stopBackgroundThread() {

        mBackgroundThread.quitSafely();

        try {

            mBackgroundThread.join();

            mBackgroundThread = null;

            mBackgroundHandler = null;

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }



    /**

     *

     * @TextureView.SurfaceTextureListener:public static interface

     * @onSurfaceTextureAvailable:Invoked when a TextureView's SurfaceTexture is ready for use.

     * @onSurfaceTextureSizeChanged:Invoked when the SurfaceTexture's buffers size changed.

     * @onSurfaceTextureDestroyed:Invoked when the specified SurfaceTexture is about to be destroyed.

     * @onSurfaceTextureUpdated:Invoked when the specified SurfaceTexture is updated through updateTexImage().

     */

    private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {

        @Override

        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {

            openCamera(width, height);

        }



        @Override

        public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {

            //configureTransform(width, height);

        }



        @Override

        public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {

            return true;

        }



        @Override

        public void onSurfaceTextureUpdated(SurfaceTexture texture) {

        }

    };



    /**

     * Shows a {@link Toast} on the UI thread.

     *

     * @param text The message to show

     */

    private void showToast(final String text) {

        runOnUiThread(new Runnable() {

                @Override

                public void run() {

                    Toast.makeText(customCarmeraActivity.this, text, Toast.LENGTH_SHORT).show();

                }

            });

    }





    /**

     * Capture a still picture. This method should be called when we get a response in

     * {@link #mCaptureCallback} from both {@link #lockFocus()}.

     */

    private void captureStillPicture() {

        try {

            if (null == mCameraDevice) {

                return;

            }

            // This is the CaptureRequest.Builder that we use to take a picture.

            final CaptureRequest.Builder captureBuilder =

                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);

            captureBuilder.addTarget(mImageReader.getSurface());//拍照时,是将mImageReader.getSurface()作为目标



            // Use the same AE and AF modes as the preview.

            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,

                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,

                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

            // Orientation

            int rotation = getWindowManager().getDefaultDisplay().getRotation();

            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));



            CameraCaptureSession.CaptureCallback CaptureCallback

                    = new CameraCaptureSession.CaptureCallback() {



                @Override

                public void onCaptureCompleted(@NonNull CameraCaptureSession session,

                                               @NonNull CaptureRequest request,

                                               @NonNull TotalCaptureResult result) {

                    showToast("Saved: " + mFile);



                    //Log.d("customCarmeraActivity", mFile.toString());

                    unlockFocus();//恢复预览

                }

            };



            mCaptureSession.stopRepeating();

            mCaptureSession.abortCaptures();

            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);

        } catch (CameraAccessException e) {

            e.printStackTrace();

        }

    }





    private CameraCaptureSession.CaptureCallback mCaptureCallback =

            new CameraCaptureSession.CaptureCallback() {

                @Override

                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,

                                               TotalCaptureResult result) {

                    //Log.d("linc","mSessionCaptureCallback, onCaptureCompleted");

                    mCaptureSession = session;

                    checkState(result);

                }



                @Override

                public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,

                                                CaptureResult partialResult) {

                    Log.d("linc","mSessionCaptureCallback,  onCaptureProgressed");

                    mCaptureSession = session;

                    checkState(partialResult);

                }



                private void checkState(CaptureResult result) {

                    switch (mState) {

                        case STATE_PREVIEW:

                            // We have nothing to do when the camera preview is working normally.

                            break;

                        case STATE_WAITING_PRECAPTURE:

                            Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);

                            if (afState == null) {

                                captureStillPicture();

                            } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {

                                // CONTROL_AE_STATE can be null on some devices

                                Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);

                                if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {

                                    //mState = STATE_PICTURE_TAKEN;

                                    captureStillPicture();

                                } else {

                                    //runPrecaptureSequence();//视频拍摄

                                }

                            }

                            break;

                    }

                }



            };

    private void createCameraPreviewSession() {

        try {

            SurfaceTexture texture = mTextureView.getSurfaceTexture();

            //assert(texture != null);



            // We configure the size of default buffer to be the size of camera preview we want.

            texture.setDefaultBufferSize(mTextureView.getWidth(), mTextureView.getHeight());

            // This is the output Surface we need to start preview.

            Surface surface = new Surface(texture);



            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            mPreviewBuilder.addTarget(surface);//预览时,是将Surface()作为目标

            mState = STATE_PREVIEW;

            mCameraDevice.createCaptureSession(

                    Arrays.asList(surface, mImageReader.getSurface()),

                    new CameraCaptureSession.StateCallback() {

                        @Override

                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

                            // The camera is already closed

                            if (null == mCameraDevice) {

                                return;

                            }

                            mCaptureSession = cameraCaptureSession;

                            try {

                                mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,

                                        CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                                mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE,

                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

                                mPreviewRequest = mPreviewBuilder.build();

                                mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);

                            } catch (CameraAccessException e) {

                                e.printStackTrace();

                                Log.e("linc","set preview builder failed."+e.getMessage());

                            }

                        }



                        @Override

                        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

                            Toast.makeText(customCarmeraActivity.this, "Camera configuration Failed", Toast.LENGTH_SHORT).show();

                        }

                    },mBackgroundHandler);

        } catch (CameraAccessException e) {

            e.printStackTrace();

        }

    }

    /**

     *

     * @CameraDevice.StateCallback:public static abstract class

     * @onOpened:This method is called when the camera is opened.  We start camera preview here.

     * @onDisconnected:The method called when a camera device is no longer available for use.

     * @onError:The method called when a camera device has encountered a serious error.

     */

    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {



        @Override

        public void onOpened(CameraDevice cameraDevice) {

            mCameraOpenCloseLock.release();

            mCameraDevice = cameraDevice;

            createCameraPreviewSession();

        }



        @Override

        public void onDisconnected(CameraDevice cameraDevice) {

            mCameraOpenCloseLock.release();

            cameraDevice.close();

            mCameraDevice = null;

        }



        @Override

        public void onError(CameraDevice cameraDevice, int error) {

            mCameraOpenCloseLock.release();

            cameraDevice.close();

            mCameraDevice = null;

        }

    };



    private void getCameraId() {

        try {

            //Return the list of currently connected camera devices by identifier, including cameras that may be in use by other camera API clients

            for (String cameraId : mCameraManagerm.getCameraIdList()) {

                //Query the capabilities of a camera device

                CameraCharacteristics characteristics = mCameraManagerm.getCameraCharacteristics(cameraId);

                if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {

                    continue;

                }

                mCameraId = cameraId;

                return;

            }

        } catch (CameraAccessException e) {

            e.printStackTrace();

        }

    }



    /**

     * Saves a JPEG {@link Image} into the specified {@link File}.

     */

    private static class ImageSaver implements Runnable {



        /**

         * The JPEG image

         */

        private final Image mImage;

        /**

         * The file we save the image into.

         */

        private final File mFile;



        ImageSaver(Image image, File file) {

            mImage = image;

            mFile = file;

        }



        @Override

        public void run() {

            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();

            byte[] bytes = new byte[buffer.remaining()];

            buffer.get(bytes);

            FileOutputStream output = null;

            try {

                output = new FileOutputStream(mFile);

                output.write(bytes);

            } catch (IOException e) {

                e.printStackTrace();

            } finally {

                mImage.close();

                if (null != output) {

                    try {

                        output.close();

                    } catch (IOException e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }



    private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {

        @Override

        public void onImageAvailable(ImageReader reader) {

            //showToast("onImageAvailable: " );

            mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(), mFile));

        }

    };

    //TODO 执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知:

    @Override

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (requestCode == REQUEST_CAMERA_PERMISSION) {

            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1

                //permission was granted, yay! Do the contacts-related task you need to do.

                //这里进行授权被允许的处理

            } else {

                //permission denied, boo! Disable the functionality that depends on this permission.

                //这里进行权限被拒绝的处理

            }

        } else {

            super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        }

    }

    /**

     * Opens the camera specified by {@link Camera2BasicFragment#mCameraId}.

     */

    private void openCamera(int width, int height) {

        //检查相机服务的访问权限

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

            //Toast.makeText(this,"Lacking privileges to access camera service, please request permission first",Toast.LENGTH_SHORT).show();

            Log.e("customCarmeraActivity.openCamera","Lacking privileges to access camera service, please request permission first");

            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);//API21后,向用户请求相机使用权限,然后执行onRequestPermissionsResult回调

            return;

        }



        getCameraId();

        //assert(mCameraId != null);



        mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG,/*maxImages*/7);

        mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);



        try {

            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {

                throw new RuntimeException("Time out waiting to lock camera opening.");

            }

            mCameraManagerm.openCamera(mCameraId, mStateCallback, mBackgroundHandler);

        } catch (CameraAccessException e) {

            e.printStackTrace();

        } catch (InterruptedException e) {

            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);

        }

    }

    /**

     * Closes the current {@link CameraDevice}.

     */

    private void closeCamera() {

        try {

            mCameraOpenCloseLock.acquire();

            if (null != mCaptureSession) {

                mCaptureSession.close();

                mCaptureSession = null;

            }

            if (null != mCameraDevice) {

                mCameraDevice.close();

                mCameraDevice = null;

            }

            if (null != mImageReader) {

                mImageReader.close();

                mImageReader = null;

            }

        } catch (InterruptedException e) {

            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);

        } finally {

            mCameraOpenCloseLock.release();

        }

    }

    /**

     * Unlock the focus. This method should be called when still image capture sequence is

     * finished.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值