news 2026/2/16 8:32:04

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙 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 特效叠加或专业模式摄影,为用户提供极致的视觉体验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/15 7:25:24

基于Java+SpringBoot的家庭食谱管理系统(源码+lw+部署文档+讲解等)

课题介绍随着人们生活水平的提升,家庭饮食健康与多样化需求日益凸显,当前家庭食谱管理存在记录杂乱、食材搭配不科学、烹饪步骤查找不便、食材库存与食谱脱节等问题,难以满足家庭成员便捷管理食谱、合理规划饮食的需求。本课题旨在设计并实现…

作者头像 李华
网站建设 2026/2/15 1:50:57

基于单片机的盲人助行器(有完整资料)

资料查找方式:特纳斯电子(电子校园网):搜索下面编号即可编号:CJ-32-2022-047设计简介:本设计是基于单片机的盲人助行器,主要实现以下功能:(1)用超声波模块来检…

作者头像 李华
网站建设 2026/2/15 18:30:27

基于单片机的智能门禁系统(有完整资料)

资料查找方式:特纳斯电子(电子校园网):搜索下面编号即可编号:CJ-32-2022-053设计简介:本设计是基于单片机的智能门禁系统,主要实现以下功能:1)红外传感器检测是否有人,有…

作者头像 李华
网站建设 2026/2/15 4:28:04

基于Java+SpringBoot的渡口流动夜市管理信息系统(源码+lw+部署文档+讲解等)

课题介绍随着夜间经济的蓬勃发展,渡口流动夜市成为拉动消费、丰富群众生活的重要载体,但传统管理模式存在摊主登记不规范、摊位分配混乱、卫生监管滞后、人流车流管控不便等问题,影响夜市有序运营与城市管理效率。本课题旨在设计并实现一个基…

作者头像 李华
网站建设 2026/2/16 14:31:11

Java计算机毕设之基于springboot的中药科普知识平台的设计与实现Springboot框架下中药材科普系统的开发与应用(完整前后端代码+说明文档+LW,调试定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/2/16 2:44:38

用Selenium实现一个免费的Web搜索API服务

用Selenium实现一个免费的Web搜索API服务 一、引言:为什么我们需要这个工具?二、核心思路:模拟人类,获取数据三、分步实现1、搭建搜索服务端(server.py)2、创建客户端(client.py) 四…

作者头像 李华