1# Video Recording Sample (ArkTS)
2
3Before developing a camera application, request permissions by following the instructions provided in [Camera Development Preparations](camera-preparation.md).
4
5This topic provides sample code that covers the complete recording process to help you understand the complete API calling sequence.
6
7Before referring to the sample code, you are advised to read [Device Input Management](camera-device-input.md), [Camera Session Management](camera-session-management.md), [Video Recording](camera-recording.md), and other related topics in [Camera Development (ArkTS)](camera-preparation.md).
8
9## Development Process
10
11After obtaining the output stream capabilities supported by the camera, create a video stream. The development process is as follows:
12
13![Recording Development Process](figures/recording-development-process.png)
14
15
16## Sample Code
17For details about how to obtain the context, see [Obtaining the Context of UIAbility](../../application-models/uiability-usage.md#obtaining-the-context-of-uiability).
18
19```ts
20import { camera } from '@kit.CameraKit';
21import { BusinessError } from '@kit.BasicServicesKit';
22import { media } from '@kit.MediaKit';
23import { common } from '@kit.AbilityKit';
24import { photoAccessHelper } from '@kit.MediaLibraryKit';
25import { fileIo as fs } from '@kit.CoreFileKit';
26
27async function videoRecording(context: common.Context, surfaceId: string): Promise<void> {
28  // Create a CameraManager instance.
29  let cameraManager: camera.CameraManager = camera.getCameraManager(context);
30  if (!cameraManager) {
31    console.error("camera.getCameraManager error");
32    return;
33  }
34
35  // Listen for camera status changes.
36  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
37    if (err !== undefined && err.code !== 0) {
38      console.error('cameraStatus with errorCode = ' + err.code);
39      return;
40    }
41    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
42    console.info(`status: ${cameraStatusInfo.status}`);
43  });
44
45  // Obtain the camera list.
46  let cameraArray: Array<camera.CameraDevice> = [];
47  try {
48    cameraArray = cameraManager.getSupportedCameras();
49  } catch (error) {
50    let err = error as BusinessError;
51    console.error(`getSupportedCameras call failed. error code: ${err.code}`);
52  }
53
54  if (cameraArray.length <= 0) {
55    console.error("cameraManager.getSupportedCameras error");
56    return;
57  }
58
59  // Obtain the supported modes.
60  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
61  let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0;
62  if (!isSupportVideoMode) {
63    console.error('video mode not support');
64    return;
65  }
66
67  // Obtain the output stream capabilities supported by the camera.
68  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
69  if (!cameraOutputCap) {
70    console.error("cameraManager.getSupportedOutputCapability error")
71    return;
72  }
73  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
74
75  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
76  if (!previewProfilesArray) {
77    console.error("createOutput previewProfilesArray == null || undefined");
78  }
79
80  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
81  if (!photoProfilesArray) {
82    console.error("createOutput photoProfilesArray == null || undefined");
83  }
84
85  let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
86  if (!videoProfilesArray) {
87    console.error("createOutput videoProfilesArray == null || undefined");
88  }
89  // The width and height of videoProfile must be the same as those of AVRecorderProfile.
90  let videoSize: camera.Size = {
91    width: 640,
92    height: 480
93  }
94  let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => {
95    return profile.size.width === videoSize.width && profile.size.height === videoSize.height;
96  });
97  if (!videoProfile) {
98    console.error('videoProfile is not found');
99    return;
100  }
101  // Configure the parameters based on those supported by the hardware device.
102  let aVRecorderProfile: media.AVRecorderProfile = {
103    audioBitrate: 48000,
104    audioChannels: 2,
105    audioCodec: media.CodecMimeType.AUDIO_AAC,
106    audioSampleRate: 48000,
107    fileFormat: media.ContainerFormatType.CFT_MPEG_4,
108    videoBitrate: 2000000,
109    videoCodec: media.CodecMimeType.VIDEO_AVC,
110    videoFrameWidth: videoSize.width,
111    videoFrameHeight: videoSize.height,
112    videoFrameRate: 30
113  };
114  let options: photoAccessHelper.CreateOptions = {
115    title: Date.now().toString()
116  };
117  let accessHelper: photoAccessHelper.PhotoAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
118  let videoUri: string = await accessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options);
119  let file: fs.File = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
120  let aVRecorderConfig: media.AVRecorderConfig = {
121    audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
122    videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
123    profile: aVRecorderProfile,
124    url: `fd://${file.fd.toString()}`, // Before passing in a file descriptor to this parameter, the file must be created by the caller and granted with the read and write permissions. Example value: fd://45--file:///data/media/01.mp4.
125    rotation: 0, // The value can be 0, 90, 180, or 270. If any other value is used, prepare() reports an error.
126    location: { latitude: 30, longitude: 130 }
127  };
128
129  let avRecorder: media.AVRecorder | undefined = undefined;
130  try {
131    avRecorder = await media.createAVRecorder();
132  } catch (error) {
133    let err = error as BusinessError;
134    console.error(`createAVRecorder call failed. error code: ${err.code}`);
135  }
136
137  if (avRecorder === undefined) {
138    return;
139  }
140
141  try {
142    await avRecorder.prepare(aVRecorderConfig);
143  } catch (error) {
144    let err = error as BusinessError;
145    console.error(`prepare call failed. error code: ${err.code}`);
146  }
147
148  let videoSurfaceId: string | undefined = undefined; // The surfaceID is passed in to the camera API to create a VideoOutput instance.
149  try {
150    videoSurfaceId = await avRecorder.getInputSurface();
151  } catch (error) {
152    let err = error as BusinessError;
153    console.error(`getInputSurface call failed. error code: ${err.code}`);
154  }
155  if (videoSurfaceId === undefined) {
156    return;
157  }
158  // Create a VideoOutput instance.
159  let videoOutput: camera.VideoOutput | undefined = undefined;
160  try {
161    videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
162  } catch (error) {
163    let err = error as BusinessError;
164    console.error(`Failed to create the videoOutput instance. error: ${JSON.stringify(err)}`);
165  }
166  if (videoOutput === undefined) {
167    return;
168  }
169  // Listen for video output errors.
170  videoOutput.on('error', (error: BusinessError) => {
171    console.error(`Preview output error code: ${error.code}`);
172  });
173
174  // Create a session.
175  let videoSession: camera.VideoSession | undefined = undefined;
176  try {
177    videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
178  } catch (error) {
179    let err = error as BusinessError;
180    console.error(`Failed to create the session instance. error: ${JSON.stringify(err)}`);
181  }
182  if (videoSession === undefined) {
183    return;
184  }
185  // Listen for session errors.
186  videoSession.on('error', (error: BusinessError) => {
187    console.error(`Video session error code: ${error.code}`);
188  });
189
190  // Start configuration for the session.
191  try {
192    videoSession.beginConfig();
193  } catch (error) {
194    let err = error as BusinessError;
195    console.error(`Failed to beginConfig. error: ${JSON.stringify(err)}`);
196  }
197
198  // Create a camera input stream.
199  let cameraInput: camera.CameraInput | undefined = undefined;
200  try {
201    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
202  } catch (error) {
203    let err = error as BusinessError;
204    console.error(`Failed to createCameraInput. error: ${JSON.stringify(err)}`);
205  }
206  if (cameraInput === undefined) {
207    return;
208  }
209  // Listen for camera input errors.
210  let cameraDevice: camera.CameraDevice = cameraArray[0];
211  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
212    console.error(`Camera input error code: ${error.code}`);
213  });
214
215  // Open the camera.
216  try {
217    await cameraInput.open();
218  } catch (error) {
219    let err = error as BusinessError;
220    console.error(`Failed to open cameraInput. error: ${JSON.stringify(err)}`);
221  }
222
223  // Add the camera input stream to the session.
224  try {
225    videoSession.addInput(cameraInput);
226  } catch (error) {
227    let err = error as BusinessError;
228    console.error(`Failed to add cameraInput. error: ${JSON.stringify(err)}`);
229  }
230
231  // Create a preview output stream. For details about the surfaceId parameter, see the XComponent. The preview stream is the surface provided by the XComponent.
232  let previewOutput: camera.PreviewOutput | undefined = undefined;
233  try {
234    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
235  } catch (error) {
236    let err = error as BusinessError;
237    console.error(`Failed to create the PreviewOutput instance. error: ${JSON.stringify(err)}`);
238  }
239
240  if (previewOutput === undefined) {
241    return;
242  }
243  // Add the preview output stream to the session.
244  try {
245    videoSession.addOutput(previewOutput);
246  } catch (error) {
247    let err = error as BusinessError;
248    console.error(`Failed to add previewOutput. error: ${JSON.stringify(err)}`);
249  }
250
251  // Add a video output stream to the session.
252  try {
253    videoSession.addOutput(videoOutput);
254  } catch (error) {
255    let err = error as BusinessError;
256    console.error(`Failed to add videoOutput. error: ${JSON.stringify(err)}`);
257  }
258
259  // Commit the session configuration.
260  try {
261    await videoSession.commitConfig();
262  } catch (error) {
263    let err = error as BusinessError;
264    console.error(`videoSession commitConfig error: ${JSON.stringify(err)}`);
265  }
266
267  // Start the session.
268  try {
269    await videoSession.start();
270  } catch (error) {
271    let err = error as BusinessError;
272    console.error(`videoSession start error: ${JSON.stringify(err)}`);
273  }
274
275  // Start the video output stream.
276  videoOutput.start((err: BusinessError) => {
277    if (err) {
278      console.error(`Failed to start the video output. error: ${JSON.stringify(err)}`);
279      return;
280    }
281    console.info('Callback invoked to indicate the video output start success.');
282  });
283
284  // Start video recording.
285  try {
286    await avRecorder.start();
287  } catch (error) {
288    let err = error as BusinessError;
289    console.error(`avRecorder start error: ${JSON.stringify(err)}`);
290  }
291
292  // Stop the video output stream.
293  videoOutput.stop((err: BusinessError) => {
294    if (err) {
295      console.error(`Failed to stop the video output. error: ${JSON.stringify(err)}`);
296      return;
297    }
298    console.info('Callback invoked to indicate the video output stop success.');
299  });
300
301  // Stop video recording.
302  try {
303    await avRecorder.stop();
304  } catch (error) {
305    let err = error as BusinessError;
306    console.error(`avRecorder stop error: ${JSON.stringify(err)}`);
307  }
308
309  // Stop the session.
310  await videoSession.stop();
311
312  // Close the files.
313  fs.closeSync(file);
314
315  // Release the camera input stream.
316  await cameraInput.close();
317
318  // Release the preview output stream.
319  await previewOutput.release();
320
321  // Release the video output stream.
322  await videoOutput.release();
323
324  // Release the session.
325  await videoSession.release();
326
327  // Set the session to null.
328  videoSession = undefined;
329}
330```
331