Demo Application
The RidgeRun Development Suite documentation for RidgeRun is presently being developed. |

The RidgeRun Development Suite (RDS) includes a powerful demo application called RR-Media, designed to serve two key purposes:
1. Showcase common use cases
RR-Media offers a collection of ready-to-run demo applications that illustrate how RidgeRun products can be used to address real-world challenges using RDS.
2. Provide a modular Python API
Beyond demonstrations, RR-Media provides a flexible, modular Python-based API that empowers developers to quickly build and iterate on custom applications. It serves as a practical foundation for creating Minimum Viable Products (MVPs), significantly reducing time to market by allowing teams to validate ideas and integrate RidgeRun technologies with minimal setup.
Running Demo Applications
Pipeline Monitoring
RR-Media offers a collection of demo applications showcasing several of RidgeRun's products. In all demos, you will have the option to enable any GstShark tracer or enable performance (framerate and CPU) monitoring of the running example.
GstShark Tracers
When you run the application for the first time, you will see the following message printed:
Do you want to activate GStreamer tracers? [y/n] (n):
Hit enter or type n to disable tracers.
If you want to enable any tracer, type y and the following menu will be shown:
Do you want to activate GStreamer tracers? [y/n] (n): y Select tracers to enable (comma separated) 1. BITRATE 2. CPUUSAGE 3. FRAMERATE 4. INTERLATENCY 5. LATENCY 6. PROCTIME 7. QUEUELEVEL 8. SCHEDULETIME Select tracers to enable (comma separated):
You can select a comma-separated list of tracers that will be executed with any of the demo applications.
When the demo application finishes, you will see a folder with name gstshark_<CURRENT DATE AND TIME>. You can use gstshark-plt to see the tracers results by running the following command:
gstshark-plot gstshark_<CURRENT DATE AND TIME>
That will open a window with plots for the different enabled tracers.
Performance Monitoring
In each of the demo applications, you will find configuration option that reads Performance monitoring (OFF). This option will allow you to enable performance monitoring which will show you periodically CPU usage and framerate. Select that option to enable/disable performance monitoring and the following menu will appear:
1. ON 2. OFF Enter your choice [1/2] (2):
Select 1 (ON) or 2 (OFF) to enable or disable performance monitoring. Then you can start the demo application. If enabled, periodic messages like this will be shown:
2025-07-08 17:19:28,443 - toolkit-demo - plugin.py 67 - INFO: perf: perf_test; timestamp: 22:29:26.333775664; bps: 27648000.000; mean_bps: 27648000.000; fps: 30.000; mean_fps: 29.999; cpu: 17; 2025-07-08 17:19:29,443 - toolkit-demo - plugin.py 67 - INFO: perf: perf_test; timestamp: 22:29:27.333821848; bps: 27648000.000; mean_bps: 27648000.000; fps: 29.999; mean_fps: 29.999; cpu: 12; 2025-07-08 17:19:30,477 - toolkit-demo - plugin.py 67 - INFO: perf: perf_test; timestamp: 22:29:28.367237777; bps: 28569600.000; mean_bps: 27955200.000; fps: 29.998; mean_fps: 29.999; cpu: 11;
Running RR-Media
Start RR-Media with the following command:
rr-media
Once executed, you will be asked whether or not you want to enable GStreamer tracers, and after that, the following menu will be shown:
Available Plugins 1. Gstd Dynamic Source Switcher 2. Metadata Demo 3. Sample Plugin 4. Dynamic Source Switcher 5. Stitcher Plugin 6. Stitcher 360 + PTZ Plugin 7. Birds Eye View (BEV) 0. Exit
Pick one of the demos and follow the corresponding instructions.
1. Gstd Dynamic Source Switcher: Dynamis sour switching using Gstreamer Daemon.
2. Metadata Demo: Test SEI, OBU and TS Metadata injection.
3. Sample Plugin: Sample demo showing a simple test pattern.
4. Dynamic Source Switcher: Dynamic source switching using RR-Media API.
5. Stitcher Plugin: 6 camera video stitching application.
6. Stitcher 360 + PTZ Plugin: 360 degree panoramic video demonstration.
7. Birds Eye View (BEV): Aerial (Birds Eye View) video demonstration.
RR-Media API
Using RR-Media API
RR-Media is designed as a modular, Python-based API that simplifies the process of building multimedia applications using RidgeRun products. Each component is encapsulated as a reusable "block" that can be easily interconnected—enabling faster, more intuitive prototyping without requiring prior GStreamer knowledge.
Think of your final application as a graph of functional blocks: you simply select the ones you need and connect them to bring your application to life—streamlining development and accelerating time to MVP.
The steps to build your application are as follows (read the API documentation for more details):
1. Import RR-Media
from rrmedia.media.core.factory import ModuleFactory from rrmedia.media.core.graph import ModuleGraph
2. Create a ModuleGraph object
# Create graph graph = ModuleGraph()
3. Use ModuleFactory to create your modules instances
# Source source = ModuleFactory.create( "gst.source.file", location="video/file/location", name="input" ) # Video sink sink = ModuleFactory.create( "jetson.sink.video", name="video_sink", extra_latency=110000000 )
4. Add your modules to the graph
# Add modules graph.add(source) graph.add(sink)
5. Connect your modules
# Connect modules graph.connect("input", "video_sink")
6. (OPTIONAL) Print the pipeline used by the graph
# Print pipeline print("Graph pipeline: %s", graph.dump_launch())
7. Start playback
# Start playback graph.play() # Start loop (this is a blocking function) graph.loop()
Your final application will look like this:

from rrmedia.media.core.factory import ModuleFactory from rrmedia.media.core.graph import ModuleGraph # Create graph graph = ModuleGraph() # Source source = ModuleFactory.create( "gst.source.file", location="video/file/location", name="input" ) # Video sink sink = ModuleFactory.create( "jetson.sink.video", name="video_sink", extra_latency=110000000 ) # Add modules graph.add(source) graph.add(sink) # Connect modules graph.connect("input", "video_sink") # Print pipeline print("Graph pipeline: %s", graph.dump_launch()) # Start playback graph.play() # Start loop (this is a blocking function) graph.loop()
Customizing RR-Media
The base of RR-Media are the Modules. A module is a class that encapsulates a portion of a GStreamer pipeline. The following picture shows the class diagram for the Modules.

In the class diagram, 4 base classes can be highlighted:
- SourceModule: For source modules. These are modules that only source data to other modules.
- SinkModule: For sink modules. These are modules that consume data from other modules.
- FilterModule: For filter modules. These are modules that consume data, perform processing, and then source the processed data to other modules.
- MisoModule: These are modules with multiple inputs and one output (a mux would be a Miso module).
Create your Module
The following is an example of a very simple module:
from rrmedia.media.core.base import SourceModule from rrmedia.media.core.factory import register_module @register_module("gst.source.test") class GstTestSource(SourceModule): def __init__(self, pattern: str, width: int = 320, height: int = 240, name: str = None): actual_name = name or f"test_src_{id(self) & 0xFFFF}" SourceModule.__init__(self, name=actual_name) self._width = width self._height = height self._pattern = pattern @property def launch_description(self) -> str: description = ( f"videotestsrc is-live=true name={self.module_name} pattern={self._pattern} ! " f"video/x-raw,width={self._width},height={self._height}" ) return description
Now let's walk through the code:
1. Module registration
@register_module("gst.source.test")
The first thing you will notice is the register_module decorator. Use it to register your module with the ModuleFactory with a unique name. In this case, gst.source.test.
2. Create your Module Class
class GstTestSource(SourceModule): def __init__(self, pattern: str, width: int = 320, height: int = 240, name: str = None): actual_name = name or f"test_src_{id(self) & 0xFFFF}" SourceModule.__init__(self, name=actual_name) self._width = width self._height = height self._pattern = pattern
Inherit from one of the available base classes depending on your module type and add any necessary argument to your constructor. In our example we are using SourceModule.
3. Implement the launch_description property
@property def launch_description(self) -> str: description = ( f"videotestsrc is-live=true name={self.module_name} pattern={self._pattern} ! " f"video/x-raw,width={self._width},height={self._height}" ) return description
Here you will implement your gst-launch-like pipeline description. In our example, a simple videotestsrc with caps.
After this, put your module into the rrmedia/media/modules directory and you are done. After this, you should be able to instantiate your module from any application by using ModuleFactory:
from rrmedia.media.core.factory import ModuleFactory my_module = ModuleFactory.create("gst.source.test", pattern=0, width=1280, height=720, name="my_source")
Then you can add it to a graph and connect it to other modules.
from rrmedia.media.core.graph import ModuleGraph graph = ModuleGraph() graph.add(my_module) graph.connect("my_module", "other_module")
Configurable Modules
In the previous example, the module properties can be set at creation time but can't be changed later. That could be enough for some use cases, but in other situations, you might want to later change any property of the module. Let's enhance our previous example to allow that:
from rrmedia.media.core.base import Configurable, SourceModule from rrmedia.media.core.factory import register_module @register_module("gst.source.test") class GstTestSource(SourceModule, Configurable): def __init__(self, pattern: str, width: int = 320, height: int = 240, name: str = None): actual_name = name or f"test_src_{id(self) & 0xFFFF}" SourceModule.__init__(self, name=actual_name) Configurable.__init__(self) self._props = { 'pattern': pattern, 'width': width, 'height': height } def set_property_runtime(self, prop: str, value): if prop == 'pattern': return super().set_property_runtime(prop, value) return self @property def launch_description(self) -> str: description = ( f"videotestsrc is-live=true name={self.module_name} pattern={self._props['pattern']} ! " f"video/x-raw,width={self._props['width']},height={self._props['height']}" ) return description
You might notice 3 main differences:
1. We are inheriting from Configurable
class GstTestSource(SourceModule, Configurable): def __init__(self, pattern: str, width: int = 320, height: int = 240, name: str = None): actual_name = name or f"test_src_{id(self) & 0xFFFF}" SourceModule.__init__(self, name=actual_name) Configurable.__init__(self)
This gives us the logic to allow changing properties and now we inherit 2 more functions: set_property and set_property_runtime.
2. We are now using an internal variable _props:
class GstTestSource(SourceModule, Configurable): def __init__(self, pattern: str, width: int = 320, height: int = 240, name: str = None): actual_name = name or f"test_src_{id(self) & 0xFFFF}" SourceModule.__init__(self, name=actual_name) Configurable.__init__(self) self._props = { 'pattern': pattern, 'width': width, 'height': height }
This is also given and used by the Configurable class. Whenever the set_property function is called, this array will be updated.
3. Implementation of set_property_runtime
def set_property_runtime(self, prop: str, value): if prop == 'pattern': return super().set_property_runtime(prop, value) return self
In this particular example, we only want the pattern property to change at runtime (after running graph.play()), that's why we overrode set_property_runtime to allow to change only the pattern property. The remaining properties will be updated in the _props array but the running pipeline won't be updated. If no set_property function is overrode, then all the properties are allowed to change by default.
Miso Modules
Miso modules can be implemented in the same way as the previous examples, just inherit from MisoModuel class. In the same way, they can also be configurable. The only different is the addition of the get_sink_template function. This allows the module to return custom descriptions for each connection that is done. See the following code as an example:
def get_sink_template(self, index: int = None) -> str: pad_description = f"queue ! nvvidconv ! {self. module_name}." if index is not None: pad_description += f"sink_{index}" return pad_description
as can be seen, the function returns f"queue ! nvvidconv ! {self. module_name}.sink_{index}", this makes this piece of code to be added to each of the connections done to the module. The index argument comes form the index argument in ModuleGraph.connect()
graph.connect("some_module", "some_miso_module", 1)
This will call get_sink_template with index=1. This allows control over which module will be attached to which pad in the sink module.
If no get_sink_template is implemented, by default the pad description will be module_name.sink_{index} if index is given or just module_name. if no index is given.