1# 如何调用设备摄像头进行拍照、预览并将拍摄结果保存在媒体库中 2 3## 场景说明 4 5调用设备摄像头进行拍照、预览是许多应用开发过程中都需要的功能。在拍照完成时显示照片预览图可以确认拍摄的照片是否达到预期,本例将为大家介绍如何实现上述功能。 6 7## 效果呈现 8 9本例效果如下: 10 11| 拍照 | 预览 | 12| :----------------------------------------------------------: | :----------------------------------------------------------: | 13| <img src="figures/camera.png" alt="contactlist" style="zoom: 45%;" /> | <img src="figures/camerapreview.gif" alt="contactlist" style="zoom: 50%;" /> | 14 15 16 17## 运行环境 18 19本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。 20 21- IDE:DevEco Studio 4.0.0.201 Beta1 22- SDK:Ohos_sdk_public 4.0.7.5 (API Version 10 Beta1) 23 24## 实现思路 25 26本例使用@ohos.multimedia.camera接口实现相机示例的主要功能:拍照、预览; 27 28- 拍照:XComponent组件负责绘制摄像头画面呈现的窗口,其onload事件调用cameraModel.ts的initCamera方法初始化相机功能输出画面信息。拍照动作使用Image组件实现,其onclick事件调用cameraModel.ts的takepicture方法开始拍照。 29 30- 预览:返回相机界面点击底部左侧预览图可进入相册应用,可以在其中查看照片和录制的视频。 31 32## 开发步骤 33 341. 申请所需权限 35 36 在model.json5中添加以下配置: 37 38 ```json 39 "requestPermissions": [ 40 { 41 "name": "ohos.permission.CAMERA"//允许应用使用相机拍摄照片和录制视频 42 }, 43 { 44 "name": "ohos.permission.MICROPHONE"//允许应用使用麦克风 45 }, 46 { 47 "name": "ohos.permission.MEDIA_LOCATION"//允许应用访问用户媒体文件中的地理位置信息 48 }, 49 { 50 "name": "ohos.permission.WRITE_MEDIA"//允许应用读写用户外部存储中的媒体文件信息 51 }, 52 { 53 "name": "ohos.permission.READ_MEDIA"//允许应用读取用户外部存储中的媒体文件信息 54 } 55 ] 56 ``` 57 582. 创建绘制组件XComponent以输出摄像头获取的画面,其绑定的onload方法中设定了画幅的大小。 59 60 ```typescript 61 build() { 62 Column() { 63 Title() 64 .visibility(this.isTitleShow ? Visibility.Visible : Visibility.None) 65 Stack({ alignContent: Alignment.Bottom }) { 66 Stack({ alignContent: Alignment.TopStart }) { 67 XComponent({ 68 id: 'componentId', 69 type: 'surface', 70 controller: this.mXComponentController //将控制器绑定至XComponent组件 71 }) 72 .onLoad(() => { 73 this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 });//设置surface大小 74 this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); 75 this.currentModel = CameraMode.modePhoto; 76 this.cameraModel.initCamera(this.surfaceId); //调用model/cameraModel.ts初始化相机功能 77 }) 78 .width('100%') 79 .height('100%') 80 .margin({ bottom: 152 }) 81 Column() { 82 } 83 .width('97%') 84 .height('100%') 85 ``` 86 873. 初始化相机功能 88 89 initCamera方法通过创建相机管理器实例cameraMgr来创建画面输出对象previewOutput。cameraMgr再通过创建CaptureSession实例来配置会话,完成相机功能的准备工作。 90 91 ```typescript 92 import image from '@ohos.multimedia.image';//自@ohos.multimedia.image引入image,提供图片处理效果 93 ... 94 private receiver: image.ImageReceiver = undefined;//图像接收类,用于获取组件surface id,接收最新的图片和读取下一张图片 95 ... 96 constructor() { 97 this.mediaModel = MediaModel.getMediaInstance();//通过调用model/MediaModel.ets中的方法创建mediaInstance类mediaModel 98 //创建ImageReceiver实例receiver 99 this.receiver = image.createImageReceiver( 100 cameraWH.width, 101 cameraWH.height, 102 FOUR, 103 EIGHT 104 ); 105 //接收图片时注册回调 106 this.receiver.on('imageArrival', () => { 107 //从ImageReceiver读取下一张图片 108 this.receiver.readNextImage((err, image) => { 109 if (err || image === undefined) { 110 return; 111 } 112 //根据图像的组件类型从图像中获取组件缓存 113 image.getComponent(FOUR, (errMsg, img) => { 114 if (errMsg || img === undefined) { 115 return; 116 } 117 let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE); 118 if (img.byteBuffer) { 119 buffer = img.byteBuffer; 120 } 121 this.saveImage(buffer, image); 122 }); 123 }); 124 }); 125 } 126 127 128 async initCamera(surfaceId: string): Promise<void> { 129 ... 130 try { 131 this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);//获取相机管理器实例 132 } 133 this.camerasArray = this.cameraMgr.getSupportedCameras();//获取支持指定的相机设备对象 134 if (this.camerasArray.length === 0) { 135 return; 136 } 137 let mCamera = this.camerasArray[0]; 138 this.cameraInput = this.cameraMgr.createCameraInput(mCamera); 139 this.cameraInput.open(); 140 this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);//查询相机设备支持的输出能力 141 let previewProfile = this.capability.previewProfiles[0]; 142 //通过相机管理器创建预览输出对象 143 this.previewOutput = this.cameraMgr.createPreviewOutput( 144 previewProfile, 145 surfaceId //surfaceId从XComponent组件获取 146 ); 147 let rSurfaceId = await this.receiver.getReceivingSurfaceId();//获取一个surface id供其他组件使用 148 let photoProfile = this.capability.photoProfiles[0]; 149 //通过相机管理器创建照片输出对象 150 this.photoOutPut = this.cameraMgr.createPhotoOutput( 151 photoProfile, 152 rSurfaceId //rSurfaceId通过构造函数中定义的图像接收类receiver获取 153 ); 154 this.capSession = this.cameraMgr.createCaptureSession();//创建CaptureSession实例 155 this.capSession.beginConfig();//开始配置会话 156 this.capSession.addInput(this.cameraInput);//将cameraInput加入会话 157 this.capSession.addOutput(this.previewOutput);//将预览输出加入会话 158 this.capSession.addOutput(this.photoOutPut);//将照片输出加入会话 159 await this.capSession.commitConfig();//提交配置信息 160 await this.capSession.start();//开始输出 161 } 162 163 ``` 164 1654. 点击按钮进行拍照 166 167 拍照按钮通过Image组件呈现,其绑定的onClick方法调用takePicture方法开始拍照。 168 169 ```typescript 170 Image(this.getCameraIcon()) 171 .size({ width: 64, height: 64 }) 172 .margin({ left: 10 }) 173 .id('camera') 174 .onClick(() => { 175 if (this.currentModel === CameraMode.modePhoto) { 176 prompt.showToast({ message: '拍照中...', duration: 200 }); 177 this.cameraModel.takePicture();//调用model/cameraModel.takePicture()开始拍照 178 } 179 }) 180 ``` 181 1825. 拍照功能具体实现 183 184 - 拍照 185 186 ```typescript 187 async takePicture(): Promise<void> { 188 //设置拍照相关参数 189 let photoSettings = { 190 rotation: this.imageRotation, 191 quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM, 192 location: { 193 // 位置信息,经纬度 194 latitude: 12.9698, 195 longitude: 77.75, 196 altitude: 1000, 197 }, 198 mirror: false, 199 }; 200 await this.photoOutPut.capture(photoSettings); 201 AppStorage.Set('isRefresh', true); 202 } 203 ``` 204 205 - 保存图片 206 207 saveImage方法使用MediaModel中的createAndGetUri方法创建Image类型资源,将拍摄到的照片写入到这个资源中去。 208 209 ```typescript 210 //model/MediaModel.ts中定义的负责保存图片的相关方法 211 async createAndGetUri(mediaType: mediaLibrary.MediaType): Promise<mediaLibrary.FileAsset> { 212 let dateTimeUtil: DateTimeUtil = new DateTimeUtil(); 213 let info: FileInfo = this.getInfoFromMediaType(mediaType); 214 let name: string = `${dateTimeUtil.getDate()}_${dateTimeUtil.getTime()}`;//获取当前时间 215 let displayName: string = `${info.prefix}${name}${info.suffix}`; 216 //获取公共目录路径。 217 let publicPath: string = await this.mediaLibraryTest.getPublicDirectory( 218 info.directory 219 );//通过引用自@ohos.multimedia.mediaLibrary的mediaLibraryTest类创建媒体资源,其中定义了媒体类型、名称、路径。 220 let fileAsset: mediaLibrary.FileAsset = await this.mediaLibraryTest.createAsset( 221 mediaType,//根据传入函数createAndGetUri的mediaType参数决定创建什么类型的媒体资源 222 displayName, 223 publicPath 224 ); 225 return fileAsset; 226 } 227 async getFdPath(fileAsset: mediaLibrary.FileAsset): Promise<number> { 228 let fd: number = await fileAsset.open('Rw');//打开当前文件 229 return fd; 230 } 231 ... 232 233 async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> { 234 this.fileAsset = await this.mediaModel.createAndGetUri(mediaLibrary.MediaType.IMAGE); 235 //通过调用MediaModel中的方法创建Image类型资源 236 this.photoPath = this.fileAsset.uri; 237 this.fd = await this.mediaModel.getFdPath(this.fileAsset); 238 await fileIo.write(this.fd, buffer);//将拍摄的照片写入到Mediamodel传回的资源中去 239 await this.fileAsset.close(this.fd);//释放open函数 240 await img.release(); 241 if (this.takePictureHandle) { 242 this.takePictureHandle(this.photoPath); 243 } 244 } 245 ``` 246 247## 全部代码 248 249本例完整代码sample示例链接:[相机](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Media/Camera) 250 251## 参考 252 253- [权限列表](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/security/AccessToken/permissions-for-all.md#ohospermissiondistributed_datasync) 254- [@ohos.multimedia.camera](../application-dev/reference/apis-camera-kit/js-apis-camera.md) 255 256 257 258