HarmonyOS 鸿蒙Next 自定义相机在 mete60, mete40 正常,在 x5 设备黑屏

HarmonyOS 鸿蒙Next 自定义相机在 mete60, mete40 正常,在 x5 设备黑屏

相机工具类

导入模块

import camera from '@ohos.multimedia.camera';
import image from '@ohos.multimedia.image';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
import { AppInfo, DeviceInfo } from 'lib_paoh_baseinfo';
import { savePictureToAlbum } from '../Photo/SaveImage';
import fileUri from '@ohos.file.fileuri';

const TAG: string = 'CAMERA_TAG';

let cameraManager: camera.CameraManager;
let photoOutput: camera.PhotoOutput;
let captureSession: camera.CaptureSession;
let cameraInput: camera.CameraInput;
let previewOutput: camera.PreviewOutput;
let cameraDevice: camera.CameraDevice
let imageReceiver: image.ImageReceiver

let context: ESObject

CameraConfig 类

export class CameraConfig {
  saveToAlbum: boolean = true
  saveToSandbox: Boolean = true
  handleTakePicture: (photoUri: string) => void = this._handleTakePicture

  _handleTakePicture(uri: string): void {
    Logger.info(TAG, `_handleTakePicture uri = ${uri}`)
  }
}

CameraService 类

export class CameraService {
  config: CameraConfig = new CameraConfig()

  constructor(config: CameraConfig) {
    this.config = config
  }

  async initCamera(baseContext: ESObject, surfaceId: string) {
    try {
     await this._initCamera(baseContext,surfaceId)
    } catch (error) {
      Logger.error(TAG, `Camera error : ${JSON.stringify(error)}`);
    }
  }

  async _initCamera(baseContext: ESObject, surfaceId: string) {

    cameraManager = camera.getCameraManager(baseContext);
    let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
    cameraDevice = cameraArray[0];

    cameraInput = cameraManager.createCameraInput(cameraDevice);

    await cameraInput.open();

    let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraDevice);

    let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles;
    let position: number = this._profilesPosition(previewProfilesArray)
    let previewProfile = previewProfilesArray[position]

    let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles;
    let photoProfile = photoProfilesArray[photoProfilesArray.length - 1]

    imageReceiver = image.createImageReceiver(previewProfile.size.width, previewProfile.size.height, 4, 8);

    let photoSurfaceId: string = await imageReceiver.getReceivingSurfaceId();
    photoOutput = cameraManager.createPhotoOutput(photoProfile, photoSurfaceId);

    previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId);

    captureSession = cameraManager.createCaptureSession();
    captureSession.beginConfig();
    captureSession.addInput(cameraInput);
    captureSession.addOutput(previewOutput);
    captureSession.addOutput(photoOutput);
    await captureSession.commitConfig();
    await captureSession.start()

    this._monitorCameraState()
  }

  capture() {
    let photoCaptureSetting: camera.PhotoCaptureSetting = {
      quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
    }
    photoOutput.capture(photoCaptureSetting, (err: BusinessError) => {
      if (err) {
        Logger.error(TAG, `Failed to capture the photo ${err.message}`);
        return;
      }
      Logger.info(TAG, 'Callback invoked to indicate the photo capture request success.');
    });
  }

  releaseCamera() {
    captureSession.stop();

    cameraInput.close();

    previewOutput.release();

    photoOutput.release();

    captureSession.release();
  }

  async savePictureToAlbum(buffer: ArrayBuffer) {
    const photoSaveOptions = new picker.PhotoSaveOptions();
    let photoUri = await savePictureToAlbum(buffer)
    if (this.config.handleTakePicture) {
      this.config.handleTakePicture(photoUri)
    }
  }

  async savePictureToSandbox(buffer: ArrayBuffer) {
    let imageName = "IMG_" + Date.parse(new Date().toString()) + ".jpg";
    let context = getContext() as common.UIAbilityContext;
    let path = context.filesDir + "/" + imageName;

    Logger.info(TAG, "图片路径:" + path);

    let file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
    let value = fs.writeSync(file.fd, buffer)
    fs.closeSync(file);

    if (value !== -1) {
      Logger.info(TAG, "图片保存成功")
      let imageUri: string = fileUri.getUriFromPath(path);
      Logger.info(TAG, "imageUri->" + imageUri.toString())
      if (this.config.handleTakePicture) {
        this.config.handleTakePicture(imageUri)
      }
    }
  }

  _monitorCameraState() {
    cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => {
      Logger.info(TAG, `camera : ${cameraStatusInfo.camera.cameraId}`);
      Logger.info(TAG, `status: ${cameraStatusInfo.status}`);
    });

    cameraInput.on('error', cameraDevice, (error: BusinessError) => {
      Logger.error(TAG, `Camera input error code: ${error.code}`);
    })

    previewOutput.on('error', (error: BusinessError) => {
      Logger.error(TAG, `Preview output error code: ${error.code}`);
    });

    photoOutput.on('captureEnd', (err: BusinessError, data: camera.CaptureEndInfo) => {
      Logger.info(TAG, `photoOutput ${err}`)
    })

    captureSession.on('error', (error: BusinessError) => {
      Logger.error(TAG, `Capture session error code: ${error.code}`);
    });

    imageReceiver.on('imageArrival', async  () => {
      try {
        let nextImage: image.Image = await imageReceiver.readNextImage()
        let imgComponent: image.Component = await nextImage.getComponent(image.ComponentType.JPEG)
        if (!(imgComponent.byteBuffer as ArrayBuffer)) {
          return
        }
        Logger.info(TAG, "接收到图片数据[" + imgComponent.byteBuffer.byteLength + "]")
        if(this.config.saveToAlbum) await this.savePictureToAlbum(imgComponent.byteBuffer)
        if(this.config.saveToSandbox) await this.savePictureToSandbox(imgComponent.byteBuffer)
        nextImage.release()
      } catch (error) {
        Logger.error(TAG, `imageArrival error = ${error}} `)
      }
    })
  }

  _profilesPosition(previewProfilesArray: Array<camera.Profile>): number {
    let position: number = 0
    let difference:number = Number.MAX_VALUE;
    if (previewProfilesArray != null) {
      previewProfilesArray.forEach((value: camera.Profile, index: number) => {
        let previewWidth = value.size.width
        let previewHeight = value.size.height

        let aspectRatio = previewWidth / previewHeight * 1000 / 1000;
        if (aspectRatio < 1) {
          aspectRatio = previewHeight / previewWidth * 1000 / 1000;
        }

        let device_width = DeviceInfo.getResolution().width
        let device_height = DeviceInfo.getResolution().height
        let device_aspectRatio = device_width / device_height * 1000 / 1000;
        if (device_aspectRatio < 1) {
          device_aspectRatio = device_height / device_width * 1000 / 1000;
        }

        let _difference = Math.abs(device_aspectRatio - aspectRatio);

        if (difference > _difference) {
          difference = _difference
          position = index;
        }
      })
    }
    return position
  }
}

更多关于HarmonyOS 鸿蒙Next 自定义相机在 mete60, mete40 正常,在 x5 设备黑屏的实战教程也可以访问 https://www.itying.com/category-93-b0.html

4 回复

这是处理器的问题,后续新版本都会适配好

更多关于HarmonyOS 鸿蒙Next 自定义相机在 mete60, mete40 正常,在 x5 设备黑屏的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


大佬,咨询一下 预览配置如何选择最优 配置, cameraOutputCap.previewProfiles 里面有几十个配置,要怎么选

配置 相机输出 预览配置

let previewProfilesArray: Array&lt;camera.Profile&gt; = cameraOutputCap.previewProfiles;
拍照页面代码

```javascript
import Logger from '../util/Logger'
import {CameraService, CameraConfig} from '../media/Camera/CameraService'
import {PreferencesTool} from 'lib_paoh_utils'
import router from '[@ohos](/user/ohos).router'
import {CameraPreviewPageModel} from './CameraPreviewPage'

[@Entry](/user/Entry)
[@Component](/user/Component)
struct CameraPage {
  preference:PreferencesTool = new PreferencesTool()

  private tag: string = 'CameraPage'
  private mXComponentController: XComponentController = new XComponentController()
  private surfaceId: string = '-1'
  private cameraConfig = this._cameraConfig()
  private cameraService: CameraService = new CameraService(this.cameraConfig)
  [@State](/user/State) imgThumbnail: string = ''
  [@State](/user/State) videoThumbnail: Resource | undefined = undefined
  [@State](/user/State) isRecording: boolean = false
  [@State](/user/State) saveButtonOptions: SaveButtonOptions = {
    icon: SaveIconStyle.FULL_FILLED,
    text: SaveDescription.SAVE_IMAGE,
    buttonType: ButtonType.Capsule
  } // 设置安全控件按钮属性

  _cameraConfig():CameraConfig {
   let config = new CameraConfig()
    // config.handleTakePicture = this.handleTakePicture
    config.handleTakePicture = (imgUri: string): void => this.handleTakePicture(imgUri)
    config.saveToAlbum = false
    return config
  }

  aboutToAppear() {
    this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
    Logger.info(this.tag, `aboutToAppear,surfaceId=${this.surfaceId}`)
    // this.cameraService.setTakePictureCallback((imgUri:string): void => this.handleTakePicture(imgUri))
  }

  async aboutToDisappear() {
    Logger.info(this.tag, `aboutToDisappear,surfaceId=${this.surfaceId}`)
    await this.cameraService.releaseCamera()
  }

  async onPageHide() {
    Logger.info(this.tag, `onPageHide,surfaceId=${this.surfaceId}`)
    await this.cameraService.releaseCamera()
  }

  async onPageShow() {
    Logger.info(this.tag, `onPageShow,surfaceId=${this.surfaceId}`)
    if(this.surfaceId) {
      await this.cameraService.releaseCamera()
      await this.cameraService.initCamera(getContext(),this.surfaceId)
    }
  }

  getCameraIcon() {
    return $r('app.media.take_photo_normal')
  }

  handleTakePicture = (thumbnail: string) => {
    this.imgThumbnail = thumbnail
    this.preference.saveValue(thumbnail,'imgUri_key')
    Logger.info(this.tag, `takePicture end , thumbnail: ${this.imgThumbnail}`)
  }

  build() {
    Column() {
      Stack({ alignContent: Alignment.Bottom }) {
        XComponent({
          id: 'componentId',
          type: 'surface',
          controller: this.mXComponentController
        })
          .onLoad( async  () => {
            Logger.info(this.tag, 'onLoad is called')
            // this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 1800, surfaceHeight: 1800 })
            this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
            Logger.info(this.tag, `onLoad surfaceId: ${this.surfaceId}`)
            await this.cameraService.initCamera(getContext(),this.surfaceId)
          })
          .height('100%')
          .width('100%')
          .margin({ bottom: 130 })
        Column() {
          Row() {

            Image(this.imgThumbnail)
              .size({ width: 70, height: 70 })
              .aspectRatio(1)
              .borderRadius(40)
              .objectFit(ImageFit.Fill)
              .backgroundColor(Color.Gray)
              .onClick((event) => {
                router.pushUrl({
                  url: 'pages/CameraPreviewPage',
                  params: new CameraPreviewPageModel(this.imgThumbnail)
                })
              })

            Image(this.getCameraIcon())
              .size({ width: 70, height: 70 })
              .margin({ left: 50 })
              .id('cameraIcon')
              .onClick(() => {
                Logger.info(this.tag, 'takePicture begin')
                this.cameraService.capture()
              })
          }
          .size({ height: 80, width: '100%' })
          .margin({ right: 50 })
          .justifyContent(FlexAlign.Center)
        }
        .size({ height: 130, width: '100%' })
        .padding({ bottom: 10 })
        .backgroundColor(Color.Black)
      }
      .width('100%')
      .height('100%')
      .layoutWeight(1)
      .backgroundColor(Color.Grey)
    }
    .width('100%')
    .height('100%')
  }
}

在HarmonyOS鸿蒙Next中,自定义相机在Mate 60和Mate 40设备上正常运行,但在X5设备上出现黑屏问题。可能的原因包括设备硬件差异、相机API兼容性问题、权限配置错误或资源文件缺失。X5设备的相机模块或传感器可能与Mate系列不同,导致API调用失败。此外,HarmonyOS在不同设备上的实现可能存在差异,某些API在X5设备上可能未完全支持或存在bug。权限配置错误或资源文件缺失也可能导致相机无法正常初始化。

回到顶部