Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 26 additions & 2 deletions examples/camera_streamer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,45 @@ configuration tests run without camera hardware.
## Protocol and configuration tests

```bash
cmake -S examples/camera_streamer/argus_sender -B /tmp/camera-protocol-build
cmake -S examples/camera_streamer/argus_sender -B /tmp/camera-protocol-build -DARGUS_SENDER_CPU_TESTS_ONLY=ON
cmake --build /tmp/camera-protocol-build --parallel 2
ctest --test-dir /tmp/camera-protocol-build --output-on-failure
```

See [the wire specification](argus_sender/WIRE_FORMAT.md). Shared camera
profiles are in `configs/`; select sensor modes using the installed camera driver.

## Jetson sender

Install the camera vendor driver and Jetson Multimedia API on the Orin. Stop any
preview using the sensors and configure the vendor PWM for the selected frame rate.
From `examples/camera_streamer` (or a copy of that directory on the robot):

```bash
cmake -S argus_sender -B argus_sender/build -DCMAKE_BUILD_TYPE=Release
cmake --build argus_sender/build --parallel 2
ctest --test-dir argus_sender/build --output-on-failure
argus_sender/build/argus_sender --list-modes
PROFILE=s56-stereo-2560x1984-30 # Choose your resolution
argus_sender/build/argus_sender --config "configs/${PROFILE}.yaml" --check-config
argus_sender/build/argus_sender --config "configs/${PROFILE}.yaml"
```

The sender listens for a TCP receiver on port 5000 by default.

## Licensing

This example is Apache-2.0 licensed. Existing upstream notices and external SDK
licenses remain in effect.
licenses remain in effect. The Argus-derived `EGLGlobal.h`, `EGLGlobal.cpp` and
`Error.h` retain their BSD-3-Clause license and notices.

## Rebuilding after a source-directory move

CMake caches absolute source paths. After updating an existing deployment, configure
and build the native sender and decoder again at the paths shown above. Do not move
or reuse an old build directory from a different source location.

For opt-in hardware smoke tests, install PyYAML for the Python interpreter selected
by CMake. Set `ARGUS_SENDER_HARDWARE_CONFIG` to your stereo profile; the smoke receiver
reads `network.port` from that same file (default 5000), so there is no separate
receiver-port override.
47 changes: 47 additions & 0 deletions examples/camera_streamer/argus_sender/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# CMake build trees and generated files
/build/
/build-*/
/cmake-build-*/
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
compile_commands.json
CTestTestfile.cmake
Testing/
_deps/

# Compiled artifacts
*.o
*.a
*.so
*.so.*
*.d

# Camera and encoded stream dumps
*.hvs2
*.avs1
*.h265
*.hevc
*.yuv
*.nv12
*.raw

# Runtime and crash output
*.log
core
core.*

# Editor and operating-system files
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Deployed wrist remap LUTs — generated by setup_video_streaming.sh from the
# calibration tree, rsynced to the AGX with the source tree.
/remaps/
125 changes: 124 additions & 1 deletion examples/camera_streamer/argus_sender/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,134 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.16)

project(argus_sender LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
include(CTest)

option(ARGUS_SENDER_CPU_TESTS_ONLY "Build only portable protocol/configuration tests" OFF)
if(ARGUS_SENDER_CPU_TESTS_ONLY)
find_package(Threads REQUIRED)
include(cmake/YamlCpp.cmake)
set(ARGUS_SENDER_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
include(cmake/Tests.cmake)
return()
endif()

option(DISABLE_MULTIPROCESS
"Link the single-process Argus implementation instead of nvargus-daemon's socket client"
OFF)

set(MMAPI_ROOT "/usr/src/jetson_multimedia_api" CACHE PATH
"Jetson Multimedia API root")
set(MMAPI_CLASS_DIR "${MMAPI_ROOT}/samples/common/classes" CACHE PATH
"Jetson Multimedia API common/classes directory")

set(NVIDIA_LIBRARY_DIRS
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/nvidia
/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/tegra
/usr/lib/aarch64-linux-gnu/nvidia
/usr/lib/aarch64-linux-gnu/tegra
/opt/nvidia/l4t-gpu-libs/openrm
/opt/nvidia/l4t-gpu-libs/nvgpu
)

find_path(ARGUS_INCLUDE_DIR Argus/Argus.h
HINTS "${MMAPI_ROOT}/argus/include" "${CMAKE_CURRENT_SOURCE_DIR}/include")
find_library(ARGUS_SOCKETCLIENT_LIBRARY NAMES nvargus_socketclient
HINTS ${NVIDIA_LIBRARY_DIRS})
find_library(ARGUS_LIBRARY NAMES nvargus
HINTS ${NVIDIA_LIBRARY_DIRS})

find_path(MMAPI_INCLUDE_DIR NvVideoEncoder.h
HINTS "${MMAPI_ROOT}/include")
find_path(CUDA_INCLUDE_DIR cuda.h
HINTS /usr/local/cuda/include /usr/include)
find_path(DRM_INCLUDE_DIR drm.h
HINTS /usr/include/libdrm)

find_library(EGL_LIBRARY NAMES EGL)
find_library(NVV4L2_LIBRARY NAMES nvv4l2
HINTS ${NVIDIA_LIBRARY_DIRS})
find_library(NVBUFSURFACE_LIBRARY NAMES nvbufsurface
HINTS ${NVIDIA_LIBRARY_DIRS})
find_library(CUDA_DRIVER_LIBRARY NAMES cuda
HINTS ${NVIDIA_LIBRARY_DIRS} /usr/lib/aarch64-linux-gnu)

find_package(Threads REQUIRED)

include(cmake/YamlCpp.cmake)

set(REQUIRED_PATHS
ARGUS_INCLUDE_DIR
MMAPI_INCLUDE_DIR
CUDA_INCLUDE_DIR
DRM_INCLUDE_DIR
MMAPI_CLASS_DIR
)
foreach(path_var IN LISTS REQUIRED_PATHS)
if(NOT ${path_var})
message(FATAL_ERROR "${path_var} was not found")
endif()
endforeach()

set(REQUIRED_LIBS
EGL_LIBRARY
NVV4L2_LIBRARY
NVBUFSURFACE_LIBRARY
CUDA_DRIVER_LIBRARY
)
foreach(lib_var IN LISTS REQUIRED_LIBS)
if(NOT ${lib_var})
message(FATAL_ERROR "${lib_var} was not found")
endif()
endforeach()

if(DISABLE_MULTIPROCESS)
if(NOT ARGUS_LIBRARY)
message(FATAL_ERROR
"DISABLE_MULTIPROCESS=ON requires the single-process nvargus library")
endif()
set(ARGUS_LINK_LIBRARY "${ARGUS_LIBRARY}")
set(ARGUS_LINK_MODE "single-process")
else()
if(NOT ARGUS_SOCKETCLIENT_LIBRARY)
message(FATAL_ERROR
"The multiprocess build requires the nvargus_socketclient library")
endif()
set(ARGUS_LINK_LIBRARY "${ARGUS_SOCKETCLIENT_LIBRARY}")
set(ARGUS_LINK_MODE "multiprocess")
endif()

set(MMAPI_CLASS_SOURCES
${MMAPI_CLASS_DIR}/NvBuffer.cpp
${MMAPI_CLASS_DIR}/NvElement.cpp
${MMAPI_CLASS_DIR}/NvElementProfiler.cpp
${MMAPI_CLASS_DIR}/NvLogging.cpp
${MMAPI_CLASS_DIR}/NvV4l2Element.cpp
${MMAPI_CLASS_DIR}/NvV4l2ElementPlane.cpp
${MMAPI_CLASS_DIR}/NvVideoEncoder.cpp
)

foreach(source_file IN LISTS MMAPI_CLASS_SOURCES)
if(NOT EXISTS "${source_file}")
message(FATAL_ERROR "Required Multimedia API source not found: ${source_file}")
endif()
endforeach()

set(ARGUS_SENDER_ROOT "${CMAKE_CURRENT_SOURCE_DIR}")
include(cmake/Tests.cmake)
add_subdirectory(src)

if(BUILD_TESTING)
include(cmake/Tests.cmake)
endif()


message(STATUS "Argus include: ${ARGUS_INCLUDE_DIR}")
message(STATUS "Argus library (${ARGUS_LINK_MODE}): ${ARGUS_LINK_LIBRARY}")
message(STATUS "Multimedia API include: ${MMAPI_INCLUDE_DIR}")
message(STATUS "nvv4l2: ${NVV4L2_LIBRARY}")
message(STATUS "nvbufsurface: ${NVBUFSURFACE_LIBRARY}")
36 changes: 36 additions & 0 deletions examples/camera_streamer/argus_sender/cmake/Tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ set(CAMERA_STREAMER_TEST_ROOT "${CAMERA_STREAMER_ROOT}/tests")
set(ARGUS_SENDER_TEST_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/src
${CAMERA_STREAMER_TEST_ROOT}/cpp
${ARGUS_INCLUDE_DIR}
)

function(argus_sender_add_cpp_test target test_name)
Expand All @@ -17,6 +18,7 @@ function(argus_sender_add_cpp_test target test_name)
${ARGUS_SENDER_TEST_INCLUDE_DIRS}
)
target_link_libraries(${target} PRIVATE
${ARGUS_LINK_LIBRARY}
Threads::Threads
)
add_test(NAME ${test_name} COMMAND ${target})
Expand All @@ -33,3 +35,37 @@ add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/frame_alignment_policy ${CMAKE
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/capture_domain_plan ${CMAKE_CURRENT_BINARY_DIR}/tests/capture_domain_plan)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/capture_priority_policy ${CMAKE_CURRENT_BINARY_DIR}/tests/capture_priority_policy)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/avs1_wire ${CMAKE_CURRENT_BINARY_DIR}/tests/avs1_wire)

if(ARGUS_SENDER_CPU_TESTS_ONLY)
return()
endif()

add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/nv_remap ${CMAKE_CURRENT_BINARY_DIR}/tests/nv_remap)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/rate_limiter ${CMAKE_CURRENT_BINARY_DIR}/tests/rate_limiter)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/alignment_policy ${CMAKE_CURRENT_BINARY_DIR}/tests/alignment_policy)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/bundle_assembler ${CMAKE_CURRENT_BINARY_DIR}/tests/bundle_assembler)
add_subdirectory(${CAMERA_STREAMER_TEST_ROOT}/cpp/tcp_transport ${CMAKE_CURRENT_BINARY_DIR}/tests/tcp_transport)

option(ARGUS_SENDER_HARDWARE_TESTS
"Enable tests that capture from physical stereo cameras"
OFF)
set(ARGUS_SENDER_HARDWARE_CONFIG
"${CMAKE_CURRENT_SOURCE_DIR}/configs/hardware_stereo_smoke.yaml"
CACHE FILEPATH "Config used by the hardware smoke test")

if(ARGUS_SENDER_HARDWARE_TESTS)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
add_test(
NAME avs1_hardware_smoke
COMMAND ${Python3_EXECUTABLE}
${CAMERA_STREAMER_TEST_ROOT}/hardware/avs1_smoke.py
--sender $<TARGET_FILE:argus_sender>
--config ${ARGUS_SENDER_HARDWARE_CONFIG}
--frames 120
)
set_tests_properties(avs1_hardware_smoke PROPERTIES
LABELS "hardware"
RUN_SERIAL TRUE
TIMEOUT 90
)
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

version: 1

network:
bind: 127.0.0.1
port: 55000
reconnect_ms: 100

runtime:
frames: 120
kpi: true

alignment:
warmup_frames: 60
interval_frames: 60

streams:
- id: smoke_stereo
type: stereo
cameras:
- {device: 0, mode: 0}
- {device: 1, mode: 0}
output:
fps: 60
codec: hevc
rotation: 0
resize_mode: strict
bitrate_mbps: 50
gop: 60
57 changes: 57 additions & 0 deletions examples/camera_streamer/argus_sender/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
add_executable(argus_sender
main.cpp
Config.cpp
Avs1Wire.cpp
OutputGeometry.cpp
MixedRateScheduler.cpp
CapturePriorityPolicy.cpp
CaptureDomainPlan.cpp
CaptureManager.cpp
FrameBundleAssembler.cpp
StreamProcessor.cpp
CaptureCoordinator.cpp
SenderSession.cpp
Runtime.cpp
CameraCatalog.cpp
FrameCopyWorker.cpp
NvRemap.cpp
FrameAlignmentPolicy.cpp
StereoAlignmentPolicy.cpp
TcpTransport.cpp
HevcEncoder.cpp
StereoTiming.cpp
EGLGlobal.cpp
NvBufSurfaceLite.cpp
${MMAPI_CLASS_SOURCES}
)

if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(argus_sender PRIVATE -Wall -Wextra)
set_source_files_properties(${MMAPI_CLASS_SOURCES}
PROPERTIES COMPILE_OPTIONS -w)
endif()

target_compile_definitions(argus_sender PRIVATE NVMMAPI_SUPPORTED)

target_include_directories(argus_sender PRIVATE
${ARGUS_INCLUDE_DIR}
${MMAPI_INCLUDE_DIR}
${MMAPI_CLASS_DIR}
${CUDA_INCLUDE_DIR}
${DRM_INCLUDE_DIR}
)

target_link_libraries(argus_sender PRIVATE
${ARGUS_LINK_LIBRARY}
${EGL_LIBRARY}
${NVV4L2_LIBRARY}
${NVBUFSURFACE_LIBRARY}
${CUDA_DRIVER_LIBRARY}
yaml-cpp::yaml-cpp
Threads::Threads
)

set_target_properties(argus_sender PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
install(TARGETS argus_sender DESTINATION bin)
Loading
Loading