热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:利用AndroidCamera2的照相机api实现实时的图像采集与预览

篇首语:本文由编程笔记#小编为大家整理,主要介绍了利用AndroidCamera2的照相机api实现实时的图像采集与预览相关的知识,希望对你有一定的参考价值。&n

篇首语:本文由编程笔记#小编为大家整理,主要介绍了利用Android Camera2 的照相机api 实现 实时的图像采集与预览相关的知识,希望对你有一定的参考价值。


   最近想要做一个客户端往服务器推送实时画面的功能,首先可以考虑到两种思路,一种是在客户端进行视频流的推送,主要利用RTSP等流媒体协议进行传输,而另外一种是通过摄像头获取当前画面,将每一帧作为对象单独传输。

   项目想要实现的功能最终目的是对实时画面的每一帧进行处理,可以考虑客户端推流到服务器,再在服务器进行帧解析的操作,但由于目前很多的流媒体推送框架在推流端或者服务端都或多或少存在限制,很少有完全开源的项目,再加上传送画面的同时需要附带部分的数据,仍然需要另外建立连接进行传输,所以暂时搁置这一方案。选择第二种思路,获取每一帧的画面,单独传输。

   要想获取实时画面,我们必须通过对安卓设备上的摄像头进行调用。

   从API21开始,安卓引入了android.hardware.camera2这个包,来替代原有的camera类,原有的camera类已经不再建议使用了。camera2中最重要的变化是,摄像头的调用不再是简单地进行实例化,而是用一种类似服务申请的方式来进行调用。通过CameraManager来管理摄像服务,需要通过建立CameraCaptureSession来建立一个调用摄像设备CameraDevices的会话,来实现对摄像头的调用。而CaptureRequest.Builder类用于建立实际的调用请求,具体的参数设置也可以通过这个类来实现(而不是对camera设备进行直接设置),这样做的目的是把对摄像头的控制与摄像头本身分离开来,用户可以通过不同的session根据不同的配置来使用摄像头。

   我们可以结合具体的代码来分析新api中摄像头调用的过程。

   首先我们想要对摄像设备进行操作,需要获得CameraManager的实例

    CameraManager cameraManager;
    cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

   我们可以调用openCamera函数打开摄像头设备

    cameraManager.openCamera(cameraId, cameraCallback, mainHandler);

   这里需要传入三个参数,cameraId是设备编号,cameraCallback控制摄像服务的回调,最后一个参数指定HandlerThread对象 

     cameraId = Integer.toString(CameraCharacteristics.LENS_FACING_FRONT);
     
     private CameraDevice.StateCallback cameraCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            Log.d("CameraCallback", "Camera Opened");
            cameraDevice = camera;
            takePreview();
        }
        @Override
        public void onDisconnected(CameraDevice cameraDevice) {
            Log.d("CameraCallback", "Camera Disconnected");
            closeCameraDevice();
        }
        @Override
        public void onError(CameraDevice cameraDevice, int i) {
            Log.d("CameraCallback", "Camera Error");
            Toast.makeText(PusherSurface.this, "摄像头开启失败", Toast.LENGTH_SHORT).show();
        }
    };

 回调函数用于指定连接摄像头设备时不同状态的操作。在这里,我们在摄像头成功连接的时候调用  takePreview()函数开启摄像头画面的预览。

private void takePreview() {
    try {
        final CaptureRequest.Builder previewRequestBuilder
                = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        previewRequestBuilder.addTarget(surfaceHolder.getSurface());
        previewRequestBuilder.addTarget(previewReader.getSurface());
        cameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(),
                previewReader.getSurface(),
                imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                if (cameraDevice == null) 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_OFF);
                    CaptureRequest previewRequest = previewRequestBuilder.build();
                    mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
            @Override
            public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
                Toast.makeText(PusherSurface.this, "配置失败", Toast.LENGTH_SHORT).show();
            }
        }, childHandler);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

要从摄像设备中获取图像,我们首先需要建立一个camera capture session。函数

createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)的第一个参数传入了我们想要绘制的视图列表,第二个参数传入的是建立摄像会话的状态回调函数,第三个参数传入相应的handler处理器。然后,我们需要利用capturerequest来定义摄像头捕获图像时候的具体参数,比如是否开启摄像头,是否自动对焦等。最后通过CamraCaptureSession.setRepeatingRequest来开启请求。这样我们就可以从capturesession传入的list中的surface列表获得连续的图像。留意到

previewRequestBuilder.addTarget(surfaceHolder.getSurface());
previewRequestBuilder.addTarget(previewReader.getSurface());

这里除了传入xml界面布局中的surfaceHolder的surface外,还传入了一个previewReader的surface。

previewReader是一个自定义的ImageReader对象。

previewReader = ImageReader.newInstance(1080, 1920, ImageFormat.YUV_420_888, 2);
        previewReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader imageReader) {
                Image image = null;
                try {
                    image = imageReader.acquireLatestImage();
                    Log.d("PreviewListener", "GetPreviewImage");
                    if (image == null) {
                        return;
                    }
                    byte[] bytes = ImageUtil.imageToByteArray(image);
                    if (pushFlag == false)
                        uploadImg(bytes);
                } finally {
                    if (image != null) {
                        image.close();
                    }
                }
            }
        }, mainHandler);

ImageReader是一个可以让我们对绘制到surface的图像进行直接操作的类。在这里我们从摄像设备中传入了连续的预览图片,也就是我们在屏幕上看到的画面,它们的格式都是未经压缩的YUV_420_888类型的(同样的如果要操作拍摄后的图片,就要设置成jpeg格式)。我们调用imageReader.acquireLatestImage或者acquireNextImage来获取图像队列中的图片。并进行操作。在这里我利用一个函数将图像压缩后转化成byte[]格式,并调用uploadImg函数上传至服务器。这样,整个摄像头的调用到预览图像的处理也就完成了。想要实现拍照功能也是大同小异,在这里我就不一一贴出了。

  欢迎更多安卓开发者一同交流。

本文出自 “DavidWillo的博客” 博客,请务必保留此出处http://davidwillo.blog.51cto.com/12613091/1908166


推荐阅读
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • Windows服务与数据库交互问题解析
    本文探讨了在Windows 10(64位)环境下开发的Windows服务,旨在定期向本地MS SQL Server (v.11)插入记录。尽管服务已成功安装并运行,但记录并未正确插入。我们将详细分析可能的原因及解决方案。 ... [详细]
  • 本文详细介绍了如何使用PHP检测AJAX请求,通过分析预定义服务器变量来判断请求是否来自XMLHttpRequest。此方法简单实用,适用于各种Web开发场景。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • c# – UWP:BrightnessOverride StartOverride逻辑 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 邮件(带附件,模拟文件上传,跨服务器)发送核心代码1.测试邮件发送附件接口***测试邮件发送附件*@parammultipartFile*@return*@RequestMappi ... [详细]
  • 本章将深入探讨移动 UI 设计的核心原则,帮助开发者构建简洁、高效且用户友好的界面。通过学习设计规则和用户体验优化技巧,您将能够创建出既美观又实用的移动应用。 ... [详细]
  • RecyclerView初步学习(一)
    RecyclerView初步学习(一)ReCyclerView提供了一种插件式的编程模式,除了提供ViewHolder缓存模式,还可以自定义动画,分割符,布局样式,相比于传统的ListVi ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 本文介绍了如何使用PHP代码实现微信平台的媒体素材上传功能,详细解释了API接口的使用方法和注意事项,确保文件路径正确以避免常见的错误。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 网络运维工程师负责确保企业IT基础设施的稳定运行,保障业务连续性和数据安全。他们需要具备多种技能,包括搭建和维护网络环境、监控系统性能、处理突发事件等。本文将探讨网络运维工程师的职业前景及其平均薪酬水平。 ... [详细]
author-avatar
陈庭勇筱玲喜芳
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有