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

2024-02-25 19:40

本文主要是介绍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:实现手机前后摄像头预览双开的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/746491

相关文章

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删