• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Deferred Photo Delivery Sample (ArkTS)
2Before developing a camera application, request permissions by following the instructions provided in [Camera Development Preparations](camera-preparation.md).
3
4This topic provides sample code that covers the complete deferred photo delivery process to help you understand the complete API calling sequence.
5
6Before 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 [Photo Capture](camera-shooting.md).
7
8## Development Process
9
10After obtaining the output stream capabilities supported by the camera, create a photo stream. The development process is as follows:
11
12![deferred-capture-development-process](figures/deferred-capture-development-process.png)
13
14## Sample Code
15
16For details about how to obtain the context, see [Obtaining the Context of UIAbility](../../application-models/uiability-usage.md#obtaining-the-context-of-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
27let photoSession: camera.PhotoSession | undefined = undefined;
28let cameraInput: camera.CameraInput | undefined = undefined;
29let previewOutput: camera.PreviewOutput | undefined = undefined;
30let photoOutput: camera.PhotoOutput | undefined = undefined;
31
32class MediaDataHandler implements photoAccessHelper.MediaAssetDataHandler<ArrayBuffer> {
33  onDataPrepared(data: ArrayBuffer) {
34    if (data === undefined) {
35      console.error('Error occurred when preparing data');
36      return;
37    }
38    console.info('on image data prepared');
39    // Release the session after the photo buffer is obtained. Releasing it beforehand can lead to photo capture failures.
40    releaseCamSession();
41  }
42}
43
44async function mediaLibRequestBuffer(photoAsset: photoAccessHelper.PhotoAsset) {
45  let requestOptions: photoAccessHelper.RequestOptions = {
46    deliveryMode: photoAccessHelper.DeliveryMode.FAST_MODE,
47  }
48  const handler = new MediaDataHandler();
49  await photoAccessHelper.MediaAssetManager.requestImageData(context, photoAsset, requestOptions, handler);
50  console.info('requestImageData successfully');
51}
52
53async function mediaLibSavePhoto(photoAsset: photoAccessHelper.PhotoAsset): Promise<void> {
54  try {
55    let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
56      new photoAccessHelper.MediaAssetChangeRequest(photoAsset);
57    assetChangeRequest.saveCameraPhoto();
58    await phAccessHelper.applyChanges(assetChangeRequest);
59    console.info('apply saveCameraPhoto successfully');
60  } catch (err) {
61    console.error(`apply saveCameraPhoto failed with error: ${err.code}, ${err.message}`);
62  }
63}
64
65function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void {
66  // After the callback is set, call capture() of photoOutput to trigger the callback upon the receiving of a low-quality image.
67  photoOutput.on('photoAssetAvailable', (err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset): void => {
68    console.info('getPhotoAsset start');
69    console.info(`err: ${JSON.stringify(err)}`);
70    if ((err !== undefined && err.code !== 0) || photoAsset === undefined) {
71      console.error('getPhotoAsset failed');
72      return;
73    }
74    // 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.
75    mediaLibSavePhoto(photoAsset);
76    // Call the mediaLibrary API to register the buffer callback to receive low-quality or high-quality images for custom processing.
77    mediaLibRequestBuffer(photoAsset);
78  });
79}
80
81async function deferredCaptureCase(baseContext: common.BaseContext, surfaceId: string): Promise<void> {
82  // Create a CameraManager object.
83  let cameraManager: camera.CameraManager = camera.getCameraManager(baseContext);
84  if (!cameraManager) {
85    console.error('camera.getCameraManager error');
86    return;
87  }
88  // Listen for camera status changes.
89  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
90    if (err !== undefined && err.code !== 0) {
91      console.error('cameraStatus with errorCode = ' + err.code);
92      return;
93    }
94    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
95    console.info(`status: ${cameraStatusInfo.status}`);
96  });
97
98  // Obtain the camera list.
99  let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
100  if (cameraArray.length <= 0) {
101    console.error('cameraManager.getSupportedCameras error');
102    return;
103  }
104
105  for (let index = 0; index < cameraArray.length; index++) {
106    console.info('cameraId : ' + cameraArray[index].cameraId); // Obtain the camera ID.
107    console.info('cameraPosition : ' + cameraArray[index].cameraPosition); // Obtain the camera position.
108    console.info('cameraType : ' + cameraArray[index].cameraType); // Obtain the camera type.
109    console.info('connectionType : ' + cameraArray[index].connectionType); // Obtain the camera connection type.
110  }
111
112  // Create a camera input stream.
113  try {
114    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
115  } catch (error) {
116    let err = error as BusinessError;
117    console.error('Failed to createCameraInput errorCode = ' + err.code);
118  }
119  if (cameraInput === undefined) {
120    return;
121  }
122
123  // Listen for camera input errors.
124  let cameraDevice: camera.CameraDevice = cameraArray[0];
125  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
126    console.error(`Camera input error code: ${error.code}`);
127  })
128
129  // Open the camera.
130  await cameraInput.open();
131
132  // Obtain the supported modes.
133  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
134  let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
135  if (!isSupportPhotoMode) {
136    console.error('photo mode not support');
137    return;
138  }
139  // Obtain the output stream capability supported by the camera.
140  let cameraOutputCap: camera.CameraOutputCapability =
141    cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_PHOTO);
142  if (!cameraOutputCap) {
143    console.error('cameraManager.getSupportedOutputCapability error');
144    return;
145  }
146  console.info('outputCapability: ' + JSON.stringify(cameraOutputCap));
147
148  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
149  if (!previewProfilesArray) {
150    console.error('createOutput previewProfilesArray == null || undefined');
151  }
152
153  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
154  if (!photoProfilesArray) {
155    console.error('createOutput photoProfilesArray == null || undefined');
156  }
157
158  // Create a preview output stream. For details about the surfaceId parameter, see the XComponent. The preview stream uses the surface provided by the XComponent.
159  try {
160    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
161  } catch (error) {
162    let err = error as BusinessError;
163    console.error(`Failed to create the PreviewOutput instance. error code: ${err.code}`);
164  }
165  if (previewOutput === undefined) {
166    return;
167  }
168
169  // Listen for preview output errors.
170  previewOutput.on('error', (error: BusinessError) => {
171    console.error(`Preview output error code: ${error.code}`);
172  });
173
174  // Create a photo output stream.
175  try {
176    photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
177  } catch (error) {
178    let err = error as BusinessError;
179    console.error('Failed to createPhotoOutput errorCode = ' + err.code);
180  }
181  if (photoOutput === undefined) {
182    return;
183  }
184
185  // Register the photoAssetAvailable callback.
186  setPhotoOutputCb(photoOutput);
187
188  // Create a session.
189  try {
190    photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
191  } catch (error) {
192    let err = error as BusinessError;
193    console.error('Failed to create the session instance. errorCode = ' + err.code);
194  }
195  if (photoSession === undefined) {
196    return;
197  }
198  // Listen for session errors.
199  photoSession.on('error', (error: BusinessError) => {
200    console.error(`Capture session error code: ${error.code}`);
201  });
202
203  // Start configuration for the session.
204  try {
205    photoSession.beginConfig();
206  } catch (error) {
207    let err = error as BusinessError;
208    console.error('Failed to beginConfig. errorCode = ' + err.code);
209  }
210
211  // Add the camera input stream to the session.
212  try {
213    photoSession.addInput(cameraInput);
214  } catch (error) {
215    let err = error as BusinessError;
216    console.error('Failed to addInput. errorCode = ' + err.code);
217  }
218
219  // Add the preview output stream to the session.
220  try {
221    photoSession.addOutput(previewOutput);
222  } catch (error) {
223    let err = error as BusinessError;
224    console.error('Failed to addOutput(previewOutput). errorCode = ' + err.code);
225  }
226
227  // Add the photo output stream to the session.
228  try {
229    photoSession.addOutput(photoOutput);
230  } catch (error) {
231    let err = error as BusinessError;
232    console.error('Failed to addOutput(photoOutput). errorCode = ' + err.code);
233  }
234
235  // Commit the session configuration.
236  await photoSession.commitConfig();
237
238  // Start the session.
239  await photoSession.start().then(() => {
240    console.info('Promise returned to indicate the session start success.');
241  });
242  // Check whether the camera has flash.
243  let flashStatus: boolean = false;
244  try {
245    flashStatus = photoSession.hasFlash();
246  } catch (error) {
247    let err = error as BusinessError;
248    console.error('Failed to hasFlash. errorCode = ' + err.code);
249  }
250  console.info('Returned with the flash light support status:' + flashStatus);
251
252  if (flashStatus) {
253    // Check whether the auto flash mode is supported.
254    let flashModeStatus: boolean = false;
255    try {
256      let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO);
257      flashModeStatus = status;
258    } catch (error) {
259      let err = error as BusinessError;
260      console.error('Failed to check whether the flash mode is supported. errorCode = ' + err.code);
261    }
262    if (flashModeStatus) {
263      // Set the flash mode to auto.
264      try {
265        photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);
266      } catch (error) {
267        let err = error as BusinessError;
268        console.error('Failed to set the flash mode. errorCode = ' + err.code);
269      }
270    }
271  }
272
273  // Check whether the continuous auto focus is supported.
274  let focusModeStatus: boolean = false;
275  try {
276    let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
277    focusModeStatus = status;
278  } catch (error) {
279    let err = error as BusinessError;
280    console.error('Failed to check whether the focus mode is supported. errorCode = ' + err.code);
281  }
282
283  if (focusModeStatus) {
284    // Set the focus mode to continuous auto focus.
285    try {
286      photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
287    } catch (error) {
288      let err = error as BusinessError;
289      console.error('Failed to set the focus mode. errorCode = ' + err.code);
290    }
291  }
292
293  // Obtain the zoom ratio range supported by the camera.
294  let zoomRatioRange: Array<number> = [];
295  try {
296    zoomRatioRange = photoSession.getZoomRatioRange();
297  } catch (error) {
298    let err = error as BusinessError;
299    console.error('Failed to get the zoom ratio range. errorCode = ' + err.code);
300  }
301  if (zoomRatioRange.length <= 0) {
302    return;
303  }
304
305  // Set a zoom ratio.
306  try {
307    photoSession.setZoomRatio(zoomRatioRange[0]);
308  } catch (error) {
309    let err = error as BusinessError;
310    console.error('Failed to set the zoom ratio value. errorCode = ' + err.code);
311  }
312  let photoCaptureSetting: camera.PhotoCaptureSetting = {
313    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // Set the photo quality to high.
314    rotation: camera.ImageRotation.ROTATION_0 // Set the rotation angle of the photo to 0.
315  }
316
317  // Use the current photo capture settings to take a photo.
318  photoOutput.capture(photoCaptureSetting, (err: BusinessError) => {
319    if (err) {
320      console.error(`Failed to capture the photo ${err.message}`);
321      return;
322    }
323    console.info('Callback invoked to indicate the photo capture request success.');
324  });
325}
326
327async function releaseCamSession() {
328  // Stop the session.
329  await photoSession?.stop();
330
331  // Release the camera input stream.
332  await cameraInput?.close();
333
334  // Release the preview output stream.
335  await previewOutput?.release();
336
337  // Release the photo output stream.
338  await photoOutput?.release();
339
340  // Release the session.
341  await photoSession?.release();
342
343  // Set the session to null.
344  photoSession = undefined;
345}
346
347@Entry
348@Component
349struct Index {
350  @State message: string = 'PhotoAssetDemo';
351  private mXComponentController: XComponentController = new XComponentController();
352  private surfaceId = '';
353
354  build() {
355    Column() {
356      Column() {
357        XComponent({
358          id: 'componentId',
359          type: XComponentType.SURFACE,
360          controller: this.mXComponentController
361        })
362          .onLoad(async () => {
363            console.info('onLoad is called');
364            this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
365            console.info(`onLoad surfaceId: ${this.surfaceId}`);
366            deferredCaptureCase(context, this.surfaceId);
367          })// The width and height of the surface are opposite to those of the XComponent.
368          .renderFit(RenderFit.RESIZE_CONTAIN)
369      }.height('95%')
370      .justifyContent(FlexAlign.Center)
371
372      Text(this.message)
373        .id('PhotoAssetDemo')
374        .fontSize(38)
375        .fontWeight(FontWeight.Bold)
376        .alignRules({
377          center: { anchor: '__container__', align: VerticalAlign.Center },
378          middle: { anchor: '__container__', align: HorizontalAlign.Center }
379        })
380    }
381    .height('100%')
382    .width('100%')
383  }
384}
385```
386