1e41f4b71Sopenharmony_ci# Dual-Channel Preview (ArkTS)
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ciDual-channel preview means that an application can use two preview streams at the same time. One preview stream is used for display on the screen, and the other is used for other operations such as image processing, so as to improve the processing efficiency.
4e41f4b71Sopenharmony_ci
5e41f4b71Sopenharmony_ciThe camera application controls the camera hardware to implement basic operations such as image display (preview), photo saving (photographing), and video recording. The camera model is developed on the surface model. That is, an application transfers data through the surface. Specifically, it obtains the photo stream through the surface of an **ImageReceiver** object and the preview stream through the surface of an **\<XComponent>** object.
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ciTo implement dual-channel preview (there are two preview streams instead of one preview stream plus one photo stream), you must create a **previewOutput** object through the surface of an **ImageReceiver** object. Other processes are the same as those of the photo stream and preview stream.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ciRead [Camera](../../reference/apis-camera-kit/js-apis-camera.md) for the API reference.
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ci## Constraints
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ci- Currently, streams cannot be dynamically added. In other words, you cannot call [addOutput](../../reference/apis-camera-kit/js-apis-camera.md#addoutput11) to add streams without calling [session.stop](../../reference/apis-camera-kit/js-apis-camera.md#stop11) first.
14e41f4b71Sopenharmony_ci- After an **ImageReceiver** object processes image data obtained, it must release the image buffer so that the buffer queue of the surface properly rotates.
15e41f4b71Sopenharmony_ci
16e41f4b71Sopenharmony_ci## API Calling Process
17e41f4b71Sopenharmony_ci
18e41f4b71Sopenharmony_ciThe figure below shows the recommended API calling process of the dual-channel preview solution.
19e41f4b71Sopenharmony_ci
20e41f4b71Sopenharmony_ci![dual-preview-streams-instructions](figures/dual-preview-streams-instructions.png)
21e41f4b71Sopenharmony_ci
22e41f4b71Sopenharmony_ci## How to Develop
23e41f4b71Sopenharmony_ci
24e41f4b71Sopenharmony_ci1. Import the image module.
25e41f4b71Sopenharmony_ci
26e41f4b71Sopenharmony_ci   Create a surface for the dual-channel preview stream. In addition to the surface of an **\<XComponent>** object, the surface ID generated by an **ImageReceiver** object is needed. The APIs provided by the image module are also needed.
27e41f4b71Sopenharmony_ci
28e41f4b71Sopenharmony_ci   ```ts
29e41f4b71Sopenharmony_ci   import { image } from '@kit.ImageKit';
30e41f4b71Sopenharmony_ci   ```
31e41f4b71Sopenharmony_ci2. Create an **ImageReceiver** object.
32e41f4b71Sopenharmony_ci   ```ts
33e41f4b71Sopenharmony_ci   let size: image.Size = {
34e41f4b71Sopenharmony_ci       width: 640,
35e41f4b71Sopenharmony_ci       height: 480
36e41f4b71Sopenharmony_ci     }
37e41f4b71Sopenharmony_ci   let receiver: image.ImageReceiver = image.createImageReceiver(size, image.ImageFormat.JPEG, 8);
38e41f4b71Sopenharmony_ci   ```
39e41f4b71Sopenharmony_ci3. Obtain the surface ID of the **ImageReceiver** object.
40e41f4b71Sopenharmony_ci
41e41f4b71Sopenharmony_ci   ```ts
42e41f4b71Sopenharmony_ci   async function getImageReceiverSurfaceId(receiver: image.ImageReceiver): Promise<string | undefined> {
43e41f4b71Sopenharmony_ci     let ImageReceiverSurfaceId: string | undefined = undefined;
44e41f4b71Sopenharmony_ci     if (receiver !== undefined) {
45e41f4b71Sopenharmony_ci       console.info('receiver is not undefined');
46e41f4b71Sopenharmony_ci       let ImageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId();
47e41f4b71Sopenharmony_ci       console.info(`ImageReceived id: ${ImageReceiverSurfaceId}`);
48e41f4b71Sopenharmony_ci     } else {
49e41f4b71Sopenharmony_ci       console.error('createImageReceiver failed');
50e41f4b71Sopenharmony_ci     }
51e41f4b71Sopenharmony_ci     return ImageReceiverSurfaceId;
52e41f4b71Sopenharmony_ci   }
53e41f4b71Sopenharmony_ci   ```
54e41f4b71Sopenharmony_ci
55e41f4b71Sopenharmony_ci4. Create a surface of an **\<XComponent>** object.
56e41f4b71Sopenharmony_ci
57e41f4b71Sopenharmony_ci   The **\<XComponent>**, the capabilities of which are provided by the UI, offers the surface for preview streams. For details about how to obtain the surface ID, see [getXcomponentSurfaceId](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#getxcomponentsurfaceid). For details about the component, see [XComponent](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md).
58e41f4b71Sopenharmony_ci   > **NOTE**
59e41f4b71Sopenharmony_ci   > 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.
60e41f4b71Sopenharmony_ci
61e41f4b71Sopenharmony_ci5. Implement dual-channel preview.
62e41f4b71Sopenharmony_ci
63e41f4b71Sopenharmony_ci   Call [createPreviewOutput](../../reference/apis-camera-kit/js-apis-camera.md#createpreviewoutput) to transfer the two surface IDs generated in steps 2 and 3 to the camera service to create two preview streams. Develop other processes based on the normal preview process.
64e41f4b71Sopenharmony_ci
65e41f4b71Sopenharmony_ci   ```ts
66e41f4b71Sopenharmony_ci   import { camera } from '@kit.CameraKit';
67e41f4b71Sopenharmony_ci
68e41f4b71Sopenharmony_ci   async function createDualChannelPreview(cameraManager: camera.CameraManager, XComponentSurfaceId: string, receiver: image.ImageReceiver): Promise<void> {
69e41f4b71Sopenharmony_ci     // Obtain the supported camera devices.
70e41f4b71Sopenharmony_ci     let camerasDevices: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
71e41f4b71Sopenharmony_ci
72e41f4b71Sopenharmony_ci     // Obtain the supported modes.
73e41f4b71Sopenharmony_ci     let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(camerasDevices[0]);
74e41f4b71Sopenharmony_ci     let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
75e41f4b71Sopenharmony_ci     if (!isSupportPhotoMode) {
76e41f4b71Sopenharmony_ci       console.error('photo mode not support');
77e41f4b71Sopenharmony_ci       return;
78e41f4b71Sopenharmony_ci     }
79e41f4b71Sopenharmony_ci
80e41f4b71Sopenharmony_ci     // Obtain the profile object.
81e41f4b71Sopenharmony_ci     let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0], camera.SceneMode.NORMAL_PHOTO); // Obtain the profiles of the camera.
82e41f4b71Sopenharmony_ci     let previewProfiles: Array<camera.Profile> = profiles.previewProfiles;
83e41f4b71Sopenharmony_ci
84e41f4b71Sopenharmony_ci     // Preview stream 1.
85e41f4b71Sopenharmony_ci     let previewProfilesObj: camera.Profile = previewProfiles[0];
86e41f4b71Sopenharmony_ci
87e41f4b71Sopenharmony_ci     // Preview stream 2.
88e41f4b71Sopenharmony_ci     let previewProfilesObj2: camera.Profile = previewProfiles[0];
89e41f4b71Sopenharmony_ci
90e41f4b71Sopenharmony_ci     // Create an output object for preview stream 1.
91e41f4b71Sopenharmony_ci     let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj, XComponentSurfaceId);
92e41f4b71Sopenharmony_ci
93e41f4b71Sopenharmony_ci     // Create an output object for preview stream 2.
94e41f4b71Sopenharmony_ci     let imageReceiverSurfaceId: string = await receiver.getReceivingSurfaceId();
95e41f4b71Sopenharmony_ci     let previewOutput2: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj2, imageReceiverSurfaceId);
96e41f4b71Sopenharmony_ci
97e41f4b71Sopenharmony_ci     // Create a CameraInput object.
98e41f4b71Sopenharmony_ci     let cameraInput: camera.CameraInput = cameraManager.createCameraInput(camerasDevices[0]);
99e41f4b71Sopenharmony_ci
100e41f4b71Sopenharmony_ci     // Open the camera.
101e41f4b71Sopenharmony_ci     await cameraInput.open();
102e41f4b71Sopenharmony_ci
103e41f4b71Sopenharmony_ci     // Create a session.
104e41f4b71Sopenharmony_ci     let photoSession: camera.PhotoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
105e41f4b71Sopenharmony_ci
106e41f4b71Sopenharmony_ci     // Start configuration for the session.
107e41f4b71Sopenharmony_ci     photoSession.beginConfig();
108e41f4b71Sopenharmony_ci
109e41f4b71Sopenharmony_ci     // Add the CameraInput object to the session.
110e41f4b71Sopenharmony_ci     photoSession.addInput(cameraInput);
111e41f4b71Sopenharmony_ci
112e41f4b71Sopenharmony_ci     // Add preview stream 1 to the session.
113e41f4b71Sopenharmony_ci     photoSession.addOutput(previewOutput);
114e41f4b71Sopenharmony_ci
115e41f4b71Sopenharmony_ci     // Add preview stream 2 to the session.
116e41f4b71Sopenharmony_ci     photoSession.addOutput(previewOutput2);
117e41f4b71Sopenharmony_ci
118e41f4b71Sopenharmony_ci     // Commit the configuration.
119e41f4b71Sopenharmony_ci     await photoSession.commitConfig();
120e41f4b71Sopenharmony_ci
121e41f4b71Sopenharmony_ci     // Start the session.
122e41f4b71Sopenharmony_ci     await photoSession.start();
123e41f4b71Sopenharmony_ci   }
124e41f4b71Sopenharmony_ci   ```
125e41f4b71Sopenharmony_ci
126e41f4b71Sopenharmony_ci6. Obtain preview images in real time through the **ImageReceiver** object.
127e41f4b71Sopenharmony_ci
128e41f4b71Sopenharmony_ci   Use the **imageArrival** event of the **ImageReceiver** object to listen for and obtain image data returned by the bottom layer. For details, see [Image](../../reference/apis-image-kit/js-apis-image.md).
129e41f4b71Sopenharmony_ci
130e41f4b71Sopenharmony_ci   ```ts
131e41f4b71Sopenharmony_ci   import { BusinessError } from '@kit.BasicServicesKit';
132e41f4b71Sopenharmony_ci
133e41f4b71Sopenharmony_ci   function onImageArrival(receiver: image.ImageReceiver): void {
134e41f4b71Sopenharmony_ci     receiver.on('imageArrival', () => {
135e41f4b71Sopenharmony_ci       receiver.readNextImage((err: BusinessError, nextImage: image.Image) => {
136e41f4b71Sopenharmony_ci         if (err || nextImage === undefined) {
137e41f4b71Sopenharmony_ci           console.error('readNextImage failed');
138e41f4b71Sopenharmony_ci           return;
139e41f4b71Sopenharmony_ci         }
140e41f4b71Sopenharmony_ci         nextImage.getComponent(image.ComponentType.JPEG, (err: BusinessError, imgComponent: image.Component) => {
141e41f4b71Sopenharmony_ci           if (err || imgComponent === undefined) {
142e41f4b71Sopenharmony_ci             console.error('getComponent failed');
143e41f4b71Sopenharmony_ci           }
144e41f4b71Sopenharmony_ci           if (imgComponent && imgComponent.byteBuffer as ArrayBuffer) {
145e41f4b71Sopenharmony_ci             // do something...
146e41f4b71Sopenharmony_ci             // If an asynchronous operation is performed on the buffer, call nextImage.release() to release the resource after the asynchronous operation is complete.
147e41f4b71Sopenharmony_ci           } else {
148e41f4b71Sopenharmony_ci             console.error('byteBuffer is null');
149e41f4b71Sopenharmony_ci           }
150e41f4b71Sopenharmony_ci           // Release the resource when the buffer is not in use.
151e41f4b71Sopenharmony_ci           nextImage.release();
152e41f4b71Sopenharmony_ci         })
153e41f4b71Sopenharmony_ci       })
154e41f4b71Sopenharmony_ci     })
155e41f4b71Sopenharmony_ci   }
156e41f4b71Sopenharmony_ci   ```
157