HarmonyOS 鸿蒙Next 自定义相机预览黑屏

HarmonyOS 鸿蒙Next 自定义相机预览黑屏

page 代码 CameraPage

import Logger from '../util/Logger'
import CameraService from '../media/Camera/CameraTool'

@Component
struct CameraPage {
  private tag: string = 'CameraPage'
  private mXComponentController: XComponentController = new XComponentController()
  private surfaceId: string = '-1'
  private cameraService: CameraService = new CameraService(getContext(this))
  @State imgThumbnail: string = ''
  @State videoThumbnail: Resource | undefined = undefined
  @State isRecording: boolean = false

  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}`)
    this.cameraService.initCamera(this.surfaceId)
  }

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

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

  build() {
    Column() {
      Stack({ alignContent: Alignment.Bottom }) {
        XComponent({
          id: 'componentId',
          type: 'surface',
          controller: this.mXComponentController
        })
          .onLoad(() => {
            Logger.info(this.tag, 'onLoad is called')
            this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 640, surfaceHeight: 480 })
            this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
            Logger.info(this.tag, `onLoad surfaceId: ${this.surfaceId}`)
            this.cameraService.initCamera(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)

            Image(this.getCameraIcon())
              .size({ width: 70, height: 70 })
              .margin({ left: 50 })
              .id('cameraIcon')
              .onClick(() => {
                Logger.info(this.tag, 'takePicture begin')
                this.cameraService.takePicture()
              })
          }
          .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%')
  }
}

相机工具类 CameraService

import camera from '@ohos.multimedia.camera'
import deviceInfo from '@ohos.deviceInfo'
import fs from '@ohos.file.fs'
import picker from '@ohos.file.picker';
import image from '@ohos.multimedia.image'
import Logger from '../../util/Logger_ts'
import { BusinessError } from '@ohos.base';

enum CameraSize  {
  WIDTH = 1920,
  HEIGHT = 1080
}

export default class CameraService {
  private context: any = undefined
  private tag: string = 'CameraService'
  private cameraManager: camera.CameraManager = undefined
  private cameras: Array<camera.CameraDevice> = undefined
  private cameraInput: camera.CameraInput = undefined
  private previewOutput: camera.PreviewOutput = undefined
  private photoOutPut: camera.PhotoOutput = undefined
  private captureSession: camera.CaptureSession = undefined
  private mReceiver: image.ImageReceiver = undefined
  private photoUri: string = ''
  private handleTakePicture: (photoUri: string) => void = undefined
  private cameraOutputCapability: camera.CameraOutputCapability = undefined

  constructor(context: any) {
    this.context = context
    this.mReceiver = image.createImageReceiver(CameraSize.WIDTH, CameraSize.HEIGHT, 4, 8)
    Logger.info(this.tag, 'createImageReceiver')
    this.receiverImage()
  }

  async receiverImage() {
    try {
    this.mReceiver.on('imageArrival', async  () => {
      Logger.info(this.tag, 'readNextImage')
      let image: image.Image = await this.mReceiver.readNextImage()
      let component:image.Component = await image.getComponent(4)
      let buffer = new ArrayBuffer(4096)
      buffer = component.byteBuffer
      this.savePicture(buffer, image)
    })
    } catch (error) {
    }
  }

  async savePicture(buffer: ArrayBuffer, img: image.Image) {
    // 创建文件管理器保存选项实例
    const photoSaveOptions = new picker.PhotoSaveOptions();
    // 保存文件名(可选),方括号里的文件名自定义,每次不能重复,设备里已有这个文件的话,名字就需要改个不一样的,不然接口会报错
    const  imgName = 'img' + this.timestamp() + '.png'
    photoSaveOptions.newFileNames = [imgName];
    const photoViewPicker = new picker.PhotoViewPicker();
    let photoSaveResult = await photoViewPicker.save(photoSaveOptions);
    let file = fs.openSync(photoSaveResult[0], fs.OpenMode.READ_WRITE);
    let writeLen = await fs.write(file.fd, buffer);
    fs.closeSync(file);
    if (this.handleTakePicture) {
      this.photoUri = photoSaveResult[0]
      this.handleTakePicture(this.photoUri)
    }
  }

  timestamp(): string {
    const currentDate: Date = new Date(); // 当前时间
    const timestamp: number = currentDate.getTime();
    return timestamp.toString()
  }
  async initCamera(surfaceId: string) {
    Logger.info(this.tag, 'initCamera')
    await this.releaseCamera()
    Logger.info(this.tag, `deviceInfo.deviceType = ${deviceInfo.deviceType}`)
    this.cameraManager = camera.getCameraManager(this.context)
    Logger.info(this.tag, 'getCameraManager')
    this.cameras = this.cameraManager.getSupportedCameras()
    Logger.info(this.tag, `get cameras ${this.cameras.length}`)
    if (this.cameras.length === 0) {
      Logger.info(this.tag, 'cannot get cameras')
      return
    }

    let cameraDevice = this.cameras[0]
    this.cameraInput =  this.cameraManager.createCameraInput(cameraDevice)
    this.cameraInput.open()
    Logger.info(this.tag, 'createCameraInput')
    this.cameraOutputCapability = this.cameraManager.getSupportedOutputCapability(cameraDevice)
    let previewProfile = this.cameraOutputCapability.previewProfiles
    this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile[previewProfile.length - 1], surfaceId)
    Logger.info(this.tag, 'createPreviewOutput')
    let mSurfaceId = await this.mReceiver.getReceivingSurfaceId()
    let photoProfiles = this.cameraOutputCapability.photoProfiles
    let photoProfile = photoProfiles[photoProfiles.length - 1]

    Logger.info(this.tag, `get photoProfiles ${this.cameraOutputCapability.photoProfiles.length}`)
    this.test_profiles(this.cameraOutputCapability.photoProfiles)
    this.photoOutPut = this.cameraManager.createPhotoOutput(photoProfile, mSurfaceId)
    this.captureSession = this.cameraManager.createCaptureSession()
    Logger.info(this.tag, 'createCaptureSession')
    this.captureSession.beginConfig()
    Logger.info(this.tag, 'beginConfig')
    this.captureSession.addInput(this.cameraInput)
    this.captureSession.addOutput(this.previewOutput)
    this.captureSession.addOutput(this.photoOutPut)
    this.captureSession.commitConfig()
    this.captureSession.start()
    Logger.info(this.tag, 'captureSession start')
  }

  test_profiles(profiles: Array<camera.Profile>) {
    profiles.forEach((element) => {
      Logger.info(this.tag, JSON.stringify(element))
    })
  }

  setTakePictureCallback(callback: (imgUri: string) => void): void {
    this.handleTakePicture = callback;
  }

  async takePicture() {
    Logger.info(this.tag, 'takePicture')

    let photoSettings: camera.PhotoCaptureSetting = {
      rotation: camera.ImageRotation.ROTATION_0,
      quality: camera.QualityLevel.QUALITY_LEVEL_MEDIUM,
      location: { // 位置信息,经纬度
        latitude: 12.9698,
        longitude: 77.7500,
        altitude: 1000
      },
      mirror: false
    }
    await this.photoOutPut.capture()
    Logger.info(this.tag, 'takePicture done')
    AppStorage.Set('isRefresh', true)
  }

  async releaseCamera() {
    Logger.info(this.tag, 'releaseCamera')

    try {
      if (this.cameraInput) {
        await this.cameraInput.close()
        this.cameraInput = undefined
      }
      if (this.previewOutput) {
        await this.previewOutput.release()
        this.previewOutput = undefined
      }
      if (this.photoOutPut) {
        await this.photoOutPut.release()
        this.photoOutPut = undefined
      }
      if (this.captureSession) {
        // await this.captureSession.stop()
        await this.captureSession.release()
        this.captureSession = undefined
      }
    } catch (error) {
      let err:BusinessError = error as BusinessError
      console.log(err.message)
    }
  }
}

更多关于HarmonyOS 鸿蒙Next 自定义相机预览黑屏的实战教程也可以访问 https://www.itying.com/category-93-b0.html

9 回复

您好,可以通过在线提单进一步解决:https://developer.huawei.com/consumer/cn/support/feedback/#/

更多关于HarmonyOS 鸿蒙Next 自定义相机预览黑屏的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


好的,已提单,华为大佬反馈是系统缺陷,正在修复中。目前 OpenHarmony-4.1.2.5 系统自定义相机功能有缺陷,等后续修复了再试一下。

这个问题我也遇到了,哪里可以看进展,

mete60 不支持 4000 以上的像素预览,

这个修复了吗?

修复了,预览的配置 不能选 4000 以上,

找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:BV1S4411E7LY/?p=17

没有,问了让升级手机系统。

在HarmonyOS鸿蒙Next中,自定义相机预览出现黑屏问题,可能与以下几个原因有关:

  1. 权限问题:未正确申请相机权限。确保在config.json中声明了ohos.permission.CAMERA权限,并在运行时动态申请。

  2. Surface未正确绑定:预览黑屏可能是由于Surface未正确绑定到相机实例。检查CameraSurface的初始化与绑定代码,确保Surface正确设置。

  3. 相机配置错误:相机参数配置不当,如分辨率、帧率等与设备不匹配。检查CameraConfig的设置,确保其与设备兼容。

  4. 生命周期管理:相机资源未在合适的生命周期内释放或初始化。确保在onCreate中初始化相机,在onDestroy中释放资源。

  5. 硬件兼容性问题:部分设备可能存在硬件兼容性问题,导致预览黑屏。检查设备日志,确认是否存在硬件相关错误。

  6. 渲染问题:预览画面未正确渲染到Surface上。检查SurfaceHolderSurfaceView的设置,确保渲染流程正确。

  7. 系统版本问题:鸿蒙Next版本可能存在已知问题。检查系统版本是否与SDK兼容,或是否存在相关的修复补丁。

  8. Logcat日志:通过Logcat查看详细日志,定位具体错误信息,如相机初始化失败、权限拒绝等。

以上是可能导致鸿蒙Next自定义相机预览黑屏的常见原因,需根据具体情况进行排查。

回到顶部