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