HarmonyOS鸿蒙Next中使用相机双路预览或者预览的时候不能开启闪光灯或手电筒吗

HarmonyOS鸿蒙Next中使用相机双路预览或者预览的时候不能开启闪光灯或手电筒吗

// 初始化相机。
async initCamera(): Promise<void> {
  console.info(`initCamera imageReceiverSurfaceId:${this.imageReceiverSurfaceId} xComponentSurfaceId:${this.xComponentSurfaceId}`);
  try {
    // 获取相机管理器实例。
    this.cameraManager = camera.getCameraManager(this.context);
    if (!this.cameraManager) {
      console.error('initCamera getCameraManager');
    }
    // 获取当前设备支持的相机device列表。
    this.cameras = this.cameraManager.getSupportedCameras();
    if (!this.cameras) {
      console.error('initCamera getSupportedCameras');
    }
    // 选择一个相机device,创建cameraInput输出对象。
    this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]);
    if (!this.cameraInput) {
      console.error('initCamera createCameraInput');
    }
    // 打开相机。
    await this.cameraInput.open().catch((err: BusinessError) => {
      console.error(`initCamera open fail: ${err}`);
    })
    // 获取相机device支持的profile。
    let capability: camera.CameraOutputCapability =
      this.cameraManager.getSupportedOutputCapability(this.cameras[0], camera.SceneMode.NORMAL_VIDEO);
    if (!capability) {
      console.error('initCamera getSupportedOutputCapability');
    }
    let minRatioDiff : number = 0.1;
    let surfaceRatio : number = this.imageWidth / this.imageHeight; // 最接近16:9宽高比。
    let previewProfile: camera.Profile = capability.previewProfiles[0];
    // 应用开发者根据实际业务需求选择一个支持的预览流previewProfile。
    // 此处以选择CAMERA_FORMAT_YUV_420_SP(NV21)格式、满足限定条件分辨率的预览流previewProfile为例。
    for (let index = 0; index < capability.previewProfiles.length; index++) {
      const tempProfile = capability.previewProfiles[index];
      let tempRatio = tempProfile.size.width >= tempProfile.size.height ?
        tempProfile.size.width / tempProfile.size.height : tempProfile.size.height / tempProfile.size.width;
      let currentRatio = Math.abs(tempRatio - surfaceRatio);
      if (currentRatio <= minRatioDiff && tempProfile.format == camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP) {
        previewProfile = tempProfile;
        break;
      }
    }
    this.imageWidth = previewProfile.size.width; // 更新xComponent组件的宽。
    this.imageHeight = previewProfile.size.height; // 更新xComponent组件的高。
    console.info(`initCamera imageWidth:${this.imageWidth} imageHeight:${this.imageHeight}`);
    // 使用imageReceiverSurfaceId创建第一路预览。
    this.previewOutput1 = this.cameraManager.createPreviewOutput(previewProfile, this.imageReceiverSurfaceId);
    if (!this.previewOutput1) {
      console.error('initCamera createPreviewOutput1');
    }
    // 使用xComponentSurfaceId创建第二路预览。
    this.previewOutput2 = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId);
    if (!this.previewOutput2) {
      console.error('initCamera createPreviewOutput2');
    }
     // 创建录像模式相机会话。
    this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;
    if (!this.session) {
      console.error('initCamera createSession');
    }
    // 开始配置会话。
    this.session.beginConfig();
    // 添加相机设备输入。
    this.session.addInput(this.cameraInput);
    // 添加第一路预览流输出。
    this.session.addOutput(this.previewOutput1);
    // 添加第二路预览流输出。
    this.session.addOutput(this.previewOutput2);
     // 提交会话配置。
    await this.session.commitConfig();
    // 开始启动已配置的输入输出流。

    // await this.session.start();
    await this.session.start().then(() => {
      console.info('Promise returned to indicate the session start success.');
    });

  } catch (error) {
    console.error(`initCamera fail: ${error}`);
  }
}

这是我的初始化相机方法,我想在使用相机双路预览的时候增加一个按钮可以手动控制开启关闭闪光灯,但是在点击中写入控制会报错Error message: Error code:7400102

,下面是我的点击方法

Button('开启拍照闪光')
  .onClick(()=>{

     if (!this.cameraManager) {
       promptAction.showToast({message: 'cameraManager == null'})
       return
      }

    // 检查是否支持
    if (this.cameraManager?.isTorchSupported() === false) {
      promptAction.showToast({message: '当前设备不支持闪光灯'})
    }

    this.cameraManager?.setTorchMode(camera.TorchMode.ON)

  })
  .margin({right:30})

然后我尝试在初始化最后添加第二路预览流输出后 增加下面这个代码,但是这会到这预览也不显示而且闪光灯也不亮

this.session.setFlashMode(camera.FlashMode.FLASH_MODE_OPEN )

这种情况我该怎么处理


更多关于HarmonyOS鸿蒙Next中使用相机双路预览或者预览的时候不能开启闪光灯或手电筒吗的实战教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复
  1. 手电筒 / 闪光灯 和 双路预览同时使用,在大多数设备上是受限的,硬件可能不支持同时驱动两个输出和开启闪光灯,楼主可以尝试在调用相机是使用闪光灯来满足场景需求:

  2. 使用 setFlashMode() 方法设置闪光灯模式,在设置前需使用 isFlashModeSupported() 方法检测设备是否支持设置所选闪光灯模式。

setFlashMode(flashMode: camera.FlashMode) {
  try {
    const isSupported = this.session?.isFlashModeSupported(flashMode);
    if (!isSupported) {
      Logger.error(TAG, `setFlashMode error: flash mode ${flashMode} is not supported`);
      return;
    }
    this.session?.setFlashMode(flashMode);
  } catch(e) {
    Logger.error(TAG, 'setFlashMode error ' + JSON.stringify(e));
  }
}

参考文档:自定义相机预览-相机-媒体 - 华为HarmonyOS开发者

更多关于HarmonyOS鸿蒙Next中使用相机双路预览或者预览的时候不能开启闪光灯或手电筒吗的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


错误码7400102表示硬件操作失败,应该是双路预览与闪光灯存在硬件资源冲突,没有正确区分FlashMode(拍照闪光)与TorchMode(手电筒)。

修正步骤

控制接口选择:使用TorchMode替代FlashMode实现常亮补光:

// 正确的手电筒控制方式

this.session.setTorchMode(camera.TorchMode.ON)

调整调用时机:在Session成功启动后设置手电筒状态

await this.session.start().then(() => {

  console.info('Session启动成功');

  // 在此处初始化闪光灯状态

  this.session.setTorchMode(camera.TorchMode.OFF)

});

按钮事件改造:增加前置条件检查和异步控制:

Button('开启手电筒')

.onClick(async () => {

  if (!this.session || !this.cameraInput) {

    promptAction.showToast({message: '相机未初始化完成'});

    return;

  }

  try {

    const currentCamera = this.cameras;

    // 检查硬件支持性

    const isTorchSupported = this.cameraManager.isTorchSupported(currentCamera);

    if (!isTorchSupported) {

      promptAction.showToast({message: '设备不支持手电筒'});

      return;

    }

    // 获取当前状态并切换

    const currentMode = await this.session.getTorchMode();

    const newMode = currentMode === camera.TorchMode.ON ? 

      camera.TorchMode.OFF : camera.TorchMode.ON;

    await this.session.setTorchMode(newMode);

  } catch (error) {

    console.error(`手电筒控制失败: ${error.code}`);

  }

})

问题原因分析

  1. SetFlashlight()的说明

该接口只能由打开cameraId指定Camera设备的调用者调用。

这意味着闪光灯控制需要与相机设备的使用状态严格匹配。

  1. 双路预览冲突
    您的代码创建了双路预览(previewOutput1previewOutput2),并同时启动了会话(session.start())。当相机设备正处于活跃的预览/录像会话时,直接通过CameraManager.setTorchMode()控制手电筒会被系统拒绝,因为:
  • 手电筒(Torch)和闪光灯(Flash)是独占性硬件资源。
  • 相机会话(Session)已占用设备时,外部控制闪光灯/手电筒的操作会被禁止(错误码 7400102)。
  1. 会话内设置失效
    您尝试在会话中调用setFlashMode,但:
  • 双路预览可能不支持闪光灯模式(某些硬件或模式下闪光灯不可用)。
  • 即使支持,闪光灯模式(FlashMode)是为拍照会话设计的,而手电筒(TorchMode)是常亮模式,两者用途不同。

解决方案

正确控制闪光灯/手电筒的方式如下:

1. 使用相机会话(Session)控制闪光灯(适用于拍照)

  • 拍照会话(PhotoSession) 中配置闪光灯模式(如FLASH_MODE_OPEN),但双路预览通常用于预览或录像,不支持直接开启闪光灯。
  • 如果业务需要双路预览+闪光灯,建议切换为单路预览(移除一路预览输出),再配置拍照会话并开启闪光灯。

2. 使用CameraManager控制手电筒(需无活跃会话)

  • 必须先停止相机会话,才能成功启用手电筒:
Button('开启手电筒')
  .onClick(async () => {
    if (!this.cameraManager) {
      promptAction.showToast({ message: 'cameraManager == null' });
      return;
    }

    // 1. 首先停止当前相机会话
    await this.session.stop().catch((err: BusinessError) => {
      console.error(`Stop session failed: ${err.code}`);
    });

    // 2. 检查设备是否支持手电筒
    if (!this.cameraManager.isTorchSupported()) {
      promptAction.showToast({ message: '当前设备不支持手电筒' });
      return;
    }

    // 3. 设置手电筒模式
    try {
      this.cameraManager.setTorchMode(camera.TorchMode.ON);
      promptAction.showToast({ message: '手电筒已开启' });
    } catch (error) {
      let err = error as BusinessError;
      console.error(`setTorchMode failed: ${err.code}`);
    }
  })
  • 重新开启预览时,需再次启动会话
Button('重新开启预览')
  .onClick(async () => {
    // 关闭手电筒
    this.cameraManager?.setTorchMode(camera.TorchMode.OFF);
    // 重新启动会话
    await this.session.start();
  })

3. 替代方案:使用单路预览+闪光灯

如果业务允许,改用单路预览并配置拍照会话的闪光灯:

// 在会话配置中添加拍照输出(PhotoOutput)
let photoProfile = capability.photoProfiles[0];
this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);
this.session.addOutput(this.photoOutput);

// 设置闪光灯模式(在会话提交前)
this.session.setFlashMode(camera.FlashMode.FLASH_MODE_OPEN);

总结

  • 根本原因:双路预览会话占用相机设备时,无法通过CameraManager.setTorchMode()直接控制手电筒(错误码 7400102)。
  • 正确做法
    • 如需使用手电筒,需先停止相机会话(session.stop())。
    • 如需使用闪光灯,应在拍照会话(非双路预览)中配置setFlashMode

在HarmonyOS Next中,相机双路预览与闪光灯/手电筒功能存在硬件资源冲突。双路预览会同时占用多个相机通道,而闪光灯需要独占部分系统资源,导致无法同时启用。该限制由系统底层资源管理机制决定,目前不支持并行操作。

在HarmonyOS Next中,双路预览模式下无法直接通过cameraManager.setTorchMode()session.setFlashMode()控制闪光灯,因为相机资源被多个输出流占用,此时闪光灯控制会被系统限制。

错误码7400102表明当前相机状态不支持该操作。解决方案如下:

  1. 单路预览时控制闪光灯
    如果业务允许,可在开启闪光灯时暂停一路预览(通过session.removeOutput()移除一个预览输出),操作完成后再重新添加。

  2. 使用拍照模式替代
    双路预览通常用于视频相关场景,而闪光灯功能在拍照模式下更稳定。可考虑切换至拍照模式(NORMAL_PHOTO)后再启用闪光灯。

  3. 检查设备兼容性
    确保设备硬件支持双路预览与闪光灯同时工作,部分设备可能存在硬件限制。

代码调整示例(单路预览控制方案):

// 暂停第二路预览并开启闪光灯
async enableFlashWithSinglePreview() {
  await this.session.stop();
  this.session.beginConfig();
  this.session.removeOutput(this.previewOutput2); // 移除一路预览
  await this.session.commitConfig();
  await this.session.start();
  
  // 此时可操作闪光灯
  this.cameraManager?.setTorchMode(camera.TorchMode.ON);
}

注意:重新添加预览输出时需要重新配置会话。实际场景中需权衡功能需求与性能影响。

回到顶部