android控制攝像頭使用Camera2拍照

Camera2是Android5.0對拍照全新的改版設計
其中主要涉及:

  • CameraMaganger:攝像頭的管理,主要檢測系統攝像頭、打開攝像頭,調用CameraManager的manager.getCameraCharacteristics(cameraId)獲取相機的屬性
  • CameraCharacteristics:攝像頭的特性,用於描述攝像機支持的所有特性。
  • CameraDevice:代表攝像頭,類似於早期的Camera
  • CameraCaptureSession:非常重要的Api,不管是預覽,拍照,都需要其來控制,控制預覽的方法setRepeatingRequest(),控制拍照Capture().
  • CameraRequest()和 CameraRequest.Builder(),當開始預覽或者拍照的時都需要傳入CameraRequest參數,CameraRequest代表一次捕獲請求,用於捕獲圖片的各種參數設置。

開發步驟:
1、獲取CameraManager實例,調用openCamera(String cameraId,callback , handler)
2、打開攝像頭成功之後,獲取CameraDevice對象。然後通過cameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()),
new CameraCaptureSession.StateCallback() ,Handler handler)創建Session
3、不管預覽還是錄製、或者拍照都需要使用cameraDevice.createCaptureRequest(int type)指定類型:CameraDevice.TEMPLATE_PREVIEW(預覽),CameraDevice.TEMPLATE_STILL_CAPTURE(拍照),CameraDevice.TEMPLATE_RECORD(拍攝)等。

完整例子:

package com.example.camera2demo;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class TakePhotoActivity extends AppCompatActivity {

    String TAG = "=======TakePhotoActivity=========";
    /*相機管理類*/
    private CameraManager manager;
    /*相機的方向  默認爲後置*/
    private String cameraId = "0";
    /*相機類*/
    private CameraDevice cameraDevice;


    private CameraCaptureSession captureSession;

    private CaptureRequest.Builder previewRequestBuilder;

    private ImageReader imageReader;
    private AutoFitTextureView mTextureView ;

    public final static SparseIntArray  o = new SparseIntArray();

    static {
        o.append(Surface.ROTATION_0,90);
        o.append(Surface.ROTATION_90,0);
        o.append(Surface.ROTATION_180,270);
        o.append(Surface.ROTATION_270,180);
    }




    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_take_photo);
        initView();
        init();
    }



    private void initView() {
        mTextureView = findViewById(R.id.mTextureView);
        mTextureView.setSurfaceTextureListener(surfaceTextureListener);

        findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                capturePicture();
            }
        });
    }



    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void init() {
        manager = (CameraManager) getSystemService(CAMERA_SERVICE);
    }




    @SuppressLint("MissingPermission")
    public void openCamera() {
        try {
            manager.openCamera(cameraId, callback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 創建預覽
     */
    private void createCameraPreviewSession() {
        try {
            previewRequestBuilder=cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            Surface surface =new Surface(mTextureView.getSurfaceTexture());
            previewRequestBuilder.addTarget(surface);
            cameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    try {
                        if (cameraDevice==null)return;

                        captureSession = session;
                        /*設置自動對焦模式*/
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                        /*設置自動曝光*/
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                        /*設置預覽是連續捕獲圖片*/
                        session.setRepeatingRequest(previewRequestBuilder.build(),null,null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                }
            },null);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    /**
     * 拍照
     */
    public void capturePicture(){

        try {
            CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);


            builder.addTarget(imageReader.getSurface());


            /*設置自動對焦模式*/
            builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            /*設置自動曝光*/
            builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);

            /*獲取設備方向*/
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            builder.set(CaptureRequest.JPEG_ORIENTATION,o.get(rotation));

            Log.v(TAG,"設備方向 = "+rotation) ;
            /*停止取景*/
            captureSession.stopRepeating();
            /*拍照*/
            captureSession.capture(builder.build(), new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                    super.onCaptureCompleted(session, request, result);

                    Log.v(TAG,"onCaptureCompleted");
                    try {
                        /*設置自動對焦模式*/
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
                        /*設置自動曝光*/
                        previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                        /*連續取景*/
                        captureSession.setRepeatingRequest(previewRequestBuilder.build(),null,null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }
            },null);

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }




    private CameraDevice.StateCallback callback = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            /*相機連接激活成功*/
            cameraDevice = camera ;
            createCameraPreviewSession();
            Log.v(TAG,"====onOpened==");
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            /*相機斷開連接*/
            cameraDevice.close();
            cameraDevice = null ;
            Log.v(TAG,"====onDisconnected==");
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            cameraDevice.close();
            cameraDevice = null ;
            Log.v(TAG,"====onError==");
        }
    };



    private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() {

        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            /*mTextureView 可用時候打開相機*/
            setUpCameraOutputs(width,height);
            openCamera();
            Log.v(TAG,"====onSurfaceTextureAvailable==");
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            Log.v(TAG,"====onSurfaceTextureSizeChanged==");
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            Log.v(TAG,"====onSurfaceTextureDestroyed==");
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
//            Log.v(TAG,"====onSurfaceTextureUpdated==");
        }
    };




    /**
     * @param width
     *
     * @param height
     */
    private void setUpCameraOutputs(int width,int height){
        try {
            CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
            CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(cameraId);
            /*獲取相機配置屬性*/
            StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            /*獲取所有支持的尺寸*/
            Size[] out = map.getOutputSizes(ImageFormat.JPEG);
            /*獲取最大支持尺寸*/
            Size maxSize = Collections.max(Arrays.asList(out), new CompareSizeByArea());

            imageReader = ImageReader.newInstance(maxSize.getWidth(),maxSize.getHeight(),ImageFormat.JPEG,2);
            imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    Log.v(TAG,"======onImageAvailable=====");
                    Image image =reader.acquireNextImage();
                    ByteBuffer  byteBuffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    FileOutputStream fileOutputStream = null;
                    BufferedOutputStream bufferedOutputStream = null;
                    try {
                        File file =  new File(Environment.getExternalStorageDirectory().getPath()+File.separator+System.currentTimeMillis()+".jpg");
                        file.createNewFile();
                        fileOutputStream=new FileOutputStream(file);
                        bufferedOutputStream=new BufferedOutputStream(fileOutputStream);
                        bufferedOutputStream.flush();
                        bufferedOutputStream.write(bytes);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(TakePhotoActivity.this,"save",Toast.LENGTH_LONG).show();
                            }
                        });
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        try {
                            if (fileOutputStream!=null){
                                fileOutputStream.close();
                            }
                            if (bufferedOutputStream!=null){
                                bufferedOutputStream.close();
                            }
                            if (image!=null){
                                image.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            },null);

            previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),width,height,maxSize);
            int oriention = getResources().getConfiguration().orientation ;
            if (oriention == Configuration.ORIENTATION_LANDSCAPE){
                mTextureView.setAspectRatio(previewSize.getWidth(),previewSize.getHeight());
            }else {
                mTextureView.setAspectRatio(previewSize.getHeight(),previewSize.getWidth());
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    Size previewSize;

    /**
     * @param width
     * @param height
     * @param aspectRatio
     * @return
     */
    public static Size chooseOptimalSize(Size[] choices,int width,int height,Size aspectRatio){
        List<Size>  bigEnough = new ArrayList<>() ;

        int w = aspectRatio.getWidth() ;
        int h = aspectRatio.getHeight() ;

        for (Size option:choices){
            if (option.getHeight() == option.getWidth() * h / w && option.getWidth()>=width && option.getHeight() >= height){
                bigEnough.add(option) ;
            }
        }

        if (bigEnough.size()>0){
            return Collections.min(bigEnough,new CompareSizeByArea());
        }else {
            return choices [0] ;
        }
    }


    /**
     * 比較  大小
     */
    static class CompareSizeByArea implements Comparator<Size> {
        @Override
        public int compare(Size o1, Size o2) {
            /*數值比較 1  0  -1 */
            return Long.signum(o1.getHeight()*o1.getWidth() - o2.getHeight()*o2.getWidth() );
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null !=cameraDevice){
            cameraDevice.close();
            cameraDevice = null ;
        }
    }
}

AutoFitTextureView .class

package com.example.camera2demo;

import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;

public class AutoFitTextureView extends TextureView {

    int mRatioWidth ;
    int mRatioHeight ;


    /**
     * 動態改變  顯示大小
     * @param mRatioWidth
     * @param mRatioHeight
     */
    public void setAspectRatio( int mRatioWidth, int mRatioHeight ){
        this.mRatioHeight = mRatioHeight ;
        this.mRatioWidth = mRatioWidth ;
        requestLayout();
    }


    public AutoFitTextureView(Context context) {
        this(context,null);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public AutoFitTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }



    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec) ;
        if ( 0 == mRatioWidth || 0==mRatioHeight){
            setMeasuredDimension(w,h);
        }else {
            if (w < h * mRatioWidth / mRatioHeight){
                setMeasuredDimension(w , mRatioHeight * w  /mRatioWidth );
            }else {
                setMeasuredDimension(h*mRatioWidth / mRatioHeight , h);
            }
        }
    }
}

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TakePhotoActivity">
    <com.example.camera2demo.AutoFitTextureView
        android:id="@+id/mTextureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <ImageView
        android:id="@+id/btn_capture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:layout_gravity="bottom|center_horizontal"/>
</FrameLayout>

權限

  <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章