相机数据采集保存
介绍
本示例主要展示了相机的相关功能 接口实现相机的预览拍照功能。
效果预览
使用说明
- 弹出是否允许“CameraSample”使用相机?点击“允许”
- 弹出是否允许“CameraSample”使用麦克风?点击“允许”
- 进入预览界面,预览正常,点击拍照按钮,跳转到图片预览页面,跳转正常,图片预览页面显示当前所拍照的图片,显示正常
- 进入预览界面,预览正常,点击拍照按钮,跳转到图片预览页面,跳转正常,图片预览页面显示当前所拍照的图片,显示正常,退出应用并进入图库应用,第一张图片显示为刚刚拍照的图片,拍照正常
- 点击图片预览页面的左上角返回按钮,重新进入预览页面,预览正常
- 进入预览界面,预览正常,滑动变焦条,变焦条上方显示变焦值,显示正常,并且预览界面随着变焦条的滑动放大或缩小,预览正常
- 进入预览界面,预览正常,点击预览显示区域进行对焦,对焦正常
- 进入预览界面,预览正常,点击“拍照”旁边的“录像”切换到录像模式,预览正常,点击录像按钮,开始录像,录像正常,点击停止录像按钮,跳转到录像预览界面,跳转正常,点击视频播放按钮,播放正常
具体实现
- 相机功能接口实现在CameraService.ets,源码参考:[CameraService.ets]
/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { camera } from '@kit.CameraKit';
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { JSON } from '@kit.ArkTS';
import { fileIo } from '@kit.CoreFileKit';
import { GlobalContext } from '../common/utils/GlobalContext';
import Logger from '../common/utils/Logger';
import { Constants } from '../common/Constants';
const TAG: string = 'CameraService';
export class SliderValue {
min: number = 1;
max: number = 6;
step: number = 0.1;
}
class CameraService {
private cameraManager: camera.CameraManager | undefined = undefined;
private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = [];
private cameraInput: camera.CameraInput | undefined = undefined;
private previewOutput: camera.PreviewOutput | undefined = undefined;
private photoOutput: camera.PhotoOutput | undefined = undefined;
private videoOutput: camera.VideoOutput | undefined = undefined;
private avRecorder: media.AVRecorder | undefined = undefined;
private session: camera.PhotoSession | camera.VideoSession | undefined = undefined;
private handlePhotoAssetCb: (photoAsset: photoAccessHelper.PhotoAsset) => void = () => {
};
private curCameraDevice: camera.CameraDevice | undefined = undefined;
private isRecording: boolean = false;
// 推荐拍照分辨率之一
private photoProfileObj: camera.Profile = {
format: 2000,
size: {
width: 1920,
height: 1080
}
};
// 推荐预览分辨率之一
private previewProfileObj: camera.Profile = {
format: 1003,
size: {
width: 1920,
height: 1080
}
};
// 推荐录像分辨率之一
private videoProfileObj: camera.VideoProfile = {
format: 1003,
size: {
width: 1920,
height: 1080
},
frameRateRange: {
min: 30,
max: 60
}
};
private curSceneMode: camera.SceneMode = camera.SceneMode.NORMAL_PHOTO;
constructor() {
}
setSavePictureCallback(callback: (photoAsset: photoAccessHelper.PhotoAsset) => void): void {
this.handlePhotoAssetCb = callback;
}
setSceneMode(sceneMode: camera.SceneMode): void {
this.curSceneMode = sceneMode;
}
getSceneMode(): camera.SceneMode {
return this.curSceneMode;
}
getPreviewProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined {
let previewProfiles = cameraOutputCapability.previewProfiles;
if (previewProfiles.length < 1) {
return undefined;
}
let index = previewProfiles.findIndex((previewProfile: camera.Profile) => {
return previewProfile.size.width === this.previewProfileObj.size.width &&
previewProfile.size.height === this.previewProfileObj.size.height &&
previewProfile.format === this.previewProfileObj.format;
});
if (index === -1) {
return undefined;
}
return previewProfiles[index];
}
getPhotoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.Profile | undefined {
let photoProfiles = cameraOutputCapability.photoProfiles;
if (photoProfiles.length < 1) {
return undefined;
}
let index = photoProfiles.findIndex((photoProfile: camera.Profile) => {
return photoProfile.size.width === this.photoProfileObj.size.width &&
photoProfile.size.height === this.photoProfileObj.size.height &&
photoProfile.format === this.photoProfileObj.format;
});
if (index === -1) {
return undefined;
}
return photoProfiles[index];
}
getVideoProfile(cameraOutputCapability: camera.CameraOutputCapability): camera.VideoProfile | undefined {
let videoProfiles = cameraOutputCapability.videoProfiles;
if (videoProfiles.length < 1) {
return undefined;
}
for (let i = 0; i < videoProfiles.length; i++) {
Logger.info(TAG, `getVideoProfile: ${JSON.stringify(videoProfiles[i])}`);
}
let index = videoProfiles.findIndex((videoProfile: camera.VideoProfile) => {
return videoProfile.size.width === this.videoProfileObj.size.width &&
videoProfile.size.height === this.videoProfileObj.size.height &&
videoProfile.format === this.videoProfileObj.format &&
videoProfile.frameRateRange.min <= Constants.MAX_VIDEO_FRAME &&
videoProfile.frameRateRange.max <= Constants.MAX_VIDEO_FRAME;
});
if (index === -1) {
return undefined;
}
return videoProfiles[index];
}
isSupportedSceneMode(cameraManager: camera.CameraManager, cameraDevice: camera.CameraDevice): boolean {
let sceneModes = cameraManager.getSupportedSceneModes(cameraDevice);
if (sceneModes === undefined) {
return false;
}
let index = sceneModes.findIndex((sceneMode: camera.SceneMode) => {
return sceneMode === this.curSceneMode;
});
if (index === -1) {
return false;
}
return true;
}
/**
* 初始化相机功能
* @param surfaceId - Surface 的 ID
* @param cameraDeviceIndex - 相机设备索引
* @returns 无返回值
*/
async initCamera(surfaceId: string, cameraDeviceIndex: number): Promise<void> {
Logger.debug(TAG, `initCamera cameraDeviceIndex: ${cameraDeviceIndex}`);
try {
await this.releaseCamera();
// 获取相机管理器实例
this.cameraManager = this.getCameraManagerFn();
if (this.cameraManager === undefined) {
Logger.error(TAG, 'cameraManager is undefined');
return;
}
// 获取支持指定的相机设备对象
this.cameras = this.getSupportedCamerasFn(this.cameraManager);
if (this.cameras.length < 1 || this.cameras.length < cameraDeviceIndex + 1) {
return;
}
this.curCameraDevice = this.cameras[cameraDeviceIndex];
let isSupported = this.isSupportedSceneMode(this.cameraManager, this.curCameraDevice);
if (!isSupported) {
Logger.error(TAG, 'The current scene mode is not supported.');
return;
}
let cameraOutputCapability =
this.cameraManager.getSupportedOutputCapability(this.curCameraDevice, this.curSceneMode);
let previewProfile = this.getPreviewProfile(cameraOutputCapability);
if (previewProfile === undefined) {
Logger.error(TAG, 'The resolution of the current preview stream is not supported.');
return;
}
this.previewProfileObj = previewProfile;
// 创建previewOutput输出对象
this.previewOutput = this.createPreviewOutputFn(this.cameraManager, this.previewProfileObj, surfaceId);
if (this.previewOutput === undefined) {
Logger.error(TAG, 'Failed to create the preview stream.');
return;
}
// 监听预览事件
this.previewOutputCallBack(this.previewOutput);
if (this.curSceneMode === camera.SceneMode.NORMAL_PHOTO) {
let photoProfile = this.getPhotoProfile(cameraOutputCapability);
if (photoProfile === undefined) {
Logger.error(TAG, 'The resolution of the current photo stream is not supported.');
return;
}
this.photoProfileObj = photoProfile;
// 创建photoOutPut输出对象
this.photoOutput = this.createPhotoOutputFn(this.cameraManager, this.photoProfileObj);
if (this.photoOutput === undefined) {
Logger.error(TAG, 'Failed to create the photo stream.');
return;
}
} else if (this.curSceneMode === camera.SceneMode.NORMAL_VIDEO) {
let videoProfile = this.getVideoProfile(cameraOutputCapability);
if (videoProfile === undefined) {
Logger.error(TAG, 'The resolution of the current video stream is not supported.');
return;
}
this.videoProfileObj = videoProfile;
this.avRecorder = await this.createAVRecorder();
if (this.avRecorder === undefined) {
Logger.error(TAG, 'Failed to create the avRecorder.');
return;
}
await this.prepareAVRecorder();
let videoSurfaceId = await this.avRecorder.getInputSurface();
// 创建videoOutPut输出对象
this.videoOutput = this.createVideoOutputFn(this.cameraManager, this.videoProfileObj, videoSurfaceId);
if (this.videoOutput === undefined) {
Logger.error(TAG, 'Failed to create the video stream.');
return;
}
}
// 创建cameraInput输出对象
this.cameraInput = this.createCameraInputFn(this.cameraManager, this.curCameraDevice);
if (this.cameraInput === undefined) {
Logger.error(TAG, 'Failed to create the camera input.');
return;
}
// 打开相机
let isOpenSuccess = await this.cameraInputOpenFn(this.cameraInput);
if (!isOpenSuccess) {
Logger.error(TAG, 'Failed to open the camera.');
return;
}
// 镜头状态回调
this.onCameraStatusChange(this.cameraManager);
// 监听CameraInput的错误事件
this.onCameraInputChange(this.cameraInput, this.curCameraDevice);
// 会话流程
await this.sessionFlowFn(this.cameraManager, this.cameraInput, this.previewOutput, this.photoOutput,