前言

OpenHarmony 框架支持在设备上操作相机和使用相机功能,为用户提供拍照和录像等场景的支持。本篇文章将介绍相机的开发流程和API的用法,带大家学习如何在自己的应用中使用相机。

API和组件介绍

OpenHarmony 框架相机功能需要调用相机管理接口、图片处理接口、媒体服务接口和媒体库管理接口等API和组件实现完整的相机操作和使用。

相机组件架构图

 

相机功能的开发流程

  1. 在配置文件中添加相机需要的权限,编写用户授权逻辑;

  2. 在页面中添加 XComponent 组件;

  3. 初始化相机管理器,获取可用摄像头设备;

  4. 创建拍照会话实例;

  5. 创建相机输入实例;

  6. 创建预览输出实例;

  7. 创建照片输出实例或者视频输出实例,并创建对应的保存逻辑;

  8. 配置拍照会话实例;

  9. 开启拍照会话实例;

  10. 在页面中添加拍照和录像触发条件,并编写逻辑;

相机权限

相机功能需要用户授予相应的权限后才能使用,如果用户不同意授权,应用是无法使用相机功能的,调用时会抛出 Permission to Access Camera Denied 错误,提示应用未取得用户授权。

相机功能需要的权限:

  • 摄像头权限: ohos.permission.CAMERA

  • 麦克风权限: ohos.permission.MICROPHONE

  • 媒体和文件操作权限: ohos.permission.MEDIA_LOCATION, ohos.permission.READ_MEDIA, ohos.permission.WRITE_MEDIA

相关文档:访问控制开发概述

配置权限

在进行相机功能开发前需要先在配置文件中配置需要的权限信息。

Stage 模型配置 module.json5

{     "module":{         ...         "requestPermissions": [            {                 "name": "ohos.permission.CAMERA"            },            {                 "name": "ohos.permission.MICROPHONE"            },            {                 "name": "ohos.permission.MEDIA_LOCATION"            },            {                 "name": "ohos.permission.READ_MEDIA"            },            {                 "name": "ohos.permission.WRITE_MEDIA"            }          ]    }     ... }

FA 模型配置 config.json

{     ...     "module":{         ...         "reqPermissions": [            {                 "name": "ohos.permission.CAMERA"            },            {                 "name": "ohos.permission.MICROPHONE"            },            {                 "name": "ohos.permission.MEDIA_LOCATION"            },            {                 "name": "ohos.permission.READ_MEDIA"            },            {                 "name": "ohos.permission.WRITE_MEDIA"            }                    ]    }     ... }

请求用户授权

获取 AbilityContext

Stage 模型通过继承 Ability 接口,在 Ability 被拉起时获取上下文环境。

export default class MainAbility extends Ability {     onCreate(want, launchParam) {         globalThis.context = this.context    } }

FA 模型通过 FeatureAbility API 获取上下文环境。

import FeatureAbility from '@ohos.ability.featureAbility'  globalThis.context = FeatureAbility.getContext()

获取 bundleName

Stage 模型通过 Context 可以直接获取到当前应用包名信息。

const bundleName = globalThis.context.abilityInfo.bundleName

FA 模型通过 getBundleName 接口获取当前应用包名信息。

const bundleName = globalThis.context.getBundleName()

获取 accessTokenId

根据给定的包名获取 ApplicationInfo (应用程序信息),其中包含字段:accessTokenId

accessTokenId 是应用在安装时由系统分配的唯一标识,重新安装会刷新。

import Bundle from '@ohos.bundle'  const appInfo = Bundle.getApplicationInfo(bundleName: string, bundleFlags: number, userId?: number): Promise<ApplicationInfo>  const accessTokenId = appInfo.accessTokenId

校验应用是否授予权限

  1. 获取访问控制模块对象。

import AbilityAccessCtrl from '@ohos.abilityAccessCtrl'  AtManager = AbilityAccessCtrl.createAtManager()
  1. 校验应用是否授予权限, tokenID 为应用程序的 accessTokenId,permissionName 为权限名称。

AtManager.verifyAccessToken(tokenID: number, permissionName: string): Promise<GrantStatus>

GrantStatus 说明

export enum GrantStatus { // access_token permission check fail PERMISSION_DENIED = -1, // access_token permission check success PERMISSION_GRANTED = 0, }

动态申请权限

拉起弹窗请求用户授权,传入需要申请的权限数组,得到用户授权结果。

Context.requestPermissionsFromUser(permissions: Array<string>) : Promise<PermissionRequestResult>;

PermissionRequestResult 说明

interface PermissionRequestResult { requestCode: number; // 用户传入的权限数组 permissions: Array<string>; // 相应请求权限的结果数组。0表示授权成功,-1表示失败 authResults: Array<number>; }

相机请求用户授权代码示例

Stage 模型为例:

Module 配置文件:module.json5 Ability 生命周期管理类:MainAbility.ts 请求用户授权页面:Permission.ets

预览功能的实现

XComponent 组件

XComponent 可用于EGL/OpenGLES和媒体数据写入,并显示在XComponent组件,相机中使用 XComponent 组件来展示预览图像数据。

  1. 在页面 build 中添加 XComponent 组件

xComponentController: XComponentController = new XComponentController() XComponent({ id: '', type: 'surface', controller: this.xComponentController }).onLoad(() => {})

XComponent 的 width 和 height 可以随页面逻辑需要设置。

  1. 获取 XComponent 对应 Surface 的 ID

xComponentController.getXComponentSurfaceId(): string
  1. 设置 XComponent 持有 Surface 的宽度和高度

xComponentController.setXComponentSurfaceSize(value: {surfaceWidth: number, surfaceHeight: number}): void

Surface:用于管理和传递图形和媒体的共享内存。具体场景包括图形的送显、合成,媒体的播放、录制等。 Surface 的 width 和 height 不可随意设置,需要根据摄像头支持的宽和高进行设置。

初始化相机功能

获取相机列表

  1. 获取相机管理器

import Camera from '@ohos.multimedia.camera' CameraManager = await Camera.getCameraManager(context: Context): Promise<CameraManager>
  1. 获取支持的所有相机设备列表

Cameras = await CameraManager.getCameras(): Promise<Array<Camera>>

Camera 说明

interface Camera { // 相机ID readonly cameraId: string; // 相机位置 0:相机连接类型 1:后置相机 2:前置相机 readonly cameraPosition: CameraPosition; // 相机类型 0:未指定相机类型 1:广角相机 2:超级广角相机 3:长焦相机 4:深度相机 readonly cameraType: CameraType; // 相机连接类型 0:内置相机 1:外置USB相机 2:分布式相机 readonly connectionType: ConnectionType; }

示例:

--------------Camera Info------------- CameraId: device@3.5/legacy/0 CameraPosition: 1 CameraType: 0 ConnectionType: 0 -------------------------------------- CameraId: device@3.5/legacy/1 CameraPosition: 2 CameraType: 0 ConnectionType: 0 -------------------------------------- CameraId: device@3.5/legacy/2 CameraPosition: 1 CameraType: 0 ConnectionType: 0 -------------------------------------- CameraId: device@3.5/legacy/3 CameraPosition: 1 CameraType: 0 ConnectionType: 0 -------------------------------------- CameraId: device@3.5/legacy/4 CameraPosition: 1 CameraType: 0 ConnectionType: 0 --------------------------------------

拍照会话类

一个会话对应我们一个操作模式,比如用后置摄像头拍照为一个会话。当我们切换摄像头之后,我们要销毁当前的会话,重新创建会话设置相机输入、预览输出和拍照输出等配置。

  1. 创建拍照会话实例

CaptureSession = await Camera.createCaptureSession(context: Context): Promise<CaptureSession>
  1. 使用 CameraId 创建相机输入实例

CameraInput = await CameraManager.createCameraInput(cameraId: string): Promise<CameraInput>
  1. 创建预览输出实例, surfaceId 为 xComponent 生成的 surfaceId

PreviewOutput = await Camera.createPreviewOutput(surfaceId: string): Promise<PreviewOutput>
  1. 开启设置此拍照会话实例

CaptureSession.beginConfig(): Promise<void>
  1. 为此拍照会话实例添加一个相机输入实例

CaptureSession.addInput(cameraInput: CameraInput): Promise<void>
  1. 为此拍照会话实例添加一个预览输出实例

CaptureSession.addOutput(previewOutput: PreviewOutput): Promise<void>
  1. 提交此拍照会话实例的设置

CaptureSession.commitConfig(): Promise<void>
  1. 开启拍照会话实例

CaptureSession.start(): Promise<void>

获取支持的图像尺寸

CameraInput.getSupportedSizes(format: CameraFormat): Promise<Array<Size>>

示例:

SupportedSizes 0 width = 4160 height = 3120 SupportedSizes 1 width = 4160 height = 2600 SupportedSizes 2 width = 4160 height = 2336 SupportedSizes 3 width = 3840 height = 2160 SupportedSizes 4 width = 3264 height = 2448 SupportedSizes 5 width = 3264 height = 1840 SupportedSizes 6 width = 3120 height = 3120 SupportedSizes 7 width = 2304 height = 1728 SupportedSizes 8 width = 2048 height = 1536 SupportedSizes 9 width = 1920 height = 1440 SupportedSizes 10 width = 1920 height = 1080 SupportedSizes 11 width = 1744 height = 1088 SupportedSizes 12 width = 1280 height = 720 SupportedSizes 13 width = 1232 height = 768 SupportedSizes 14 width = 1152 height = 720 SupportedSizes 15 width = 640 height = 480 SupportedSizes 16 width = 320 height = 240

预览功能代码示例

Stage 模型为例:

预览相机页面:Preview.ets

拍照功能的实现

图像接收器

在使用相机进行拍照操作时,需要创建图像接收器和拍照会话进行绑定,相机在触发拍照动作后,会把图像发送给图像接收器,并触发 imageArrival 回调。

  1. 创建图像接收器实例,传入图像的宽、高、格式和同时访问的最大图像数。

import Image from '@ohos.multimedia.image' ImageReceiver = Image.createImageReceiver(width: number, height: number, format: number, capacity: number): ImageReceiver
  1. 获取图像接收器实例的 surfaceId

SurfaceId = await ImageReceiver.getReceivingSurfaceId(): Promise<string>
  1. 设置收到图像时的监听

ImageReceiver.on(type: 'imageArrival', callback: AsyncCallback<void>): void
  1. 在监听的回调函数中,获取图像实例

Image = await ImageReceiver.readNextImage(): Promise<Image>
  1. 获取图像的组件实例

Component = await Image.getComponent(componentType: ComponentType): Promise<Component>

ComponentType 说明

enum ComponentType { // 亮度信息 YUV_Y = 1, // 色度信息 YUV_U = 2, // 色度信息 YUV_V = 3, // Jpeg 类型 JPEG = 4, }

Component 说明

interface Component { // 组件类型 readonly componentType: ComponentType; // 行距 readonly rowStride: number; // 像素间距 readonly pixelStride: number; // 组件缓冲区 readonly byteBuffer: ArrayBuffer; }
  1. 获取图像的 Buffer

Buffer = Component.byteBuffer

媒体资源文件

创建媒体资源

  1. 获取媒体库的实例

import MediaLibrary from '@ohos.multimedia.mediaLibrary' MediaLib = MediaLibrary.getMediaLibrary(context: Context): MediaLibrary
  1. 获取媒体检索配置关键字和媒体类型

FileKeyObj = MediaLibrary.FileKey MediaType = MediaLibrary.MediaType.IMAGE
  1. 获取公共目录路径

type = MediaLibrary.DirectoryType.DIR_IMAGE PublicPath = await MediaLib.getPublicDirectory(type: DirectoryType): Promise<string>

DirectoryType 说明

enum DirectoryType { // 表示Camera文件路径 DIR_CAMERA = 0, // 表示视频路径 DIR_VIDEO, // 表示图片路径 DIR_IMAGE, // 表示音频路径 DIR_AUDIO, // 表示文档路径 DIR_DOCUMENTS, // 表示下载路径 DIR_DOWNLOAD }
  1. 创建媒体资源文件

FileAsset = await MediaLib.createAsset(mediaType: MediaType, displayName: string, relativePath: string): Promise<FileAsset>

验证媒体资源文件创建是否成功

  1. 创建检索条件

let args = FileAsset.id.toString() let fetchOp = { selections: `${FileKeyObj.ID} = ? `, selectionArgs: [args], }
  1. 根据条件检索文件

FetchFileResult = await MediaLib.getFileAssets(options: MediaFetchOptions): Promise<FetchFileResult>
  1. 获取文件检索结果中的所有文件资产

FileAssets = await FetchFileResult.getAllObject(): Promise<Array<FileAsset>>

如果 FileAssets 数据大于 0 则文件创建成功

保存图片数据

  1. 打开当前文件的读写权限

FdNumber = await FileAsset.open(mode: string): Promise<number>

mode:打开文件方式,如:'r'(只读), 'w'(只写), 'rw'(读写)

  1. 将数据写入文件

import FileIO from '@ohos.fileio' FileIO.write(fd: number, buffer: ArrayBuffer | string, options?: { offset?: number; length?: number; position?: number; encoding?: string; }): Promise<number>
  1. 关闭当前文件

FileAsset.close(fd: number): Promise<void>

拍照会话类

  1. 初始化拍照会话类

同相机预览功能实现一样的步骤,创建拍照会话实例、相机输入实例和预览输出实例,并进行配置。

  1. 创建照片输出实例,此处的 surfaceId 为图像接收器获取的 surfaceId

PhotoOutPut = await CameraManager.createPhotoOutput(surfaceId: string): Promise<PhotoOutput>
  1. 将照片输出实例配置进拍照会话实例

CaptureSession.addOutput(photoOutput: PhotoOutput): Promise<void>
  1. 提交此拍照会话实例的设置

CaptureSession.commitConfig(): Promise<void>
  1. 开启拍照会话实例

CaptureSession.start(): Promise<void>

拍照事件

  1. 创建拍照设置

const photoSettings = { // 图片旋转角度 rotation: Camera.ImageRotation.ROTATION_0, // 图片质量 quality: Camera..QUALITY_LEVEL_HIGH, }

ImageRotation 说明

enum ImageRotation { // 图片旋转0度 ROTATION_0 = 0, // 图片旋转90度 ROTATION_90 = 90, // 图片旋转180度 ROTATION_180 = 180, // 图片旋转270度 ROTATION_270 = 270 }

QualityLevel 说明

enum QualityLevel { // 图片质量高 QUALITY_LEVEL_HIGH = 0, // 图片质量中等 QUALITY_LEVEL_MEDIUM, // 图片质量差 QUALITY_LEVEL_LOW }
  1. 调用照片输出实例的拍照方法,触发图像接收器的监听事件,保存图片

PhotoOutput. capture(setting?: PhotoCaptureSetting): Promise<void>

拍照功能代码示例

Stage 模型为例:

拍照功能页面:Photo.ets

录像功能的实现

视频录制器

  1. 创建媒体资源文件

创建视频资源文件与照片的媒体资源文件创建步骤是一样的,但在创建的时候获取公共资源路径的 MediaType 需要设置为 MediaLibrary.MediaType.VIDEO。

  1. 获取 FdNumber

FdNumber = await FileAsset.open(mode: string): Promise<number>
  1. 创建视频录制配置文件

let videoProfile = { // 音频编码比特率 audioBitrate: 48000, // 音频编码比特率 audioChannels: 2, // 音频编码比特率 audioCodec: 'audio/mp4a-latm', // 音频采样率 audioSampleRate: 48000, // 文件的容器格式 fileFormat: 'mp4', // 视频编码比特率 videoBitrate: 48000, // 视频编码格式 videoCodec: 'video/mp4v-es', // 录制视频帧的宽 videoFrameWidth: 1280, // 录制视频帧的高 videoFrameHeight: 720, // 录制视频帧的高 videoFrameRate: 30 } let videoConfig = { // 视频录制的音频源类型 // AUDIO_SOURCE_TYPE_DEFAULT 0 , AUDIO_SOURCE_TYPE_MIC 1 audioSourceType: 0, // 视频录制的音频源类型 // VIDEO_SOURCE_TYPE_SURFACE_YUV 0, VIDEO_SOURCE_TYPE_SURFACE_YUV 1 videoSourceType: 0, // 视频录制的profile profile: this.videoProfile, // 视频输出URL:fd://xx (fd number) // 文件需要由调用者创建,并赋予适当的权限 url: `fd://${FdNumber}`, // 录制视频的旋转角度 rotation: 0, // 录制视频的旋转角度 location: { latitude: 30, longitude: 130 } }
  1. 创建视频录制器实例

import Media from '@ohos.multimedia.media' videoRecorder = await Media.createVideoRecorder(): Promise<VideoRecorder>
  1. 视频录制的参数设置

videoRecorder.prepare(config: VideoRecorderConfig): Promise<void>
  1. 获取视频录制器实例的 surfaceId

SurfaceId = await videoRecorder.getInputSurface(): Promise<string>

拍照会话类

  1. 初始化拍照会话类

同相机预览功能实现一样的步骤,创建拍照会话实例、相机输入实例和预览输出实例,并进行配置。

  1. 创建视频输出实例,此处的 surfaceId 为视频录制器实例获取的 surfaceId

VideoOutput = await CameraManager.createVideoOutput(surfaceId: string): Promise<VideoOutput>
  1. 将视频输出实例配置进拍照会话实例

CaptureSession.addOutput(videoOutput: VideoOutput): Promise<void>
  1. 提交此拍照会话实例的设置

CaptureSession.commitConfig(): Promise<void>
  1. 开启拍照会话实例

CaptureSession.start(): Promise<void>

录像事件

  1. 开始录制

开启视频输出实例

VideoOutput.start(): Promise<void>

开始视频录制

VideoRecorder.start(): Promise<void>
  1. 暂停视频录制

停止视频输出实例

VideoOutput.stop(): Promise<void>

暂停视频录制

VideoRecorder.pause(): Promise<void>
  1. 恢复视频录制

开启视频输出实例

VideoOutput.start(): Promise<void>

恢复视频录制

VideoRecorder.resume(): Promise<void>
  1. 录制结束,停止视频录制

停止视频输出实例

VideoOutput.stop(): Promise<void>

停止视频录制

VideoRecorder.stop(): Promise<void>

录像功能代码示例

Stage 模型为例:

录像功能页面:Video.ets

释放资源

当相机功能使用完毕或需要退出相机所在页面或应用时,需要对相机调用的资源进行释放。

  1. 释放相机输入实例

CameraInput.release(): Promise<void>
  1. 释放预览输出实例

PreviewOutput.release(): Promise<void>
  1. 释放图像接收器实例

ImageReceiver.release(): Promise<void>
  1. 释放照片输出实例

PhotoOutPut.release(): Promise<void>
  1. 释放视频录制器实例

VideoRecorder.release(): Promise<void>
  1. 释放视频输出实例

VideoOutput.release(): Promise<void>
  1. 关闭媒体资源文件实例

FileAsset.close(fd: number): Promise<void>
  1. 释放拍照会话实例

CaptureSession.release(): Promise<void>

相机的其他功能

设置闪光灯模式

判断设备是否支持闪光灯。

CameraInput.hasFlash(): Promise<boolean>

判断设备是否支持指定闪光灯模式。

CameraInput.isFlashModeSupported(flashMode: FlashMode): Promise<boolean>

设置闪光灯模式,进行设置之前,需要先检查:

  • 设备是否支持闪光灯。

  • 设备是否支持指定的闪光灯模式。

CameraInput.setFlashMode(flashMode: FlashMode): Promise<void>

FlashMode 说明

enum FlashMode { // 闪光灯关闭 FLASH_MODE_CLOSE = 0, // 闪光灯关闭。 FLASH_MODE_OPEN, // 自动闪光灯 FLASH_MODE_AUTO, // 闪光灯常亮 FLASH_MODE_ALWAYS_OPEN }

设置焦距模式

判断设备是否支持指定的焦距模式。

CameraInput.isFocusModeSupported(afMode: FocusMode): Promise<boolean>

设置焦距模式,进行设置之前,需要先检查设备是否支持指定的焦距模式。

CameraInput.setFocusMode(afMode: FocusMode): Promise<void>

FocusMode 说明

enum FocusMode { // 手动变焦模式 FOCUS_MODE_MANUAL = 0, // 连续自动变焦模式 FOCUS_MODE_CONTINUOUS_AUTO, // 自动变焦模式 FOCUS_MODE_AUTO, // 定焦模式 FOCUS_MODE_LOCKED }

设置可变焦距比

获取可变焦距比范围

CameraInput.getZoomRatioRange(): Promise<Array<number>>

获取当前的可变焦距比

CameraInput.getZoomRatio(): Promise<number>

设置可变焦距比

CameraInput.setZoomRatio(zoomRatio: number): Promise<void>
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐