Android:实现手机前后摄像头预览同开

news2024/11/19 3:16:38

效果展示

一.概述

本博文讲解如何实现手机前后两颗摄像头同时预览并显示

我之前博文《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() {
	@Override
	public void onConfigured(CameraCaptureSession session) {
		Log.v(TAG, "CameraCaptureSession onConfigured");

        ...

		mFrontCaptureSession = session;
		try {
		
		    ...
			
			mFrontCaptureSession.setRepeatingRequest(mFrontPreviewRequest,
					mPreviewBackCallback, mBackgroundHandler);
		} catch (CameraAccessException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onConfigureFailed(CameraCaptureSession session) {
		Log.v(TAG, "onConfigureFailed");
		showToast("onConfigureFailed");
	}
};

后摄:

CameraCaptureSession.StateCallback mBackStateCallback = new CameraCaptureSession.StateCallback() {

	@Override
	public void onConfigured(CameraCaptureSession session) {
		Log.v(TAG, "CameraCaptureSession onConfigured");
        
		...
          
		mBackCaptureSession = session;
		try {
		
		    ...
			
			mBackCaptureSession.setRepeatingRequest(mBackPreviewRequest,
					mPreviewFrontCallback, mFrontgroundHandler);
		} catch (CameraAccessException e) {
			e.printStackTrace();
		}
	}

	@Override
	public 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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextureView
            android:id="@+id/texture_back"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>

        <TextureView
            android: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">

    <ImageView
        android:id="@+id/iv_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <com.android.cameraapp.UiUtil.AutoFitTextureView
        android:id="@+id/texture_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints" />

    <com.android.cameraapp.UiUtil.AutoFitTextureView
        android:id="@+id/texture_front"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

2.代码优化

如果看到这里,证明你已经跟随博文实现了前后双摄,回过头来看代码,会发现比较简单粗糙,就是博文开始时所述,将单个摄像头预览开启流程重复了一遍

这样的代码不简洁也不美观,且不易于扩展,可以使用工厂模式将功能代码抽象成一个Camera2Proxy,这一过程就不在此复述了。

七.结束语

前后双摄的实现过程和关键代码讲解到此结束

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

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

相关文章

抄写Linux源码(Day19:读取硬盘前的准备工作有哪些?)

回忆我们需要做的事情&#xff1a; 为了支持 shell 程序的执行&#xff0c;我们需要提供&#xff1a; 1.缺页中断(不理解为什么要这个东西&#xff0c;只是闪客说需要&#xff0c;后边再说) 2.硬盘驱动、文件系统 (shell程序一开始是存放在磁盘里的&#xff0c;所以需要这两个东…

如何在VMware workstation虚拟机中安装ensp(完美运行),解决报错40

如何在VMware workstation虚拟机中安装ensp&#xff08;完美运行&#xff09; 效果如图&#xff1a; 问题体现是ensp的路由器报错40&#xff0c;这是vbox相关的问题 在虚拟机中安装ensp的关键其实是vbox&#xff08;ensp依赖vbox&#xff09; ensp自带的vbox版本太低&#x…

想要精通算法和SQL的成长之路 - 存在重复元素

想要精通算法和SQL的成长之路 - 存在重复元素 前言一. 存在重复元素II二. 存在重复元素III2.1 基于红黑树增删改查 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 存在重复元素II 原题链接 思路&#xff1a; 我们用HashSet存储元素&#xff0c;做到去重的效果。同时存储…

防火墙的相关技术

安全技术&#xff1a;&#xff08;市场上常见的防御&#xff09; 1.入侵检测机制 阻断&#xff0c;量化&#xff0c;定位来自内外的网络的威胁情况 提供报警和事后监督。类似于监控。 2.入侵防御 以透明模式工作&#xff0c;分析数据包的内容&#xff0c;一切进入本机的内容…

windows 任务计划自动提交 笔记到github 、gitee

一、必须有个git仓库托管到git上。 这个就不用说了&#xff0c;自己在github或者码云上新建一个仓库就行了。 二、创建自动提交脚本 这个bat脚本是在windows环境下使用的。 注意&#xff1a;windows定时任务下 调用自动提交git前&#xff0c;必须先进入该git仓库目录&#x…

Windows11 安全中心页面不可用问题(无法打开病毒和威胁防护)解决方案汇总(图文介绍版)

本文目录 Windows版本与报错信息问题详细图片&#xff1a; 解决方案:方案一、管理员权限&#xff08;若你确定你的电脑只有你一个账户&#xff0c;则此教程无效&#xff0c;若你也不清楚&#xff0c;请阅读后再做打算&#xff09;方案二、修改注册表(常用方案)方案三、进入开发…

服务器or虚拟机安装SSH和虚拟机or服务器设置远程服务权限

第一步 服务器/虚拟机安装SSH工具,这是外部SSH终端连接服务器/虚拟机的第一步! sudo apt update && sudo apt upgrade#更新apt sudo apt install openssh-server#安装SSH工具 service ssh status#查看SSh运行状态 sudo systemctl enable --now ssh#运行SSH工具第二步…

基于SpringBoot的信息化在线教学平台的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 学生信息管理 教师信息管理 学生成绩管理 留言板 学生注册管理 留言反馈 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已…

各平台更新根证书

windows7 windows7根证书更新 https://support.microsoft.com/en-us/topic/support-for-urgent-trusted-root-updates-for-windows-root-certificate-program-in-windows-a4ac4d6c-7c62-3b6e-dfd2-377982bf3ea5 Linux 查看证书 ls -l /etc/ssl/certs/更新根证书 update-ca…

Cocos Creator3.8 项目实战(七)Listview 控件的实现和使用

滚动列表在游戏中也很常见&#xff0c;比如排行榜 、充值记录等&#xff0c;在这些场景中&#xff0c;都有共同的特点&#xff0c; 那就是&#xff1a;数据量大 &#xff0c; 结构相同。 在cocoscreator 中&#xff0c;没有现成的 Listview 控件&#xff0c; 无奈之下&#xff…

[python 刷题] 4 Median of Two Sorted Arrays

[python 刷题] 4 Median of Two Sorted Arrays 题目&#xff1a; Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O ( l o g ( m n ) ) O(log (mn)) O(lo…

lv8 嵌入式开发-网络编程开发 14

目录 1 I/O基本概念 1.1 IO概念 1.2 同步和异步 1.3 阻塞和非阻塞 2 五种I/O模型 2.1 阻塞IO 2.2 非阻塞I/O 2.3 多路复用I/O ​编辑 2.4 信号驱动式I/O ​编辑 2.5 异步I/O模型​编辑 3 五种I/O模型比较 4 练习 1 I/O基本概念 1.1 IO概念 I/O即数据的读取&#x…

功能定义-前方交通穿行提示制动

功能概述 前方交通穿行提示(Front Cross Traffic Alert)&#xff0c;简称FCTA&#xff0c;其功能表现为在车辆低速前进时&#xff0c;实时监测车辆前部横向接近的其他道路使用者&#xff0c;并在可能发生碰撞风险时发出警告信息 前方交通穿行制动(Front Cross Traffic Braking…

算法题:分发饼干

这个题目是贪心算法的基础练习题&#xff0c;解决思路是排序双指针谈心法&#xff0c;先将两个数组分别排序&#xff0c;优先满足最小胃口的孩子。&#xff08;本题完整题目附在了最后面&#xff09; 代码如下&#xff1a; class Solution(object):def findContentChildren(se…

【Unity ShaderGraph】| 快速制作一个实用的 模型溶解效果

前言 【Unity ShaderGraph】| 快速制作一个实用的 模型溶解效果一、效果展示二、简易溶解效果三、进阶溶解效果四、应用实例 前言 本文将使用ShaderGraph制作一个模型溶解的效果&#xff0c;可以直接拿到项目中使用。对ShaderGraph还不了解的小伙伴可以参考这篇文章&#xff1…

【Docker内容大集合】Docker从认识到实践再到底层原理大汇总

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总https://blog.csdn.net/yu_cblog/categ…

Android SurfaceFlinger导读(04)理解BufferQueue

该系列文章总纲链接&#xff1a;Android GUI系统之SurfaceFlinger 系列文章目录 说明&#xff1a; 关于导读&#xff1a;导读部分主要是方便初学者理解SurfaceFlinger代码中的机制&#xff0c;为后面分析代码打下一个更好的基础&#xff0c;这样就可以把更多的精力放在surfac…

使用opencv及FFmpeg编辑视频

使用opencv及FFmpeg编辑视频 1.融合两个视频2.为视频添加声音2.1 安装ffmpy Python包2.2 下载ffmpeg2.3 代码实现 3.效果参考文献 帮朋友做了一个小作业&#xff0c;具体实现分为几个过程&#xff1a; 将两个mp4格式视频融合到一起为新视频添加声音 1.融合两个视频 其中一个…

AI智能创作系统ChatGPT商业运营源码+AI绘画系统/支持GPT联网提问/支持Midjourney绘画+支持国内AI提问模型+Prompt应用

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统&#xff0c;支持国内AI提问模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f…

VSCode Intellij IDEA CE 数据库连接

VSCode & Intellij IDEA CE 数据库连接 大概记一下现在正在用的几个工具/插件 VSCode VSCode 里面的工具我下载了很多&#xff0c;如果只是链接 MySQL 的话&#xff0c;可能用 Jun Han 这位大佬的 MySQL 就好了&#xff1a; 使用这个插件直接打开 .sql 文件单击运行就能…