Android Camera2 API 后台服务

最近在搞CameraAPP需要将Camera2弄成一个后台服务,发现跟预览的Activity没多大变动只是加了Service,和一些简单的修改。之前的公司也用到Camera2,发现用到的时候还是蛮多的所以记录一下,代码在文章末尾

camera2的结构如下,主要是通过相机管理器(CameraManager)获得相机设备(CameraDevice),然后再开启一个控制相机的会话,最后发送 拍照、预览、录像等请求。

Camera流程大概如下

1.获取Camera2服务管理器,遍历摄像头,打开每一个摄像头

    public void onCreate() {
        super.onCreate();
        mActivity = this;
        //获取Camera管理器
        CameraManager manager = (CameraManager) this.getSystemService("camera");
        try {
            String[] ids = manager.getCameraIdList();
            mCameraNum = ids.length ;
            mCameraIds = ids;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        mBackgroundThread = new HandlerThread[mCameraNum];
        mBackgroundHandler = new Handler[mCameraNum];
        mCameraOpenCloseLock = new Semaphore[mCameraNum];
        mPreviewBuilder = new CaptureRequest.Builder[mCameraNum];
        mPreviewSession =new CameraCaptureSession[mCameraNum];
        mCameraDevice = new CameraDevice[mCameraNum];
        mStateCallback =new StateCallback[mCameraNum];
        mVideoSize = new Size[mCameraNum];
        mPreviewSize = new Size[mCameraNum];
        mImageReader = new RefCountedAutoCloseable[mCameraNum];
        mFrameListener = new FrameListener[mCameraNum];
        for (int i = 0; i < mCameraNum; i++) {

            mCameraOpenCloseLock[i]= new Semaphore(1);
            mStateCallback[i] = new StateCallback(i);

        }
        int width =1920;
        int height = 1080;
        mOpenCameraList.clear();
       //遍历摄像头,分别打开
        for (int i = 0; i < mCameraNum; i++) {
            int CameraId = Integer.valueOf(mCameraIds[i]);
            mFrameListener[i] = new FrameListener(CameraId,this);
            if(CameraId < 100 ){
                Log.e(TAG,"只打开USB摄像头 skip:"+mCameraIds[i]);
                continue;
            }
            mOpenCameraList.add(CameraId);
            startBackgroundThread(i);
            打开摄像头
            openCamera(i,width, height);
        }
        //设置前台服务
        bindNotification("Launcher 进程");
    }

1.获取摄像头参数,设置图像回调,打开摄像头

private void openCamera(int cameraNum, int width, int height) {
    if (!hasPermissionsGranted(VIDEO_PERMISSIONS)) {
        requestVideoPermissions();
        return;
    }
    CameraManager manager = (CameraManager) this.getSystemService(this.CAMERA_SERVICE);

    try {
        if (!mCameraOpenCloseLock[cameraNum].tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw new RuntimeException("Time out waiting to lock camera opening.");
        }
        Log.e(TAG, String.valueOf(manager.getCameraIdList().length));
        String cameraId = manager.getCameraIdList()[cameraNum];
        mCameraIds[cameraNum] = cameraId;
        // Choose the sizes for camera preview and video recording
        获取摄像头的参数
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

        if (map == null) {
            throw new RuntimeException("Cannot get available preview/video sizes");
        }

        mVideoSize[cameraNum] = new Size(4096,2160);
        mPreviewSize[cameraNum] = new Size(4096,2160);
        int orientation = getResources().getConfiguration().orientation;

        configureTransform(cameraNum,width, height);

        if (mImageReader[cameraNum] == null || mImageReader[cameraNum].getAndRetain() == null) {
          //设置摄像头的图像回调
            mImageReader[cameraNum] = new RefCountedAutoCloseable<>(
                    ImageReader.newInstance(mPreviewSize[cameraNum].getWidth(),
                            mPreviewSize[cameraNum].getHeight(), ImageFormat.YUV_420_888, /*maxImages*/5));

        }
        if (mImageReader[cameraNum] !=null){
            mImageReader[cameraNum].get().setOnImageAvailableListener(mFrameListener[cameraNum]
                    , mBackgroundHandler[cameraNum]);
        }
        Log.d(TAG,"openCamera:"+cameraId);
       //打开摄像头,打开成功会调用到 mStateCallback.onOpened
        manager.openCamera(cameraId, mStateCallback[cameraNum], null);
    } catch (CameraAccessException e) {
    } catch (NullPointerException e) {
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera opening.");
    }
}
class StateCallback extends CameraDevice.StateCallback {
    int cameraNum;
    public StateCallback(int cameraNum) {
        super();
        this.cameraNum = cameraNum;
    }
    //打开成功会调用到这里
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
        mCameraDevice[cameraNum] = cameraDevice;
        startPreview(cameraNum);
        mCameraOpenCloseLock[cameraNum].release();
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        mCameraOpenCloseLock[cameraNum].release();
        cameraDevice.close();
        mCameraDevice[cameraNum] = null;
    }

    @Override
    public void onError(@NonNull CameraDevice cameraDevice, int error) {
        mCameraOpenCloseLock[cameraNum].release();
        cameraDevice.close();
        mCameraDevice[cameraNum] = null;
    }

};

1.打开成功开始重定向输出对象到ImageReader

    private void startPreview(final int cameraNum) {
        if (null == mCameraDevice[cameraNum]  || null == mPreviewSize[cameraNum]) {
            return;
        }
        try {
            closePreviewSession(cameraNum);
            //设置Camera为预览输出
            mPreviewBuilder[cameraNum] = mCameraDevice[cameraNum].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            List<Surface> surfaces;
              //获取mImageReader的SureFace ,就能通过ImageReader的图像回调获取数据
         mPreviewBuilder[cameraNum].addTarget(mImageReader[cameraNum].get().getSurface());
                surfaces = Arrays.asList(
                          mImageReader[cameraNum].get().getSurface()
                );

            mCameraDevice[cameraNum].createCaptureSession(surfaces,
                    new CameraCaptureSession.StateCallback() {

                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession session) {
                            mPreviewSession[cameraNum] = session;
                            updatePreview(cameraNum);
                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                        }
                    }, mBackgroundHandler[cameraNum]);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

图像数据回调

class FrameListener implements   ImageReader.OnImageAvailableListener{
        int cameraNum;
        Context context;
        public FrameListener(int cameraNum, Context context) {
            this.cameraNum = cameraNum;
            this.context = context;
        }
        long frameID = 0;
        @Override
        public void onImageAvailable(ImageReader reader) {

            Image image = reader.acquireNextImage();
            if (image !=null) {
                 frameID++;
                 int width = image.getWidth();//1920
                 int height = image.getHeight();//1080
                 //摄像头1920*1080 y长度2073600,uv1036800
                 //获取y数据地址
                 ByteBuffer ybuffer = image.getPlanes()[0].getBuffer();
                //u数据地址,一般uv数据都是交替存放,所以这里包含有uv的数据
                 ByteBuffer ubuffer = image.getPlanes()[1].getBuffer();
                 //ByteBuffer vbuffer = image.getPlanes()[2].getBuffer();
                 int yLen = ybuffer.remaining();
                 int uLen = ubuffer.remaining();
                 int vLen = vbuffer.remaining();
                 byte[] yBytes = new byte[yLen];
                 byte[] uBytes = new byte[uLen];
                 //byte[] vBytes = new byte[vLen];
                 byte[] yuvBytes = new byte[3110400];
                 ybuffer.get(yBytes);
                 ubuffer.get(uBytes);
                 //vbuffer.get(vBytes);


                 System.arraycopy(yBytes,0,yuvBytes,0,2073600);
                 System.arraycopy(uBytes,0,yuvBytes,2073600,1036800);
                 nativeReadImageBuf(width,height,image.getFormat(),yuvBytes, 3110400, mOpenCameraList.size(), mOpenCameraList.indexOf(cameraNum));
                image.close();
            }
        }
    }

当服务起来后会直接打开摄像头,获取回调数据

运行一段时间后服务自动停止,原因是没有和APP活动在同一个生命周期

使用

        /**
     * 设置为前台服务
     * @param title
     */
protected void bindNotification(String title){
        String CHANNEL_ONE_ID = "com.example.android.camera2videopushnew";
        String CHANNEL_ONE_NAME = "com.example.android.camera2videopushnew.name";

        Notification notification = null;

        NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
                CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_LOW);
        notificationChannel.enableLights(false);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.createNotificationChannel(notificationChannel);

        notification = new Notification.Builder(this,CHANNEL_ONE_ID)
                .setContentTitle(title)
                .setContentText(title)
                .build();

        notification.flags |= Notification.FLAG_NO_CLEAR;
        startForeground(1, notification);
    }

结束

APP代码链接:【免费】AndroidCamera2后台服务APP资源-CSDN文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/368710.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

〔57页成品论文〕2024美赛E题完整1~4小问解答+配套数据代码+后续保姆级答疑服务

基于 SVM 模型的财产保险可持续性分析 摘 要 根据最新数据&#xff0c;越来越多地区的极端天气现象逐渐增多&#xff0c;保险业面临的困境&#xff1a;保险公 司的利润危机以及财产所有者的支付能力问题越发明显。本文基于 SVM 模型对不同地区 进行分类&#xff0c;为相应部门在…

【Nginx】nginx入门

文章目录 一、Web服务器二、Nginx三、Nginx的作用Web服务器正向代理反向代理 四、CentOS上安装Nginx(以CentOS 7.9为例) 一、Web服务器 Web 服务器&#xff0c;一般是指“网站服务器”&#xff0c;是指驻留于互联网上某种类型计算机的程序。Web 服务器可以向 Web 浏览器等客户…

【达梦数据库】一种另辟蹊径免密登录方式

最近对在鼓捣disql 希望可以自定义一些好玩的东西&#xff0c;来更快捷的实现登录&#xff0c;本方案借助于达梦提供的外部加密的密码文件&#xff0c;在登录的时候通过读取密码文件来实现代填用户名和密码。这种代填密码的方式在其他的软件中也有广泛的应用 概述 具体实现分两…

简单使用阿里云OSS对象存储

首先我们先去阿里云控制台开通oss对象存储&#xff08;阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台&#xff09; 这篇文件是借鉴至&#xff08;教你三分钟上手阿里云OOS上传操作_阿里云定时上传怎么使用-CSDN博客&#xff09;的&#xff0c;源码也给了&a…

2024 RTE行业(实时互动行业)人才发展学习总结

解决方案 人才画像 开发者人才素质要求&#xff1a; 具备多个领域的技术知识注重团队合作&#xff0c;具备协作能力以用户为导向的用户体验意识具备创新思维和解决问题的能力需快速响应行业变化和持续的学习能力具备项目管理能力 学习和吸收新知识的渠道 RTE人才分类

前端vue/react项目压缩图片工具@yireen/squoosh-browser

想要在前端项目中压缩图片&#xff0c;然后再上传到后端保存&#xff0c;就需要一个压缩工具的帮助&#xff0c;暂时有两个依赖库可以选择&#xff1a;image-conversion和yireen/squoosh-browser&#xff0c;看了官方仓库地址和更新时间等详情&#xff0c;发现还是yireen/squoo…

计算机硬件基础知识

mos管 电阻 电容 MOS管的功能&#xff1a; 开关功能&#xff1a;MOS管可以用作开关&#xff0c;通过控制栅极电压来控制电流的通断。当栅极电压为高电平时&#xff0c;MOS管导通&#xff0c;允许电流通过&#xff1b;当栅极电压为低电平时&#xff0c;MOS管截止&#xff0c;阻止…

LCD——与LCD有关的基础知识

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 1、s5pv210 LCD编程原理 - biaohc - 博客园 2、LCD常用接口原理_5组信号线是几位-CSDN博客 一、LCD简介 1、LCD是什么&#xff1f; LCD&#xff08;Liquid Crystal Display&#xff09;…

LeetCode 每日一题Day 54 - 61

2859. 计算 K 置位下标对应元素的和 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 请你用整数形式返回 nums 中的特定元素之 和 &#xff0c;这些特定元素满足&#xff1a;其对应下标的二进制表示中恰存在 k 个置位。 整数的二进制表示中的 1 就是这个整数的 置位…

【MATLAB第96期】基于MATLAB的SVM(线性)、SVM(高斯)、决策树、KNN等机器学习算法回归及分类Boost集成学习模型(含不同模型权重)

【MATLAB第96期】基于MATLAB的SVM(线性)、SVM(高斯)、决策树、KNN等机器学习算法回归及分类Boost集成学习模型&#xff08;含不同模型权重&#xff09; 引言 文章使用Boost集成学习方法&#xff0c;对多个机器学习模型进行融合&#xff0c;并通过算法得到对应权重。 因暂时精…

springboot整合Sa-Token实现登录认证和权限校验(万字长文)

目前在国内的后端开发中&#xff0c;常用的安全框架有spring security、shiro。现在&#xff0c;介绍一款由国人开发的安全框架Sa-Token。这个框架完全由国人开发&#xff0c;所提供的Api文档和一些设置都是比较符合国人的开发习惯的&#xff0c;本次就来介绍一下如何在spring …

计算机设计大赛 深度学习 大数据 股票预测系统 - python lstm

文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

【gcc】webrtc发送侧计算 丢包率

大神的分析 : 提到: 每当收到cc-feedback或者收到RR-report的时候就能统计出丢包率,在cc-controller中就会调用SendSideBandwidthEstimation::UpdatePacketsLost()去更新丢包率,同时进行码率预估 G:\CDN\rtcCli\m98\src\modules\congestion_controller\goog_cc\send_side_b…

计算机网络_1.6.1 常见的三种计算机网络体系结构

1.6.1 常见的三种计算机网络体系结构 1、OSI&#xff08;七层协议&#xff09;标准失败的原因2、TCP/IP参考模型3、三种网络体系结构对比 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 1、OSI&#xff08;七层协议&#xff09;标准失败的原因 &#xff08;1&#xf…

filebeat采集中断与变慢问题分析

4、未采集的那段时间内无以下日志&#xff0c;这段时间内数据源正常&#xff0c;应能被正常采集到。 5、相关进程资源&#xff0c;服务器磁盘、cpu、内存无明显异常。 6、日志中断前有如下报错。 2022-02-15T15:22:22.2230800 INFO log/harvester.go:254 Harvester started fo…

Qt程序设计-右键菜单栏功能

本文讲解如何实现Qt右键菜单栏功能 创建窗体项目,本文给窗体添加右键菜单栏功能,包含最大化、最小化、退出,当然也可以给某个控件添加,操作相同。 选中窗体,右键-转到槽,打开对话框如下 选中图示的信号,进行添加 剩余的功能直接在代码中添加。 代码如下 #ifndef MA…

一文读懂「LM,Large Model / Foundation Model」大模型

近年来&#xff0c;随着计算机技术和大数据的快速发展&#xff0c;深度学习在各个领域取得了显著的成果。为了提高模型的性能&#xff0c;研究者们不断尝试增加模型的参数数量&#xff0c;从而诞生了大模型这一概念。 一、什么是大模型&#xff1f; 1.1 概念介绍 一句话介绍…

Spring Framework(6.x)源码编译与源码阅读入门

目录 一、Spring Framework 源码获取问题 1.1 Spring Framework 官网 1.2 Spring Framework 源码地址 1.3 关于访问不了GitHub 官网的解决方案 1.3.1 修改本地hosts文件 1.3.2 GitHub520 1.3.3 Gitee 导入 二、Spring Framework 源码编译 2.1 环境说明 2.1.1 JDK版本 …

【鸿蒙】大模型对话应用(四):页面发起请求实现对话能力

Demo介绍 本demo对接阿里云和百度的大模型API&#xff0c;实现一个简单的对话应用。 DecEco Studio版本&#xff1a;DevEco Studio 3.1.1 Release HarmonyOS SDK版本&#xff1a;API9 关键点&#xff1a;ArkTS、ArkUI、UIAbility、网络http请求、列表布局、层叠布局 定义接…

CSS常用动画网站(纯css echarts等 建议经常阅读 积累素材)

CSS动画代码集合 https://www.webhek.com/post/css3-animation-sniplet-collection/#/ 这个网站中将常见的css动画都进行了集合,并且有详细的代码,可以直接使用 echarts图表 https://www.isqqw.com/ echarts也是前端常用的,虽然官方文档已经给出了很多的案例,但是有时候产品还…