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
您好,可以通过在线提单进一步解决: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中,自定义相机预览出现黑屏问题,可能与以下几个原因有关:
-
权限问题:未正确申请相机权限。确保在
config.json
中声明了ohos.permission.CAMERA
权限,并在运行时动态申请。 -
Surface未正确绑定:预览黑屏可能是由于
Surface
未正确绑定到相机实例。检查Camera
和Surface
的初始化与绑定代码,确保Surface
正确设置。 -
相机配置错误:相机参数配置不当,如分辨率、帧率等与设备不匹配。检查
CameraConfig
的设置,确保其与设备兼容。 -
生命周期管理:相机资源未在合适的生命周期内释放或初始化。确保在
onCreate
中初始化相机,在onDestroy
中释放资源。 -
硬件兼容性问题:部分设备可能存在硬件兼容性问题,导致预览黑屏。检查设备日志,确认是否存在硬件相关错误。
-
渲染问题:预览画面未正确渲染到
Surface
上。检查SurfaceHolder
和SurfaceView
的设置,确保渲染流程正确。 -
系统版本问题:鸿蒙Next版本可能存在已知问题。检查系统版本是否与SDK兼容,或是否存在相关的修复补丁。
-
Logcat日志:通过Logcat查看详细日志,定位具体错误信息,如相机初始化失败、权限拒绝等。
以上是可能导致鸿蒙Next自定义相机预览黑屏的常见原因,需根据具体情况进行排查。