Android Camera2 API 后台服务

news2024/11/14 13:56:34

最近在搞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文库

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常用的前端模块化标准总结

1、模块化标准出现以前使用的模块化方案&#xff1a; 1&#xff09;文件划分&#xff1a; 将不同的模块定义在不同的文件中&#xff0c;然后使用时通过script标签引入这些文件 缺点&#xff1a; 模块变量相当于是定义在全局的&#xff0c;容易造成变量名冲突&#xff08;即不…

代码随想录算法训练营第25天 | 216.组合总和III ,17.电话号码的字母组合

回溯章节理论基础&#xff1a; https://programmercarl.com/%E5%9B%9E%E6%BA%AF%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 216.组合总和III 题目链接&#xff1a;https://leetcode.cn/problems/combination-sum-iii/ 思路: 本题就是在[1,2,3,4,5,6,7,…

Git简单了解

文章目录 1、Git概述2、Git下载与安装3、Git代码托管服务3.1、使用码云托管服务 1、Git概述 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件&#xff08;Java类、xml文件、html页面等&#xff09;&#xff0c;在软件开发过程中被广泛使…

免费文字转语音工具,一款优秀且永久免费的文字转语音工具,同时拥有多种类型男声女声,支持多国语言转换,支持语速调节和下载!

一、软件简介 该工具只有一个功能&#xff0c;就是将输入框内的纯文本内容转换为指定语言的音频&#xff0c;并且可以自由调节语速及音色&#xff08;男声/女声&#xff09;&#xff0c;其内置了多种语音包&#xff0c;包含男声、女声、普通话、粤语以及方言&#xff0c;并且支…

Ubuntu安装SVN服务并结合内网穿透实现公网访问本地存储文件

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

Visual Studio 2022中创建的C++项目无法使用万能头<bits/stdc++.h>解决方案

目录 发现问题 解决办法 第一步 第二步 第三步 第四步 最后一步 问题解决 发现问题 如果大家也遇到下面这种问题&#xff0c;可能是没有include文件夹中没有bits/stdc.h 解决办法 第一步 打开一个C项目&#xff0c;鼠标移动至头文件上右击&#xff0c;选择转到文档或…

c#: 表达式树的简化

环境&#xff1a; .net 6 一、问题&#xff1f; 有下面的表达式&#xff1a; var nums new List<int> { 1, 2, 3 }; Expression<Func<int, bool>> exp i > i > nums.Max();我们知道&#xff0c;它其实就是&#xff1a;exp i > i > 3; 那么…

06-OpenFeign-使用HtppClient连接池

默认下OpenFeign使用URLConnection 请求连接&#xff0c;每次都需要创建、销毁连接 1、添加ApacheHttpClient依赖 <!-- 使用Apache HttpClient替换Feign原生httpclient--><dependency><groupId>org.apache.httpcomponents</groupId><artifact…

编曲学习:旋律创作基础概念 和弦进行作曲 和弦外音使用 作曲技巧

旋律创作基础概念 和弦进行作曲 和弦外音使用 作曲技巧https://app8epdhy0u9502.pc.xiaoe-tech.com/live_pc/l_65be1ba7e4b064a83b92a3d7?course_id=course_2XLKtQnQx9GrQHac7OPmHD9tqbv文档https://app8epdhy0u9502.pc.xiaoe-tech.com/p/t_pc/course_pc_detail/camp_pro/cour…

【GAMES101】Lecture 19 相机

目录 相机 视场 Field of View (FOV) 曝光&#xff08;Exposure&#xff09; 感光度&#xff08;ISO&#xff09; 光圈 快门 相机 成像可以通过我们之前学过的光栅化成像和光线追踪成像来渲染合成&#xff0c;也可以用相机拍摄成像 今天就来学习一下相机是如何成像的…

Golang数据库编程详解 | 深入浅出Go语言原生数据库编程

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie。 Golang学习专栏&#xff1a;https://blog.csdn.net/qq_35716689/category_12575301.html 前言 对数据库…

【Java八股面试系列】JVM-常见参数设置

目录 堆内存相关 显式指定堆内存–Xms和-Xmx 显式新生代内存(Young Generation) 显式指定永久代/元空间的大小 垃圾收集相关 垃圾回收器 GC 日志记录 处理 OOM JDK监控和故障处理工具总结 堆内存相关 Java 虚拟机所管理的内存中最大的一块&#xff0c;Java 堆是所有线…

【JavaScript + CSS】随机生成十六进制颜色

效果图 实现 <template><div class"year_area"><div class"year_list"><el-row :span"24"><div :class"showAll"><el-col :span"5" v-for"(item, index) in defaulList" :key&…

代码随想录算法训练营DAY16 | 二叉树 (3)

一、LeetCode 104 二叉树的最大深度 题目链接&#xff1a;104.二叉树的最大深度https://leetcode.cn/problems/maximum-depth-of-binary-tree/ 思路&#xff1a;采用后序遍历递归求解。 class Solution {int ans 0;public int maxDepth(TreeNode root) {if(root null){retur…

【Linux】Linux开发工具(yum、gdb、git)详解

一、软件包管理器 yum 1、什么是软件包 在 Linux 下安装软件&#xff0c;通常的办法是下载到程序的源代码&#xff0c;并进行编译&#xff0c;得到可执行程序。但这样太麻烦了&#xff0c;于是有些人把一些常用的软件提前编译好&#xff0c;做成软件包&#xff08;可以理解成…

mac电脑flutter环境配置,解决疑难问题

准备工作 首先搭建flutter的环境需要使用到flutter的sdk&#xff0c;可以直接跳去官网下载&#xff1a;Choose your first type of app - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter&#xff0c;下载时要注意你电脑所使用的芯片是Intel的还是苹果的芯片。 下载好的…

邮箱营销软件推荐?企业邮箱群发会限制吗?

邮箱营销平台怎么选择&#xff1f;哪种邮箱适合做外贸邮件群发&#xff1f; 邮箱营销凭借其精准触达、低成本和高回报的特点&#xff0c;依然是许多企业不可或缺的营销手段。该如何选择一款适合自己的工具呢&#xff1f;蜂邮EDM将为您推荐几款优秀的邮箱营销软件&#xff0c;并…

微信的年终奖牛逼,但真相是……

loonggg 读完需要 3分钟 速读仅需 1 分钟 可能标题党了一些哈。 我看最近很多人都用微信年终奖这个标题来吸引流量。 前几天我在媒体上也看到了关于腾讯年终奖的爆料&#xff0c;确实&#xff0c;微信团队相比于腾讯其他团队的年终奖高很多。 尤其是&#xff0c;微信视频号团队…

Linux学习笔记(centOS)—— 文件系统

目录 一、Linux中的文件 打开方式 二、目录结构​ 三、相关命令 切换目录命令 列出当前目录下的文件和目录命令 一、Linux中的文件 “万物皆文件。” 图1.1 所有文件 打开方式 图形化界面左上角的位置→计算机&#xff0c;打开以后就可以看到Linux全部的文件了&#xf…

Codeforces Round 923 (Div. 3)

Codeforces Round 923 (Div. 3) Codeforces Round 923 (Div. 3) A. Make it White 题意&#xff1a;略 思路&#xff1a;找最小和最大的‘B’下标即可 AC code&#xff1a; void solve() {cin >>n;string s; cin>> s;int mn INF, mx 0;for (int i 0; i <…