1# Preview (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 10Preview is the image you see after you start the camera application but before you take photos or record videos. 11 12## How to Develop 13 14Read [Module Description](../../reference/apis-camera-kit/arkts-apis-camera.md) for the API reference. 15 161. Import the camera module, which provides camera-related attributes and methods. 17 18 ```ts 19 import { camera } from '@kit.CameraKit'; 20 import { BusinessError } from '@kit.BasicServicesKit'; 21 ``` 22 232. Create a surface. 24 25 The camera development model relies on the surface model, which uses surface for data interaction. When developing the camera application UI, you need to create an XComponent to provide a surface for the preview stream, and then obtain the surface ID associated with the XComponent to create a preview stream. The preview stream can be directly rendered within the XComponent. For details about how to obtain the surface ID, see [getXComponentSurfaceId](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#getxcomponentsurfaceid9). The capabilities of the XComponent are provided by the UI. For details, see [XComponent](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md). 26 27 > **NOTE** 28 > 29 > The preview stream and video output stream must have the same aspect ratio of the resolution. For example, the aspect ratio of the surface of the **XComponent** is 1920:1080 (which is equal to 16:9), then the aspect ratio of the resolution of the preview stream must also be 16:9. This means that the resolution can be 640:360, 960:540, 1920:1080, or the like. 30 31 ```ts 32 @Entry 33 @Component 34 struct example { 35 xComponentCtl: XComponentController = new XComponentController(); 36 surfaceId:string = ''; 37 imageWidth: number = 1920; 38 imageHeight: number = 1080; 39 private uiContext: UIContext = this.getUIContext(); 40 41 build() { 42 XComponent({ 43 id: 'componentId', 44 type: XComponentType.SURFACE, 45 controller: this.xComponentCtl 46 }) 47 .onLoad(async () => { 48 console.info('onLoad is called'); 49 this.surfaceId = this.xComponentCtl.getXComponentSurfaceId(); // Obtain the surface ID of the component. 50 // Use the surface ID to create a preview stream and start the camera. The component renders the preview stream data of each frame in real time. 51 }) 52 // The width and height of the surface are opposite to those of the XComponent. Alternatively, you can use .renderFit(RenderFit.RESIZE_CONTAIN) to automatically adjust the display without manually setting the width and height. 53 .width(this.uiContext.px2vp(this.imageHeight)) 54 .height(this.uiContext.px2vp(this.imageWidth)) 55 } 56 } 57 ``` 58 593. Use **previewProfiles** in the [CameraOutputCapability](../../reference/apis-camera-kit/arkts-apis-camera-i.md#cameraoutputcapability) class to obtain the preview output capabilities, in the format of an **previewProfilesArray** array, supported by the current device. Then call [createPreviewOutput](../../reference/apis-camera-kit/arkts-apis-camera-CameraManager.md#createpreviewoutput) to create a PreviewOutput object, with the first parameter set to the preview profile supported by the camera and the second parameter set to the surface ID obtained in step 2. 60 61 ```ts 62 function getPreviewOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability, surfaceId: string): camera.PreviewOutput | undefined { 63 let previewProfilesArray: Array<camera.Profile> = cameraOutputCapability.previewProfiles; 64 let previewOutput: camera.PreviewOutput | undefined = undefined; 65 try { 66 // Choose the preview profile from previewProfilesArray that matches the aspect ratio set in Step 2. Selecting the first item in the array is for illustrative purposes only. 67 previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId); 68 } catch (error) { 69 let err = error as BusinessError; 70 console.error("Failed to create the PreviewOutput instance. error code: " + err.code); 71 } 72 return previewOutput; 73 } 74 ``` 75 764. Call [Session.start](../../reference/apis-camera-kit/arkts-apis-camera-Session.md#start11) to start outputting the preview stream. If the call fails, an error code is returned. For details, see [CameraErrorCode](../../reference/apis-camera-kit/arkts-apis-camera-e.md#cameraerrorcode). 77 78 ```ts 79 async function startPreviewOutput(cameraManager: camera.CameraManager, previewOutput: camera.PreviewOutput): Promise<void> { 80 let cameraArray: Array<camera.CameraDevice> = []; 81 cameraArray = cameraManager.getSupportedCameras(); 82 if (cameraArray.length == 0) { 83 console.error('no camera.'); 84 return; 85 } 86 // Obtain the supported modes. 87 let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]); 88 let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0; 89 if (!isSupportPhotoMode) { 90 console.error('photo mode not support'); 91 return; 92 } 93 let cameraInput: camera.CameraInput | undefined = undefined; 94 cameraInput = cameraManager.createCameraInput(cameraArray[0]); 95 if (cameraInput === undefined) { 96 console.error('cameraInput is undefined'); 97 return; 98 } 99 // Open the camera. 100 await cameraInput.open(); 101 let session: camera.PhotoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; 102 session.beginConfig(); 103 session.addInput(cameraInput); 104 session.addOutput(previewOutput); 105 await session.commitConfig(); 106 await session.start(); 107 } 108 ``` 109 110 111## Status Listening 112 113During camera application development, you can listen for the preview output stream status, including preview stream start, preview stream end, and preview stream output errors. 114 115- Register the **'frameStart'** event to listen for preview start events. This event can be registered when a PreviewOutput object is created and is triggered when the bottom layer starts exposure for the first time. The preview stream starts as long as a result is returned. 116 117 ```ts 118 function onPreviewOutputFrameStart(previewOutput: camera.PreviewOutput): void { 119 previewOutput.on('frameStart', (err: BusinessError) => { 120 if (err !== undefined && err.code !== 0) { 121 return; 122 } 123 console.info('Preview frame started'); 124 }); 125 } 126 ``` 127 128- Register the **'frameEnd'** event to listen for preview end events. This event can be registered when a PreviewOutput object is created and is triggered when the last frame of preview ends. The preview stream ends as long as a result is returned. 129 130 ```ts 131 function onPreviewOutputFrameEnd(previewOutput: camera.PreviewOutput): void { 132 previewOutput.on('frameEnd', (err: BusinessError) => { 133 if (err !== undefined && err.code !== 0) { 134 return; 135 } 136 console.info('Preview frame ended'); 137 }); 138 } 139 ``` 140 141- Register the **'error'** event to listen for preview output errors. The callback function returns an error code when an API is incorrectly used. For details about the error code types, see [CameraErrorCode](../../reference/apis-camera-kit/arkts-apis-camera-e.md#cameraerrorcode). 142 143 ```ts 144 function onPreviewOutputError(previewOutput: camera.PreviewOutput): void { 145 previewOutput.on('error', (previewOutputError: BusinessError) => { 146 console.error(`Preview output error code: ${previewOutputError.code}`); 147 }); 148 } 149 ``` 150 151## Sample Code 152 153```ts 154import { camera } from '@kit.CameraKit'; 155import { image } from '@kit.ImageKit'; 156import { BusinessError } from '@kit.BasicServicesKit'; 157import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit'; 158 159 160@Entry 161@Component 162struct Index { 163 private imageReceiver: image.ImageReceiver | undefined = undefined; 164 private imageReceiverSurfaceId: string = ''; 165 private xComponentCtl: XComponentController = new XComponentController(); 166 private xComponentSurfaceId: string = ''; 167 @State imageWidth: number = 1920; 168 @State imageHeight: number = 1080; 169 private cameraManager: camera.CameraManager | undefined = undefined; 170 private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = []; 171 private cameraInput: camera.CameraInput | undefined = undefined; 172 private previewOutput: camera.PreviewOutput | undefined = undefined; 173 private session: camera.VideoSession | undefined = undefined; 174 private uiContext: UIContext = this.getUIContext(); 175 private context: Context | undefined = this.uiContext.getHostContext(); 176 private cameraPermission: Permissions = 'ohos.permission.CAMERA'; // For details about how to request permissions, see the instructions provided at the beginning of this topic. 177 @State isShow: boolean = false; 178 179 180 async requestPermissionsFn(): Promise<void> { 181 let atManager = abilityAccessCtrl.createAtManager(); 182 if (this.context) { 183 let res = await atManager.requestPermissionsFromUser(this.context, [this.cameraPermission]); 184 for (let i =0; i < res.permissions.length; i++) { 185 if (this.cameraPermission.toString() === res.permissions[i] && res.authResults[i] === 0) { 186 this.isShow = true; 187 } 188 } 189 } 190 } 191 192 aboutToAppear(): void { 193 this.requestPermissionsFn(); 194 } 195 196 onPageShow(): void { 197 console.info('onPageShow'); 198 if (this.xComponentSurfaceId !== '') { 199 this.initCamera(); 200 } 201 } 202 203 onPageHide(): void { 204 console.info('onPageHide'); 205 this.releaseCamera(); 206 } 207 208 build() { 209 Column() { 210 if (this.isShow) { 211 XComponent({ 212 id: 'componentId', 213 type: XComponentType.SURFACE, 214 controller: this.xComponentCtl 215 }) 216 .onLoad(async () => { 217 console.info('onLoad is called'); 218 this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId(); // Obtain the surface ID of the component. 219 // Initialize the camera. The component renders the preview stream data of each frame in real time. 220 this.initCamera() 221 }) 222 .width(this.uiContext.px2vp(this.imageHeight)) 223 .height(this.uiContext.px2vp(this.imageWidth)) 224 } 225 } 226 .justifyContent(FlexAlign.Center) 227 .height('100%') 228 .width('100%') 229 } 230 231 232 // Initialize a camera. 233 async initCamera(): Promise<void> { 234 console.info(`initCamera previewOutput xComponentSurfaceId:${this.xComponentSurfaceId}`); 235 try { 236 // Obtain a camera manager instance. 237 this.cameraManager = camera.getCameraManager(this.context); 238 if (!this.cameraManager) { 239 console.error('initCamera getCameraManager'); 240 } 241 // Obtain the list of cameras supported by the device. 242 this.cameras = this.cameraManager.getSupportedCameras(); 243 if (!this.cameras) { 244 console.error('initCamera getSupportedCameras'); 245 } 246 // Select a camera device and create a CameraInput object. 247 this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]); 248 if (!this.cameraInput) { 249 console.error('initCamera createCameraInput'); 250 } 251 // Open the camera. 252 await this.cameraInput.open().catch((err: BusinessError) => { 253 console.error(`initCamera open fail: ${err}`); 254 }) 255 // Obtain the profile supported by the camera device. 256 let capability: camera.CameraOutputCapability = 257 this.cameraManager.getSupportedOutputCapability(this.cameras[0], camera.SceneMode.NORMAL_VIDEO); 258 if (!capability) { 259 console.error('initCamera getSupportedOutputCapability'); 260 } 261 let minRatioDiff : number = 0.1; 262 let surfaceRatio : number = this.imageWidth / this.imageHeight; // The closest aspect ratio to 16:9. 263 let previewProfile: camera.Profile = capability.previewProfiles[0]; 264 // Select a supported preview stream profile based on service requirements. 265 // The following uses the preview stream profile with the CAMERA_FORMAT_YUV_420_SP (NV21) format that meets the resolution constraints as an example. 266 for (let index = 0; index < capability.previewProfiles.length; index++) { 267 const tempProfile = capability.previewProfiles[index]; 268 let tempRatio = tempProfile.size.width >= tempProfile.size.height ? 269 tempProfile.size.width / tempProfile.size.height : tempProfile.size.height / tempProfile.size.width; 270 let currentRatio = Math.abs(tempRatio - surfaceRatio); 271 if (currentRatio <= minRatioDiff && tempProfile.format == camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP) { 272 previewProfile = tempProfile; 273 break; 274 } 275 } 276 this.imageWidth = previewProfile.size.width; // Update the width of the XComponent. 277 this.imageHeight = previewProfile.size.height; // Update the height of the XComponent. 278 console.info(`initCamera imageWidth:${this.imageWidth} imageHeight:${this.imageHeight}`); 279 280 // Create a preview using xComponentSurfaceId. 281 this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId); 282 if (!this.previewOutput) { 283 console.error('initCamera createPreviewOutput'); 284 } 285 // Create a camera session in recording mode. 286 this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; 287 if (!this.session) { 288 console.error('initCamera createSession'); 289 } 290 // Start configuration for the session. 291 this.session.beginConfig(); 292 // Add a camera input. 293 this.session.addInput(this.cameraInput); 294 // Add a preview output stream. 295 this.session.addOutput(this.previewOutput); 296 // Commit the session configuration. 297 await this.session.commitConfig(); 298 // Start the configured input and output streams. 299 await this.session.start(); 300 } catch (error) { 301 console.error(`initCamera fail: ${error}`); 302 } 303 } 304 305 306 // Release the camera. 307 async releaseCamera(): Promise<void> { 308 console.info('releaseCamera E'); 309 try { 310 // Stop the session. 311 await this.session?.stop(); 312 // Release the camera input stream. 313 await this.cameraInput?.close(); 314 // Release the preview output stream. 315 await this.previewOutput?.release(); 316 // Release the session. 317 await this.session?.release(); 318 } catch (error) { 319 console.error(`initCamera fail: ${error}`); 320 } 321 } 322} 323``` 324