Android开发 Camera2开发_1_拍照功能开发

 介绍

  google已经在Android5.1以后取消了对Camera1的更新,转而提供了功能更增强大的Camera2.虽然新版本依然可使用Camera1可是,不论是各类机型适配仍是拍照参数自定义都是很鸡肋的.跟上最新的技术了解Camera2是必要的.关于Camera2的兼容通常是支持API22以后包括API22的Android版本,可是也发现一些机型(好比三星)在API22版本上并无支持Camera2.java

须要使用的API介绍

由于Camera2提供的功能更增强大,因此使用比Camera1会复杂许多.须要调用的API和回调也更多.这里简单介绍一下这些API的对应功能.好初步认识Camera2.android

CameraManager

摄像头管理类:算法

  主要有4个功能:缓存

  1. 获取摄像头的ID
  2. 获取摄像头的特征信息(好比摄像头先后位置信息和支持的分辨率信息等等)
  3. 打开指定id的摄像头
  4. 打开和关闭闪光灯

CameraDevice

摄像头设备类:session

  主要功能有3个app

  1. 建立获取数据请求类CaptureRequest.Builder(或者叫捕获请求),下面会介绍这个类
  2. 建立获取数据会话(建立预览或者拍照的会话通道)
  3. 关闭摄像头

CameraDevice.StateCallback

摄像头状态接口回调类:ide

  主要是负责回调摄像头的开启/断开/异常/销毁.咱们使用CameraManager打开指定id的摄像头时须要添加这个回调.oop

CameraCaptureSession.StateCallback

获取数据会话的状态接口回调类:ui

  咱们建立相机预览图像/拍照/录像都须要这个回调类,来告诉咱们获取数据会话的通道状态是配置成功或者配置失败.它还负责给咱们回调一个重要的CameraCaptureSession提供给咱们操做,这个CameraCaptureSession类我下面会介绍this

CameraCaptureSession.CaptureCallback

获取数据会话的数据接口回调类:

  负责回调获取数据的生命周期(好比开始/进行中/完成/失败等等),若是你并不须要对生命周期里作操做,因此有时候没有啥做用.可是它也是必需建立的一个回调接口类,是在建立预览图像/拍照/录像的时候添加进去,可是拍照或者录像的数据都不在这个回调接口里出来(一开始很容易误解,觉得拍照数据会从这里返回).除了回调获取数据的生命周期,还能够在回调方法里获取拍照或者录制过程的的一些参数信息,好比图片的Size/分辨率等等.

CaptureRequest.Builder

获取数据请求配置类:

  很重要,也是咱们频繁操做的一个配置类.由CameraDevice类建立.主要负责

  1. 设置返回数据的surface(显示预览View好比TextureView的surface 或者 照片ImageReadersurface)
  2. 配置预览/拍照/录制的拍照参数,好比自动对焦/自动曝光/拍照自动闪光/设置HZ值/颜色校订等等你能在系统相机上看到的功能.

数据配置完成后交给CameraCaptureSession会话类,让CameraCaptureSession操做提供咱们须要的数据,例如图像预览或者拍照/录制视频

 

CameraCaptureSession

获取数据会话类:

  很重要,是咱们频繁操做的一个数据会话类,好比建立预览/中止预览/拍照/录像都要它来操做,它由CameraCaptureSession.StateCallback这个接口回调方法里回调提供给咱们.

 

ImageReader

图片读取类:

  不属于Camera2Api的类,可是是拍照功能重要的类,照片的数据流由它缓存,而后咱们提取保存到本地成为图片文件或者显示在ImageView里

Camera2的操做流程

在上面的API介绍里,你是否是对这么多的配置类/会话类/接口回调类感到眼花缭乱?是的,Camera2的使用是至关眼花缭乱的,可是咱们抓住一条线慢慢从上面跟到下面就应该能明白是怎么一回事了.下面咱们来简单介绍一些Camera2的操做流程:

初始化流程:

  1. 初始化动态受权,这是基本操做
  2. 初始化一个子线程的Handler,Camera2的操做能够放在主线程也能够放在子线程.按例通常都是子线程里,可是Camera2只须要咱们提供一个子线程的Handler就好了.
  3. 初始化ImageReader,这个没有初始化顺序要求,而且它有数据回调接口,接口回调的图片数据咱们直接保存到内部存储空间,因此提早初始化提供给后续使用.
  4. 初始化TextureView,添加TextureView的接口回调.
  5. 在TextureView的接口回调里回调启用成功方法后,咱们开始初始化相机管理类initCameraManager
  6. 而后继续初始化CameraDevice.StateCallback 摄像头设备状态接口回调类,先初始化提供给后续使用.(在这个接口类的开启相机的回调方法里,咱们须要实现建立预览图像请求配置和建立获取数据会话)
  7. 继续初始化CameraCaptureSession.StateCallback 摄像头获取数据会话类的状态接口回调类,先初始化提供给后续使用.(在这个接口类的配置成功回调方法里,咱们须要实现预览图像或者实现拍照)
  8. 继续初始化CameraCaptureSession.CaptureCallback 摄像头获取数据会话类的获取接口回调类,先初始化提供给后续使用.(啥都不干)
  9. 判断摄像头先后,选择对应id
  10. 打开指定id的摄像头
  11. 实现拍照

逻辑流程:

动态相机权限获取 >> 设置TextureView回调 >> TextureView启用成功回调方法触发 >> 选择摄像头 >> 打开相机 >> 相机开启回调方法触发 >> 建立CaptureRequest.Builder配置类 >> 设置配置类图像预览模式 >>  配置类导入须要显示预览的TextureView的surface >> 建立数据会话 >> 数据会话的配置成功回调方法触发 >> 建立预览图像 >> 预览图像显示成功 >> 按键点击拍照 >> 建立新的CaptureRequest.Builder配置类,添加目标为拍照 >> 配置类导入ImageReader的surface >> 数据会话使用这个配置类建立拍照 >> ImageReader的接口类图片可用方法触发 >> 保存图片

 

代码部分

实现简单的拍照功能demo

 

package demo.yt.com.demo;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
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.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
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.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

public class Camera2Activity extends AppCompatActivity {
    private static final String TAG = Camera2Activity.class.getName();
    private String[] permission = {Manifest.permission.CAMERA};
    private TextureView mTextureView;//注意使用TextureView须要开启硬件加速,开启方法很简单在AndroidManifest.xml 清单文件里,你须要使用TextureView的activity添加 android:hardwareAccelerated="true"
    private Button mBtnPhotograph;
    private Handler mMainHandler = new Handler(Looper.getMainLooper());
    private Handler mChildHanler = null;
    private CameraManager mCameraManager;//相机管理类,用于检测系统相机获取相机id
    private CameraDevice mCameraDevice;//Camera设备类
    private CameraCaptureSession.StateCallback mSessionStateCallback;//获取的会话类状态回调
    private CameraCaptureSession.CaptureCallback mSessionCaptureCallback;//获取会话类的获取数据回调
    private CaptureRequest.Builder mCaptureRequest;//获取数据请求配置类
    private CameraDevice.StateCallback mStateCallback; //摄像头状态回调
    private CameraCaptureSession mCameraCaptureSession; //获取数据会话类
    private ImageReader mImageReader; //照片读取器
    private String mCurrentCameraId;
    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);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera2);
        mTextureView = findViewById(R.id.textureview);
        mBtnPhotograph = findViewById(R.id.btn_Photograph);
        mBtnPhotograph.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                try {
                    mCameraCaptureSession.stopRepeating();//中止重复   取消任何正在进行的重复捕获集 在这里就是中止画面预览
                    
                    /*    mCameraCaptureSession.abortCaptures(); 终止获取   尽量快地放弃当前挂起和正在进行的全部捕获。
                     * 这里有一个坑,其实这个并不能随便调用(我是看到别的demo这么使用,可是实际上是错误的,因此就在这里备注这个坑).
             * 最好只在Activity里的onDestroy调用它,终止获取是耗时操做,须要必定时间从新打开会话通道.
                     * 在这个demo里我并无恢复预览,若是你调用了这个方法关闭了会话又拍照后恢复图像预览,会话就会频繁的开关,
             * 会致使拍照图片在处理耗时缓存时你又关闭了会话.致使照片缓存不完整而且失败.
                     * 因此切记不要随便使用这个方法,会话开启后并不须要关闭刷新.后续其余拍照/预览/录制视频直接操做这个会话便可
                     */
                     
                    takePicture();//拍照
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
        });
        initPermission();
        initChildThread();
        initImageReader();
        initTextureView();

    }

    private void initPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, permission, 1);
        }

    }

    private void initTextureView(){
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                Log.e(TAG,"TextureView 启用成功");
                initCameraManager();
                initCameraCallback();
                initCameraCaptureSessionStateCallback();
                initCameraCaptureSessionCaptureCallback();
                selectCamera();
                openCamera();

            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                Log.e(TAG,"TextureView 变化");

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                Log.e(TAG,"TextureView 销毁");
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

    /**
     * 初始化子线程
     */
    private void initChildThread() {
        HandlerThread handlerThread = new HandlerThread("camera2");
        handlerThread.start();
        mChildHanler = new Handler(handlerThread.getLooper());
    }

    /**
     * 初始化相机管理
     */
    private void initCameraManager() {
        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

    }

    /**
     * 获取匹配的大小
     * @return
     */
    private Size getMatchingSize(){

            Size selectSize = null;
            float selectProportion = 0;
        try {
            float viewProportion = (float)mTextureView.getWidth() / (float)mTextureView.getHeight();
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            for (int i = 0; i < sizes.length; i++){
                Size itemSize = sizes[i];
                float itemSizeProportion = (float)itemSize.getHeight() / (float)itemSize.getWidth();
                float differenceProportion = Math.abs(viewProportion - itemSizeProportion);
                Log.e(TAG, "相减差值比例="+differenceProportion );
                if (i == 0){
                    selectSize = itemSize;
                    selectProportion = differenceProportion;
                    continue;
                }
                if (differenceProportion <= selectProportion){
                    if (differenceProportion == selectProportion){
                        if (selectSize.getWidth() + selectSize.getHeight() < itemSize.getWidth() + itemSize.getHeight()){
                            selectSize = itemSize;
                            selectProportion = differenceProportion;
                        }

                    }else {
                        selectSize = itemSize;
                        selectProportion = differenceProportion;
                    }
                }
            }

        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "getMatchingSize: 选择的比例是="+selectProportion);
        Log.e(TAG, "getMatchingSize: 选择的尺寸是 宽度="+selectSize.getWidth()+"高度="+selectSize.getHeight());
        return selectSize;
    }

    /**
     * 选择摄像头
     */
    private void selectCamera() {
        try {
            String[] cameraIdList = mCameraManager.getCameraIdList();//获取摄像头id列表
            if (cameraIdList.length == 0) {
                return;
            }

            for (String cameraId : cameraIdList) {
                Log.e(TAG, "selectCamera: cameraId=" + cameraId);
                //获取相机特征,包含先后摄像头信息,分辨率等
                CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
                Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);//获取这个摄像头的面向
                //CameraCharacteristics.LENS_FACING_BACK 后摄像头
                //CameraCharacteristics.LENS_FACING_FRONT 前摄像头
                //CameraCharacteristics.LENS_FACING_EXTERNAL 外部摄像头,好比OTG插入的摄像头
                if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
                    mCurrentCameraId = cameraId;

                }

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

    /**
     * 初始化摄像头状态回调
     */
    private void initCameraCallback() {
        mStateCallback = new CameraDevice.StateCallback() {
            /**
             * 摄像头打开时
             * @param camera
             */
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                Log.e(TAG, "相机开启");
                mCameraDevice = camera;


                try {

                    SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
                    Size matchingSize = getMatchingSize();
                    surfaceTexture.setDefaultBufferSize(matchingSize.getWidth(),matchingSize.getHeight());//设置预览的图像尺寸
                    Surface surface = new Surface(surfaceTexture);

//                CaptureRequest能够彻底自定义拍摄参数,可是须要配置的参数太多了,因此Camera2提供了一些快速配置的参数,以下:
//          TEMPLATE_PREVIEW :预览
//                TEMPLATE_RECORD:拍摄视频
//                TEMPLATE_STILL_CAPTURE:拍照
//                TEMPLATE_VIDEO_SNAPSHOT:建立视视频录制时截屏的请求
//                TEMPLATE_ZERO_SHUTTER_LAG:建立一个适用于零快门延迟的请求。在不影响预览帧率的状况下最大化图像质量。
//                TEMPLATE_MANUAL:建立一个基本捕获请求,这种请求中全部的自动控制都是禁用的(自动曝光,自动白平衡、自动焦点)。
                    mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);//建立预览请求
                    mCaptureRequest.addTarget(surface); //添加目标
                    mCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自动对焦
                    /**
                     * 建立获取会话
                     * 这里会有一个容易忘记的坑,那就是Arrays.asList(surface, mImageReader.getSurface())这个方法
                     * 这个方法须要你导入后面须要操做功能的全部surface,好比预览/拍照若是你2个都要操做那就要导入2个
                     * 不然后续操做没有添加的那个功能就报错surface没有准备好,这也是我为何先初始化ImageReader的缘由,由于在这里就能够拿到ImageReader的surface了
                     */
                    mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionStateCallback, mChildHanler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }

            }

            /**
             *摄像头断开时
             * @param camera
             */
            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {

            }

            /**
             * 出现异常状况时
             * @param camera
             * @param error
             */
            @Override
            public void onError(@NonNull CameraDevice camera, int error) {

            }

            /**
             * 摄像头关闭时
             * @param camera
             */
            @Override
            public void onClosed(@NonNull CameraDevice camera) {
                super.onClosed(camera);
            }
        };
    }

    /**
     * 摄像头获取会话状态回调
     */
    private void initCameraCaptureSessionStateCallback(){
        mSessionStateCallback = new CameraCaptureSession.StateCallback() {


            //摄像头完成配置,能够处理Capture请求了。
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    mCameraCaptureSession = session;
                    //注意这里使用的是 setRepeatingRequest() 请求经过此捕获会话无休止地重复捕获图像。用它来一直请求预览图像
                    mCameraCaptureSession.setRepeatingRequest(mCaptureRequest.build(), mSessionCaptureCallback, mChildHanler);


//                    mCameraCaptureSession.stopRepeating();//中止重复   取消任何正在进行的重复捕获集
//                    mCameraCaptureSession.abortCaptures();//终止获取   尽量快地放弃当前挂起和正在进行的全部捕获。请只在销毁activity的时候调用它
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }

            }

            //摄像头配置失败
            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {

            }
        };
    }

    /**
     * 摄像头获取会话数据回调
     */
    private void initCameraCaptureSessionCaptureCallback(){
        mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                super.onCaptureStarted(session, request, timestamp, frameNumber);
            }

            @Override
            public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                super.onCaptureProgressed(session, request, partialResult);
            }

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
//                Log.e(TAG, "onCaptureCompleted: 触发接收数据");
//                Size size = request.get(CaptureRequest.JPEG_THUMBNAIL_SIZE);

            }

            @Override
            public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                super.onCaptureFailed(session, request, failure);
            }

            @Override
            public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) {
                super.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
            }

            @Override
            public void onCaptureSequenceAborted(@NonNull CameraCaptureSession session, int sequenceId) {
                super.onCaptureSequenceAborted(session, sequenceId);
            }

            @Override
            public void onCaptureBufferLost(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull Surface target, long frameNumber) {
                super.onCaptureBufferLost(session, request, target, frameNumber);
            }
        };
    }

    /**
     * 打开摄像头
     */
    private void openCamera() {
        try {
            if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
                mCameraManager.openCamera(mCurrentCameraId, mStateCallback, mMainHandler);
                return;
            }
            Toast.makeText(this, "没有受权", Toast.LENGTH_SHORT).show();

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

    /**
     * 初始化图片读取器
     */
    private void initImageReader(){
        //建立图片读取器,参数为分辨率宽度和高度/图片格式/须要缓存几张图片,我这里写的2意思是获取2张照片
        mImageReader = ImageReader.newInstance(1080, 1920, ImageFormat.JPEG, 2);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
//        image.acquireLatestImage();//从ImageReader的队列中获取最新的image,删除旧的
//        image.acquireNextImage();//从ImageReader的队列中获取下一个图像,若是返回null没有新图像可用
                Image image = reader.acquireNextImage();
                try {
                    File path = new File(Camera2Activity.this.getExternalCacheDir().getPath());
                    if (!path.exists()){
                        Log.e(TAG, "onImageAvailable: 路径不存在");
                        path.mkdirs();
                    }else {
                        Log.e(TAG, "onImageAvailable: 路径存在" );
                    }
                    File file = new File(path,"demo.jpg");
                    FileOutputStream fileOutputStream = new FileOutputStream(file);

//        这里的image.getPlanes()[0]实际上是图层的意思,由于个人图片格式是JPEG只有一层因此是geiPlanes()[0],若是你是其余格式(例如png)的图片会有多个图层,就能够获取指定图层的图像数据       
                    ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    fileOutputStream.write(bytes);
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    image.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }


            }
        },mChildHanler);
    }

    private void takePicture(){
        CaptureRequest.Builder captureRequestBuilder = null;
        try {
            captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自动对焦
            captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自动爆光
//            // 获取手机方向,若是你的app有提供横屏和竖屏,那么就须要下面的方法来控制照片为竖立状态
//            int rotation = getWindowManager().getDefaultDisplay().getRotation();
//            Log.e(TAG, "takePicture: 手机方向="+rotation);
//            Log.e(TAG, "takePicture: 照片方向="+ORIENTATIONS.get(rotation));
            captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, 270);//个人项目不须要,直接写死270度 将照片竖立
            Surface surface = mImageReader.getSurface();
            captureRequestBuilder.addTarget(surface);
            CaptureRequest request = captureRequestBuilder.build();
            mCameraCaptureSession.capture(request,null,mChildHanler); //获取拍照
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (permissions.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    Toast.makeText(this, "受权成功", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(this, "受权失败", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
        }
    }
}

 

 

比较规范的拍照功能demo

上面的只是简简单单的demo,下面是实际使用的样子,其实差很少,贴出来也是多一个参考

 

public class FaceCameraActivity extends BaseActivity implements View.OnClickListener {
    private TextureView mTextureView;
    private Button mBtnCamera;
    private ImageView mBack;
    private MaterialDialog mHandlerImageWaitDialog;
    private CameraManager mCameraManager;
    private CameraDevice mCameraDevice;
    private ImageReader mImageReader;
    private CaptureRequest.Builder mCaptureRequest;
    private CameraDevice.StateCallback mCameraDeviceStateCallback;
    private CameraCaptureSession.StateCallback mCameraCaptureSessionStateCallback;
    private CameraCaptureSession.CaptureCallback mCameraCaptureSessionCaptureCallback;
    private CameraCaptureSession mCameraCaptureSession;
    private String mCurrentCameraId;
    private Size mCurrentSelectSize;
    private Handler mChildHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initChildThread();
        initCameraManager();
        initSelectCamera();
        initHandlerMatchingSize();
        initImageReader();
        initTextureViewListener();
        initCameraDeviceStateCallbackListener();
        initCameraCaptureSessionStateCallbackListener();
        initCameraCaptureSessionCaptureCallbackListener();

    }

    @Override
    public int getLayout() {
        return R.layout.activity_face_camera;
    }

    @Override
    public void initView() {
        mBack = findViewById(R.id.back);
        mTextureView = findViewById(R.id.texture_view);
        mBtnCamera = findViewById(R.id.btn_camera);
        mBack.setOnClickListener(this);
        mBtnCamera.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_camera:
                if (ButtonDelayUtil.isFastClick()){
                    handlerImageWaitDialog().show();
                    stopPreview();
                    takePicture();

                }

                break;
            case R.id.back:
                finish();
                break;
            default:
                break;
        }

    }

    private void initChildThread() {
        HandlerThread handlerThread = new HandlerThread("faceCamera");
        handlerThread.start();
        mChildHandler = new Handler(handlerThread.getLooper());

    }


    /**
     * 初始化相机管理
     */
    private void initCameraManager() {
        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    }

    /**
     * 初始化选择摄像头
     */
    private void initSelectCamera() {
        try {
            String[] cameraIdArray = mCameraManager.getCameraIdList();
            for (String itemId : cameraIdArray) {
                CameraCharacteristics itemCharacteristics = mCameraManager.getCameraCharacteristics(itemId);
                Integer facing = itemCharacteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
                    mCurrentCameraId = itemId;
                    break;
                }

            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        if (mCurrentCameraId == null) {
            finish();
            Toast.makeText(this, "此设备不支持前摄像头", Toast.LENGTH_SHORT).show();
        }

    }

   /**
     * 初始化计算适合当前屏幕分辨率的拍照分辨率
     * @return
     */
    private void initHandlerMatchingSize() {
        try {
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId);
            StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
            DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
            int deviceWidth = displayMetrics.widthPixels;
            int deviceHeigh = displayMetrics.heightPixels;
            L.e("当前屏幕密度宽度="+deviceWidth+"高度="+deviceHeigh);
            for (int j = 1; j < 81; j++) {
                for (int i = 0; i < sizes.length; i++) {
                    Size itemSize = sizes[i];
                    if (itemSize.getHeight() < (deviceWidth + j * 5) && itemSize.getHeight() > (deviceWidth - j * 5)) {
                        if (mCurrentSelectSize != null) { //若是以前已经找到一个匹配的宽度
                            if (Math.abs(deviceHeigh-itemSize.getWidth()) < Math.abs(deviceHeigh - mCurrentSelectSize.getWidth())){ //求绝对值算出最接近设备高度的尺寸
                                mCurrentSelectSize = itemSize;
                                continue;

                            }
                        }else {
                            mCurrentSelectSize = itemSize;
                        }

                    }
                }
                if (mCurrentSelectSize != null) { //若是不等于null 说明已经找到了 跳出循环
                    break;
                }
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        L.e("当前预览宽度="+mCurrentSelectSize.getWidth()+"高度="+mCurrentSelectSize.getHeight());
    }

    private void initImageReader() {
        L.e("初始化图片ImageReader的宽="+mCurrentSelectSize.getWidth()+"高="+mCurrentSelectSize.getHeight());
        mImageReader = ImageReader.newInstance(mCurrentSelectSize.getWidth()
                , mCurrentSelectSize.getHeight()
                , ImageFormat.JPEG
                , 2);

        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                FilePathSession.deleteFaceImageFile();
                Image image = reader.acquireLatestImage();
                ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
                byte[] bytes = new byte[byteBuffer.remaining()];
                byteBuffer.get(bytes);
                try {
                    FileOutputStream fileOutputStream = new FileOutputStream(FilePathSession.getFaceImagePath());
                    fileOutputStream.write(bytes);
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    image.close();
                    startPreview();
                    handlerImageWaitDialog().dismiss();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Intent startFaceConfirm = new Intent(FaceCameraActivity.this, FaceConfirmActivity.class);
                            startActivity(startFaceConfirm);
                            FaceCameraActivity.this.finish();
                        }
                    });

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, mChildHandler);
    }

    private void initTextureViewListener() {
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                openCamera();

            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }

    private void initCameraDeviceStateCallbackListener() {
        mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                //相机开启
                mCameraDevice = camera;
                try {
                    SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
                    surfaceTexture.setDefaultBufferSize(mCurrentSelectSize.getWidth(),mCurrentSelectSize.getHeight());
                    Surface surface = new Surface(surfaceTexture);
                    mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                    mCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                    mCaptureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自动爆光
                    mCaptureRequest.addTarget(surface);
                    mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface())
                            , mCameraCaptureSessionStateCallback
                            , mChildHandler);

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

            }

            @Override
            public void onDisconnected(@NonNull CameraDevice camera) {

            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {
                finish();
                Toast.makeText(FaceCameraActivity.this, "相机打开失败", Toast.LENGTH_SHORT).show();
                L.e("CameraDevice.StateCallback onError : 相机异常 error code="+error);

            }
        };
    }

    private void initCameraCaptureSessionStateCallbackListener() {
        mCameraCaptureSessionStateCallback = new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                mCameraCaptureSession = session;
                startPreview();

            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                finish();
                Toast.makeText(FaceCameraActivity.this, "相机打开失败", Toast.LENGTH_SHORT).show();
                L.e("CameraCaptureSession.StateCallback onConfigureFailed : CameraCaptureSession会话通道建立失败");

            }
        };
    }

    private void initCameraCaptureSessionCaptureCallbackListener() {
        mCameraCaptureSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                super.onCaptureStarted(session, request, timestamp, frameNumber);
                //获取开始
            }

            @Override
            public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
                super.onCaptureProgressed(session, request, partialResult);
                //获取中
            }

            @Override
            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                //获取结束
            }

            @Override
            public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                super.onCaptureFailed(session, request, failure);
                //获取失败

                Toast.makeText(FaceCameraActivity.this, "拍照失败", Toast.LENGTH_SHORT).show();
                L.e("失败报告Reason="+failure.getReason());

            }
        };
    }

    @SuppressLint("MissingPermission")
    private void openCamera() {
        try {
            mCameraManager.openCamera(mCurrentCameraId, mCameraDeviceStateCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    private MaterialDialog handlerImageWaitDialog(){
        if (mHandlerImageWaitDialog == null){
            mHandlerImageWaitDialog = new MaterialDialog.Builder(this)
                    .content("正在处理图像中...")
                    .progress(true,-1)
                    .cancelable(false)
                    .build();
        }
        return mHandlerImageWaitDialog;
    }

    /**
     * 开始预览
     */
    private void startPreview(){
        try {
            mCameraCaptureSession.setRepeatingRequest(mCaptureRequest.build(), mCameraCaptureSessionCaptureCallback, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 中止预览
     */
    private void stopPreview(){
        try {
            mCameraCaptureSession.stopRepeating();
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

     /**
     * 拍照
     */
    private void takePicture(){
        try {
            CaptureRequest.Builder takePictureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            takePictureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自动对焦
            takePictureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自动爆光
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            int angle = getJpegOrientation(mCameraManager.getCameraCharacteristics(mCurrentCameraId), rotation);
            L.i("人脸拍照 照片角度="+angle);
            takePictureRequest.set(CaptureRequest.JPEG_ORIENTATION, angle);
            Surface surface = mImageReader.getSurface();
            takePictureRequest.addTarget(surface);
            CaptureRequest request = takePictureRequest.build();
            mCameraCaptureSession.capture(request, null, mChildHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    /**
     * 官方提供的JPEG图片方向算法
     * @param c
     * @param deviceOrientation
     * @return
     */
    private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
          if (deviceOrientation == OrientationEventListener.ORIENTATION_UNKNOWN){
              return 0;
          }
          int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);//获取传感器方向

          // Round device orientation to a multiple of 90
          deviceOrientation = (deviceOrientation + 45) / 90 * 90;

          // Reverse device orientation for front-facing cameras
          boolean facingFront = c.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT;//判断摄像头面向
          if (facingFront) {
              deviceOrientation = -deviceOrientation;
          }

          // Calculate desired JPEG orientation relative to camera orientation to make
          // the image upright relative to the device orientation
          int jpegOrientation = (sensorOrientation + deviceOrientation + 360) % 360;

          return jpegOrientation;
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mImageReader != null){
            mImageReader.close();
            mImageReader = null;
        }
        if (mCameraCaptureSession != null){
            stopPreview();
            try {
                mCameraCaptureSession.abortCaptures();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }
        if (mCameraDevice != null){
            mCameraDevice.close();
            mCameraDevice = null;
        }
        mCameraManager = null;
       if (mChildHandler != null){
           mChildHandler.removeCallbacksAndMessages(null);
           mChildHandler = null;

       }
    }

}
相关文章
相关标签/搜索