DECXIN-3261V1DECXIN-3362V1_会话总结
2026/7/23 16:06:44 网站建设 项目流程

decxin 会话总结与因果分析

本文整理的是本地 Codex 会话里,所有与cam_cz005/ 双目相机 /mono-capture相关的问答。
范围主要覆盖 2026-06-13 到 2026-06-14 的几轮排查,重点是:

  • 相机是否真的接上了
  • 驱动到底是谁提供的
  • 为什么能测到 60fps
  • 为什么朋友那份代码更像 30fps
  • “一个 H.265 编码器、一个 H.264 编码器”这句话该怎么理解
  • YUV/YUYVMJPEGJPEGUSBUVC的关系
  • 4000x1200、160px 编码区、1920px sensor 宽度的来源

1. 会话时间线

1.1 先确认设备是否连上

第一轮问题是“现在是不是连接了一个相机”。系统检查后确认:

  • 识别到DECXIN Camera
  • 设备节点是/dev/video0
  • 驱动名是uvcvideo
  • 当前默认格式是4000x1200 MJPG @ 30fps

这一步的关键结论是:相机不是“靠某个项目自带驱动”才可用,而是被 Linux 的 UVC 通用摄像头驱动识别了。
相关证据在 mono-capture 的 V4L2 后端 和会话记录里。

1.2 追问“是不是自定义驱动”

你怀疑朋友可能自己写了一套驱动,因为“Orange Pi 应该不会插上就有驱动”。
后来把/home/orangepi/code/home/orangepi/mono-capture等目录翻了一遍,看到的主要是应用层采集代码,不是内核驱动工程:

  • mono-capture走的是V4L2ioctl
  • 没看到.koKconfig、内核模块工程
  • uvcvideo其实已经编进内核,是 builtin,不是额外的自定义.ko

结论变成:这台机子上能直接识别相机,不是因为厂商给了单独驱动包,而是因为 Linux 内核自带 UVC 驱动已经支持它。

1.3 测 30 秒视频帧率

之后用你自己的采集思路测相机实际帧率。最后结论是:

  • 稳态出帧约60.09 fps
  • 把启动预热算进去,30 秒墙钟均值约57.33 fps

这个结果说明相机本体不是“只会 30fps”。如果测试方法把启动等待算进去,数值会被拉低。

1.4 对比朋友代码

你让我对比你朋友那版mono-capture,为什么他看起来只能测出 30fps。
最后查出来不是单一原因,而是几层叠在一起:

  • config/v4l2.yaml里本来就是fps: 30
  • main.cppv4l2分支没有把fps传给V4l2Backend
  • V4l2Backend::initialize()自己默认也是 30
  • capabilities()里写死了 30
  • mpp_hevc.cpp的编码参数也硬编码了 30
  • V4L2 采集循环里解码和 IMU 解析是同步做的,60fps 下更容易顶住

也就是说,朋友那份代码不是“请求了 60 但被相机拒绝”,而是整个链路本来就按 30fps 设计。

1.5 解释格式和带宽

中间还专门解释了:

  • YUV/YUYVMJPEG的区别
  • JPEGYUYV的关系
  • 为什么YUYV在 4000x1200 下只标 30fps,而MJPEG可以到 60fps
  • USB 和 UVC 的区别
  • 为什么 MJPEG 是相机端能力,不是电脑软件临时“压”出来的

这部分的核心结论是:

  • YUYV是接近原始的像素流,带宽非常大
  • MJPEG是相机端压缩后的帧流,能显著降低 USB 传输压力
  • UVC是 USB 上的摄像头标准,USB是底层通道

1.6 澄清 H.265 / H.264 编码器归属

你朋友提到“硬件是双目一个 H.265 编码器、一个 H.264 编码器”。会话里最后形成的判断是:
这句话如果说的是Orange Pi 主机端编码路径,可以对应到mono-capture配置里左路mpp_hevc、右路mpp_h264;但如果说的是相机本体内部自带一个 H.265、一个 H.264 视频编码器,目前没有证据。

仓库和资料里能确认的是:

  • 相机 UVC 输出格式是MJPEG / YUV2(YUYV),不是直接输出 H.264/H.265。
  • cam_cz005的 C 版是在主机端把 MJPEG 解码后再编码成双路 H.265。
  • mono-capturempp_hevc/mpp_h264是 Rockchip MPP 主机硬件编码器处理器,不是相机内部编码器。
  • 厂商文档里的“编码区”是图像里的时间戳/IMU 数据条带,不是视频编解码器。

2. 关键问答归纳

2.1 相机到底是不是已经连上

是。系统识别到了相机,/dev/video0可用,驱动是uvcvideo
这说明当前工作路径是 Linux 内核 UVC 驱动 + V4L2 应用层采集。

2.2 默认格式怎么得出的

默认格式来自v4l2-ctl --all一类的设备查询,看到的是:

  • 分辨率4000x1200
  • 像素格式MJPG
  • 帧率30fps

但这只是当时协商出来的当前工作点,不等于设备上限。

2.3 驱动是谁写的,在哪里

这里说的“驱动”,主要是 Linux 内核里的uvcvideo,不是项目里的.cpp文件。
更准确地说:

  • 厂商相机端有自己的固件或硬件实现
  • Linux 主机侧用的是内核自带 UVC 驱动
  • 你的程序和朋友程序都只是通过 V4L2 去调用这个驱动

2.4 4000x1200 里为什么是 160 + 1920 + 1920

这个来源有三层:

  • 设备文档里写明整幅是4000x1200
  • 文档和真机样例里说明最左边 160px 是编码区
  • 代码里按sensor_w = (width - code_width) / 2切图

所以:

(4000 - 160) / 2 = 1920

这不是拍脑袋猜的,是“文档 + 真机帧布局 + 代码切图逻辑”共同决定的。

2.5 为什么YUYV只有 30fps,MJPEG能到 60fps

本质原因是带宽。

YUYV近似是裸像素流,4000x1200 一帧约 9.6MB。
30fps 时已经接近 288MB/s,60fps 则会到 576MB/s,太吃 USB3.0 的有效带宽。

MJPEG先在相机端把每帧压成 JPEG,再通过 USB 发出去,单帧数据更小,所以 60fps 能落在可承载范围里。

2.6YUV/YUYVJPEG有什么关系

它们不是同一层面的概念:

  • YUYV是像素组织方式
  • JPEG是压缩编码方式

JPEG 内部通常会先转成类似 YCbCr 的亮度/色度空间,再做压缩;所以它和 YUV 有关联,但不是一回事。

2.7 MJPEG 是硬件写好的,还是软件做的

更准确的说法是:这是相机端能力,不是主机软件“现算”出来的。

电脑端通过 UVC 请求mjpeg只是协商输出格式。
相机如果不支持,就不会按这个格式吐流。
所以 MJPEG 是设备端暴露出来的能力,通常由相机芯片里的编码模块和固件协同完成。

2.8 USB 和 UVC 有什么区别

  • USB是物理接口和底层总线
  • UVC是跑在 USB 上的摄像头标准

可以把 USB 理解成“路”,把 UVC 理解成“这辆摄像头车该怎么跑”。

2.9 USB3.0 带宽通常是多少

USB 3.0 标称速率通常是:

5 Gbps = 625 MB/s

但摄像头实际能稳定使用的有效带宽要扣掉协议编码、USB 包头、UVC 视频流开销、主控和线材余量、驱动缓冲等。
所以实际稳定视频有效带宽通常明显低于 625MB/s,会话里粗略按300~450 MB/s这个量级理解。

这也解释了:

4000x1200 YUYV 30fps ≈ 288 MB/s 4000x1200 YUYV 60fps ≈ 576 MB/s

30fps 还比较现实,60fps 就接近甚至超过 USB3.0 摄像头链路实际可用能力。因此厂商给出4000x1200@60 MJPEG4000x1200@30 YUYV是合理的。

2.10 “软解暴力测 60fps”这句话哪里不准确

这句话容易把两个层次混在一起:

  • 相机端输出能力:相机通过 UVC 输出4000x1200@60 MJPEG
  • 主机端处理能力:电脑收到 MJPEG 后要解码,单线程可能解不动,所以用多个 worker 并行软解

主机端“软解”只是把相机已经发出来的 MJPEG 解开,并不会把 30fps 变成 60fps。
如果相机本身只吐 30fps,主机再怎么软解也不能凭空得到 60 个真实采集帧。


3. 你这边为什么能测到 60fps

你自己的apps/stereo-recorder代码里,关键点很直接:

  • cfg.fps = 60
  • av_dict_set(..., "framerate", ...)
  • av_dict_set(..., "vcodec", "mjpeg", 0)
  • 采集主线程只读包,不做重活
  • MJPEG 解码拆成多个 worker 并行
  • 左右路切图和编码也拆开

对应代码可见 capture_record.c:533、capture_record.c:580、capture_record.c:634。

所以你测到的 60fps,不是“硬解出来的假 60”,而是:

  1. 相机端真的支持4000x1200 MJPEG @ 60fps
  2. 主机侧请求了 60fps
  3. 采集与解码流水线没有把帧率压回去

3.1 你这份代码相对朋友代码的优势

优势主要在“明确请求 60fps”和“把瓶颈隔离开”。

  1. 明确请求 60fps
    你的 C 版默认fps=60,并且打开相机时显式设置framerate=60vcodec=mjpeg

  2. 采集线程轻
    采集主循环基本只做av_read_frame,拿到 MJPEG 包后立刻放进队列。USB 读包不会被解码、编码、IMU 解析拖住。

  3. MJPEG 并行解码
    多个独立 MJPEG decoder worker 并行处理帧,再按序号重排。MJPEG 是帧内编码,各帧天然适合并行。

  4. 流水线拆得细
    采集、解码、重排、IMU/时间戳、左右路转换、左右路编码分别放到不同阶段。这样某个阶段慢,不会直接卡死相机读包。

  5. 左右两路编码解耦
    左右图各自有转换和编码路径,互相影响小。对双目 60fps 来说,这比单个压缩线程吞所有 stream 更容易撑住吞吐。

  6. 统计更贴近真实瓶颈
    你的程序会打印读包 fps、队列深度、丢帧和各级平均耗时,更容易判断到底卡在采集、解码、转换还是编码。

一句话概括:你的代码是围绕“持续4000x1200 MJPEG 60fps”专门设计的多级并行流水线;朋友代码更像通用采集系统,结构完整,但默认和关键路径更偏 30fps。


4. 为什么朋友那份代码更像 30fps

朋友代码的问题不在一个点,而在整条链:

4.1 配置本身就是 30

/home/orangepi/mono-capture/config/v4l2.yaml里写的是:

fps:30

所以即使把配置传进去,目标仍然是 30fps。
相关位置见 v4l2.yaml:49。

4.2 V4L2 分支没有把 fps 传进去

main.cppv4l2分支只传了设备名,没有把config.encoding.fps放进backend_cfg["fps"]
见 main.cpp:322。

4.3 backend 默认值仍是 30

V4l2Backend::initialize()里默认fps就是 30。
见 v4l2_backend.cpp:132。

4.4 capabilities 也写死 30

capabilities()里 color / right / IMU 的 fps 都写成了 30。
见 v4l2_backend.cpp:111。

4.5 编码器侧也硬编码 30

mpp_hevc.cpprc:goprc:fps_in_numrc:fps_out_num都是 30。
见 mpp_hevc.cpp:131。

4.6 采集循环本身偏重

朋友那份代码在 V4L2 loop 里同步做:

  • DQBUF
  • MJPEG 解码
  • IMU 解析
  • dispatch
  • QBUF

这类串行路径在 60fps 下更容易卡住。
见 v4l2_backend.cpp:452 到 v4l2_backend.cpp:560。

所以“他只能测出 30”的真正原因不是相机能力不足,而是代码目标、默认值和流水线设计都更贴近 30fps。

4.7 朋友代码也有合理的地方

会话里并没有把朋友代码简单判断为“烂代码”。更准确的评价是:它对 30fps 多流采集是有工程化基础的,但不适合直接拿来证明这颗相机只能 30fps。

合理处包括:

  1. 使用标准 V4L2 接口
    它通过/dev/video0VIDIOC_QUERYCAPVIDIOC_S_FMTVIDIOC_S_PARMVIDIOC_DQBUF/QBUF打开和配置相机。这说明它是在 Linux UVC/V4L2 驱动上做应用层采集,不是乱写私有驱动。

  2. 主动设置 MJPEG 4000x1200
    它没有完全依赖默认格式,而是会设置V4L2_PIX_FMT_MJPEG。对于 4000x1200 高分辨率,这个方向是正确的。

  3. 有 pipeline 分层
    主程序创建了CaptureThreadCompressThreadIoThread、monitor、xsens 等线程,比一个巨大同步循环更容易维护。

  4. 有 buffer pool
    实时系统里复用 buffer 能减少 malloc/free 抖动。

  5. 有监控和队列深度探针
    这对定位吞吐问题是有价值的。

但它的问题也很清楚:默认目标是 30fps;如果要跑 60fps,配置、能力声明、编码器参数、解码策略和 QBUF 时机都需要重新梳理。

4.8 关于mpp_hevcmpp_h264

mono-capture/config/v4l2.yaml里确实有:

-id:"color"processor:"mpp_hevc"output_suffix:".h265"-id:"color_right"processor:"mpp_h264"output_suffix:".h264"

但是这只能说明朋友代码把左路交给主机端 MPP H.265,右路交给主机端 MPP H.264
它不能证明相机本体内部有“一路 H.265 编码器、一路 H.264 编码器”。

相机侧目前能从文档和 V4L2 能力确认的是MJPEG / YUYV输出。
主机侧 H.265/H.264 是接收 MJPEG 后再编码出来的。


5. 因果分析

5.1 为什么相机能连上

因为相机符合 UVC 设备模型,Linux 内核里已经有uvcvideo驱动。
结果是:插上就能枚举成/dev/video0,应用层再走 V4L2 就能取流。

5.2 为什么 MJPEG 能跑到 60fps

因为 USB 带宽固定,而 MJPEG 把每帧压小了。
在有效带宽没超限时,60fps 就能稳定传。

5.3 为什么 YUYV 更慢

因为它几乎不压缩,单帧字节数太大。
同样分辨率下,YUYV 会更快撞到 USB3.0 的实际承载上限。

5.4 为什么你比朋友更容易测到 60fps

因为你的路径是“先把相机请求到 60fps,再把 CPU 热点拆开”。
朋友那边是“配置默认 30 + 后端默认 30 + 编码默认 30 + loop 偏重”。
两条路的设计目标根本不同。

5.5 为什么“MJPEG 是相机能力,不是软件临时压缩”

因为主机端只是请求格式,真正输出压缩包的是相机设备。
如果设备不支持 MJPEG,主机请求也没用。

5.6 为什么“一个 H.265、一个 H.264”容易误解

因为工程里同时出现了三种“编码”:

  1. 相机输出编码
    相机通过 UVC 输出MJPEGYUYV

  2. 图像编码区
    4000x1200 左侧 160px 是时间戳/IMU 数据条带,也被称为“编码区”。

  3. 主机视频编码
    主机把左右图再压成 H.265 或 H.264,例如hevc_amflibx265mpp_hevcmpp_h264

你朋友那句话大概率把第 3 种主机视频编码,说成了第 1 种相机硬件能力;或者把“编码区”误听成了视频编码器。
因果上,这会导致一个错误推论:认为相机硬件只支持某种 30fps 编码路径。实际证据指向的是,相机通过 MJPEG 能输出 60fps,后面的 H.265/H.264 是主机端再编码。


6. 可以直接复用的短结论

  1. 这台相机不是靠自定义驱动才能用,Linux 内核的uvcvideo已经支持它。
  2. 4000x1200 @ 60fps能成立的关键是MJPEG,不是YUYV
  3. YUYVJPEG不在同一层,前者是像素格式,后者是压缩格式。
  4. 你这边能测到 60fps,是因为相机真支持 60fps MJPEG,且你的代码链路把 60fps 真的跑通了。
  5. 朋友那边更像 30fps,是因为配置、默认值、编码器和采集流水线都偏向 30。
  6. “相机硬件一个 H.265、一个 H.264 编码器”目前没有证据;代码里的 H.265/H.264 是主机端编码处理器。

7. 相关代码和文档

  • 双目相机测试_进展与问题.md
  • apps/stereo-recorder/README.md
  • apps/stereo-recorder/src/capture_record.c
  • mono-capture/config/v4l2.yaml
  • mono-capture/src/main.cpp
  • mono-capture/src/backends/v4l2_backend.cpp
  • mono-capture/src/processors/mpp_hevc.cpp

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询