• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import camera from '@ohos.multimedia.camera';
17import deviceInfo from '@ohos.deviceInfo';
18import fileIo from '@ohos.file.fs';
19import image from '@ohos.multimedia.image';
20import media from '@ohos.multimedia.media';
21import photoAccessHelper from '@ohos.file.photoAccessHelper';
22import Logger from '../utlis/Logger';
23import MediaModel from './MediaModel';
24
25
26const FOUR = 4; // format
27const EIGHT = 8; // capacity
28const FOUR_THOUSAND_AND_SIXTY_NINE = 4096; // buffer大小
29
30const cameraMode = {
31  modePhoto: 0, // 拍照模式
32  modeVideo: 1, // 录像模式
33};
34
35const cameraWH = {
36  width: 480,
37  height: 360,
38};
39
40/**
41 * 分辨率
42 */
43export enum VideoFrame {
44  VIDEOFRAME_1920_1080,
45  VIDEOFRAME_1280_720,
46  VIDEOFRAME_800_600,
47};
48
49type VideoFrameWH = {
50  width: number;
51  height: number;
52};
53
54const TAG = '[CameraModel]';
55
56export default class CameraService {
57  private mediaModel: MediaModel = undefined;
58  private fileAsset: photoAccessHelper.PhotoAsset | undefined = undefined;
59  private cameraMgr: camera.CameraManager = undefined;
60  private camerasArray: Array<camera.CameraDevice> = undefined;
61  private cameraInput: camera.CameraInput = undefined;
62  private previewOutput: camera.PreviewOutput = undefined;
63  private photoOutPut: camera.PhotoOutput = undefined;
64  private capSession: camera.CaptureSession = undefined;
65  private videoOutput: camera.VideoOutput = undefined;
66  private capability: camera.CameraOutputCapability = undefined;
67
68  private avRecorder: media.AVRecorder = undefined;
69  private receiver: image.ImageReceiver = undefined;
70  private photoPath: string = '';
71  private fd: number = -1;
72  private takePictureHandle: (photoUri: string) => void = undefined;
73  private currentMode:number = cameraMode.modePhoto;
74
75  private videoFrameWH: VideoFrameWH = {
76    width: 480,
77    height: 360,
78  }; // 视频分辨率
79  private imageRotation: camera.ImageRotation = camera.ImageRotation.ROTATION_0; // 图片旋转角度
80
81  private videoProfile: media.VideoRecorderProfile = {
82    audioChannels: 2,
83    audioCodec: media.CodecMimeType.AUDIO_AAC,
84    audioBitrate: 48000,
85    audioSampleRate: 48000,
86    fileFormat: media.ContainerFormatType.CFT_MPEG_4,
87    videoBitrate: 48000,
88    videoCodec: media.CodecMimeType.VIDEO_AVC,
89    videoFrameWidth: 480,
90    videoFrameHeight: 360,
91    videoFrameRate: 30,
92  };
93  private videoSourceType: number = 0;
94
95  constructor() {
96    this.mediaModel = MediaModel.getMediaInstance();
97    this.receiver = image.createImageReceiver(
98      cameraWH.width,
99      cameraWH.height,
100      FOUR,
101      EIGHT
102    );
103    Logger.info(TAG, 'createImageReceiver');
104    this.receiver.on('imageArrival', () => {
105      Logger.info(TAG, 'imageArrival');
106      this.receiver.readNextImage((err, image) => {
107        Logger.info(TAG, 'readNextImage');
108        if (err || image === undefined) {
109          Logger.error(TAG, 'failed to get valid image');
110          return;
111        }
112        image.getComponent(FOUR, (errMsg, img) => {
113          Logger.info(TAG, 'getComponent');
114          if (errMsg || img === undefined) {
115            Logger.info(TAG, 'failed to get valid buffer');
116            return;
117          }
118          let buffer = new ArrayBuffer(FOUR_THOUSAND_AND_SIXTY_NINE);
119          if (img.byteBuffer) {
120            buffer = img.byteBuffer;
121          } else {
122            Logger.error(TAG, 'img.byteBuffer is undefined');
123          }
124          this.saveImage(buffer, image);
125        });
126      });
127    });
128  }
129
130  /**
131   * 设置分辨率
132   * @param videoFrame
133   */
134  setVideoFrameWH(videoFrame: VideoFrame): void {
135    switch (videoFrame) {
136      case VideoFrame.VIDEOFRAME_800_600:
137        this.videoFrameWH = {
138          width: 800,
139          height: 600,
140        };
141        Logger.info(
142          TAG,
143          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
144        );
145        break;
146      case VideoFrame.VIDEOFRAME_1280_720:
147        this.videoFrameWH = {
148          width: 1280,
149          height: 720,
150        };
151        Logger.info(
152          TAG,
153          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
154        );
155        break;
156      case VideoFrame.VIDEOFRAME_1920_1080:
157        this.videoFrameWH = {
158          width: 1920,
159          height: 1080,
160        };
161        Logger.info(
162          TAG,
163          `setVideoFrameWH videoFrameWH:${JSON.stringify(this.videoFrameWH)}`
164        );
165        break;
166    }
167  }
168  /**
169   * 设置图片旋转角度
170   * @param imageRotation
171   */
172  setImageRotation(imageRotation: camera.ImageRotation): void {
173    this.imageRotation = imageRotation;
174  }
175
176  /**
177   * 保存图片
178   * @param buffer
179   * @param img
180   */
181  async saveImage(buffer: ArrayBuffer, img: image.Image): Promise<void> {
182    Logger.info(TAG, 'savePicture');
183    this.fileAsset = await this.mediaModel.createAndGetUri(photoAccessHelper.PhotoType.IMAGE);
184    this.photoPath = this.fileAsset.uri;
185    Logger.info(TAG, `this.photoUri = ${this.photoPath}`);
186    this.fd = await this.mediaModel.getFdPath(this.fileAsset);
187    Logger.info(TAG, `this.fd = ${this.fd}`);
188    await fileIo.write(this.fd, buffer);
189    await this.fileAsset.close(this.fd);
190    await img.release();
191    Logger.info(TAG, 'save image done');
192    if (this.takePictureHandle) {
193      this.takePictureHandle(this.photoPath);
194    }
195  }
196
197  /**
198   * 初始化相机
199   * @param surfaceId
200   */
201  async initCamera(surfaceId: string): Promise<void> {
202    Logger.info(TAG, `initCamera surfaceId:${surfaceId}`);
203    await this.cameraRelease();
204    Logger.info(TAG, `deviceInfo.deviceType = ${deviceInfo.deviceType}`);
205    if (deviceInfo.deviceType === 'default') {
206      Logger.info(TAG, `deviceInfo.deviceType default 1 = ${deviceInfo.deviceType}`);
207      this.videoSourceType = 1;
208      Logger.info(TAG, `deviceInfo.deviceType default 2 = ${deviceInfo.deviceType}`);
209    } else {
210      Logger.info(TAG, `deviceInfo.deviceType other 1 = ${deviceInfo.deviceType}`);
211      this.videoSourceType = 0;
212      Logger.info(TAG, `deviceInfo.deviceType other 2 = ${deviceInfo.deviceType}`);
213    }
214    Logger.info(TAG, 'getCameraManager begin');
215    try {
216      Logger.info(TAG, 'getCameraManager try begin');
217      this.cameraMgr = camera.getCameraManager(globalThis.cameraContext);
218      Logger.info(TAG, 'getCameraManager try end');
219    } catch (e) {
220      Logger.info(TAG, `getCameraManager catch e:${JSON.stringify(e)}`);
221      Logger.info(TAG, `getCameraManager catch code:${JSON.stringify(e.code)}`);
222      Logger.info(TAG, `getCameraManager catch message:${JSON.stringify(e.message)}`);
223    }
224    Logger.info(TAG, 'getCameraManager end');
225    Logger.info(TAG, `getCameraManager ${JSON.stringify(this.cameraMgr)}`);
226    this.camerasArray = this.cameraMgr.getSupportedCameras();
227    Logger.info(TAG, `get cameras ${this.camerasArray.length}`);
228    if (this.camerasArray.length === 0) {
229      Logger.info(TAG, 'cannot get cameras');
230      return;
231    }
232
233    let mCamera = this.camerasArray[0];
234    this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
235    this.cameraInput.open();
236    Logger.info(TAG, 'createCameraInput');
237    this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera);
238    let previewProfile = this.capability.previewProfiles[0];
239    this.previewOutput = this.cameraMgr.createPreviewOutput(
240      previewProfile,
241      surfaceId
242    );
243    Logger.info(TAG, 'createPreviewOutput');
244    let rSurfaceId = await this.receiver.getReceivingSurfaceId();
245    let photoProfile = this.capability.photoProfiles[0];
246    this.photoOutPut = this.cameraMgr.createPhotoOutput(
247      photoProfile,
248      rSurfaceId
249    );
250    this.capSession = this.cameraMgr.createCaptureSession();
251    Logger.info(TAG, 'createCaptureSession');
252    this.capSession.beginConfig();
253    Logger.info(TAG, 'beginConfig');
254    this.capSession.addInput(this.cameraInput);
255    this.capSession.addOutput(this.previewOutput);
256    this.capSession.addOutput(this.photoOutPut);
257    await this.capSession.commitConfig();
258    await this.capSession.start();
259    Logger.info(TAG, 'captureSession start');
260  }
261
262  setTakePictureHandleCallback(callback): void {
263    this.takePictureHandle = callback;
264  }
265
266  /**
267   * 拍照
268   */
269  async takePicture(): Promise<void> {
270    Logger.info(TAG, 'takePicture');
271    if (this.currentMode === cameraMode.modeVideo) {
272      this.currentMode = cameraMode.modePhoto;
273    }
274    Logger.info(TAG, `takePicture imageRotation:${this.imageRotation}`);
275    let photoSettings = {
276      rotation: this.imageRotation,
277      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
278      location: {
279        // 位置信息,经纬度
280        latitude: 12.9698,
281        longitude: 77.75,
282        altitude: 1000,
283      },
284      mirror: false,
285    };
286    await this.photoOutPut.capture(photoSettings);
287    Logger.info(TAG, 'takePicture done');
288  }
289
290  /**
291   * 开始录像
292   */
293  async startVideo(): Promise<void> {
294    Logger.info(TAG, 'startVideo begin');
295    Logger.info(TAG, 'startVideo 1');
296    await this.capSession.stop();
297    Logger.info(TAG, 'startVideo 2');
298    this.capSession.beginConfig();
299    Logger.info(TAG, 'startVideo 3');
300    if (this.currentMode === cameraMode.modePhoto) {
301      this.currentMode = cameraMode.modeVideo;
302      if (this.photoOutPut) {
303        this.capSession.removeOutput(this.photoOutPut);
304        this.photoOutPut.release();
305      }
306    } else {
307      if (this.videoOutput) {
308        try {
309          Logger.info(TAG, 'startVideo 4');
310          this.capSession.removeOutput(this.videoOutput);
311          Logger.info(TAG, 'startVideo 5');
312        } catch (e) {
313          Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`);
314          Logger.info(TAG, `startVideo catch code:${JSON.stringify(e.code)}`);
315          Logger.info(TAG, `startVideo catch message:${JSON.stringify(e.message)}`);
316        }
317      }
318    }
319    if (this.videoOutput) {
320      try {
321        Logger.info(TAG, 'startVideo 6');
322        this.capSession.removeOutput(this.videoOutput);
323        Logger.info(TAG, 'startVideo 7');
324      } catch (e) {
325        Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`);
326        Logger.info(TAG, `startVideo catch code:${JSON.stringify(e.code)}`);
327        Logger.info(TAG, `startVideo catch message:${JSON.stringify(e.message)}`);
328      }
329      try {
330        Logger.info(TAG, 'startVideo release 1');
331        await this.videoOutput.release();
332        Logger.info(TAG, 'startVideo release 2');
333      } catch (e) {
334        Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`);
335        Logger.info(TAG, `startVideo catch code:${JSON.stringify(e.code)}`);
336        Logger.info(TAG, `startVideo catch message:${JSON.stringify(e.message)}`);
337      }
338    }
339    Logger.info(TAG, 'startVideo 8');
340    this.fileAsset = await this.mediaModel.createAndGetUri(photoAccessHelper.PhotoType.VIDEO);
341    Logger.info(TAG, `startVideo fileAsset:${this.fileAsset}`);
342    this.fd = await this.mediaModel.getFdPath(this.fileAsset);
343    Logger.info(TAG, `startVideo fd:${this.fd}`);
344    media.createAVRecorder(async (error, recorder) => {
345      Logger.info(TAG, `startVideo into createAVRecorder:${recorder}`);
346      if (recorder != null) {
347        Logger.info(TAG, `startVideo into recorder:${recorder}`);
348        this.avRecorder = recorder;
349        Logger.info(TAG, `startVideo createAVRecorder success:${this.avRecorder}`);
350        // 当前录像配置
351        let currConfig = {
352          audioSourceType: 1,
353          videoSourceType: this.videoSourceType,
354          profile: this.videoProfile,
355          url: `fd://${this.fd}`,
356          rotation: 0
357        };
358        Logger.info(TAG, `startVideo into recorder:${recorder}`);
359        await this.avRecorder.prepare(currConfig);
360        Logger.info(TAG, `startVideo videoConfig:${JSON.stringify(currConfig)}`);
361        let videoId = await this.avRecorder.getInputSurface();
362        let videoProfile = this.capability.videoProfiles[0];
363        Logger.info(TAG, `startVideo capability.videoProfiles[]=: ${JSON.stringify(this.capability.videoProfiles)}`);
364        Logger.info(TAG, `startVideo videoProfile:${JSON.stringify(videoProfile)}`);
365        this.videoOutput = this.cameraMgr.createVideoOutput(videoProfile, videoId);
366        Logger.info(TAG, `startVideo videoOutput:${this.videoOutput}`);
367        this.capSession.addOutput(this.videoOutput);
368        Logger.info(TAG, 'startVideo addOutput');
369        await this.capSession.commitConfig();
370        Logger.info(TAG, 'startVideo commitConfig');
371        await this.capSession.start();
372        Logger.info(TAG, 'startVideo commitConfig capSession');
373        await this.videoOutput.start();
374        Logger.info(TAG, 'startVideo commitConfig videoOutput');
375        try {
376          Logger.info(TAG, 'startVideo avRecorder.start 1');
377          await this.avRecorder.start();
378          Logger.info(TAG, 'startVideo avRecorder.start 2');
379        } catch (e) {
380          Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`);
381        }
382
383        Logger.info(TAG, 'startVideo end');
384
385      } else {
386        Logger.info(TAG, `startVideo createAVRecorder fail, error:${error}`);
387      }
388    });
389  }
390
391  /**
392   * 停止录像
393   */
394  async stopVideo(): Promise<void> {
395    Logger.info(TAG, 'stopVideo called');
396    await this.avRecorder.stop();
397    await this.avRecorder.release();
398    await this.videoOutput.stop();
399    await this.fileAsset.close(this.fd);
400  }
401
402  /**
403   * 资源释放
404   */
405  async cameraRelease(): Promise<void> {
406    Logger.info(TAG, 'releaseCamera');
407    if (this.cameraInput) {
408      await this.cameraInput.close();
409    }
410    if (this.previewOutput) {
411      await this.previewOutput.release();
412    }
413    if (this.photoOutPut) {
414      await this.photoOutPut.release();
415    }
416    if (this.videoOutput) {
417      await this.videoOutput.release();
418    }
419    if (this.capSession) {
420      await this.capSession.release();
421    }
422  }
423}
424