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
36model.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