Created
April 9, 2025 22:57
-
-
Save edxmorgan/5c754ff65bc92953b0724cfaeca35145 to your computer and use it in GitHub Desktop.
stream udp video
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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