decxin 会话总结与因果分析本文整理的是本地 Codex 会话里所有与cam_cz005/ 双目相机 /mono-capture相关的问答。范围主要覆盖 2026-06-13 到 2026-06-14 的几轮排查重点是相机是否真的接上了驱动到底是谁提供的为什么能测到 60fps为什么朋友那份代码更像 30fps“一个 H.265 编码器、一个 H.264 编码器”这句话该怎么理解YUV/YUYV、MJPEG、JPEG、USB、UVC的关系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走的是V4L2和ioctl没看到.ko、Kconfig、内核模块工程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: 30main.cpp的v4l2分支没有把fps传给V4l2BackendV4l2Backend::initialize()自己默认也是 30capabilities()里写死了 30mpp_hevc.cpp的编码参数也硬编码了 30V4L2 采集循环里解码和 IMU 解析是同步做的60fps 下更容易顶住也就是说朋友那份代码不是“请求了 60 但被相机拒绝”而是整个链路本来就按 30fps 设计。1.5 解释格式和带宽中间还专门解释了YUV/YUYV和MJPEG的区别JPEG和YUYV的关系为什么YUYV在 4000x1200 下只标 30fps而MJPEG可以到 60fpsUSB 和 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-capture里mpp_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只有 30fpsMJPEG能到 60fps本质原因是带宽。YUYV近似是裸像素流4000x1200 一帧约 9.6MB。30fps 时已经接近 288MB/s60fps 则会到 576MB/s太吃 USB3.0 的有效带宽。MJPEG先在相机端把每帧压成 JPEG再通过 USB 发出去单帧数据更小所以 60fps 能落在可承载范围里。2.6YUV/YUYV和JPEG有什么关系它们不是同一层面的概念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/s30fps 还比较现实60fps 就接近甚至超过 USB3.0 摄像头链路实际可用能力。因此厂商给出4000x120060 MJPEG和4000x120030 YUYV是合理的。2.10 “软解暴力测 60fps”这句话哪里不准确这句话容易把两个层次混在一起相机端输出能力相机通过 UVC 输出4000x120060 MJPEG主机端处理能力电脑收到 MJPEG 后要解码单线程可能解不动所以用多个 worker 并行软解主机端“软解”只是把相机已经发出来的 MJPEG 解开并不会把 30fps 变成 60fps。如果相机本身只吐 30fps主机再怎么软解也不能凭空得到 60 个真实采集帧。3. 你这边为什么能测到 60fps你自己的apps/stereo-recorder代码里关键点很直接cfg.fps 60av_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”而是相机端真的支持4000x1200 MJPEG 60fps主机侧请求了 60fps采集与解码流水线没有把帧率压回去3.1 你这份代码相对朋友代码的优势优势主要在“明确请求 60fps”和“把瓶颈隔离开”。明确请求 60fps你的 C 版默认fps60并且打开相机时显式设置framerate60和vcodecmjpeg。采集线程轻采集主循环基本只做av_read_frame拿到 MJPEG 包后立刻放进队列。USB 读包不会被解码、编码、IMU 解析拖住。MJPEG 并行解码多个独立 MJPEG decoder worker 并行处理帧再按序号重排。MJPEG 是帧内编码各帧天然适合并行。流水线拆得细采集、解码、重排、IMU/时间戳、左右路转换、左右路编码分别放到不同阶段。这样某个阶段慢不会直接卡死相机读包。左右两路编码解耦左右图各自有转换和编码路径互相影响小。对双目 60fps 来说这比单个压缩线程吞所有 stream 更容易撑住吞吐。统计更贴近真实瓶颈你的程序会打印读包 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.cpp里v4l2分支只传了设备名没有把config.encoding.fps放进backend_cfg[fps]。见 main.cpp:322。4.3 backend 默认值仍是 30V4l2Backend::initialize()里默认fps就是 30。见 v4l2_backend.cpp:132。4.4 capabilities 也写死 30capabilities()里 color / right / IMU 的 fps 都写成了 30。见 v4l2_backend.cpp:111。4.5 编码器侧也硬编码 30mpp_hevc.cpp里rc:gop、rc:fps_in_num、rc:fps_out_num都是 30。见 mpp_hevc.cpp:131。4.6 采集循环本身偏重朋友那份代码在 V4L2 loop 里同步做DQBUFMJPEG 解码IMU 解析dispatchQBUF这类串行路径在 60fps 下更容易卡住。见 v4l2_backend.cpp:452 到 v4l2_backend.cpp:560。所以“他只能测出 30”的真正原因不是相机能力不足而是代码目标、默认值和流水线设计都更贴近 30fps。4.7 朋友代码也有合理的地方会话里并没有把朋友代码简单判断为“烂代码”。更准确的评价是它对 30fps 多流采集是有工程化基础的但不适合直接拿来证明这颗相机只能 30fps。合理处包括使用标准 V4L2 接口它通过/dev/video0、VIDIOC_QUERYCAP、VIDIOC_S_FMT、VIDIOC_S_PARM、VIDIOC_DQBUF/QBUF打开和配置相机。这说明它是在 Linux UVC/V4L2 驱动上做应用层采集不是乱写私有驱动。主动设置 MJPEG 4000x1200它没有完全依赖默认格式而是会设置V4L2_PIX_FMT_MJPEG。对于 4000x1200 高分辨率这个方向是正确的。有 pipeline 分层主程序创建了CaptureThread、CompressThread、IoThread、monitor、xsens 等线程比一个巨大同步循环更容易维护。有 buffer pool实时系统里复用 buffer 能减少 malloc/free 抖动。有监控和队列深度探针这对定位吞吐问题是有价值的。但它的问题也很清楚默认目标是 30fps如果要跑 60fps配置、能力声明、编码器参数、解码策略和 QBUF 时机都需要重新梳理。4.8 关于mpp_hevc和mpp_h264mono-capture/config/v4l2.yaml里确实有-id:colorprocessor:mpp_hevcoutput_suffix:.h265-id:color_rightprocessor:mpp_h264output_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”容易误解因为工程里同时出现了三种“编码”相机输出编码相机通过 UVC 输出MJPEG或YUYV。图像编码区4000x1200 左侧 160px 是时间戳/IMU 数据条带也被称为“编码区”。主机视频编码主机把左右图再压成 H.265 或 H.264例如hevc_amf、libx265、mpp_hevc、mpp_h264。你朋友那句话大概率把第 3 种主机视频编码说成了第 1 种相机硬件能力或者把“编码区”误听成了视频编码器。因果上这会导致一个错误推论认为相机硬件只支持某种 30fps 编码路径。实际证据指向的是相机通过 MJPEG 能输出 60fps后面的 H.265/H.264 是主机端再编码。6. 可以直接复用的短结论这台相机不是靠自定义驱动才能用Linux 内核的uvcvideo已经支持它。4000x1200 60fps能成立的关键是MJPEG不是YUYV。YUYV和JPEG不在同一层前者是像素格式后者是压缩格式。你这边能测到 60fps是因为相机真支持 60fps MJPEG且你的代码链路把 60fps 真的跑通了。朋友那边更像 30fps是因为配置、默认值、编码器和采集流水线都偏向 30。“相机硬件一个 H.265、一个 H.264 编码器”目前没有证据代码里的 H.265/H.264 是主机端编码处理器。7. 相关代码和文档双目相机测试_进展与问题.mdapps/stereo-recorder/README.mdapps/stereo-recorder/src/capture_record.cmono-capture/config/v4l2.yamlmono-capture/src/main.cppmono-capture/src/backends/v4l2_backend.cppmono-capture/src/processors/mpp_hevc.cpp