关于使用Android新版Camera即Camera2的使用介绍 暨解决Camera.PreviewCallback和MediaRecorder无法同时进行

  新的相机API也就是Camera2是在Android 5.0引进的。通常情况下,我们都是使用Android旧的相机API,纵然在Android Studio里老是提示已经废弃,但是只要都能用,也就没必要单独为了使用新的API而写两套代码。那为什么要介绍Camera2的使用呢?一切问题的根源都是多样化的需求引起的,特别是在Android领域,兼容性问题更是层出不穷。经常会碰到,其他手机都可以,怎么就这个不行……

  我也是跟大家一样,碰到了一个跟相机有关的兼容性问题。我们APP在进行活体识别的时候,除了要进行每个frame的检测同时也要进行当前活体检测的视频录制,使用的都是旧的相机API。在多样化机型测试下,我们发现在红米Note2和魅族MX5下,无法正常的同时进行活体检测和视频录制,换得更技术一点的说法就是,在旧的API下,Camera.PreviewCallback和MediaRecorder不能同时进行。怎么办?google 来波search,你会发现,然并卵……刚开始,我们还专门联系了魅族的相机开发人员,以为会有什么比较“魅族化”的方案,结果他们直接回了一句:平台相关,MX5不支持录像输出的同时提供预览数据。怎么办?砍需求?这种关键流程,都是经过法务部门专门审核过的,那能说砍就砍。我们一边申请是否可以砍掉这个需求,同时也依然继续研究怎么解决这个问题~

  我们发现这两款不能同时进行的手机都是5.0以上的,于是我就想,也许新的Camera2有可能解决的~下面开始进行专业技术干货解说模式……

  当我们要学着使用某个新的API时,最好是直接到官网去找reference,然后尽量科学上网。Android的大部分API示例,都在https://github.com/googlesamples里面,这次提到的关于Camera2的使用,当然也是从那里下载下来的,源码地址如下:

https://github.com/googlesamples/android-Camera2Basic以及https://github.com/googlesamples/android-Camera2Video。但是googlesamples里面的代码都是比较原始的代码。

  我们需要静下心来分析相机使用的过程:

  1、首先一定得判断权限,是否有权利使用相机;

  2、通过什么方式连上相机设备;

  3、拿到相机设备后怎么进行录像;

  4、如何在录像的过程中监听到每一帧的数据;

  

  一、关于检查权限就不说了,这里补充一句有一个类叫ActivityCompat,大家如果以前没用过可以看一下,是v4包下的类。

  二、Camera2打开相机设备的方式跟老的不一样,以前直接就new一个就open了,比较直接。现在把camera当做一种服务去对待,要申请,而申请的方式如下,

private void openCamera() {
        if (isOpened) {
            return;
        }
        isOpened = true;
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String cameraId = manager.getCameraIdList()[0];//这个可能会有很多个,但是通常都是两个,第一个是后置,第二个是前置;
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            assert map != null;
            imageDimension = map.getOutputSizes(SurfaceTexture.class)[0];
            manager.openCamera(cameraId, new CameraDevice.StateCallback() {
                @Override
                public void onOpened(CameraDevice camera) {
                    G.i("onOpened");
                    createCameraPreview(camera);
                }

                @Override
                public void onDisconnected(CameraDevice camera) {
                    G.i("onDisconnected");
                    camera.close();
                }

                @Override
                public void onError(CameraDevice camera, int error) {
                    G.e("onError -> " + error);
                    camera.close();
                }
            }, handlerHelper.getBackgroundHandler());//这个指定其后台运行,如果直接UI线程也可以,直接填null;
            G.i("open Camera " + cameraId);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

  三、拿到相机设备的回调就是如上代码的 public void onOpened(CameraDevice camera) 方法,此时的cameraDevice就是我们可以完全使用的Camera。拿到相机以后,就开始创建预览即preview。

protected void createCameraPreview(final CameraDevice cameraDevice) {
        try {
            if (null == cameraDevice) {
                G.i("updatePreview error, return");
                return;
            }
            setUpImageReader();
            setUpMediaRecorder();
            final CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
            SurfaceTexture texture = textureView.getSurfaceTexture();
            texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight());
            Surface textureSurface = new Surface(texture);
            Surface recorderSurface = mMediaRecorder.getSurface();
            Surface imageSurface = imageReader.getSurface();
            captureRequestBuilder.addTarget(textureSurface);
            captureRequestBuilder.addTarget(recorderSurface);
            captureRequestBuilder.addTarget(imageSurface);
            List<Surface> surfaceList = Arrays.asList(textureSurface, recorderSurface, imageSurface);
            cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {//配置要接受图像的surface
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
                    try {
                        cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, handlerHelper.getBackgroundHandler());//成功配置后,便开始进行相机图像的监听
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                    mMediaRecorder.start();
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                    ToastUtils.show("Configuration change");
                }
            }, handlerHelper.getBackgroundHandler());
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void setUpMediaRecorder() {
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setProfile(getCamcorderProfile());
        mMediaRecorder.setOutputFile(new File(getExternalCacheDir(), System.currentTimeMillis() + ".mp4").getAbsolutePath());
        try {
            mMediaRecorder.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void setUpImageReader() {
        imageReader = ImageReader.newInstance(imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.YUV_420_888, 10);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireLatestImage();
                if (image != null) {
                    image.close();
                }
                G.i("onImageAvailable");
            }
        }, handlerHelper.getBackgroundHandler());
    }

由于camera2的api使用的方式是,相机设备可以向意多个surface进行图像的输出。如上代码,通过cameraDevice.createCaptureSession(....)方法去配置要输出的surface,任意一个没有被配置过的surface在使用的时候都会报错。同时有回调通知,是否配置成功,成功以后便可以开始启动图像的输出监听,即cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, handlerHelper.getBackgroundHandler());其中captureRequestBuilder就是配置相机属性以及添加那些已经成功配置过了surface,至于怎么接收相机的图像,便有各个surface的所有者自己去定义。这里使用到的是MediaRecorder和ImageReader,一个是为了录像,一个是为了所谓的监听PreviewCallback。

  注意:使用ImageReader的时候会比较卡,特别是如果使用JPEG的格式的话,因为使用JPEG,ImageReader需要进行额外的处理。我为了使回调与旧的PreviewCallback一样使用了ImageFormat.YUV_420_888格式。这个格式,输出非常流畅。

  

时间: 2024-10-10 07:21:26

关于使用Android新版Camera即Camera2的使用介绍 暨解决Camera.PreviewCallback和MediaRecorder无法同时进行的相关文章

Android Camera API/Camera2 API 相机预览及滤镜、贴纸等处理

Android Lollipop 增加了Camera2 API,并将原来的Camera API标记为废弃了.相对原来的Camera API来说,Camera2是重新定义的相机 API,也重构了相机 API 的架构.初看之下,可能会感觉Camera2使用起来比Camera要复杂,然而使用过后,你也许就会喜欢上使用Camera2了.无论是Camera还是Camera2,当相机遇到OpenGL就比较好玩了. 问题及思路 Camera的预览比较常见的是使用SurfaceHolder来预览,Camera2

Android实战技巧之三十一:拍照和录像 with Camera

Developer Guides中有一篇是专门讲Camera的,而且讲的特别细.千万别以为有了这么好的文档就可以轻松的使用android.hardware.Camera这个包去拍照和录像了,各种坑在前面等着你呢.好了,下面将要讲述我们如何像辽宁队在常规赛中填坑的经历. 一.借助intent 这就十分easy了,发个intent就有人帮你搞定拍照和录像. 拍照: public void onTakePhoto(View view) { // create Intent to take a pict

Android快速开发之appBase——(1).appBase介绍

Android快速开发之appBase--(1).appBase介绍 一直想写博客,苦于自己的文笔实在不行,在CSDN潜水了好几年,中间差不多3年没有写过博客.原因有二:1.文笔差:2.没时间. 今年开始,时间充裕了,开始计划练练自己的文笔,也让自己成长起来,希望从中能够提升自己的能力.望大家多多支持和关注!! 导读:appBase是什么? appBase是一个Android app开发的基础集合,目的是任何应用都可以在这个基础之上开发app,省去了搭建框架的时间. appBase=xutils

Failed to fetch URl https://dl-ssl.google.com/android/repository/addo Android SDK更新以及ADT更新出现问题的解决办法

Failed to fetch URl https://dl-ssl.google.com/android/repository/addo Android SDK更新以及ADT更新出现问题的解决办法

Android drawable 玩转自定义图片以及bug的解决

很久没有空更新博客了,以至于挺多东西都用过之后就忘记了,没有很好的记录下来,之前在工作的时候也是这样,用完就忘记,所以觉得还是很有必要把自己用过的一些东西,解决的一些问题记录下来的,所以以后尽量坚持一周写一次博客,记录一下自己解决的问题,也与大学共享一下,建议大家也写一下博客或笔记什么的,因为在工作中,自己接触的东西并不可能只是自己刚开始的东西,比如说Android,其实在开发一个app或平时在公司工作的时候,还需要用到很多的东西,而且还有可能有一段时间去使用别的语言去开发,如果自己不记录一下,

Android ListView的item点击无响应的解决方法

如果listitem里面包括button或者checkbox等控件,默认情况下listitem会失去焦点,导致无法响应item的事件,最常用的解决办法 是在listitem的布局文件中设置descendantFocusability属性. item的布局文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.andro

部分 CM11 系统 Android 平板运行植物大战僵尸 2 黑屏的解决办法

原文 http://forum.xda-developers.com/showthread.php?t=2755197 部分 CM11 系统的 Android 平板(例如三星 GT-P5110 )运行植物大战僵尸 2 时黑屏,解决方法如下: 安装 NOMone Resolution Changer 使用上述软件(需要 ROOT 权限)将分辨率调整到 1280 * 768 (适用于 1280 * 800 分辨率的平板),保持 DPI 不变 运行植物大战僵尸 2 部分 CM11 系统 Android

Android开发常见的Activity中内存泄漏及解决办法

上一篇文章楼主提到由Context引发的内存泄漏,在这一篇文章里,我们来谈谈Android开发中常见的Activity内存泄漏及解决办法.本文将会以"为什么""怎么解决"的方式来介绍这几种内存泄漏. 在开篇之前,先来了解一下什么是内存泄漏. 什么是内存泄漏? 内存泄漏是当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗.内存泄漏并不是指物理上的内存消失,这里的内存泄漏是值由程序分配的内存但是由于程序逻辑错误而导致程序失去了对该内存的控制,使得内存浪费. 怎

【Java/Android性能优3】Android性能调优工具TraceView使用介绍

本文转自:http://blog.csdn.net/innost/article/details/9008691 在软件开发过程中,想必很多读者都遇到过系统性能问题.而解决系统性能问题的几个主要步骤是: 测评:对系统进行大量有针对性的测试,以得到合适的测试数据. 分析系统瓶颈:分析测试数据,找到其中的hotspot(热点,即bottleneck). 性能优化:对hotspot相关的代码进行优化. 由上述步骤可知,性能优化的目标对象是hotspot.如果找到的hotspot并非真正的热点,则性能优