From fef0f3a980f28d5bc602880cddd76381c6119328 Mon Sep 17 00:00:00 2001 From: Barzan Hayati Date: Tue, 1 Jul 2025 10:15:44 +0000 Subject: [PATCH] Create sink --- CMakeLists.txt | 4 +- data/{example.txt => addresses.txt} | 0 data/configuration.json | 6 +- src/main.cpp | 9 +-- src/pipeline_manager.cpp | 1 + src/pipeline_manager.hpp | 2 + src/sink_manager.cpp | 110 ++++++++++++++++++++++++++++ src/sink_manager.hpp | 21 ++++++ 8 files changed, 142 insertions(+), 11 deletions(-) rename data/{example.txt => addresses.txt} (100%) create mode 100644 src/sink_manager.cpp create mode 100644 src/sink_manager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e1f2b8a..a8a12da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,13 +56,13 @@ include_directories(${PROJECT_SOURCE_DIR}/nv_video_convert_manager.hpp) include_directories(${PROJECT_SOURCE_DIR}/nv_osd_manager.hpp) include_directories(${PROJECT_SOURCE_DIR}/queue_manager.hpp) include_directories(${PROJECT_SOURCE_DIR}/nv_ds_logger_manager.hpp) - +include_directories(${PROJECT_SOURCE_DIR}/sink_manager.hpp) set(SOURCES src/main.cpp src/camera_manager.cpp src/pipeline_manager.cpp src/streammux_manager.cpp src/source_bin.cpp src/gstds_example_manager.cpp src/tiler_manager.cpp src/nv_video_convert_manager.cpp src/nv_osd_manager.cpp src/queue_manager.cpp - src/nv_ds_logger_manager.cpp) + src/nv_ds_logger_manager.cpp src/sink_manager.cpp) # Create the executable add_executable(${PROJECT_NAME} ${SOURCES}) diff --git a/data/example.txt b/data/addresses.txt similarity index 100% rename from data/example.txt rename to data/addresses.txt diff --git a/data/configuration.json b/data/configuration.json index 9a15f28..0c1383a 100644 --- a/data/configuration.json +++ b/data/configuration.json @@ -1,4 +1,6 @@ { "MUXER_OUTPUT_HEIGHT": 1080, - "MUXER_OUTPUT_WIDTH": 1920 -} \ No newline at end of file + "MUXER_OUTPUT_WIDTH": 1920, + "output_video_path": "test.mkv", + "display_output": 1 +} diff --git a/src/main.cpp b/src/main.cpp index d0449bc..f2c771c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,12 +36,11 @@ int load_rtsp_address(CameraManager *camera_manager, fs::path file_path) { } std::cout << "Contents of '" << file_path << "':\n"; - std::cout << "-----------------\n"; std::string line; int line_number = 1; while (std::getline(file, line)) { - std::cout << "Line " << line_number++ << ": " << line << '\n'; + std::cout << line << '\n'; camera_manager->add_rtsp_camera(line, line_number); } } catch (const std::exception &e) { @@ -60,7 +59,7 @@ int main(int argc, char *argv[]) { CameraManager *camera_manager = new CameraManager(); // Path handling works across platforms fs::path data_dir = "../data"; - fs::path file_path = data_dir / "example.txt"; + fs::path file_path = data_dir / "addresses.txt"; load_rtsp_address(camera_manager, file_path); char **url_camera = new char *[camera_manager->camera_list.size() + 1]; @@ -72,10 +71,6 @@ int main(int argc, char *argv[]) { for (guint i = 0; i < num_sources; i++) { url_camera[i + 1] = &camera_manager->camera_list.at(i).address[0]; } - std::cout << "print content of camera urls" << std::endl; - for (int i = 0; i < (int)num_sources + 1; i++) { - std::cout << url_camera[i] << std::endl; - } PipelineManager *pipeline_manager = new PipelineManager(num_sources, url_camera); diff --git a/src/pipeline_manager.cpp b/src/pipeline_manager.cpp index 26daece..61b524e 100644 --- a/src/pipeline_manager.cpp +++ b/src/pipeline_manager.cpp @@ -89,6 +89,7 @@ bool PipelineManager::create_pipeline_elements(int num_sources, } nv_ds_logger_manager->create_nv_ds_logger(); + sink_manager->create_sink(prop); return true; } \ No newline at end of file diff --git a/src/pipeline_manager.hpp b/src/pipeline_manager.hpp index 6be5da3..6159db1 100644 --- a/src/pipeline_manager.hpp +++ b/src/pipeline_manager.hpp @@ -7,6 +7,7 @@ #include "nv_osd_manager.hpp" #include "nv_video_convert_manager.hpp" #include "queue_manager.hpp" +#include "sink_manager.hpp" #include "source_bin.hpp" #include "streammux_manager.hpp" #include "tiler_manager.hpp" @@ -24,6 +25,7 @@ class PipelineManager { new NvVideoConvertManager(); NvOsdManager *nv_osd_manager = new NvOsdManager(); NvDsLoggerManager *nv_ds_logger_manager = new NvDsLoggerManager(); + SinkManager *sink_manager = new SinkManager(); public: int current_device = -1; diff --git a/src/sink_manager.cpp b/src/sink_manager.cpp new file mode 100644 index 0000000..1a081e7 --- /dev/null +++ b/src/sink_manager.cpp @@ -0,0 +1,110 @@ +#include "sink_manager.hpp" + +#define SET_GPU_ID(object, gpu_id) \ + g_object_set(G_OBJECT(object), "gpu-id", gpu_id, NULL); +#define GPU_ID 0 + +using json = nlohmann::json; + +SinkManager::SinkManager() { + json j; + std::ifstream i("../data/configuration.json"); + i >> j; + j.at("output_video_path").get_to(output_video_path); + j.at("display_output").get_to(display_output); +} + +bool SinkManager::create_sink(cudaDeviceProp prop) { + if (display_output == 0) { + output_sink = "fake_sink"; + sink = gst_element_factory_make("fakesink", "nvvideo-renderer"); + g_object_set(G_OBJECT(sink), "name", "fakesink", "qos", 0, "sync", + FALSE, NULL); + } else if (display_output == 1) { + output_sink = "displaying_window"; + sink = gst_element_factory_make("nveglglessink", "nvvideo-renderer"); + g_object_set(G_OBJECT(sink), "sync", FALSE, NULL); + g_object_set(G_OBJECT(sink), "qos", 0, NULL); + } else if (display_output == 2) { + output_sink = "to_vido_file"; + sink = gst_element_factory_make("nvvideoencfilesinkbin", + "nvvideo-renderer"); + g_object_set(G_OBJECT(sink), "container", 2, "output-file", + output_video_path.c_str(), NULL); + g_object_set(G_OBJECT(sink), "name", "nvvideoencfilesinkbin", "qos", 0, + "sync", FALSE, NULL); + } else if (display_output == 3) { + output_sink = "to_rtsp"; + nvvidconv_postosd = + gst_element_factory_make("nvvideoconvert", "convertor_postosd"); + if (!nvvidconv_postosd) { + g_printerr("Unable to create nvvidconv_postosd.\n"); + return false; + } + + // Create a caps filter + caps = gst_element_factory_make("capsfilter", "filter"); + g_object_set( + caps, "caps", + gst_caps_from_string("video/x-raw(memory:NVMM), format=I420"), + NULL); + if (!caps) { + g_printerr("Unable to create caps. Exiting.\n"); + return false; + } + + // Make the encoder + if (!strcmp(codec.c_str(), "H264")) { + encoder = gst_element_factory_make("nvv4l2h264enc", "encoder"); + g_printerr("Creating H264 Encoder.\n"); + } else if (!strcmp(codec.c_str(), "H265")) { + encoder = gst_element_factory_make("nvv4l2h265enc", "encoder"); + g_printerr("Creating H265 Encoder.\n"); + } else { + g_printerr( + "RTSP Streaming Codec should be H264/H265 , " + "default=H264. Exiting.\n"); + return false; + } + g_object_set(encoder, "bitrate", bitrate, NULL); + if (!encoder) { + g_printerr("Unable to create encoder. Exiting.\n"); + return false; + } + + // Make the payload-encode video into RTP packets + if (!strcmp(codec.c_str(), "H264")) { + rtppay = gst_element_factory_make("rtph264pay", "rtppay"); + g_printerr("Creating H264 rtppay.\n"); + } else if (!strcmp(codec.c_str(), "H265")) { + rtppay = gst_element_factory_make("rtph265pay", "rtppay"); + g_printerr("Creating H265 rtppay.\n"); + } + if (!rtppay) { + g_printerr("Unable to create rtppay. Exiting.\n"); + return false; + } + + // Make the UDP sink + sink = gst_element_factory_make("udpsink", "udpsink"); + g_object_set(G_OBJECT(sink), "host", host.c_str(), NULL); + g_object_set(G_OBJECT(sink), "port", updsink_port_num, NULL); + g_object_set(G_OBJECT(sink), "async", FALSE, NULL); + g_object_set(G_OBJECT(sink), "sync", 1, NULL); + + if (!sink) { + g_printerr("Unable to create udpsink. Exiting.\n"); + return false; + } + } + + if (!prop.integrated && display_output < 3) { + SET_GPU_ID(sink, GPU_ID); + } + + if (!sink) { + g_printerr("Could not create sink. Exiting.\n"); + return false; + } + return true; +} \ No newline at end of file diff --git a/src/sink_manager.hpp b/src/sink_manager.hpp new file mode 100644 index 0000000..3c5d581 --- /dev/null +++ b/src/sink_manager.hpp @@ -0,0 +1,21 @@ +#include +#include + +#include +#include + +#include "cuda_runtime_api.h" +#include "json.hpp" + +class SinkManager { + private: + public: + GstElement *sink = NULL, *nvvidconv_postosd = NULL, *caps = NULL, + *encoder = NULL, *rtppay = NULL; + std::string codec, host, output_sink, output_video_path; + int display_output = 1, bitrate; + guint updsink_port_num; + SinkManager(); + bool create_sink(cudaDeviceProp prop); + ~SinkManager(); +}; \ No newline at end of file