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 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