From 4fdc3fd9b7f9b99bbf68c2c2e0e271e49c5931fe Mon Sep 17 00:00:00 2001 From: suqikuan Date: Fri, 5 Sep 2025 17:38:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E4=B8=8EREADME=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: suqikuan --- README.en.md | 2 +- README.md | 8 +- entry/obfuscation-rules.txt | 6 +- entry/src/main/ets/pages/Index.ets | 123 +++---- ...ovingPhotoView.ets => MovingPhotoPage.ets} | 17 +- entry/src/main/ets/utils/CameraShooter.ets | 197 ++++++------ entry/src/main/ets/utils/GravityUtil.ets | 50 +-- entry/src/main/ets/utils/PreviewUtil.ets | 17 +- entry/src/main/ets/utils/VideoRecorder.ets | 302 +++++++++--------- 9 files changed, 386 insertions(+), 336 deletions(-) rename entry/src/main/ets/pages/{MovingPhotoView.ets => MovingPhotoPage.ets} (61%) diff --git a/README.en.md b/README.en.md index 8a7ef90..b74ba3f 100644 --- a/README.en.md +++ b/README.en.md @@ -33,7 +33,7 @@ The application allows users to click on the circular button at the bottom to ta Use Camera Kit to take photos and use **photoAccessHelper** to save photos and videos. ## Required Permissions -1. The media permission is restricted and requires applying for [ACL](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions-in-acl), generating debugging certificates, and obtaining permission to store photos in the system gallery. +1. The media permission is restricted and requires applying for [ACL](https://developer.huawei.com/consumer/en/doc/harmonyos-guides/declare-permissions-in-acl), generating debugging certificates, and obtaining permission to store photos in the system gallery. 2. Camera permissions: **ohos.permission.CAMERA** :Used for third-party camera development scenarios; diff --git a/README.md b/README.md index b6723c3..d011eaa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 基于相机开放能力实现系统级相机 -本示例展示了如何使用HarmonyOS提供的相机开放能力,主要包括使用camera kit拍摄以及photoAccessHelper进行保存图片和视频。本示例主要适用于第三方应用调用系统相机能力,实现系统相机级别的效果和能力,比如分辨率、动图、视频防抖、连续变焦等。 +本示例展示了如何使用HarmonyOS提供的相机开放能力,主要包括使用Camera kit拍摄以及photoAccessHelper进行保存图片和视频。本示例主要适用于第三方应用调用系统相机能力,实现系统相机级别的效果和能力,比如分辨率、动图、视频防抖、连续变焦等。 ## 效果展示 @@ -20,7 +20,8 @@ │ ├──entryability │ │ └──EntryAbility.ets │ ├──pages -│ │ └──Index.ets // 主页 +│ │ ├──Index.ets // 主页 +│ │ └──MovingPhotoPage.ets // 动态照片预览页 │ └──utils │ ├──CameraShooter.ets // 拍照 │ ├──GravityUtil.ets // 重力工具类 @@ -30,7 +31,8 @@ ``` ## 实现思路 -使用camera kit拍摄以及photoAccessHelper进行保存图片和视频。 + +使用Camera kit拍摄以及photoAccessHelper进行保存图片和视频。 ## 相关权限 1、媒体库权限是受限权限,需要申请[ACL提权](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions-in-acl),生成调试证书,获得权限将照片存入系统图库 diff --git a/entry/obfuscation-rules.txt b/entry/obfuscation-rules.txt index 985b2ae..a1dfa0b 100644 --- a/entry/obfuscation-rules.txt +++ b/entry/obfuscation-rules.txt @@ -15,4 +15,8 @@ # Keep options: # -keep-property-name: specifies property names that you want to keep -# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file +# -keep-global-name: specifies names that you want to keep in the global scope +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 9a27cb8..b51a44d 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -57,7 +57,8 @@ let storage = new LocalStorage(); let videoUri: string; let currentFov: number = 1; const DEFAULT_TOP_OFFSET = - CameraConstants.IMAGE_HEIGHT + CameraConstants.MARGIN * 2 + CameraConstants.MARGIN_TOP + CameraConstants.MARGIN_HEIGHT; + CameraConstants.IMAGE_HEIGHT + CameraConstants.MARGIN * 2 + CameraConstants.MARGIN_TOP + + CameraConstants.MARGIN_HEIGHT; class CountDownTimerModifier implements ContentModifier { applyContent(): WrappedBuilder<[TextTimerConfiguration]> { @@ -110,7 +111,7 @@ struct XComponentPage { // Window object from EntryAbility windowClass = AppStorage.get('window'); // Fold status for camera auto switching - foldStatus: display.FoldStatus = display.getFoldStatus(); + foldStatus: display.FoldStatus = display.FoldStatus.FOLD_STATUS_UNKNOWN; // Icon rotate angle @State rotation: number = 0; // Show zoom info on screen @@ -178,45 +179,49 @@ struct XComponentPage { } async aboutToAppear() { - sensor.on(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => { - // Disable icon rotation when display rotation is not zero - if (display.getDefaultDisplaySync().rotation !== 0) { - this.rotation = 0; - return; - } - let degree: number = -1; - degree = this.getCalDegree(data.x, data.y, data.z); - if (degree >= 0 && (degree <= 30 || degree >= 330)) { - this.rotation = camera.ImageRotation.ROTATION_0; - } else if (degree >= 60 && degree <= 120) { - // Use ROTATION_90 when degree range is [60, 120] - this.rotation = camera.ImageRotation.ROTATION_270; - } else if (degree >= 150 && degree <= 210) { - // Use ROTATION_180 when degree range is [150, 210] - this.rotation = camera.ImageRotation.ROTATION_180; - } else if (degree >= 240 && degree <= 300) { - // Use ROTATION_270 when degree range is [240, 300] - this.rotation = camera.ImageRotation.ROTATION_90; - } - }); - this.updatePreview(getPreviewSize(isVideo)); - abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.permissions).then(() => { - setTimeout(async () => { - // Update preview when window rectangle changed - this.windowClass?.on('windowRectChange', () => { - const currFoldStatus = display.getFoldStatus(); - if (currFoldStatus !== this.foldStatus) { - // Update camera and preview when fold status changed - this.foldStatus = currFoldStatus; - cameraShooting(isVideo, cameraPosition, surfaceId, this.context, this.updatePreview); - } else { - // Update preview only - this.updatePreview(getPreviewSize(isVideo)); - } - }); - zoomRatioRange = await cameraShooting(isVideo, cameraPosition, surfaceId, this.context, this.updatePreview); - }, 200); - }); + try { + sensor.on(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => { + // Disable icon rotation when display rotation is not zero + if (display.getDefaultDisplaySync().rotation !== 0) { + this.rotation = 0; + return; + } + let degree: number = -1; + degree = this.getCalDegree(data.x, data.y, data.z); + if (degree >= 0 && (degree <= 30 || degree >= 330)) { + this.rotation = camera.ImageRotation.ROTATION_0; + } else if (degree >= 60 && degree <= 120) { + // Use ROTATION_90 when degree range is [60, 120] + this.rotation = camera.ImageRotation.ROTATION_270; + } else if (degree >= 150 && degree <= 210) { + // Use ROTATION_180 when degree range is [150, 210] + this.rotation = camera.ImageRotation.ROTATION_180; + } else if (degree >= 240 && degree <= 300) { + // Use ROTATION_270 when degree range is [240, 300] + this.rotation = camera.ImageRotation.ROTATION_90; + } + }); + this.updatePreview(getPreviewSize(isVideo)); + abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.context, this.permissions).then(() => { + setTimeout(async () => { + // Update preview when window rectangle changed + this.windowClass?.on('windowRectChange', () => { + const currFoldStatus = display.getFoldStatus(); + if (currFoldStatus !== this.foldStatus) { + // Update camera and preview when fold status changed + this.foldStatus = currFoldStatus; + cameraShooting(isVideo, cameraPosition, surfaceId, this.context, this.updatePreview); + } else { + // Update preview only + this.updatePreview(getPreviewSize(isVideo)); + } + }); + zoomRatioRange = await cameraShooting(isVideo, cameraPosition, surfaceId, this.context, this.updatePreview); + }, 200); + }); + } catch (error) { + console.error(`The updatePreview call failed. error: ${JSON.stringify(error)}`); + } } aboutToDisappear(): void { @@ -399,24 +404,24 @@ struct XComponentPage { .animation({ curve: curves.springMotion() }) .bindMenu( this.isPhoto ? this.burstCaptureView() : - [ - { - value: '1080P | 60fps', - action: (): void => { - qualityLevel = 0; - stopRecordPreview(); - videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); + [ + { + value: '1080P | 60fps', + action: (): void => { + qualityLevel = 0; + stopRecordPreview(); + videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); + } + }, + { + value: '2160P | 60fps', + action: (): void => { + qualityLevel = 1; + stopRecordPreview(); + videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); + } } - }, - { - value: '2160P | 60fps', - action: (): void => { - qualityLevel = 1; - stopRecordPreview(); - videoRecording(this.isStabilization, cameraPosition, qualityLevel, surfaceId, this.context); - } - } - ] + ] ) } .id('optionPanel') @@ -613,7 +618,7 @@ struct XComponentPage { .animation({ curve: curves.springMotion() }) .onClick(async () => { cameraPosition = cameraPosition === camera.CameraPosition.CAMERA_POSITION_BACK ? - camera.CameraPosition.CAMERA_POSITION_UNSPECIFIED : camera.CameraPosition.CAMERA_POSITION_BACK; + camera.CameraPosition.CAMERA_POSITION_UNSPECIFIED : camera.CameraPosition.CAMERA_POSITION_BACK; if (this.isPhoto) { cameraShooting(isVideo, cameraPosition, surfaceId, this.context, this.updatePreview); } else { diff --git a/entry/src/main/ets/pages/MovingPhotoView.ets b/entry/src/main/ets/pages/MovingPhotoPage.ets similarity index 61% rename from entry/src/main/ets/pages/MovingPhotoView.ets rename to entry/src/main/ets/pages/MovingPhotoPage.ets index a4a6c9c..994cbbc 100644 --- a/entry/src/main/ets/pages/MovingPhotoView.ets +++ b/entry/src/main/ets/pages/MovingPhotoPage.ets @@ -1,7 +1,8 @@ import { MovingPhotoView, MovingPhotoViewController, photoAccessHelper } from '@kit.MediaLibraryKit'; +@Entry @Component -struct TestCode { +struct MovingPhotoPage { // [Start moving_photo_view_controller] @State src: photoAccessHelper.MovingPhoto | undefined = undefined; @@ -9,11 +10,13 @@ struct TestCode { // [End moving_photo_view_controller] build() { - // [Start moving_photo_view] - MovingPhotoView({ - movingPhoto: this.src, - controller: this.controller - }) - // [End moving_photo_view] + Column() { + // [Start moving_photo_view] + MovingPhotoView({ + movingPhoto: this.src, + controller: this.controller + }) + // [End moving_photo_view] + } } } \ No newline at end of file diff --git a/entry/src/main/ets/utils/CameraShooter.ets b/entry/src/main/ets/utils/CameraShooter.ets index 3017f5f..40f1fb1 100644 --- a/entry/src/main/ets/utils/CameraShooter.ets +++ b/entry/src/main/ets/utils/CameraShooter.ets @@ -35,104 +35,107 @@ export async function cameraShooting(isVideo: boolean, cameraPosition: number, s if (isVideo) { return videoRecording(false, cameraPosition, 0, surfaceId, context, callback); } + let zoomRatioRange: number[] = []; currentContext = context; isVideo = false; releaseCamera(); // [Start camera_manager] - let cameraManager: camera.CameraManager = camera.getCameraManager(context); - // [End camera_manager] - if (!cameraManager) { - return []; - } + try { + let cameraManager: camera.CameraManager = camera.getCameraManager(context); + // [End camera_manager] - // Obtaining the Camera List - // [Start camera_Array] - let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras(); - // [End camera_Array] - if (cameraArray.length <= 0) { - return []; - } - // [Start camera_Input] - cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]); - await cameraInput.open(); - // [End camera_Input] - let sceneModes: camera.SceneMode[] = cameraManager.getSupportedSceneModes(cameraArray[cameraPosition]); - // [Start camera_OutputCap] - let cameraOutputCap: camera.CameraOutputCapability = - cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_PHOTO); - // [End camera_OutputCap] - let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0; - if (!isSupportPhotoMode) { - return []; - } - if (!cameraOutputCap) { - return []; - } - // [Start preview_ProfilesArray] - let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles; - // [End preview_ProfilesArray] - // [Start photo_Profiles_Array] - let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles.slice().reverse(); - // [End photo_Profiles_Array] + // Obtaining the Camera List + // [Start camera_Array] + let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras(); + // [End camera_Array] + if (cameraArray.length <= 0) { + return zoomRatioRange; + } + // [Start camera_Input] + cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]); + await cameraInput.open(); + // [End camera_Input] + let sceneModes: camera.SceneMode[] = cameraManager.getSupportedSceneModes(cameraArray[cameraPosition]); + // [Start camera_OutputCap] + let cameraOutputCap: camera.CameraOutputCapability = + cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_PHOTO); + // [End camera_OutputCap] + let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0; + if (!isSupportPhotoMode) { + return zoomRatioRange; + } + if (!cameraOutputCap) { + return zoomRatioRange; + } + // [Start preview_ProfilesArray] + let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles; + // [End preview_ProfilesArray] + // [Start photo_Profiles_Array] + let photoProfilesArray: camera.Profile[] = cameraOutputCap.photoProfiles.slice().reverse(); + // [End photo_Profiles_Array] - const previewProfile = getTargetPreviewProfile(CameraConstants.PHOTO_RATIO, previewProfilesArray); - const rPhotoProfilesArray = photoProfilesArray.slice().reverse(); - let photoProfile: camera.Profile | undefined = undefined; - if (previewProfile !== undefined) { - photoProfile = rPhotoProfilesArray.find((profile: camera.Profile) => { - return profile.size.height * previewProfile!.size.width === previewProfile!.size.height * profile.size.width; - }) - } - // Set preview window by callback - callback?.(getPreviewSize(false)); - // [Start preview_ProfilesArray] - previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId); - // [End preview_ProfilesArray] - if (previewOutput === undefined) { - return []; - } - // [Start photo_Profiles_Array] - photoOutPut = cameraManager.createPhotoOutput(photoProfile); - // [End photo_Profiles_Array] - if (photoOutPut === undefined) { - return []; - } - // Save Picture - setPhotoOutputCb(photoOutPut); - // [Start photo_Session] - photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; - // [End photo_Session] - if (photoSession === undefined) { - return []; - } - // [Start photo_Session1] - photoSession.beginConfig(); - photoSession.addInput(cameraInput); - photoSession.addOutput(previewOutput); - photoSession.addOutput(photoOutPut); - photoSession.setColorSpace(colorSpaceManager.ColorSpace.DISPLAY_P3); - await photoSession.commitConfig(); - await photoSession.start(); - // [End photo_Session1] + const previewProfile = getTargetPreviewProfile(CameraConstants.PHOTO_RATIO, previewProfilesArray); + const rPhotoProfilesArray = photoProfilesArray.slice().reverse(); + let photoProfile: camera.Profile | undefined = undefined; + if (previewProfile !== undefined) { + photoProfile = rPhotoProfilesArray.find((profile: camera.Profile) => { + return profile.size.height * previewProfile!.size.width === previewProfile!.size.height * profile.size.width; + }) + } + // Set preview window by callback + callback?.(getPreviewSize(false)); + // [Start preview_ProfilesArray] + previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId); + // [End preview_ProfilesArray] + if (previewOutput === undefined) { + return []; + } + // [Start photo_Profiles_Array] + photoOutPut = cameraManager.createPhotoOutput(photoProfile); + // [End photo_Profiles_Array] + if (photoOutPut === undefined) { + return []; + } + // Save Picture + setPhotoOutputCb(photoOutPut); + // [Start photo_Session] + photoSession = cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; + // [End photo_Session] + if (photoSession === undefined) { + return []; + } + // [Start photo_Session1] + photoSession.beginConfig(); + photoSession.addInput(cameraInput); + photoSession.addOutput(previewOutput); + photoSession.addOutput(photoOutPut); + photoSession.setColorSpace(colorSpaceManager.ColorSpace.DISPLAY_P3); + await photoSession.commitConfig(); + await photoSession.start(); + // [End photo_Session1] - // Check whether the device supports the flash. - let flashStatus: boolean = photoSession.hasFlash(); - if (flashStatus) { - // [Start setFlash_Mode] - photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE); - // [End setFlash_Mode] - } - // Determine whether the continuous automatic zoom mode is supported. - let focusModeStatus: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); - if (focusModeStatus) { - // Set the continuous auto zoom mode - // [Start setFocus_Mode] - photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); - // [End setFocus_Mode] + // Check whether the device supports the flash. + let flashStatus: boolean = photoSession.hasFlash(); + if (flashStatus) { + // [Start setFlash_Mode] + photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSE); + // [End setFlash_Mode] + } + // Determine whether the continuous automatic zoom mode is supported. + let focusModeStatus: boolean = photoSession.isFocusModeSupported(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); + if (focusModeStatus) { + // Set the continuous auto zoom mode + // [Start setFocus_Mode] + photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO); + // [End setFocus_Mode] + } + // Obtains the variable focal length ratio range supported by the camera. + zoomRatioRange = photoSession.getZoomRatioRange(); + } catch (error) { + console.error(`The cameraShooting call failed. error: ${JSON.stringify(error)}`); + } finally { + return zoomRatioRange; } - // Obtains the variable focal length ratio range supported by the camera. - let zoomRatioRange = photoSession.getZoomRatioRange(); - return zoomRatioRange; } export function enableLivePic(isMovingPhoto: boolean): void { @@ -222,10 +225,14 @@ function setPhotoOutputCb(photoOutput: camera.PhotoOutput): void { 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()); + try { + assetChangeRequest.saveCameraPhoto(); + await accessHelper.applyChanges(assetChangeRequest); + uri = photoAsset.uri; + AppStorage.setOrCreate('photoUri', await photoAsset.getThumbnail()); + } catch (error) { + console.error(`The setPhotoOutputCb call failed. error: ${error.code}`); + } }); } @@ -242,6 +249,8 @@ export function previewPhoto(context: Context): void { action: 'ohos.want.action.viewData', bundleName: 'com.huawei.hmos.photos', abilityName: 'com.huawei.hmos.photos.MainAbility' + }, (err: BusinessError) => { + console.error(`The previewPhoto call failed. error: ${err.code}`); }) } diff --git a/entry/src/main/ets/utils/GravityUtil.ets b/entry/src/main/ets/utils/GravityUtil.ets index 75b3809..2b222d7 100644 --- a/entry/src/main/ets/utils/GravityUtil.ets +++ b/entry/src/main/ets/utils/GravityUtil.ets @@ -32,12 +32,17 @@ function getRealData(data: sensor.GravityResponse): number { if ((x * x + y * y) * 3 < z * z) { return getDeviceDegree; } else { - let sd: Decimal = Decimal.atan2(y, -x); - let sc: Decimal = Decimal.round(Number(sd) / 3.141592653589 * 180) - getDeviceDegree = 90 - Number(sc); - getDeviceDegree = getDeviceDegree >= 0 ? getDeviceDegree % 360 : getDeviceDegree % 360 + 360; + try { + let sd: Decimal = Decimal.atan2(y, -x); + let sc: Decimal = Decimal.round(Number(sd) / 3.141592653589 * 180) + getDeviceDegree = 90 - Number(sc); + getDeviceDegree = getDeviceDegree >= 0 ? getDeviceDegree % 360 : getDeviceDegree % 360 + 360; + } catch (error) { + console.error(`The getRealData call failed. error: ${JSON.stringify(error)}`); + } finally { + return getDeviceDegree; + } } - return getDeviceDegree; } /** @@ -45,23 +50,28 @@ function getRealData(data: sensor.GravityResponse): number { * @returns */ export async function getGravity(): Promise { + let promise: Promise = Promise.resolve(0); let isSupported: boolean = false - const data = await sensor.getSensorList() - for (let i = 0; i < data.length; i++) { - if (data[i].sensorId === sensor.SensorId.GRAVITY) { - isSupported = true; - break; + try { + const data = await sensor.getSensorList() + for (let i = 0; i < data.length; i++) { + if (data[i].sensorId === sensor.SensorId.GRAVITY) { + isSupported = true; + break; + } } - } - if (!isSupported) { - console.error(`The getGravity call failed. error is device unsupported`); - return 0; - } else { - const promise: Promise = new Promise((resolve) => { - sensor.once(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => { - resolve(getRealData(data)); - }); - }) + if (!isSupported) { + console.error(`The getGravity call failed. error is device unsupported`); + } else { + promise = new Promise((resolve) => { + sensor.once(sensor.SensorId.GRAVITY, (data: sensor.GravityResponse) => { + resolve(getRealData(data)); + }); + }) + } + } catch (error) { + console.error(`The getGravity call failed. error: ${JSON.stringify(error)}`); + } finally { return promise; } } \ No newline at end of file diff --git a/entry/src/main/ets/utils/PreviewUtil.ets b/entry/src/main/ets/utils/PreviewUtil.ets index 60ed58d..ea362dc 100644 --- a/entry/src/main/ets/utils/PreviewUtil.ets +++ b/entry/src/main/ets/utils/PreviewUtil.ets @@ -13,9 +13,9 @@ * limitations under the License. */ -import { display, window } from "@kit.ArkUI"; -import { camera } from "@kit.CameraKit"; -import { CameraConstants } from "../constants/CameraConstants"; +import { display, window } from '@kit.ArkUI'; +import { camera } from '@kit.CameraKit'; +import { CameraConstants } from '../constants/CameraConstants'; /** * Get camera preview size @@ -102,6 +102,13 @@ export function getCurrentWindowSize() { * @returns screen display size */ export function getCurrentDisplaySize() { - const displaySize = display.getDefaultDisplaySync(); - return { width: displaySize.width, height: displaySize.height } as Size; + let size: Size = { width: 0, height: 0 }; + try { + const displaySize = display.getDefaultDisplaySync(); + size = { width: displaySize.width, height: displaySize.height }; + } catch (error) { + console.error(`The getCurrentDisplaySize call failed. error: ${JSON.stringify(error)}`); + } finally { + return size; + } } \ No newline at end of file diff --git a/entry/src/main/ets/utils/VideoRecorder.ets b/entry/src/main/ets/utils/VideoRecorder.ets index b60c7eb..f4906e7 100644 --- a/entry/src/main/ets/utils/VideoRecorder.ets +++ b/entry/src/main/ets/utils/VideoRecorder.ets @@ -35,155 +35,158 @@ let uri: string; export async function videoRecording(isStabilization: boolean, cameraPosition: number, qualityLevel: number, surfaceId: string, context: Context, callback?: (previewSize: Size) => void): Promise { - let cameraManager: camera.CameraManager = camera.getCameraManager(context); - if (!cameraManager) { - return []; - } - // [Start camera_array] - let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras(); - // [End camera_array] - if (cameraArray.length <= 0) { - return []; - } - let sceneModes: camera.SceneMode[] = cameraManager.getSupportedSceneModes(cameraArray[0]); - let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0; - if (!isSupportVideoMode) { - return []; - } - // [Start out_cap] - // Obtains the output stream capability supported by the camera. - let cameraOutputCap: camera.CameraOutputCapability = - cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_VIDEO); - // [End out_cap] - if (!cameraOutputCap) { - return []; - } - // [Start pre_pro_file] - let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles; - // [End pre_pro_file] - if (!previewProfilesArray) { - return []; - } - // [Start video_profile1] - let videoProfilesArray: camera.VideoProfile[] = cameraOutputCap.videoProfiles; - // [End video_profile1] - if (!videoProfilesArray) { - return []; - } + let zoomRatioRange: number[] = []; + try { + let cameraManager: camera.CameraManager = camera.getCameraManager(context); + // [Start camera_array] + let cameraArray: camera.CameraDevice[] = cameraManager.getSupportedCameras(); + // [End camera_array] + if (cameraArray.length <= 0) { + return zoomRatioRange; + } + let sceneModes: camera.SceneMode[] = cameraManager.getSupportedSceneModes(cameraArray[0]); + let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0; + if (!isSupportVideoMode) { + return zoomRatioRange; + } + // [Start out_cap] + // Obtains the output stream capability supported by the camera. + let cameraOutputCap: camera.CameraOutputCapability = + cameraManager.getSupportedOutputCapability(cameraArray[cameraPosition], camera.SceneMode.NORMAL_VIDEO); + // [End out_cap] + if (!cameraOutputCap) { + return zoomRatioRange; + } + // [Start pre_pro_file] + let previewProfilesArray: camera.Profile[] = cameraOutputCap.previewProfiles; + // [End pre_pro_file] + if (!previewProfilesArray) { + return zoomRatioRange; + } + // [Start video_profile1] + let videoProfilesArray: camera.VideoProfile[] = cameraOutputCap.videoProfiles; + // [End video_profile1] + if (!videoProfilesArray) { + return zoomRatioRange; + } - let previewProfile = getTargetPreviewProfile(CameraConstants.VIDEO_RATIO, previewProfilesArray); - // [Start video_profile1] - 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 === (previewProfile.size.height / previewProfile.size.width) * profile.size.width && - profile.frameRateRange.max === 30; + let previewProfile = getTargetPreviewProfile(CameraConstants.VIDEO_RATIO, previewProfilesArray); + // [Start video_profile1] + 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 === (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 === (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 === (previewProfile.size.height / previewProfile.size.width) * profile.size.width && + profile.frameRateRange.max === 60; + } + return undefined; + }) + // [End video_profile1] + // Set preview window by callback + callback?.(getPreviewSize(true)); + // Set the parameters based on the actual hardware range. + let aVRecorderProfile: media.AVRecorderProfile = { + audioBitrate: 48000, + audioChannels: 2, + audioCodec: media.CodecMimeType.AUDIO_AAC, + audioSampleRate: 48000, + fileFormat: media.ContainerFormatType.CFT_MPEG_4, + videoBitrate: 32000000, + videoCodec: qualityLevel === 1 && cameraPosition === 0 ? media.CodecMimeType.VIDEO_HEVC : + media.CodecMimeType.VIDEO_AVC, + videoFrameWidth: videoProfile?.size.width, + videoFrameHeight: videoProfile?.size.height, + videoFrameRate: cameraPosition === 0 ? 60 : 30, } - if (previewProfile && qualityLevel === 0) { - return profile.size.width <= 1920 && profile.size.width >= 1080 && profile.size.height >= 1080 && - profile.size.height === (previewProfile.size.height / previewProfile.size.width) * profile.size.width && - profile.frameRateRange.max === 60; + // [Start option1] + 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()}`, + rotation: cameraPosition === 0 ? 90 : 270, + location: { latitude: 30, longitude: 130 } + }; + // [End option1] + uri = videoUri; + avRecorder = await media.createAVRecorder(); + if (avRecorder === undefined) { + return zoomRatioRange; } - if (previewProfile && qualityLevel === 1 && cameraPosition === 0) { - return profile.size.width <= 4096 && profile.size.width >= 3000 && - profile.size.height === (previewProfile.size.height / previewProfile.size.width) * profile.size.width && - profile.frameRateRange.max === 60; + await avRecorder.prepare(aVRecorderConfig); + let videoSurfaceId: string | undefined = await avRecorder.getInputSurface(); + if (videoSurfaceId === undefined) { + return zoomRatioRange; } - return undefined; - }) - // [End video_profile1] - // Set preview window by callback - callback?.(getPreviewSize(true)); - // Set the parameters based on the actual hardware range. - let aVRecorderProfile: media.AVRecorderProfile = { - audioBitrate: 48000, - audioChannels: 2, - audioCodec: media.CodecMimeType.AUDIO_AAC, - audioSampleRate: 48000, - fileFormat: media.ContainerFormatType.CFT_MPEG_4, - videoBitrate: 32000000, - videoCodec: qualityLevel === 1 && cameraPosition === 0 ? media.CodecMimeType.VIDEO_HEVC : - media.CodecMimeType.VIDEO_AVC, - videoFrameWidth: videoProfile?.size.width, - videoFrameHeight: videoProfile?.size.height, - videoFrameRate: cameraPosition === 0 ? 60 : 30, - } - // [Start option1] - 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()}`, - rotation: cameraPosition === 0 ? 90 : 270, - location: { latitude: 30, longitude: 130 } - }; - // [End option1] - uri = videoUri; - avRecorder = await media.createAVRecorder(); - if (avRecorder === undefined) { - return []; - } - await avRecorder.prepare(aVRecorderConfig); - let videoSurfaceId: string | undefined = await avRecorder.getInputSurface(); - if (videoSurfaceId === undefined) { - return []; - } - // [Start video_profile1] - videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId); - // [End video_profile1] - if (videoOutput === undefined) { - return []; - } - // [Start video_session1] - videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; - // [End video_session1] - if (videoSession === undefined) { - return []; - } - // [Start begin_config] - videoSession.beginConfig(); - // [End begin_config] - // [Start open1] - cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]); - // [StartExclude open1] - if (cameraInput === undefined) { - return []; - } - // [EndExclude open1] - await cameraInput.open(); - // [End open1] - // [Start begin_config] - videoSession.addInput(cameraInput); - // [End begin_config] - // [Start pre_pro_file] - // Create previewOutput with target previewProfile from previewProfilesArray - let previewOutput: camera.PreviewOutput | undefined = cameraManager.createPreviewOutput(previewProfile, surfaceId); - // [End pre_pro_file] - if (previewOutput === undefined) { - return []; - } - // [Start begin_config] - videoSession.addOutput(previewOutput); - videoSession.addOutput(videoOutput); - await videoSession.commitConfig(); - // [StartExclude begin_config] - if (videoSession.isVideoStabilizationModeSupported(camera.VideoStabilizationMode.MIDDLE)) { - videoSession.setVideoStabilizationMode(isStabilization ? camera.VideoStabilizationMode.AUTO : - camera.VideoStabilizationMode.OFF); - } - // [EndExclude begin_config] - await videoSession.start(); - // [End begin_config] + // [Start video_profile1] + videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId); + // [End video_profile1] + if (videoOutput === undefined) { + return zoomRatioRange; + } + // [Start video_session1] + videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; + // [End video_session1] + if (videoSession === undefined) { + return zoomRatioRange; + } + // [Start begin_config] + videoSession.beginConfig(); + // [End begin_config] + // [Start open1] + cameraInput = cameraManager.createCameraInput(cameraArray[cameraPosition]); + // [StartExclude open1] + if (cameraInput === undefined) { + return zoomRatioRange; + } + // [EndExclude open1] + await cameraInput.open(); + // [End open1] + // [Start begin_config] + videoSession.addInput(cameraInput); + // [End begin_config] + // [Start pre_pro_file] + // Create previewOutput with target previewProfile from previewProfilesArray + let previewOutput: camera.PreviewOutput | undefined = cameraManager.createPreviewOutput(previewProfile, surfaceId); + // [End pre_pro_file] + if (previewOutput === undefined) { + return zoomRatioRange; + } + // [Start begin_config] + videoSession.addOutput(previewOutput); + videoSession.addOutput(videoOutput); + await videoSession.commitConfig(); + // [StartExclude begin_config] + if (videoSession.isVideoStabilizationModeSupported(camera.VideoStabilizationMode.MIDDLE)) { + videoSession.setVideoStabilizationMode(isStabilization ? camera.VideoStabilizationMode.AUTO : + camera.VideoStabilizationMode.OFF); + } + // [EndExclude begin_config] + await videoSession.start(); + // [End begin_config] - // Obtains the variable focal length ratio range supported by the camera. - let zoomRatioRange = videoSession.getZoomRatioRange(); - return zoomRatioRange; + // Obtains the variable focal length ratio range supported by the camera. + zoomRatioRange = videoSession.getZoomRatioRange(); + } catch (error) { + console.error(`The videoRecording call failed. error: ${JSON.stringify(error)}`); + } finally { + return zoomRatioRange; + } } export async function stopRecordPreview(): Promise { @@ -235,8 +238,13 @@ export async function stopRecord(): Promise { await avRecorder.stop(); // [End recorder2] await avRecorder.release(); - fileIo.closeSync(file); - return uri; + try { + fileIo.closeSync(file); + } catch (error) { + console.error(`The stopRecord call failed. error: ${JSON.stringify(error)}`); + } finally { + return uri; + } } // [Start pre_video1] @@ -247,6 +255,8 @@ export function previewVideo(context: Context, videoUri: string): void { action: 'ohos.want.action.viewData', bundleName: 'com.huawei.hmos.photos', abilityName: 'com.huawei.hmos.photos.MainAbility' + }, (err: BusinessError) => { + console.error(`The previewVideo call failed. error: ${err.code}`); }) } -- Gitee