1# Deferred Photo Delivery Sample (ArkTS)
2
3This topic provides sample code that covers the complete deferred photo delivery process to help you understand the complete API calling sequence.
4
5Before referring to the sample code, you are advised to read [Deferred Photo Delivery (ArkTS)](camera-deferred-capture.md), [Device Input Management](camera-device-input.md), [Camera Session Management](camera-session-management.md), and [Camera Photographing](camera-shooting.md).
6
7## Development Process
8
9After obtaining the output stream capabilities supported by the camera, create a photo stream. The development process is as follows:
10
11![deferred-capture-development-process](figures/deferred-capture-development-process.png)
12
13## Sample Code
14
15For details about how to obtain the context, see [Obtaining the Context of UIAbility](../../application-models/uiability-usage.md#obtaining-the-context-of-uiability).
16
17```ts
18import { camera } from '@kit.CameraKit';
19import { BusinessError } from '@kit.BasicServicesKit';
20import { common } from '@kit.AbilityKit';
21import { photoAccessHelper } from '@kit.MediaLibraryKit';
22
23let context = getContext(this);
24let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
25
26class MediaDataHandler implements photoAccessHelper.MediaAssetDataHandler<ArrayBuffer> {
27  onDataPrepared(data: ArrayBuffer) {
28    if (data === undefined) {
29      console.error('Error occurred when preparing data');
30      return;
31    }
32    console.info('on image data prepared');
33  }
34}
35
36async function mediaLibRequestBuffer(photoAsset: photoAccessHelper.PhotoAsset) {
37  let requestOptions: photoAccessHelper.RequestOptions = {
38    deliveryMode: photoAccessHelper.DeliveryMode.HIGH_QUALITY_MODE,
39  }
40  const handler = new MediaDataHandler();
41  await photoAccessHelper.MediaAssetManager.requestImageData(context, photoAsset, requestOptions, handler);
42  console.info('requestImageData successfully');
43}
44
45async function mediaLibSavePhoto(photoAsset: photoAccessHelper.PhotoAsset): Promise<void> {
46  try {
47    let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest = new photoAccessHelper.MediaAssetChangeRequest(photoAsset);
48    assetChangeRequest.saveCameraPhoto();
49    await phAccessHelper.applyChanges(assetChangeRequest);
50    console.info('apply saveCameraPhoto successfully');
51  } catch (err) {
52    console.error(`apply saveCameraPhoto failed with error: ${err.code}, ${err.message}`);
53  }
54}
55
56function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void {
57  // After the callback is set, call capture() of photoOutput to trigger the callback upon the receiving of a low-quality image.
58  photoOutput.on('photoAssetAvailable', (err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset): void => {
59    console.info('getPhotoAsset start');
60    console.info(`err: ${JSON.stringify(err)}`);
61    if ((err !== undefined && err.code !== 0) || photoAsset === undefined) {
62      console.error('getPhotoAsset failed');
63      return;
64    }
65    // Call the mediaLibrary flush API to save the low-quality image in the first phase. After the real image in the second phase is ready, the mediaLibrary proactively replaces the image flushed.
66    mediaLibSavePhoto(photoAsset);
67    // Call the mediaLibrary API to register the buffer callback to receive low-quality or high-quality images for custom processing.
68    mediaLibRequestBuffer(photoAsset);
69  });
70}
71
72async function deferredCaptureCase(baseContext: common.BaseContext, surfaceId: string): Promise<void> {
73  // Create a CameraManager object.
74  let cameraManager: camera.CameraManager = camera.getCameraManager(baseContext);
75  if (!cameraManager) {
76    console.error("camera.getCameraManager error");
77    return;
78  }
79  // Listen for camera status changes.
80  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
81    if (err !== undefined && err.code !== 0) {
82      console.error('cameraStatus with errorCode = ' + err.code);
83      return;
84    }
85    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
86    console.info(`status: ${cameraStatusInfo.status}`);
87  });
88
89  // Obtain the camera list.
90  let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
91  if (cameraArray.length <= 0) {
92    console.error("cameraManager.getSupportedCameras error");
93    return;
94  }
95
96  for (let index = 0; index < cameraArray.length; index++) {
97    console.info('cameraId : ' + cameraArray[index].cameraId);                          // Obtain the camera ID.
98    console.info('cameraPosition : ' + cameraArray[index].cameraPosition);              // Obtain the camera position.
99    console.info('cameraType : ' + cameraArray[index].cameraType);                      // Obtain the camera type.
100    console.info('connectionType : ' + cameraArray[index].connectionType);              // Obtain the camera connection type.
101  }
102
103  // Create a camera input stream.
104  let cameraInput: camera.CameraInput | undefined = undefined;
105  try {
106    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
107  } catch (error) {
108    let err = error as BusinessError;
109    console.error('Failed to createCameraInput errorCode = ' + err.code);
110  }
111  if (cameraInput === undefined) {
112    return;
113  }
114
115  // Listen for camera input errors.
116  let cameraDevice: camera.CameraDevice = cameraArray[0];
117  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
118    console.error(`Camera input error code: ${error.code}`);
119  })
120
121  // Open a camera.
122  await cameraInput.open();
123
124  // Obtain the supported modes.
125  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
126  let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
127  if (!isSupportPhotoMode) {
128    console.error('photo mode not support');
129    return;
130  }
131  // Obtain the output streams supported by the camera.
132  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_PHOTO);
133  if (!cameraOutputCap) {
134    console.error("cameraManager.getSupportedOutputCapability error");
135    return;
136  }
137  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
138
139  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
140  if (!previewProfilesArray) {
141    console.error("createOutput previewProfilesArray == null || undefined");
142  }
143
144  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
145  if (!photoProfilesArray) {
146    console.error("createOutput photoProfilesArray == null || undefined");
147  }
148
149  // Create a preview output stream. For details about the surfaceId parameter, see the XComponent. The preview stream uses the surface provided by the XComponent.
150  let previewOutput: camera.PreviewOutput | undefined = undefined;
151  try {
152    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
153  } catch (error) {
154    let err = error as BusinessError;
155    console.error(`Failed to create the PreviewOutput instance. error code: ${err.code}`);
156  }
157  if (previewOutput === undefined) {
158    return;
159  }
160  // Listen for preview output errors.
161  previewOutput.on('error', (error: BusinessError) => {
162    console.error(`Preview output error code: ${error.code}`);
163  });
164
165  // Create a photo output stream.
166  let photoOutput: camera.PhotoOutput | undefined = undefined;
167  try {
168    photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
169  } catch (error) {
170    let err = error as BusinessError;
171    console.error('Failed to createPhotoOutput errorCode = ' + err.code);
172  }
173  if (photoOutput === undefined) {
174    return;
175  }
176
177  // Register the photoAssetAvailable callback.
178  setPhotoOutputCb(photoOutput);
179
180  // Create a session.
181  let photoSession: camera.PhotoSession | undefined = undefined;
182  try {
183    photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
184  } catch (error) {
185    let err = error as BusinessError;
186    console.error('Failed to create the session instance. errorCode = ' + err.code);
187  }
188  if (photoSession === undefined) {
189    return;
190  }
191  // Listen for session errors.
192  photoSession.on('error', (error: BusinessError) => {
193    console.error(`Capture session error code: ${error.code}`);
194  });
195
196  // Start configuration for the session.
197  try {
198    photoSession.beginConfig();
199  } catch (error) {
200    let err = error as BusinessError;
201    console.error('Failed to beginConfig. errorCode = ' + err.code);
202  }
203
204  // Add the camera input stream to the session.
205  try {
206    photoSession.addInput(cameraInput);
207  } catch (error) {
208    let err = error as BusinessError;
209    console.error('Failed to addInput. errorCode = ' + err.code);
210  }
211
212  // Add the preview output stream to the session.
213  try {
214    photoSession.addOutput(previewOutput);
215  } catch (error) {
216    let err = error as BusinessError;
217    console.error('Failed to addOutput(previewOutput). errorCode = ' + err.code);
218  }
219
220  // Add the photo output stream to the session.
221  try {
222    photoSession.addOutput(photoOutput);
223  } catch (error) {
224    let err = error as BusinessError;
225    console.error('Failed to addOutput(photoOutput). errorCode = ' + err.code);
226  }
227
228  // Commit the session configuration.
229  await photoSession.commitConfig();
230
231  // Start the session.
232  await photoSession.start().then(() => {
233    console.info('Promise returned to indicate the session start success.');
234  });
235  // Check whether the camera has flash.
236  let flashStatus: boolean = false;
237  try {
238    flashStatus = photoSession.hasFlash();
239  } catch (error) {
240    let err = error as BusinessError;
241    console.error('Failed to hasFlash. errorCode = ' + err.code);
242  }
243  console.info('Returned with the flash light support status:' + flashStatus);
244
245  if (flashStatus) {
246    // Check whether the auto flash mode is supported.
247    let flashModeStatus: boolean = false;
248    try {
249      let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO);
250      flashModeStatus = status;
251    } catch (error) {
252      let err = error as BusinessError;
253      console.error('Failed to check whether the flash mode is supported. errorCode = ' + err.code);
254    }
255    if(flashModeStatus) {
256      // Set the flash mode to auto.
257      try {
258        photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);
259      } catch (error) {
260        let err = error as BusinessError;
261        console.error('Failed to set the flash mode. errorCode = ' + err.code);
262      }
263    }
264  }
265
266  // Check whether the continuous auto focus is supported.
267  let focusModeStatus: boolean = false;
268  try {
269    let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
270    focusModeStatus = status;
271  } catch (error) {
272    let err = error as BusinessError;
273    console.error('Failed to check whether the focus mode is supported. errorCode = ' + err.code);
274  }
275
276  if (focusModeStatus) {
277    // Set the focus mode to continuous auto focus.
278    try {
279      photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
280    } catch (error) {
281      let err = error as BusinessError;
282      console.error('Failed to set the focus mode. errorCode = ' + err.code);
283    }
284  }
285
286  // Obtain the zoom ratio range supported by the camera.
287  let zoomRatioRange: Array<number> = [];
288  try {
289    zoomRatioRange = photoSession.getZoomRatioRange();
290  } catch (error) {
291    let err = error as BusinessError;
292    console.error('Failed to get the zoom ratio range. errorCode = ' + err.code);
293  }
294  if (zoomRatioRange.length <= 0) {
295    return;
296  }
297  // Set a zoom ratio.
298  try {
299    photoSession.setZoomRatio(zoomRatioRange[0]);
300  } catch (error) {
301    let err = error as BusinessError;
302    console.error('Failed to set the zoom ratio value. errorCode = ' + err.code);
303  }
304  let photoCaptureSetting: camera.PhotoCaptureSetting = {
305    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // Set the photo quality to high.
306    rotation: camera.ImageRotation.ROTATION_0 // Set the rotation angle of the photo to 0.
307  }
308  // Use the current photographing settings to take photos.
309  photoOutput.capture(photoCaptureSetting, (err: BusinessError) => {
310    if (err) {
311      console.error(`Failed to capture the photo ${err.message}`);
312      return;
313    }
314    console.info('Callback invoked to indicate the photo capture request success.');
315  });
316  // Stop the session.
317  photoSession.stop();
318
319  // Release the camera input stream.
320  cameraInput.close();
321
322  // Release the preview output stream.
323  previewOutput.release();
324
325  // Release the photo output stream.
326  photoOutput.release();
327
328  // Release the session.
329  photoSession.release();
330
331  // Set the session to null.
332  photoSession = undefined;
333}
334```
335