Android Camera2学习笔记

文章目录

          • **使用流程:**
          • **代码:**
          • **关键类与关键方法:**
          • **注意点:**
          • **说明:**

使用流程:
  • 创建预览组件surfaceView

  • surfaceView创建好后打开摄像头

  • 摄像头状态回调,创建预览至surfaceView

  • 拍照

    SurfaceHolder.Callback()中openCamera() -> CameraDevice.StateCallback 中创建 CameraCaptureSession -> CameraCaptureSession.StateCallback 中发起请求

代码:
package com.haoyang.camera;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.Camera;
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.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.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends AppCompatActivity {

    private SurfaceView surfaceView;
    private Button btSn;
    private ImageView ivBitmap;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        surfaceView = findViewById(R.id.sv_surfaceView);
        btSn = findViewById(R.id.bt_sn);
        ivBitmap = findViewById(R.id.iv_bitmap);
        surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                //打开摄像头
                try {
                    openCamera();
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {

            }
        });
        btSn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sn();
            }
        });
    }


    CameraDevice mCameraDevice;
    CameraManager cameraManager;

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void openCamera() throws CameraAccessException {
        cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        assert cameraManager != null;
        cameraManager.openCamera("0", new CameraDevice.StateCallback() {
            @Override
            public void onOpened(@NonNull CameraDevice camera) {
                mCameraDevice = camera;
                createCameraPreviewSession();
            }

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

            }

            @Override
            public void onError(@NonNull CameraDevice camera, int error) {

            }
        }, null);
    }

    CaptureRequest.Builder previewRequestBuilder;
    CaptureRequest previewRequest;
    CameraCaptureSession captureSession;
    ImageReader imageReader;
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void createCameraPreviewSession() {
        try {
            // 获取指定摄像头的特性
            CameraCharacteristics characteristics = null;
            characteristics = cameraManager.getCameraCharacteristics("0");
            // 获取摄像头支持的配置属性
            StreamConfigurationMap map = characteristics.get(
                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            // 获取摄像头支持的最大尺寸
            Size largest = Collections.max(
                    Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                    new CompareSizesByArea());
            imageReader = ImageReader.newInstance(largest.getWidth(),largest.getHeight(),
                    ImageFormat.JPEG, 1);

            SurfaceControl surfaceControl = surfaceView.getSurfaceControl();
            Surface surface = new Surface(surfaceControl);
            // 创建作为预览的CaptureRequest.Builder
            previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            // 将textureView的surface作为CaptureRequest.Builder的目标
            previewRequestBuilder.addTarget(surface);
            // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
            mCameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback()
                    {
                        @Override
                        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                            // 如果摄像头为null,直接结束方法
                            if (null == mCameraDevice) {
                                return;
                            }

                            // 当摄像头已经准备好时,开始显示预览
                            captureSession = cameraCaptureSession;
                            try {
                                // 开始显示相机预览
                                previewRequest = previewRequestBuilder.build();
                                // 设置预览时连续捕获图像数据
                                captureSession.setRepeatingRequest(previewRequest,
                                        null, null);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                            Toast.makeText(MainActivity.this, "配置失败!"
                                    , Toast.LENGTH_SHORT).show();
                        }
                    }, null
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void sn(){
        try {
            CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            previewRequestBuilder.addTarget(imageReader.getSurface());
            captureSession.stopRepeating();
            captureSession.capture(previewRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                    try {
                        captureSession.setRepeatingRequest(previewRequest,null,null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }
            },null);
            imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    Image image = reader.acquireNextImage();
                    ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.length);
                    ivBitmap.setImageBitmap(bitmap);
                    image.close();
                }
            },null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    static class CompareSizesByArea implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            // 强转为long保证不会发生溢出
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                    (long) rhs.getWidth() * rhs.getHeight());
        }
    }
}
关键类与关键方法:

具体说明请查询api文档

SurfaceView

用途:创建surfaceView并回调状态

方法:

surfaceView.getHolder.addCallback(new SurfaceHolder.Callback());

CameraManager

用途:管理camera,此处开启摄像头,摄像头回调开启状态

方法:

cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameraManager.openCamera("0", new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice camera) {
        mCameraDevice = camera;
        //摄像头开启时开始预览
        createCameraPreviewSession();
    }

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

    }

    @Override
    public void onError(@NonNull CameraDevice camera, int error) {

    }
}, null);

CameraDevice

用途:返回CaptureRequest.Builder对象,创建CameraCaptureSession对象

方法:

mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback()
                    {
                        @Override
                        public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                            // 如果摄像头为null,直接结束方法
                            if (null == mCameraDevice) {
                                return;
                            }

                            // 当摄像头已经准备好时,开始显示预览
                            captureSession = cameraCaptureSession;
                            try {
                                // 开始显示相机预览
                                previewRequest = previewRequestBuilder.build();
                                // 设置预览时连续捕获图像数据
                                captureSession.setRepeatingRequest(previewRequest,
                                        null, null);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                            Toast.makeText(MainActivity.this, "配置失败!"
                                    , Toast.LENGTH_SHORT).show();
                        }
                    }, null
            );

CaptureRequest

用途:拍照请求作为session参数,可由CaptureRequest.Builder的buidler方法生成

CaptureRequest.Builder

用途:提供许多配置项,预览

方法:

//聚焦、曝光等配置
captureRequestBuilder.set();
//将预览流填入surface
captureRequestBuilder.addTarget(surface);

CameraCaptureSession

用途:控制拍照,捕获图像,需要CaptureRequest作为参数

方法:

captureSession.stopRepeating();
captureSession.capture(previewRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
    @Override
    public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
        try {
            captureSession.setRepeatingRequest(previewRequest,null,null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
},null);

ImageReader

用途:捕获图像到ImageReader,用于转成Image

方法:

//将预览流填入imageReader.surface,以下方法提供imageReader.surface的数据来源
captureRequestBuilder.addTarget(imageReader.surface);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
    }
},new Handler());
//将预览流传入ImageReader
captureRequestBuilder.addTarget(ImageReader.getsurface());

Image

用途:可转化为byte数组,转bitmap、存图等

方法:

Image image = imageReader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
注意点:

1:拍照时提示captureRequest包含一个未配置输入/输出的surface,我们拍照时调用了一句代码是captureRequestBuilder.addTarget(imageReader.getSurface()),这里的surface(imageReader.getSurface())未在创建session时加进List outputs:
Android Camera2学习笔记_第1张图片
报错在199行的previewRequestBuilder.build(),其实是187行的ImageReader.getSufce()存在问题
在这里插入图片描述
解决方法:createCaptureSession的时候把surface放进List带进去
在这里插入图片描述
2:拍照存Image时,拍了一定数量的照片后发现不走imageReader.setOnImageAvailableListener()的回调方法而且拍照时一直卡在一个画面,后面拿不到图或者报以下错误
在这里插入图片描述
解决方法:处理之后记得释放资源
Android Camera2学习笔记_第2张图片
3:拍照时发现拍出来的图片显示不出来,可能是图片太大了,我的手机支持最大像素尺寸6000*8000
在这里插入图片描述
尝试方法:尝试修改下尺寸imageReader的尺寸,此处的largest.getWidth(),largest.getHeight()适当的改小一些

说明:

1.需要申请权限
2.surfaceView大小此处未做调整,预览出来的画面有点变形,拍照生成的图片会有270度变化,这些需要我们调整

你可能感兴趣的:(camera2,android学习笔记)