• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![camera-in-ui-thread](figures/camera-in-ui-thread.png)
244
245Worker used:
246
247![camera-in-worker-thread](figures/camera-in-worker-thread.png)
248