Skip to content

GalaxyPatrick/DualCameraRecord

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

双目高精度同步采集

基于 Jetson Orin + Argus API 的高精度双目同步采集。

Compiled and Testd with L4T 36.4.7 on Jetson Orin Nano

项目使用公共构建文件 CMakeLists.txt,同时构建多个目标,其中基本环境路径、公共库依赖移植自 jetson_multimedia_api/samples/Rules.mk

cd dual_camera_record && mkdir build
# 构建
cmake -S . -B build
# 编译
cmake --build build

./bin 下是二进制备份:

bin\
    * 老版本,源代码已被迭代
    dual_camera_record
    dual_camera_mp4
    * 20260202 版
    DualRecordMp4-2k
    DualRecordMp4-4k
    * Newest Version
    DualRecordMp4-260505

./src All Source files with headers Here.
其他都是测试生成文件,gitignore。

Start Up with build

# cd dual_camera_record/
mkdir <your_build_dir>
cmake -B <your_build_dir> -S .
cmake --build <your_build_dir>

Start program with script

mkdir outputs/
./script/2kdual.sh 120 5000
# or
./script/4kdual.sh 120 5000

Explanation

PublicDefine 公共模块

包含一些公共常用头文件引用、宏定义。声明并定义 Application 的全局参数,包含分辨率、帧率、缓冲区大小等,使用 namespace 区分。

DualRecordMp4

一个双目同步采集视频流,软件编码为 MP4 的工具。
源文件:./src/dual_record_mp4.cpp

牢记Jetson Orin Nano 系列没有硬件编码器,诸如 NVENCNvVideoEncoder::createVideoEncodernvv4l2h264enc 等都无法使用!

$ ./build/DualRecordMp4  -h
Usage: DualRecordMP4 [OPTIONS]
Options:
  -r        Set output resolution WxH [Default 1640x1232]
  -o        Set output prefix [Default output]
  -d        Set capture duration [Default 5 seconds]
  -f        Set capture framerate [Default 21 fps]
  -e        Encode MJPEG (AVI) output on the fly (CPU, libavcodec)
  -p        Enable profiling
  -v        Enable verbose message
  -h        Print this help
Outputs:
  <prefix>_left.avi, <prefix>_right.avi (with -e)
  <prefix>_left.nv12, <prefix>_right.nv12 (without -e)
  <prefix>_left_meta.csv, <prefix>_right_meta.csv

关键问题

  1. ffmpeg 软件编码库实现 MJPEG(AVI)输出到磁盘,由 Codex 实现,重构于 SoftEncoder.cpp/h 中。编码线程与采集线程解耦,队列满则丢帧避免阻塞采集。

  2. 传感器模式控制:
    使用 nvargus_nvraw 提前查看传感器支持的模式和详细信息:

    # 显示分辨率和 Mode Index
    nvargus_nvraw --lps
    # 指定 Mode Index 查看详细参数
    nvargus_nvraw --c 0 --mode 0 --sensorinfo

    目前使用 IMX219 1640x1232 的原生分辨率,nvargus_nvraw 显示该最大 FR 为 29。
    因此需要将 DEFAULT_FPS 对应的 frameDurationNs clamp 到范围内:

        const uint64_t targetFrameNs = 1000000000ULL / static_cast<uint64_t>(DEFAULT_FPS);
        frameDurationNs = clampUint64(targetFrameNs, frameRange.min(), frameRange.max());

    曝光时间 ExposureTime 和帧生成时间 FrameDuration 不同,前者往往小于后者。
    配置曝光锁定、关闭自动增益:

    execute() {
        ...
        SensorMode* mode_select = mode_list[3];
        ...
        i_source_settings->setSensorMode(mode_select)
        i_source_settings->setFrameDurationRange(Range<uint64_t>(frameDurationNs, frameDurationNs))
        i_source_settings->setExposureTimeRange(Range<uint64_t>(exposureNs, exposureNs))
        ...
        i_auto_settings->setAeMode(AE_MODE_OFF)
    }
  3. 软件编码需要真实的 EFFECTIVE_FPS,来自于实际的帧生成时间 frameDurationNs,否则播放时的 FPS 会漂。

  4. 缓冲区与 NVMM/EGL 的关系:
    NvBuffer 是 MM API 的通用缓冲区封装(包含 plane/FD/stride 等)。
    NvNativeBuffer 是 Argus samples 的 native buffer,继承 NativeBuffer(IEGLImageSource),它在 NVMM 上分配并能创建 EGLImage
    当前 DmaBuffer 通过多继承同时提供 NvNativeBuffer 的 EGLImage 能力和 NvBuffer 的 plane/FD 结构,便于 Argus + CPU/NVMM 共享同一块内存。

  5. EGLStream/NV/ImageNativeBuffer.h 是 EGLStream 场景下的扩展接口,允许把 EGLStream 的 Image 拷贝到 NvBuffer(可缩放/格式转换)。
    当前项目使用的是 BufferOutputStream + EGLImage,不走 EGLStream;如果后续改为 EGLStream(FrameConsumer/VPI)可考虑使用该接口。

  6. NVBufferEGLImage 不是同一类型,但可以指向同一块 NVMM:EGLImage 是 GPU/EGL 视角的“句柄”,NvBuffer/NvBufSurface 是 DMABUF + plane 元数据视角。两者通过 DMABUF 互相导入。

  7. STREAM_SIZE 在 BufferOutputStream 中通过 缓冲区尺寸生效,没有 setResolution()。若设置了更小的 DmaBuffer 尺寸,Argus 会把传感器输出缩放到该尺寸;要拿到原始分辨率就要分配同尺寸的缓冲区。

  8. 试着开启了 Sync,不知道有什么用。

    execute() {
        ...
        i_bufstream_settings->setSyncType(SYNC_TYPE_EGL_SYNC);
        ...
    }
    // 需要配套的同步处理
    ConsumerThread::dumpNv12Frame() {
        if (iBuffer->getSyncType() == SYNC_TYPE_EGL_SYNC)
        {
            IEGLSync *iSync = interface_cast<IEGLSync>(buffer);
            EGLSyncKHR acquireSync = EGL_NO_SYNC_KHR;
            if (iSync->getAcquireSync(eglDisplay, &acquireSync) != STATUS_OK)
                ...
    }

    若开启 EGL Sync,就必须 获取并等待/销毁 acquire sync,否则会出现:
    (Argus) Error InvalidState: Buffer was released with an acquire sync that was never retreived
    其中有两个关键函数 eglClientWaitSyncKHReglDestroySyncKHR 的调用,可以在 jetson_multimedia_api/include/NvEglRenderer.h 中看到,\

    但是构建时链接错误 undefined reference to ArgusSamples::eglClientWaitSyncKHR 之类,根据 Codex 的说法,eglClientWaitSyncKHR/eglDestroySyncKHR 属于 EGL 扩展符号,Jetson 的 libEGL 可能并不导出这些符号,必须通过 eglGetProcAddress 动态获取。解决方案是通过 initEglSyncFunctions() 使用函数指针调取函数。\

    但是 jetson_multimedia_api/argus/samples/eglImage/main.cpp 中就是原来这么写的。很奇怪,这个例子还没有编译过,不清楚能否正常执行。

  9. CaptureMetadata::getSourceIndex() 在 BufferOutputStream 场景里可能一直是 0,不能当作左右相机 ID;

  10. VPI 管线建议:当前 BufferOutputStream + EGLImage 已经落在 NVMM,可用 NvBufSurfaceFromFd 获取 NvBufSurface,再用 VPI 的 NVMM wrapper 创建 VPIImage 让 GPU/CPU/VIC 调度。
    若走 EGLStream 路线,则用 EGLOutputStream + VPI 的 EGLStream consumer;EGLStream 更适合 GPU-only 路径,但对同步和 consumer 管理要求更高。

TODO

20260514

  1. 适配 Orin NX,采用 NVENC 编码
  2. 统一采集时间戳,为接入 ROS 做准备
  3. Publish data e.g. image raw on NVMM, camera raw metadata to ROS topic
  4. Wrap the program as ROS Node

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages