鸿蒙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_AUTO设置为关闭,目前支持关闭,自动,常亮,打开。

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、付费专栏及课程。

余额充值