• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2025 Huawei Device 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 userFileManager from '@ohos.filemanagement.userFileManager';
22import Logger from '../model/Logger';
23import MediaUtils, { FileType } from '../model/MediaUtils';
24import { Size } from '@kit.ArkUI';
25
26const CameraMode = {
27  MODE_PHOTO: 0, // 拍照模式
28  MODE_VIDEO: 1 // 录像模式
29}
30
31const CameraSize = {
32  WIDTH: 1920,
33  HEIGHT: 1080
34}
35
36export default class CameraService {
37  private tag: string = 'CameraService';
38  private context: any = undefined;
39  private mediaUtil: MediaUtils = undefined;
40  private cameraManager: camera.CameraManager = undefined;
41  private cameras: Array<camera.CameraDevice> = undefined;
42  private cameraId: string = '';
43  private cameraInput: camera.CameraInput = undefined;
44  private previewOutput: camera.PreviewOutput = undefined;
45  private photoOutPut: camera.PhotoOutput = undefined;
46  private captureSession: camera.CaptureSession = undefined;
47  private mReceiver: image.ImageReceiver = undefined;
48  private photoUri: string = '';
49  private fileAsset: userFileManager.FileAsset = undefined;
50  private fd: number = -1;
51  private curMode = CameraMode.MODE_PHOTO;
52  private videoRecorder: media.VideoRecorder = undefined;
53  private videoOutput: camera.VideoOutput = undefined;
54  private handleTakePicture: (photoUri: string) => void = undefined;
55  private cameraOutputCapability: camera.CameraOutputCapability = undefined;
56  private previewSize: Size = undefined;
57  private videoConfig: any = {
58    audioSourceType: 1,
59    videoSourceType: 0,
60    profile: {
61      audioBitrate: 48000,
62      audioChannels: 2,
63      audioCodec: 'audio/mp4v-es',
64      audioSampleRate: 48000,
65      durationTime: 1000,
66      fileFormat: 'mp4',
67      videoBitrate: 48000,
68      videoCodec: 'video/mp4v-es',
69      videoFrameWidth: 640,
70      videoFrameHeight: 480,
71      videoFrameRate: 30
72    },
73    url: '',
74    orientationHint: 0,
75    location: {
76      latitude: 30, longitude: 130
77    },
78    maxSize: 10000,
79    maxDuration: 10000
80  };
81
82  constructor(context: any) {
83    this.context = context
84    this.mediaUtil = MediaUtils.getInstance(context)
85    this.mReceiver = image.createImageReceiver(CameraSize.WIDTH, CameraSize.HEIGHT, 4, 8)
86    Logger.info(this.tag, 'createImageReceiver')
87    this.mReceiver.on('imageArrival', () => {
88      Logger.info(this.tag, 'imageArrival')
89      this.mReceiver.readNextImage((err, image) => {
90        Logger.info(this.tag, 'readNextImage')
91        if (err || image === undefined) {
92          Logger.error(this.tag, 'failed to get valid image')
93          return
94        }
95        image.getComponent(4, (errMsg, img) => {
96          Logger.info(this.tag, 'getComponent')
97          if (errMsg || img === undefined) {
98            Logger.info(this.tag, 'failed to get valid buffer')
99            return
100          }
101          let buffer = new ArrayBuffer(4096)
102          if (img.byteBuffer) {
103            buffer = img.byteBuffer
104          } else {
105            Logger.error(this.tag, 'img.byteBuffer is undefined')
106          }
107          this.savePicture(buffer, image)
108        })
109      })
110    })
111  }
112
113  async savePicture(buffer: ArrayBuffer, img: image.Image) {
114    Logger.info(this.tag, 'savePicture')
115    this.fileAsset = await this.mediaUtil.createAndGetUri(FileType.IMAGE)
116    this.photoUri = this.fileAsset.uri
117    Logger.info(this.tag, `this.photoUri = ${this.photoUri}`)
118    this.fd = await this.mediaUtil.getFdPath(this.fileAsset)
119    Logger.info(this.tag, `this.fd = ${this.fd}`)
120    await fileio.write(this.fd, buffer)
121    await this.fileAsset.close(this.fd)
122    await img.release()
123    Logger.info(this.tag, 'save image done')
124    if (this.handleTakePicture) {
125      this.handleTakePicture(this.photoUri)
126    }
127  }
128
129  async initCamera(surfaceId: string) {
130    Logger.info(this.tag, 'initCamera');
131    await this.releaseCamera();
132    Logger.info(this.tag, `deviceInfo.deviceType = ${deviceInfo.deviceType}`);
133    if (deviceInfo.deviceType === 'default') {
134      this.videoConfig.videoSourceType = 1;
135    } else {
136      this.videoConfig.videoSourceType = 0;
137    }
138    this.cameraManager = camera.getCameraManager(this.context);
139    Logger.info(this.tag, 'getCameraManager');
140    this.cameras = this.cameraManager.getSupportedCameras();
141    Logger.info(this.tag, `get cameras ${this.cameras.length}`);
142    if (this.cameras.length === 0) {
143      Logger.info(this.tag, 'cannot get cameras');
144      return;
145    }
146
147    let cameraDevice = this.cameras[0];
148    this.cameraInput = this.cameraManager.createCameraInput(cameraDevice);
149    this.cameraInput.open();
150    Logger.info(this.tag, 'createCameraInput');
151    this.cameraOutputCapability = this.cameraManager.getSupportedOutputCapability(cameraDevice, camera.SceneMode.NORMAL_VIDEO);
152    let previewProfile = this.cameraOutputCapability.previewProfiles[0];
153    this.previewSize = previewProfile.size;
154    this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId);
155    Logger.info(this.tag, 'createPreviewOutput');
156    let mSurfaceId = await this.mReceiver.getReceivingSurfaceId();
157    let profile = this.cameraOutputCapability.photoProfiles[0];
158    this.photoOutPut = this.cameraManager.createPhotoOutput(profile, mSurfaceId);
159    this.captureSession = this.cameraManager.createCaptureSession();
160    Logger.info(this.tag, 'createCaptureSession');
161    this.captureSession.beginConfig();
162    Logger.info(this.tag, 'beginConfig');
163    this.captureSession.addInput(this.cameraInput);
164    this.captureSession.addOutput(this.previewOutput);
165    this.captureSession.addOutput(this.photoOutPut);
166    await this.captureSession.commitConfig();
167    await this.captureSession.start();
168    Logger.info(this.tag, 'captureSession start');
169  }
170
171  setTakePictureCallback(callback) {
172    this.handleTakePicture = callback
173  }
174
175  async takePicture() {
176    Logger.info(this.tag, 'takePicture')
177    if (this.curMode === CameraMode.MODE_VIDEO) {
178      this.curMode = CameraMode.MODE_PHOTO
179    }
180    let photoSettings = {
181      rotation: camera.ImageRotation.ROTATION_0,
182      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
183      location: { // 位置信息,经纬度
184        latitude: 12.9698,
185        longitude: 77.7500,
186        altitude: 1000
187      },
188      mirror: false
189    }
190    await this.photoOutPut.capture(photoSettings)
191    Logger.info(this.tag, 'takePicture done')
192  }
193
194  async startVideo() {
195    Logger.info(this.tag, 'startVideo begin');
196    await this.captureSession.stop();
197    this.captureSession.beginConfig();
198    if (this.curMode === CameraMode.MODE_PHOTO) {
199      this.curMode = CameraMode.MODE_VIDEO;
200      if (this.photoOutPut) {
201        try {
202          this.captureSession.removeOutput(this.photoOutPut);
203          this.photoOutPut.release();
204        } catch (err) {
205          Logger.error(this.tag, 'remove photoOutPut error: ' + JSON.stringify(err));
206        }
207      }
208    } else {
209      if (this.videoOutput) {
210        try {
211          this.captureSession.removeOutput(this.videoOutput);
212        } catch (err) {
213          Logger.error(this.tag, 'remove videoOutput error: ' + JSON.stringify(err));
214        }
215      }
216    }
217    if (this.videoOutput) {
218      try {
219        this.captureSession.removeOutput(this.videoOutput);
220        await this.videoOutput.release();
221      } catch (err) {
222        Logger.error(this.tag, 'remove videoOutput error: ' + JSON.stringify(err));
223      }
224    }
225    this.fileAsset = await this.mediaUtil.createAndGetUri(FileType.VIDEO);
226    this.fd = await this.mediaUtil.getFdPath(this.fileAsset);
227    this.videoRecorder = await media.createVideoRecorder();
228    this.videoConfig.url = `fd://${this.fd}`;
229    this.videoConfig.profile.videoFrameWidth = this.previewSize.width;
230    this.videoConfig.profile.videoFrameHeight = this.previewSize.height;
231    await this.videoRecorder.prepare(this.videoConfig);
232    let videoId = await this.videoRecorder.getInputSurface();
233    let profile = this.cameraOutputCapability.videoProfiles[0];
234    this.videoOutput = this.cameraManager.createVideoOutput(profile, videoId);
235    this.captureSession.addOutput(this.videoOutput);
236    try {
237      await this.captureSession.commitConfig();
238      await this.captureSession.start();
239      await this.videoOutput.start();
240      await this.videoRecorder.start();
241    } catch (err) {
242      Logger.error(this.tag, 'record start error: ' + JSON.stringify(err));
243    }
244    Logger.info(this.tag, 'startVideo end');
245  }
246
247  async stopVideo() {
248    Logger.info(this.tag, 'stopVideo called');
249    try {
250      await this.videoRecorder.stop();
251      await this.videoOutput.stop();
252      await this.videoRecorder.release();
253      await this.fileAsset.close(this.fd);
254    } catch (err) {
255      Logger.error(this.tag, 'stopVideo error: ' + JSON.stringify(err));
256    }
257  }
258
259  async releaseCamera() {
260    Logger.info(this.tag, 'releaseCamera');
261    try {
262      if (this.cameraInput) {
263        await this.cameraInput.close();
264      }
265      if (this.previewOutput) {
266        await this.previewOutput.release();
267      }
268      if (this.photoOutPut) {
269        await this.photoOutPut.release();
270      }
271      if (this.videoOutput) {
272        await this.videoOutput.release();
273      }
274      if (this.captureSession) {
275        await this.captureSession.release();
276      }
277    } catch (err) {
278      Logger.error(this.tag, 'releaseCamera error: ' + JSON.stringify(err));
279    }
280  }
281}