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 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