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

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

HarmonyOS
2025-01-09 18:28:42
浏览
收藏 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,
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.
  • 335.
  • 336.
  • 337.
  • 338.
  • 339.
  • 340.
  • 341.
  • 342.
  • 343.
  • 344.
  • 345.
  • 346.
  • 347.
  • 348.
  • 349.
  • 350.
  • 351.
  • 352.
  • 353.
  • 354.
  • 355.
  • 356.
  • 357.
  • 358.
  • 359.
  • 360.
  • 361.
  • 362.
  • 363.
  • 364.
  • 365.
  • 366.
  • 367.
  • 368.
  • 369.
  • 370.
  • 371.
  • 372.
  • 373.
分享
微博
QQ
微信
回复
2025-01-09 20:10:44
相关问题
HarmonyOS 图片自定义分辨率问题
575浏览 • 1回复 待解决
如何在图片显示的分辨率
2514浏览 • 1回复 待解决
如何设置图片显示的分辨率
1127浏览 • 1回复 待解决
应用图标分辨率规范问题
1703浏览 • 1回复 待解决
HarmonyOS 手机分辨率怎么获取?
949浏览 • 1回复 待解决
HarmonyOS 如何获取屏幕分辨率
1328浏览 • 1回复 待解决
获取鸿蒙的分辨率高度不对
6937浏览 • 1回复 待解决
HarmonyOS 如何获取视频时长和分辨率
589浏览 • 1回复 待解决
OpenGL无法正常渲染某些分辨率YUV数据
1100浏览 • 0回复 待解决