openharmony摄像头驱动到应用浏览显示 第1章 主链调用总览

张开发
2026/4/11 21:16:50 15 分钟阅读

分享文章

openharmony摄像头驱动到应用浏览显示 第1章 主链调用总览
本文为《开源鸿蒙相机驱动到应用预览显示》系列发布版。当前章节第1章 主链调用总览图表代码基线~/work/ohos5.1/nt_backclip_vendor基于 OpenHarmony 5.1.0 Release 移植发布说明源码路径与命令可直接用于复现。第1章 主链调用总览图表本章目标这章只做一件事把“应用点预览”到“屏幕看到画面”的主链走通。你读完后要能做到说清楚每一层做了什么不是只记函数名。拿着日志能快速判断断在 App、Service、VDI、还是 V4L2。按源码路径直接跳转到关键函数。这一章怎么读最省力推荐阅读节拍先看“主链一图看全”建立空间感。再看“参数流向图”建立参数的时间感。最后按 1~5 步时序把源码锚点串起来。这样读的好处是不会陷入“每个函数都懂一点但连不成完整因果链”的常见困境。主链一图看全先建立空间感App(ArkTS)CameraService.createPreviewOutput() -createSession(begin/addInput/addOutput/commit/start)|vFramework(Native)CameraManager::CreatePreviewOutput()|vCameraServiceHCaptureSession::CommitConfig()-HStreamOperator::CreateStreams()-HStreamOperator::CommitStreams()-HStreamOperator::StartPreviewStream()|vVDIStreamOperatorVdiImpl::CreateStreams/CommitStreams|vV4L2VIDIOC_REQBUFS-VIDIOC_QBUF-DQBUF|vSurface/XComponent- 屏幕显示参数流向图回答“值从哪来到哪生效”[ArkTS previewProfile] width/height/format |vCameraManager::CreatePreviewOutput(profile,...) - metaFormat w/h 传给 serviceProxy-CreatePreviewOutput |vHStreamCommon::SetStreamInfo streamInfo.width_/height_/format_ |vVDI StreamInfoToStreamConfiguration config.width/height/format |vV4L2 buffer与format配置生效 不一致 - QUERYBUF/QBUF失败术语桥把同一个概念在不同层的名字对齐你在日志里看到的词在代码里的常见对象这两个词为什么会被混淆previewProfileProfile / StreamInfo / StreamConfiguration都在描述“预览规格”只是层级不同modeSceneMode / operationMode一个偏业务场景一个偏提交流程outputCaptureOutput / IStreamRepeat / streamId上层是对象下层是流句柄start successSESSION_STARTED会话启动成功不等于显示链已经可见为什么这样分层设计优点与取舍设计点为什么这样设计优点代价/取舍不这样会怎样App 先选profile再建输出把“业务目标分辨率/格式”前置避免后面盲建流错误尽量早暴露定位更快App 侧要处理更多能力匹配逻辑容易在 Service/VDI 才爆参数错排障成本高Framework 做参数合法性门禁在跨进程前拦住明显非法参数减少无效 IPC 与脏状态传播多一层校验代码非法参数会在更下层才触发日志更难读Service 使用CommitConfig - Start状态机把“配置正确”和“开始运行”拆成两个确定阶段状态清晰、可回滚、可并发管理状态机理解成本更高直接 start 容易出现半配置态运行VDI 再做一次CheckStreamsSupported让设备能力做最终裁决避免把不支持配置压到驱动崩溃提交路径更长驱动层才发现不支持错误更重V4L2 用REQBUFS/QUERYBUF/QBUF三段式先申请、再核验、再入队保证缓冲闭环缓冲问题可分段定位需要维护更多中间状态黑屏时只能看到“无帧”定位粒度差按时间顺序走一遍每步都给源码锚点1) App入口先选 profile再绑 surface作用决定“要什么规格的预览流”。源码锚点applications/standard/camera/common/src/main/ets/default/camera/CameraService.ts:251源码截取publicasynccreatePreviewOutput(surfaceId:string, mode:string) {constsize SettingManager.getInstance().getPreviewSize(mode);letpreviewProfiles this.outputCapability.previewProfiles; previewProfile previewProfiles.find(itemitem.size.width size.width item.size.height size.height item.format1003);Log.info(${TAG}createPreviewOutput selected profile:${JSON.stringify(previewProfile)});this.mPreviewOutputthis.mCameraManager.createPreviewOutput(previewProfile, surfaceId); }这一段在整体里的意义输入mode、能力集previewProfiles、surfaceId。输出mPreviewOutput后续会话要用。典型失败createPreviewOutput failed: 7400101常见是 profile 不匹配。设计优点把“业务选择”前置在 App错误能更早暴露不会延迟到驱动侧。2) Framework把 profile 变成可下发的 native 流参数作用把 ArkTS 选到的 profile 转给 service 层。源码锚点foundation/multimedia/camera_framework/frameworks/native/camera/src/input/camera_manager.cpp:530源码截取int CameraManager::CreatePreviewOutput(Profile profile, sptrSurfacesurface, sptrPreviewOutput*pPreviewOutput) { CHECK_ERROR_RETURN_RET_LOG((profile.GetCameraFormat() CAMERA_FORMAT_INVALID) || (profile.GetSize().width 0) || (profile.GetSize().height 0), CameraErrorCode::INVALID_ARGUMENT,CreatePreviewOutput invalid fomrat...); camera_format_t metaFormat GetCameraMetadataFormat(profile.GetCameraFormat()); int32_t retCode serviceProxy-CreatePreviewOutput( surface-GetProducer(), metaFormat,profile.GetSize().width,profile.GetSize().height, streamRepeat); }这一段在整体里的意义输入profile.format/width/height。输出serviceProxy-CreatePreviewOutput(...)参数。门禁宽高为 0、format 无效会直接返回。设计优点跨层前做合法性收敛避免错误参数污染后续状态机。3) Service会话编排commit 时做校验并链接输入输出作用把“配置态”转成“可运行态”。源码锚点foundation/multimedia/camera_framework/services/camera_service/src/hcapture_session.cpp:651foundation/multimedia/camera_framework/services/camera_service/src/hcapture_session.cpp:550源码截取ACommit主干int32_t HCaptureSession::CommitConfig(){ errorCode ValidateSession();CHECK_ERROR_RETURN(errorCode! CAMERA_OK); errorCode LinkInputAndOutputs();CHECK_ERROR_RETURN_LOG(errorCode! CAMERA_OK,...Failed to commit config...); stateMachine_.Transfer(CaptureSessionState::SESSION_CONFIG_COMMITTED); }源码截取B输出门禁int32_t HCaptureSession::ValidateSessionOutputs(){ auto hStreamOperatorSptr GetStreamOperator();CHECK_ERROR_RETURN_RET_LOG((hStreamOperatorSptrnullptr||hStreamOperatorSptr-GetStreamsSize()0), CAMERA_INVALID_SESSION_CFG,HCaptureSession::ValidateSessionOutputs No outputs present); return CAMERA_OK; }这一段在整体里的意义输入已 add 的 input/output。输出提交成功后的 session 状态迁移。门禁没输出流、mode 不合法、链接失败都会阻断。设计优点将“能不能运行”与“开始运行”分离便于定位 commit 期与 start 期问题。4) Service-HDI/VDI创建流并提交流作用把逻辑流变成设备可执行流。源码锚点foundation/multimedia/camera_framework/services/camera_service/src/hstream_operator.cpp:1055foundation/multimedia/camera_framework/services/camera_service/src/hstream_operator.cpp:1609foundation/multimedia/camera_framework/services/camera_service/src/hstream_common.cpp:170drivers/peripheral/camera/vdi_base/v4l2/src/stream_operator/stream_operator_vdi_impl.cpp:213drivers/peripheral/camera/vdi_base/v4l2/src/stream_operator/stream_operator_vdi_impl.cpp:312源码截取AService侧 CreateCommitint32_t HStreamOperator::CreateAndCommitStreams(...){intretCode CreateStreams(streamInfos);CHECK_ERROR_RETURN_RET(retCode! CAMERA_OK,retCode); returnCommitStreams(deviceSettings,operationMode); }源码截取BVDI参数落地voidStreamOperatorVdiImpl::StreamInfoToStreamConfiguration(StreamConfiguration scg, const VdiStreamInfoinfo) { scg.width info.width_; scg.height info.height_; PixelFormat pf static_castPixelFormat(info.format_); scg.format BufferAdapter::PixelFormatToCameraFormat(pf); }源码截取CService侧把流规格写入 StreamInfovoid HStreamCommon::SetStreamInfo(StreamInfo_V1_1 streamInfo){ streamInfo.v1_0.width_ width_; streamInfo.v1_0.height_ height_; streamInfo.v1_0.format_ pixelFormat; streamInfo.v1_0.bufferQueue_ newBufferProducerSequenceable(producer_); }这一段在整体里的意义输入StreamInfo(width/height/format/type)。输出StreamConfigurationCommitStreams。门禁CheckStreamsSupported返回不支持时直接失败。设计优点设备能力做最终裁决减少“上层以为能跑、底层实际不支持”的错位。5) 启动预览与V4L2缓冲真正决定“有没有帧”作用流启动成功后缓冲队列是否正常决定是否能显示。源码锚点foundation/multimedia/camera_framework/services/camera_service/src/hstream_operator.cpp:864drivers/peripheral/camera/vdi_base/common/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp:75drivers/peripheral/camera/vdi_base/common/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp:185源码截取AStartPreviewint32_tHStreamOperator::StartPreviewStream(...){for(auto item : repeatStreams) {autocurStreamRepeat CastStreamHStreamRepeat(item);if(curStreamRepeat-GetRepeatStreamType() ! RepeatStreamType::PREVIEW)continue; errorCode curStreamRepeat-Start(settings);if(errorCode ! CAMERA_OK) {MEDIA_ERR_LOG(HStreamOperator::Start(), Failed to start preview, rc: %{public}d, errorCode);break; } } }源码截取BV4L2缓冲门禁if(ioctl(fd, VIDIOC_REQBUFS, req) 0) {CAMERA_LOGE(does not support memory mapping %{public}s\n,strerror(errno)); return RC_ERROR; }...intrc ioctl(fd, VIDIOC_QBUF, buf);if(rc 0) {CAMERA_LOGE(ioctl VIDIOC_QBUF failed: %{public}s\n,strerror(errno)); return RC_ERROR; }这一段在整体里的意义输入已提交流 buffer 配置。输出有帧进入 surface。门禁REQBUFS/QBUF任一失败显示链直接断。设计优点缓冲链路分段可观测能把“格式问题”和“队列问题”拆开定位。一眼判断“断在哪层”章节级决策表观测信号优先怀疑层级第一检查点第二检查点下一章建议createPreviewOutput failed: 7400101App/Framework 参数匹配App profile 选型Framework 参数门禁第5章ValidateSessionOutputs No outputs presentService 会话组装addOutput 是否执行Commit 前状态机第4章repeatStream is nullptrFramework 到 Service 输出绑定AddOutput 返回值outputType/streamType 对齐第6章VIDIOC_QUERYBUF/QBUF failedV4L2 缓冲链format/plane 一致性memory type 匹配第2章 第8章Start success 但黑屏显示链或缓冲链Surface 绑定BufferQueue 投递第5章 第8章关键参数速查初学者高频参数是什么从哪读在哪生效常见坑previewProfile.width/height/format预览输出规格ArkTS 能力集previewProfilesService/VDI 建流与 V4L2 缓冲选了不支持组合mode(operationMode)会话工作模式会话配置阶段CommitStreams/CheckStreamsSupportedmode 与能力表不一致streamType流用途预览/录像/拍照addOutput 时确定Start 路径选择把拍照流异常误判成预览链异常故障分叉图从日志到动作现象start 成功但黑屏 - A. 看 App 是否出现 createPreviewOutput failed/7400101- 是先修 profile 选型能力集 width/height/format - 否继续 - B. 看 CommitConfig 前后是否有 ValidateSessionOutputs / LinkInputAndOutputs 错误 - 是先修会话组装input/output/mode - 否继续 - C. 看 V4L2 是否有 REQBUFS/QBUF 错误 - 是先修格式与缓冲模型一致性 - 否继续 - D. 查 surface 绑定与尺寸一致性显示链章节知识点连接把本章和后续章节绑紧本章解决“主链时序与层间职责”。第3章会细化“能力注入为什么会影响这里的 profile 分叉”。第4章会细化“Commit/Start 门禁为什么决定时序是否可持续”。第5章会细化“createPreviewOutput/surface 为什么决定最后是否可见”。第6章会把这章的 5 个阶段下钻到函数级输入输出。知识点依赖与跨章连接本章显式块本章阶段前置依赖下一跳章节跳转理由驱动与能力准备阶段第2章参数基线第3章需要把 HCS 参数注入链讲透会话组装与门禁阶段第3章能力结果第4章需要理解 Commit/Start 的状态机门禁输出绑定与可见性阶段第4章会话状态第5章需要理解 profile/surface 绑定为何影响黑屏函数级归因阶段第1章整体时序第6章需要把阶段断点压缩成函数断点发散异常归口阶段第1章分叉图第7章、第8章需要把异常挂点化并固化为可执行剧本信号 - 判断 - 动作旅程速查信号判断动作createPreviewOutput failed: 7400101旅程断在“输出创建”阶段先核previewProfile.width/height/format再跳第5章ValidateSessionOutputs No outputs present旅程断在“会话组装”阶段回查 addOutput 顺序与输出对象有效性跳第4章repeatStream is nullptr旅程断在“预览流绑定”阶段检查 PREVIEW 输出类型与绑定对象跳第6章VIDIOC_QUERYBUF/QBUF failed旅程断在“缓冲建队”阶段降档验证并核对格式/plane跳第2章第8章Start success Bufferqueue -5控制链已推进显示链断裂进入显示链分支排障跳第5章第7章本章最小动作拿一段你自己的日志只做 4 步找selected profile找CommitConfig前后错误找StartPreview是否报错找REQBUFS/QBUF是否失败。四步就能把问题先定位到“配置链”还是“缓冲显示链”。下一跳建议想看每个函数更细粒度输入输出去第6章。想系统看会话门禁和状态机去第4章。想专攻黑屏/尺寸不一致去第2章和第3章。本章一句话小结本章把“现象 - 层级 - 函数段 - 最小动作”这条判断链建立起来后续章节不再重复讲全旅程而是各自把其中一个关键段讲深讲透。发布后补链上一篇待回填下一篇待回填

更多文章