Skip to content

Conversation

@danal97
Copy link
Contributor

@danal97 danal97 commented Sep 15, 2025

Enhanced the Rivermax RX manager to support multiple streams on the same thread with stream-specific packet processing and queueing capabilities.
Enhance TX manager to support sending multiple streams on the same thread.

Key changes:

  • Enhanced RDK applications (ipo_receiver, rtp_receiver and media_sender) to use an extension of RDK AppSetting in order to set threads setting. Each thread has it's dedicated core, ID and streams settings.
  • Enhanced burst manager to support per-stream ID burst queues. (Each stream ID has its own dedicated burst queue for packet segregation).
  • Maintained backward compatibility: bench RX processes bursts regardless of stream ID.
  • Add new application for receiving and processing multiple video streams simultaneously using the advanced networking framework with Rivermax manager. This application serves as a reference implementation for multi-stream media reception scenarios in advanced networking deployments.
  • Rivermax TX manager also supports multiple streams - bench TX application sends the same data via multiple streams

This PR is rebased to Rony's PR/970.
The last 4 commits are new.

Summary by CodeRabbit

  • New Features

    • Advanced Networking Media Player application for receiving and displaying network video streams with GPU-accelerated processing.
    • Advanced Networking Media Sender application for transmitting media over the network using Rivermax.
    • Multi-stream media reception and transmission support with zero-copy optimization paths.
    • Rivermax-based benchmark configurations for testing network performance.
  • Framework Updates

    • Migrated from legacy Rivermax library to rivermax-dev-kit 1.70.32+ with SMPTE 2110-20 support.

✏️ Tip: You can customize this high-level summary in your review settings.

ronyrad and others added 18 commits September 15, 2025 12:28
- Remove duplicate docstring headers from source files and overridden functions
- Centralize documentation in class definitions to reduce maintenance overhead
- Improve code readability by eliminating redundant comments

Signed-off-by: Rony Rado <[email protected]>
- Rename all code references from 'rmax' to 'rivermax' for consistency
- Update filenames containing 'rmax' to use full 'rivermax' naming
- Modify documentation/comments using abbreviated form
- Update CMakeLists.txt and build configurations

Affected components:
* rmax_config_manager → rivermax_config_manager
* rmax_utils → rivermax_utils
* RmaxParams → RivermaxParams
* All instances in source headers/docs/comments

Signed-off-by: Rony Rado <[email protected]>
- Update Rivermax SDK from 1.60.6 to 1.70.21
- Replace legacy rmax-ral-lib with modern rivermax-dev-kit components
- Implement multi-receiver architecture supporting:
  - IPO receivers SMPTE 2022-7
  - RTP receivers SMPTE 2210-20
  - Media Frame Transmitters (SMPTE 2110-20)
- Refactor configuration system with typed builders:
  - Strong type checking for queue parameters
  - Validation pipelines for settings
  - Auto-generated documentation hooks
- Update build system:
  - New CMake targets for dev-kit services
  - Dockerfile SDK version alignment
  - Dependency cleanup
- Improve memory management and resource cleanup.
- Add proper shutdown handling for services.
- Enhance error handling and logging.

Removed components:
- Legacy IPO receiver service implementation
- RMAX base service classes
- Obsolete chunk consumer interfaces

Added components:
- RivermaxQueueConfigs hierarchy
- TypedConfigBuilderHolder pattern
- MediaSenderService

Affected components:
- RivermaxMgr core implementation
- Burst management system
- Packet processing pipeline
- YAML configuration parsing
- Docker build infrastructure

Dependencies:
- Requires Rivermax SDK >=1.70.21

Signed-off-by: Rony Rado <[email protected]>
- Add hardware-accelerated video streaming with three memory modes:
 (GPU, host pinned, huge pages)
- Enable concurrent TX/RX operations with thread-safe buffer management
- Support multiple video formats:
  - YCbCr 4:2:2/4:4:4/4:2:0
  - RGB 8/10/12-bit
- Add Rivermax TX benchmarking tools for performance evaluation
- Update Rivermax SDK from 1.70.21 to 1.70.32

Key components:
- MediaSenderService with CUDA-accelerated frame processing
- BurstParams lifecycle management system
- YAML configuration support for:
  - Video resolution (HD/4K)
  - Frame rates (24-60 fps)
  - Traffic shaping parameters

Added configurations:
- TX-only scenario (adv_networking_bench_rivermax_tx.yaml)
- Combined TX+RX scenario (adv_networking_bench_rivermax_tx_rx.yaml)

Signed-off-by: Rony Rado <[email protected]>
- Add comprehensive tracing for TX burst lifecycle:
- Implement context-aware error reporting:
- Improve thread synchronization:
  - Add mutex guards for burst parameter access
  - Remove redundant lock from availability check
  - Implement RAII-style resource cleanup
- Strengthen packet validation:
  - Add auto-truncation for oversized chunks
  - Verify header-data split configuration
- Optimize configuration parameters:
  - Increase max_path_diff_us from 100μs → 10ms
  - Set sleep_between_operations_us to 0 for low latency
  - Update CPU core assignments in YAML examples
- Fix critical issues:
  - Memory leak in TX buffer release path
  - Shutdown sequence race conditions
  - Header-data split flag propagation
- Update Readme

Key components modified:
- MediaSenderService burst handling
- RivermaxManager shutdown sequence
- ANO benchmark TX operator
- Example configurations (TX/RX scenarios)

Signed-off-by: Rony Rado <[email protected]>
- Add burst_flags to burst header.
  Each manager would be able to set it's own flags for controlling flows/logic

Signed-off-by: Rony Rado <[email protected]>
- Add Media Sender service with Zero Copy support
- Service doesn't own frames memory pool and forwards received
  frames to the RDK Media Sender service

Signed-off-by: Rony Rado <[email protected]>
- Add yaml option "num_of_packets_in_chunk" .

Signed-off-by: Rony Rado <[email protected]>
Update apps installation command

Signed-off-by: Rony Rado <[email protected]>
This commit introduces basic testing for Rivermax manager
- New pytest test case for Rivermax TX/RX validation (with both receiver
  applications - ipo_receiver and rtp_receiver)
- Rivermax applications uses the NIC IP address, therefore this commit
  enhance the network interface utilities with IP address detection.

Note: Validation currently relies on receiver-side metrics only. Future
enhancement will include sender-side packet comparison once
Rivermax-dev-kit provides sender logging capabilities.

Signed-off-by: Dana Lessner <[email protected]>
- Introduced Python network initialization function `adv_net_init` to handle Holoscan config objects.
- Updated various functions for burst creation, packet management, and network interface operations.

Signed-off-by: Rony Rado <[email protected]>
- Add AdvNetworkMediaRxOp for receiving media streams over Rivermax
  - Supports packet-to-frame conversion with ANO Burst processing
  - Configurable video formats, bit depths, and frame dimensions
  - Output as VideoBuffer or Tensor entities

- Add AdvNetworkMediaTxOp for transmitting media streams over Rivermax
  - Processes VideoBuffer/Tensor inputs for network transmission
  - Configurable network interface and queue management
  - Support for various video formats

Features:
  - SMPTE 2110 compliance for professional media over IP
  - GPU acceleration and GPUDirect support
  - Low latency optimizations
  - Python bindings for both operators

These operators build upon the Advanced Network library to provide
specialized functionality for broadcast and media streaming
use cases requiring strict timing and high throughput performance.

Signed-off-by: Rony Rado <[email protected]>
- Add high-performance media streaming application built on Advanced Network Media Tx operator
  - Utilizes AdvNetworkMediaTxOp for media transmission over Rivermax
  - Integrates with Advanced Network Manager for optimized network resource management
  - SMPTE 2110 compliant for professional broadcast applications
  - Real-time transmission of media files with precise timing control

- Support multiple video formats and resolutions
  - RGB888, YUV420, NV12 and other common video formats
  - Configurable bit depths (8, 10, 12, 16-bit)
  - Multiple resolution support up to 4K and beyond
  - GPU acceleration with GPUDirect for zero-copy operations

* Implement dual language support
  - C++ implementation for maximum performance
  - Python implementation for ease of use and development

* Support professional broadcast features
  - Frame-accurate timing for live streaming applications
  - Looping playback for continuous streaming
  - VideoStreamReplayer integration for file-based sources

Pipeline: VideoStreamReplayer → AdvNetworkMediaTxOp → Advanced Network Manager → Network Interface

This application demonstrates how to use the Advanced Network Media operators
for professional-grade media transmission in broadcast and media production
environments requiring ultra-low latency and high throughput performance.

Signed-off-by: Rony Rado <[email protected]>
- Add high-performance media receiving application built on Advanced Network Media Rx operator
  - Utilizes AdvNetworkMediaRxOp for professional media reception over Rivermax
  - Integrates with Advanced Network Manager for optimized network resource management
  - SMPTE 2110 compliant for professional broadcast applications
  - Real-time reception and processing of media streams with ultra-low latency

- Support flexible output modes and format handling
  - Real-time visualization using HolovizOp for live monitoring
  - File output capability for recording and analysis
  - Format conversion support for optimal display

- Support multiple video formats and resolutions
  - RGB888, YUV420, NV12, RGBA and other common video formats
  - Configurable bit depths (8, 10, 12, 16-bit)
  - Multiple resolution support up to 4K and beyond
  - GPU acceleration with GPUDirect for zero-copy operations

- Implement dual language support
  - C++ implementation for maximum performance
  - Python implementation for ease of use and development

Pipeline: Network Interface → Advanced Network Manager → AdvNetworkMediaRxOp → [FormatConverter] → HolovizOp/FramesWriter

This application demonstrates how to use the Advanced Network Media operators
for professional-grade media reception in broadcast and media production
environments requiring ultra-low latency and high throughput performance.

Signed-off-by: Rony Rado <[email protected]>
@danal97 danal97 force-pushed the october branch 5 times, most recently from f580e1c to df42744 Compare September 17, 2025 05:18
@danal97 danal97 force-pushed the october branch 4 times, most recently from 5db2626 to 3c8f9f3 Compare October 12, 2025 14:54
@bhashemian
Copy link
Member

@danal97 could you please resolve the conflicts and update this PR with the latest changes in the main branch? Thanks

Enhanced the Rivermax manager to support multiple streams on the same
thread with stream-specific packet processing and queueing capabilities.

Key changes:
- Enhanced RDK applications (ipo_receiver and rtp_receiver) to get an
  extension to RDK AppSetting in order to set threads setting. Each
  thread has it's dedicated core, ID and streams settings.
- Enhanced burst manager to support per-stream ID burst queues. (Each
  stream ID gets its own dedicated burst queue for packet segregation).
- Maintained backward compatibility: bench RX processes bursts
  regardless of stream ID.

Signed-off-by: Dana Lessner <[email protected]>
Enhance the media player application to receive and process multiple
video streams simultaneously using the advanced networking with Rivermax
manager.
This application served as a reference implementation for multi-stream
media reception scenarios in advanced networking deployment.

Key changes:
- Receive multiple streams with Rivermax manager.
- Save each stream to different file.
- Add MockReceiverOp to simulate data reception for testing

Note: The application supports visualization using holoviz - this is
currently limited and allowed only when receiving a single stream.

Signed-off-by: Dana Lessner <[email protected]>
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Oct 26, 2025

Skipped: This PR changes more files than the configured file change limit: (106 files found, 100 file limit)

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 8, 2025

Walkthrough

This PR adds two new advanced networking applications (media_player and media_sender) with C++ and Python implementations, refactors the advanced_network operator from legacy RMAX to Rivermax dev-kit, introduces advanced_network_media RX/TX operators with burst/packet processing, extends Python bindings for enhanced burst/packet APIs, and includes comprehensive YAML configurations and documentation.

Changes

Cohort / File(s) Summary
New Applications - Media Player
applications/adv_networking_media_player/CMakeLists.txt, README.md, adv_networking_media_player.yaml, cpp/CMakeLists.txt, cpp/adv_networking_media_player.cpp, cpp/metadata.json, python/CMakeLists.txt, python/adv_networking_media_player.py, python/metadata.json
Introduces a complete media player application with C++ and Python implementations, including lifecycle management, frame writing, mock receiver fallback, configuration loading, and visualization integration via Holoviz.
New Applications - Media Sender
applications/adv_networking_media_sender/CMakeLists.txt, README.md, adv_networking_media_sender.yaml, cpp/CMakeLists.txt, cpp/adv_networking_media_sender.cpp, cpp/metadata.json, python/CMakeLists.txt, python/adv_networking_media_sender.py, python/metadata.json
Introduces a complete media sender application with C++ and Python implementations, including video stream replay, TX operator wiring, and Rivermax configuration management.
Advanced Network Operator - Rivermax Core Manager
operators/advanced_network/advanced_network/managers/rivermax/adv_network_rivermax_mgr.h, rivermax_mgr_impl/adv_network_rivermax_mgr.cpp, rivermax_mgr_impl/rivermax_mgr_service.h, rivermax_mgr_impl/rivermax_mgr_service.cpp
Implements full Rivermax manager with service lifecycle, burst/packet handling, TX/RX pathways, and extensive packet/header utilities; includes per-stream RX support.
Advanced Network Operator - Rivermax Services
managers/rivermax/applications/\*, rivermax_mgr_impl/burst_manager.*, rivermax_mgr_impl/packet_processor.h, rivermax_mgr_impl/rivermax_chunk_consumer_ano.h, rivermax_mgr_impl/rivermax_queue_configs.*
Adds Rivermax service implementations (IPO receiver, RTP receiver, media sender variants), burst/queue management, packet processing pipeline, and configuration structure definitions with validators and builders.
Advanced Network Operator - Configuration & Build
managers/rivermax/CMakeLists.txt, managers/rivermax/rivermax_service/CMakeLists.txt, rivermax_mgr_impl/rivermax_config_manager.*, advanced_network/manager.*, common.*
Introduces comprehensive configuration management (parsing, validation, type conversion), Rivermax SDK integration via FetchContent, new get_rx_burst overload for stream support.
Advanced Network Operator - Removed Legacy
rmax_mgr_impl/\*, rmax_service/\*, rivermax_ano_data_types.h (renamed from rmax)
Removes legacy RMAX implementation files and replaces with Rivermax equivalents; updates data type naming and structures.
Advanced Network Benchmark Updates
applications/adv_networking_bench/README.md, adv_networking_bench_rivermax_*.yaml, cpp/CMakeLists.txt, cpp/main.cpp, cpp/rivermax_bench_op_tx.h, cpp/testing/\*
Renames benchmark configs to Rivermax, adds TX operator support, introduces test utilities for media generation and Rivermax benchmark parsing.
Advanced Network Media Operators
operators/advanced_network_media/CMakeLists.txt, advanced_network_media_rx/\*, advanced_network_media_rx/burst_processor.*, advanced_network_media_rx/adv_network_media_rx.*, README.md
Introduces new RX operator with frame buffering, burst processing, HDS support, and packet-to-frame conversion; includes extensive operator parameters and metadata.
Python Bindings Extension
operators/advanced_network/python/adv_network_common_pybind.cpp
Expands Python bindings with new enums (ManagerType, Direction, BufferLocation, MemoryKind), BurstHeaderParams/BurstParams classes, adv_net_init function, and comprehensive burst/packet/header manipulation APIs.
Application Registration & Build Integration
applications/CMakeLists.txt, operators/CMakeLists.txt
Registers two new holohub applications (adv_networking_media_player, adv_networking_media_sender) and adds advanced_network_media subdirectory to operator build.
Documentation & Configuration
operators/advanced_network/CHANGELOG.md, operators/advanced_network/Dockerfile, operators/advanced_network/README.md, operators/advanced_network/advanced_network/types.h
Updates Dockerfile with Rivermax 1.71.4 and expanded dependencies, documents Rivermax RX/TX configuration, adds changelog entries, and extends BurstHeaderParams/BurstParams with new fields.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Specific areas requiring extra attention:

  • Rivermax Manager Implementation (rivermax_mgr_impl/adv_network_rivermax_mgr.cpp): Complex lifecycle, per-stream RX burst handling, TX/RX service orchestration, burst pool management, and extensive packet/header utilities; requires validation of initialization flow, error handling, and memory management correctness.
  • Configuration Management Pipeline (rivermax_mgr_impl/rivermax_config_manager.*): Parsing, validation, and settings building across IPO/RTP RX and Media Sender TX; verify type conversions, memory region allocation strategies, allocator selection logic, and per-thread/stream distribution.
  • Burst/Packet Processing (rivermax_mgr_impl/burst_manager.*, packet_processor.h, rivermax_chunk_consumer_ano.h): Per-stream burst queuing, RTP header parsing, packet-to-frame conversion; verify thread-safety, synchronization, and correctness of per-stream index mapping and stream ID handling.
  • Python Bindings (adv_network_common_pybind.cpp): New enum bindings, adv_net_init YAML parsing, burst/packet API wrappers; confirm argument passing, memory ownership, and Python/C++ type conversions.
  • New Operators (advanced_network_media_rx/adv_network_media_rx.cpp): Frame pool lifecycle, output format selection (VIDEO_BUFFER vs TENSOR), frame entity creation; validate memory location handling and GXF entity release semantics.
  • YAML Configuration Structure: Verify consistency across benchmark, media_player, and media_sender YAML files; confirm alignment with parser expectations (thread_settings, network_settings, allocator types).
  • Legacy Code Removal (rmax_* files): Confirm all references to old RMAX types and functions are properly migrated to Rivermax equivalents; check for any remaining RMAX usage.

Possibly related PRs

  • Add Rivermax ANO testing  #1065: Adds Rivermax ANO tests and overlapping test utilities (benchmark result parsing, media file generation, NIC IP retrieval); directly related via test infrastructure and utilities.
  • Update Advanced Network Manager Python bindings #941: Modifies adv_network_common_pybind.cpp to add/extend Python bindings (enums, Burst types, adv_net_init, burst/packet APIs); directly overlaps with Python binding changes in this PR.

Suggested reviewers

  • ronyrad
  • grlee77
  • bhashemian
  • brudfors

Pre-merge checks

✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: enhancing the Rivermax Manager to support multiple streams per thread, which is the primary focus of the changeset.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
operators/advanced_network/README.md (1)

325-362: Consolidate duplicate "Transmit Configuration (tx)" sections.

Lines 325 and 343 both have the heading "Transmit Configuration (tx)", violating the MD024 markdown rule (no duplicate headings). Additionally, lines 327-342 and 345-362 contain substantial overlap describing the same queue configuration options.

The structure indicates that the older TX section (lines 325-342) was not removed when the new, more detailed extended Rivermax TX section (starting at line 343) was added. Remove the first, incomplete section entirely and keep only the comprehensive section starting at line 343.

Apply this diff to consolidate:

-##### Transmit Configuration (tx)
-
-- **`queues`**: List of queues on NIC
-	type: `list`
-	full path: `cfg\interfaces\rx\queues`
-	- **`name`**: Name of queue
-  		- type: `string`
-	- **`id`**: Integer ID used for flow connection or lookup in operator compute method
-  		- type: `integer`
-	- **`cpu_core`**: CPU core ID. Should be isolated when CPU polls the NIC for best performance.. <mark>Not in use for Doca GPUNetIO</mark>
-		Rivermax manager can accept coma separated list of CPU IDs
-  		- type: `string`
-	- **`batch_size`**: Number of packets in a batch passed from the NIC to the downstream operator. A
-	larger number increases throughput but reduces end-to-end latency, as it takes longer to populate a single
-	buffer. A smaller number reduces end-to-end latency but can also reduce throughput.
-  		- type: `integer`
-	- **`memory_regions`**: List of memory regions where buffers are stored. memory regions names are configured in the [Memory Regions](#memory-regions) section
-		type: `list`
-
-##### Transmit Configuration (tx)
 
 - **`queues`**: List of queues on NIC
   **Type**: `list`
🟠 Major comments (17)
operators/advanced_network/advanced_network/managers/rivermax/README.md-1-517 (1)

1-517: Add usage instructions, explicit requirements section, and configuration examples.

This comprehensive documentation covers architecture and operational flows well, but is missing several essential sections per coding guidelines for operator README files. The document references YAML configuration and describes initialization processes but doesn't demonstrate practical usage.

Missing Sections:

  1. Usage Instructions (critical gap): How does a developer integrate and use the Rivermax Manager? Add a "Usage" or "Getting Started" section with:

    • Basic initialization steps
    • How to select between RX/TX services
    • Integration with applications
  2. Requirements (critical gap): While hardware dependencies are scattered in lines 220-230, create an explicit "Requirements" section covering:

    • Hardware requirements (ConnectX-6+, MOFED driver versions)
    • Software dependencies (Rivermax SDK version, C++ standard requirements)
    • Memory requirements (huge pages configuration, GPU memory if applicable)
  3. Configuration Examples (important gap): Lines 241 and 289 reference "YAML configuration" but never show an example. Add:

    • Sample RX configuration (IPO/RTP receiver setup)
    • Sample TX configuration (MediaSender vs MediaSenderZeroCopy)
    • Memory region configuration
    • Thread/core affinity settings (referenced in RDK AppSetting extension)

Recommended additions:

## Quick Start

### Basic RX Configuration Example
\`\`\`yaml
[example RX YAML configuration]
\`\`\`

### Basic TX Configuration Example
\`\`\`yaml
[example TX YAML configuration]
\`\`\`

## Requirements

### Hardware
- ConnectX-6 or later NIC
- MOFED driver version X.X+
- CPU with isolated cores for pinning

### Software
- Rivermax SDK version [version]
- C++17 or later
- NVIDIA CUDA [version if GPU support needed]

### Memory Configuration
- Huge pages enabled: [how many/setup instructions]
- GPU memory [if applicable]
applications/adv_networking_bench/cpp/testing/nvidia_nic_utils.py-36-40 (1)

36-40: Make ip_address type optional to match runtime values

ip_address is annotated as str but is later set to None when IP discovery fails, which can confuse type checkers and callers expecting a string. Consider updating the type to be explicitly optional and documenting the semantics:

-from typing import List
+from typing import List, Optional
@@
-    ip_address: str  # IP address
+    ip_address: Optional[str]  # IP address (None if no IPv4 address found)

The rest of the __str__ update looks good and will handle None by printing ip=None.

Committable suggestion skipped: line range outside the PR's diff.

applications/adv_networking_media_player/cpp/metadata.json-16-16 (1)

16-16: Correct the category tag.

The first tag in the tags array serves as the category and must match one of the approved categories. "Network" is not an approved category. Based on the application's purpose, use "Networking and Distributed Computing" as the first tag.

As per coding guidelines, the approved categories are: Benchmarking, Camera, Computer Vision and Perception, Converter, Deployment, Development, Extended Reality, Healthcare AI, Image Processing, Inference, Interoperability, Medical Imaging, Natural Language and Conversational AI, Networking and Distributed Computing, Optimization, Quantum Computing, Rendering, Robotics, Scheduler, Signal Processing, Streaming, Threading, Video, Video Capture, Visualization, XR.

-		"tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],
+		"tags": ["Networking and Distributed Computing", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],
applications/adv_networking_media_sender/cpp/metadata.json-16-16 (1)

16-16: Correct the category tag.

The first tag in the tags array serves as the category and must match one of the approved categories. "Network" is not an approved category. Based on the application's purpose, use "Networking and Distributed Computing" as the first tag.

As per coding guidelines, the approved categories are: Benchmarking, Camera, Computer Vision and Perception, Converter, Deployment, Development, Extended Reality, Healthcare AI, Image Processing, Inference, Interoperability, Medical Imaging, Natural Language and Conversational AI, Networking and Distributed Computing, Optimization, Quantum Computing, Rendering, Robotics, Scheduler, Signal Processing, Streaming, Threading, Video, Video Capture, Visualization, XR.

-		"tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax", "Media", "Media Sender"],
+		"tags": ["Networking and Distributed Computing", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax", "Media", "Media Sender"],
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_rtp_receiver.h-30-33 (1)

30-33: Remove using-namespace directives from header file.

Placing using namespace directives in a header file pollutes the namespace for all translation units that include this header, potentially causing naming conflicts and violating C++ best practices.

Use fully qualified names or namespace aliases instead:

-using namespace rivermax::dev_kit::apps;
-using namespace rivermax::dev_kit::io_node;
-using namespace rivermax::dev_kit::services;
-using namespace rivermax::dev_kit::core;
+namespace rdk_apps = rivermax::dev_kit::apps;
+namespace rdk_io = rivermax::dev_kit::io_node;
+namespace rdk_services = rivermax::dev_kit::services;
+namespace rdk_core = rivermax::dev_kit::core;

Then use these aliases or fully qualified names throughout the file.

operators/advanced_network/advanced_network/managers/rivermax/rivermax_service/CMakeLists.txt-73-82 (1)

73-82: Bug: Second patch uses potentially stale variable NEW_CONTENTS.

The second if block on line 79 checks CONTENTS but operates on NEW_CONTENTS, which is only defined if the first if block executed. If only the second patch is needed (first condition false, second true), NEW_CONTENTS will be undefined, causing incorrect behavior.

Apply this diff to fix the sequential patching logic:

 # Will be removed once RDK makes the change
 file(READ "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" CONTENTS)
+set(PATCHED_CONTENTS "${CONTENTS}")
 if(NOT CONTENTS MATCHES "virtual ReturnStatus find_internal_stream_index\\(")
-    string(REPLACE "ReturnStatus find_internal_stream_index(" "virtual ReturnStatus find_internal_stream_index(" NEW_CONTENTS "${CONTENTS}")
-    file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${NEW_CONTENTS}")
+    string(REPLACE "ReturnStatus find_internal_stream_index(" "virtual ReturnStatus find_internal_stream_index(" PATCHED_CONTENTS "${PATCHED_CONTENTS}")
 endif()
-if (NOT CONTENTS MATCHES "virtual ReturnStatus get_app_settings\\(")
-    string(REPLACE "ReturnStatus get_app_settings(" "virtual ReturnStatus get_app_settings(" NEW_CONTENTS2 "${NEW_CONTENTS}")
-    file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${NEW_CONTENTS2}")
+if(NOT CONTENTS MATCHES "virtual ReturnStatus get_app_settings\\(")
+    string(REPLACE "ReturnStatus get_app_settings(" "virtual ReturnStatus get_app_settings(" PATCHED_CONTENTS "${PATCHED_CONTENTS}")
 endif()
+if(NOT "${PATCHED_CONTENTS}" STREQUAL "${CONTENTS}")
+    file(WRITE "${rivermax-dev-kit_SOURCE_DIR}/source/apps/include/rdk/apps/rmax_base_app.h" "${PATCHED_CONTENTS}")
+endif()
operators/advanced_network/python/adv_network_common_pybind.cpp-225-235 (1)

225-235: Verify format_eth_addr validates and handles invalid MAC address formats.

The wrapper calls format_eth_addr without error checking. Ensure this utility validates the input string format (e.g., rejects "invalid", truncated strings, invalid hex characters) and does not cause undefined behavior. Standard validation should include format checking (XX:XX:XX:XX:XX:XX pattern), hex digit validation, and optionally semantic checks (all-zero, broadcast, multicast rejection). The wrapper should also handle and propagate any errors from format_eth_addr rather than silently passing invalid results to set_eth_header.

applications/adv_networking_media_sender/cpp/adv_networking_media_sender.cpp-17-17 (1)

17-17: Remove unused include.

The assert.h header is included but never used in this file. No assert() calls are present.

Apply this diff:

-#include <assert.h>
 #include <sys/time.h>
operators/advanced_network/advanced_network/manager.cpp-319-321 (1)

319-321: Add documentation for the new stream-specific overload.

The new get_rx_burst overload accepting a stream parameter is a key addition for multi-stream support, but it lacks documentation. Please add a comment block explaining when this overload should be used and when subclasses should override it.

Apply this diff:

+/**
+ * @brief Get RX burst for a specific port, queue, and stream
+ * This method enables stream-specific burst retrieval for managers that support
+ * multiple streams per queue (e.g., Rivermax). Base implementation returns NOT_SUPPORTED.
+ *
+ * @param burst Pointer to receive burst parameters
+ * @param port Port ID
+ * @param q Queue ID
+ * @param stream Stream ID within the queue
+ * @return Status::NOT_SUPPORTED in base implementation, subclasses may override
+ */
 Status Manager::get_rx_burst(BurstParams** burst, int port, int q, int stream) {
   return Status::NOT_SUPPORTED;
 }
applications/adv_networking_media_player/cpp/CMakeLists.txt-26-37 (1)

26-37: Use add_holohub_application() instead of add_executable() for proper Holohub build system integration.

Applications in this repository must use add_holohub_application() for build system integration rather than raw CMake commands. Replace add_executable(${PROJECT_NAME} ...) with the appropriate add_holohub_application() call to ensure consistent handling of dependencies, installation, and testing across the Holohub framework.

operators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-34-57 (1)

34-57: Static variables prevent correct multi-instance behavior.

The static variables (burst_info_initialized, last_header_stride, etc.) are shared across all BurstProcessor instances. This conflicts with the PR's multi-stream objectives—if multiple streams use separate BurstProcessor instances, they will incorrectly share configuration state.

Consider moving these to instance members:

+// In burst_processor.h class BurstProcessor:
+private:
+  bool burst_info_initialized_ = false;
+  size_t last_header_stride_ = 0;
+  size_t last_payload_stride_ = 0;
+  bool last_hds_on_ = false;
+  bool last_payload_on_cpu_ = false;
+  bool strategy_logged_ = false;

 // In burst_processor.cpp:
 void BurstProcessor::ensure_converter_configuration(BurstParams* burst) {
-  static bool burst_info_initialized = false;
-  static size_t last_header_stride = 0;
-  static size_t last_payload_stride = 0;
-  static bool last_hds_on = false;
-  static bool last_payload_on_cpu = false;
+  // Use instance members instead of static variables

Committable suggestion skipped: line range outside the PR's diff.

applications/adv_networking_media_sender/cpp/CMakeLists.txt-52-60 (1)

52-60: Security concern: WORLD_WRITE permission on installed files.

Setting WORLD_WRITE permission on configuration files is overly permissive and could allow any user to modify application configuration. Use WORLD_READ instead.

 install(
   FILES
     ../adv_networking_media_sender.yaml
   DESTINATION examples/${PROJECT_NAME}
   COMPONENT ${PROJECT_NAME}-configs
   PERMISSIONS OWNER_READ OWNER_WRITE
               GROUP_READ GROUP_WRITE
-              WORLD_READ WORLD_WRITE
+              WORLD_READ
 )

Apply the same fix to the other install() blocks at lines 62-70 and 72-80.

applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp-406-414 (1)

406-414: Mock receiver is added but not connected to any data flow.

When neither visualize nor write_to_file is enabled, the mock_receiver operator is added using add_operator() but there's no data flow from the RX operators to it. The RX operators created in adv_net_media_rx_map remain unconnected, potentially causing data to be dropped or causing pipeline issues.

Consider connecting the mock receiver to the RX operators or using a different approach to consume the data:

     } else {
       HOLOSCAN_LOG_WARN("No output type (write_to_file/visualize) defined. Data will be received but not processed.");
-      auto mock_receiver = make_operator<ops::MockReceiverOp>(
-          "mock_receiver",
-          Arg("interface_name", interface_name),
-          make_condition<BooleanCondition>("is_alive", true)
-      );
-      add_operator(mock_receiver);
+      for (const auto& [stream_name, rx_op] : adv_net_media_rx_map) {
+        auto mock_receiver = make_operator<ops::MockReceiverOp>(
+            "mock_receiver_" + stream_name,
+            Arg("interface_name", interface_name),
+            make_condition<BooleanCondition>("is_alive", true)
+        );
+        add_flow(rx_op, mock_receiver);
+      }
     }
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/adv_network_rivermax_mgr.cpp-224-231 (1)

224-231: Memory leak: burst_tx_pool arrays are never freed.

The destructor is empty, but initialize() allocates arrays for each element in burst_tx_pool (lines 228-230). These allocations are never released.

Add cleanup in the destructor:

 RivermaxMgr::RivermaxMgrImpl::~RivermaxMgrImpl() {
+  for (int i = 0; i < MAX_TX_BURST; i++) {
+    delete[] burst_tx_pool[i].pkts[0];
+    delete[] burst_tx_pool[i].pkt_lens[0];
+    delete[] burst_tx_pool[i].pkt_extra_info;
+  }
 }

Also applies to: 404-404

operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/burst_manager.h-33-33 (1)

33-33: Avoid using namespace at namespace scope in headers.

Same issue as in ano_media_sender.h - this pollutes the namespace for all includers.

-using namespace rivermax::dev_kit::services;
+// Prefer explicit namespace qualification or local using declarations

Committable suggestion skipped: line range outside the PR's diff.

operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.h-33-36 (1)

33-36: Avoid using namespace directives at namespace scope in headers.

These directives pollute the namespace for all translation units that include this header, potentially causing name collisions and ambiguity for downstream code.

Consider using explicit namespace prefixes or importing only specific symbols within function/method scope:

-using namespace rivermax::dev_kit::apps;
-using namespace rivermax::dev_kit::io_node;
-using namespace rivermax::dev_kit::services;
-using namespace rivermax::dev_kit::core;
+// Use explicit prefixes like rivermax::dev_kit::apps::AppSettings
+// or import within function scope only

Committable suggestion skipped: line range outside the PR's diff.

operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp-422-436 (1)

422-436: Status check occurs after the settings pointer is already used.

The dynamic_cast at line 425-426 uses settings which could be invalid if get_app_settings failed. The status check at line 433 happens too late.

 bool MediaSenderService::post_init_setup() {
   const AppSettings* settings = nullptr;
   auto status = tx_service_->get_app_settings(settings);
+
+  if (status != ReturnStatus::success) {
+    HOLOSCAN_LOG_ERROR("Failed to get settings from TX service");
+    return false;
+  }
+
   const ANOMediaSenderSettings* media_settings =
       dynamic_cast<const ANOMediaSenderSettings*>(settings);
   if (!media_settings) {
     HOLOSCAN_LOG_ERROR("Failed to cast AppSettings to ANOMediaSenderSettings");
     return false;
   }
 
-
-  if (status != ReturnStatus::success) {
-    HOLOSCAN_LOG_ERROR("Failed to get settings from TX service");
-    return false;
-  }
   size_t pool_buffer_size = settings->media.bytes_per_frame * MEDIA_FRAME_POOL_SIZE;
🟡 Minor comments (28)
operators/advanced_network/CHANGELOG.md-18-27 (1)

18-27: Fix unordered list indentation.

The nested list items use 4-space indentation before the dash, but Markdown best practices and markdownlint (MD007) require 2-space indentation for nested lists.

Apply this diff to correct the indentation:

  Rivermax Manager:
- -   **Framework Migration**
-     -   Migrated from legacy rmax-ral-lib to modern rivermax-dev-kit framework (1.70.32).
- -   **SMPTE 2110-20 Support**
-     -   Implemented hardware-accelerated video streaming with support for multiple memory modes (GPU, host pinned, huge pages) and video formats (YCbCr 4:2:2/4:4:4/4:2:0, RGB 8/10/12-bit).
- -   **Zero-Copy Performance**
-     -   Added MediaSenderZeroCopyService that forwards received frames without owning memory pools for improved performance.
- -   **Enhanced Configuration**
-     -   Added configurable packet chunking (`num_of_packets_in_chunk`) and burst flags for better flow control.
- -   **Code Quality**
-     -   Standardized naming convention from 'rmax' to 'rivermax' project-wide and removed redundant documentation.
+ -   **Framework Migration**
+   -   Migrated from legacy rmax-ral-lib to modern rivermax-dev-kit framework (1.70.32).
+ -   **SMPTE 2110-20 Support**
+   -   Implemented hardware-accelerated video streaming with support for multiple memory modes (GPU, host pinned, huge pages) and video formats (YCbCr 4:2:2/4:4:4/4:2:0, RGB 8/10/12-bit).
+ -   **Zero-Copy Performance**
+   -   Added MediaSenderZeroCopyService that forwards received frames without owning memory pools for improved performance.
+ -   **Enhanced Configuration**
+   -   Added configurable packet chunking (`num_of_packets_in_chunk`) and burst flags for better flow control.
+ -   **Code Quality**
+   -   Standardized naming convention from 'rmax' to 'rivermax' project-wide and removed redundant documentation.
operators/advanced_network/README.md-235-235 (1)

235-235: Fix hard tabs and list indentation per linter violations.

Lines 235 and 261 contain hard tabs (MD010) that should be replaced with spaces. Additionally, Line 261 has incorrect list indentation (MD007: expected 2 spaces, actual 1 space).

Replace hard tabs with spaces throughout these lines and adjust list indentation to 2 spaces.

-	- **`memory_registration`**: Flag, when enabled, reduces the number of memory keys in use by registering all the memory in a single pass on the application side.
+  - **`memory_registration`**: Flag, when enabled, reduces the number of memory keys in use by registering all the memory in a single pass on the application side.

Apply similar formatting to line 261 and any other hard tabs in the document.

Also applies to: 261-261

operators/advanced_network/README.md-355-355 (1)

355-355: Fix sentence structure: provide subject for dangling phrase.

Line 355 begins with a sentence fragment. The phrase "Should be isolated when CPU polls the NIC..." lacks a clear grammatical subject. Revise to form a complete sentence.

-  - **`cpu_core`**: CPU core ID. Should be isolated when CPU polls the NIC for best performance. <mark>Not in use for DOCA GPUNetIO</mark>. Rivermax Manager can accept comma-separated list of CPU IDs.
+  - **`cpu_core`**: CPU core ID on which the NIC should poll for best performance. <mark>Not in use for DOCA GPUNetIO</mark>. Rivermax Manager can accept comma-separated list of CPU IDs.
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_chunk_consumer_ano.h-94-98 (1)

94-98: Silent packet discarding may cause data loss.

When chunk_size > max_burst_size_, the code logs a warning but silently discards the excess packets. This could lead to data loss that's difficult to debug. Consider either:

  1. Returning an error status to signal the overflow condition
  2. Processing packets in multiple batches
  3. Adding metrics/counters to track discarded packets
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_queue_configs.cpp-321-326 (1)

321-326: Inconsistent stream ID validation between IPO and RTP receivers.

IPO receiver (lines 322-325) has stream ID sequential check commented out, while RTP receiver (lines 386-390) enforces it. This inconsistency may cause confusion. Consider aligning the behavior or documenting the difference.

applications/adv_networking_bench/cpp/testing/benchmark_utils.py-598-603 (1)

598-603: Handle edge case where file_path has no directory component.

If file_path is just a filename (e.g., "output.bin"), os.path.dirname() returns an empty string, and os.makedirs("") will raise FileNotFoundError.

Apply this diff:

     # Create the directory if it doesn't exist
     try:
-        os.makedirs(os.path.dirname(file_path), exist_ok=True)
+        dir_name = os.path.dirname(file_path)
+        if dir_name:
+            os.makedirs(dir_name, exist_ok=True)
     except OSError as e:
-        logger.error(f"Failed to create directory: {e}")
+        logger.exception(f"Failed to create directory: {e}")
         raise
applications/adv_networking_media_player/python/metadata.json-16-16 (1)

16-16: Use approved category tag.

The first tag "Network" is not in the approved categories list. Based on coding guidelines, the category must be one of the approved values. For networking-related applications, use "Networking and Distributed Computing" instead.

Apply this diff:

-	"tags": ["Network", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],
+	"tags": ["Networking and Distributed Computing", "Networking", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],

As per coding guidelines, the category (first tag in the tags array) must match one of the approved categories.

operators/advanced_network_media/advanced_network_media_rx/metadata.json-15-15 (1)

15-15: Use approved category tag.

The first tag "Network" is not in the approved categories list. For networking-related operators, use "Networking and Distributed Computing" as the first tag.

Apply this diff:

-	"tags": ["Network", "Networking", "DPDK", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],
+	"tags": ["Networking and Distributed Computing", "Networking", "DPDK", "UDP", "Ethernet", "IP", "GPUDirect", "Rivermax"],

As per coding guidelines, the category (first tag in the tags array) must match one of the approved categories.

applications/adv_networking_media_sender/cpp/adv_networking_media_sender.cpp-43-46 (1)

43-46: Remove duplicate log message.

Lines 43 and 46 both log the manager type with nearly identical messages. Keep one and remove the other to avoid redundancy.

Apply this diff:

     HOLOSCAN_LOG_INFO("Using Advanced Network manager {}",
                       advanced_network::manager_type_to_string(mgr_type));
 
-    HOLOSCAN_LOG_INFO("Using ANO manager {}", advanced_network::manager_type_to_string(mgr_type));
     if (!tx_en) {
applications/adv_networking_bench/adv_networking_bench_rivermax_tx_rx.yaml-65-86 (1)

65-86: Align RX configuration format with other Rivermax YAMLs.

This file uses a legacy flat format for Rivermax RX settings (lines 73-84) with direct IP address arrays, while adv_networking_bench_rivermax_rx.yaml uses the newer hierarchical rx_threadsnetwork_settings structure. This inconsistency may confuse users and doesn't showcase the multi-stream per thread capability.

Consider updating to match the hierarchical format:

           rivermax_rx_settings:
             settings_type: "ipo_receiver"
             memory_registration: true
             #allocator_type: "huge_page_2mb"
             verbose: true
             max_path_diff_us: 10000
             ext_seq_num: true
             sleep_between_operations_us: 0
-            local_ip_addresses:
-              - 2.1.0.12
-              - 2.1.0.12
-            source_ip_addresses:
-              - 2.1.0.2
-              - 2.1.0.2
-            destination_ip_addresses:
-              - 224.1.1.1
-              - 224.1.1.2
-            destination_ports:
-              - 50001
-              - 50001
+            rx_threads:
+            - thread_id: 0
+              network_settings:
+              - stream_id: 0
+                local_ip_addresses:
+                  - 2.1.0.12
+                source_ip_addresses:
+                  - 2.1.0.2
+                destination_ip_addresses:
+                  - 224.1.1.1
+                destination_ports:
+                  - 50001
+              - stream_id: 1
+                local_ip_addresses:
+                  - 2.1.0.12
+                source_ip_addresses:
+                  - 2.1.0.2
+                destination_ip_addresses:
+                  - 224.1.1.2
+                destination_ports:
+                  - 50001
             stats_report_interval_ms: 1000
             send_packet_ext_info: true
operators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-105-115 (1)

105-115: Static strategy_logged also affects multi-instance behavior.

Similar to the configuration statics, this static bool strategy_logged means the strategy detection message will only be logged for the first stream that detects a strategy, not for each stream independently. Consider making this an instance member as well.

applications/adv_networking_media_sender/adv_networking_media_sender.yaml-1-3 (1)

1-3: Update copyright year for new file.

This is a new file added in the PR, but the copyright header shows 2022-2023. Consider updating to 2023-2025 to match other new files in this PR.

 %YAML 1.2
-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
applications/adv_networking_media_player/cpp/CMakeLists.txt-1-2 (1)

1-2: Update copyright year for new file.

The copyright shows 2022-2023 but this is a new file added in this PR. Consider updating to 2023-2025 for consistency.

-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
applications/adv_networking_media_player/python/adv_networking_media_player.py-3-4 (1)

3-4: Update copyright year for new file.

Similar to the YAML file, the copyright header shows 2022-2023 but this is a new file. Consider updating to 2023-2025.

-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
operators/advanced_network_media/advanced_network_media_rx/burst_processor.cpp-1-3 (1)

1-3: Update copyright year for consistency.

The copyright shows 2023 only. For consistency with other new files in this PR, consider updating to 2023-2025.

 /*
- * SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ * SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
operators/advanced_network_media/advanced_network_media_rx/adv_network_media_rx.cpp-395-399 (1)

395-399: Typo in parameter description: "devices" should be "device".

The description says 'host' or 'devices' but should say 'host' or 'device' to match the actual comparison logic at line 95.

   spec.param(memory_location_,
              "memory_location",
              "Memory Location for Output Frames",
-             "Memory location for output frames ('host' or 'devices')",
+             "Memory location for output frames ('host' or 'device')",
              std::string("device"));
applications/adv_networking_media_sender/python/adv_networking_media_sender.py-3-3 (1)

3-3: Update copyright year for new file.

The copyright year 2022-2023 appears outdated for a new file. Consider updating to include 2025.

-# SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-568-574 (1)

568-574: Typo in video format string: "YUV442" should likely be "YUV444".

Line 572 maps "YUV442" to YCbCr_4_4_4, but the standard naming for 4:4:4 subsampling would be "YUV444". This appears to be a typo.

     if (format == "RGB888") return VideoSampling::RGB;
     if (format == "YUV422") return VideoSampling::YCbCr_4_2_2;
     if (format == "YUV420") return VideoSampling::YCbCr_4_2_0;
-    if (format == "YUV442") return VideoSampling::YCbCr_4_4_4;
+    if (format == "YUV444") return VideoSampling::YCbCr_4_4_4;
     return VideoSampling::Unknown;
applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp-129-129 (1)

129-129: Off-by-one error in frame recording check.

The condition frames_recorded_ > num_of_frames_to_record_.get() allows recording num_of_frames_to_record + 1 frames before stopping. Should use >= for exact count.

-    if (frames_recorded_ > num_of_frames_to_record_.get()) { return; }
+    if (frames_recorded_ >= num_of_frames_to_record_.get()) { return; }
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/burst_manager.cpp-400-402 (1)

400-402: Missing braces around multi-statement conditional.

Line 400-401 appears to be a conditional check, but line 401 is executed unconditionally due to missing braces. If burst_out_size_ validation is intended to be conditional, add braces.

-  if (burst_out_size_ > RivermaxBurst::MAX_PKT_IN_BURST || burst_out_size_ == 0)
-  burst_out_size_ = RivermaxBurst::MAX_PKT_IN_BURST;
+  if (burst_out_size_ > RivermaxBurst::MAX_PKT_IN_BURST || burst_out_size_ == 0) {
+    burst_out_size_ = RivermaxBurst::MAX_PKT_IN_BURST;
+  }
applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-387-392 (1)

387-392: Infinite retry loop when add_item_blocking fails.

If add_item_blocking returns a non-SUCCESS status other than queue being full (e.g., if stopped), the inner while loop will spin indefinitely with only a sleep, never breaking out. The add_item_blocking already handles waiting for space, so the retry loop may be unnecessary.

Consider simplifying to handle the stop case:

-      while (!stop_) {
-        auto status = frame_buffer_queue_.add_item_blocking(frame_buffer);
-
-        if (status == Status::SUCCESS) { break; }
-        std::this_thread::sleep_for(std::chrono::microseconds(sleep_duration_microseconds_));
-      }
+      auto status = frame_buffer_queue_.add_item_blocking(frame_buffer);
+      if (status != Status::SUCCESS) {
+        HOLOSCAN_LOG_DEBUG("Failed to add frame to queue: {}", static_cast<int>(status));
+      }
applications/adv_networking_bench/cpp/rivermax_bench_op_tx.h-520-525 (1)

520-525: Missing CUDA error checking on cudaMemcpy.

The cudaMemcpy call on line 522 doesn't check for errors. If the copy fails, the corrupt or incomplete data will be sent.

Consider using the CUDA_TRY pattern or checking the return value:

     // Copy frame data into the burst
     if (payload_memory_.get() == "device") {
-      cudaMemcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size(), cudaMemcpyDefault);
+      cudaError_t err = cudaMemcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size(), cudaMemcpyDefault);
+      if (err != cudaSuccess) {
+        HOLOSCAN_LOG_ERROR("cudaMemcpy failed: {}", cudaGetErrorString(err));
+        free_tx_burst(cur_msg_);
+        cur_msg_ = nullptr;
+        return;
+      }
     } else {
       std::memcpy(cur_msg_->pkts[0][0], frame->get(), frame->get_size());
     }
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.h-74-75 (1)

74-75: Incorrect comment: "recv flows" should be "send flows" or similar.

The comment says "Network recv flows" but this is the sender application, and the member stores thread/stream configuration for sending.

-  /* Network recv flows */
+  /* Per-thread stream configurations */
   std::vector<std::vector<TwoTupleFlow>> m_threads_streams;
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp-352-358 (1)

352-358: new operator throws on failure; null check is ineffective.

The new byte_t[pool_buffer_size] expression will throw std::bad_alloc on failure rather than returning nullptr. The null check will never trigger.

Consider using new (std::nothrow) if you want to handle allocation failure gracefully:

   } else {
-    pool_buffer = new byte_t[pool_buffer_size];
-    if (pool_buffer == nullptr) {
-      HOLOSCAN_LOG_ERROR("Failed to allocate host memory");
-      return nullptr;
-    }
+    pool_buffer = new (std::nothrow) byte_t[pool_buffer_size];
+    if (pool_buffer == nullptr) {
+      HOLOSCAN_LOG_ERROR("Failed to allocate host memory");
+      return nullptr;
+    }
     HOLOSCAN_LOG_INFO("Allocated host memory for frames memory pool at address: {}", pool_buffer);
   }
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-359-360 (1)

359-360: Copy-paste error: Log message says "RX" but this is a TX function.

The log message incorrectly refers to "Rivermax RX ANO settings" in the TX configuration path.

Apply this fix:

   HOLOSCAN_LOG_INFO(
-      "Rivermax RX ANO settings for {} ({}) on port {}", q.common_.name_, queue_id, port_id);
+      "Rivermax TX ANO settings for {} ({}) on port {}", q.common_.name_, queue_id, port_id);
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-857-883 (1)

857-883: Unreachable code after try block.

The return true; at line 882 is unreachable because all paths in the try block either return or fall through to the catch block which also returns.

Remove the unreachable statement:

   } catch (const std::out_of_range& e) {
     if (num_of_mrs == 1)
       HOLOSCAN_LOG_ERROR("Invalid memory region for Rivermax RX queue: {}", queue_mr_names[0]);
     else
       HOLOSCAN_LOG_ERROR("Invalid memory region for Rivermax RX queue: {} or {}",
                          queue_mr_names[0],
                          queue_mr_names[1]);
     return false;
   }
-
-  return true;
 }
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.h-85-90 (1)

85-90: Uninitialized protected members port_id_ and queue_id_.

These members are declared but not initialized in the base class constructor. If derived classes access them before explicitly setting values, this could lead to undefined behavior.

Consider adding default initialization:

 protected:
   uint32_t service_id_;       ///< Unique identifier for the service
-  uint16_t port_id_;          ///< Port ID derived from the service ID
-  uint16_t queue_id_;         ///< Queue ID derived from the service ID
+  uint16_t port_id_ = 0;      ///< Port ID derived from the service ID
+  uint16_t queue_id_ = 0;     ///< Queue ID derived from the service ID
   bool initialized_ = false;  ///< Flag indicating whether the service is initialized
 };
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_config_manager.cpp-779-801 (1)

779-801: Dead variable to_reset_cores_vector in validate_cores.

This variable is declared but never used, likely copied from parse_and_set_cores where it serves a purpose.

Remove the unused variable:

 int ConfigManagerUtilities::validate_cores(const std::string& cores) {
   std::istringstream iss(cores);
   std::string coreStr;
-  bool to_reset_cores_vector = true;
   int num_cores = 0;

Comment on lines +106 to +110
add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect dependency for rivermax_tx_rx target.

Line 108 has the wrong dependency file. The target copies adv_networking_bench_rivermax_tx_rx.yaml but depends on adv_networking_bench_rivermax_rx.yaml, which will cause incorrect rebuild behavior.

Apply this diff:

 add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
   COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
-  DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_rx.yaml"
+  DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/../adv_networking_bench_rivermax_tx_rx.yaml"
 )
🤖 Prompt for AI Agents
In applications/adv_networking_bench/cpp/CMakeLists.txt around lines 106 to 110,
the custom target adv_networking_bench_rivermax_tx_rx_yaml copies
adv_networking_bench_rivermax_tx_rx.yaml but incorrectly lists
adv_networking_bench_rivermax_rx.yaml as a DEPENDS; update the DEPENDS to
reference the correct source file (adv_networking_bench_rivermax_tx_rx.yaml with
the same ../ path) so the target depends on the actual file being copied and
rebuilds correctly.

Comment on lines +85 to +101
add_custom_target(adv_networking_bench_rivermax_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml)

add_custom_target(adv_networking_bench_rivermax_tx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml)

add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Target names in add_dependencies don't match add_custom_target names.

The custom targets are defined with rivermax in the name, but add_dependencies references rmax. This mismatch will cause CMake errors.

 add_custom_target(adv_networking_bench_rivermax_rx_yaml
   COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml"
 )
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_rx_yaml)

 add_custom_target(adv_networking_bench_rivermax_tx_yaml
   COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml"
 )
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_yaml)

 add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
   COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
   DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml"
 )
-add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml)
+add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
add_custom_target(adv_networking_bench_rivermax_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_rx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rmax_tx_rx_yaml)
add_custom_target(adv_networking_bench_rivermax_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rivermax_rx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_yaml)
add_custom_target(adv_networking_bench_rivermax_tx_rx_yaml
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml" ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/adv_networking_bench_rivermax_tx_rx.yaml"
)
add_dependencies(adv_networking_bench adv_networking_bench_rivermax_tx_rx_yaml)
🤖 Prompt for AI Agents
In applications/adv_networking_bench/cpp/CMakeLists.txt.install around lines 85
to 101, the add_dependencies calls reference targets with "rmax" while the
add_custom_target definitions use "rivermax", causing a target name mismatch;
update each add_dependencies line to reference the exact custom target names
(adv_networking_bench_rivermax_rx_yaml, adv_networking_bench_rivermax_tx_yaml,
adv_networking_bench_rivermax_tx_rx_yaml) so they match the corresponding
add_custom_target names.

from_config("holoviz"),
Arg("cuda_stream_pool", cuda_stream_pool),
Arg("allocator") = allocator);
add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Type mismatch: accessing unordered_map<string, ...> with integer key.

adv_net_media_rx_map is declared as std::unordered_map<std::string, std::shared_ptr<ops::AdvNetworkMediaRxOp>> but is accessed with adv_net_media_rx_map[0] using an integer key. This will implicitly convert 0 to a string "0", which is unlikely to match any stream name.

The map should be accessed by stream name. Consider using an iterator to get the first element:

-      add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
+      add_flow(adv_net_media_rx_map.begin()->second, visualizer, {{"out_video_buffer", "receivers"}});

Or store the first stream name and use it:

+      std::string first_stream_name = multi_streams_adv_net_media_rx_yaml[0]["name"].as<std::string>();
-      add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
+      add_flow(adv_net_media_rx_map[first_stream_name], visualizer, {{"out_video_buffer", "receivers"}});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
add_flow(adv_net_media_rx_map[0], visualizer, {{"out_video_buffer", "receivers"}});
add_flow(adv_net_media_rx_map.begin()->second, visualizer, {{"out_video_buffer", "receivers"}});
🤖 Prompt for AI Agents
In applications/adv_networking_media_player/cpp/adv_networking_media_player.cpp
around line 383, the code uses adv_net_media_rx_map[0] which treats 0 as string
"0" and is a type/key mismatch; replace this access with a proper lookup of the
desired stream—either use adv_net_media_rx_map.begin()->second to get the first
element (after checking the map is not empty) or use the actual stream name
string key (e.g., adv_net_media_rx_map.at(streamName) or find(streamName) and
handle missing keys); ensure you check the map is non-empty or the key exists
before calling add_flow to avoid dereferencing end().

Comment on lines +15 to +59
cmake_minimum_required(VERSION 3.20)
project(advanced_network_media_rx)

find_package(holoscan 2.6 REQUIRED CONFIG
PATHS "/opt/nvidia/holoscan" "/workspace/holoscan-sdk/install")

add_library(${PROJECT_NAME} SHARED
adv_network_media_rx.cpp
burst_processor.cpp
packets_to_frames_converter.cpp
)

add_library(holoscan::ops::${PROJECT_NAME} ALIAS ${PROJECT_NAME})

target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../../advanced_network
${CMAKE_CURRENT_SOURCE_DIR}/../../advanced_network/advanced_network
)

target_link_libraries(${PROJECT_NAME}
PRIVATE
holoscan::core
GXF::multimedia
rivermax-dev-kit
advanced_network_common
advanced_network_media_common
)

set_target_properties(${PROJECT_NAME} PROPERTIES
OUTPUT_NAME "holoscan_op_advanced_network_media_rx"
EXPORT_NAME ops::advanced_network_media_rx
)

# Installation
install(
TARGETS
${PROJECT_NAME}
EXPORT holoscan-networking-targets
COMPONENT advanced_network-cpp
)

if(HOLOHUB_BUILD_PYTHON)
add_subdirectory(python)
endif()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find the add_holohub_operator macro definition
fd -t f "add_holohub_operator" --search-path . || rg "macro.*add_holohub_operator" -t cmake

Repository: nvidia-holoscan/holohub

Length of output: 190


🏁 Script executed:

# Search for add_holohub_operator usage in CMakeLists.txt files across operators
rg "add_holohub_operator" --type cmake operators/ -l | head -20

Repository: nvidia-holoscan/holohub

Length of output: 190


🏁 Script executed:

# Find the file structure of the advanced_network operator
find operators/advanced_network -type f -name "CMakeLists.txt" | head -10

Repository: nvidia-holoscan/holohub

Length of output: 190


🌐 Web query:

holoscan holohub add_holohub_operator cmake macro

💡 Result:

Short answer — what it is and where to use it:

  • add_holohub_operator is a Holohub-provided CMake helper that registers an operator component with the Holohub build infrastructure (so the operator can be built, packaged and depended-on by Holohub applications). [1][2][3]

Usage (typical / high-level):

  • Call from an operators/CMakeLists.txt inside a Holohub component:
    add_holohub_operator(<operator_name> [DEPENDS <other_components>] [EXTENSIONS <gxf_extensions> ...])
  • The macro is part of Holohub’s CMake helper files (the HoloHubConfigHelpers / CMake utility files in the holohub repo) and is used by Holohub’s top-level build scripts (the holohub tooling) to wire operator targets, dependencies and packaging. [1][2]

Where to read/examples:

  • Holohub repository (cmake helpers and operators/ CMakeLists usage). [1]
  • Holoscan/Holohub developer docs and examples (shows patterns for add_holohub_operator, add_holohub_application, add_holohub_extension, etc.). [2][3]

If you want, I can:

  • fetch the exact definition/arguments from HoloHubConfigHelpers.cmake in the holohub repo and paste the macro signature + a concrete CMake example.

🌐 Web query:

holoscan holohub operators CMakeLists.txt build integration

💡 Result:

Short summary and steps to integrate Holohub operators into a Holoscan C++ build (with minimal example CMake snippets).

Key points

  • Use find_package(holoscan) to load the SDK (point to /opt/nvidia/holoscan or set Holoscan_ROOT). [1][2]
  • Build operators as shared libraries that link holoscan::core. [2]
  • If the operator and application are in the same CMake project: add the operator target and list it in target_link_libraries() for the app. [2]
  • If operator and app are separate projects: export the operator (install/EXPORT) and import it in the app (find_package or find_library / target import), then link the imported operator target to the app. [2]
  • Holohub repo includes a holohub script that wraps container/build steps and places build outputs under ./build/. Use ./holohub build or run-container for development. [4]

Minimal CMake examples

  • Operator CMakeLists.txt (operator project)
    cmake_minimum_required(VERSION 3.20)
    project(my_operator CXX)
    find_package(holoscan REQUIRED CONFIG PATHS "/opt/nvidia/holoscan")
    add_library(my_operator SHARED my_operator.cpp)
    target_link_libraries(my_operator PUBLIC holoscan::core)

  • App CMakeLists.txt (same project)
    add_executable(my_app main.cpp)
    target_link_libraries(my_app PRIVATE holoscan::core my_operator)

  • App CMakeLists.txt (separate project; import operator)
    find_package(holoscan REQUIRED CONFIG PATHS "/opt/nvidia/holoscan")

    either find imported operator via find_package/find_library or add dependency to installed target

    add_executable(my_app main.cpp)
    target_link_libraries(my_app PRIVATE holoscan::core <imported_operator_target_or_library>)

Build commands (example)
cmake -S <src_dir> -B <build_dir> -D Holoscan_ROOT="/opt/nvidia/holoscan"
cmake --build <build_dir> -j

References

  • Holoscan SDK — Creating an Application (CMake usage). [1]
  • Holoscan SDK — Creating Operators (build/link guidance and same-project vs separate-project usage). [2]
  • NVIDIA holohub GitHub (holohub script, container/build conventions). [4]

If you want, I can:

  • produce a complete example repo CMakeLists.txt pair (operator + app) you can copy, or
  • show the CMake export/install snippet for publishing an operator for separate-project consumption. Which would you prefer?

Replace add_library() with add_holohub_operator() for proper build system integration.

This CMakeLists.txt does not use the required add_holohub_operator() macro. Per Holohub coding guidelines, all operators in the repository must use add_holohub_operator() for proper registration with the Holohub build infrastructure.

Additionally, custom operator/extension dependencies (advanced_network_common and advanced_network_media_common) should be declared via the DEPENDS argument to add_holohub_operator() rather than directly in target_link_libraries().

🤖 Prompt for AI Agents
In operators/advanced_network_media/advanced_network_media_rx/CMakeLists.txt
around lines 15-59, replace the direct add_library() usage with the repository
macro add_holohub_operator() so the operator is properly registered; move custom
operator/extension dependencies (advanced_network_common and
advanced_network_media_common) out of target_link_libraries and declare them via
the DEPENDS argument to add_holohub_operator(); keep non-operator libs
(holoscan::core, GXF::multimedia, rivermax-dev-kit) in target_link_libraries as
PRIVATE, preserve target_include_directories and set_target_properties as needed
(or remove redundant install/export if add_holohub_operator handles them), and
ensure the alias holoscan::ops::${PROJECT_NAME} is created after the operator
target is defined.

Comment on lines +164 to +174
void ANOIPOReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size());
m_ipo_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_ipo_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: reserve() followed by direct indexing causes undefined behavior.

m_streams_per_thread.reserve() only allocates capacity without creating elements. Accessing m_streams_per_thread[thread_index] on non-existent indices is undefined behavior. Use resize() to create elements, or use push_back().

 void ANOIPOReceiverApp::distribute_work_for_threads()
 {
-  m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size());
+  m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size());
   m_ipo_receiver_settings->num_of_total_streams = 0;
   int thread_index = 0;
   for (const auto& thread : m_ipo_receiver_settings->thread_settings) {
     m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
     m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
     thread_index++;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void ANOIPOReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_ipo_receiver_settings->thread_settings.size());
m_ipo_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_ipo_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
void ANOIPOReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size());
m_ipo_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_ipo_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_ipo_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_ipo_receiver.cpp
around lines 164-174, the code calls m_streams_per_thread.reserve(...) then
writes to m_streams_per_thread[thread_index], which is undefined because reserve
only sets capacity not size; change to either call
m_streams_per_thread.resize(m_ipo_receiver_settings->thread_settings.size())
before the loop so indexing is valid, or replace the indexed assignment with
m_streams_per_thread.push_back(thread.stream_network_settings.size()) and
increment num_of_total_streams accordingly; keep the num_of_total_streams
accumulation as-is and remove the manual thread_index if using push_back.

Comment on lines +208 to +219
void ANOMediaSenderApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size());
m_media_sender_settings->num_of_total_streams = 0;
m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size();
int thread_idx = 0;
for (const auto& thread : m_media_sender_settings->thread_settings) {
m_streams_per_thread[thread_idx] = thread.stream_network_settings.size();
m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_idx++;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential out-of-bounds access due to reserve vs resize.

m_streams_per_thread.reserve() only allocates capacity but does not create elements. Accessing m_streams_per_thread[thread_idx] on line 215 causes undefined behavior since the vector is empty.

Apply this diff to fix:

 void ANOMediaSenderApp::distribute_work_for_threads()
 {
-  m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size());
+  m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size());
   m_media_sender_settings->num_of_total_streams = 0;
   m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size();
   int thread_idx = 0;
   for (const auto& thread : m_media_sender_settings->thread_settings) {
     m_streams_per_thread[thread_idx] = thread.stream_network_settings.size();
     m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size();
     thread_idx++;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void ANOMediaSenderApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_media_sender_settings->thread_settings.size());
m_media_sender_settings->num_of_total_streams = 0;
m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size();
int thread_idx = 0;
for (const auto& thread : m_media_sender_settings->thread_settings) {
m_streams_per_thread[thread_idx] = thread.stream_network_settings.size();
m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_idx++;
}
}
void ANOMediaSenderApp::distribute_work_for_threads()
{
m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size());
m_media_sender_settings->num_of_total_streams = 0;
m_media_sender_settings->num_of_threads = m_media_sender_settings->thread_settings.size();
int thread_idx = 0;
for (const auto& thread : m_media_sender_settings->thread_settings) {
m_streams_per_thread[thread_idx] = thread.stream_network_settings.size();
m_media_sender_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_idx++;
}
}
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_media_sender.cpp
around lines 208-219, m_streams_per_thread.reserve(...) is used but later
indexed with m_streams_per_thread[thread_idx], which is undefined because
reserve does not create elements; change the allocation to create elements
(e.g., call
m_streams_per_thread.resize(m_media_sender_settings->thread_settings.size())
before the loop) or alternatively replace the indexed assignment with
m_streams_per_thread.push_back(...) inside the loop so elements are valid when
written, and keep the rest of the counting logic unchanged.

Comment on lines +154 to +164
void ANORTPReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size());
m_rtp_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_rtp_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: reserve() followed by direct indexing causes undefined behavior.

Same issue as in ano_ipo_receiver.cpp: reserve() only allocates capacity without creating elements. Direct indexing into uninitialized positions is undefined behavior.

 void ANORTPReceiverApp::distribute_work_for_threads()
 {
-  m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size());
+  m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size());
   m_rtp_receiver_settings->num_of_total_streams = 0;
   int thread_index = 0;
   for (const auto& thread : m_rtp_receiver_settings->thread_settings) {
     m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
     m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
     thread_index++;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void ANORTPReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.reserve(m_rtp_receiver_settings->thread_settings.size());
m_rtp_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_rtp_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
void ANORTPReceiverApp::distribute_work_for_threads()
{
m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size());
m_rtp_receiver_settings->num_of_total_streams = 0;
int thread_index = 0;
for (const auto& thread : m_rtp_receiver_settings->thread_settings) {
m_streams_per_thread[thread_index] = thread.stream_network_settings.size();
m_rtp_receiver_settings->num_of_total_streams += thread.stream_network_settings.size();
thread_index++;
}
}
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/applications/ano_rtp_receiver.cpp
around lines 154-164, the code calls m_streams_per_thread.reserve(...) and then
assigns via m_streams_per_thread[thread_index], which is undefined because
reserve() doesn't create elements; change to create elements first (e.g., call
m_streams_per_thread.resize(m_rtp_receiver_settings->thread_settings.size())
before the loop) or avoid indexing by using push_back/emplace_back inside the
loop, and keep incrementing num_of_total_streams as before.

Comment on lines +563 to +567
auto out_burst_shared = queue_it->second[stream]->dequeue_burst();
if (out_burst_shared == nullptr) { return Status::NULL_PTR; }
*burst = out_burst_shared.get();
return Status::SUCCESS;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Use-after-free with shared_ptr raw pointer extraction.

dequeue_burst() returns a shared_ptr<RivermaxBurst>. Calling .get() and assigning to *burst results in the shared_ptr being destroyed at statement end, potentially freeing the burst while the caller still holds a raw pointer.

This pattern appears in both get_rx_burst overloads. Consider returning the shared_ptr directly or storing it in a member to extend its lifetime:

auto out_burst_shared = queue_it->second[stream]->dequeue_burst();
if (out_burst_shared == nullptr) { return Status::NULL_PTR; }
*burst = out_burst_shared.get();  // Dangling pointer after this line

Also applies to: 594-597

🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/adv_network_rivermax_mgr.cpp
around lines 563-567 (and similarly 594-597) the code extracts a raw pointer via
shared_ptr.get() and assigns it to *burst, causing a use-after-free when the
temporary shared_ptr is destroyed; change the API or caller usage so the
shared_ptr lifetime is preserved — either modify the function to return or
output a std::shared_ptr<RivermaxBurst> (or accept a
std::shared_ptr<RivermaxBurst>& out parameter) or store the dequeued shared_ptr
in a member/longer-lived container before assigning the raw pointer; update
callers accordingly to hold the shared_ptr rather than a raw pointer so the
burst object remains valid.

Comment on lines +104 to +113
Status RivermaxManagerRxService::get_rx_burst(BurstParams** burst, int stream_id) {
if (rx_bursts_out_queue_.count(stream_id) == 0) {
HOLOSCAN_LOG_ERROR("RX bursts out queue not initialized for stream_id: {}", stream_id);
return Status::NOT_READY;
}
auto out_burst = rx_bursts_out_queue_[stream_id]->dequeue_burst().get();
*burst = static_cast<BurstParams*>(out_burst);
if (*burst == nullptr) { return Status::NOT_READY; }
return Status::SUCCESS;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potential use-after-free: returning raw pointer from shared_ptr.

dequeue_burst() returns a shared_ptr<RivermaxBurst>, but .get() is called and the raw pointer is returned. Once the local shared_ptr goes out of scope, the burst may be deleted, leaving *burst as a dangling pointer.

The caller receives a raw pointer that could become invalid immediately. Verify that the queue maintains ownership or consider a different ownership pattern.

auto out_burst = rx_bursts_out_queue_[stream_id]->dequeue_burst().get();
*burst = static_cast<BurstParams*>(out_burst);  // out_burst may be freed here
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp
around lines 104-113, the code calls dequeue_burst().get() and returns the raw
pointer which becomes a dangling pointer when the local shared_ptr is destroyed;
instead preserve ownership by returning or passing a shared_ptr (e.g., change
the API to return std::shared_ptr<BurstParams> or change the out parameter to
std::shared_ptr<BurstParams>&) and assign the dequeued shared_ptr directly (do
not call .get()), or if the queue guarantees ownership transfer store the
dequeued shared_ptr in a caller-visible container to extend its lifetime; update
the function signature and all call sites accordingly so the burst is kept alive
by shared_ptr rather than returning a raw pointer.

Comment on lines +325 to +332
} else if (media_sender_builder_->memory_pool_location_ == MemoryKind::HOST_PINNED) {
cudaError_t cuda_status = cudaHostAlloc(&pool_buffer, pool_buffer_size, cudaHostAllocDefault);
if (cuda_status != cudaSuccess) {
HOLOSCAN_LOG_ERROR("Failed to allocate pinned host memory: {}",
cudaGetErrorString(cuda_status));
cudaFree(pool_buffer);
return nullptr;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Wrong API used to free host-pinned memory.

When cudaHostAlloc fails, cudaFree is called on pool_buffer. However, pool_buffer was never successfully allocated (the allocation just failed), so calling cudaFree on an uninitialized/null pointer is undefined behavior. Additionally, if the intent was cleanup, cudaFreeHost should be used for memory allocated with cudaHostAlloc, not cudaFree.

   } else if (media_sender_builder_->memory_pool_location_ == MemoryKind::HOST_PINNED) {
     cudaError_t cuda_status = cudaHostAlloc(&pool_buffer, pool_buffer_size, cudaHostAllocDefault);
     if (cuda_status != cudaSuccess) {
       HOLOSCAN_LOG_ERROR("Failed to allocate pinned host memory: {}",
                          cudaGetErrorString(cuda_status));
-      cudaFree(pool_buffer);
       return nullptr;
     }
🤖 Prompt for AI Agents
In
operators/advanced_network/advanced_network/managers/rivermax/rivermax_mgr_impl/rivermax_mgr_service.cpp
around lines 325-332, the code calls cudaFree after cudaHostAlloc failure on
pool_buffer which is undefined and the wrong API for host-pinned memory;
initialize pool_buffer to nullptr before allocation, remove the
cudaFree/cudaFreeHost call in the immediate failure branch (just log the error
and return nullptr), and if you need to free previously allocated pinned memory
in other error paths use cudaFreeHost (not cudaFree) to release host-pinned
allocations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants