Android:实现手机前后摄像头预览同开
效果展示
一.概述
本博文讲解如何实现手机前后两颗摄像头同时预览并显示
我之前博文《OpenGLES:GLSurfaceView实现Android Camera预览》对单颗摄像头预览做过详细讲解,而前后双摄实现原理其实也并不复杂,粗糙点说就是把单摄像头预览流程写两遍。
与之前博文中使用GLSurfaceView实现相机预览不同,这次前后双摄使用TextureView来完成
二.变量定义
2.1 公共变量
//权限
public static final int REQUEST_CAMERA_PERMISSION = 1;private String mCameraId;
private Size mPreviewSize;
public final int mMaxImages = 5;//相机状态信号量
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
2.2 摄像头相关变量
...private TextureView mFrontTextureView;
private CameraCaptureSession mFrontCaptureSession;private TextureView mBackTextureView;
private CameraCaptureSession mBackCaptureSession;...
两个CaptureSession、两个TextureView(也就是同时两个Surface)
三.OpenCamera()
在 onResume() 中先判断 TextureView 状态是否 Available()
- 如果是就 OpenCamera()
- 如果不是就设置 SurfaceTexture 监听,在 onSurfaceTextureAvailable() 监听回调中再OpenCamera()
onResume()代码:
@Override
public void onResume() {super.onResume();if (mBackTextureView.isAvailable()) {openCamera(true, mBackTextureView.getWidth(), mBackTextureView.getHeight());} else {mBackTextureView.setSurfaceTextureListener(mBackSurfaceTextureListener);}if (mFrontTextureView.isAvailable()) {openCamera(false, mFrontTextureView.getWidth(), mFrontTextureView.getHeight());} else {mFrontTextureView.setSurfaceTextureListener(mFrontSurfaceTextureListener);}startBackgroundThread();
}
OpenCamera()时需要判断当前打开的是哪颗摄像头,然后走各自对应的流程
OpenCamera()代码:
private void openCamera(boolean isBack, int width, int height) {...if (isBack) {mCameraId = manager.getCameraIdList()[0];//预览size先写成固定值mPreviewSize = new Size(1440, 1080);mBackImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, mMaxImages);mBackImageReader.setOnImageAvailableListener(mOnImageAvailableListenerBack, mBackgroundHandler);Log.v(TAG, "openCamera mCameraId=" + mCameraId);manager.openCamera(mCameraId, mStateCallBack, mBackgroundHandler);} else {mCameraId = manager.getCameraIdList()[1];//预览size先写成固定值mPreviewSize = new Size(1080, 720);mFrontImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, mMaxImages);mFrontImageReader.setOnImageAvailableListener(mOnImageAvailableListenerFront, mFrontgroundHandler);Log.v(TAG, "openCamera mCameraId=" + mCameraId);manager.openCamera(mCameraId, mStateCallFront, mFrontgroundHandler);}...}
四.createCaptureSession()
OpenCamera()之后,分别为前后摄创建CaptureSession
private void createCameraPreviewSession(boolean isBack) {try {if (isBack) {SurfaceTexture texture = mBackTextureView.getSurfaceTexture();assert texture != null;texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());ArrayList<Surface> surfaces = new ArrayList<Surface>();Surface surface = new Surface(texture);surfaces.add(surface);surfaces.add(mBackImageReader.getSurface());...mCameraDeviceBack.createCaptureSession(surfaces, mBackStateCallback, mBackgroundHandler);} else {SurfaceTexture texture = mFrontTextureView.getSurfaceTexture();assert texture != null;texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());ArrayList<Surface> surfaces = new ArrayList<Surface>();Surface surface = new Surface(texture);surfaces.add(surface);surfaces.add(mFrontImageReader.getSurface());...mCameraDeviceFront.createCaptureSession(surfaces, mFrontStateCallback, mFrontgroundHandler);}} catch (CameraAccessException e) {e.printStackTrace();}
}
五.setRepeatingRequest()
createCaptureSession()之后,在前后摄各自的状态回调StatCallback中调用setRepeatingRequest()启动预览。
前摄:
CameraCaptureSession.StateCallback mFrontStateCallback = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {Log.v(TAG, "CameraCaptureSession onConfigured");...mFrontCaptureSession = session;try {...mFrontCaptureSession.setRepeatingRequest(mFrontPreviewRequest,mPreviewBackCallback, mBackgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {Log.v(TAG, "onConfigureFailed");showToast("onConfigureFailed");}
};
后摄:
CameraCaptureSession.StateCallback mBackStateCallback = new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(CameraCaptureSession session) {Log.v(TAG, "CameraCaptureSession onConfigured");...mBackCaptureSession = session;try {...mBackCaptureSession.setRepeatingRequest(mBackPreviewRequest,mPreviewFrontCallback, mFrontgroundHandler);} catch (CameraAccessException e) {e.printStackTrace();}}@Overridepublic void onConfigureFailed(CameraCaptureSession session) {Log.v(TAG, "onConfigureFailed");showToast("onConfigureFailed");}
};
六.注意
1.布局优化
本篇博文最开始,展示了两种前后双摄效果
第一种是分屏显示,前后摄预览各占1/2,但是画面有压缩
第二种是重合显示,前后摄预览重合在一起,画面没有压缩,但是有部分区域重叠覆盖
两种不同的显示方式,其实只是两个TextureView在布局文件中不同的配置
(1).第一种是在两个TextureView控件外加了一层LinearLayout控件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextureViewandroid:id="@+id/texture_back"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/><TextureViewandroid:id="@+id/texture_front"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout>
(2).第二种去掉了LinearLayout,且在源生TextureView基础上略微封装了一个自定义的AutoFitTextureView,自动适配传入的显示区域宽高
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/iv_background"android:layout_width="match_parent"android:layout_height="match_parent" /><com.android.cameraapp.UiUtil.AutoFitTextureViewandroid:id="@+id/texture_back"android:layout_width="wrap_content"android:layout_height="wrap_content"tools:ignore="MissingConstraints" /><com.android.cameraapp.UiUtil.AutoFitTextureViewandroid:id="@+id/texture_front"android:layout_width="wrap_content"android:layout_height="wrap_content"tools:ignore="MissingConstraints" /></androidx.constraintlayout.widget.ConstraintLayout>
2.代码优化
如果看到这里,证明你已经跟随博文实现了前后双摄,回过头来看代码,会发现比较简单粗糙,就是博文开始时所述,将单个摄像头预览开启流程重复了一遍。
这样的代码不简洁也不美观,且不易于扩展,可以使用工厂模式将功能代码抽象成一个Camera2Proxy,这一过程就不在此复述了。
七.结束语
前后双摄的实现过程和关键代码讲解到此结束
相关文章:
Android:实现手机前后摄像头预览同开
效果展示 一.概述 本博文讲解如何实现手机前后两颗摄像头同时预览并显示 我之前博文《OpenGLES:GLSurfaceView实现Android Camera预览》对单颗摄像头预览做过详细讲解,而前后双摄实现原理其实也并不复杂,粗糙点说就是把单摄像头预览流程写两…...
2.2.4 yocto poky openembedded bitbake关系
一 基本概念 The Yocto Project is an open-source project that delivers a set of tools that create operating system images for embedded Linux systems. Poky is the reference operating system distribution built with Yocto Project tools, and OpenEmbedded is a …...
开源后台管理系统 (go-vue-admin)
go-vue-admin 是一套基于go语言开源的后台管理系统。功能参考诺依网站 ,前后端分离。 简介 前端采用vue3、Element Plus 、RuoYi-Vue3后端采用gofrome 框架、mysql、redis、Jwt实现了一键生成前后端代码,高效开发。 内置功能 用户管理:用…...
想升级macOS Big Sur,但是MacBook内存空间不够该怎么办?
随着使用时间的增长,我们会发现Mac电脑的存储空间越来越少,这时候我们就需要对Mac电脑进行清理,以释放更多的存储空间。那么,Mac空间不足怎么解决呢? 1.清理垃圾文件 Mac空间不足怎么解决?首先要做的就是清…...
结构化面试 --- 介绍 + 人际关系
目录 一、介绍 1、认识考试 2、认识考官 3、认识对手 4、认识考场 5、认识规则 6、如何备考 二、人际关系 练习题 第一题(换岗) 第二题(办法) 第三题(相处) 第四题 第五题 第六题 …...
李沐深度学习记录5:13.Dropout
Dropout从零开始实现 import torch from torch import nn from d2l import torch as d2l# 定义Dropout函数 def dropout_layer(X, dropout):assert 0 < dropout < 1# 在本情况中,所有元素都被丢弃if dropout 1:return torch.zeros_like(X)# 在本情况中&…...
计算机竞赛 题目:基于大数据的用户画像分析系统 数据分析 开题
文章目录 1 前言2 用户画像分析概述2.1 用户画像构建的相关技术2.2 标签体系2.3 标签优先级 3 实站 - 百货商场用户画像描述与价值分析3.1 数据格式3.2 数据预处理3.3 会员年龄构成3.4 订单占比 消费画像3.5 季度偏好画像3.6 会员用户画像与特征3.6.1 构建会员用户业务特征标签…...
MFC ExtTextOut函数学习
ExtTextOut - 扩展的文本输出; win32 api的声明如下; ExtTextOut( DC: HDC; {设备环境句柄} X, Y: Integer; {起点坐标} Options: Longint; {选项} Rect: PRect; {指定显示范围; 0 表示限制范围} Str: PChar; {字符串…...
Java中阻塞队列原理、特点、适用场景
文章目录 阻塞队列对比、总览阻塞队列本质思想主要队列讲解ArrayBlockingQueueLinkedBlockingQueueSynchronousQueueLinkedTransferQueuePriorityBlockingQueueDelayQueueLinkedBlockingDeque 阻塞队列对比、总览 阻塞队列本质思想 阻塞队列都是线程安全的队列. 其最主要的功能…...
PHP之linux、apache和nginx与安全优化面试题
1.linux常用命令 查看目录pwd 创建文件touch 创建目录mkdir 删除文件rm 删除目录rmdir移动改名文件 mc 查询目录find 修改权限chmod 压缩包 tar 安装 yum install 修改文件vi查看进程ps 停止进程kill 定时任务crontab 2、nginx的优化 gzip压缩优化 expires缓存…...
算法笔记:0-1背包问题
n个商品组成集合O,每个商品有两个属性vi(体积)和pi(价格),背包容量为C。 求解一个商品子集S,令 优化目标 1. 枚举所有商品组合 共2^n - 1种情况 2. 递归求解 KnapsackSR(h, i, c)ÿ…...
C++入门-day02
引言:在上一节中我们接触了C中的命名空间,学会了C中的标准输出流。这一节,我标题一们讲讲缺省、重载。 一、缺省参数 在C中,给函数的形参默认给一个值就是缺省参数,你可能会比较懵逼,下面看一段代码。 正常…...
模板方法模式,基于继承实现的简单的设计模式(设计模式与开发实践 P11)
文章目录 实现举例应用钩子 Hook 模板方法模式是一种基于继承的设计模式,由两部分构成: 抽象父类(一般封装了子类的算法框架)具体的实现子类 实现 简单地通过继承就可以实现 举例 足球赛 和 篮球赛 都有 3 个步骤,…...
php实战案例记录(16)php://input输入流
php://input是PHP中的一个特殊的输入流,它允许访问请求的原始数据。它主要用于处理非表单的POST请求,例如当请求的内容类型为application/json或application/xml时。使用php://input可以获取到POST请求中的原始数据,无论数据是什么格式。使用…...
cad图纸如何防止盗图(一个的制造设计型企业如何保护设计图纸文件)
在现代企业中,设计图纸是公司的重要知识产权,关系到公司的核心竞争力。然而,随着技术的发展,员工获取和传播设计图纸的途径越来越多样化,如何有效地防止员工复制设计图纸成为了企业管理的一大挑战。本文将从技术、管理…...
Windows11 安全中心页面不可用问题(无法打开病毒和威胁防护)解决方案汇总(图文介绍版)
本文目录 Windows版本与报错信息问题详细图片: 解决方案:方案一、管理员权限(若你确定你的电脑只有你一个账户,则此教程无效,若你也不清楚,请阅读后再做打算)方案二、修改注册表(常用方案)方案三、进入开发…...
1329: 【C2】【排序】奖学金
题目描述 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金。期末,每个学生都有3门课的成绩:语文、数学、英语。先按总分从高到低排序,如果两个同学总分相同,再按语文成绩从高到低排序,…...
解决dockerfile创建镜像时pip install报错的bug
项目场景: 使用docker-compose创建django容器 问题描述 > [5/5] RUN /bin/bash -c source ~/.bashrc && python3 -m pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple: 0.954 Looking in indexes: https://…...
算法题:分发饼干
这个题目是贪心算法的基础练习题,解决思路是排序双指针谈心法,先将两个数组分别排序,优先满足最小胃口的孩子。(本题完整题目附在了最后面) 代码如下: class Solution(object):def findContentChildren(se…...
WebSocket编程golang
WebSocket编程 WebSocket协议解读 websocket和http协议的关联: 都是应用层协议,都基于tcp传输协议。跟http有良好的兼容性,ws和http的默认端口都是80,wss和https的默认端口都是443。websocket在握手阶段采用http发送数据。 we…...
PHP之redis 和 memache面试题
目录 1、什么是Redis?它的主要特点是什么? 2、redis数据类型 3、Redis的持久化机制有哪些?它们之间有什么区别? 4、Redis的主从复制是什么?如何配置Redis的主从复制? 5、Redis的集群模式是什么…...
java socket实现代理Android App
实现逻辑就是转发请求和响应。 核心代码 // 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {Overridepublic void run() {try {// 监听指定的端口int port 8098; //一…...
Nacos与Eureka的区别
大家好我是苏麟今天说一说Nacos与Eureka的区别. Nacos Nacos的服务实例分为两种l类型: 临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。非临时实例:如果实例宕机,不会从服务列表剔除&…...
浅谈Rob Pike的五条编程规范
又是一篇需要我们多些思考的文章~ 简介下Rob Pike Rob Pike是Unix的先驱,UTF-8的设计人,Go语言核心设计者之一。 Rob Pike的5条编程规则 原文地址:http://users.ece.utexas.edu/~adnan/pike.html 中文翻译: 罗布派克&#x…...
LeetCode 377.组合总和IV 可解决一步爬m个台阶到n阶楼顶问题( 完全背包 + 排列数)
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围 示例 1: 输入:nums [1,2,3], target 4 输出:7 解释&#x…...
C中volatile总结
在CPU处理过程中,需要将内存中的数据载入到寄存器中才能计算,所以可能涉及到一个问题,如果内存中的数据被更改了,但是寄存器还是使用的旧数据,这样就会造成数据的不同步。 一、volatile关键字的作用 使用volatile关键…...
asp.net班级管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
一、源码特点 asp.net班级管理系统 是一套完善的web设计管理系统,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为vs2010,数据库为sqlserver2008,使用c#语言开发 asp.net班级管理系统 二、功能介绍 1…...
【Pytorch笔记】6.Transforms
pytorch官方文档 - transforms transforms需要使用计算机视觉工具包:torchvision。 torchvision.transforms:常用的图像预处理方法; torchvision.datasets:常用数据集的dataset实现,如MNIST、CIFAR-10、ImageNet等&am…...
nodejs+vue临沂特色产品销售平台elementui
从实际工作出发,对过去的临沂特色产品销售平台存在的问题进行分析,完善用户的使用体会。采用计算机系统来管理信息 提高了工作的效率。 随着信息化社会的形成和微电子技术日新月异的发展,临沂特色产品销售平台是针对目前临沂特色产品销售…...
机器学习必修课 - 使用管道 Pipeline
目标:学习使用管道(pipeline)来提高机器学习代码的效率。 1. 运行环境:Google Colab import pandas as pd from sklearn.model_selection import train_test_split!git clone https://github.com/JeffereyWu/Housing-prices-dat…...
如何用dreamweaver做网站/爱站网备案查询
问题描述:在将项目由原先的UE4打包版转换为源码编译版本时,插件由打包版复制黏贴到编译版,无法重新编译,显示Couldnt find target rules file for target 错误 解决:把项目对应的.uproject 里头的“EnterPrise”:true删…...
轩与巧之歌wordpress/大数据免费查询平台
求斐波那契额数列 ainput(输入斐波那契数列的项数n:); Azeros(a); BA(1,:); B(1,1)1; B(1,2)1; for m3:a B(m)B(m-1)B(m-2); end disp(B)...
更改网站名字/网推团队
在github上问作者,如何使用cookie,作者回复是,leptus是基于restful的框架,对于cookie和session的支持,建议转换为cowboy用,以下是转换方式 leptus_req:get_req(Req) -> cowboy_req:req() leptus_req:set…...
你眼中的网络营销是什么/seo属于什么职位类型
一、添加新的一行 0.取出最后一个子控件 UIView *last [self.view.subviews lastObject]; // 新增这行的Y 最后一个子控件Y 最后一个子控件的高度 CGFloat rowY last.frame.origin.y lat.frame.size.height 1; // 1为行间距 1. 创建一行 // UIView *rowView [[UIView al…...
工信部怎么查网站备案/网络推广方式主要有
目录 webpack构建流程(原理) 业务场景和对应解决方案 自定义webpack扩展 小结 webpack构建流程(原理) 从启动构建到输出结果一系列过程: (1)初始化参数:解析webpack配置参数&a…...
深圳建设银行社会招聘网站/搜索引擎广告图片
1:初学JAVA,都知道JAVA是面向对象的编程。笔者这节开始说说类和对象。(实例仅供参考,如若复制粘贴记得修改包名和类名,避免出错) 学习JAVA的快捷键,Alt/代码补全功能,其实此快捷键启…...