• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 录像实践(ArkTS)
2<!--Kit: Camera Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @qano-->
5<!--Designer: @leo_ysl-->
6<!--Tester: @xchaosioda-->
7<!--Adviser: @zengyawen-->
8
9在开发相机应用时,需要先[申请相关权限](camera-preparation.md)。
10
11当前示例提供完整的录像流程介绍,方便开发者了解完整的接口调用顺序。
12
13在参考以下示例前,建议开发者查看[相机开发指导(ArkTS)](camera-device-management.md)的具体章节,了解[设备输入](camera-device-input.md)、[会话管理](camera-session-management.md)、[录像](camera-recording.md)等单个流程。
14
15如需要将视频保存到媒体库中可参考[保存媒体库资源](../medialibrary/photoAccessHelper-savebutton.md)。
16## 开发流程
17
18在获取到相机支持的输出流能力后,开始创建录像流,开发流程如下。
19
20![Recording Development Process](figures/recording-development-process.png)
21
22
23## 完整示例
24Context获取方式请参考:[获取UIAbility的上下文信息](../../application-models/uiability-usage.md#获取uiability的上下文信息)。
25
26```ts
27import { camera } from '@kit.CameraKit';
28import { BusinessError } from '@kit.BasicServicesKit';
29import { media } from '@kit.MediaKit';
30import { common } from '@kit.AbilityKit';
31import { fileIo as fs } from '@kit.CoreFileKit';
32
33async function videoRecording(context: common.Context, surfaceId: string): Promise<void> {
34  // 创建CameraManager对象。
35  let cameraManager: camera.CameraManager = camera.getCameraManager(context);
36  if (!cameraManager) {
37    console.error("camera.getCameraManager error");
38    return;
39  }
40
41  // 监听相机状态变化。
42  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
43    if (err !== undefined && err.code !== 0) {
44      console.error('cameraStatus with errorCode = ' + err.code);
45      return;
46    }
47    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
48    console.info(`status: ${cameraStatusInfo.status}`);
49  });
50
51  // 获取相机列表。
52  let cameraArray: Array<camera.CameraDevice> = [];
53  try {
54    cameraArray = cameraManager.getSupportedCameras();
55  } catch (error) {
56    let err = error as BusinessError;
57    console.error(`getSupportedCameras call failed. error code: ${err.code}`);
58  }
59
60  if (cameraArray.length <= 0) {
61    console.error("cameraManager.getSupportedCameras error");
62    return;
63  }
64
65  // 获取支持的模式类型。
66  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
67  let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0;
68  if (!isSupportVideoMode) {
69    console.error('video mode not support');
70    return;
71  }
72
73  // 获取相机设备支持的输出流能力。
74  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
75  if (!cameraOutputCap) {
76    console.error("cameraManager.getSupportedOutputCapability error")
77    return;
78  }
79  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
80
81  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
82  if (!previewProfilesArray) {
83    console.error("createOutput previewProfilesArray == null || undefined");
84  }
85
86  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
87  if (!photoProfilesArray) {
88    console.error("createOutput photoProfilesArray == null || undefined");
89  }
90
91  let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
92  if (!videoProfilesArray || videoProfilesArray.length === 0) {
93    console.error("createOutput videoProfilesArray == null || undefined");
94  }
95
96  // videoProfile的宽高需要与AVRecorderProfile的宽高保持一致,并且需要使用AVRecorderProfile所支持的宽高。
97  // 示例代码默认选择第一个videoProfile,实际开发需根据所需筛选videoProfile。
98  let videoProfile: camera.VideoProfile = videoProfilesArray[0];
99  let isHdr = videoProfile.format === camera.CameraFormat.CAMERA_FORMAT_YCBCR_P010 || videoProfile.format === camera.CameraFormat.CAMERA_FORMAT_YCRCB_P010;
100  // 配置参数以实际硬件设备支持的范围为准。
101  let aVRecorderProfile: media.AVRecorderProfile = {
102    audioBitrate: 48000,
103    audioChannels: 2,
104    audioCodec: media.CodecMimeType.AUDIO_AAC,
105    audioSampleRate: 48000,
106    fileFormat: media.ContainerFormatType.CFT_MPEG_4,
107    videoBitrate: 2000000,
108    videoCodec: isHdr ? media.CodecMimeType.VIDEO_HEVC : media.CodecMimeType.VIDEO_AVC,
109    videoFrameWidth: videoProfile.size.width,
110    videoFrameHeight: videoProfile.size.height,
111    videoFrameRate: 30,
112    isHdr: isHdr
113  };
114
115  let avMetadata: media.AVMetadata = {
116    videoOrientation: '0', // 合理值0、90、180、270,非合理值prepare接口将报错。
117    location: { latitude: 30, longitude: 130 }
118  }
119  let videoUri: string = context.filesDir + '/' + 'VIDEO_' + Date.parse(new Date().toString()) + '.mp4'; // 本地沙箱路径。
120  let file: fs.File = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
121  let aVRecorderConfig: media.AVRecorderConfig = {
122    audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
123    videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
124    profile: aVRecorderProfile,
125    url: `fd://${file.fd.toString()}`, // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4
126    metadata: avMetadata
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; // 该surfaceID用于传递给相机接口创造videoOutput。
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  // 创建VideoOutput对象。
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: ${err}`);
165  }
166  if (videoOutput === undefined) {
167    return;
168  }
169  // 监听视频输出错误信息。
170  videoOutput.on('error', (error: BusinessError) => {
171    console.error(`Preview output error code: ${error.code}`);
172  });
173
174  //创建会话。
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: ${err}`);
181  }
182  if (videoSession === undefined) {
183    return;
184  }
185  // 监听session错误信息。
186  videoSession.on('error', (error: BusinessError) => {
187    console.error(`Video session error code: ${error.code}`);
188  });
189
190  // 开始配置会话。
191  try {
192    videoSession.beginConfig();
193  } catch (error) {
194    let err = error as BusinessError;
195    console.error(`Failed to beginConfig. error: ${err}`);
196  }
197
198  // 创建相机输入流。
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: ${err}`);
205  }
206  if (cameraInput === undefined) {
207    return;
208  }
209  // 监听cameraInput错误信息。
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  // 打开相机。
216  try {
217    await cameraInput.open();
218  } catch (error) {
219    let err = error as BusinessError;
220    console.error(`Failed to open cameraInput. error: ${err}`);
221  }
222
223  // 向会话中添加相机输入流。
224  try {
225    videoSession.addInput(cameraInput);
226  } catch (error) {
227    let err = error as BusinessError;
228    console.error(`Failed to add cameraInput. error: ${err}`);
229  }
230
231  // 创建预览输出流,预览流为XComponent组件提供的surface。
232  let previewOutput: camera.PreviewOutput | undefined = undefined;
233  let previewProfile = previewProfilesArray.find((previewProfile: camera.Profile) => {
234    return Math.abs((previewProfile.size.width / previewProfile.size.height) - (videoProfile.size.width / videoProfile.size.height)) < Number.EPSILON;
235  }); // 筛选与录像分辨率宽高比一致的预览分辨率。
236  if (previewProfile === undefined) {
237    return;
238  }
239  try {
240    previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId);
241  } catch (error) {
242    let err = error as BusinessError;
243    console.error(`Failed to create the PreviewOutput instance. error: ${err}`);
244  }
245  if (previewOutput === undefined) {
246    return;
247  }
248
249  // 向会话中添加预览输出流。
250  try {
251    videoSession.addOutput(previewOutput);
252  } catch (error) {
253    let err = error as BusinessError;
254    console.error(`Failed to add previewOutput. error: ${err}`);
255  }
256
257  // 向会话中添加录像输出流。
258  try {
259    videoSession.addOutput(videoOutput);
260  } catch (error) {
261    let err = error as BusinessError;
262    console.error(`Failed to add videoOutput. error: ${err}`);
263  }
264
265  // 提交会话配置。
266  try {
267    await videoSession.commitConfig();
268  } catch (error) {
269    let err = error as BusinessError;
270    console.error(`videoSession commitConfig error: ${err}`);
271    return;
272  }
273
274  // 启动会话。
275  try {
276    await videoSession.start();
277  } catch (error) {
278    let err = error as BusinessError;
279    console.error(`videoSession start error: ${err}`);
280  }
281
282  // 启动录像输出流。
283  videoOutput.start((err: BusinessError) => {
284    if (err) {
285      console.error(`Failed to start the video output. error: ${err}`);
286      return;
287    }
288    console.info('Callback invoked to indicate the video output start success.');
289  });
290
291  // 开始录像。
292  try {
293    await avRecorder.start();
294  } catch (error) {
295    let err = error as BusinessError;
296    console.error(`avRecorder start error: ${err}`);
297  }
298
299  // 停止录像输出流。
300  videoOutput.stop((err: BusinessError) => {
301    if (err) {
302      console.error(`Failed to stop the video output. error: ${err}`);
303      return;
304    }
305    console.info('Callback invoked to indicate the video output stop success.');
306  });
307
308  // 停止录像。
309  try {
310    await avRecorder.stop();
311  } catch (error) {
312    let err = error as BusinessError;
313    console.error(`avRecorder stop error: ${err}`);
314  }
315
316  // 停止当前会话。
317  await videoSession.stop();
318
319  // 关闭文件。
320  try {
321    fs.closeSync(file);
322  } catch (error) {
323    let err = error as BusinessError;
324    console.error(`closeSync failed, error: ${err}`);
325  }
326
327
328  // 释放相机输入流。
329  await cameraInput.close();
330
331  // 释放预览输出流。
332  try {
333    await previewOutput.release();
334  } catch (error) {
335    let err = error as BusinessError;
336    console.error(`release previewOutput failed, error: ${err.code}`);
337  }
338
339
340  // 释放录像输出流。
341  try {
342    await videoOutput.release();
343  } catch (error) {
344    let err = error as BusinessError;
345    console.error(`release videoOutput failed, error: ${err.code}`);
346  }
347
348  // 释放会话。
349  try {
350    await videoSession.release();
351  } catch (error) {
352    let err = error as BusinessError;
353    console.error(`release videoSession failed, error: ${err.code}`);
354  }
355
356  // 会话置空。
357  videoSession = undefined;
358}
359```
360