android Camera2 API使用详解-阿里云开发者社区

开发者社区> 杰克.陈> 正文

android Camera2 API使用详解

简介: 原文:android Camera2 API使用详解 由于最近需要使用相机拍照等功能,鉴于老旧的相机API问题多多,而且新的设备都是基于安卓5.0以上的,于是本人决定研究一下安卓5.0新引入的Camera2 API 来实现 Camera2API地址 首先我们来熟悉一下官方给的这几个图: 这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。
+关注继续查看
原文:android Camera2 API使用详解

由于最近需要使用相机拍照等功能,鉴于老旧的相机API问题多多,而且新的设备都是基于安卓5.0以上的,于是本人决定研究一下安卓5.0新引入的Camera2 API 来实现 Camera2API地址

首先我们来熟悉一下官方给的这几个图:


这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。

fig.2


其中 CameraManager 是那个站在高处统管所有摄像投设备(CameraDevice)的管理者,而每个 CameraDevice 自己会负责建立 CameraCaptureSession 以及建立 CaptureRequest。CameraCharacteristics 是 CameraDevice 的属性描述类,非要做个对比的话,那么它与原来的 CameraInfo 有相似性。 类图中有着三个重要的 callback,虽然这增加了阅读代码的难度,但是你必须要习惯,因为这是新包的风格。其中 CameraCaptureSession.CaptureCallback 将处理预览和拍照图片的工作,需要重点对待。这些类是如何相互配合的?下面是简单的流程图。

fig.3

有了这三张图,那么接下来就好了理解了,我们按照拍照流程的指示,来吧整个过程走一遍

1首先定义一个SufaceView 用来实现预览照片用

surfaceView=(SurfaceView) findViewById(R.id.surfaceView);

surfaceHolder.addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        initCamera();
    }

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
});
其中initCamera2()方法是用来初始化相机的方法
2获取Camera ID,该ID是用来打开相机的关键,一般后置摄像头是0,前置摄像头是1,这里我们选择后置摄像头做详解
mCameraID = "" + CameraCharacteristics.LENS_FACING_BACK;//后摄像头
3通过Camera ID 来打开摄像头,这里我们需要使用CamerManager,这是类是一个管理服务类,值得注意的是,打开摄像头是一个相当复杂的过程,不能直接在主线程中直接执行,其核心代码为:

HandlerThread handlerThread=new HandlerThread("Camera2");
handlerThread.start();
childHandler=new Handler(handlerThread.getLooper());
mainHandler=new Handler(getMainLooper());

mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地
    @Override
    public void onImageAvailable(ImageReader reader) {
        mCameraDevice.close();
        surfaceView.setVisibility(View.GONE);
        iv_show.setVisibility(View.VISIBLE);
        // 拿到拍照照片数据
        Image image = reader.acquireNextImage();
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);//由缓冲区存入字节数组
        final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        if (bitmap != null) {
            iv_show.setImageBitmap(bitmap);
        }
    }
}, mainHandler);

cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    //打开摄像头
    cameraManager.openCamera(mCameraID, stateCallback, mainHandler);
} catch (CameraAccessException e) {
    e.printStackTrace();
}
注意打开摄像头需要权限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
4 开启相机后有一个回调,stateCallback,该回调是用来返回相机是否正常打开的状态的
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice camera) {//打开摄像头
        mCameraDevice = camera;
        //开启预览
        takePreview();
    }

    @Override
    public void onDisconnected(CameraDevice camera) {//关闭摄像头
        if (null != mCameraDevice) {
            mCameraDevice.close();
            MainActivity.this.mCameraDevice = null;
        }
    }

    @Override
    public void onError(CameraDevice camera, int error) {//发生错误
        Toast.makeText(MainActivity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show();
    }
};

5 相机开启成功后,执行回调中的onOpen方法,在该方法中,我们实现让图像显示在界面上
/**
 * 开始预览
 */
private void takePreview() {
    try {
        // 创建预览需要的CaptureRequest.Builder
        final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        // 将SurfaceView的surface作为CaptureRequest.Builder的目标
        previewRequestBuilder.addTarget(surfaceHolder.getSurface());
        // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
        mCameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③
        {
            @Override
            public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                if (null == mCameraDevice) return;
                // 当摄像头已经准备好时,开始显示预览
                mCameraCaptureSession = cameraCaptureSession;
                try {
                    // 自动对焦
                    previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                    // 打开闪光灯
                    previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
                    // 显示预览
                    CaptureRequest previewRequest = previewRequestBuilder.build();
                    mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show();
            }
        }, childHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}
6,预览完成后,接下来就是拍照了,注意某些手机的摄像头会和正常手机的摄像头另类,成像会成180度的倒立像,比如nexus 5x,这时候只需要设置
rORIENTATIONS的值来调整角度就可以
/**
 * 拍照
 */
private void takePicture() {
    if (mCameraDevice == null) return;
    // 创建拍照需要的CaptureRequest.Builder
    final CaptureRequest.Builder captureRequestBuilder;
    try {
        captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        // 将imageReader的surface作为CaptureRequest.Builder的目标
        captureRequestBuilder.addTarget(mImageReader.getSurface());
        // 自动对焦
        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);
        // 获取手机方向
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        // 根据设备方向计算设置照片的方向
        captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)+rORIENTATIONS);
        //拍照
        CaptureRequest mCaptureRequest = captureRequestBuilder.build();
        mCameraCaptureSession.capture(mCaptureRequest, null, childHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Discord 公司如何使用 Cassandra 存储上亿条线上数据
Discord 是一款国外的类似 YY 的语音聊天软件。Discord 语音聊天软件及我们的 UGC 内容的增长速度比想象中要快得多。随着越来越多用户的加入,带来了更多聊天消息。2016 年 7 月,每天大约有 4 千万条消息;2016 年 12 月,每天超过亿条。
4196 0
Android Native禁止使用系统私有库详解
解读Android Native对于系统私有库的限制,老版本的黑科技代码在N版本之后都可能导致APP崩溃。
1663 0
Android使用Canvas实现跑马灯
  网上的很多的教程都是通过更改TextView的属性进行跑马灯的设计。这样做有很多的缺点: 1、如果TextView没有获取焦点,那么跑马灯的效果无法实现。
1080 0
Android Studio 使用Intent实现页面的跳转(带参数)
不管是在APP,还是在网站中,页面之间的跳转都是很常见的,本文主要讲一下在APP中,如何通过Intent实现页面的跳转。   不带参数: 写在MainActivity页面的代码: 1 Intent intent = new Intent(); 2 intent.
2466 0
+关注
杰克.陈
一个安静的程序猿~
9798
文章
2
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载