Skip to content

Gstreamer video recorder: add python bindings#1518

Open
asierfernandez wants to merge 36 commits intonvidia-holoscan:mainfrom
tecnalia-medical-robotics:feature/gstreamer-python-binding
Open

Gstreamer video recorder: add python bindings#1518
asierfernandez wants to merge 36 commits intonvidia-holoscan:mainfrom
tecnalia-medical-robotics:feature/gstreamer-python-binding

Conversation

@asierfernandez
Copy link
Copy Markdown
Contributor

@asierfernandez asierfernandez commented Apr 18, 2026

Added python bindings for gstreamer video recorder operator

Summary by CodeRabbit

  • New Features

    • Python bindings for the GStreamer video recorder, a new Python recorder application (pattern and V4L2 sources), and a dedicated C++ recorder executable as separate build options.
  • Documentation

    • README updated to require --language <cpp|python> and show separate C++/Python quick starts and install notes (CuPy guidance).
  • Tests

    • Added/updated build-time tests that run recorder, verify output file, and validate MPEG via ffprobe for both implementations.
  • Chores

    • Conditional C++/Python subdirectory builds and added gstreamer1.0-tools to install script.

@asierfernandez asierfernandez marked this pull request as ready for review April 18, 2026 11:45
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This PR adds Python bindings for GstVideoRecorderOp (operators/gstreamer/python/) and a new Python recorder application (applications/gstreamer/gst_video_recorder/python/), restructuring the existing C++ app into a cpp/ subdirectory and updating the build system to select implementations via --language cpp|python. Previously flagged issues — the missing pydoc #include, the --storage default mismatch, the ArgumentTypeError in parse_key_value_properties, and the unconditional CuPy import messaging — are all resolved in this revision.

Confidence Score: 5/5

Safe to merge; all previously flagged P0/P1 issues are resolved and only minor P2 style findings remain.

All substantive prior review concerns (pydoc wiring, storage default mismatch, ArgumentTypeError propagation, CuPy import messaging) are addressed. Remaining findings are a typo in the README and a missing changelog entry — neither blocks correctness or functionality.

applications/gstreamer/gst_video_recorder/python/metadata.json (version/changelog mismatch), applications/gstreamer/gst_video_recorder/README.md (double-space typo)

Important Files Changed

Filename Overview
operators/gstreamer/python/gst_video_recorder_op.cpp New pybind11 binding for GstVideoRecorderOp; pydoc header is correctly included and all docstrings are properly wired to the class and method definitions.
operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp New pydoc header with constructor, initialize, and setup docstrings; content is accurate and complete.
applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py New Python application; CuPy import is unconditional (intentional per README), default --storage=1 matches C++, and parse_key_value_properties correctly raises SystemExit.
applications/gstreamer/gst_video_recorder/python/metadata.json New metadata for Python app; version is 1.1.0 but the changelog only contains the 1.0.0 entry — missing a 1.1.0 entry.
applications/gstreamer/gst_video_recorder/README.md Updated README to document C++/Python implementations; minor double-space typo in the Python pattern quick-start command.
operators/gstreamer/python/CMakeLists.txt New CMake build for Python bindings using pybind11_add_holohub_module; import test correctly asserts doc truthy, which is valid given the pydoc integration.
operators/gstreamer/python/tests/test_gst_video_recorder_op.py New unit tests for Python binding; covers import, default construction, and kwarg construction.
applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt Moved from parent directory into cpp/ subdirectory; updated include paths and added rm -f cleanup before recording test.
applications/gstreamer/install_deps.sh Adds gstreamer1.0-tools needed for ffprobe/gst-launch CLI tools used in tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Pattern["Pattern Source (--source pattern)"]
        PAT[PatternGeneratorOp\ngradient / checkerboard / colorbars]
    end

    subgraph V4L2["V4L2 Source (--source v4l2)"]
        V4L[V4L2VideoCaptureOp]
        FC[FormatConverterOp\nrgba8888 → rgba8888]
        V4L --> FC
    end

    subgraph Recorder
        GST[GstVideoRecorderOp\nC++ or Python binding]
    end

    PAT -->|output / input| GST
    FC -->|tensor / input| GST
    GST -->|GStreamer pipeline| MP4[output.mp4]

    subgraph Build
        CPP_BUILD["--language cpp\nHOLOHUB_BUILD_CPP"]
        PY_BUILD["--language python\nHOLOHUB_BUILD_PYTHON"]
    end
Loading

Reviews (14): Last reviewed commit: "Clarify CuPy requirement in Python gst_v..." | Re-trigger Greptile

Comment thread operators/gstreamer/python/gst_video_recorder_op.cpp
Comment thread operators/gstreamer/python/gst_video_recorder_op.cpp Outdated
Comment thread operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Condenses gst_video_recorder into C++ and Python subtrees, makes Python subdirs conditional, adds pybind11 Python bindings and pydoc for GstVideoRecorderOp, implements a Python gst_video_recorder application and tests, moves C++ app into a cpp subtree with tests, updates metadata/README and adds gstreamer tools to deps.

Changes

Cohort / File(s) Summary
Operators CMake & Python binding
operators/gstreamer/CMakeLists.txt, operators/gstreamer/python/CMakeLists.txt
Guard addition of python subdir with HOLOHUB_BUILD_PYTHON; add pybind11 module target holoscan_gstreamer_bridge and associated CTest import test.
Python binding implementation & docs
operators/gstreamer/python/gst_video_recorder_op.cpp, operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp
New pybind11 module _holoscan_gstreamer_bridge exporting GstVideoRecorderOp (constructor, initialize, setup) and __version__; added PYDOC macros documenting the operator and methods.
Application build split & README
applications/gstreamer/gst_video_recorder/CMakeLists.txt, applications/gstreamer/gst_video_recorder/README.md
Top-level CMake now conditionally adds cpp/python subdirs; README updated to require `--language <cpp
C++ application files & tests
applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt, applications/gstreamer/gst_video_recorder/cpp/gst_video_recorder.cpp, applications/gstreamer/gst_video_recorder/cpp/metadata.json
New C++ subtree with gst-video-recorder executable, install rules and three chained CTest cases; adjusted includes and bumped metadata to 1.1.0 with updated run path.
Python application files & tests
applications/gstreamer/gst_video_recorder/python/CMakeLists.txt, applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py, applications/gstreamer/gst_video_recorder/python/metadata.json
New Python app script (CLI, PatternGeneratorOp, GstVideoRecorderApp), CMake custom target to copy/install script, CTest chain mirroring C++ tests, and application metadata for Python runnable modes.
Packaging / deps
applications/gstreamer/install_deps.sh
Add gstreamer1.0-tools to apt-get install list.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • GilShpr
  • bhashemian
  • jjomier
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding Python bindings for the GStreamer video recorder operator, which is the primary objective of this pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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
Copy Markdown
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: 1

🧹 Nitpick comments (1)
operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp (1)

50-66: Tighten the duplicated parameter text.

The framerate and block docs repeat the same guidance, and output.mp4 should be formatted consistently as a string literal.

📝 Proposed doc cleanup
 framerate : str
-    Video framerate as fraction or decimal.
-    Formats: "30/1", "30000/1001", "29.97", "60"
-    ``"30/1"`` or ``"30000/1001"``, and decimal values such as ``"29.97"``.
-    Special: ``"0/1"`` for live mode (no framerate control, process frames as fast as they come)
+    Video framerate as a fraction or decimal, for example ``"30/1"``,
+    ``"30000/1001"``, ``"29.97"``, or ``"60"``.
+    Special value ``"0/1"`` enables live mode with no framerate control.
     Default value is ``"30/1"``.
 max_buffers : int
     Maximum number of buffers to queue (0 = unlimited)
     Default value is ``10``.
 block : bool
-    Whether ``push_buffer()`` should block when the internal queue is full.
-    Whether push_buffer() should block when the queue is full (true = block, false = non-blocking, may drop/timeout)
+    Whether ``push_buffer()`` should block when the queue is full
+    (``True`` = block, ``False`` = non-blocking, may drop/timeout).
     Default value is ``True``.
 filename : str
     Output video filename.
-    If no extension is provided, ``".mp4"`` is automatically appended
-    Default value is ``output.mp4``.
+    If no extension is provided, ``".mp4"`` is automatically appended.
+    Default value is ``"output.mp4"``.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp` around lines 50 -
66, Consolidate and tighten the duplicated parameter descriptions: for framerate
remove the repeated sentence so it reads one concise description listing
accepted formats (e.g., "30/1", "30000/1001", "29.97", "60"), mention special
"0/1" for live mode, and state the default "30/1"; for block keep a single clear
sentence like whether push_buffer() blocks when the internal queue is full (True
= block, False = non-blocking) and the default True; for filename ensure the
default value is formatted as a string literal "output.mp4" and note that ".mp4"
will be appended if no extension is provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@operators/gstreamer/python/gst_video_recorder_op.cpp`:
- Around line 24-30: Include and use the pydoc header so Python help() exposes
docs: add an `#include` for "gst_video_recorder_op_pydoc.hpp" in
gst_video_recorder_op.cpp, then wire the pydoc symbols into the pybind11 class
and method definitions by passing the doc string identifiers to the py::class_
constructor and the .def() calls for initialize and setup (referencing the pydoc
symbols from gst_video_recorder_op_pydoc.hpp when constructing py::class_ and
when defining Operator::initialize and Operator::setup).

---

Nitpick comments:
In `@operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp`:
- Around line 50-66: Consolidate and tighten the duplicated parameter
descriptions: for framerate remove the repeated sentence so it reads one concise
description listing accepted formats (e.g., "30/1", "30000/1001", "29.97",
"60"), mention special "0/1" for live mode, and state the default "30/1"; for
block keep a single clear sentence like whether push_buffer() blocks when the
internal queue is full (True = block, False = non-blocking) and the default
True; for filename ensure the default value is formatted as a string literal
"output.mp4" and note that ".mp4" will be appended if no extension is provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 622137e4-9a51-4af5-828f-481915dc0d8a

📥 Commits

Reviewing files that changed from the base of the PR and between 049a55a and 9144e91.

📒 Files selected for processing (4)
  • operators/gstreamer/CMakeLists.txt
  • operators/gstreamer/python/CMakeLists.txt
  • operators/gstreamer/python/gst_video_recorder_op.cpp
  • operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp

Comment thread operators/gstreamer/python/gst_video_recorder_op.cpp
asierfernandez and others added 7 commits April 18, 2026 16:37
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
@asierfernandez asierfernandez force-pushed the feature/gstreamer-python-binding branch from 22fdf1d to 93498ff Compare April 18, 2026 14:37
@asierfernandez
Copy link
Copy Markdown
Contributor Author

Hi @nvidia-holoscan/holohub @GilShpr this is ready for review.
I did test this with a simple application and it works fine. Do you need any other additional test?

@GilShpr
Copy link
Copy Markdown
Contributor

GilShpr commented Apr 20, 2026

Thanks @asierfernandez for contributing the GstVideoRecorderOp Python bindings—we appreciate the work.

Required before merge:

  1. Python sample — Add a runnable Python app that mirrors the existing GStreamer video recorder behavior (same pipeline intent as gst_video_recorder: e.g. pattern vs V4L2-style sources where applicable, GstVideoRecorderOp, output file), implemented with holoscan.core and from holohub.holoscan_gstreamer_bridge import GstVideoRecorderOp.
  2. Layout — Reorganize the sample like other HoloHub apps: move the current C++ implementation under a cpp/ subtree and add the new entry point under python/ (same pattern as apps such as aja_video_capture or endoscopy_tool_tracking with sibling cpp/ and python/ trees). Update metadata.json, CMakeLists.txt, and paths accordingly.
  3. Docs — Short run notes for both languages, including PYTHONPATH for build/python/lib when running the Python app.

We need this structure and parity with the C++ recorder to review and merge.

@bhashemian bhashemian requested a review from Copilot April 21, 2026 13:58
@bhashemian bhashemian added the Action Required by Author An action is required by author to proceed with review and approval. label Apr 21, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 21, 2026

Want your agent to iterate on Greptile's feedback? Try greploops.

Copy link
Copy Markdown
Member

@bhashemian bhashemian left a comment

Choose a reason for hiding this comment

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

Thanks, @GilShpr, for the review.

@asierfernandez, could you please address the feeback so that we can proceed with this PR? Just want to reiterate that there is a requirement for any new operators to have a sample application to test the implemented functionality. Thanks!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a pybind11-based Python extension module for the existing GStreamer video recorder operator so it can be constructed/configured directly from Python within the HoloHub Python build.

Changes:

  • Added pybind11 bindings exposing GstVideoRecorderOp in a new _holoscan_gstreamer_bridge Python module.
  • Added pydoc strings for the Python-facing API.
  • Wired the new Python bindings into the GStreamer operator build when HOLOHUB_BUILD_PYTHON is enabled.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp Adds Python docstrings for the GstVideoRecorderOp binding.
operators/gstreamer/python/gst_video_recorder_op.cpp Implements the pybind11 module and trampoline constructor for GstVideoRecorderOp.
operators/gstreamer/python/CMakeLists.txt Builds the pybind11 module for the holoscan_gstreamer_bridge target.
operators/gstreamer/CMakeLists.txt Adds the python/ subdirectory to the build under HOLOHUB_BUILD_PYTHON.

Comment thread operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp
Comment thread operators/gstreamer/python/gst_video_recorder_op.cpp
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Copy link
Copy Markdown
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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt`:
- Around line 43-84: Remove stale OUTPUT_FILE before running the recorder test
by ensuring the gst_video_recorder_test clears the previous file; update the
COMMAND for the test that defines gst_video_recorder_test (which uses
$<TARGET_FILE:gst-video-recorder> and OUTPUT_FILE/RECORDING_DIR) to first remove
${OUTPUT_FILE} (e.g. run "rm -f ${OUTPUT_FILE} && ..." or create a small setup
test that runs "rm -f ${OUTPUT_FILE}" and make gst_video_recorder_test DEPEND on
it) so subsequent CTest runs won't be masked by stale test_output.mp4.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`:
- Around line 1-396: The file fails Black formatting; run the Black
auto-formatter on the module (e.g., the gst_video_recorder.py that defines
parse_pattern, parse_v4l2_pixel_format, parse_key_value_properties,
PatternGeneratorOp, GstVideoRecorderApp, build_arg_parser, validate_args, main)
and commit the resulting changes so the file adheres to Black's style (use black
. or black <file> and then add/commit the reformatted file); no code logic
changes are required—only reformat and push.
- Around line 353-359: The CLI adds a "--storage" argument in
parser.add_argument defaulting to 1 (device/CUDA) which forces optional CuPy to
be present; change the default to 0 (host memory) so the no-args pattern and
CMake smoke tests work without CuPy, i.e., update the parser.add_argument call
for "--storage" to default=0 and adjust any tests or docs to pass or mention
"--storage 1" when device/CUDA is required; ensure references to the storage
choice in code (e.g., logic that checks storage value) remain consistent.
- Around line 364-375: validate_args currently only checks numeric args and
leaves string flags empty; add non-empty validation for args.output,
args.encoder, and args.device in validate_args so blank strings fail fast. For
each of args.output, args.encoder, args.device, check that the value is not None
and that str(value).strip() is non-empty and raise SystemExit with a clear
message (e.g., "--output cannot be empty", "--encoder cannot be empty",
"--device cannot be empty") when invalid; keep these checks alongside the
existing width/height/framerate validations in the validate_args function.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef7d84d9-4042-477f-b66c-cdfbece43c2c

📥 Commits

Reviewing files that changed from the base of the PR and between 4f104cf and 7ce6073.

📒 Files selected for processing (10)
  • applications/gstreamer/gst_video_recorder/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/README.md
  • applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/cpp/gst_video_recorder.cpp
  • applications/gstreamer/gst_video_recorder/cpp/gst_video_recorder_v4l2.yaml
  • applications/gstreamer/gst_video_recorder/cpp/metadata.json
  • applications/gstreamer/gst_video_recorder/python/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py
  • applications/gstreamer/gst_video_recorder/python/metadata.json
  • applications/gstreamer/install_deps.sh
✅ Files skipped from review due to trivial changes (5)
  • applications/gstreamer/install_deps.sh
  • applications/gstreamer/gst_video_recorder/cpp/gst_video_recorder.cpp
  • applications/gstreamer/gst_video_recorder/README.md
  • applications/gstreamer/gst_video_recorder/cpp/metadata.json
  • applications/gstreamer/gst_video_recorder/python/metadata.json

Comment thread applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt
Comment thread applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py Outdated
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (2)
applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py (2)

254-255: Dead branch: argparse choices=("pattern", "v4l2") already rejects other values.

This else can only be reached if someone mutates args.source after parsing. Safe to drop, or demote to an assert to document the invariant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`
around lines 254 - 255, The explicit RuntimeError branch after parsing is dead
because argparse enforces choices=("pattern","v4l2"); remove the else branch (or
replace it with an assert) to document the invariant instead of raising a
runtime error—locate the code that checks self.args.source (the block that
currently ends with raise RuntimeError(f"unsupported source
'{self.args.source}'")) and either delete that else branch or change it to
assert self.args.source in ("pattern","v4l2"), preserving surrounding logic and
comments.

157-181: Color bars: replace the per-column Python loop with a vectorized fill.

For 1920-wide frames this is 1920 Python iterations per frame through NumPy/CuPy fancy indexing. A single repeat/broadcast produces the same result in one op. Strictly an optimization — no behavior change expected.

♻️ Proposed refactor
-        frame = self.xp.empty((self.height, self.width, RGBA_CHANNELS), dtype=self.xp.uint8)
-        bar_width = self.width // SMPTE_COLOR_BARS
-        if bar_width == 0:
-            bar_width = 1
-
-        for x in range(self.width):
-            bar_idx = x // bar_width
-            if bar_idx >= SMPTE_COLOR_BARS:
-                bar_idx = SMPTE_COLOR_BARS - 1
-            frame[:, x, :] = colors[bar_idx]
-        return frame
+        bar_width = max(1, self.width // SMPTE_COLOR_BARS)
+        xs = self.xp.arange(self.width)
+        bar_idx = self.xp.minimum(xs // bar_width, SMPTE_COLOR_BARS - 1)
+        row = colors[bar_idx]  # shape (width, 4)
+        return self.xp.broadcast_to(row, (self.height, self.width, RGBA_CHANNELS)).copy()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`
around lines 157 - 181, The _colorbars method currently fills frame per-column
with a Python loop causing O(width) Python iterations; replace that loop with a
vectorized operation using self.xp (NumPy/CuPy) to build a 1D array of bar
indices of length self.width via repeat or tile based on bar_width and
SMPTE_COLOR_BARS, then index into colors and broadcast/reshape to assign the
entire frame in one operation (use colors[bar_indices] then reshape or
expand_dims to set frame[:, :, :] accordingly), preserving RGBA_CHANNELS and
edge handling where bar_idx >= SMPTE_COLOR_BARS by clamping the bar indices to
SMPTE_COLOR_BARS-1.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`:
- Around line 370-373: The validation rejects an explicit --count 0 even though
the code treats 0 as the sentinel for "unlimited" (see args.count,
_source_condition_args and CountCondition); update the check to accept 0 as a
valid value (i.e., allow args.count == 0 or 1 <= args.count <= 1_000_000_000) so
an explicit --count 0 maps to the unlimited sentinel, and keep or adjust the
error message to mention that 0 represents unlimited if it remains used in
user-facing output.
- Around line 87-110: The __init__ currently declares allocator=None but
swallows it locally so callers passing allocator have no effect; either remove
the allocator parameter from gst_video_recorder.PatternGeneratorOp.__init__
signature (since pattern mode uses NumPy/CuPy and doesn't need a Holoscan
allocator) and also remove allocator=allocator where PatternGeneratorOp is
instantiated, or explicitly forward allocator to the base class by passing
allocator through to super().__init__(..., allocator=allocator, **kwargs) so the
allocator is registered with the operator; update the instantiation sites (e.g.,
where PatternGeneratorOp(...) is called) accordingly to avoid duplicate/unused
parameters.

---

Nitpick comments:
In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`:
- Around line 254-255: The explicit RuntimeError branch after parsing is dead
because argparse enforces choices=("pattern","v4l2"); remove the else branch (or
replace it with an assert) to document the invariant instead of raising a
runtime error—locate the code that checks self.args.source (the block that
currently ends with raise RuntimeError(f"unsupported source
'{self.args.source}'")) and either delete that else branch or change it to
assert self.args.source in ("pattern","v4l2"), preserving surrounding logic and
comments.
- Around line 157-181: The _colorbars method currently fills frame per-column
with a Python loop causing O(width) Python iterations; replace that loop with a
vectorized operation using self.xp (NumPy/CuPy) to build a 1D array of bar
indices of length self.width via repeat or tile based on bar_width and
SMPTE_COLOR_BARS, then index into colors and broadcast/reshape to assign the
entire frame in one operation (use colors[bar_indices] then reshape or
expand_dims to set frame[:, :, :] accordingly), preserving RGBA_CHANNELS and
edge handling where bar_idx >= SMPTE_COLOR_BARS by clamping the bar indices to
SMPTE_COLOR_BARS-1.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 59d374ba-667e-471e-9b90-45465d58ec3d

📥 Commits

Reviewing files that changed from the base of the PR and between 7ce6073 and d71230d.

📒 Files selected for processing (3)
  • applications/gstreamer/gst_video_recorder/README.md
  • applications/gstreamer/gst_video_recorder/python/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py
✅ Files skipped from review due to trivial changes (1)
  • applications/gstreamer/gst_video_recorder/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • applications/gstreamer/gst_video_recorder/python/CMakeLists.txt

Comment thread applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py Outdated
@asierfernandez
Copy link
Copy Markdown
Contributor Author

OMG! Bots never stop finding issues ;-)

Thanks @GilShpr and @bhashemian for your comments.

I included @GilShpr requests changes. I can also finish fixing issues detected by bots, even they don't have a big impact.

Currently with my laptop I can not test gpu path neither cpp or python falvours, because I get an error with cudaconvert.
I suspect it is related with the docker base image for igpu, because I was not experiencing it with our lab's machine with a dgpu.

bhashemian and others added 9 commits April 22, 2026 10:44
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (2)
applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py (2)

91-114: ⚠️ Potential issue | 🟡 Minor

allocator kwarg is still accepted and silently discarded.

allocator=None is consumed by the explicit keyword and never forwarded to super().__init__, but the caller on line 229 passes allocator=allocator. Either drop the parameter (pattern mode uses NumPy/CuPy directly and doesn't need a Holoscan allocator) and remove allocator=allocator from the instantiation site, or forward it explicitly: super().__init__(fragment, *args, allocator=allocator, **kwargs).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`
around lines 91 - 114, The __init__ accepts an allocator kwarg but currently
swallows it (allocator=None) and doesn't forward it to the base constructor;
update the constructor to either remove the allocator parameter or forward it to
super: change the __init__ signature handling so allocator is passed through in
the call to super().__init__ (i.e., call super().__init__(fragment, *args,
allocator=allocator, **kwargs)) so the allocator passed by callers (see
instantiation site that uses allocator=allocator) is respected; ensure no other
code assumes allocator was consumed and keep existing storage_type/xp logic
intact.

375-378: ⚠️ Potential issue | 🟡 Minor

--count 0 is still rejected despite 0 being the "unlimited" sentinel.

_source_condition_args on line 207 treats count > 0 as "bounded" and count == 0 as "unlimited", and --help advertises "default: unlimited", but an explicit --count 0 still errors with "must be between 1 and 1000000000". Accept 0 as an explicit alias for unlimited or make the message mention it.

Proposed fix
-    if args.count is None:
+    if args.count is None or args.count == 0:
         args.count = 0
-    elif not (1 <= args.count <= 1_000_000_000):
-        raise SystemExit("--count must be between 1 and 1000000000")
+    elif not (1 <= args.count <= 1_000_000_000):
+        raise SystemExit("--count must be 0 (unlimited) or between 1 and 1000000000")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`
around lines 375 - 378, The check rejects an explicit --count 0 even though 0 is
intended as the "unlimited" sentinel used by _source_condition_args; update the
validation around args.count so that 0 is accepted as a valid value (or update
the error text to mention 0 as unlimited). Specifically, replace the current
range check on args.count in gst_video_recorder.py (the block that sets
args.count when None and then raises SystemExit) with logic that allows
args.count == 0 or 1 <= args.count <= 1_000_000_000, and ensure the SystemExit
message reflects that 0 is accepted as "unlimited" if you choose to keep the
error path.
🧹 Nitpick comments (1)
applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py (1)

189-196: Dead else branch after parse_pattern validation.

parse_pattern already rejects anything outside {0, 1, 2} at CLI parse time, so self.pattern can never reach the else fallback. Drop the redundant branch to keep the control flow honest.

Proposed fix
-        if self.pattern == 0:
-            frame = self._gradient()
-        elif self.pattern == 1:
-            frame = self._checkerboard()
-        elif self.pattern == 2:
-            frame = self._colorbars()
-        else:
-            frame = self._gradient()
+        if self.pattern == 0:
+            frame = self._gradient()
+        elif self.pattern == 1:
+            frame = self._checkerboard()
+        else:  # self.pattern == 2
+            frame = self._colorbars()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`
around lines 189 - 196, The final else branch is redundant because parse_pattern
enforces pattern ∈ {0,1,2}; update the branch in the method that selects frames
(the block using self.pattern and calling _gradient, _checkerboard, _colorbars)
to remove the else fallback and leave an explicit if / elif / elif chain, and
add an assertion (e.g., assert self.pattern in (0,1,2)) or a defensive
ValueError immediately before selection to document the invariant enforced by
parse_pattern; refer to parse_pattern, self.pattern, and the helper methods
_gradient, _checkerboard, _colorbars when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`:
- Around line 91-114: The __init__ accepts an allocator kwarg but currently
swallows it (allocator=None) and doesn't forward it to the base constructor;
update the constructor to either remove the allocator parameter or forward it to
super: change the __init__ signature handling so allocator is passed through in
the call to super().__init__ (i.e., call super().__init__(fragment, *args,
allocator=allocator, **kwargs)) so the allocator passed by callers (see
instantiation site that uses allocator=allocator) is respected; ensure no other
code assumes allocator was consumed and keep existing storage_type/xp logic
intact.
- Around line 375-378: The check rejects an explicit --count 0 even though 0 is
intended as the "unlimited" sentinel used by _source_condition_args; update the
validation around args.count so that 0 is accepted as a valid value (or update
the error text to mention 0 as unlimited). Specifically, replace the current
range check on args.count in gst_video_recorder.py (the block that sets
args.count when None and then raises SystemExit) with logic that allows
args.count == 0 or 1 <= args.count <= 1_000_000_000, and ensure the SystemExit
message reflects that 0 is accepted as "unlimited" if you choose to keep the
error path.

---

Nitpick comments:
In `@applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py`:
- Around line 189-196: The final else branch is redundant because parse_pattern
enforces pattern ∈ {0,1,2}; update the branch in the method that selects frames
(the block using self.pattern and calling _gradient, _checkerboard, _colorbars)
to remove the else fallback and leave an explicit if / elif / elif chain, and
add an assertion (e.g., assert self.pattern in (0,1,2)) or a defensive
ValueError immediately before selection to document the invariant enforced by
parse_pattern; refer to parse_pattern, self.pattern, and the helper methods
_gradient, _checkerboard, _colorbars when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e30679d4-29a4-420b-b31d-8a05a8595651

📥 Commits

Reviewing files that changed from the base of the PR and between d71230d and 197ca99.

📒 Files selected for processing (7)
  • applications/gstreamer/gst_video_recorder/README.md
  • applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/python/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py
  • operators/gstreamer/python/CMakeLists.txt
  • operators/gstreamer/python/gst_video_recorder_op.cpp
  • operators/gstreamer/python/gst_video_recorder_op_pydoc.hpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • applications/gstreamer/gst_video_recorder/README.md
  • operators/gstreamer/python/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/python/CMakeLists.txt
  • applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt

Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Comment thread applications/gstreamer/gst_video_recorder/README.md Outdated
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
@asierfernandez asierfernandez requested a review from GilShpr April 22, 2026 18:23
Copy link
Copy Markdown
Member

@bhashemian bhashemian left a comment

Choose a reason for hiding this comment

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

Thanks @asierfernandez for this contribution. I’ve added a few inline comments that would be great to address.

Comment thread applications/gstreamer/gst_video_recorder/python/metadata.json Outdated
Comment thread applications/gstreamer/gst_video_recorder/cpp/metadata.json Outdated
Comment thread applications/gstreamer/gst_video_recorder/README.md Outdated
Comment thread applications/gstreamer/gst_video_recorder/python/gst_video_recorder.py Outdated
Comment thread operators/gstreamer/python/gst_video_recorder_op.cpp Outdated
Comment thread operators/gstreamer/CMakeLists.txt
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
@asierfernandez
Copy link
Copy Markdown
Contributor Author

Thanks @bhashemian @GilShpr for the detailed review and for taking the time to go through this so carefully.

Also, sorry for the multiple re-review requests on this PR. I hope this round is finally the last one, but if there is still anything that does not quite line up, I will be happy to fix it.

Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Signed-off-by: Asier Fernandez <asier.fernandez@tecnalia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Action Required by Author An action is required by author to proceed with review and approval.

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants