• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Preview (ArkTS)
2<!--Kit: Camera Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @qano-->
5<!--SE: @leo_ysl-->
6<!--TSE: @xchaosioda-->
7
8Before developing a camera application, request permissions by following the instructions provided in [Requesting Camera Development Permissions](camera-preparation.md).
9
10Preview is the image you see after you start the camera application but before you take photos or record videos.
11
12## How to Develop
13
14Read [Module Description](../../reference/apis-camera-kit/arkts-apis-camera.md) for the API reference.
15
161. Import the camera module, which provides camera-related attributes and methods.
17
18   ```ts
19   import { camera } from '@kit.CameraKit';
20   import { BusinessError } from '@kit.BasicServicesKit';
21   ```
22
232. Create a surface.
24
25    The camera development model relies on the surface model, which uses surface for data interaction. When developing the camera application UI, you need to create an XComponent to provide a surface for the preview stream, and then obtain the surface ID associated with the XComponent to create a preview stream. The preview stream can be directly rendered within the XComponent. For details about how to obtain the surface ID, see [getXComponentSurfaceId](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#getxcomponentsurfaceid9). The capabilities of the XComponent are provided by the UI. For details, see [XComponent](../../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md).
26
27    > **NOTE**
28    >
29    > 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.
30
31    ```ts
32    @Entry
33    @Component
34    struct example {
35      xComponentCtl: XComponentController = new XComponentController();
36      surfaceId:string = '';
37      imageWidth: number = 1920;
38      imageHeight: number = 1080;
39      private uiContext: UIContext = this.getUIContext();
40
41      build() {
42        XComponent({
43          id: 'componentId',
44          type: XComponentType.SURFACE,
45          controller: this.xComponentCtl
46        })
47          .onLoad(async () => {
48            console.info('onLoad is called');
49            this.surfaceId = this.xComponentCtl.getXComponentSurfaceId(); // Obtain the surface ID of the component.
50            // Use the surface ID to create a preview stream and start the camera. The component renders the preview stream data of each frame in real time.
51          })
52          // The width and height of the surface are opposite to those of the XComponent. Alternatively, you can use .renderFit(RenderFit.RESIZE_CONTAIN) to automatically adjust the display without manually setting the width and height.
53          .width(this.uiContext.px2vp(this.imageHeight))
54          .height(this.uiContext.px2vp(this.imageWidth))
55      }
56    }
57    ```
58
593. Use **previewProfiles** in the [CameraOutputCapability](../../reference/apis-camera-kit/arkts-apis-camera-i.md#cameraoutputcapability) class to obtain the preview output capabilities, in the format of an **previewProfilesArray** array, supported by the current device. Then call [createPreviewOutput](../../reference/apis-camera-kit/arkts-apis-camera-CameraManager.md#createpreviewoutput) to create a PreviewOutput object, with the first parameter set to the preview profile supported by the camera and the second parameter set to the surface ID obtained in step 2.
60
61   ```ts
62   function getPreviewOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability, surfaceId: string): camera.PreviewOutput | undefined {
63     let previewProfilesArray: Array<camera.Profile> = cameraOutputCapability.previewProfiles;
64     let previewOutput: camera.PreviewOutput | undefined = undefined;
65     try {
66       // Choose the preview profile from previewProfilesArray that matches the aspect ratio set in Step 2. Selecting the first item in the array is for illustrative purposes only.
67       previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
68     } catch (error) {
69       let err = error as BusinessError;
70       console.error("Failed to create the PreviewOutput instance. error code: " + err.code);
71     }
72     return previewOutput;
73   }
74   ```
75
764. Call [Session.start](../../reference/apis-camera-kit/arkts-apis-camera-Session.md#start11) to start outputting the preview stream. If the call fails, an error code is returned. For details, see [CameraErrorCode](../../reference/apis-camera-kit/arkts-apis-camera-e.md#cameraerrorcode).
77
78   ```ts
79   async function startPreviewOutput(cameraManager: camera.CameraManager, previewOutput: camera.PreviewOutput): Promise<void> {
80     let cameraArray: Array<camera.CameraDevice> = [];
81     cameraArray = cameraManager.getSupportedCameras();
82     if (cameraArray.length == 0) {
83       console.error('no camera.');
84       return;
85     }
86     // Obtain the supported modes.
87     let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
88     let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;
89     if (!isSupportPhotoMode) {
90       console.error('photo mode not support');
91       return;
92     }
93     let cameraInput: camera.CameraInput | undefined = undefined;
94     cameraInput = cameraManager.createCameraInput(cameraArray[0]);
95     if (cameraInput === undefined) {
96       console.error('cameraInput is undefined');
97       return;
98     }
99     // Open the camera.
100     await cameraInput.open();
101     let session: camera.PhotoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
102     session.beginConfig();
103     session.addInput(cameraInput);
104     session.addOutput(previewOutput);
105     await session.commitConfig();
106     await session.start();
107   }
108   ```
109
110
111## Status Listening
112
113During camera application development, you can listen for the preview output stream status, including preview stream start, preview stream end, and preview stream output errors.
114
115- Register the **'frameStart'** event to listen for preview start events. This event can be registered when a PreviewOutput object is created and is triggered when the bottom layer starts exposure for the first time. The preview stream starts as long as a result is returned.
116
117  ```ts
118  function onPreviewOutputFrameStart(previewOutput: camera.PreviewOutput): void {
119    previewOutput.on('frameStart', (err: BusinessError) => {
120      if (err !== undefined && err.code !== 0) {
121        return;
122      }
123      console.info('Preview frame started');
124    });
125  }
126  ```
127
128- Register the **'frameEnd'** event to listen for preview end events. This event can be registered when a PreviewOutput object is created and is triggered when the last frame of preview ends. The preview stream ends as long as a result is returned.
129
130  ```ts
131  function onPreviewOutputFrameEnd(previewOutput: camera.PreviewOutput): void {
132    previewOutput.on('frameEnd', (err: BusinessError) => {
133      if (err !== undefined && err.code !== 0) {
134        return;
135      }
136      console.info('Preview frame ended');
137    });
138  }
139  ```
140
141- Register the **'error'** event to listen for preview output errors. The callback function returns an error code when an API is incorrectly used. For details about the error code types, see [CameraErrorCode](../../reference/apis-camera-kit/arkts-apis-camera-e.md#cameraerrorcode).
142
143  ```ts
144  function onPreviewOutputError(previewOutput: camera.PreviewOutput): void {
145    previewOutput.on('error', (previewOutputError: BusinessError) => {
146      console.error(`Preview output error code: ${previewOutputError.code}`);
147    });
148  }
149  ```
150
151## Sample Code
152
153```ts
154import { camera } from '@kit.CameraKit';
155import { image } from '@kit.ImageKit';
156import { BusinessError } from '@kit.BasicServicesKit';
157import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
158
159
160@Entry
161@Component
162struct Index {
163  private imageReceiver: image.ImageReceiver | undefined = undefined;
164  private imageReceiverSurfaceId: string = '';
165  private xComponentCtl: XComponentController = new XComponentController();
166  private xComponentSurfaceId: string = '';
167  @State imageWidth: number = 1920;
168  @State imageHeight: number = 1080;
169  private cameraManager: camera.CameraManager | undefined = undefined;
170  private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = [];
171  private cameraInput: camera.CameraInput | undefined = undefined;
172  private previewOutput: camera.PreviewOutput | undefined = undefined;
173  private session: camera.VideoSession | undefined = undefined;
174  private uiContext: UIContext = this.getUIContext();
175  private context: Context | undefined = this.uiContext.getHostContext();
176  private cameraPermission: Permissions = 'ohos.permission.CAMERA'; // For details about how to request permissions, see the instructions provided at the beginning of this topic.
177  @State isShow: boolean = false;
178
179
180  async requestPermissionsFn(): Promise<void> {
181    let atManager = abilityAccessCtrl.createAtManager();
182    if (this.context) {
183      let res = await atManager.requestPermissionsFromUser(this.context, [this.cameraPermission]);
184      for (let i =0; i < res.permissions.length; i++) {
185        if (this.cameraPermission.toString() === res.permissions[i] && res.authResults[i] === 0) {
186          this.isShow = true;
187        }
188      }
189    }
190  }
191
192  aboutToAppear(): void {
193    this.requestPermissionsFn();
194  }
195
196  onPageShow(): void {
197    console.info('onPageShow');
198    if (this.xComponentSurfaceId !== '') {
199      this.initCamera();
200    }
201  }
202
203  onPageHide(): void {
204    console.info('onPageHide');
205    this.releaseCamera();
206  }
207
208  build() {
209    Column() {
210      if (this.isShow) {
211        XComponent({
212          id: 'componentId',
213          type: XComponentType.SURFACE,
214          controller: this.xComponentCtl
215        })
216          .onLoad(async () => {
217            console.info('onLoad is called');
218            this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId(); // Obtain the surface ID of the component.
219            // Initialize the camera. The component renders the preview stream data of each frame in real time.
220            this.initCamera()
221          })
222          .width(this.uiContext.px2vp(this.imageHeight))
223          .height(this.uiContext.px2vp(this.imageWidth))
224      }
225    }
226    .justifyContent(FlexAlign.Center)
227    .height('100%')
228    .width('100%')
229  }
230
231
232  // Initialize a camera.
233  async initCamera(): Promise<void> {
234    console.info(`initCamera previewOutput xComponentSurfaceId:${this.xComponentSurfaceId}`);
235    try {
236      // Obtain a camera manager instance.
237      this.cameraManager = camera.getCameraManager(this.context);
238      if (!this.cameraManager) {
239        console.error('initCamera getCameraManager');
240      }
241      // Obtain the list of cameras supported by the device.
242      this.cameras = this.cameraManager.getSupportedCameras();
243      if (!this.cameras) {
244        console.error('initCamera getSupportedCameras');
245      }
246      // Select a camera device and create a CameraInput object.
247      this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]);
248      if (!this.cameraInput) {
249        console.error('initCamera createCameraInput');
250      }
251      // Open the camera.
252      await this.cameraInput.open().catch((err: BusinessError) => {
253        console.error(`initCamera open fail: ${err}`);
254      })
255      // Obtain the profile supported by the camera device.
256      let capability: camera.CameraOutputCapability =
257        this.cameraManager.getSupportedOutputCapability(this.cameras[0], camera.SceneMode.NORMAL_VIDEO);
258      if (!capability) {
259        console.error('initCamera getSupportedOutputCapability');
260      }
261      let minRatioDiff : number = 0.1;
262      let surfaceRatio : number = this.imageWidth / this.imageHeight; // The closest aspect ratio to 16:9.
263      let previewProfile: camera.Profile = capability.previewProfiles[0];
264      // Select a supported preview stream profile based on service requirements.
265      // The following uses the preview stream profile with the CAMERA_FORMAT_YUV_420_SP (NV21) format that meets the resolution constraints as an example.
266      for (let index = 0; index < capability.previewProfiles.length; index++) {
267        const tempProfile = capability.previewProfiles[index];
268        let tempRatio = tempProfile.size.width >= tempProfile.size.height ?
269          tempProfile.size.width / tempProfile.size.height : tempProfile.size.height / tempProfile.size.width;
270        let currentRatio = Math.abs(tempRatio - surfaceRatio);
271        if (currentRatio <= minRatioDiff && tempProfile.format == camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP) {
272          previewProfile = tempProfile;
273          break;
274        }
275      }
276      this.imageWidth = previewProfile.size.width; // Update the width of the XComponent.
277      this.imageHeight = previewProfile.size.height; // Update the height of the XComponent.
278      console.info(`initCamera imageWidth:${this.imageWidth} imageHeight:${this.imageHeight}`);
279
280      // Create a preview using xComponentSurfaceId.
281      this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId);
282      if (!this.previewOutput) {
283        console.error('initCamera createPreviewOutput');
284      }
285      // Create a camera session in recording mode.
286      this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
287      if (!this.session) {
288        console.error('initCamera createSession');
289      }
290      // Start configuration for the session.
291      this.session.beginConfig();
292      // Add a camera input.
293      this.session.addInput(this.cameraInput);
294      // Add a preview output stream.
295      this.session.addOutput(this.previewOutput);
296      // Commit the session configuration.
297      await this.session.commitConfig();
298      // Start the configured input and output streams.
299      await this.session.start();
300    } catch (error) {
301      console.error(`initCamera fail: ${error}`);
302    }
303  }
304
305
306  // Release the camera.
307  async releaseCamera(): Promise<void> {
308    console.info('releaseCamera E');
309    try {
310      // Stop the session.
311      await this.session?.stop();
312      // Release the camera input stream.
313      await this.cameraInput?.close();
314      // Release the preview output stream.
315      await this.previewOutput?.release();
316      // Release the session.
317      await this.session?.release();
318    } catch (error) {
319      console.error(`initCamera fail: ${error}`);
320    }
321  }
322}
323```
324