Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8d28b19
Gstreamer video recorder: add python bindings
asierfernandez Apr 18, 2026
f7fb101
Fix linter
asierfernandez Apr 18, 2026
c0c2a1e
Update operators/gstreamer/python/gst_video_recorder_op.cpp
asierfernandez Apr 18, 2026
cecfeaf
Fix identation
asierfernandez Apr 18, 2026
48d7ecf
delete duplicated comments
asierfernandez Apr 18, 2026
aedee55
Connect docstrings to bindings
asierfernandez Apr 18, 2026
93498ff
Clean text in pydoc
asierfernandez Apr 18, 2026
4f104cf
Merge branch 'main' into feature/gstreamer-python-binding
bhashemian Apr 21, 2026
f21d34f
Add new layout to handle cpp and python applications
asierfernandez Apr 22, 2026
d38520e
Add python gst_video_recoder application
asierfernandez Apr 22, 2026
5f140be
Install gstreamer tools for development and debugging
asierfernandez Apr 22, 2026
7ce6073
Update documentation for the python gst_video_recorder application
asierfernandez Apr 22, 2026
668d687
Fix the inconsistency between default storage and cupy
asierfernandez Apr 22, 2026
c46e64f
Complete cli arguments validation
asierfernandez Apr 22, 2026
ce15c98
Apply black formatter
asierfernandez Apr 22, 2026
d71230d
Fix pre-commit errors
asierfernandez Apr 22, 2026
e47b070
Merge branch 'main' into feature/gstreamer-python-binding
bhashemian Apr 22, 2026
f5e0e4d
fix gst_video_recorder common include path
asierfernandez Apr 22, 2026
6c68bd0
Remove stale recorder output for tests
asierfernandez Apr 22, 2026
9b094e4
vectorize SMPTE color bar generation
asierfernandez Apr 22, 2026
63bc0bd
Align GstVideoRecorderOp pydoc symbol naming
asierfernandez Apr 22, 2026
a967bde
Add Python import smoke test for GstVideoRecorderOp binding
asierfernandez Apr 22, 2026
90b04f6
Set default device storage in python implementation
asierfernandez Apr 22, 2026
197ca99
Add required tests
asierfernandez Apr 22, 2026
e6009a4
Handle invalid --property values with SystemExit
asierfernandez Apr 22, 2026
549a488
Remove unused allocator from Python pattern generator
asierfernandez Apr 22, 2026
074ead6
Accept --count 0 as explicit unlimited alias
asierfernandez Apr 22, 2026
a0a6f6a
Fix mode order
asierfernandez Apr 22, 2026
9f2310c
Adjust metadata version release numbers
asierfernandez Apr 23, 2026
46ca63c
Remove unreachable CuPy guard in pattern generator
asierfernandez Apr 23, 2026
178236b
Move GstVideoRecorderOp Python bindings to holoscan namespace
asierfernandez Apr 23, 2026
0ca0515
Fix gst_video_recorder launching command example
asierfernandez Apr 23, 2026
80c6b18
Document Python binding usage for GstVideoRecorderOp
asierfernandez Apr 23, 2026
a5fbf2d
The Python docstring now includes both fragment and name
asierfernandez Apr 23, 2026
4437b75
Added a minimal Python test suite for the binding covering importability
asierfernandez Apr 23, 2026
ee7ecd7
Clarify CuPy requirement in Python gst_video_recorder
asierfernandez Apr 23, 2026
50d8b0f
fix metadata and readme
bhashemian Apr 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 5 additions & 68 deletions applications/gstreamer/gst_video_recorder/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,73 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.

project(gst_video_recorder CXX)

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

add_executable(gst-video-recorder
../common/pattern_generator.cpp
gst_video_recorder.cpp
)

target_include_directories(gst-video-recorder
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../common
)

target_link_libraries(gst-video-recorder
PRIVATE
holoscan::core
holoscan::ops::gstreamer_bridge
holoscan::ops::v4l2
holoscan::ops::format_converter
)

# Install target
install(TARGETS gst-video-recorder DESTINATION bin/gst_video_recorder)


if(BUILD_TESTING)
# Set and create the recording directory
set(RECORDING_DIR ${CMAKE_CURRENT_BINARY_DIR}/recording_output)
file(MAKE_DIRECTORY ${RECORDING_DIR})

# Set the output file
set(OUTPUT_FILE ${RECORDING_DIR}/test_output.mp4)
message(STATUS "Output test file: ${OUTPUT_FILE}")

# Test 1: check if the pipeline runs successfully
add_test(
NAME gst_video_recorder_test
COMMAND $<TARGET_FILE:gst-video-recorder> --source pattern --count 10 -o ${OUTPUT_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# Set the test passing if the pipeline runs successfully
set_tests_properties(gst_video_recorder_test PROPERTIES
PASS_REGULAR_EXPRESSION "EOS processed, pipeline finished cleanly"
)

# Test 2: check if the output file was created
add_test(
NAME gst_video_recorder_output_file_test
COMMAND bash -c "test -f ${OUTPUT_FILE}"
)

# Make the file check test depend on the pipeline test
set_tests_properties(gst_video_recorder_output_file_test PROPERTIES
DEPENDS gst_video_recorder_test
)

# Test 3: check if the output file is a valid MPEG video file
add_test(
NAME gst_video_recorder_valid_mpeg_test
COMMAND bash -c "ffprobe -v error ${OUTPUT_FILE}"
)
if(HOLOHUB_BUILD_CPP)
add_subdirectory(cpp)
endif()

# Make the validation test depend on the file check test
set_tests_properties(gst_video_recorder_valid_mpeg_test PROPERTIES
DEPENDS gst_video_recorder_output_file_test
)
if(HOLOHUB_BUILD_PYTHON)
add_subdirectory(python)
endif()
30 changes: 22 additions & 8 deletions applications/gstreamer/gst_video_recorder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

A Holoscan application that demonstrates video recording using the GStreamer encoding pipeline.

This application is available in both **C++** and **Python** implementations.
Both versions expose the **same command-line interface** and behavior.
The only difference when building or launching the application is the `--language` flag: use `--language cpp` for the C++ version or `--language python` for the Python version.

![GStreamer Video Recorder Pipeline](docs/pipeline_diagram.png)
*Fig. 1: Application architecture showing the integration of Holoscan operators with GStreamer's encoding pipeline*

Expand Down Expand Up @@ -42,11 +46,19 @@ For more information about this application, refer to:

## Quick Start

To run the application with the default settings, run one of the following commands:
To run the application with the default settings, choose the implementation with `--language cpp` or `--language python` and then use the same runtime arguments.

Examples with the C++ implementation:

| Using the V4L2 Camera | Generating Test Patterns |
| --- | --- |
| `./holohub run gst_video_recorder --language cpp v4l2` | `./holohub run gst_video_recorder --language cpp pattern` |
Comment thread
asierfernandez marked this conversation as resolved.
Outdated

Examples with the Python implementation:

| Using the V4L2 Camera | Generating Test Patterns |
| --- | --- |
| `./holohub run gst_video_recorder v4l2` | `./holohub run gst_video_recorder pattern` |
| `./holohub run gst_video_recorder --language python v4l2` | `./holohub run gst_video_recorder --language python pattern` |

These commands build and run the customized container for this application with all the dependencies installed (defined by `Dockerfile`), and then build and start the application using the default settings. The output video will be saved in the build directory as `output.mp4`.

Expand All @@ -66,14 +78,16 @@ The `install_deps.sh` script installs:
- GStreamer development libraries
- All necessary GStreamer plugins for encoding

Choose one of the following options to build the application:
Choose one of the following options to build the application.
Select the implementation with `--language cpp` or `--language python`:

| Containerized Build (Recommended) | Local Build |
| --- | --- |
| Install the application: | Install dependencies, from the `gst_video_recorder` directory: |
| `./holohub build gst_video_recorder` | `./install_deps.sh` |
| | Then build locally: |
| | `./holohub build --local gst_video_recorder` |
| No manual dependency installation is required. Dependencies are installed in the application container image. | Install dependencies on the host, from the `gst_video_recorder` directory: |
| `./holohub build gst_video_recorder --language cpp` | `./install_deps.sh` |
| `./holohub build gst_video_recorder --language python` | Then build locally: |
| | `./holohub build --local gst_video_recorder --language cpp` |
| | `./holohub build --local gst_video_recorder --language python` |

### Usage Reference

Expand All @@ -87,7 +101,7 @@ Reference for running `gst_video_recorder` that includes:
The recommended way to run the application is through the `holohub` launcher:

```bash
./holohub run gst_video_recorder --run-args="[OPTIONS]"
./holohub run gst_video_recorder --language <cpp|python> --run-args="[OPTIONS]"
```

Alternatively, if you know the binary location, you can run it directly:
Expand Down
85 changes: 85 additions & 0 deletions applications/gstreamer/gst_video_recorder/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

project(gst_video_recorder CXX)

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

add_executable(gst-video-recorder
../../common/pattern_generator.cpp
gst_video_recorder.cpp
)

target_include_directories(gst-video-recorder
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../common
)

target_link_libraries(gst-video-recorder
PRIVATE
holoscan::core
holoscan::ops::gstreamer_bridge
holoscan::ops::v4l2
holoscan::ops::format_converter
)

# Install target
install(TARGETS gst-video-recorder DESTINATION bin/gst_video_recorder)


if(BUILD_TESTING)
# Set and create the recording directory
set(RECORDING_DIR ${CMAKE_CURRENT_BINARY_DIR}/recording_output)
file(MAKE_DIRECTORY ${RECORDING_DIR})

# Set the output file
set(OUTPUT_FILE ${RECORDING_DIR}/test_output.mp4)
message(STATUS "Output test file: ${OUTPUT_FILE}")

# Test 1: check if the pipeline runs successfully
add_test(
NAME gst_video_recorder_test
COMMAND $<TARGET_FILE:gst-video-recorder> --source pattern --count 10 -o ${OUTPUT_FILE}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

# Set the test passing if the pipeline runs successfully
set_tests_properties(gst_video_recorder_test PROPERTIES
PASS_REGULAR_EXPRESSION "EOS processed, pipeline finished cleanly"
)

# Test 2: check if the output file was created
add_test(
NAME gst_video_recorder_output_file_test
COMMAND bash -c "test -f ${OUTPUT_FILE}"
)

# Make the file check test depend on the pipeline test
set_tests_properties(gst_video_recorder_output_file_test PROPERTIES
DEPENDS gst_video_recorder_test
)

# Test 3: check if the output file is a valid MPEG video file
add_test(
NAME gst_video_recorder_valid_mpeg_test
COMMAND bash -c "ffprobe -v error ${OUTPUT_FILE}"
)

# Make the validation test depend on the file check test
set_tests_properties(gst_video_recorder_valid_mpeg_test PROPERTIES
DEPENDS gst_video_recorder_output_file_test
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
endif()
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
#include <holoscan/operators/v4l2_video_capture/v4l2_video_capture.hpp>
#include <holoscan/operators/format_converter/format_converter.hpp>
#include <gst_video_recorder_op.hpp>
#include "pattern_generator.hpp"
#include "../common/arg_parser.hpp"
#include "../../common/pattern_generator.hpp"
#include "../../common/arg_parser.hpp"

namespace {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
}
],
"language": "C++",
"version": "1.0.0",
"version": "1.1.0",
"changelog": {
"1.0.0": "Initial Release"
"1.0.0": "Initial Release",
"1.1.0": "Moved C++ implementation to cpp subtree"
Comment thread
asierfernandez marked this conversation as resolved.
Outdated
},
"requirements": {
"system_packages": [
Expand All @@ -29,7 +30,7 @@
"holoscan_sdk": {
"minimum_required_version": "3.8.0",
"tested_versions": [
"3.8.0"
"4.1.0"
]
},
"platforms": [
Expand All @@ -44,21 +45,21 @@
"default": {
"description": "Record Holoscan video streams to file using GStreamer encoding (V4L2 camera or pattern generator).",
"run": {
"command": "applications/gstreamer/gst_video_recorder/gst-video-recorder",
"command": "<holohub_app_bin>/gst-video-recorder",
"workdir": "holohub_bin"
}
},
"v4l2": {
"description": "Record video from a V4L2 camera",
"run": {
"command": "applications/gstreamer/gst_video_recorder/gst-video-recorder --source v4l2",
"command": "<holohub_app_bin>/gst-video-recorder --source v4l2",
"workdir": "holohub_bin"
}
},
"pattern": {
"description": "Record a pattern generator video",
"run": {
"command": "applications/gstreamer/gst_video_recorder/gst-video-recorder --source pattern",
"command": "<holohub_app_bin>/gst-video-recorder --source pattern",
"workdir": "holohub_bin"
}
}
Expand Down
58 changes: 58 additions & 0 deletions applications/gstreamer/gst_video_recorder/python/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: Copyright (c) 2026, TECNALIA. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copy gst_video_recorder application file
add_custom_target(python_gst_video_recorder ALL
COMMAND ${CMAKE_COMMAND} -E copy
"${CMAKE_CURRENT_SOURCE_DIR}/gst_video_recorder.py"
${CMAKE_CURRENT_BINARY_DIR}
DEPENDS "gst_video_recorder.py"
BYPRODUCTS "gst_video_recorder.py"
)

# Add testing
if(BUILD_TESTING)
# To get the environment path
find_package(holoscan 1.0 REQUIRED CONFIG PATHS "/opt/nvidia/holoscan" "/workspace/holoscan-sdk/install")

set(RECORDING_DIR ${CMAKE_CURRENT_BINARY_DIR}/recording_output)
file(MAKE_DIRECTORY ${RECORDING_DIR})

add_test(
NAME gst_video_recorder_python_test
COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/gst_video_recorder.py
--source pattern
--count 10
-o ${RECORDING_DIR}/python_gst_video_recorder_output.mp4
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

set_property(
TEST gst_video_recorder_python_test
PROPERTY ENVIRONMENT
"PYTHONPATH=${GXF_LIB_DIR}/../python/lib:${CMAKE_BINARY_DIR}/python/lib"
)

set_tests_properties(gst_video_recorder_python_test PROPERTIES
PASS_REGULAR_EXPRESSION "EOS processed, pipeline finished cleanly"
FAIL_REGULAR_EXPRESSION "[^a-z]Error;ERROR;Failed"
)
endif()

# Install application into the install/ directory for packaging
install(
FILES gst_video_recorder.py
DESTINATION bin/gst_video_recorder/python
)
Loading
Loading