本文为《开源鸿蒙相机:驱动到应用预览显示》系列发布版。
当前章节:第7章 异常挂点与主链归因
代码基线:~/work/ohos5.1/nt_backclip_vendor(基于 OpenHarmony 5.1.0 Release 移植)
发布说明:使用ai codex移植调试开源鸿蒙摄像头的任务全流程在链接https://github.com/chenlong3388/codex_fix_OpenHarmony_Camera

第7章 异常挂点与主链归因

本章只解决一个问题

把“发散知识点”拆成可独立分析的故障点,再明确它和主线怎么连接、起什么作用、如何起作用。

本章方法

每个发散知识点都按同一模板展开:

  1. 独立分析:这个信号本身在说什么。
  2. 主线连接:它位于主线哪一段,前后节点分别是什么。
  3. 作用机制:它为什么会影响最终结果。
  4. 证据:源码节选 + 历史日志片段。

发散知识点1:createPreviewOutput failed: {"code":"7400101"}

独立分析

这是“输出对象创建参数不成立”信号,常见触发条件是:

  • 选不到合法 previewProfile
  • surfaceId 或尺寸格式语义不成立;
  • 传入 CreatePreviewOutput 的 profile/surface 不满足门禁。

与主线怎么连接

主线位置:
App 选 profile -> createPreviewOutput -> Framework CreatePreviewOutput

前置节点:outputCapability.previewProfiles 是否包含可用组合。
后继节点:只有 createPreviewOutput 成功,才可能进入有效 addOutput 与可见预览链。

作用与起作用方式

  • 作用:决定“预览链能否起跑”。
  • 起作用方式:在主线最前段直接阻断,导致后续会话日志可能出现“伴生推进”但预览对象为空。

源码证据

源码:applications/standard/camera/common/src/main/ets/default/camera/CameraService.ts

previewProfile = previewProfiles.find(item => item.size.width === size.width &&
  item.size.height === size.height && item.format === 1003);
if (!previewProfile) {
  previewProfile = previewProfiles.find(item => item.size.width === size.width && item.size.height === size.height);
}
if (!previewProfile && previewProfiles.length > 0) {
  previewProfile = previewProfiles[0];
}
this.mPreviewOutput = this.mCameraManager.createPreviewOutput(previewProfile, surfaceId);

源码:foundation/multimedia/camera_framework/frameworks/native/camera/src/input/camera_manager.cpp

CHECK_ERROR_RETURN_RET_LOG((serviceProxy == nullptr) || (surface == nullptr), CameraErrorCode::INVALID_ARGUMENT,
    "CreatePreviewOutput serviceProxy is null or previewOutputSurface/profile is null");
CHECK_ERROR_RETURN_RET_LOG((profile.GetCameraFormat() == CAMERA_FORMAT_INVALID) || (profile.GetSize().width == 0)
    || (profile.GetSize().height == 0), CameraErrorCode::INVALID_ARGUMENT,
    "CreatePreviewOutput invalid fomrat or width or height is zero");

日志证据

日志:tmp_test/ov5648_api_debug_0402_091325/hilog_x.txt

04-02 09:13:35.701 ... createPreviewOutput failed: {"code":"7400101"}
04-02 09:13:35.725 ... mPreviewOutput: undefined
04-02 09:13:35.764 ... createSession failed: {"code":"7400101"}

发散知识点2:ValidateOutputProfile in mode(x) ... fail

独立分析

这是“会话输出准入失败”信号。核心含义:

  • 当前 mode 下,输出 profile 不在支持集合中;
  • 即使 fallback 到 NORMAL 也可能仍不匹配。

与主线怎么连接

主线位置:
createSession -> AddOutput -> ValidateOutputProfile

前置节点:能力注入(basic/extend/profile)是否与 mode/format/size 一致。
后继节点:校验失败会导致输出无法有效加入会话,或者进入“半成功”状态。

作用与起作用方式

  • 作用:决定“输出对象能否被当前模式接受”。
  • 起作用方式:在会话准入门禁处拒绝不匹配组合,触发后续黑屏或对象链异常。

源码证据

源码:foundation/multimedia/camera_framework/frameworks/native/camera/src/session/capture_session.cpp

auto modeName = GetMode();
...
bool result = std::any_of(profiles.begin(), profiles.end(), [&validateProfile](const auto& profile) {
    return validateProfile == profile;
});
CHECK_ERROR_PRINT_LOG(!result, "CaptureSession::ValidateOutputProfile fail! Not in the profiles set.");
if (modeName != SceneMode::NORMAL) {
    auto normalIt = profileMap.find(SceneMode::NORMAL);
    ...
}

日志证据

日志:tmp_test/ov5648_api_debug_0402_162846/hilog_x.txt

04-02 16:28:56.240 ... ValidateOutputProfile in mode(1): w(1280),h(960),f(2000), profiles size is:1
04-02 16:28:56.240 ... CaptureSession::ValidateOutputProfile fail!

发散知识点3:PreviewOutput::SetCameraApi() repeatStream is nullptr

独立分析

这是“预览输出对象链未闭环”信号。它不等于 open 失败,而是指:

  • AddOutput 阶段没拿到可用 IStreamRepeat
  • 预览输出对象类型/绑定路径存在断点。

与主线怎么连接

主线位置:
AddOutput -> GetStream -> (PREVIEW) SetCameraApi

前置节点:createPreviewOutput 是否产出有效预览输出对象。
后继节点:若此处为空,后续即使 Start 成功也可能无可见预览。

作用与起作用方式

  • 作用:决定“预览流是否真正挂到会话里”。
  • 起作用方式:在“会话看似继续”的情况下制造“显示侧无有效预览流”的假成功。

源码证据

源码:foundation/multimedia/camera_framework/frameworks/native/camera/src/session/capture_session.cpp

sptr<IStreamCommon> stream = output->GetStream();
IStreamRepeat* repeatStream = nullptr;
if (output->GetOutputType() == CAPTURE_OUTPUT_TYPE_PREVIEW) {
    repeatStream = static_cast<IStreamRepeat*>(stream.GetRefPtr());
}
if (repeatStream) {
    errItemCode = repeatStream->SetCameraApi(apiCompatibleVersion);
} else {
    MEDIA_ERR_LOG("PreviewOutput::SetCameraApi() repeatStream is nullptr");
}

日志证据

日志:tmp_test/ov5648_api_debug_0402_162846/hilog_x.txt

04-02 16:28:56.241 ... CaptureSession::AddOutput StreamType = 1
04-02 16:28:56.241 ... PreviewOutput::SetCameraApi() repeatStream is nullptr
04-02 16:28:56.310 ... HCaptureSession::Start execute success, sessionID: 3

发散知识点4:ioctl VIDIOC_QUERYBUF failed: Invalid argument

独立分析

这是 V4L2 缓冲协商失败信号。常见原因:

  • bufferType/memoryType/plane/index 与驱动能力不一致;
  • 建缓冲参数语义错误。

与主线怎么连接

主线位置:
Start -> HDI Capture -> ReqBuffers -> QUERYBUF

前置节点:stream 配置和缓冲模型参数。
后继节点:QUERYBUF 失败后,后续无稳定帧进入显示链。

作用与起作用方式

  • 作用:决定“底层是否能产出可用帧缓冲”。
  • 起作用方式:在产帧前直接失败,导致上层看到黑屏或间歇失败。

源码证据

源码:drivers/peripheral/camera/vdi_base/common/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp

buf.type = bufferType_;
buf.memory = memoryType_;
buf.index = (uint32_t)frameSpec->buffer_->GetIndex();
if (bufferType_ == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
    buf.m.planes = planes;
    buf.length = 1;
}
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
    CAMERA_LOGE("error: ioctl VIDIOC_QUERYBUF failed: %{public}s\n", strerror(errno));
    return RC_ERROR;
}

日志证据

日志:tmp_test/ov5648_api_debug_0402_122831/hilog_x.txt

04-02 12:28:41.537 ... V4L2AllocBuffer() V4L2AllocBuffer enter fd 9
04-02 12:28:41.537 ... V4L2AllocBuffer() error: ioctl VIDIOC_QUERYBUF failed: Invalid argument
04-02 12:28:41.537 ... CreatBuffer() error: Creatbuffer: V4L2AllocBuffer error

发散知识点5:SetMetadata Failed with -5

独立分析

这是显示消费链失败信号。含义是:

  • 显示层元数据写入失败;
  • 即使控制链推进成功,显示侧仍可能不可见。

与主线怎么连接

主线位置:
帧到达 Surface -> SetMetadata -> Render 消费

前置节点:上游输出流是否带来可消费元数据。
后继节点:显示链失败会直接表现为黑屏或不可见画面。

作用与起作用方式

  • 作用:决定“最终画面能否在显示端被消费”。
  • 起作用方式:在主线末段失败,制造“控制链成功但用户仍黑屏”的现象。

源码证据

源码:foundation/graphic/graphic_surface/surface/src/surface_buffer_impl.cpp

auto dRet = displayBuffer->SetMetadata(*handle_, key, value);
if (dRet == GRAPHIC_DISPLAY_SUCCESS) {
    return GSERROR_OK;
}
BLOGE("SetMetadata Failed with %{public}d", dRet);
return GSERROR_HDI_ERROR;

日志证据

日志:tmp_test/ov5648_api_debug_0402_091325/hilog_x.txt

04-02 09:13:35.757 ... Bufferqueue: <surface_buffer_impl.cpp:610-SetMetadata>: SetMetadata Failed with -5
04-02 09:13:35.763 ... PreviewOutput::SetCameraApi() repeatStream is nullptr

发散知识点与主线的统一结论

  1. 7400101:主线前段(输入语义)断点。
  2. ValidateOutputProfile fail:主线中段(模式能力)断点。
  3. repeatStream is nullptr:主线中后段(对象绑定)断点。
  4. VIDIOC_QUERYBUF failed:主线底层(缓冲协商)断点。
  5. SetMetadata Failed with -5:主线末段(显示消费)断点。

本章最小动作

选择一个真实日志窗口,按“这5个知识点”逐条写:

  • 独立分析;
  • 主线连接;
  • 作用机制;
  • 源码与日志双证据。

知识点依赖与跨章连接

本章挂点 前置章节 后续章节 跳转理由
7400101 参数门禁 第5章 输出创建与显示绑定 第6章 函数级链路走读 先确认 profile/surface 语义,再落到函数级门禁
ValidateOutputProfile fail 第3章 能力注入与流桥接 第4章 会话编排与门禁 能力来源决定 mode/profile 是否可被会话接受
repeatStream is nullptr 第5章 输出创建与显示绑定 第6章 函数级链路走读 输出对象链路与 AddOutput/SetCameraApi 强关联
VIDIOC_QUERYBUF failed 第2章 驱动与硬件基线 第8章 调试方法与证据模板 先收敛底层缓冲语义,再固化命令剧本
SetMetadata Failed with -5 第5章 输出创建与显示绑定 第9章 实战演练与复盘 控制链与显示链并行结论需在复盘中显式落地

信号 -> 判断 -> 动作

信号 判断 动作
createPreviewOutput failed: 7400101 输出创建参数不成立 回查 previewProfile + surfaceId,再核 CreatePreviewOutput 门禁
ValidateOutputProfile ... fail mode/profile 能力不匹配 对齐 HCS basic/extend 与当前 mode 能力表
repeatStream is nullptr 预览输出对象链断裂 createPreviewOutput -> AddOutput -> SetCameraApi 回溯
VIDIOC_QUERYBUF failed 缓冲语义不匹配 先核 type/memory/plane/index,必要时先降档验证
SetMetadata Failed with -5 显示消费链失败 与控制链结果并列输出,不把它误判为 open 失败

发布后补链

全章导航

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐