鸿蒙5.0&next开发【生态应用相机实现系统级相机】媒体开发实战

概述

本文针对三方相机开发场景,基于HarmonyOS提供的相机开放能力,实现系统相机级别的效果和能力,比如分辨率、动图、视频防抖、连续变焦等。

效果展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

场景使用说明

适用范围

本场景主要适用于第三方应用调用系统相机能力,实现系统级相机效果。

限制版本Developer Beta1及以上。

场景优势

场景分类三方相机原生相机
多摄连续变焦(画面亮度,颜色一致)支持支持
视频防抖支持支持
照片高动态HDR支持支持

场景分析

典型场景

场景名称描述实现方案
拍照照片拍摄Camera kit
录像视频录制AVRecorder和Camera kit
动态照片动态照片拍摄以及预览Camera kit和MovingPhotoView组件

场景实现

场景整体介绍

原理介绍

三方相机开放能力采用与系统相机统一底层接口调用方式,使最终的拍摄保持系统级效果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

整体流程

本场景解决方案按照如下流程实现三方相机效果,推荐开发者参考相同流程进行接入,以保证更好体验:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拍照

如图1所示,应用可以点击底部圆形按钮拍摄照片,同时可以调节变焦,闪光灯等参数。

图1 拍照界面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时序图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关键点说明

需要获得将照片存入图库的权限。

关键代码片段

1、创建CameraManager对象,需要通过UIContext创建,用来对相机进行操作。

let cameraManager: camera.CameraManager = camera.getCameraManager(context);

2、获取相机列表,系统会提供前置和后置相机,供开发者选择切换。

let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras();

3、选择摄像头,创建输入流,作为预览流和拍照流的提供方。

cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]);
await cameraInput.open();

4、获取相机设备支持的输出流能力,此处指定NORMAL_PHOTO获取的是照片流,用来创建拍照输出流和预览输出流。

let cameraOutputCap: camera.CameraOutputCapability =
  cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_PHOTO);

5、创建预览输出流,通过surfaceId绑定显示组件XComponent。

let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles;

previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId);

6、创建拍照输出流,可以输出照片文件。

let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles;

photoOutPut = cameraManager.createPhotoOutput(photoProfile);

7、创建相机会话,用于控制修改拍照参数。

photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;

8、开始配置会话,向会话中添加各路输入输出流,之后可以拍照。

photoSession.beginConfig();
photoSession.addInput(cameraInput);
photoSession.addOutput(previewOutput);
photoSession.addOutput(photoOutPut);
await photoSession.commitConfig();
await photoSession.start();

9、调节闪光灯,通过FLASH_MODE_CLOSE设置为关闭,目前支持关闭,自动,常亮,打开。

photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE);

10、调节自动变焦模式,通过FOCUS_MODE_CONTINUOUS_AUTO设置为自动模式。目前支持连续自动对焦、自动对焦、手动对焦。

photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);

11、调节相机焦距,超出范围则只保留支持范围的值。

photoSession.setZoomRatio(zoom);

12、点击拍照,通过QUALITY_LEVEL_HIGH选择高质量模式。

let settings: camera.PhotoCaptureSetting = {
  quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
  rotation: camera.ImageRotation.ROTATION_0,
  mirror: isFront
};
photoOutPut.capture(settings);

13、保存图片,此处需要调用系统图库接口photoAccessHelper。

function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void {
  photoOutput.on('photoAssetAvailable',
    async (_err: BusinessError, photoAsset: photoAccessHelper.PhotoAsset): Promise<void> => {
      let accessHelper: photoAccessHelper.PhotoAccessHelper =
        photoAccessHelper.getPhotoAccessHelper(currentContext);
      let assetChangeRequest: photoAccessHelper.MediaAssetChangeRequest =
        new photoAccessHelper.MediaAssetChangeRequest(photoAsset);
      assetChangeRequest.saveCameraPhoto();
      await accessHelper.applyChanges(assetChangeRequest);
      uri = photoAsset.uri;
      AppStorage.setOrCreate('photoUri', await photoAsset.getThumbnail());
    });
}

14、预览图片,应用将跳转进入系统图库应用进行预览,需要传入对应的图库uri。

export function previewPhoto(context: Context): void {
  let photoContext = context as common.UIAbilityContext;
  photoContext.startAbility({
    parameters: { uri: uri },
    action: 'ohos.want.action.viewData',
    bundleName: 'com.huawei.hmos.photos',
    abilityName: 'com.huawei.hmos.photos.MainAbility'
  })
}

录像

用户在应用界面上点击按钮开始录制,再次点击红色按钮结束录制,同时可以调节变焦,闪光灯,分辨率,视频防抖等参数

图2 录像界面

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时序图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关键点说明

需要获得将照片存入图库的权限。

关键代码片段

1、创建CameraManager对象,需要通过UIContext创建,用来对相机进行操作。

let cameraManager: camera.CameraManager = camera.getCameraManager(context);

2、获取相机列表,系统会提供前置和后置相机,供开发者选择切换。

let cameraArray: camera.CameraDevice[] = [];
cameraArray = cameraManager.getSupportedCameras();

3、选择摄像头,创建输入流,作为预览流和拍照流的提供方。

cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]);
// ...
await cameraInput.open();

4、获取相机设备支持的输出流能力,此处指定NORMAL_VIDEO获取的是视频流,用来创建视频输出流和视频预览输出流。

let cameraOutputCap: camera.CameraOutputCapability =
  cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_VIDEO);

5、创建预览输出流,通过surfaceId绑定显示组件XComponent。

let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles;

let previewOutput: camera.PreviewOutput | undefined = cameraManager.createPreviewOutput(previewProfile, surfaceId);

6、创建视频输出流,可以输出视频文件。

let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => {
  if (previewProfile && cameraPosition === 1) {
    return profile.size.width >= 1080 && profile.size.height >= 1080
      && profile.size.height === (foldAbleStatus === display.FoldStatus.FOLD_STATUS_EXPANDED ? 1 :
        (previewProfile.size.height / previewProfile.size.width)) * profile.size.width
      && profile.frameRateRange.max === 30;
  }
  if (previewProfile && qualityLevel === 0) {
    return profile.size.width <= 1920 && profile.size.width >= 1080 && profile.size.height >= 1080
      && profile.size.height === (foldAbleStatus === display.FoldStatus.FOLD_STATUS_EXPANDED ? 1 :
        (previewProfile.size.height / previewProfile.size.width)) * profile.size.width
      && profile.frameRateRange.max === 60;
  }
  if (previewProfile && qualityLevel === 1 && cameraPosition === 0) {
    return profile.size.width <= 4096 && profile.size.width >= 3000
      && profile.size.height === (foldAbleStatus === display.FoldStatus.FOLD_STATUS_EXPANDED ? 1 :
        (previewProfile.size.height / previewProfile.size.width)) * profile.size.width
      && profile.frameRateRange.max === 60;
  }
  return undefined;
})

videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId);

7、创建相机会话,用于控制修改视频参数。

videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession;

8、开始配置会话,向会话中添加各路输入输出流,之后可以开始录像。

videoSession.beginConfig();

videoSession.addInput(cameraInput);

videoSession.addOutput(previewOutput);
videoSession.addOutput(videoOutput);
await videoSession.commitConfig();
// ...
await videoSession.start();

9、通过avRecorder开始录像。

await avRecorder.start();

10、通过avRecorder停止录像。

await avRecorder.stop();

11、保存视频,注意需要将文件uri转化为fd赋值给AVRecorderConfig。

let options: photoAccessHelper.CreateOptions = {
  title: Date.now().toString()
};
let accessHelper: photoAccessHelper.PhotoAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);
let videoUri: string = await accessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options);
file = fileIo.openSync(videoUri, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
let aVRecorderConfig: media.AVRecorderConfig = {
  audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
  videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV,
  profile: aVRecorderProfile,
  url: `fd://${file.fd.toString()}`,
  // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4
  rotation: cameraPosition === 0 ? 90 : 270,
  // 合理值0、90、180、270,非合理值prepare接口将报错
  location: { latitude: 30, longitude: 130 }
};

12、预览视频,应用将跳转进入系统图库应用进行预览,需要传入对应的图库uri。

export function previewVideo(context: Context, videoUri: string): void {
  let videoContext = context as common.UIAbilityContext;
  videoContext.startAbility({
    parameters: { uri: videoUri },
    action: 'ohos.want.action.viewData',
    bundleName: 'com.huawei.hmos.photos',
    abilityName: 'com.huawei.hmos.photos.MainAbility'
  })
}

动态照片

拍照按钮触发动态照片拍摄,限制在3s,效果如下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时序图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

关键点说明

使能动态照片前需要分段式拍照能力。固定拍摄时间固3s。

关键代码片段

1、创建拍照输出流。

let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles;

photoOutPut = cameraManager.createPhotoOutput(photoProfile);

2、查询当前设备当前模式是否支持动态照片能力。

let isSupported: boolean = photoOutPut.isMovingPhotoSupported();

3、使能动态照片拍照能力。

if (isSupported) {
  photoOutPut.enableMovingPhoto(isMovingPhoto);
}

4、预览需要使用MovingPhotoView,长按图片会播放动图。

@State src: photoAccessHelper.MovingPhoto | undefined = undefined;

controller: MovingPhotoViewController = new MovingPhotoViewController();
MovingPhotoView({
  movingPhoto: this.src,
  controller: this.controller
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值