• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Practices for High-Performance Photo Capture (for System Applications Only) (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 high-performance photo capture process to help you understand the complete API calling sequence.
11
12Before referring to the sample code, you are advised to read [High-Performance Photo Capture (for System Applications Only) (ArkTS)](camera-deferred-photo.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-photo-development-process](figures/deferred-photo-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 { image } from '@kit.ImageKit';
27import { BusinessError } from '@kit.BasicServicesKit';
28import { fileIo, fileUri } from '@kit.CoreFileKit';
29import { photoAccessHelper } from '@kit.MediaLibraryKit';
30
31// Flush the original image in write-file mode.
32async function savePicture(photoObj: camera.Photo, context: Context, showSaveToMediaLibrary: boolean = true): Promise<void> {
33  let filePath = `${context.filesDir}/${Date.now()}.jpg`;
34  let file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
35  let buffer: ArrayBuffer | undefined = undefined;
36  let component = await photoObj.main.getComponent(image.ComponentType.JPEG);
37  if (component === undefined) {
38    console.error('getComponent failed');
39    return;
40  }
41  if (component.byteBuffer) {
42    buffer = component.byteBuffer;
43  } else {
44    console.error('byteBuffer is null');
45    return;
46  }
47  if (buffer) {
48    fileIo.writeSync(file.fd, buffer);
49    if (showSaveToMediaLibrary) {
50      await saveToMediaLibrary(context, filePath, file.fd);
51    } else {
52      fileIo.closeSync(file.fd);
53    }
54  }
55}
56
57async function saveToMediaLibrary(context: Context, filePath: string, fd: number): Promise<void> {
58  let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
59  try {
60    let res = fileIo.accessSync(filePath);
61    if (res) {
62      await fileIo.unlink(filePath);
63    }
64    // Specify the URI of the image in the application sandbox directory to be saved.
65    let srcFileUri = fileUri.getUriFromPath(filePath); // Obtain the absolute path of the resource.
66    let srcFileUris: Array<string> = [
67      srcFileUri // Pass the absolute path.
68    ];
69    // Set parameters such as the file name extension, image file type, title (optional) and image subtype (optional) of the image to save.
70    let photoCreationConfigs: Array<photoAccessHelper.PhotoCreationConfig> = [
71      {
72        title: 'test', // This parameter is optional.
73        fileNameExtension: 'jpg',
74        photoType: photoAccessHelper.PhotoType.IMAGE,
75        subtype: photoAccessHelper.PhotoSubtype.DEFAULT, // This parameter is optional.
76      }
77    ];
78    // Obtain the target URI in the media library based on pop-up authorization.
79    let desFileUris: Array<string> = await phAccessHelper.showAssetsCreationDialog(srcFileUris, photoCreationConfigs);
80    // Write the image content from the application sandbox directory to the file specified by the target URI in the media library.
81    let desFile: fileIo.File = await fileIo.open(desFileUris[0], fileIo.OpenMode.WRITE_ONLY);
82    await fileIo.copyFile(fd, desFile.fd);
83    fileIo.closeSync(fd);
84    fileIo.closeSync(desFile);
85    console.info('create asset by dialog successfully');
86  } catch (err) {
87    console.error(`failed to create asset by dialog successfully errCode is: ${err.code}, ${err.message}`);
88  }
89}
90
91// Flush the thumbnail by calling the media library API.
92async function saveDeferredPhoto(proxyObj: camera.DeferredPhotoProxy, context: Context): Promise<void> {
93  try {
94    // Create a photoAsset.
95    let accessHelper = photoAccessHelper.getPhotoAccessHelper(context);
96    let testFileName = 'testFile' + Date.now() + '.jpg';
97    let photoAsset = await accessHelper.createAsset(testFileName);
98    // Pass the thumbnail proxy class object to the media library.
99    let mediaRequest: photoAccessHelper.MediaAssetChangeRequest = new photoAccessHelper.MediaAssetChangeRequest(photoAsset);
100    mediaRequest.addResource(photoAccessHelper.ResourceType.PHOTO_PROXY, proxyObj);
101    let res = await accessHelper.applyChanges(mediaRequest);
102    console.info('saveDeferredPhoto success.');
103  } catch (err) {
104    console.error(`Failed to saveDeferredPhoto. error: ${err}`);
105  }
106}
107
108async function deferredPhotoCase(context: Context, surfaceId: string): Promise<void> {
109  // Create a CameraManager object.
110  let cameraManager: camera.CameraManager = camera.getCameraManager(context);
111  if (!cameraManager) {
112    console.error("camera.getCameraManager error");
113    return;
114  }
115  // Listen for camera status changes.
116  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
117    if (err !== undefined && err.code !== 0) {
118      console.error(`cameraStatus with errorCode: ${err.code}`);
119      return;
120    }
121    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
122    console.info(`status: ${cameraStatusInfo.status}`);
123  });
124
125  // Obtain the camera list.
126  let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
127  if (cameraArray.length <= 0) {
128    console.error("cameraManager.getSupportedCameras error");
129    return;
130  }
131
132  for (let index = 0; index < cameraArray.length; index++) {
133    console.info('cameraId : ' + cameraArray[index].cameraId);                          // Obtain the camera ID.
134    console.info('cameraPosition : ' + cameraArray[index].cameraPosition);              // Obtain the camera position.
135    console.info('cameraType : ' + cameraArray[index].cameraType);                      // Obtain the camera type.
136    console.info('connectionType : ' + cameraArray[index].connectionType);              // Obtain the camera connection type.
137  }
138
139  // Create a camera input stream.
140  let cameraInput: camera.CameraInput | undefined = undefined;
141  try {
142    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
143  } catch (error) {
144    let err = error as BusinessError;
145    console.error('Failed to createCameraInput errorCode = ' + err.code);
146  }
147  if (cameraInput === undefined) {
148    return;
149  }
150
151  // Listen for camera input errors.
152  let cameraDevice: camera.CameraDevice = cameraArray[0];
153  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
154    console.error(`Camera input error code: ${error.code}`);
155  })
156
157  // Open the camera.
158  await cameraInput.open();
159
160  // Obtain the supported modes.
161  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
162  let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
163  if (!isSupportPhotoMode) {
164    console.error('photo mode not support');
165    return;
166  }
167  // Obtain the output stream capability supported by the camera.
168  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_PHOTO);
169  if (!cameraOutputCap) {
170    console.error("cameraManager.getSupportedOutputCapability error");
171    return;
172  }
173  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
174
175  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
176  if (!previewProfilesArray) {
177    console.error("createOutput previewProfilesArray == null || undefined");
178  }
179
180  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
181  if (!photoProfilesArray) {
182    console.error("createOutput photoProfilesArray == null || undefined");
183  }
184
185  // Create a preview output stream. For details about the surfaceId parameter, see the XComponent. The preview stream uses the surface provided by the XComponent.
186  let previewOutput: camera.PreviewOutput | undefined = undefined;
187  try {
188    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
189  } catch (error) {
190    let err = error as BusinessError;
191    console.error(`Failed to create the PreviewOutput instance. error code: ${err.code}`);
192  }
193  if (previewOutput === undefined) {
194    return;
195  }
196  // Listen for preview output errors.
197  previewOutput.on('error', (error: BusinessError) => {
198    console.error(`Preview output error code: ${error.code}`);
199  });
200  // Create a photo output stream.
201  let photoOutput: camera.PhotoOutput | undefined = undefined;
202  try {
203    photoOutput = cameraManager.createPhotoOutput(photoProfilesArray[0]);
204  } catch (error) {
205    let err = error as BusinessError;
206    console.error('Failed to createPhotoOutput errorCode = ' + err.code);
207  }
208  if (photoOutput === undefined) {
209    return;
210  }
211  // Create a session.
212  let photoSession: camera.PhotoSession | undefined = undefined;
213  try {
214    photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
215  } catch (error) {
216    let err = error as BusinessError;
217    console.error('Failed to create the photoSession instance. errorCode = ' + err.code);
218  }
219  if (photoSession === undefined) {
220    return;
221  }
222  // Listen for session errors.
223  photoSession.on('error', (error: BusinessError) => {
224    console.error(`Capture session error code: ${error.code}`);
225  });
226
227  // Start configuration for the session.
228  try {
229    photoSession.beginConfig();
230  } catch (error) {
231    let err = error as BusinessError;
232    console.error('Failed to beginConfig. errorCode = ' + err.code);
233  }
234
235  // Add the camera input stream to the session.
236  try {
237    photoSession.addInput(cameraInput);
238  } catch (error) {
239    let err = error as BusinessError;
240    console.error('Failed to addInput. errorCode = ' + err.code);
241  }
242
243  // Add the preview output stream to the session.
244  try {
245    photoSession.addOutput(previewOutput);
246  } catch (error) {
247    let err = error as BusinessError;
248    console.error('Failed to addOutput(previewOutput). errorCode = ' + err.code);
249  }
250
251  // Add the photo output stream to the session.
252  try {
253    photoSession.addOutput(photoOutput);
254  } catch (error) {
255    let err = error as BusinessError;
256    console.error('Failed to addOutput(photoOutput). errorCode = ' + err.code);
257  }
258
259  // Register a callback to listen for original images.
260  photoOutput.on('photoAvailable', (err: BusinessError, photoObj: camera.Photo): void => {
261    if (err) {
262      console.error(`photoAvailable error: ${err}.`);
263      return;
264    }
265    savePicture(photoObj, context).then(() => {
266      // Release the photo object after the flushing is complete.
267      photoObj.release();
268    });
269  });
270
271  // Register a callback to listen for thumbnail proxies.
272  photoOutput.on('deferredPhotoProxyAvailable', (err: BusinessError, proxyObj: camera.DeferredPhotoProxy): void => {
273    if (err) {
274      console.error(`deferredPhotoProxyAvailable error: ${err}.`);
275      return;
276    }
277    console.info('photoOutPutCallBack deferredPhotoProxyAvailable');
278    // Obtain the PixelMap of a thumbnail.
279    proxyObj.getThumbnail().then((thumbnail: image.PixelMap) => {
280      AppStorage.setOrCreate('proxyThumbnail', thumbnail);
281    });
282    // Call the media library API to flush the thumbnail.
283    saveDeferredPhoto(proxyObj, context).then(() => {
284      // Release the thumbnail proxy class object after the flushing is complete.
285      proxyObj.release();
286    });
287  });
288
289  // Check whether deferred photo delivery is supported.
290  let isSupportDeferred: boolean = photoOutput.isDeferredImageDeliverySupported(camera.DeferredDeliveryImageType.PHOTO);
291  console.info('isDeferredImageDeliverySupported res:' + isSupportDeferred);
292  if (isSupportDeferred) {
293    // Enable deferred photo delivery.
294	photoOutput.deferImageDelivery(camera.DeferredDeliveryImageType.PHOTO);
295    // Check whether deferred photo delivery is enabled.
296    let isSupportEnabled: boolean = photoOutput.isDeferredImageDeliveryEnabled(camera.DeferredDeliveryImageType.PHOTO);
297    console.info('isDeferredImageDeliveryEnabled res:' + isSupportEnabled);
298  }
299
300  // Commit the session configuration.
301  await photoSession.commitConfig();
302
303  // Start the session.
304  await photoSession.start().then(() => {
305    console.info('Promise returned to indicate the session start success.');
306  });
307  // Check whether the camera has flash.
308  let flashStatus: boolean = false;
309  try {
310    flashStatus = photoSession.hasFlash();
311  } catch (error) {
312    let err = error as BusinessError;
313    console.error('Failed to hasFlash. errorCode = ' + err.code);
314  }
315  console.info('Returned with the flash light support status:' + flashStatus);
316
317  if (flashStatus) {
318    // Check whether the auto flash mode is supported.
319    let flashModeStatus: boolean = false;
320    try {
321      let status: boolean = photoSession.isFlashModeSupported(camera.FlashMode.FLASH_MODE_AUTO);
322      flashModeStatus = status;
323    } catch (error) {
324      let err = error as BusinessError;
325      console.error('Failed to check whether the flash mode is supported. errorCode = ' + err.code);
326    }
327    if(flashModeStatus) {
328      // Set the flash mode to auto.
329      try {
330        photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);
331      } catch (error) {
332        let err = error as BusinessError;
333        console.error('Failed to set the flash mode. errorCode = ' + err.code);
334      }
335    }
336  }
337
338  // Check whether the continuous auto focus is supported.
339  let focusModeStatus: boolean = false;
340  try {
341    let status: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
342    focusModeStatus = status;
343  } catch (error) {
344    let err = error as BusinessError;
345    console.error('Failed to check whether the focus mode is supported. errorCode = ' + err.code);
346  }
347
348  if (focusModeStatus) {
349    // Set the focus mode to continuous auto focus.
350    try {
351      photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
352    } catch (error) {
353      let err = error as BusinessError;
354      console.error('Failed to set the focus mode. errorCode = ' + err.code);
355    }
356  }
357
358  // Obtain the zoom ratio range supported by the camera.
359  let zoomRatioRange: Array<number> = [];
360  try {
361    zoomRatioRange = photoSession.getZoomRatioRange();
362  } catch (error) {
363    let err = error as BusinessError;
364    console.error('Failed to get the zoom ratio range. errorCode = ' + err.code);
365  }
366  if (zoomRatioRange.length <= 0) {
367    return;
368  }
369  // Set a zoom ratio.
370  try {
371    photoSession.setZoomRatio(zoomRatioRange[0]);
372  } catch (error) {
373    let err = error as BusinessError;
374    console.error('Failed to set the zoom ratio value. errorCode = ' + err.code);
375  }
376  let photoCaptureSetting: camera.PhotoCaptureSetting = {
377    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // Set the photo quality to high.
378    rotation: camera.ImageRotation.ROTATION_0 // Set the rotation angle of the photo to 0.
379  }
380  // Use the current photo capture settings to take photos.
381  photoOutput.capture(photoCaptureSetting, (err: BusinessError) => {
382    if (err) {
383      console.error(`Failed to capture the photo ${err.message}`);
384      return;
385    }
386    console.info('Callback invoked to indicate the photo capture request success.');
387  });
388
389  // After the photo capture is complete, call the following APIs to close the camera and release the session. Do not release the session before the photo capture is complete.
390  // Stop the session.
391  await photoSession.stop();
392
393  // Release the camera input stream.
394  await cameraInput.close();
395
396  // Release the preview output stream.
397  await previewOutput.release();
398
399  // Release the photo output stream.
400  await photoOutput.release();
401
402  // Release the session.
403  await photoSession.release();
404
405  // Set the session to null.
406  photoSession = undefined;
407}
408```
409