• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 拍照实现方案(ArkTS)
2
3在开发相机应用时,需要先申请相机相关权限[开发准备](camera-preparation.md)。
4当前示例提供完整的拍照流程介绍,方便开发者了解完整的接口调用顺序。
5
6在参考以下示例前,建议开发者查看[相机开发指导(ArkTS)](camera-preparation.md)的具体章节,了解[设备输入](camera-device-input.md)、[会话管理](camera-session-management.md)、[拍照](camera-shooting.md)等单个流程。
7
8## 开发流程
9
10在获取到相机支持的输出流能力后,开始创建拍照流,开发流程如下。
11
12![Photographing Development Process](figures/photographing-development-process.png)
13
14## 完整示例
15
16Context获取方式请参考:[获取UIAbility的上下文信息](../../application-models/uiability-usage.md#获取uiability的上下文信息)。
17
18如需要在图库中看到所保存的图片、视频资源,需要将其保存到媒体库,保存方式请参考:[保存媒体库资源](../medialibrary/photoAccessHelper-savebutton.md)。
19
20需要在[photoOutput.on('photoAvailable')](../../reference/apis-camera-kit/js-apis-camera.md#onphotoavailable11)接口获取到buffer时,将buffer在安全控件中保存到媒体库。
21```ts
22import { camera } from '@kit.CameraKit';
23import { image } from '@kit.ImageKit';
24import { BusinessError } from '@kit.BasicServicesKit';
25import { common } from '@kit.AbilityKit';
26import { fileIo as fs } from '@kit.CoreFileKit';
27import { photoAccessHelper } from '@kit.MediaLibraryKit';
28
29let context = getContext(this);
30
31function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void {
32  //设置回调之后,调用photoOutput的capture方法,就会将拍照的buffer回传到回调中
33  photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => {
34    console.info('getPhoto start');
35    console.info(`err: ${JSON.stringify(errCode)}`);
36    if (errCode || photo === undefined) {
37      console.error('getPhoto failed');
38      return;
39    }
40    let imageObj = photo.main;
41    imageObj.getComponent(image.ComponentType.JPEG, (errCode: BusinessError, component: image.Component): void => {
42      console.info('getComponent start');
43      if (errCode || component === undefined) {
44        console.error('getComponent failed');
45        return;
46      }
47      let buffer: ArrayBuffer;
48      if (component.byteBuffer) {
49        buffer = component.byteBuffer;
50      } else {
51        console.error('byteBuffer is null');
52        return;
53      }
54
55      // 如需要在图库中看到所保存的图片、视频资源,请使用用户无感的安全控件创建媒体资源。
56
57      // buffer处理结束后需要释放该资源,如果未正确释放资源会导致后续拍照获取不到buffer
58      imageObj.release();
59    });
60  });
61}
62
63async function cameraShootingCase(baseContext: common.BaseContext, surfaceId: string): Promise<void> {
64  // 创建CameraManager对象
65  let cameraManager: camera.CameraManager = camera.getCameraManager(baseContext);
66  if (!cameraManager) {
67    console.error("camera.getCameraManager error");
68    return;
69  }
70  // 监听相机状态变化
71  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
72    if (err !== undefined && err.code !== 0) {
73      console.error('cameraStatus with errorCode = ' + err.code);
74      return;
75    }
76    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
77    console.info(`status: ${cameraStatusInfo.status}`);
78  });
79
80  // 获取相机列表
81  let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
82  if (cameraArray.length <= 0) {
83    console.error("cameraManager.getSupportedCameras error");
84    return;
85  }
86
87  for (let index = 0; index < cameraArray.length; index++) {
88    console.info('cameraId : ' + cameraArray[index].cameraId);                          // 获取相机ID
89    console.info('cameraPosition : ' + cameraArray[index].cameraPosition);              // 获取相机位置
90    console.info('cameraType : ' + cameraArray[index].cameraType);                      // 获取相机类型
91    console.info('connectionType : ' + cameraArray[index].connectionType);              // 获取相机连接类型
92  }
93
94  // 创建相机输入流
95  let cameraInput: camera.CameraInput | undefined = undefined;
96  try {
97    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
98  } catch (error) {
99    let err = error as BusinessError;
100    console.error('Failed to createCameraInput errorCode = ' + err.code);
101  }
102  if (cameraInput === undefined) {
103    return;
104  }
105
106  // 监听cameraInput错误信息
107  let cameraDevice: camera.CameraDevice = cameraArray[0];
108  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
109    console.error(`Camera input error code: ${error.code}`);
110  })
111
112  // 打开相机
113  await cameraInput.open();
114
115  // 获取支持的模式类型
116  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
117  let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
118  if (!isSupportPhotoMode) {
119    console.error('photo mode not support');
120    return;
121  }
122  // 获取相机设备支持的输出流能力
123  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_PHOTO);
124  if (!cameraOutputCap) {
125    console.error("cameraManager.getSupportedOutputCapability error");
126    return;
127  }
128  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
129
130  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
131  if (!previewProfilesArray) {
132    console.error("createOutput previewProfilesArray == null || undefined");
133  }
134
135  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
136  if (!photoProfilesArray) {
137    console.error("createOutput photoProfilesArray == null || undefined");
138  }
139
140  // 创建预览输出流,其中参数 surfaceId 参考上文 XComponent 组件,预览流为XComponent组件提供的surface
141  let previewOutput: camera.PreviewOutput | undefined = undefined;
142  try {
143    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
144  } catch (error) {
145    let err = error as BusinessError;
146    console.error(`Failed to create the PreviewOutput instance. error code: ${err.code}`);
147  }
148  if (previewOutput === undefined) {
149    return;
150  }
151  // 监听预览输出错误信息
152  previewOutput.on('error', (error: BusinessError) => {
153    console.error(`Preview output error code: ${error.code}`);
154  });
155
156  // 创建拍照输出流
157  let photoOutput: camera.PhotoOutput | undefined = undefined;
158  try {
159    photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
160  } catch (error) {
161    let err = error as BusinessError;
162    console.error('Failed to createPhotoOutput errorCode = ' + err.code);
163  }
164  if (photoOutput === undefined) {
165    return;
166  }
167
168    //调用上面的回调函数来保存图片
169  setPhotoOutputCb(photoOutput);
170
171  //创建会话
172  let photoSession: camera.PhotoSession | undefined = undefined;
173  try {
174    photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
175  } catch (error) {
176    let err = error as BusinessError;
177    console.error('Failed to create the session instance. errorCode = ' + err.code);
178  }
179  if (photoSession === undefined) {
180    return;
181  }
182  // 监听session错误信息
183  photoSession.on('error', (error: BusinessError) => {
184    console.error(`Capture session error code: ${error.code}`);
185  });
186
187  // 开始配置会话
188  try {
189    photoSession.beginConfig();
190  } catch (error) {
191    let err = error as BusinessError;
192    console.error('Failed to beginConfig. errorCode = ' + err.code);
193  }
194
195  // 向会话中添加相机输入流
196  try {
197    photoSession.addInput(cameraInput);
198  } catch (error) {
199    let err = error as BusinessError;
200    console.error('Failed to addInput. errorCode = ' + err.code);
201  }
202
203  // 向会话中添加预览输出流
204  try {
205    photoSession.addOutput(previewOutput);
206  } catch (error) {
207    let err = error as BusinessError;
208    console.error('Failed to addOutput(previewOutput). errorCode = ' + err.code);
209  }
210
211  // 向会话中添加拍照输出流
212  try {
213    photoSession.addOutput(photoOutput);
214  } catch (error) {
215    let err = error as BusinessError;
216    console.error('Failed to addOutput(photoOutput). errorCode = ' + err.code);
217  }
218
219  // 提交会话配置
220  await photoSession.commitConfig();
221
222  // 启动会话
223  await photoSession.start().then(() => {
224    console.info('Promise returned to indicate the session start success.');
225  });
226  // 判断设备是否支持闪光灯
227  let flashStatus: boolean = false;
228  try {
229    flashStatus = photoSession.hasFlash();
230  } catch (error) {
231    let err = error as BusinessError;
232    console.error('Failed to hasFlash. errorCode = ' + err.code);
233  }
234  console.info('Returned with the flash light support status:' + flashStatus);
235
236  if (flashStatus) {
237    // 判断是否支持自动闪光灯模式
238    let flashModeStatus: boolean = false;
239    try {
240      let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO);
241      flashModeStatus = status;
242    } catch (error) {
243      let err = error as BusinessError;
244      console.error('Failed to check whether the flash mode is supported. errorCode = ' + err.code);
245    }
246    if(flashModeStatus) {
247      // 设置自动闪光灯模式
248      try {
249        photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);
250      } catch (error) {
251        let err = error as BusinessError;
252        console.error('Failed to set the flash mode. errorCode = ' + err.code);
253      }
254    }
255  }
256
257  // 判断是否支持连续自动变焦模式
258  let focusModeStatus: boolean = false;
259  try {
260    let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
261    focusModeStatus = status;
262  } catch (error) {
263    let err = error as BusinessError;
264    console.error('Failed to check whether the focus mode is supported. errorCode = ' + err.code);
265  }
266
267  if (focusModeStatus) {
268    // 设置连续自动变焦模式
269    try {
270      photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
271    } catch (error) {
272      let err = error as BusinessError;
273      console.error('Failed to set the focus mode. errorCode = ' + err.code);
274    }
275  }
276
277  // 获取相机支持的可变焦距比范围
278  let zoomRatioRange: Array<number> = [];
279  try {
280    zoomRatioRange = photoSession.getZoomRatioRange();
281  } catch (error) {
282    let err = error as BusinessError;
283    console.error('Failed to get the zoom ratio range. errorCode = ' + err.code);
284  }
285  if (zoomRatioRange.length <= 0) {
286    return;
287  }
288  // 设置可变焦距比
289  try {
290    photoSession.setZoomRatio(zoomRatioRange[0]);
291  } catch (error) {
292    let err = error as BusinessError;
293    console.error('Failed to set the zoom ratio value. errorCode = ' + err.code);
294  }
295  let photoCaptureSetting: camera.PhotoCaptureSetting = {
296    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // 设置图片质量高
297    rotation: camera.ImageRotation.ROTATION_0 // 设置图片旋转角度0
298  }
299  // 使用当前拍照设置进行拍照
300  photoOutput.capture(photoCaptureSetting, (err: BusinessError) => {
301    if (err) {
302      console.error(`Failed to capture the photo ${err.message}`);
303      return;
304    }
305    console.info('Callback invoked to indicate the photo capture request success.');
306  });
307
308  // 需要在拍照结束之后调用以下关闭摄像头和释放会话流程,避免拍照未结束就将会话释放。
309  // 停止当前会话
310  await photoSession.stop();
311
312  // 释放相机输入流
313  await cameraInput.close();
314
315  // 释放预览输出流
316  await previewOutput.release();
317
318  // 释放拍照输出流
319  await photoOutput.release();
320
321  // 释放会话
322  await photoSession.release();
323
324  // 会话置空
325  photoSession = undefined;
326}
327```
328
329