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

鸿蒙 HarmonyOS 6 | 多媒体 (01):相机开发 Camera Kit 拍照、录像与预览流处理

文章目录

    • 前言
    • 一、 流水线思维:输入、会话与输出
    • 二、 预览流渲染:XComponent 与 SurfaceId 的羁绊
    • 三、 捕捉光影:拍照与录像实现细节
      • 1. 拍照 (PhotoOutput)
      • 2. 录像 (VideoOutput)
    • 四、 实战代码示例
    • 五、 总结

前言

在移动互联网时代,相机早已超越了单一的“拍照工具”范畴,深度渗透到扫码支付、人脸识别、AR 互动及内容创作等核心场景。对于开发者而言,在应用内实现一个功能完备、画面流畅且 UI 高度定制的相机功能,面临着硬件生命周期管理、多分辨率适配以及性能平衡等多重挑战。

鸿蒙 HarmonyOS 6 (API 20)中,Camera Kit引入了一套基于Session 会话机制的全新开发范式。它将复杂的硬件操作抽象为清晰的输入流 (Input)会话管理 (Session)输出流 (Output),极大地降低了开发门槛。

本文将脱离简单的 Intent 跳转,深入底层,手把手带你构建一个支持预览、拍照和录像的自定义相机应用。

一、 流水线思维:输入、会话与输出

驾驭 Camera Kit 的关键在于建立 流水线 思维模型。在 API 20 中,相机的运作是一个完整的数据流动过程,可类比为摄影棚工作流:

  • 输入流 (CameraInput):相当于摄影师,负责采集光影信号。
  • 会话 (CaptureSession):相当于导演,控制全场调度(启动/停止/配置)。
  • 输出流 (Output):相当于不同的终端设备。
    • 预览输出 (PreviewOutput):送往屏幕显示(监视器)。
    • 拍照输出 (PhotoOutput):送往图像处理引擎生成图片(冲印室)。
    • 录像输出 (VideoOutput):送往编码器生成视频文件(录像机)。

开发流程通常始于camera.getCameraManager。通过相机管理器,我们查询设备支持的相机列表(前置/后置)及其能力集(分辨率/帧率)。选择合适的相机设备后,创建 Input 和 Session,并将 Output 像积木一样组装起来。

值得注意的是,Session 是核心枢纽,所有配置修改(如变焦、闪光灯)必须在Session.commitConfig()后才能生效。

二、 预览流渲染:XComponent 与 SurfaceId 的羁绊

预览是相机开发的第一道难关,因为它要求实时、高帧率地渲染。普通的 UI 组件无法通过性能瓶颈,鸿蒙为此提供了XComponent

  • XComponent:专为高性能渲染设计,提供底层渲染表面 (Surface),允许硬件直接写入显存,绕过 UI 层冗余。
  • SurfaceId:连接相机与屏幕的纽带。
    1. 在 UI 中放置XComponent
    2. 监听onLoad回调,获取唯一的surfaceId
    3. 调用cameraManager.createPreviewOutput时传入该 ID。

若 ID 传递错误或时序颠倒,预览画面将呈现黑屏。

三、 捕捉光影:拍照与录像实现细节

1. 拍照 (PhotoOutput)

创建PhotoOutput时,需通过PhotoProfile指定分辨率。通常策略是遍历设备能力集,选择满足需求(如最高像素)的配置。调用capture()方法触发快门时,可传入单次拍摄参数(如地理位置、镜像)。

2. 录像 (VideoOutput)

录像实现更为复杂,涉及音频录制、视频编码及文件封装。在鸿蒙中,VideoOutput需配合AVRecorder模块:

  1. 初始化AVRecorder,配置编码格式 (H.264/H.265)、音频采样率等。
  2. AVRecorder获取输入 Surface 的 ID。
  3. 将此 ID 传给VideoOutput,建立数据通路。

注意:录像涉及底层硬件编码器资源,释放逻辑 (release) 必须严谨,否则极易导致后续录像失败或相机卡死。

四、 实战代码示例

以下代码封装了一个CameraService单例类,完整展示了从获取权限 -> 初始化相机 -> 绑定 XComponent -> 启动预览 -> 实现拍照 -> 资源释放的全流程。

import { camera } from '@kit.CameraKit'; import { image } from '@kit.ImageKit'; import { common } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { promptAction } from '@kit.ArkUI'; // ------------------------------------------------------------- // 1. 相机服务封装类 (核心逻辑) // ------------------------------------------------------------- class CameraService { private cameraManager: camera.CameraManager | null = null; private cameraInput: camera.CameraInput | null = null; private captureSession: camera.PhotoSession | null = null; // API 20 推荐使用 PhotoSession private previewOutput: camera.PreviewOutput | null = null; private photoOutput: camera.PhotoOutput | null = null; // 当前的 SurfaceId,由 XComponent 提供 private surfaceId: string = ''; /** * 初始化相机管理器 */ init(context: common.Context) { if (!this.cameraManager) { this.cameraManager = camera.getCameraManager(context); } } /** * 启动相机预览 * @param surfaceId XComponent 提供的渲染表面 ID */ async startPreview(surfaceId: string) { this.surfaceId = surfaceId; if (!this.cameraManager) return; try { // 1. 获取支持的相机设备列表 const cameras = this.cameraManager.getSupportedCameras(); if (cameras.length === 0) { console.error('[Camera] No camera devices found'); return; } // 默认选择第一个相机 (通常是后置主摄) const cameraDevice = cameras[0]; // 2. 创建相机输入流 (Input) this.cameraInput = this.cameraManager.createCameraInput(cameraDevice); await this.cameraInput.open(); // 3. 获取相机能力集,选择合适的配置 (Profile) const capability = this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_PHOTO); // 简单起见,选择预览流的第一个配置 const previewProfile = capability.previewProfiles[0]; // 选择拍照流的第一个配置 (实际开发应筛选最高分辨率) const photoProfile = capability.photoProfiles[0]; // 4. 创建输出流 (Output) // 预览输出:绑定到 XComponent 的 surfaceId this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.surfaceId); // 拍照输出 this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile); // 5. 创建会话 (Session) 并组装 this.captureSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; this.captureSession.beginConfig(); this.captureSession.addInput(this.cameraInput); this.captureSession.addOutput(this.previewOutput); this.captureSession.addOutput(this.photoOutput); await this.captureSession.commitConfig(); await this.captureSession.start(); console.info('[Camera] Preview started successfully'); } catch (err) { const error = err as BusinessError; console.error(`[Camera] Failed to start preview: ${error.message}`); } } /** * 拍照功能 */ async takePhoto() { if (!this.photoOutput) return; try { // 配置拍照参数 const photoCaptureSetting: camera.PhotoCaptureSetting = { quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, rotation: camera.ImageRotation.ROTATION_0 }; // 触发拍照 await this.photoOutput.capture(photoCaptureSetting); // 注意:PhotoOutput.capture 只是触发动作 // 实际获取图片数据通常需要监听 'photoAvailable' 事件并配合 PhotoAccessHelper 保存 // 这里仅做触发演示 promptAction.showToast({ message: '咔嚓!拍照触发成功' }); } catch (err) { console.error(`[Camera] Take photo failed: ${(err as BusinessError).message}`); } } /** * 释放资源 * 必须在页面销毁时调用,否则可能导致相机无法再次打开 */ async release() { console.info('[Camera] Releasing resources...'); try { await this.captureSession?.stop(); await this.captureSession?.release(); await this.cameraInput?.close(); await this.previewOutput?.release(); await this.photoOutput?.release(); } catch (err) { console.error('[Camera] Release failed', err); } finally { this.captureSession = null; this.cameraInput = null; this.previewOutput = null; this.photoOutput = null; } } } // 导出单例 const cameraService = new CameraService(); // ------------------------------------------------------------- // 2. 相机预览与交互页面 // ------------------------------------------------------------- @Entry @Component struct CameraPage { private xComponentController: XComponentController = new XComponentController(); // 标记 XComponent 是否加载完成 @State isSurfaceReady: boolean = false; aboutToAppear(): void { const context = getContext(this) as common.UIAbilityContext; cameraService.init(context); } aboutToDisappear(): void { // 页面销毁时务必释放相机资源 cameraService.release(); } build() { Stack() { // 1. 相机预览区域 // 使用 XComponent 承载预览流 XComponent({ id: 'cameraPreview', type: XComponentType.SURFACE, controller: this.xComponentController }) .onLoad(() => { // 核心:当 XComponent 加载完成,获取 surfaceId this.xComponentController.setXComponentSurfaceSize({ surfaceWidth: 1080, surfaceHeight: 1920 }); const surfaceId = this.xComponentController.getXComponentSurfaceId(); console.info(`[UI] Surface created: ${surfaceId}`); this.isSurfaceReady = true; // 启动预览 cameraService.startPreview(surfaceId); }) .width('100%') .height('100%') // 2. 拍照控制层 (覆盖在预览之上) Column() { // 顶部工具栏 (模拟) Row() { // 仅作为 UI 占位示意 Text('Flash').fontColor(Color.White).fontSize(14) Text('HDR').fontColor(Color.White).fontSize(14) } .width('100%') .justifyContent(FlexAlign.SpaceBetween) .padding({ top: 40, left: 20, right: 20 }) // 底部拍照按钮 Blank() // 占位,把按钮顶到底部 Row() { // 拍照快门键 Button() .width(80) .height(80) .borderRadius(40) .backgroundColor(Color.White) .border({ width: 4, color: '#CCCCCC' }) .onClick(() => { cameraService.takePhoto(); }) // 添加一个按压效果动画 .stateEffect(true) } .width('100%') .justifyContent(FlexAlign.Center) .padding({ bottom: 50 }) } .width('100%') .height('100%') // 让点击事件穿透到下层 (除了按钮本身) .hitTestBehavior(HitTestMode.Transparent) } .width('100%') .height('100%') .backgroundColor(Color.Black) // 相机加载前显示黑色背景 } }

五、 总结

自定义相机开发是鸿蒙多媒体开发中极具挑战也极具价值的领域。通过本文,我们掌握了:

  1. Session 范式:理解Input -> Session -> Output的数据流转模型。
  2. 渲染机制:利用XComponentSurfaceId实现高性能预览。
  3. 资源管理:严格遵循生命周期,正确创建与释放相机资源。

一旦掌握了这套基础架构,你便可以在此基础上扩展出更丰富的功能,如扫码识别、AR 特效叠加或专业模式摄影,为用户提供极致的视觉体验。

http://www.jsqmd.com/news/363163/

相关文章:

  • 基于互联网的智能门锁控制系统设计(有完整资料)
  • 电脑文件msvcp110.dll丢失修复都有哪些方法?msvcp110.dll是什么?
  • 模型自动优化之调参技巧:自动调参工具的使用指南!
  • 基于STM32的智能奶瓶加热器系统(有完整资料)
  • Java毕设项目:基于springboot的学校行政办公管理系统(源码+文档,讲解、调试运行,定制等)
  • 基于STM32的盆栽养护自动监控系统(有完整资料)
  • 2026代理记账优质品牌推荐 合规专业服务可靠 - 优质品牌商家
  • 基于Java+SpringBoot的肺癌治疗药品信息管理系统(源码+lw+部署文档+讲解等)
  • Java毕设项目:基于springboot+小程序的智慧心理健康自助平台小程序的设计与实现(源码+文档,讲解、调试运行,定制等)
  • 基于Java+SpringBoot的服装销售管理系统(源码+lw+部署文档+讲解等)
  • RC缓冲电路-可控的损耗,换取系统的安全和宁静。
  • 基于单片机的盲人助行器(有完整资料)
  • Python 多线程与异步爬虫实战:以今日头条为例
  • 基于Springboot的闲一品交易平台
  • FA_融合和滤波(FF)-数据融合(0)
  • c盘红了怎么清理c盘空间,C盘变红解决方案(图文版),Windows优化工具
  • Java毕设项目推荐-基于Springboot的中药材知识科普平台设计与实现 基于springboot的中药科普知识平台的设计与实现【附源码+文档,调试定制服务】
  • 微软常用运行库合集 64位官网优化版下载,微软常用运行库合集 v2026.01.17 官方最新版 32/64位,vc运行库合集下载
  • 超实用攻略盘点:7款高效AI写论文网站技巧权威排名大公开
  • Java计算机毕设之基于springboot+小程序的心理健康咨询系统小程序的设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • 权威推荐:7个高效AI论文写作网站实用技巧全面解析
  • 芯谷科技——D805X 系列:250MHz 轨到轨输出 CMOS 运算放大器
  • 一线民警的“最强大脑”:AR眼镜如何破解四大实战难题
  • 深度测评:7款AI辅助论文写作工具的高效使用指南
  • 【图文教程】彻底关闭Windows 11自动更新
  • 2026年哈尔滨冠心病室上速厂家权威推荐榜:哈尔滨偏瘫大小便失禁/哈尔滨偏瘫无法行走/哈尔滨偏瘫肢体瘫痪/选择指南 - 优质品牌商家
  • 基于Java+SpringBoot的渡口流动夜市管理信息系统(源码+lw+部署文档+讲解等)
  • 【课程设计/毕业设计】基于Springboot框架下中药材科普系统的开发与应用springboot的中药科普知识平台的设计与实现【附源码、数据库、万字文档】
  • LangGraph速记
  • 基于Java+SpringBoot的大学校园篮球赛事管理系统(源码+lw+部署文档+讲解等)