> 文档中心 > OpenHarmony相机开发

OpenHarmony相机开发

1、OpenHarmony相机开发简介

         OpenHarmony相机模块支持相机业务的开发,开发者可以通过已开放的接口实现相机硬件的访问、操作和新功能开发,最常见的操作如:预览、拍照和录像等。开发者也可以通过合适的接口或者接口组合实现闪光灯控制、曝光时间控制、手动对焦和自动对焦控制、变焦控制以及更多的功能。

开发者在调用Camera能力时,需要了解Camera的一些基本概念:

  • 相机静态能力:用于描述相机的固有能力的一系列参数,比如朝向、支持的分辨率等信息。
  • 物理相机:物理相机就是独立的实体摄像头设备。物理相机ID是用于标志每个物理摄像头的唯一字串。
  • 异步操作:为保证UI线程不被阻塞,部分Camera接口采用异步调用方式。异步方式API均提供了callback函数和Promise函数。

2、OpenHarmony相机开发流程及接口说明

        接口说明

        详细API含义请参考:相机管理API文档

 

参考代码如下: 

const PERMISSIONS: Array = [    'ohos.permission.CAMERA',    'ohos.permission.MICROPHONE',    'ohos.permission.MEDIA_LOCATION',    'ohos.permission.READ_MEDIA',    'ohos.permission.WRITE_MEDIA']function applyPermission() { console.info('[permission] get permission'); globalThis.abilityContext.requestPermissionFromUser(PERMISSIONS)    }

(1)、 创建相机实例:

        在实现一个相机应用之前必须先创建一个独立的相机设备,然后才能继续相机的其他操作。如果此步骤操作失败,相机可能被占用或无法使用。如果被占用,必须等到相机释放后才能重新获取CameraManager对象。通过getSupportedCameras() 方法,获取当前使用的设备支持的相机列表。相机列表中存储了当前设备拥有的所有相机ID,如果列表不为空,则列表中的每个ID都支持独立创建相机对象;否则,说明正在使用的设备无可用的相机,不能继续后续的操作。相机设备具备预览、拍照、录像、Metadata等输出流,需要通过getSupportedOutputCapability()接口获取各个输出流的具体能力,通过该接口,可以获取当前设备支持的所有输出流能力,分别在CameraOutputCapability中的各个profile字段中,相机设备创建的建议步骤如下:

import camera from '@ohos.multimedia.camera'import image from '@ohos.multimedia.image'import media from '@ohos.multimedia.media'// 创建CameraManager对象context: any = getContext(this)let cameraManager = camera.getCameraManager(this.context)if (!cameraManager) {    console.error("camera.getCameraManager error")    return;} // 监听相机状态变化cameraManager.on('cameraStatus', (cameraStatusInfo) => {    console.log(`camera : ${cameraStatusInfo.camera.cameraId}`);    console.log(`status: ${cameraStatusInfo.status}`);})// 获取相机列表let cameraArray = cameraManager.getSupportedCameras();if (cameraArray.length <= 0) {    console.error("cameraManager.getSupportedCameras error")    return;} for (let index = 0; index  {    console.log(`Camera input error code: ${error.code}`);})// 打开相机await cameraInput.open();// 获取相机设备支持的输出流能力let cameraOutputCap = cameraManager.getSupportedOutputCapability(cameraArray[0]);if (!cameraOutputCap) {    console.error("cameraManager.getSupportedOutputCapability error")    return;}console.info("outputCapability: " + JSON.stringify(cameraOutputCap));let previewProfilesArray = cameraOutputCap.previewProfiles;if (!previewProfilesArray) {    console.error("createOutput previewProfilesArray == null || undefined")} let photoProfilesArray = cameraOutputCap.photoProfiles;if (!photoProfilesArray) {    console.error("createOutput photoProfilesArray == null || undefined")} let videoProfilesArray = cameraOutputCap.videoProfiles;if (!videoProfilesArray) {    console.error("createOutput videoProfilesArray == null || undefined")} let metadataObjectTypesArray = cameraOutputCap.supportedMetadataObjectTypes;if (!metadataObjectTypesArray) {    console.error("createOutput metadataObjectTypesArray == null || undefined")}// 创建预览输出流,其中参数 surfaceId 参考下面 XComponent 组件,预览流为XComponent组件提供的surfacelet previewOutputtry {    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId)} catch (error) {    console.error("Failed to create the PreviewOutput instance.")}// 监听预览输出错误信息previewOutput.on('error', (error) => {    console.log(`Preview output error code: ${error.code}`);})// 创建ImageReceiver对象,并设置照片参数:分辨率大小是根据前面 photoProfilesArray 获取的当前设备所支持的拍照分辨率大小去设置let imageReceiver = await image.createImageReceiver(1920, 1080, 4, 8)// 获取照片显示SurfaceIdlet photoSurfaceId = await imageReceiver.getReceivingSurfaceId()// 创建拍照输出流let photoOutputtry {    photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0], photoSurfaceId)} catch (error) {   console.error('Failed to createPhotoOutput errorCode = ' + error.code);}// 创建视频录制的参数let videoConfig = {    audioSourceType: 1,    videoSourceType: 1,    profile: { audioBitrate: 48000, audioChannels: 2, audioCodec: 'audio/mp4v-es', audioSampleRate: 48000, durationTime: 1000, fileFormat: 'mp4', videoBitrate: 48000, videoCodec: 'video/mp4v-es', videoFrameWidth: 640, videoFrameHeight: 480, videoFrameRate: 30    },    url: 'file:///data/media/01.mp4',    orientationHint: 0,    maxSize: 100,    maxDuration: 500,    rotation: 0}// 创建录像输出流let videoRecordermedia.createVideoRecorder().then((recorder) => {    console.log('createVideoRecorder called')    videoRecorder = recorder})// 设置视频录制的参数videoRecorder.prepare(videoConfig)//获取录像SurfaceIdlet videoSurfaceIdvideoRecorder.getInputSurface().then((id) => {    console.log('getInputSurface called')    videoSurfaceId = id})// 创建VideoOutput对象let videoOutputtry {    videoOutput = cameraManager.createVideoOutput(videoProfilesArray[0], videoSurfaceId)} catch (error) {    console.error('Failed to create the videoOutput instance. errorCode = ' + error.code);}// 监听视频输出错误信息videoOutput.on('error', (error) => {    console.log(`Preview output error code: ${error.code}`);})

         预览流、拍照流和录像流的输入均需要提前创建surface,其中预览流为XComponent组件提供的surface,拍照流为ImageReceiver提供的surface,录像流为VideoRecorder的surface。

XComponent

mXComponentController: XComponentController = new XComponentController     // 创建XComponentControllerbuild() {    Flex() { XComponent({      // 创建XComponent     id: '',     type: 'surface',     libraryname: '',     controller: this.mXComponentController }) .onload(() => {   // 设置onload回调     // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置     this.mXComponentController.setXComponentSurfaceSize({surfaceWidth:1920,surfaceHeight:1080})     // 获取Surface ID     globalThis.surfaceId = mXComponentController.getXComponentSurfaceId() }) .width('1920px')  // 设置XComponent宽度 .height('1080px') // 设置XComponent高度    }}

ImageReceiver

function getImageReceiverSurfaceId() {    let receiver = image.createImageReceiver(640, 480, 4, 8)    console.log(TAG + 'before ImageReceiver check')    if (receiver !== undefined) {      console.log('ImageReceiver is ok')      surfaceId1 = receiver.getReceivingSurfaceId()      console.log('ImageReceived id: ' + JSON.stringify(surfaceId1))    } else {      console.log('ImageReceiver is not ok')    }  }

VideoRecorder 

function getVideoRecorderSurface() { await getFd('CameraManager.mp4'); mVideoConfig.url = mFdPath; media.createVideoRecorder((err, recorder) => {     console.info('Entering create video receiver')     mVideoRecorder = recorder     console.info('videoRecorder is :' + JSON.stringify(mVideoRecorder))     console.info('videoRecorder.prepare called.')     mVideoRecorder.prepare(mVideoConfig, (err) => {  console.info('videoRecorder.prepare success.')  mVideoRecorder.getInputSurface((err, id) => {      console.info('getInputSurface called')      mVideoSurface = id      console.info('getInputSurface surfaceId: ' + JSON.stringify(mVideoSurface))  })     }) })    }

 (2)、创建相机实例

//创建会话let captureSessiontry {    captureSession = cameraManager.createCaptureSession()} catch (error) {    console.error('Failed to create the CaptureSession instance. errorCode = ' + error.code);}// 监听session错误信息captureSession.on('error', (error) => {    console.log(`Capture session error code: ${error.code}`);})// 开始配置会话try {    captureSession.beginConfig()} catch (error) {    console.error('Failed to beginConfig. errorCode = ' + error.code);}// 向会话中添加相机输入流try {    captureSession.addInput(cameraInput)} catch (error) {    console.error('Failed to addInput. errorCode = ' + error.code);}// 向会话中添加预览输入流try {    captureSession.addOutput(previewOutput)} catch (error) {    console.error('Failed to addOutput(previewOutput). errorCode = ' + error.code);}// 向会话中添加拍照输出流try {    captureSession.addOutput(photoOutput)} catch (error) {    console.error('Failed to addOutput(photoOutput). errorCode = ' + error.code);}// 提交会话配置await captureSession.commitConfig()// 启动会话await captureSession.start().then(() => {    console.log('Promise returned to indicate the session start success.');})

(3)、切换回话

// 停止当前会话await captureSession.stop()// 开始配置会话try {    captureSession.beginConfig()} catch (error) {    console.error('Failed to beginConfig. errorCode = ' + error.code);}// 从会话中移除拍照输出流try {    captureSession.removeOutput(photoOutput)} catch (error) {    console.error('Failed to removeOutput(photoOutput). errorCode = ' + error.code);}// 向会话中添加录像输出流try {    captureSession.addOutput(videoOutput)} catch (error) {    console.error('Failed to addOutput(videoOutput). errorCode = ' + error.code);}// 提交会话配置await captureSession.commitConfig()// 启动会话await captureSession.start().then(() => {    console.log('Promise returned to indicate the session start success.');})

 (4)、参数设置

// 判断设备是否支持闪光灯let flashStatustry {    flashStatus = captureSession.hasFlash()} catch (error) {    console.error('Failed to hasFlash. errorCode = ' + error.code);}console.log('Promise returned with the flash light support status:' + flashStatus);if (flashStatus) {    // 判断是否支持自动闪光灯模式    let flashModeStatus    try { let status = captureSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO) flashModeStatus = status    } catch (error) { console.error('Failed to check whether the flash mode is supported. errorCode = ' + error.code);    }    if(flashModeStatus) { // 设置自动闪光灯模式 try {     captureSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO) } catch (error) {     console.error('Failed to set the flash mode. errorCode = ' + error.code); }    }}// 判断是否支持连续自动变焦模式let focusModeStatustry {    let status = captureSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO)    focusModeStatus = status} catch (error) {    console.error('Failed to check whether the focus mode is supported. errorCode = ' + error.code);}if (focusModeStatus) {    // 设置连续自动变焦模式    try { captureSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO)    } catch (error) { console.error('Failed to set the focus mode. errorCode = ' + error.code);    }}// 获取相机支持的可变焦距比范围let zoomRatioRangetry {    zoomRatioRange = captureSession.getZoomRatioRange()} catch (error) {    console.error('Failed to get the zoom ratio range. errorCode = ' + error.code);}// 设置可变焦距比try {    captureSession.setZoomRatio(zoomRatioRange[0])} catch (error) {    console.error('Failed to set the zoom ratio value. errorCode = ' + error.code);}

(5)、拍照

let settings = {    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,  // 设置图片质量高    rotation: camera.ImageRotation.ROTATION_0  // 设置图片旋转角度0}// 使用当前拍照设置进行拍照photoOutput.capture(settings, async (err) => {    if (err) { console.error('Failed to capture the photo ${err.message}'); return;    }    console.log('Callback invoked to indicate the photo capture request success.');});

 (6)、录像

// 启动录像输出流videoOutput.start(async (err) => {    if (err) { console.error('Failed to start the video output ${err.message}'); return;    }    console.log('Callback invoked to indicate the video output start success.');});// 开始录像videoRecorder.start().then(() => {    console.info('videoRecorder start success');}// 停止录像videoRecorder.stop().then(() => {    console.info('stop success');}// 停止录像输出流videoOutput.stop((err) => {    if (err) { console.error('Failed to stop the video output ${err.message}'); return;    }    console.log('Callback invoked to indicate the video output stop success.');});

(7)、资源释放

// 停止当前会话captureSession.stop()// 释放相机输入流cameraInput.close()// 释放预览输出流previewOutput.release()// 释放拍照输出流photoOutput.release()// 释放录像输出流videoOutput.release()// 释放会话captureSession.release()// 会话置空captureSession = null

3、应用使用相机的流程图