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