• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 录像实现方案(ArkTS)
2
3在开发相机应用时,需要先申请相机相关权限[开发准备](camera-preparation.md)。
4当前示例提供完整的录像流程介绍,方便开发者了解完整的接口调用顺序。
5
6在参考以下示例前,建议开发者查看[相机开发指导(ArkTS)](camera-preparation.md)的具体章节,了解[设备输入](camera-device-input.md)、[会话管理](camera-session-management.md)、[录像](camera-recording.md)等单个流程。
7
8## 开发流程
9
10在获取到相机支持的输出流能力后,开始创建录像流,开发流程如下。
11
12![Recording Development Process](figures/recording-development-process.png)
13
14
15## 完整示例
16Context获取方式请参考:[获取UIAbility的上下文信息](../../application-models/uiability-usage.md#获取uiability的上下文信息)。
17
18```ts
19import { camera } from '@kit.CameraKit';
20import { BusinessError } from '@kit.BasicServicesKit';
21import { media } from '@kit.MediaKit';
22import { common } from '@kit.AbilityKit';
23import { photoAccessHelper } from '@kit.MediaLibraryKit';
24import { fileIo as fs } from '@kit.CoreFileKit';
25
26async function videoRecording(context: common.Context, surfaceId: string): Promise<void> {
27  // 创建CameraManager对象
28  let cameraManager: camera.CameraManager = camera.getCameraManager(context);
29  if (!cameraManager) {
30    console.error("camera.getCameraManager error");
31    return;
32  }
33
34  // 监听相机状态变化
35  cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
36    if (err !== undefined && err.code !== 0) {
37      console.error('cameraStatus with errorCode = ' + err.code);
38      return;
39    }
40    console.info(`camera : ${cameraStatusInfo.camera.cameraId}`);
41    console.info(`status: ${cameraStatusInfo.status}`);
42  });
43
44  // 获取相机列表
45  let cameraArray: Array<camera.CameraDevice> = [];
46  try {
47    cameraArray = cameraManager.getSupportedCameras();
48  } catch (error) {
49    let err = error as BusinessError;
50    console.error(`getSupportedCameras call failed. error code: ${err.code}`);
51  }
52
53  if (cameraArray.length <= 0) {
54    console.error("cameraManager.getSupportedCameras error");
55    return;
56  }
57
58  // 获取支持的模式类型
59  let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]);
60  let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0;
61  if (!isSupportVideoMode) {
62    console.error('video mode not support');
63    return;
64  }
65
66  // 获取相机设备支持的输出流能力
67  let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO);
68  if (!cameraOutputCap) {
69    console.error("cameraManager.getSupportedOutputCapability error")
70    return;
71  }
72  console.info("outputCapability: " + JSON.stringify(cameraOutputCap));
73
74  let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
75  if (!previewProfilesArray) {
76    console.error("createOutput previewProfilesArray == null || undefined");
77  }
78
79  let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
80  if (!photoProfilesArray) {
81    console.error("createOutput photoProfilesArray == null || undefined");
82  }
83
84  let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles;
85  if (!videoProfilesArray) {
86    console.error("createOutput videoProfilesArray == null || undefined");
87  }
88  // videoProfile的宽高需要与AVRecorderProfile的宽高保持一致,并且需要使用AVRecorderProfile锁支持的宽高
89  let videoSize: camera.Size = {
90    width: 640,
91    height: 480
92  }
93  let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => {
94    return profile.size.width === videoSize.width && profile.size.height === videoSize.height;
95  });
96  if (!videoProfile) {
97    console.error('videoProfile is not found');
98    return;
99  }
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: media.CodecMimeType.VIDEO_AVC,
109    videoFrameWidth: videoSize.width,
110    videoFrameHeight: videoSize.height,
111    videoFrameRate: 30
112  };
113  let options: photoAccessHelper.CreateOptions = {
114    title: Date.now().toString()
115  };
116  let accessHelper: photoAccessHelper.PhotoAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
117  let videoUri: string = await accessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options);
118  let file: fs.File = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
119  let aVRecorderConfig: media.AVRecorderConfig = {
120    audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
121    videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
122    profile: aVRecorderProfile,
123    url: `fd://${file.fd.toString()}`, // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4
124    rotation: 0, // 合理值0、90、180、270,非合理值prepare接口将报错
125    location: { latitude: 30, longitude: 130 }
126  };
127
128  let avRecorder: media.AVRecorder | undefined = undefined;
129  try {
130    avRecorder = await media.createAVRecorder();
131  } catch (error) {
132    let err = error as BusinessError;
133    console.error(`createAVRecorder call failed. error code: ${err.code}`);
134  }
135
136  if (avRecorder === undefined) {
137    return;
138  }
139
140  try {
141    await avRecorder.prepare(aVRecorderConfig);
142  } catch (error) {
143    let err = error as BusinessError;
144    console.error(`prepare call failed. error code: ${err.code}`);
145  }
146
147  let videoSurfaceId: string | undefined = undefined; // 该surfaceID用于传递给相机接口创造videoOutput
148  try {
149    videoSurfaceId = await avRecorder.getInputSurface();
150  } catch (error) {
151    let err = error as BusinessError;
152    console.error(`getInputSurface call failed. error code: ${err.code}`);
153  }
154  if (videoSurfaceId === undefined) {
155    return;
156  }
157  // 创建VideoOutput对象
158  let videoOutput: camera.VideoOutput | undefined = undefined;
159  try {
160    videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);
161  } catch (error) {
162    let err = error as BusinessError;
163    console.error(`Failed to create the videoOutput instance. error: ${JSON.stringify(err)}`);
164  }
165  if (videoOutput === undefined) {
166    return;
167  }
168  // 监听视频输出错误信息
169  videoOutput.on('error', (error: BusinessError) => {
170    console.error(`Preview output error code: ${error.code}`);
171  });
172
173  //创建会话
174  let videoSession: camera.VideoSession | undefined = undefined;
175  try {
176    videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
177  } catch (error) {
178    let err = error as BusinessError;
179    console.error(`Failed to create the session instance. error: ${JSON.stringify(err)}`);
180  }
181  if (videoSession === undefined) {
182    return;
183  }
184  // 监听session错误信息
185  videoSession.on('error', (error: BusinessError) => {
186    console.error(`Video session error code: ${error.code}`);
187  });
188
189  // 开始配置会话
190  try {
191    videoSession.beginConfig();
192  } catch (error) {
193    let err = error as BusinessError;
194    console.error(`Failed to beginConfig. error: ${JSON.stringify(err)}`);
195  }
196
197  // 创建相机输入流
198  let cameraInput: camera.CameraInput | undefined = undefined;
199  try {
200    cameraInput = cameraManager.createCameraInput(cameraArray[0]);
201  } catch (error) {
202    let err = error as BusinessError;
203    console.error(`Failed to createCameraInput. error: ${JSON.stringify(err)}`);
204  }
205  if (cameraInput === undefined) {
206    return;
207  }
208  // 监听cameraInput错误信息
209  let cameraDevice: camera.CameraDevice = cameraArray[0];
210  cameraInput.on('error', cameraDevice, (error: BusinessError) => {
211    console.error(`Camera input error code: ${error.code}`);
212  });
213
214  // 打开相机
215  try {
216    await cameraInput.open();
217  } catch (error) {
218    let err = error as BusinessError;
219    console.error(`Failed to open cameraInput. error: ${JSON.stringify(err)}`);
220  }
221
222  // 向会话中添加相机输入流
223  try {
224    videoSession.addInput(cameraInput);
225  } catch (error) {
226    let err = error as BusinessError;
227    console.error(`Failed to add cameraInput. error: ${JSON.stringify(err)}`);
228  }
229
230  // 创建预览输出流,其中参数 surfaceId 参考下面 XComponent 组件,预览流为XComponent组件提供的surface
231  let previewOutput: camera.PreviewOutput | undefined = undefined;
232  try {
233    previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
234  } catch (error) {
235    let err = error as BusinessError;
236    console.error(`Failed to create the PreviewOutput instance. error: ${JSON.stringify(err)}`);
237  }
238
239  if (previewOutput === undefined) {
240    return;
241  }
242  // 向会话中添加预览输出流
243  try {
244    videoSession.addOutput(previewOutput);
245  } catch (error) {
246    let err = error as BusinessError;
247    console.error(`Failed to add previewOutput. error: ${JSON.stringify(err)}`);
248  }
249
250  // 向会话中添加录像输出流
251  try {
252    videoSession.addOutput(videoOutput);
253  } catch (error) {
254    let err = error as BusinessError;
255    console.error(`Failed to add videoOutput. error: ${JSON.stringify(err)}`);
256  }
257
258  // 提交会话配置
259  try {
260    await videoSession.commitConfig();
261  } catch (error) {
262    let err = error as BusinessError;
263    console.error(`videoSession commitConfig error: ${JSON.stringify(err)}`);
264  }
265
266  // 启动会话
267  try {
268    await videoSession.start();
269  } catch (error) {
270    let err = error as BusinessError;
271    console.error(`videoSession start error: ${JSON.stringify(err)}`);
272  }
273
274  // 启动录像输出流
275  videoOutput.start((err: BusinessError) => {
276    if (err) {
277      console.error(`Failed to start the video output. error: ${JSON.stringify(err)}`);
278      return;
279    }
280    console.info('Callback invoked to indicate the video output start success.');
281  });
282
283  // 开始录像
284  try {
285    await avRecorder.start();
286  } catch (error) {
287    let err = error as BusinessError;
288    console.error(`avRecorder start error: ${JSON.stringify(err)}`);
289  }
290
291  // 停止录像输出流
292  videoOutput.stop((err: BusinessError) => {
293    if (err) {
294      console.error(`Failed to stop the video output. error: ${JSON.stringify(err)}`);
295      return;
296    }
297    console.info('Callback invoked to indicate the video output stop success.');
298  });
299
300  // 停止录像
301  try {
302    await avRecorder.stop();
303  } catch (error) {
304    let err = error as BusinessError;
305    console.error(`avRecorder stop error: ${JSON.stringify(err)}`);
306  }
307
308  // 停止当前会话
309  await videoSession.stop();
310
311  // 关闭文件
312  fs.closeSync(file);
313
314  // 释放相机输入流
315  await cameraInput.close();
316
317  // 释放预览输出流
318  await previewOutput.release();
319
320  // 释放录像输出流
321  await videoOutput.release();
322
323  // 释放会话
324  await videoSession.release();
325
326  // 会话置空
327  videoSession = undefined;
328}
329```
330