1# Using the Camera in the Worker Thread (ArkTS) 2 3[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. 4 5When 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. 6 7## How to Develop 8 91. Create a worker thread file and configure the worker. 10 11 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**. 12 13 Example of the CameraWorker.ets file: 14 15 ```ts 16 import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS'; 17 import CameraService from '../CameraService'; 18 19 const workerPort: ThreadWorkerGlobalScope = worker.workerPort; 20 21 // Custom message format. 22 interface MessageInfo { 23 hasResolve: boolean; 24 type: string; 25 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. 26 surfaceId: string; 27 } 28 29 workerPort.onmessage = async (e: MessageEvents) => { 30 const messageInfo: MessageInfo = e.data; 31 console.info(`worker onmessage type:${messageInfo.type}`) 32 if ('initCamera' === messageInfo.type) { 33 // The worker thread receives a camera initialization message from the host thread. 34 console.info(`worker initCamera surfaceId:${messageInfo.surfaceId}`) 35 // Initialize the camera in the worker thread. 36 await CameraService.initCamera(messageInfo.context, messageInfo.surfaceId); 37 } else if ('releaseCamera' === messageInfo.type) { 38 // The worker thread receives a camera release message from the host thread. 39 console.info('worker releaseCamera.'); 40 // Release the camera in the worker thread. 41 await CameraService.releaseCamera(); 42 } 43 } 44 45 workerPort.onmessageerror = (e: MessageEvents) => { 46 } 47 48 workerPort.onerror = (e: ErrorEvent) => { 49 } 50 ``` 51 522. Create a camera service proxy class, in which all APIs provided by Camera Kit method are called. 53 54 ```ts 55 import { BusinessError } from '@kit.BasicServicesKit'; 56 import { camera } from '@kit.CameraKit'; 57 58 class CameraService { 59 private imageWidth: number = 1920; 60 private imageHeight: number = 1080; 61 private cameraManager: camera.CameraManager | undefined = undefined; 62 private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = []; 63 private cameraInput: camera.CameraInput | undefined = undefined; 64 private previewOutput: camera.PreviewOutput | undefined = undefined; 65 private photoOutput: camera.PhotoOutput | undefined = undefined; 66 private session: camera.PhotoSession | camera.VideoSession | undefined = undefined; 67 68 // Initialize a camera. 69 async initCamera(context: Context, surfaceId: string): Promise<void> { 70 console.info(`initCamera surfaceId: ${surfaceId}`); 71 try { 72 await this.releaseCamera(); 73 // Obtain a camera manager instance. 74 this.cameraManager = camera.getCameraManager(context); 75 if (this.cameraManager === undefined) { 76 console.error('cameraManager is undefined'); 77 return; 78 } 79 this.cameras = this.cameraManager.getSupportedCameras(); 80 81 // Create a cameraInput object. 82 this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]); 83 if (this.cameraInput === undefined) { 84 console.error('Failed to create the camera input.'); 85 return; 86 } 87 // Open a camera. 88 await this.cameraInput.open(); 89 90 let previewProfile: camera.Profile = { 91 format: camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP, 92 size: { 93 width: this.imageWidth, 94 height: this.imageHeight 95 } 96 }; 97 // Create a preview output stream. 98 this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId); 99 if (this.previewOutput === undefined) { 100 console.error('Failed to create the preview stream.'); 101 return; 102 } 103 104 let photoProfile: camera.Profile = { 105 format: camera.CameraFormat.CAMERA_FORMAT_JPEG, 106 size: { 107 width: this.imageWidth, 108 height: this.imageHeight 109 } 110 }; 111 // Create a photo output stream. 112 this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile); 113 if (this.photoOutput === undefined) { 114 console.error('Failed to create the photoOutput.'); 115 return; 116 } 117 118 // Create a camera session and start the session. 119 this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; 120 this.session.beginConfig(); 121 this.session.addInput(this.cameraInput); 122 this.session.addOutput(this.previewOutput); 123 this.session.addOutput(this.photoOutput); 124 await this.session.commitConfig(); 125 await this.session.start(); 126 } catch (error) { 127 let err = error as BusinessError; 128 console.error(`initCamera fail: ${JSON.stringify(err)}`); 129 } 130 } 131 132 // Release the camera resource. 133 async releaseCamera(): Promise<void> { 134 console.info('releaseCamera is called'); 135 try { 136 await this.previewOutput?.release(); 137 await this.photoOutput?.release(); 138 await this.session?.release(); 139 await this.cameraInput?.close(); 140 } catch (error) { 141 let err = error as BusinessError; 142 console.error(`releaseCamera fail: error: ${JSON.stringify(err)}`); 143 } finally { 144 this.previewOutput = undefined; 145 this.photoOutput = undefined; 146 this.cameraManager = undefined; 147 this.session = undefined; 148 this.cameraInput = undefined; 149 } 150 console.info('releaseCamera success'); 151 } 152 } 153 154 export default new CameraService(); 155 ``` 156 1573. 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. 158 159 ```ts 160 import { worker } from '@kit.ArkTS'; 161 162 @Entry 163 @Component 164 struct Index { 165 private mXComponentController: XComponentController = new XComponentController(); 166 private surfaceId: string = ''; 167 @State imageWidth: number = 1920; 168 @State imageHeight: number = 1080; 169 // Create a ThreadWorker object to obtain a worker instance. 170 private workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/CameraWorker.ets'); 171 172 onPageShow(): void { 173 if ('' !== this.surfaceId) { 174 // Send a message to the worker thread through the worker instance to initialize the camera. 175 this.workerInstance.postMessage({ 176 type: 'initCamera', 177 context: getContext(this), 178 surfaceId: this.surfaceId, 179 }) 180 } 181 } 182 183 onPageHide(): void { 184 // Send a message to the worker thread through the worker instance to destroy the camera. 185 this.workerInstance.postMessage({ 186 type: 'releaseCamera', 187 }) 188 } 189 190 build() { 191 Column() { 192 Column() { 193 XComponent({ 194 id: 'componentId', 195 type: 'surface', 196 controller: this.mXComponentController 197 }) 198 .onLoad(async () => { 199 console.info('onLoad is called'); 200 // Initialize the XComponent to obtain the surface ID of the preview stream. 201 this.surfaceId = this.mXComponentController.getXComponentSurfaceId(); 202 let surfaceRect: SurfaceRect = { 203 surfaceWidth: this.imageHeight, 204 surfaceHeight: this.imageWidth 205 }; 206 this.mXComponentController.setXComponentSurfaceRect(surfaceRect); 207 console.info(`onLoad surfaceId: ${this.surfaceId}`); 208 if (!this.workerInstance) { 209 console.error('create stage worker failed'); 210 return; 211 } 212 // The host thread sends a camera initialization message to the worker thread. 213 this.workerInstance.postMessage({ 214 type: 'initCamera', 215 context: getContext(this), // Pass the context of the host thread to the worker thread. 216 surfaceId: this.surfaceId, // Pass the surface ID to the worker thread. 217 }) 218 })// The width and height of the surface are opposite to those of the XComponent. 219 .width(px2vp(this.imageHeight)) 220 .height(px2vp(this.imageWidth)) 221 222 }.justifyContent(FlexAlign.Center) 223 .height('90%') 224 225 Text('WorkerDemo') 226 .fontSize(36) 227 } 228 .justifyContent(FlexAlign.End) 229 .height('100%') 230 .width('100%') 231 } 232 } 233 ``` 234 235## Trace Comparison 236 237Worker not used: 238 239 240 241Worker used: 242 243 244