Consume a Live RTSP Stream
| Field | Value |
|---|---|
| Difficulty | Intermediate |
| Estimated Read Time | 5-10 minutes |
| Labels | rtsp, streaming, input-group, live-input |
Concept
Attach a live H.264 RTSP stream to a Session with the RtspDecodedInput node group. The group handles RTSP connect, depacketize, and H.264 decode — you hand it a URL and pull decoded frames.
This is the first chapter where input originates outside the program. Previous chapters manufactured test images or read files from disk. Here, frames arrive continuously from a network stream.
The chapter deliberately stops at "pull the decoded frames." Feeding them into a Model is covered by the earlier chapters (001 for a single model run, 007 for plugging a model into a pipeline, 015 for embedding a model inside a graph). This chapter is about the input group only.
APIs introduced
pyneat.RtspDecodedInputOptions()— configure the RTSP client (URL, transport).pyneat.groups.rtsp_decoded_input(opts)— theNodeGroupthat feeds decoded frames into a session.session.build(pyneat.RunOptions())— thebuildoverload used when the input originates inside the pipeline (no priming sample to pass).
When to use this pattern
- Camera feeds streamed over RTSP from an IP camera, DVR, or another server.
- Any pipeline where the source is a pre-existing network stream.
Not for:
- Serving a stream. This chapter is a consumer only. To publish a stream, run a separate RTSP server (e.g.
mediamtx) and point--urlat it.
Prerequisites
- Chapter 003 — how a
Sessioncomposes nodes. - An accessible RTSP URL.
References
Learning Process
- Configure
RtspDecodedInputOptionswith the stream URL. - Build a
Sessioncomposing the RTSP group + an output node. - Pull decoded frames from the session in a loop and inspect their tensor shape.
Run
This chapter consumes a live RTSP stream. If you do not have a camera, publish an MP4 through mediamtx + ffmpeg and pass the URL via --url.
CTest reads SIMANEAT_APPS_TEST_RTSP_URL for this chapter's RTSP source. If you manage several sources, set SIMANEAT_APPS_TEST_RTSP_URLS and the first URL is used.
Python:
python3 share/sima-neat/tutorials/017_consume_rtsp_stream/consume_rtsp_stream.py \
--url rtsp://host:port/stream --frames 5
C++ (prebuilt):
./lib/sima-neat/tutorials/tutorial_017_consume_rtsp_stream \
--url rtsp://host:port/stream --frames 5
C++ (build from source):
./build.sh --target tutorial_017_consume_rtsp_stream
./build/tutorials-standalone/tutorial_017_consume_rtsp_stream \
--url rtsp://host:port/stream --frames 5
To integrate this chapter's C++ source into your own project with a custom CMakeLists.txt (no extras folder required), see How to Run Tutorials on the landing page.
Code
// Consume a live H.264 RTSP stream via the RtspDecodedInput node group.
//
// The group handles RTSP connect, depacketize, and H.264 decode — you hand it
// a URL and pull decoded frames. This chapter is about the input group only.
//
// Usage:
// tutorial_017_consume_rtsp_stream --url rtsp://host/path [--frames 5]
#include "neat.h"
#include "nodes/groups/RtspDecodedInput.h"
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
namespace {
bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}
int parse_int_arg(int argc, char** argv, const std::string& key, int def) {
std::string v;
if (!get_arg(argc, argv, key, v))
return def;
return std::stoi(v);
}
} // namespace
int main(int argc, char** argv) {
try {
std::string url;
if (!get_arg(argc, argv, "--url", url)) {
std::cerr << "Usage: tutorial_017_consume_rtsp_stream --url <rtsp://...> [--frames <n>]\n";
return 1;
}
const int frames = parse_int_arg(argc, argv, "--frames", 5);
// CORE LOGIC
// Configure RtspDecodedInputOptions, build a Session whose only stages
// are the RTSP group and an Output node, and pull decoded frames.
simaai::neat::nodes::groups::RtspDecodedInputOptions rtsp_opt;
rtsp_opt.url = url;
rtsp_opt.tcp = true;
simaai::neat::Session s;
s.add(simaai::neat::nodes::groups::RtspDecodedInput(rtsp_opt));
s.add(simaai::neat::nodes::Output());
auto run = s.build(simaai::neat::RunOptions{});
for (int i = 0; i < frames; ++i) {
auto sample = run.pull(/*timeout_ms=*/5000);
if (!sample.has_value() || simaai::neat::tensors_from_sample(*sample, true).empty()) {
std::cout << "frame=" << i << " rtsp_timeout\n";
break;
}
const auto tensors = simaai::neat::tensors_from_sample(*sample, true);
const auto& shape = tensors.front().shape;
std::cout << "frame=" << i << " shape=[";
for (std::size_t d = 0; d < shape.size(); ++d) {
std::cout << shape[d] << (d + 1 < shape.size() ? ", " : "");
}
std::cout << "]\n";
}
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}