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![camera-in-ui-thread](figures/camera-in-ui-thread.png)
240
241Worker used:
242
243![camera-in-worker-thread](figures/camera-in-worker-thread.png)
244