1# Using the Camera in the Worker Thread (ArkTS) 2<!--Kit: Camera Kit--> 3<!--Subsystem: Multimedia--> 4<!--Owner: @qano--> 5<!--SE: @leo_ysl--> 6<!--TSE: @xchaosioda--> 7 8[Worker](../../arkts-utils/worker-introduction.md) is mainly used to offer applications a multithreaded environment. It enables applications to perform time-consuming operations in background threads. This greatly prevents computing-intensive or high-latency tasks from blocking the running of the main thread. 9 10When using camera capabilities, you often need to create camera sessions and continuously receive and process preview, photo, and video streams to achieve the desired camera functionalities. If these resource-demanding operations are performed in the main thread (UI thread), UI rendering may be blocked. Therefore, you are advised to implement the camera functionalities in the Worker thread. 11 12## How to Develop 131. Import dependencies, including dependencies related to Worker and camera framework. 14 ```ts 15 import { BusinessError } from '@kit.BasicServicesKit'; 16 import { camera } from '@kit.CameraKit'; 17 import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; 18 ``` 19 202. Create a camera service proxy class, in which all APIs provided by Camera Kit method are called. 21 22 ```ts 23 class CameraService { 24 private imageWidth: number = 1920; 25 private imageHeight: number = 1080; 26 private cameraManager: camera.CameraManager | undefined = undefined; 27 private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = []; 28 private cameraInput: camera.CameraInput | undefined = undefined; 29 private previewOutput: camera.PreviewOutput | undefined = undefined; 30 private photoOutput: camera.PhotoOutput | undefined = undefined; 31 private session: camera.PhotoSession | camera.VideoSession | undefined = undefined; 32 33 // Initialize a camera. 34 async initCamera(context: Context, surfaceId: string): Promise<void> { 35 console.info(`initCamera surfaceId: ${surfaceId}`); 36 try { 37 await this.releaseCamera(); 38 // Obtain a camera manager instance. 39 this.cameraManager = camera.getCameraManager(context); 40 if (this.cameraManager === undefined) { 41 console.error('cameraManager is undefined'); 42 return; 43 } 44 this.cameras = this.cameraManager.getSupportedCameras(); 45 46 // Create a cameraInput object. 47 this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]); 48 if (this.cameraInput === undefined) { 49 console.error('Failed to create the camera input.'); 50 return; 51 } 52 // Open the camera. 53 await this.cameraInput.open(); 54 55 let previewProfile: camera.Profile = { 56 format: camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP, 57 size: { 58 width: this.imageWidth, 59 height: this.imageHeight 60 } 61 }; 62 // Create a preview output stream. 63 this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId); 64 if (this.previewOutput === undefined) { 65 console.error('Failed to create the preview stream.'); 66 return; 67 } 68 69 let photoProfile: camera.Profile = { 70 format: camera.CameraFormat.CAMERA_FORMAT_JPEG, 71 size: { 72 width: this.imageWidth, 73 height: this.imageHeight 74 } 75 }; 76 // Create a photo output stream. 77 this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile); 78 if (this.photoOutput === undefined) { 79 console.error('Failed to create the photoOutput.'); 80 return; 81 } 82 83 // Create a camera session and start the session. 84 this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; 85 this.session.beginConfig(); 86 this.session.addInput(this.cameraInput); 87 this.session.addOutput(this.previewOutput); 88 this.session.addOutput(this.photoOutput); 89 await this.session.commitConfig(); 90 await this.session.start(); 91 } catch (error) { 92 let err = error as BusinessError; 93 console.error(`initCamera fail: ${err}`); 94 } 95 } 96 97 // Release the camera resource. 98 async releaseCamera(): Promise<void> { 99 console.info('releaseCamera is called'); 100 try { 101 await this.previewOutput?.release(); 102 await this.photoOutput?.release(); 103 await this.session?.release(); 104 await this.cameraInput?.close(); 105 } catch (error) { 106 let err = error as BusinessError; 107 console.error(`releaseCamera fail: error: ${err}`); 108 } finally { 109 this.previewOutput = undefined; 110 this.photoOutput = undefined; 111 this.cameraManager = undefined; 112 this.session = undefined; 113 this.cameraInput = undefined; 114 } 115 console.info('releaseCamera success'); 116 } 117 } 118 ``` 119 1203. Create a Worker thread file and configure the Worker. 121 122 DevEco Studio supports one-click generation of Worker threads. Right-click any position in the {moduleName} directory and choose **New > Worker** to generate the template file and configuration information of the Worker thread. You do not need to configure the related fields in **build-profile.json5**. 123 124 Example of the CameraWorker.ets file: 125 126 ```ts 127 let cameraService = new CameraService(); 128 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 129 130 // Custom message format. 131 interface MessageInfo { 132 hasResolve: boolean; 133 type: string; 134 context: Context; // The Worker thread cannot use getContext() to obtain the context of the host thread. Instead, the context must be passed through messages from the host thread to the Worker thread. 135 surfaceId: string; 136 } 137 138 workerPort.onmessage = async (e: MessageEvents) => { 139 const messageInfo: MessageInfo = e.data; 140 console.info(`worker onmessage type:${messageInfo.type}`) 141 if ('initCamera' === messageInfo.type) { 142 // The Worker thread receives a camera initialization message from the host thread. 143 console.info(`worker initCamera surfaceId:${messageInfo.surfaceId}`) 144 // Initialize the camera in the Worker thread. 145 await cameraService.initCamera(messageInfo.context, messageInfo.surfaceId); 146 } else if ('releaseCamera' === messageInfo.type) { 147 // The Worker thread receives a camera release message from the host thread. 148 console.info('worker releaseCamera.'); 149 // Release the camera in the Worker thread. 150 await cameraService.releaseCamera(); 151 } 152 } 153 154 workerPort.onmessageerror = (e: MessageEvents) => { 155 } 156 157 workerPort.onerror = (e: ErrorEvent) => { 158 } 159 ``` 160 1614. Create a component to display the preview stream, create a ThreadWorker instance in the page-related lifecycle, and initialize and release the camera in the Worker thread. 162 163 ```ts 164 @Entry 165 @Component 166 struct Index { 167 private mXComponentController: XComponentController = new XComponentController(); 168 private surfaceId: string = ''; 169 @State imageWidth: number = 1920; 170 @State imageHeight: number = 1080; 171 // Create a ThreadWorker object to obtain a Worker instance. 172 private workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/CameraWorker.ets'); 173 private uiContext: UIContext = this.getUIContext(); 174 private context: Context | undefined = this.uiContext.getHostContext(); 175 176 onPageShow(): void { 177 if ('' !== this.surfaceId) { 178 // Send a message to the Worker thread through the Worker instance to initialize the camera. 179 this.workerInstance.postMessage({ 180 type: 'initCamera', 181 context: this.context, 182 surfaceId: this.surfaceId, 183 }) 184 } 185 } 186 187 onPageHide(): void { 188 // Send a message to the Worker thread through the Worker instance to destroy the camera. 189 this.workerInstance.postMessage({ 190 type: 'releaseCamera', 191 }) 192 } 193 194 build() { 195 Column() { 196 Column() { 197 XComponent({ 198 id: 'componentId', 199 type: XComponentType.SURFACE, 200 controller: this.mXComponentController 201 }) 202 .onLoad(async () => { 203 console.info('onLoad is called'); 204 // Initialize the XComponent to obtain the surface ID of the preview stream. 205 this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); 206 let surfaceRect: SurfaceRect = { 207 surfaceWidth: this.imageHeight, 208 surfaceHeight: this.imageWidth 209 }; 210 this.mXComponentController.setXComponentSurfaceRect(surfaceRect); 211 console.info(`onLoad surfaceId: ${this.surfaceId}`); 212 if (!this.workerInstance) { 213 console.error('create stage worker failed'); 214 return; 215 } 216 // The host thread sends a camera initialization message to the Worker thread. 217 this.workerInstance.postMessage({ 218 type: 'initCamera', 219 context: this.context, // Pass the context of the host thread to the Worker thread. 220 surfaceId: this.surfaceId, // Pass the surface ID to the Worker thread. 221 }) 222 })// The width and height of the surface are opposite to those of the XComponent. 223 .width(this.uiContext.px2vp(this.imageHeight)) 224 .height(this.uiContext.px2vp(this.imageWidth)) 225 226 }.justifyContent(FlexAlign.Center) 227 .height('90%') 228 229 Text('WorkerDemo') 230 .fontSize(36) 231 } 232 .justifyContent(FlexAlign.End) 233 .height('100%') 234 .width('100%') 235 } 236 } 237 ``` 238 239## Trace Comparison 240 241Worker not used: 242 243 244 245Worker used: 246 247 248