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

第8章 调试方法与证据模板

本章只解决一个问题

不依赖当前测试环境,只用“历史日志 + 源码锚点 + 命令组合”说明调试技巧为什么有效。

本章边界

  1. 不讲脚本入口,不讲脚本门禁,不讲“跑脚本通过/失败”。
  2. 只讲命令怎么组合、每条命令在判定链里的作用、组合后的效果。
  3. 所有技巧都配“源码节选 + 历史日志片段”。

调试命令分层(先总览)

1:目标与窗口
  hdc list targets / 固定日志窗口文件

层2:控制链提取
  rg 过滤 Create/Open/Start/ValidateOutputProfile

层3:显示链提取
  rg 过滤 createPreviewOutput/repeatStream/QUERYBUF/SetMetadata

层4:源码回锚
  rg 函数名 + nl/sed 取行级片段

层5:结论整形
  控制链结论 + 显示链结论 + 联合结论

技巧1:双链分流过滤(最核心)

命令组合:

rg -n "GetCameraIds|CreateCameraDevice|OpenCamera|HCaptureSession::Start|ValidateOutputProfile" <hilog.txt> -S
rg -n "createPreviewOutput failed|repeatStream is nullptr|VIDIOC_QUERYBUF failed|SetMetadata Failed with -5" <hilog.txt> -S

为什么这样组合:

  1. 第一条先看控制链推进状态。
  2. 第二条再看显示链是否闭环。
  3. 两条链分开后,Start success 和黑屏可以同时成立,不冲突。

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

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 ... PreviewOutput::SetCameraApi() repeatStream is nullptr
04-02 16:28:56.299 ... HCaptureSession::Start prepare execute, sessionID: 3
04-02 16:28:56.310 ... HCaptureSession::Start execute success, sessionID: 3

效果:

  • 直接避免“只看到 Start success 就判定预览成功”的误判。

技巧2:参数非法要先回到输入创建点

命令组合:

rg -n "createPreviewOutput failed: \{\"code\":\"7400101\"\}|selected profile|previewProfile" <hilog.txt> -S
rg -n "CreatePreviewOutput\(" foundation/multimedia/camera_framework/frameworks/native/camera/src/input/camera_manager.cpp -S

为什么这样组合:

  1. 先看 App 是否选到了合法 profile。
  2. 再看 Framework CreatePreviewOutput 参数门禁是否被触发。

源码节选: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"}

效果:

  • 避免把“参数非法”误改成“驱动问题”。

技巧3:模式不匹配先看 fallback 语义,不要先改显示链

命令组合:

rg -n "ValidateOutputProfile in mode\(|fallback to NORMAL|Not in the profiles set" <hilog.txt> -S
nl -ba foundation/multimedia/camera_framework/frameworks/native/camera/src/session/capture_session.cpp | sed -n '4467,4496p'

为什么这样组合:

  1. mode(x) 失败通常是能力语义问题。
  2. 先看是否进入 NORMAL fallback,再决定是否回到 HCS 能力定义。

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

auto modeIt = profileMap.find(modeName);
if (modeIt != profileMap.end() && validateOutputProfileFunc(validateProfile, modeIt->second)) {
    return true;
}
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!

效果:

  • 降低“显示链表象故障”覆盖“模式能力根因”的概率。

技巧4:看到 QUERYBUF 失败先做缓冲语义定位

命令组合:

rg -n "VIDIOC_QUERYBUF failed|ReqBuffers|V4L2AllocBuffer" <hilog.txt> -S
nl -ba drivers/peripheral/camera/vdi_base/common/adapter/platform/v4l2/src/driver_adapter/src/v4l2_buffer.cpp | sed -n '369,380p'

为什么这样组合:

  1. 先确认 ReqBuffers 是否已成功。
  2. 再看 QUERYBUF 失败,说明 type/memory/plane 组合有问题。

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

buf.type = bufferType_;
buf.memory = memoryType_;
...
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 ... ioctl VIDIOC_QUERYBUF failed: Invalid argument
04-02 12:28:41.537 ... Creatbuffer: V4L2AllocBuffer error

效果:

  • 避免在上层反复试错,把定位快速收敛到 V4L2 缓冲协商层。

技巧5:控制链成功与显示链失败要写成并行结论

命令组合:

rg -n "HCaptureSession::Start execute success|SetMetadata Failed with -5" <hilog.txt> -S
nl -ba foundation/graphic/graphic_surface/surface/src/surface_buffer_impl.cpp | sed -n '602,611p'

为什么这样组合:

  1. Start execute success 属于控制链信号。
  2. SetMetadata Failed with -5 属于显示消费链信号。
  3. 两者同窗共现时应输出“并行结论”,不是互相否定。

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

auto dRet = displayBuffer->SetMetadata(*handle_, key, value);
...
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 ... SetMetadata Failed with -5
04-02 09:13:35.763 ... PreviewOutput::SetCameraApi() repeatStream is nullptr

效果:

  • 避免“只看一条链就下全局结论”。

命令组合模板(可直接复用在历史日志回放)

  1. 先跑双链过滤:控制链一条、显示链一条。
  2. 对每个关键信号回锚一个函数片段。
  3. 输出三句:控制链结论、显示链结论、联合结论。

常见误用与纠偏

误用 后果 纠偏
只过滤 Start 相关日志 误判“一定有画面” 同时过滤 SetMetadata/QUERYBUF/repeatStream
不做源码回锚 结论停留在关键词层 每个结论补一段函数片段
把同窗共现当直接因果 结论越界 明确“并行结论”与“直接因果”分层
一次解释太多信号 归因混乱 先一个信号一段分析,再汇总

本章最小动作

不连设备也可做:任选一个历史 hilog_x.txt,按“信号过滤 -> 源码回锚 -> 三元结论”完成一份离线分析。

下一跳建议

读第9章:看这些技巧在完整实战过程里如何按时间顺序串起来,直到问题闭环。

知识点依赖与跨章连接

本章方法块 前置章节 后续章节 跳转理由
双链分流过滤 第1章 主链调用总览 第9章 实战演练与复盘 先有总览分段,复盘时才能稳定落三元结论
参数门禁回锚 第5章 输出创建与显示绑定 第7章 异常挂点与主链归因 先确认输入语义,再做挂点归因
mode/fallback 检查 第3章 能力注入与流桥接 + 第4章 会话编排与门禁 第9章 实战演练与复盘 能力定义与会话门禁是同一条判定链
QUERYBUF 缓冲定位 第2章 驱动与硬件基线 第7章 异常挂点与主链归因 硬件缓冲语义先收敛,再归因到主链节点
显示消费链并行结论 第5章 输出创建与显示绑定 第9章 实战演练与复盘 避免把显示侧失败误写成控制链失败

信号 -> 判断 -> 动作

信号 判断 动作
Start execute successrepeatStream is nullptr 控制链推进,但预览对象链断裂 执行双链分流,回锚 AddOutput/SetCameraApi
createPreviewOutput failed: 7400101 输入参数语义不成立 selected profileCreatePreviewOutput 参数门禁
ValidateOutputProfile ... fail mode/profile 匹配失败 ParseCapability 输入并回看 fallback 路径
VIDIOC_QUERYBUF failed 底层缓冲协商失败 type/memory/plane/index,必要时分辨率降档
SetMetadata Failed with -5 显示消费链独立失败 与控制链并列写结论,不做单链覆盖判断

发布后补链

全章导航

Logo

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

更多推荐