• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 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
16// Reverse camera_ Multiple workstations_ Take photos Video
17
18import DateTimeUtil from '../model/DateTimeUtil';
19import Logger from '../model/Logger';
20import cameraDemo from 'libentry.so';
21import mediaLibrary from '@ohos.multimedia.mediaLibrary';
22import image from '@ohos.multimedia.image';
23import media from '@ohos.multimedia.media';
24import MediaUtils from '../model/MediaUtils';
25import deviceInfo from '@ohos.deviceInfo';
26import fileio from '@ohos.fileio';
27import { SettingDataObj } from '../common/Constants'
28import common from '@ohos.app.ability.common'
29
30let context = getContext(this) as common.UIAbilityContext;
31
32interface CameraSize {
33  WIDTH: number,
34  HEIGHT: number
35};
36
37interface PhotoSettings {
38  quality: number, // Photo quality
39  rotation: number, // Photo direction
40  mirror: boolean, // Mirror Enable
41  latitude: number, // geographic location
42  longitude: number, // geographic location
43  altitude: number // geographic location
44};
45
46interface PhotoRotationMap {
47  rotation0: number,
48  rotation90: number,
49  rotation180: number,
50  rotation270: number,
51};
52
53@Component
54export struct modeSwitchPage {
55  private tag: string = 'sample modeSwitchPage:';
56  private mediaUtil = MediaUtils.getInstance();
57  private fileAsset?: mediaLibrary.FileAsset;
58  private fd: number = -1;
59  @State videoId: string = '';
60  @State mSurfaceId: string = '';
61  private cameraSize: CameraSize = {
62    WIDTH: 1280,
63    HEIGHT: 720
64  };
65  private photoSettings: PhotoSettings = {
66    quality: 0,
67    rotation: 0,
68    mirror: false,
69    latitude: 12.9698,
70    longitude: 77.7500,
71    altitude: 1000
72  };
73  private mReceiver?: image.ImageReceiver;
74  private videoRecorder?: media.AVRecorder;
75  private videoConfig: media.AVRecorderConfig = {
76    audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
77    videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
78    profile: {
79      audioBitrate: 48000,
80      audioChannels: 2,
81      audioCodec: media.CodecMimeType.AUDIO_AAC,
82      audioSampleRate: 48000,
83      fileFormat: media.ContainerFormatType.CFT_MPEG_4,
84      videoBitrate: 512000,
85      videoCodec: media.CodecMimeType.VIDEO_AVC,
86      videoFrameWidth: 640,
87      videoFrameHeight: 480,
88      videoFrameRate: 30
89    },
90    url: '',
91    rotation: 0
92  };
93  private photoRotationMap: PhotoRotationMap = {
94    rotation0: 0,
95    rotation90: 90,
96    rotation180: 180,
97    rotation270: 270,
98  };
99  // Front and rear cameras
100  @Link cameraDeviceIndex: number;
101  // SurfaceID
102  @Prop surfaceId: string;
103  // Countdown value
104  @Link countdownNum: number;
105  // Countdown timer
106  @State countTimerInt: number = -1;
107  @State countTimerOut: number = -1;
108  // Photo Thumbnails
109  @State imgThumbnail: string = '';
110  // Recording time
111  @State videoRecodeTime: number = 0;
112  // Recording time timer
113  @State timer: number = -1;
114  // Time Manager
115  @State dateTimeUtil: DateTimeUtil = new DateTimeUtil();
116  // Select mode
117  @State modelBagCol: string = 'photo';
118  // Choose camera or capture
119  @State @Watch('onChangeIsModeBol') isModeBol: boolean = true;
120  // Video Thumbnails
121  // private videoThumbnail?: image.PixelMap;
122  @State videoThumbnail: image.PixelMap | undefined | null = undefined;
123  private settingDataObj: SettingDataObj = {
124    mirrorBol: false,
125    videoStabilizationMode: 0,
126    exposureMode: 1,
127    focusMode: 2,
128    photoQuality: 1,
129    locationBol: false,
130    photoFormat: 1,
131    photoOrientation: 0,
132    photoResolution: 0,
133    videoResolution: 0,
134    videoFrame: 0,
135    referenceLineBol: false
136  };
137
138  // After pausing, click 'stop' to reset the pause to default
139  onChangeIsModeBol() {
140  }
141
142  // Countdown capture and video
143  countTakeVideoFn() {
144    if (this.countdownNum) {
145      // Clear Countdown
146      if (this.countTimerOut) {
147        clearTimeout(this.countTimerOut);
148      }
149      if (this.countTimerInt) {
150        clearInterval(this.countTimerInt);
151      }
152      // Turn on timer
153      this.countTimerOut = setTimeout(() => {
154        // Determine whether it is in video or photo mode
155        this.isVideoPhotoFn();
156      }, this.countdownNum * 1000)
157      // Turn on timer
158      this.countTimerInt = setInterval(() => {
159        this.countdownNum--;
160        if (this.countdownNum === 0) {
161          clearInterval(this.countTimerInt);
162        }
163      }, 1000)
164    } else {
165      this.isVideoPhotoFn();
166    }
167  }
168
169  async getVideoSurfaceID() {
170    Logger.info(this.tag, `getVideoSurfaceID`);
171    this.videoRecorder = await media.createAVRecorder();
172    Logger.info(this.tag, `getVideoSurfaceID videoRecorder: ${this.videoRecorder}`);
173
174    this.fileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.VIDEO);
175    Logger.info(this.tag, `getVideoSurfaceID fileAsset: ${this.fileAsset}`);
176
177    this.fd = await this.mediaUtil.getFdPath(this.fileAsset);
178    Logger.info(this.tag, `getVideoSurfaceID fd: ${this.fd}`);
179
180    this.videoConfig.url = `fd://${this.fd}`;
181    Logger.info(this.tag, `getVideoSurfaceID videoConfig.url : ${this.videoConfig.url}`);
182
183    if (deviceInfo.deviceType == 'default') {
184      Logger.info(this.tag, `deviceType = default`);
185      this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_ES;
186    }
187    if (deviceInfo.deviceType == 'phone') {
188      Logger.info(this.tag, `deviceType = phone`)
189      this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV;
190      this.videoConfig.profile.videoCodec = media.CodecMimeType.VIDEO_MPEG4;
191      if (this.cameraDeviceIndex == 1) {
192        this.videoConfig.rotation = this.photoRotationMap.rotation270;
193      } else {
194        this.videoConfig.rotation = this.photoRotationMap.rotation90;
195      }
196    }
197    if (deviceInfo.deviceType == 'tablet') {
198      Logger.info(this.tag, `deviceType = tablet`);
199      this.videoConfig.videoSourceType = media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV;
200    }
201
202    this.videoConfig.profile.videoFrameWidth = cameraDemo.getVideoFrameWidth();
203    this.videoConfig.profile.videoFrameHeight = cameraDemo.getVideoFrameHeight();
204    this.videoConfig.profile.videoFrameRate = cameraDemo.getVideoFrameRate();
205
206    await this.videoRecorder.prepare(this.videoConfig);
207    this.videoId = await this.videoRecorder.getInputSurface();
208    Logger.info(this.tag, `getVideoSurfaceID videoId: ${this.videoId}`);
209  }
210
211  createImageReceiver() {
212    try {
213      this.mReceiver = image.createImageReceiver(this.cameraSize.WIDTH, this.cameraSize.HEIGHT, 2000, 8);
214      Logger.info(this.tag, `createImageReceiver value: ${this.mReceiver} `);
215      this.mReceiver.on('imageArrival', () => {
216        Logger.info(this.tag, 'imageArrival start');
217        if (this.mReceiver) {
218          this.mReceiver.readNextImage((err, image) => {
219            Logger.info(this.tag, 'readNextImage start');
220            if (err || image === undefined) {
221              Logger.error(this.tag, 'readNextImage failed ');
222              return;
223            }
224            image.getComponent(4, (errMsg, img) => {
225              Logger.info(this.tag, 'getComponent start');
226              if (errMsg || img === undefined) {
227                Logger.info(this.tag, 'getComponent failed ');
228                return;
229              }
230              let buffer = new ArrayBuffer(2048);
231              if (img.byteBuffer) {
232                buffer = img.byteBuffer;
233              } else {
234                Logger.error(this.tag, 'img.byteBuffer is undefined');
235              }
236              this.savePicture(buffer, image);
237            })
238          })
239        }
240      })
241    } catch {
242      Logger.info(this.tag, 'savePicture err');
243    }
244  }
245
246  // Read Image
247  async savePicture(buffer: ArrayBuffer, img: image.Image) {
248    try {
249      Logger.info(this.tag, 'savePicture start');
250      let imgFileAsset = await this.mediaUtil.createAndGetUri(mediaLibrary.MediaType.IMAGE);
251      let imgPhotoUri = imgFileAsset.uri;
252      Logger.info(this.tag, `photoUri = ${imgPhotoUri}`);
253      let imgFd = await this.mediaUtil.getFdPath(imgFileAsset);
254      Logger.info(this.tag, `fd = ${imgFd}`);
255      await fileio.write(imgFd, buffer);
256      await imgFileAsset.close(imgFd);
257      await img.release();
258      Logger.info(this.tag, 'save image End');
259      // problem
260      if (this.handleTakePicture) {
261        this.handleTakePicture(imgPhotoUri);
262      }
263    } catch (err) {
264      Logger.info(this.tag, 'savePicture err' + JSON.stringify(err.message));
265    }
266  }
267
268  async getPhotoSurfaceID() {
269    if (this.mReceiver) {
270      Logger.info(this.tag, 'imageReceiver has been created');
271    } else {
272      this.createImageReceiver();
273    }
274    if (this.mReceiver) {
275      this.mSurfaceId = await this.mReceiver.getReceivingSurfaceId();
276    }
277    if (this.mSurfaceId) {
278      Logger.info(this.tag, `createImageReceiver mSurfaceId: ${this.mSurfaceId} `);
279    } else {
280      Logger.info(this.tag, `Get mSurfaceId failed `);
281    }
282  }
283
284  // Determine the video or photo mode
285  async isVideoPhotoFn() {
286    await this.getPhotoSurfaceID();
287
288    if (this.modelBagCol == 'photo') {
289      cameraDemo.startPhotoOrVideo(this.modelBagCol, this.videoId, this.mSurfaceId);
290    } else if (this.modelBagCol == 'video') {
291      this.isModeBol = false;
292      if (this.timer) {
293        clearInterval(this.timer);
294      }
295      // Start record
296      await this.getVideoSurfaceID();
297      cameraDemo.startPhotoOrVideo(this.modelBagCol, this.videoId, this.mSurfaceId);
298      cameraDemo.videoOutputStart();
299      if (this.videoRecorder) {
300        this.videoRecorder.start();
301      }
302    }
303  }
304
305  aboutToAppear() {
306  }
307
308  handleTakePicture = (thumbnail: string) => {
309    this.imgThumbnail = thumbnail;
310    Logger.info(this.tag, `takePicture end , thumbnail: ${this.imgThumbnail}`);
311  }
312
313  build() {
314    if (this.isModeBol) {
315      Column() {
316        Text('拍照')
317          .size({ width: 64, height: 28 })
318          .borderRadius(14)
319          .fontSize(15)
320          .fontColor(Color.White)
321          .onClick(() => {
322            cameraDemo.releaseSession()
323            cameraDemo.initCamera(this.surfaceId, this.settingDataObj.focusMode, this.cameraDeviceIndex)
324            this.modelBagCol = 'photo'
325          })
326      }.position({ x: '45%', y: '77%' })
327
328      Column() {
329        Text('录像')
330          .fontSize(15)
331          .fontColor(Color.White)
332          .borderRadius(14)
333          .size({ width: 64, height: 28 })
334          .onClick(() => {
335            cameraDemo.releaseSession()
336            cameraDemo.initCamera(this.surfaceId, this.settingDataObj.focusMode, this.cameraDeviceIndex)
337            this.modelBagCol = 'video'
338          })
339      }.position({ x: '60%', y: '77%' })
340
341      // album
342      Column() {
343        Row() {
344          if (this.modelBagCol === 'photo') {
345            Text(this.imgThumbnail || 'app.media.camera_thumbnail_4x')
346            .width('1px')
347            .height('1px')
348            .borderRadius('1px')
349            .id('ThumbnailText')
350            Image(this.imgThumbnail || $r('app.media.camera_thumbnail_4x'))
351              .objectFit(ImageFit.Fill)
352              .width('200px')
353              .height('200px')
354          } else {
355            Image(this.videoThumbnail || $r('app.media.camera_thumbnail_4x'))
356              .objectFit(ImageFit.Fill)
357              .width('200px')
358              .height('200px')
359          }
360        }.onClick(() => {
361          if (deviceInfo.deviceType == 'default') {
362            context.startAbility({
363              bundleName: 'com.ohos.photos',
364              abilityName: 'com.ohos.photos.MainAbility'
365            })
366          } else if (deviceInfo.deviceType == 'phone') {
367            context.startAbility({
368              bundleName: 'com.huawei.hmos.photos',
369              abilityName: 'com.huawei.hmos.photos.MainAbility'
370            })
371          }
372
373        })
374      }.position({ x: '10%', y: '85%' })
375      .id('Thumbnail')
376
377      // capture video icon
378      Column() {
379        Row() {
380          if (this.modelBagCol === 'photo') {
381            Image($r('app.media.camera_take_photo_4x'))
382              .width('200px')
383              .height('200px')
384              .onClick(() => {
385                // Countdown camera recording - default camera recording
386                this.countTakeVideoFn();
387              })
388          } else {
389            Image($r('app.media.camera_take_video_4x'))
390              .width('200px')
391              .height('200px')
392              .onClick(() => {
393                // Countdown camera recording - default camera recording
394                this.countTakeVideoFn();
395              })
396          }
397        }
398      }.position({ x: '40%', y: '85%' })
399      .id('CaptureOrVideoButton')
400
401      // Front and rear camera switching
402      Column() {
403        Row() {
404          Image($r('app.media.camera_switch_4x'))
405          .width('200px')
406          .height('200px')
407          .onClick(async () => {
408            // Switching Cameras
409            this.cameraDeviceIndex ? this.cameraDeviceIndex = 0 : this.cameraDeviceIndex = 1;
410            // Clear configuration
411            cameraDemo.releaseSession();
412            // Start preview
413            cameraDemo.initCamera(this.surfaceId, this.settingDataObj.focusMode, this.cameraDeviceIndex);
414          })
415        }
416      }.position({ x: '70%', y: '85%' })
417      .id('SwitchCameraButton')
418    } else {
419      Column() {
420        Row() {
421          Text().size({ width: 12, height: 12 }).backgroundColor($r('app.color.theme_color')).borderRadius(6)
422          Text(this.dateTimeUtil.getVideoTime(this.videoRecodeTime))
423            .fontSize(30)
424            .fontColor(Color.White)
425            .margin({ left: 8 })
426        }.offset({ x: -580, y: -180 })
427      }.position({ x: 120, y: 450 })
428
429      Column() {
430        // Video capture button
431        Image($r('app.media.camera_take_photo_4x'))
432        .width('200px')
433        .height('200px')
434        .onClick(() => {
435          cameraDemo.takePictureWithSettings(this.photoSettings);
436        })
437      }.position({ x: '10%', y: '85%' })
438      .id('VideoCaptureButton')
439
440      Column() {
441        Row() {
442          Column() {
443            // video stop button
444            Image($r('app.media.camera_pause_video_4x')).size({ width: 25, height: 25 })
445            .width('200px')
446            .height('200px')
447            .id('StopVideo')
448            .onClick(() => {
449              if (this.timer) {
450                clearInterval(this.timer);
451              }
452              // Stop video
453              this.stopVideo().then(async (fileAsset: mediaLibrary.FileAsset) => {
454                this.videoRecodeTime = 0;
455                this.isModeBol = true;
456                try {
457                  // Get video thumbnail
458                  this.videoThumbnail = await fileAsset.getThumbnail();
459                } catch (err) {
460                  Logger.info(this.tag, 'videoThumbnail err----------:' + JSON.stringify(err.message));
461                }
462              })
463            })
464          }
465          .width('180px')
466          .height('180px')
467        }
468        .width('200px')
469        .height('200px')
470      }.position({ x: '40%', y: '85%' })
471    }
472  }
473
474  async stopVideo() {
475    try {
476      if (this.videoRecorder) {
477        await this.videoRecorder.stop();
478        await this.videoRecorder.release();
479      }
480      cameraDemo.videoOutputStopAndRelease();
481      if (this.fileAsset) {
482        await this.fileAsset.close(this.fd);
483        return this.fileAsset;
484      }
485      Logger.info(this.tag, 'stopVideo end');
486    } catch (err) {
487      Logger.info(this.tag, 'stopVideo err: ' + JSON.stringify(err));
488    }
489    return;
490  }
491}