HarmonyOS 自定义分辨率保存图片闪退问题

在savePicture方法中使用了自定义分辨率保存图片方法,但是每次拍照的时候会闪退。

HarmonyOS
2天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
shlp

在调用pixelmap.scale需要加await,参考demo如下:

import camera from '@ohos.multimedia.camera'; // 导入相机模块
import image from '@ohos.multimedia.image'; // 导入图像模块
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common'; // 导入公共能力模块
import { BusinessError } from '@ohos.base'; // 导入业务错误模块
import fs from '@ohos.file.fs'; // 导入文件系统模块
import photoAccessHelper from '@ohos.file.photoAccessHelper'; // 导入相册访问助手模块
import { customScan, scanBarcode, scanCore } from '@kit.ScanKit'; // 导入扫描工具模块
import { fileIo } from '@kit.CoreFileKit'; // 导入文件输入输出工具模块
import { buffer, util } from '@kit.ArkTS';
import { systemDateTime } from '@kit.BasicServicesKit';

interface FileInfo {
  prefix: string;
  suffix: string;
}

interface prviewProTs {
  width: number;
  height: number;
}

const prviewPro: prviewProTs = {
  width: 1080, // 预览宽度
  height: 1920 // 预览高度
}

class CameraTest {
  resolution: number | string = 10; //像素比支持480P,720P,1080P,4K,以及自定义格式:xxx*xxx
  autoFocus: boolean = true; //是否自动对焦
  isUseFrontCamera: boolean = true; // 初始设为前置摄像头
  isNeedBase64: boolean = true; //是否提供base64
  compressRatio: number = 1; //压缩系数 0~1
  needSaveToAlbum: boolean = true; //是否保存到相册
  takePhoto: () => void = () => {}

  previewProfilesObj: camera.Profile | undefined; // 预览配置对象
  photoProfilesObj: camera.Profile | undefined; // 拍照配置对象
  previewOutput: camera.PreviewOutput | undefined; // 预览输出对象
  photoOutput: camera.PhotoOutput | undefined; // 拍照输出对象
  photoSession: camera.PhotoSession | undefined; // 拍照会话对象
  photoSurfaceId: string | undefined = undefined; // 拍照表面ID
  photoAccessHelper: photoAccessHelper.PhotoAccessHelper | undefined = undefined; // 相册模块管理实例
  imgBase64: string = ''; // 存储图片的Base64编码
  private cameraDevice?: camera.CameraDevice; // 相机设备对象
  private cameraInput?: camera.CameraInput; // 相机输入对象
  private renderId: string = '-1'; // 渲染ID
  private context: common.BaseContext; // 应用上下文
  private imageReceiver?: image.ImageReceiver; // 图像接收器
  private updateImgBase64: (base64: string) => void;

  constructor(context: common.BaseContext, renderId: string, initFront: boolean | undefined,
    updateImgBase64: (base64: string) => void) {
    this.context = context; // 初始化上下文
    this.renderId = renderId; // 初始化渲染ID
    this.updateImgBase64 = updateImgBase64;
    this.photoAccessHelper =
      photoAccessHelper.getPhotoAccessHelper(this.context as common.UIAbilityContext); // 获取相册管理模块的实例
    this.imageReceiver = image.createImageReceiver(prviewPro, image.ImageFormat.JPEG, 8); // 创建图像接收器
    if (this.imageReceiver) {
      this.initCamera(this.renderId, this.imageReceiver, initFront ?? this.isUseFrontCamera); // 初始化相机
      this.isUseFrontCamera = initFront ?? this.isUseFrontCamera; // 初始化前置摄像头
    } else {
      console.error('Failed to create ImageReceiver');
    }
    console.info(`renderId=${this.renderId}`);
  }

  async getFdPath(fileuri: string): Promise<number> {
    let file = fs.openSync(fileuri, fs.OpenMode.READ_WRITE); // 打开文件
    let fd: number = file.fd; // 获取文件描述符
    return fd;
  }

  aboutToDisappear(): void {
    this.releaseCamera(); // 页面消失时释放相机
  }

  async initCamera(XComponentSurfaceId: string, receiver: image.ImageReceiver,
    isUseFrontCamera: boolean): Promise<void> {
    try {
      await this.releaseCamera(); // 确保释放旧的相机资源

      let cameraManager = camera.getCameraManager(getContext(this) as common.UIAbilityContext); // 获取相机管理器
      const cameraDevices = cameraManager.getSupportedCameras(); // 获取支持的相机设备
      if (cameraDevices.length === 0) {
        console.error('No camera devices found');
        return;
      }
      // 获取是哪个摄像头
      this.cameraDevice = this.getDesiredCameraDevice(cameraDevices, isUseFrontCamera); // 获取指定的相机设备
      if (!this.cameraDevice) {
        console.error('Desired camera device not found');
        return;
      }
      this.cameraInput = cameraManager.createCameraInput(this.cameraDevice); // 创建相机输入
      await this.cameraInput.open(); // 打开相机输入

      this.photoSurfaceId = await receiver.getReceivingSurfaceId(); // 获取接收表面ID
      let profiles: camera.CameraOutputCapability =
        await cameraManager.getSupportedOutputCapability(this.cameraDevice, camera.SceneMode.NORMAL_PHOTO); // 获取相机输出能力
      console.info('获取对应相机设备profiles', JSON.stringify(profiles));

      this.previewProfilesObj = profiles.previewProfiles[0]; // 获取预览配置对象
      this.photoProfilesObj = profiles.photoProfiles[0]; // 获取拍照配置对象
      console.info('profiles.photoProfiles[5]', JSON.stringify(profiles.photoProfiles))

      this.previewOutput = cameraManager.createPreviewOutput(this.previewProfilesObj, XComponentSurfaceId); // 创建预览输出
      console.info('previewOutput 创建预览流输出对象', JSON.stringify(profiles));

      this.photoOutput = cameraManager.createPhotoOutput(this.photoProfilesObj); // 创建拍照输出
      console.info('photoSurfaceId 创建拍照输出流', JSON.stringify(profiles));

      this.photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; // 创建拍照会话
      this.photoSession.beginConfig(); // 开始配置会话
      this.photoSession.addInput(this.cameraInput); // 添加相机输入
      this.photoSession.addOutput(this.previewOutput); // 添加预览输出
      this.photoSession.addOutput(this.photoOutput); // 添加拍照输出

      this.pz();
      await this.photoSession.commitConfig(); // 提交配置
      await this.photoSession.start(); // 开始会话
      if (await this.photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO)) {
        await this.photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);
      }
    } catch (err) {
      console.error('Error in initCamera:', err);
    }
  }

  async pz(){
    this.photoOutput?.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => { // 拍照可用事件
      console.info('photoAvailable')
      let imageObj = photo.main; // 获取主图像对象
      imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => {
        if (errCode || component === undefined) {
          return;
        }
        let imgBuff: ArrayBuffer;
        imgBuff = component.byteBuffer; // 获取图像缓冲区
        console.info('imgBuff', JSON.stringify(imgBuff));
        await this.savePicture(imgBuff,imageObj); // 保存图片
      });

    });
  }

  async savePicture(imgBuff: ArrayBuffer, imageObj: image.Image): Promise<void> {
    try {
      // 设置压缩选项
      const imagePacker = image.createImagePacker();
      const imageSource = image.createImageSource(imgBuff);
      const pixelmap = await imageSource.createPixelMap({
        editable: true,
        desiredPixelFormat: 3,
      });
      let imageInfo = await pixelmap.getImageInfo();
      let scaleX: number = 1000 / imageInfo.size.width;
      let scaleY: number = 1500 / imageInfo.size.height;
      await pixelmap.scale(scaleX, scaleY);
      let imageInfo2 = await pixelmap.getImageInfo();
      // pixelmap换成imageSource就正常了
      const compressedImgBuff = await new Promise<ArrayBuffer>((resolve, reject) => {
        imagePacker.packing(pixelmap, {
          format: "image/jpeg",
          quality: 75 // 压缩质量 (0-100)
        }, (err: BusinessError, data: ArrayBuffer) => {
          if (err) {
            reject(err);
          } else {
            resolve(data);
          }
        });
      });

      this.imgBase64 = buffer.from(compressedImgBuff).toString('base64');
      this.updateImgBase64(buffer.from(compressedImgBuff).toString('base64'));
      console.info('Compressed base64 image:', this.imgBase64);
    } catch (err) {
      console.error('Error saving compressed picture:', err);
    }
    imageObj.release(); // 释放图像对象
  }

  async releaseCamera(): Promise<void> {
    try {
      if (this.photoSession) {
        await this.photoSession.stop(); // 停止拍照会话
        this.photoSession.release(); // 释放会话资源
        this.photoSession = undefined;
      }
      if (this.cameraInput) {
        await this.cameraInput.close(); // 关闭相机输入
        this.cameraInput = undefined;
      }
      if (this.previewOutput) {
        this.previewOutput.release(); // 释放预览输出资源
        this.previewOutput = undefined;
      }
      this.photoSurfaceId = undefined;
      this.previewProfilesObj = undefined;
      this.photoProfilesObj = undefined;
      this.cameraDevice = undefined;
    } catch (err) {
      console.error('Error releasing camera:', err);
    }
  }

  async switchCamera(): Promise<void> {
    this.isUseFrontCamera = !this.isUseFrontCamera; // 切换前后摄像头
    await this.releaseCamera(); // 释放当前相机资源
    if (this.imageReceiver) {
      await this.initCamera(this.renderId, this.imageReceiver, this.isUseFrontCamera); // 重新初始化相机
    } else {
      console.error('ImageReceiver is not available');
    }
  }

  async takePhotoFn(): Promise<void> {
    if (!this.photoSession) {
      console.error("Photo session is not initialized.");
      return;
    }

    try {
      let photoCaptureSetting: camera.PhotoCaptureSetting = {
        quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // 设置照片质量
        rotation: camera.ImageRotation.ROTATION_0 // 设置照片旋转角度
      };
      this.photoOutput && this.photoOutput.capture(photoCaptureSetting); //进行拍照
    } catch (err) {
      console.error('Error capturing photo:', err);
    }
  }

  async toggleFlashLight(type: camera.FlashMode): Promise<void> {
    if (!this.photoSession) {
      console.error("Photo session is not initialized.");
      return;
    }

    if (this.photoSession.hasFlash()) {
      try {
        await this.photoSession.setFlashMode(type); // 设置闪光灯模式
        console.info(`Flashlight mode set to ${type}`);
      } catch (err) {
        console.error('闪光灯打开类型:', JSON.stringify(err));
      }
    } else {
      console.error("没有闪光灯选项");
    }
  }

  private getDesiredCameraDevice(cameraDevices: Array<camera.CameraDevice>,
    isUseFrontCamera: boolean): camera.CameraDevice | undefined {
    for (const device of cameraDevices) {
      if (isUseFrontCamera && device.cameraPosition === camera.CameraPosition.CAMERA_POSITION_FRONT) {
        return device;
      }
      if (!isUseFrontCamera && device.cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK) {
        return device;
      }
    }
    return undefined;
  }
}

@Entry
@Component
struct Index {
  private controller: XComponentController = new XComponentController; // XComponent控制器
  private renderId: string = '-1'; // 渲染ID
  private context: common.BaseContext = getContext(this); // 应用上下文
  initisUseFrontCamera: boolean | undefined;
  cameraManage: CameraTest | undefined;
  @State imgBase64: string | undefined = ''

  aboutToAppear() {
    abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context as common.UIAbilityContext, [// 请求权限
      'ohos.permission.CAMERA',
      'ohos.permission.MEDIA_LOCATION',
      'ohos.permission.READ_MEDIA',
      'ohos.permission.WRITE_MEDIA',
      'ohos.permission.READ_IMAGEVIDEO',
      'ohos.permission.WRITE_IMAGEVIDEO'
    ]).then(() => {
      this.init(); // 初始化
    });
  }

  onPageShow(): void {
    this.init(); // 页面显示时初始化
  }

  onPageHide(): void {
    this.initisUseFrontCamera = this.cameraManage?.isUseFrontCamera; // 页面隐藏时记录当前摄像头状态
  }

  init() {
    this.controller.setXComponentSurfaceRect({
      surfaceWidth: 1080, // 设置组件表面宽度
      surfaceHeight: 1440 // 设置组件表面高度
    });
    this.initPrview(); // 初始化预览
    if (this.renderId !== '-1') {
      this.cameraManage = new CameraTest(this.context, this.renderId, this.initisUseFrontCamera,
        this.updateImgBase64.bind(this)); // 创建CameraTest实例
    } else {
      console.error('Render ID is not available.');
    }
  }

  aboutToDisappear(): void {
    this.cameraManage?.aboutToDisappear(); // 页面消失时释放相机
  }

  initPrview() {
    this.renderId = this.controller.getXComponentSurfaceId(); // 获取组件表面ID
  }

  updateImgBase64(base64: string): void {
    console.info('base64', base64)
    this.imgBase64 = base64;
  }

  build() {
    Column() {
      XComponent({
        id: '',
        type: 'surface',
        libraryname: '',
        controller: this.controller
      })
        .onLoad(() => {
          this.initPrview(); // 加载组件时初始化预览
        })
        .width('1440px')
        .height('1080px');
      Column() {
        Button("拍照").onClick(() => {
          this.cameraManage?.takePhotoFn();
        });
        Button("自动闪光灯").onClick(() => {
          this.cameraManage?.toggleFlashLight(camera.FlashMode.FLASH_MODE_OPEN);
        });
        Button("打开闪光灯").onClick(() => {
          this.cameraManage?.toggleFlashLight(camera.FlashMode.FLASH_MODE_ALWAYS_OPEN);
        });
        Button("关闭闪光灯").onClick(() => {
          this.cameraManage?.toggleFlashLight(camera.FlashMode.FLASH_MODE_CLOSE);
        });
        Button("切换摄像头").onClick(() => {
          this.cameraManage?.switchCamera();
        });
        Image('data:image/png;base64,' + this.imgBase64)
      }
    }
  }
}

interface resolutionFace {
  '1080P': number;
  '720P': number;
  '4k': number;
  '480P': number;
}

const resolutionMap: resolutionFace = {
  '1080P': 3,
  '720P': 2,
  '4k': 10,
  '480P': 0,
}
分享
微博
QQ
微信
回复
2天前
相关问题
HarmonyOS 图片自定义分辨率问题
13浏览 • 1回复 待解决
如何设置图片显示的分辨率
653浏览 • 1回复 待解决
如何在图片显示的分辨率
2032浏览 • 1回复 待解决
应用图标分辨率规范问题
1263浏览 • 1回复 待解决
HarmonyOS 手机分辨率怎么获取?
433浏览 • 1回复 待解决
HarmonyOS 如何获取屏幕分辨率
726浏览 • 1回复 待解决
获取鸿蒙的分辨率高度不对
6297浏览 • 1回复 待解决
HarmonyOS 如何获取视频时长和分辨率
23浏览 • 1回复 待解决
OpenGL无法正常渲染某些分辨率YUV数据
581浏览 • 0回复 待解决
HarmonyOS应用退问题
859浏览 • 1回复 待解决