当前位置: 首页 > article >正文

相机预览流程:从Surface到屏幕的每一帧

引言:预览,不只是"看个大概"打开相机App的瞬间,你看到的那一帧实时画面,背后经历了什么?很多开发者以为相机预览就是"把摄像头的数据显示出来"——听起来简单,做起来却暗藏玄机。一个60fps的流畅预览背后,涉及HAL层数据采集、BufferQueue生产者/消费者模型、SurfaceFlinger合成渲染、以及AF/AE/AWB三大自动控制算法的持续收敛,任何一个环节出问题都可能导致画面卡顿、曝光忽明忽暗、焦点游移不定。本文将带你完整走一遍Camera2 API的预览流程,从Surface的创建到每一帧图像最终出现在屏幕上,把中间的每个技术环节都说清楚。如果你是相机开发的新手,看完本文你会对预览流程有系统性认识;如果你是老手,也许能从3A控制和性能优化部分找到一些新收获。一、预览的起点:Surface与SurfaceTexture在Camera2中,预览的本质是将相机数据输出到一个Surface。Surface是Android图形系统的核心抽象,它代表一块可以被渲染的缓冲区队列。预览用的Surface通常来自两个来源:TextureView和SurfaceView。1.1 TextureView vs SurfaceView这是Camera开发中最常见的选择题,两者各有利弊:特性TextureViewSurfaceView渲染方式在View树中渲染(主线程合成)独立Surface,SurfaceFlinger直接合成动画支持✅ 支持旋转、缩放、透明度动画❌ 不支持普通View动画性能开销较高(需要GPU合成到View树)较低(硬件直接合成)圆角/裁剪容易实现需要额外处理适用场景需要复杂UI效果的预览性能敏感场景、大尺寸预览// 方案一:使用 TextureViewpublicclassCameraPreviewActivityextendsAppCompatActivityimplementsTextureView.SurfaceTextureListener{privateTextureViewmTextureView;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);mTextureView=newTextureView(this);mTextureView.setSurfaceTextureListener(this);setContentView(mTextureView);}@OverridepublicvoidonSurfaceTextureAvailable(SurfaceTexturesurface,intwidth,intheight){// SurfaceTexture 就绪,可以开始打开相机openCamera(width,height);}@OverridepublicvoidonSurfaceTextureSizeChanged(SurfaceTexturesurface,intwidth,intheight){// 尺寸变化,需要重新配置预览(如旋转后)configureTransform(width,height);}@OverridepublicbooleanonSurfaceTextureDestroyed(SurfaceTexturesurface){// Surface 销毁,需要关闭相机closeCamera();returntrue;}@OverridepublicvoidonSurfaceTextureUpdated(SurfaceTexturesurface){// 每帧更新时回调,可用于帧率统计}}1.2 SurfaceTexture的内部机制TextureView内部持有一个SurfaceTexture,这是整个预览管道的关键组件。SurfaceTexture从BufferQueue拿到相机输出的图像帧后,会将其作为OpenGL ES纹理附着到GPU,再由TextureView渲染到屏幕。相机HAL → BufferQueue → SurfaceTexture → OpenGL纹理 → TextureView → SurfaceFlinger → 屏幕SurfaceTexture的核心接口是updateTexImage(),调用后会将最新的帧更新为当前的OpenGL纹理。TextureView会在每次onSurfaceTextureUpdated()回调时自动调用这个方法,你通常不需要手动调用——但如果你需要精确控制帧的消费时机(比如做帧率控制),可以手动接管这个流程。1.3 选择合适的预览尺寸这是一个容易踩坑的地方。相机支持的预览尺寸由CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP决定,你需要从中选出最合适的尺寸:privateSizechooseOptimalSize(Size[]choices,inttextureViewWidth,inttextureViewHeight,SizeaspectRatio){// 收集满足最小尺寸要求的候选项ListSizebigEnough=newArrayList();ListSizenotBigEnough=newArrayList();intw=aspectRatio.getWidth();inth=aspectRatio.getHeight();for(Sizeoption:choices){// 宽高比必须匹配(允许小误差)if(option.getHeight()==option.getWidth()*h/w){if(option.getWidth()=textureViewWidthoption.getHeight()=textureViewHeight){bigEnough.add(option);}else{notBigEnough.add(option);}}}// 优先选择满足条件的最小尺寸(节省带宽),否则选最大的if(!bigEnough.isEmpty()){returnCollections.min(bigEnough,newCompareSizesByArea());}elseif(!notBigEnough.isEmpty()){returnCollections.max(notBigEnough,newCompareSizesByArea());}else{Log.e(TAG,"找不到合适的预览尺寸");returnchoices[0];}}关键原则:预览尺寸的宽高比必须与拍照尺寸的宽高比保持一致,否则预览画面会出现拉伸变形。这是一个让很多新手困惑的问题——“我的预览怎么看起来胖了?”——十有八九就是宽高比没对齐。二、配置预览管道:CaptureSession的建立有了Surface之后,下一步是建立CameraCaptureSession,这是Camera2中代表"相机配置状态"的核心类。2.1 打开相机设备privatevoidopenCamera(intwidth,intheight){// 权限检查(必须在运行时申请 CAMERA 权限)if(ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)!=PackageManager.PERMISSION_GRANTED){requestCameraPermission();return;}// 设置预览尺寸(宽高比对齐拍照尺寸)setUpCameraOutputs(width,height);// 调整TextureView的变换矩阵,处理旋转问题configureTransform(width,height);CameraManagermanager=(CameraManager)getSystemService(Context.CAMERA_SERVICE);try{// 获取相机ID(通常0是后置,1是前置)StringcameraId=manager.getCameraIdList()[0];// 异步打开相机,结果通过 CameraDevice.StateCallback 回调manager.openCamera(cameraId,mStateCallback,mBackgroundHandler);}catch(CameraAccessExceptione){e.printStackTrace();}}privatefinalCameraDevice.StateCallbackmStateCallback=newCameraDevice.StateCallback(){@OverridepublicvoidonOpened(@NonNullCameraDevicecameraDevice){// 相机已打开,保存设备引用,开始创建预览会话mCameraDevice=cameraDevice;createCameraPreviewSession();}@OverridepublicvoidonDisconnected(@NonNullCameraDevicecameraDevice){cameraDevice.close();mCameraDevice=null;}@OverridepublicvoidonError(@NonNullCameraDevicecameraDevice,interror){cameraDevice.close();mCameraDevice=null;// 错误码:ERROR_CAMERA_IN_USE, ERROR_MAX_CAMERAS_IN_USE 等Log.e(TAG,"相机打开失败,错误码:"+error);}};2.2 创建CaptureSessioncreateCameraPreviewSession()是配置预览的核心方法。Camera2采用"会话"模型:你告诉相机"我要输出到这些Surface",相机硬件会据此配置数据通路:privatevoidcreateCameraPreviewSession(){try{SurfaceTexturetexture=mTextureView.getSurfaceTexture();// 设置默认缓冲区尺寸(必须!否则缓冲区大小是默认值)texture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());

相关文章:

相机预览流程:从Surface到屏幕的每一帧

引言:预览,不只是"看个大概" 打开相机App的瞬间,你看到的那一帧实时画面,背后经历了什么? 很多开发者以为相机预览就是"把摄像头的数据显示出来"——听起来简单,做起来却暗藏玄机。一个60fps的流畅预览背后,涉及HAL层数据采集、BufferQueue生产者…...

Radiology子刊(IF=6.3)复旦大学附属金山医院强金伟教授等团队:基于多参数MRI的深度学习和影像组学评估早期宫颈癌淋巴结转移

01文献学习今天分享的文献是由复旦大学附属金山医院强金伟教授等团队于2026年4月3日在《Radiology: Imaging Cancer》(中科院2区,IF6.3)上发表的研究“Multiparametric MRI-based Deep Learning and Radiomics for Evaluating Lymph Node Met…...

利用快马平台AI能力,十分钟快速原型一个tokenp钱包基础框架

今天想和大家分享一个快速验证区块链钱包原型的经验。最近在研究以太坊生态,发现用InsCode(快马)平台可以十分钟就搭出tokenp钱包的基础框架,特别适合做技术验证。 为什么需要快速原型 做区块链产品最怕的就是花几周开发完才发现技术路线有问题。tokenp这…...

从仿真到真机:在快马平台构建基于OpenClaw与ROS的机械臂智能抓取实战系统

从仿真到真机:在快马平台构建基于OpenClaw与ROS的机械臂智能抓取实战系统 最近在研究机器人抓取项目时,发现OpenClaw这个开源的智能抓取算法库效果很不错,但要把算法真正落地到实际机械臂上却遇到了不少坑。经过在InsCode(快马)平台上反复尝…...

Spring 注解 @Qualifier 详细解析

1. 概述 今天带你了解一下 Spring 框架中的 Qualifier 注解,它解决了哪些问题,以及如何使用它。我们还将了解它与 Primary 注解的不同之处。 2. 痛点 使用 Autowired 注解是 Spring 依赖注入的绝好方法。但是有些场景下仅仅靠这个注解不足以让Spring知道…...

AI赋能开发:让快马平台的Kimi和DeepSeek帮你思考和编写openclaw抓取策略

AI赋能开发:让快马平台的Kimi和DeepSeek帮你思考和编写openclaw抓取策略 最近在做一个机器人抓取项目,需要为三指夹爪设计不同的抓取策略。传统开发方式需要反复调试参数和算法,效率很低。后来尝试用InsCode(快马)平台的AI辅助功能&#xff…...

利用快马平台快速搭建esp8266物联网原型,十分钟完成温湿度监测系统

利用快马平台快速搭建esp8266物联网原型,十分钟完成温湿度监测系统 最近在做一个智能家居的小项目,需要实时监测房间的温湿度数据。作为硬件小白,我选择了性价比超高的ESP8266开发板,配合DHT11传感器就能实现基础功能。但最让我头…...

别再只看电流了!航模电调(ESC)的隐藏功能与安全机制深度解读

航模电调(ESC)的隐藏功能与安全机制深度解析 当你的航模飞机在高速俯冲时突然失去动力,或是悬停表演中电机莫名停转,这些惊险时刻往往与电调的保护机制密切相关。大多数玩家只把电调当作简单的"油门开关",却…...

安卓手机玩PS1游戏全攻略:DuckStation模拟器0.1-8675版汉化+BIOS配置指南

安卓手机畅玩PS1经典游戏:DuckStation模拟器深度配置指南 还记得那些年在PlayStation上度过的美好时光吗?《最终幻想7》的史诗冒险、《合金装备》的紧张潜入、《生化危机》的惊悚体验,这些经典游戏如今都能在你的安卓手机上完美重现。DuckSta…...

华为GaussDB开发者认证实验通关实录:从Java连接到SQL增删改查,手把手带你搞定7个核心任务

华为GaussDB开发者认证实战指南:7大核心任务深度解析 第一次接触华为GaussDB开发者认证的实验环节时,那种既兴奋又忐忑的心情至今记忆犹新。作为一款企业级分布式数据库,GaussDB在金融、电信等行业有着广泛应用,而认证考试正是检验…...

Chrome跨域访问失效排查指南:从--disable-web-security到SameSite策略的深度解析

1. 为什么--disable-web-security突然失效了? 最近不少开发者反馈,明明按照老方法给Chrome添加了--disable-web-security参数,浏览器顶部也显示了黄色警告条,但跨域请求依然被拦截。这个问题其实和Chrome近年来逐步收紧的安全策略…...

Elsevier Tracker:解放科研作者的审稿状态智能追踪方案

Elsevier Tracker:解放科研作者的审稿状态智能追踪方案 【免费下载链接】Elsevier-Tracker 项目地址: https://gitcode.com/gh_mirrors/el/Elsevier-Tracker 你是否曾经历过这样的科研投稿循环:每天早晨第一件事就是登录Elsevier系统&#xff0c…...

国产化替代实战:在信创环境下用Docker-compose部署Nacos 2.2.0并连接达梦数据库

信创环境下Nacos与达梦数据库的无缝集成实战 在信息技术应用创新的大背景下,国产化替代已成为企业数字化转型的关键路径。作为微服务架构的核心组件,Nacos的国产化适配能力直接关系到整个系统的稳定性和可持续性。本文将深入探讨如何在信创环境中&#x…...

从电机到USB:一文搞懂嵌入式里的感性负载、容性负载与阻抗匹配(附功率因数校正实例)

从电机到USB:一文搞懂嵌入式里的感性负载、容性负载与阻抗匹配(附功率因数校正实例) 在嵌入式系统设计中,工程师常常需要同时面对两种看似截然不同的挑战:大功率电机驱动的强电控制和高速数字通信的弱电信号处理。当电…...

Pads Layout 高效工作流——库管理优化与文件转换实战

1. 库管理优化:从混乱到高效的关键步骤 刚开始用Pads Layout那会儿,我最头疼的就是库文件管理。每次打开不同版本的设计文件,总会出现各种封装丢失、属性错乱的问题。后来才发现,库管理就像整理衣柜——分类清晰才能快速找到需要的…...

OpenClaw任务监控实战:Phi-3-vision-128k-instruct长流程管理

OpenClaw任务监控实战:Phi-3-vision-128k-instruct长流程管理 1. 为什么需要长流程监控 去年夏天,我接手了一个需要处理大量图文混合数据的项目。最初尝试用传统脚本串联处理,结果发现当任务运行到第37小时突然中断时,我甚至不知…...

基于RISC-V五级流水线设计的32位CPU:支持多种特性与AXI总线接口,适合初学者学习并附...

Riscv五级流水线32位cpu,systemverilog编写,指令集rv32i,支持数据前递,csr寄存器与中断控制器,可跑通dhrystone测试。 支持2bit饱和分支预测 本商品包括: 1.rv32五级流水线cpu代码 2.可以选择拓展的axi4总线接口代码 3…...

别再死记硬背了!一张图帮你理清InfiniBand那些让人头疼的术语(HCA/QP/LID/GID)

从数据流视角拆解InfiniBand:用一次完整通信串联核心术语 第一次接触InfiniBand的技术文档时,那些缩写字母组合——HCA、QP、CQ、LID、GID——就像天书般令人困惑。它们被分门别类地罗列在文档中,却缺乏实际场景中的互动关系。本文将打破传统…...

SpringMVC+MyBatis整合微信H5支付全流程实战(附避坑指南)

SpringMVCMyBatis整合微信H5支付全流程实战(附避坑指南) 移动支付已成为现代商业的基础设施,而微信H5支付作为连接移动网页与支付系统的重要桥梁,其技术实现却常让开发者陷入各种"坑"。本文将带你从零开始,…...

保姆级教程:用cam_lidar_calibration搞定激光雷达与相机标定(附避坑指南)

从零实现激光雷达与相机高精度标定:cam_lidar_calibration实战全解析 当激光雷达的点云遇上相机的像素,如何让它们"说同一种语言"?传感器标定就像给两个陌生人做翻译,而外参标定决定了翻译的准确性。今天我们要拆解的ca…...

高效获取金融数据:pywencai驱动的量化投资新范式

高效获取金融数据:pywencai驱动的量化投资新范式 【免费下载链接】pywencai 获取同花顺问财数据 项目地址: https://gitcode.com/gh_mirrors/py/pywencai 在数据驱动投资决策的时代,快速获取高质量金融数据成为量化策略开发的核心竞争力。pywenca…...

AssetStudio资源处理指南:从教育素材提取到独立游戏开发的创新应用

AssetStudio资源处理指南:从教育素材提取到独立游戏开发的创新应用 【免费下载链接】AssetStudio AssetStudio - Based on the archived Perfares AssetStudio, I continue Perfares work to keep AssetStudio up-to-date, with support for new Unity versions and…...

Oracle 数据仓库星型模型设计原则

星型模式(Star Schema)是 Oracle 数据仓库最核心、最常用的建模范式,核心是1 张中心事实表 N 张维度表,事实表存储度量数据,维度表存储描述属性,通过外键关联,结构清晰、查询高效、适配 Oracle…...

企业级数据采集架构实战:破解动态字体加密的高性能爬虫系统

企业级数据采集架构实战:破解动态字体加密的高性能爬虫系统 【免费下载链接】dianping_spider 大众点评爬虫(全站可爬,解决动态字体加密,非OCR)。持续更新 项目地址: https://gitcode.com/gh_mirrors/di/dianping_sp…...

魔兽争霸3游戏性能优化全攻略:从卡顿到流畅的实战指南

魔兽争霸3游戏性能优化全攻略:从卡顿到流畅的实战指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 当你在魔兽争霸3的团战关键时刻&…...

**光计算驱动下的编程新范式:用Python探索光子芯片加速的AI推理**在传统电子计算面临物理极限

光计算驱动下的编程新范式:用Python探索光子芯片加速的AI推理 在传统电子计算面临物理极限的今天,**光计算(Photonic Computing)**正从实验室走向产业化落地。它利用光子替代电子进行信息传输和处理,具备超低功耗、超…...

解锁B站视频离线观看:BilibiliDown下载神器完全指南

解锁B站视频离线观看:BilibiliDown下载神器完全指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/…...

OneDrive彻底卸载指南:从残留清理到系统优化的完整方案

OneDrive彻底卸载指南:从残留清理到系统优化的完整方案 【免费下载链接】OneDrive-Uninstaller Batch script to completely uninstall OneDrive in Windows 10 项目地址: https://gitcode.com/gh_mirrors/on/OneDrive-Uninstaller 一、问题诊断:…...

D3KeyHelper:暗黑3玩家必备的终极按键助手,彻底告别手指疲劳

D3KeyHelper:暗黑3玩家必备的终极按键助手,彻底告别手指疲劳 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面,可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 还在为暗黑破坏神…...

Unity实时翻译工具:从技术原理到实战应用

Unity实时翻译工具:从技术原理到实战应用 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 在全球化游戏市场中,语言差异已成为玩家体验与开发者传播的双重障碍。当玩家面对心仪的日…...