=========== 15.Camera =========== 以SC-3568HA为例,该板卡支持USB摄像头与MIPI摄像头 MIPI摄像头连接如下图: |CAM_0| 目前该板卡支持GC8034、OV5695这两个MIPI摄像头 **15.1 MIPI摄像头DTS配置** ============================= + arch/arm64/boot/dts/rockchip/rk3568-toybrick-x0.dtsi .. code-block:: c :linenos: &i2c4 { status = "okay"; gc8034: gc8034@37 { compatible = "galaxycore,gc8034"; reg = <0x37>; clocks = <&cru CLK_CIF_OUT>;//CLK_CAM0_OUT>; clock-names = "xvclk"; power-domains = <&power RK3568_PD_VI>; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; reset-gpios = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "RK-CMK-8M-2-v1"; rockchip,camera-module-lens-name = "CK8401"; port { gc8034_out: endpoint { remote-endpoint = <&mipi_in_ucam1>; data-lanes = <1 2 3 4>; }; }; }; ...... ov5695: ov5695@36 { status = "okay"; compatible = "ovti,ov5695"; reg = <0x36>; clocks = <&cru CLK_CIF_OUT>; clock-names = "xvclk"; power-domains = <&power RK3568_PD_VI>; pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; reset-gpios = <&gpio0 RK_PD6 GPIO_ACTIVE_LOW>; pwdn-gpios = <&gpio4 RK_PB4 GPIO_ACTIVE_LOW>; rockchip,camera-module-index = <0>; rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "TongJu"; rockchip,camera-module-lens-name = "CHT842-MD"; port { ov5695_out: endpoint { remote-endpoint = <&mipi_in_ucam2>; data-lanes = <1 2>; }; }; }; }; **15.2 摄像头测试** ===================== 使用鸿蒙系统自带的摄像头应用测试: |CAM_1| 测试预览,拍照,录像都正常 **15.3 摄像头 API使用与实践** ===================== 1.标准API使用方法 -------------- .. note:: 本模块为开发者提供一套简单且易于理解的相机服务接口,开发者通过调用接口可以开发相机应用。 应用通过访问和操作相机硬件,实现基础操作,如预览、拍照和录像;还可以通过接口组合完成更多操作,如控制闪光灯和曝光时间、对焦或调焦等。 + **camera标准接口** **@ohos.multimedia.camera (相机管理)(系统接口)** + **API使用说明** 使用camera相关API开发时候,需要先了解熟悉第一个open Harmony工程的创建,相关文档 :doc:`Hello World应用以及部署<../../Quick-Start/OpenHarmony/04-应用以及部署>` 在使用一个API时,需要注意以下几点: + API权限说明 + API的参数与返回值 + API调用错误的时候,参考API错误码和通用错误码 + API示例的正确使用 如下图所示,即为标准API文档 |cameraDemo| + **官方标准开发文档** .. seealso:: `Camera官方标准API开发文档 `_ 2.社区Demo -------------- + **简介** 为了帮助开发者更快速的使用板子开发和学习,我们在gitee上提供了一个WiFi相关的使用示例,每一个项目都是独立的DevEco Studio工程,开发者可以将工程导入到DevEco Studio中即可,通过浏览代码、编译工程、安装和运行应用示例来了解应用示例中涉及API的使用方法。 `giteeCamera示例 `_ .. tip:: 在导入社区Demo工程的时候,需要开发者需要注意本地的开发环境是否与项目的一致,即本地SDK是否与项目SDK一致。 + **导入模块** 在使用Camera标准API的时候,最重要的一步是导入Camera的模块,才能使用Camera相应的API接口。通常模块导入是在文件头导入 导入模块如下: ** import camera from '@ohos.multimedia.camera'** + **API 介绍** + **camera.getCameraManager(获取相机管理器实例)** + getCameraManager(context: Context): CameraManager + **getSupportedCameras(获取支持指定的相机设备对象)** + getSupportedCameras(): Array + **getSupportedOutputCapability(查询相机设备支持的输出能力)** + getSupportedOutputCapability(camera: CameraDevice): CameraOutputCapability + **isCameraMuted(查询相机当前的禁用状态)** + isCameraMuted(): boolean + **createCameraInput(使用CameraDevice对象创建CameraInput实例)** + createCameraInput(camera: CameraDevice): CameraInput + 需要权限: ohos.permission.CAMERA + **createPreviewOutput(创建预览输出对象)** + createPreviewOutput(profile: Profile, surfaceId: string): PreviewOutput + **CameraInput.open(打开相机,通过注册回调函数获取状态)** + open(callback: AsyncCallback): void + **CameraInput.close(关闭相机,通过注册回调函数获取状态)** + close(callback: AsyncCallback): void + **CameraInput.on(‘error’) (监听CameraInput的错误事件,通过注册回调函数获取结果)** + on(type: ‘error’, camera: CameraDevice, callback: ErrorCallback): void + **CameraInput.off(‘error’) (注销监听CameraInput的错误事件)** + off(type: ‘error’, camera: CameraDevice, callback?: ErrorCallback): void + **Demo主要实现源码** camera.ets .. code-block:: TypeScript :linenos: /* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import camera from '@ohos.multimedia.camera' import deviceInfo from '@ohos.deviceInfo' import fileio from '@ohos.fileio' import image from '@ohos.multimedia.image' import media from '@ohos.multimedia.media' import mediaLibrary from '@ohos.multimedia.mediaLibrary' import Logger from './Logger' import MediaUtils from './MediaUtils' const CameraMode = { MODE_PHOTO: 0, // 拍照模式 MODE_VIDEO: 1 // 录像模式 } const CameraSize = { WIDTH: 1920, HEIGHT: 1080 } export default class CameraService { private tag: string = 'CameraService' private context: any = undefined private mediaUtil: MediaUtils = undefined private cameraManager: camera.CameraManager = undefined private cameras: Array = undefined private cameraId: string = '' private cameraInput: camera.CameraInput = undefined private previewOutput: camera.PreviewOutput = undefined private photoOutPut: camera.PhotoOutput = undefined private captureSession: camera.CaptureSession = undefined private mReceiver: image.ImageReceiver = undefined private photoUri: string = '' private fileAsset: mediaLibrary.FileAsset = undefined private fd: number = -1 private curMode = CameraMode.MODE_PHOTO private videoRecorder: media.VideoRecorder = undefined private videoOutput: camera.VideoOutput = undefined private handleTakePicture: (photoUri: string) => void = undefined private cameraOutputCapability: camera.CameraOutputCapability = undefined private videoConfig: any = { audioSourceType: 1, videoSourceType: 0, 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: '', orientationHint: 0, location: { latitude: 30, longitude: 130 }, maxSize: 10000, maxDuration: 10000 } constructor(context: any) { this.context = context this.mediaUtil = MediaUtils.getInstance(context) this.mReceiver = image.createImageReceiver(CameraSize.WIDTH, CameraSize.HEIGHT, 4, 8) Logger.debug(this.tag, 'createImageReceiver') this.mReceiver.on('imageArrival', () => { Logger.debug(this.tag, 'imageArrival') this.mReceiver.readNextImage((err, image) => { Logger.debug(this.tag, 'readNextImage') if (err || image === undefined) { Logger.error(this.tag, 'failed to get valid image') return } image.getComponent(4, (errMsg, img) => { Logger.debug(this.tag, 'getComponent') if (errMsg || img === undefined) { Logger.debug(this.tag, 'failed to get valid buffer') return } let buffer = new ArrayBuffer(4096) if (img.byteBuffer) { buffer = img.byteBuffer } else { Logger.error(this.tag, 'img.byteBuffer is undefined') } this.savePicture(buffer, image) }) }) }) } async savePicture(buffer: ArrayBuffer, img: image.Image) { Logger.debug(this.tag, 'savePicture') this.fileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.IMAGE) this.photoUri = this.fileAsset.uri Logger.debug(this.tag, `this.photoUri = ${this.photoUri}`) this.fd = await this.mediaUtil.getFdPath(this.fileAsset) Logger.debug(this.tag, `this.fd = ${this.fd}`) await fileio.write(this.fd, buffer) await this.fileAsset.close(this.fd) await img.release() Logger.debug(this.tag, 'save image done') if (this.handleTakePicture) { this.handleTakePicture(this.photoUri) } } async initCamera(surfaceId: string): Promise { try { Logger.debug(this.tag, 'initCamera') await this.releaseCamera() Logger.debug(this.tag, `deviceInfo.deviceType = ${deviceInfo.deviceType}`) if (deviceInfo.deviceType === 'default') { this.videoConfig.videoSourceType = 1 } else { this.videoConfig.videoSourceType = 0 } this.cameraManager = await camera.getCameraManager(this.context) Logger.debug(this.tag, 'getCameraManager') this.cameras = await this.cameraManager.getSupportedCameras() Logger.debug(this.tag, `get cameras ${this.cameras.length}`) if (this.cameras.length === 0) { Logger.debug(this.tag, 'cannot get cameras') return } let cameraDevice = this.cameras[0] this.cameraInput = await this.cameraManager.createCameraInput(cameraDevice) this.cameraInput.open() Logger.debug(this.tag, 'createCameraInput') this.cameraOutputCapability = await this.cameraManager.getSupportedOutputCapability(cameraDevice) let previewProfile = this.cameraOutputCapability.previewProfiles[0] this.previewOutput = await this.cameraManager.createPreviewOutput(previewProfile, surfaceId) this.previewOutput.on('frameStart', () => { Logger.debug(this.tag, 'Preview frame started'); }) this.previewOutput.on('frameEnd', () => { Logger.debug(this.tag, 'Preview frame ended'); }) this.previewOutput.on('error', (previewOutputError) => { Logger.debug(this.tag, `Preview output error code: ${previewOutputError.code}`); }) Logger.debug(this.tag, 'createPreviewOutput') let mSurfaceId = await this.mReceiver.getReceivingSurfaceId() let photoProfile = this.cameraOutputCapability.photoProfiles[0] this.photoOutPut = await this.cameraManager.createPhotoOutput(photoProfile, mSurfaceId) this.captureSession = await this.cameraManager.createCaptureSession() Logger.debug(this.tag, 'createCaptureSession') await this.captureSession.beginConfig() Logger.debug(this.tag, 'beginConfig') await this.captureSession.addInput(this.cameraInput) await this.captureSession.addOutput(this.previewOutput) await this.captureSession.addOutput(this.photoOutPut) await this.captureSession.commitConfig() await this.captureSession.start().then(() => { Logger.debug(this.tag, 'Promise returned to indicate the session start success.'); }).catch((err) => { Logger.debug(this.tag, `Failed to start the session ${err.code}`); return false }); Logger.debug(this.tag, 'captureSession start') } catch (error) { Logger.debug(this.tag, "error code:" + error.code ) Logger.debug(this.tag, "error code:" + JSON.parse(error)) return false } return true } setTakePictureCallback(callback) { this.handleTakePicture = callback } async takePicture() { Logger.debug(this.tag, 'takePicture') if (this.curMode === CameraMode.MODE_VIDEO) { this.curMode = CameraMode.MODE_PHOTO } let photoSettings = { rotation: camera.ImageRotation.ROTATION_0, quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM, location: { // 位置信息,经纬度 latitude: 12.9698, longitude: 77.7500, altitude: 1000 }, mirror: false } await this.photoOutPut.capture(photoSettings) Logger.debug(this.tag, 'takePicture done') AppStorage.Set('isRefresh', true) } async startVideo() { Logger.debug(this.tag, 'startVideo begin') await this.captureSession.stop() await this.captureSession.beginConfig() if (this.curMode === CameraMode.MODE_PHOTO) { this.curMode = CameraMode.MODE_VIDEO if (this.photoOutPut) { await this.captureSession.removeOutput(this.photoOutPut) this.photoOutPut.release() } } else { if (this.videoOutput) { await this.captureSession.removeOutput(this.videoOutput) } } if (this.videoOutput) { await this.captureSession.removeOutput(this.videoOutput) await this.videoOutput.release() } this.fileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.VIDEO) this.fd = await this.mediaUtil.getFdPath(this.fileAsset) this.videoRecorder = await media.createVideoRecorder() this.videoConfig.url = `fd://${this.fd}` await this.videoRecorder.prepare(this.videoConfig) let videoId = await this.videoRecorder.getInputSurface() let videoProfile = this.cameraOutputCapability.videoProfiles[0]; this.videoOutput = await this.cameraManager.createVideoOutput(videoProfile, videoId) await this.captureSession.addOutput(this.videoOutput) await this.captureSession.commitConfig() await this.captureSession.start() await this.videoOutput.start() await this.videoRecorder.start() Logger.debug(this.tag, 'startVideo end') } async stopVideo() { Logger.debug(this.tag, 'stopVideo called') await this.videoRecorder.stop() await this.videoOutput.stop() await this.videoRecorder.release() await this.fileAsset.close(this.fd) } async releaseCamera() { Logger.debug(this.tag, 'releaseCamera') if (this.cameraInput) { await this.cameraInput.close() } if (this.previewOutput) { await this.previewOutput.release() } if (this.photoOutPut) { await this.photoOutPut.release() } if (this.videoOutput) { await this.videoOutput.release() } if (this.captureSession) { await this.captureSession.release() } } } 3.代码编译 -------------- .. tip:: 代码编译详细流程可见,:doc:`Hello World应用以及部署<../../Quick-Start/OpenHarmony/04-应用以及部署>` 中的第二部分(构建第一个页面部分内容) 4.代码运行效果 -------------- 用以上标准API接口实现Camera Demo,如下图所示: |camera| .. |camera| image:: media1/camera.png :width: 5.75in :height: 3.92708in .. |cameraIndex| image:: media1/cameraIndex.png :width: 5.75in :height: 3.92708in .. |cameraDemo| image:: media1/cameraDemo.png :width: 5.75in :height: 3.92708in .. |CAM_0| image:: picture/CAM_0.png :width: 7in :height: 4in .. |CAM_1| image:: picture/CAM_1.png :width: 4.5in :height: 6in