简介
本文针对三方相机开发场景,基于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
})