当前位置: 首页 > news >正文

720P / 1080P / 4K / 高画质——HarmonyOS PreconfigType 和 Preconfig

文章目录

      • 什么是 preconfig?
      • 支持的档位
      • 使用流程
      • 完整的 CameraService 实现
      • 页面里加个画质选择器
      • 录像模式下也可以用
      • preconfig vs 手动配置,代码量对比
      • 几个注意点
      • 写在最后

写过相机分辨率适配代码的人都知道这有多烦:要遍历设备支持的所有 profile,筛宽高比,防止拉伸,预览/拍照/录像还要分别处理,不同手机支持的分辨率也不一样……

光分辨率配置就能写几百行。

后来发现了 preconfig,代码量直接缩到 6 行。

什么是 preconfig?

preconfig(预配置)是 HarmonyOS 的"一键设定画质档位"方案:

  • 你只需要告诉系统"我要 1080P 的画质"
  • 系统自动帮你选好预览/拍照/录像的最优分辨率组合
  • 不同手机会自动适配

类比一下:就像点外卖时选"小份/中份/大份",不需要你指定"我要 200g 鸡腿、50g 米饭……"

支持的档位

// 画质档位enumPreconfigType{PRECONFIG_720P=0,// 720×1280,省流量,速度快PRECONFIG_1080P=1,// 1080×1920,日常使用推荐PRECONFIG_4K=2,// 2160×3840,高清,但文件大PRECONFIG_HIGH_QUALITY=3// 最高画质(支持 HDR),色彩最丰富}// 画面比例enumPreconfigRatio{PRECONFIG_RATIO_1_1=0,// 1:1 方形(适合头像、朋友圈)PRECONFIG_RATIO_4_3=1,// 4:3(传统照片比例)PRECONFIG_RATIO_16_9=2// 16:9(全面屏手机默认比例)}

组合起来就是:你说要"1080P + 16:9",系统帮你配好一切。

使用流程

关键点preconfig必须在beginConfig之前调用。

完整的 CameraService 实现

这里为了避免命名冲突 在文件名后面多加一个D 注意区分

// model/CameraServiceD.etsimport{camera}from'@kit.CameraKit';import{BusinessError}from'@kit.BasicServicesKit';import{display}from'@kit.ArkUI';constTAG='CameraService';classCameraServiceD{privatecameraManager:camera.CameraManager;privatecameraInput:camera.CameraInput|undefined=undefined;privatepreviewOutput:camera.PreviewOutput|undefined=undefined;privatephotoOutput:camera.PhotoOutput|undefined=undefined;privatesession:camera.PhotoSession|undefined=undefined;constructor(context:Context){this.cameraManager=camera.getCameraManager(context);}/** * 用 preconfig 初始化相机,指定画质档位和画面比例 */asyncinitCameraWithPreconfig(surfaceId:string,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promise<void>{try{awaitthis.releaseAll();constcameras=this.cameraManager.getSupportedCameras();constdevice=cameras.find(d=>d.cameraPosition===camera.CameraPosition.CAMERA_POSITION_BACK);if(!device)return;this.cameraInput=this.cameraManager.createCameraInput(device);awaitthis.cameraInput.open();// 不传 profile,让 preconfig 决定分辨率this.previewOutput=this.cameraManager.createPreviewOutput(surfaceId);this.photoOutput=this.cameraManager.createPhotoOutput();awaitthis.buildSessionWithPreconfig(this.cameraInput,this.previewOutput,this.photoOutput,preconfigType,configRatio);}catch(e){console.error(TAG,`initCameraWithPreconfig failed:${JSON.stringify(e)}`);}}privateasyncbuildSessionWithPreconfig(cameraInput:camera.CameraInput,previewOutput:camera.PreviewOutput,photoOutput:camera.PhotoOutput,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promise<void>{constsession=this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO)ascamera.PhotoSession;// 先检查是否支持,不检查直接调 preconfig 会抛异常letisSupported=false;try{isSupported=session.canPreconfig(preconfigType,configRatio);}catch(e){console.error(TAG,`canPreconfig error:${(easBusinessError).code}`);return;}if(!isSupported){console.warn(TAG,`preconfig not supported, should fallback to manual config`);return;}// 一行搞定分辨率配置session.preconfig(preconfigType,configRatio);console.info(TAG,`preconfig applied: type=${preconfigType}, ratio=${configRatio}`);// 之后按正常流程走session.beginConfig();session.addInput(cameraInput);session.addOutput(previewOutput);session.addOutput(photoOutput);awaitsession.commitConfig();// 顺手设一下预览旋转角try{constdisplayRotation=display.getDefaultDisplaySync().rotation;constimageRotation=displayRotation*camera.ImageRotation.ROTATION_90;constpreviewRotation=previewOutput.getPreviewRotation(imageRotation);previewOutput.setPreviewRotation(previewRotation,false);}catch(e){console.error(TAG,`setPreviewRotation failed`);}awaitsession.start();this.session=session;console.info(TAG,'preconfig session started');}asyncreleaseAll():Promise<void>{awaitthis.session?.stop();awaitthis.previewOutput?.release();awaitthis.photoOutput?.release();awaitthis.cameraInput?.close();awaitthis.session?.release();this.session=undefined;this.previewOutput=undefined;this.photoOutput=undefined;this.cameraInput=undefined;}}exportdefaultCameraServiceD;

页面里加个画质选择器

// pages/CameraServicePage.etsimportCameraServiceDfrom'../model/CameraServiceD';import{camera}from'@kit.CameraKit';@Entry@Componentstruct CameraServicePage{privatepreconfigOptions:SelectOption[]=[{value:'720P 省流量'},{value:'1080P 推荐'},{value:'4K 高清'},{value:'最高画质 HDR'}];privateratioOptions:SelectOption[]=[{value:'1:1 方形'},{value:'4:3 传统'},{value:'16:9 全屏'}];@StateselectedPreconfig:number=1;// 默认 1080P@StateselectedRatio:number=2;// 默认 16:9privatemXComponentController:XComponentController=newXComponentController();privatesurfaceId:string='';privateCameraServiceD:CameraServiceD|undefined=undefined;privategetPreconfigType():camera.PreconfigType{constmap=[camera.PreconfigType.PRECONFIG_720P,camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigType.PRECONFIG_4K,camera.PreconfigType.PRECONFIG_HIGH_QUALITY];returnmap[this.selectedPreconfig]??camera.PreconfigType.PRECONFIG_1080P;}privategetPreconfigRatio():camera.PreconfigRatio{constmap=[camera.PreconfigRatio.PRECONFIG_RATIO_1_1,camera.PreconfigRatio.PRECONFIG_RATIO_4_3,camera.PreconfigRatio.PRECONFIG_RATIO_16_9];returnmap[this.selectedRatio]??camera.PreconfigRatio.PRECONFIG_RATIO_16_9;}privateasyncreinitCamera():Promise<void>{constcontext=this.getUIContext().getHostContext()!;this.CameraServiceD=newCameraServiceD(context);awaitthis.CameraServiceD.initCameraWithPreconfig(this.surfaceId,this.getPreconfigType(),this.getPreconfigRatio());}build(){Column(){// 顶部画质和比例选择Row({space:16}){Text('画质:').fontSize(14).fontColor(Color.White)Select(this.preconfigOptions).selected(this.selectedPreconfig).onSelect(async(index)=>{this.selectedPreconfig=index;if(this.surfaceId)awaitthis.reinitCamera();})Text('比例:').fontSize(14).fontColor(Color.White)Select(this.ratioOptions).selected(this.selectedRatio).onSelect(async(index)=>{this.selectedRatio=index;if(this.surfaceId)awaitthis.reinitCamera();})}.width('100%').padding({left:16,top:8,right:16,bottom:8})// 相机预览XComponent({type:XComponentType.SURFACE,controller:this.mXComponentController}).width('100%').layoutWeight(1).onLoad(async()=>{this.surfaceId=this.mXComponentController.getXComponentSurfaceId();awaitthis.reinitCamera();})}.width('100%').height('100%').backgroundColor(Color.Black)}}

录像模式下也可以用

asyncfunctionbuildVideoSessionWithPreconfig(cameraManager:camera.CameraManager,cameraInput:camera.CameraInput,previewOutput:camera.PreviewOutput,videoOutput:camera.VideoOutput,preconfigType:camera.PreconfigType,configRatio:camera.PreconfigRatio):Promise<void>{constsession=cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO)ascamera.VideoSession;if(!session.canPreconfig(preconfigType,configRatio))return;session.preconfig(preconfigType,configRatio);session.beginConfig();session.addInput(cameraInput);session.addOutput(previewOutput);session.addOutput(videoOutput);awaitsession.commitConfig();awaitsession.start();}

preconfig vs 手动配置,代码量对比

手动配置分辨率(传统方式)大概要写这么多:

// 手动配置:遍历 profiles,匹配宽高比,选最优分辨率……constcapability=cameraManager.getSupportedOutputCapability(device,sceneMode);constpreviewProfiles=capability.previewProfiles;letoptimalProfile:camera.Profile|undefined;letmaxHeight=0;for(constprofileofpreviewProfiles){if(profile.format!==1003)continue;constratio=profile.size.width/profile.size.height;if(Math.abs(ratio-targetRatio)>0.2)continue;if(profile.size.height>=maxHeight){optimalProfile=profile;maxHeight=profile.size.height;}}// 还需要对 photoProfiles 和 videoProfiles 做同样的事……

preconfig 方式:

// 就这两行,其他不管if(session.canPreconfig(camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigRatio.PRECONFIG_RATIO_16_9)){session.preconfig(camera.PreconfigType.PRECONFIG_1080P,camera.PreconfigRatio.PRECONFIG_RATIO_16_9);}

几个注意点

必须先canPreconfigpreconfig:跳过检查直接调会抛异常,别省这一行。

preconfig要在beginConfig之前调用:顺序不能乱。

旧设备可能不支持:API 20 以下的设备canPreconfig可能返回false,要做 fallback 处理。

切换画质需要重建 Session:调了preconfig后需要重新commitConfig+start,不能在 Session 运行中途切换。

写在最后

preconfig 真的让我省了很多时间。如果不是需要精确控制分辨率(比如 AI 摄像头要求特定输入尺寸),直接用 preconfig 就好了。
嗯这里得说下 哪个尺寸设计有点问题 各位同学在写的时候注意下哈 ,我是真的不想调试了

http://www.jsqmd.com/news/786622/

相关文章:

  • AI智能体本地记忆中枢Guild:基于MCP协议实现持久化认知协作
  • Flutter for OpenHarmony 校园闲置跳蚤市场APP 实战DAY4:发布闲置页面+表单校验+本地存储提交
  • OpenPawz/OPIDE:构建宠物健康数据开放生态的技术架构与实践
  • 混合信号神经形态芯片与脉冲神经网络在线学习算法
  • License Manager软件授权管理系统v1.1.2发布:新增配置模块,优化多项功能
  • OpenClaw热潮退去,用户吐槽部署繁琐、性价比低,Hermes成替代之选
  • RGBW LED矩阵调光技术与LT3965驱动方案详解
  • Zilliz Skill:构建标准化技能库,增强大语言模型工具调用能力
  • NiMH电池模拟锂电池的电源管理方案设计与实现
  • 那个从不加班的同事,晋升却比我快,我偷学了他的工作流
  • 2026年4月岗亭出售厂家推荐,岗亭售货亭/岗亭移动厕所/移动岗亭/停车场岗亭/成品移动岗亭,岗亭实力厂家口碑推荐 - 品牌推荐师
  • AI创作全链路实战:从代码生成到视觉海报批量制作完整指南
  • CANN/pypto argsort排序索引
  • ChatLLM.cpp:纯C++本地大模型推理引擎部署与实战指南
  • 毕业两年了,25岁转行网络安全来得及吗?网络运维安全培训+就业(职等你来)
  • Modern-Cursors-v2:现代化鼠标光标主题的设计、安装与深度定制指南
  • Tracciatto:为现代Ruby项目设计的VS Code深度调试扩展
  • 哪里可以找到 Linux 简介教程?
  • 抽蓄电站加劲环压力明管结构可靠性智能优化【附模型】
  • ComfyUI-Bridge:AI绘画工作流转换工具,实现SD WebUI到ComfyUI的无缝迁移
  • 基于Zilliz-Skill框架构建AI智能体技能:从原理到工程实践
  • FastbootEnhance:Windows上最直观的Fastboot工具箱,告别命令行恐惧症
  • claud code 学习记录
  • CoolRunner-II CPLD低功耗设计与DataGATE技术解析
  • 2026届学术党必备的六大AI写作助手推荐榜单
  • ARM与Thumb指令集架构解析及优化实践
  • 告别“凭感觉编程”:AI应用开发的工程化避坑指南与OpenSpec实践
  • 技术分享的内卷化:从知识传播到表演竞赛的异化
  • 从零构建文档问答技能:RAG架构、LangChain实践与OpenClaw集成
  • 信息安全工程师-病毒、木马、蠕虫技术原理与防御基础