HarmonyOS 鸿蒙Next相机预览画面如何设置随屏幕旋转而旋转?

发布于 1周前 作者 songsunli 来自 鸿蒙OS

HarmonyOS 鸿蒙Next相机预览画面如何设置随屏幕旋转而旋转?

App按照默认方向启动 进入相机页面后 设置Window为横屏 相机预览画面与手机的方向也不一致,相机预览画面如何设置随屏幕旋转而旋转?

2 回复

在单框架应用中设置屏幕自动旋转,需要在模块配置文件module.json5中给EntryAbility设置"orientation": “auto_rotation”,再打开手机自动旋转即可。

以下内容是旋转过程画面会出现倒置的现象的解决方案

相机框架会根据屏幕方向、前后摄、相机安装方向,输出正立的旋转角度,xComponent预览时会使用这个角度做渲染;窗口自动旋转时,xComponent又转一次的话,必然会导致预览内容就不再是正立

import camera from '[@ohos](/user/ohos).multimedia.camera';

import image from '[@ohos](/user/ohos).multimedia.image';

import abilityAccessCtrl from '[@ohos](/user/ohos).abilityAccessCtrl';

import common from '[@ohos](/user/ohos).app.ability.common';

import fs from '[@ohos](/user/ohos).file.fs';

import {BusinessError} from '[@ohos](/user/ohos).base';

[@Entry](/user/Entry)

[@Component](/user/Component)

struct Index {

 [@State](/user/State) message: string = 'Hello World'

 private mXComponentController: XComponentController = new XComponentController;

 private surfaceId: string = '-1';

 [@State](/user/State) imgUrl: PixelMap | undefined = undefined;

 private context: ESObject = undefined

 private previewProfilesObj2: camera.Profile | undefined = undefined;

 private receiver: image.ImageReceiver | undefined = undefined;

 aboutToAppear() {

   //申请权限

   let context = getContext() as common.UIAbilityContext;

   abilityAccessCtrl.createAtManager().requestPermissionsFromUser(context, ['ohos.permission.CAMERA']).then(() => {

     this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 });

     // 获取Surface ID

     this.surfaceId = this.mXComponentController.getXComponentSurfaceId();

     this.createDualChannelPreview(this.surfaceId);

   });

   console.info(`surfaceId=${this.surfaceId}`);

 }

 async createDualChannelPreview(XComponentSurfaceId: string): Promise<void> {

   let cameraManager = await camera.getCameraManager(getContext() as ESObject);

   let camerasDevices: Array<camera.CameraDevice> = cameraManager.getSupportedCameras(); // 获取支持的相机设备对象

   // 获取支持的模式类型

   let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(camerasDevices[0]);

   let isSupportPhotoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_PHOTO) >= 0;

   if (!isSupportPhotoMode) {

     console.error('photo mode not support');

     return;

   }

   // 获取profile对象

   let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0]); // 获取对应相机设备profiles

   let previewProfiles: Array<camera.Profile> = profiles.previewProfiles;

   // 预览流1

   let previewProfilesObj: camera.Profile = previewProfiles[0];

   // 预览流2

   this.previewProfilesObj2 = previewProfiles[0];

   let size:image.Size = {

     height: this.previewProfilesObj2.size.height,

     width: this.previewProfilesObj2.size.width

   }

   this.receiver= image.createImageReceiver(size, image.ImageFormat.JPEG, 8)

   // 创建 预览流1 输出对象

   let previewOutput: camera.PreviewOutput = cameraManager.createPreviewOutput(previewProfilesObj, XComponentSurfaceId);

   // 创建 预览流2 输出对象

   let imageReceiverSurfaceId: string = await this.receiver.getReceivingSurfaceId();

   let previewOutput2: camera.PreviewOutput = cameraManager.createPreviewOutput(this.previewProfilesObj2, imageReceiverSurfaceId);

   // 创建cameraInput对象

   let cameraInput: camera.CameraInput = cameraManager.createCameraInput(camerasDevices[0]);

   // 打开相机

   await cameraInput.open();

   // 会话流程

   let captureSession: camera.CaptureSession = cameraManager.createCaptureSession();

   // 开始配置会话

   captureSession.beginConfig();

   // 把CameraInput加入到会话

   captureSession.addInput(cameraInput);

   // 把 预览流1 加入到会话

   captureSession.addOutput(previewOutput);

   // 把 预览流2 加入到会话

   captureSession.addOutput(previewOutput2);

   // 提交配置信息

   await captureSession.commitConfig();

   // 会话开始

   await captureSession.start();

   this.onImageArrival(this.receiver);

 }

 async onImageArrival(receiver: image.ImageReceiver): Promise<void> {

   receiver.on('imageArrival', () => {

     console.error("imageArrival callback");

     receiver.readNextImage((err, nextImage: image.Image) => {

       let a = nextImage.format

       nextImage.getComponent(image.ComponentType.JPEG, async (err, imgComponent: image.Component) => {

         if (err || imgComponent === undefined) {

           return;

         }

         this.saveImageToFile(imgComponent.byteBuffer);

         if (imgComponent.byteBuffer as ArrayBuffer) {

           let sourceOptions: image.SourceOptions = {

             sourceDensity: 120,

             sourcePixelFormat: 8, // NV21

             sourceSize: {

               height: this.previewProfilesObj2!.size.height,

               width: this.previewProfilesObj2!.size.width

             },

           }

           let imageResource = image.createImageSource(imgComponent.byteBuffer, sourceOptions);

           let imagePackerApi = image.createImagePacker();

           let packOpts: image.PackingOption = { format: "image/jpeg", quality: 98 };

           const filePath: string = getContext().cacheDir + "/image.jpg";

           let file = fs.openSync(filePath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE);

           imagePackerApi.packToFile(imageResource, file.fd, packOpts).then(() => {

             console.error('pack success: ' + filePath);

           }).catch((error: BusinessError) => {

             console.error('Failed to pack the image. And the error is: ' + error);

           })

           imageResource.createPixelMap({}).then((res)=>{

             this.imgUrl = res;

           });

         } else {

           return;

         }

         nextImage.release();

       })

     })

   })

 }

 saveImageToFile(data: ArrayBuffer) {

   const context = getContext(this);

   let filePath = context.tempDir + "/test.jpg";

   console.info("path is " + filePath);

   let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);

   fs.write(file.fd, data, (err, writeLen) => {

     if (err) {

       console.info("write failed with error message: " + err.message + ", error code: " + err.code);

     } else {

       console.info("write data to file succeed and size is:" + writeLen);

       fs.closeSync(file);

     }

   });

   // context.tempDir + "/test.jpg" 对应绝对路径 /data/app/el2/100/base/com.example.image_example/haps/Packer/temp/test.jpg (也可通过hdc shell 查询对应目录)

   // hdc file recv /data/app/el2/100/base/com.example.image_example/haps/Packer/temp/test.jpg D:\ (获取文件到本地,查看保存结果)

 }

 build() {

   Column() {

     // 创建XComponent

     XComponent({

       id: '',

       type: 'surface',

       libraryname: '',

       controller: this.mXComponentController

     })

       .onLoad(() => {

         // 设置Surface宽高(1920*1080),预览尺寸设置参考前面 previewProfilesArray 获取的当前设备所支持的预览分辨率大小去设置

         this.mXComponentController.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 });

         // 获取Surface ID

         this.surfaceId = this.mXComponentController.getXComponentSurfaceId();

         this.createDualChannelPreview(this.surfaceId);

       })

       .width('1920px')

       .height('1080px')

     Row() {

       // 将编辑好的pixelMap传递给状态变量imagePixelMap后,通过Image组件进行渲染

       Image(this.imgUrl).objectFit(ImageFit.None)

     }.width('100%').height('50%').backgroundColor('#F0F0F0')

   }

 }

}

更多关于HarmonyOS 鸿蒙Next相机预览画面如何设置随屏幕旋转而旋转?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS 鸿蒙Next系统中,要使相机预览画面随屏幕旋转而旋转,可以通过调整相机的预览显示方向来实现。具体步骤如下:

  1. 获取屏幕旋转角度: 使用系统提供的传感器或API,实时获取设备的屏幕旋转角度(如0度、90度、180度、270度)。

  2. 调整相机预览方向: 根据获取到的屏幕旋转角度,动态调整相机的预览方向。这通常涉及对相机预览帧的旋转处理。

  3. 更新UI布局: 在检测到屏幕旋转事件后,及时更新相机的预览UI布局,确保预览画面与屏幕方向一致。

  4. 确保相机资源释放: 在屏幕旋转过程中,注意正确释放和重新获取相机资源,避免资源泄露或应用崩溃。

  5. 适配不同屏幕尺寸: 考虑不同屏幕尺寸和分辨率的适配问题,确保相机预览画面在各种设备上都能正确显示。

综上所述,通过获取屏幕旋转角度、调整相机预览方向、更新UI布局以及确保资源正确释放,可以实现HarmonyOS 鸿蒙Next相机预览画面随屏幕旋转而旋转的功能。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。

回到顶部