Skip to content

Instantly share code, notes, and snippets.

@edxmorgan
Created April 9, 2025 22:57
Show Gist options
  • Save edxmorgan/5c754ff65bc92953b0724cfaeca35145 to your computer and use it in GitHub Desktop.
Save edxmorgan/5c754ff65bc92953b0724cfaeca35145 to your computer and use it in GitHub Desktop.
stream udp video
// File: video_stream.cpp
// sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
// g++ video_stream.cpp -o video_stream $(pkg-config --cflags --libs opencv4 gstreamer-1.0 gstreamer-app-1.0)
#include <iostream>
#include <gst/gst.h>
#include <gst/app/gstappsink.h>
#include <opencv2/opencv.hpp>
int main(int argc, char* argv[]) {
// Build the GStreamer pipeline string.
// NOTE: We removed "h264parse" from the pipeline.
std::string pipelineStr =
"udpsrc port=5600 ! application/x-rtp, payload=96 "
"! rtph264depay ! avdec_h264 "
"! videoconvert ! video/x-raw,format=(string)BGR "
"! appsink name=mysink emit-signals=true sync=false max-buffers=2 drop=true";
// Initialize GStreamer.
gst_init(&argc, &argv);
GError *error = nullptr;
// Create the pipeline.
GstElement *pipeline = gst_parse_launch(pipelineStr.c_str(), &error);
if (!pipeline) {
std::cerr << "Failed to create pipeline: " << (error ? error->message : "unknown error") << std::endl;
if (error) g_error_free(error);
return -1;
}
// Set the pipeline to PLAYING state.
GstStateChangeReturn ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
std::cerr << "Unable to set the pipeline to the playing state." << std::endl;
gst_object_unref(pipeline);
return -1;
}
// Retrieve the appsink element from the pipeline.
GstElement *appsinkElem = gst_bin_get_by_name(GST_BIN(pipeline), "mysink");
if (!appsinkElem) {
std::cerr << "Failed to get appsink element from pipeline." << std::endl;
gst_object_unref(pipeline);
return -1;
}
GstAppSink *appsink = GST_APP_SINK(appsinkElem);
std::cout << "Streaming... Press 'q' in the video window to quit." << std::endl;
// Main loop: Pull samples from the appsink and convert to OpenCV Mat.
while (true) {
// Pull a sample (blocking call).
GstSample *sample = gst_app_sink_pull_sample(appsink);
if (!sample) {
std::cerr << "Failed to pull sample from appsink." << std::endl;
break;
}
// Retrieve buffer and capabilities.
GstBuffer *buffer = gst_sample_get_buffer(sample);
GstCaps *caps = gst_sample_get_caps(sample);
if (!caps) {
std::cerr << "Failed to get caps from sample." << std::endl;
gst_sample_unref(sample);
continue;
}
// Get video frame dimensions from the caps structure.
GstStructure *s = gst_caps_get_structure(caps, 0);
int width = 0, height = 0;
if (!gst_structure_get_int(s, "width", &width) ||
!gst_structure_get_int(s, "height", &height)) {
std::cerr << "Failed to retrieve width/height from caps." << std::endl;
gst_sample_unref(sample);
continue;
}
// Map the buffer to access the image data.
GstMapInfo map;
if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
std::cerr << "Failed to map buffer data." << std::endl;
gst_sample_unref(sample);
continue;
}
// Create an OpenCV Mat from the mapped data.
// Note: Cloning is necessary because the buffer memory will be unmapped.
cv::Mat frame(height, width, CV_8UC3, (char*)map.data);
cv::Mat frameCopy = frame.clone();
// Unmap the buffer and release the sample.
gst_buffer_unmap(buffer, &map);
gst_sample_unref(sample);
// Display the frame using OpenCV.
cv::imshow("UDP Video Stream", frameCopy);
if (cv::waitKey(1) == 'q') {
break;
}
}
// Clean up: set pipeline state to NULL and unref objects.
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(appsinkElem);
gst_object_unref(pipeline);
cv::destroyAllWindows();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment