From 2ae0ef40d4878e5d895ca9ee5c7d0cd2c1fd1619 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Mon, 24 Jan 2022 20:18:33 +0100 Subject: [PATCH] SrtReceiverSource for broadcasted stream Implemented dedicated source, with UI for creation and saving appropriate settings. Also updated info and imgui visitors accordingly --- CMakeLists.txt | 1 + ImGuiToolkit.cpp | 4 +- ImGuiToolkit.h | 2 +- ImGuiVisitor.cpp | 33 +++++++++- ImGuiVisitor.h | 1 + InfoVisitor.cpp | 138 +++++++++++++++++++++++++-------------- InfoVisitor.h | 1 + MediaPlayer.cpp | 16 +++-- Mixer.cpp | 14 +++- Mixer.h | 1 + NetworkToolkit.cpp | 4 +- PatternSource.cpp | 4 +- Settings.cpp | 13 +++- Settings.h | 6 +- Source.h | 1 + SrtReceiverSource.cpp | 56 ++++++++++++++++ SrtReceiverSource.h | 32 +++++++++ Stream.cpp | 18 +++++ Stream.h | 7 ++ UserInterfaceManager.cpp | 40 +++++++++++- UserInterfaceManager.h | 1 + VideoBroadcast.cpp | 1 + VideoBroadcast.h | 4 +- Visitor.h | 2 + 24 files changed, 330 insertions(+), 70 deletions(-) create mode 100644 SrtReceiverSource.cpp create mode 100644 SrtReceiverSource.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 207af1c..62c9860 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -342,6 +342,7 @@ set(VMIX_SRCS Metronome.cpp ControlManager.cpp VideoBroadcast.cpp + SrtReceiverSource.cpp ) diff --git a/ImGuiToolkit.cpp b/ImGuiToolkit.cpp index 2c5424e..50fd644 100644 --- a/ImGuiToolkit.cpp +++ b/ImGuiToolkit.cpp @@ -1708,9 +1708,9 @@ static int InputTextCallback(ImGuiInputTextCallbackData* data) return 0; } -bool ImGuiToolkit::InputText(const char* label, std::string* str) +bool ImGuiToolkit::InputText(const char* label, std::string* str, ImGuiInputTextFlags flag) { - ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CharsNoBlank; + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize | flag; return ImGui::InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, str); } diff --git a/ImGuiToolkit.h b/ImGuiToolkit.h index 7610e84..857c338 100644 --- a/ImGuiToolkit.h +++ b/ImGuiToolkit.h @@ -67,7 +67,7 @@ namespace ImGuiToolkit void Spacing(); // text input - bool InputText(const char* label, std::string* str); + bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flag = ImGuiInputTextFlags_CharsNoBlank); bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0)); void TextMultiline(const char* label, const std::string &str, float width); diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 78a8092..81b3692 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -48,6 +48,7 @@ #include "PatternSource.h" #include "DeviceSource.h" #include "NetworkSource.h" +#include "SrtReceiverSource.h" #include "MultiFileSource.h" #include "SessionCreator.h" #include "SessionVisitor.h" @@ -826,7 +827,7 @@ void ImGuiVisitor::visit (DeviceSource& s) } ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - if (ImGui::BeginCombo("##Hardware", s.device().c_str())) + if (ImGui::BeginCombo("Device", s.device().c_str())) { for (int d = 0; d < Device::manager().numDevices(); ++d){ std::string namedev = Device::manager().name(d); @@ -980,3 +981,33 @@ void ImGuiVisitor::visit (GenericStreamSource& s) } } + + +void ImGuiVisitor::visit (SrtReceiverSource& s) +{ + ImGuiToolkit::Icon(s.icon().x, s.icon().y); + ImGui::SameLine(0, IMGUI_SAME_LINE); + ImGui::Text("SRT Receiver"); + + // network info + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN); + s.accept(info); + ImGui::Text("%s", info.str().c_str()); + ImGui::PopTextWrapPos(); + + // icon (>) to open player + if ( s.playable() ) { + ImVec2 pos = ImGui::GetCursorPos(); + ImGui::SameLine(0, 0); + ImGui::SameLine(0, IMGUI_SAME_LINE + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN); + if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player")) + UserInterface::manager().showSourceEditor(&s); + ImGui::SetCursorPos(pos); + } + +// if ( ImGui::Button( ICON_FA_REPLY " Reconnect", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ) +// { +// s.setConnection(s.connection()); +// info.reset(); +// } +} diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index dedcaaa..e6dc429 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -34,6 +34,7 @@ public: void visit (NetworkSource& s) override; void visit (MultiFileSource& s) override; void visit (GenericStreamSource& s) override; + void visit (SrtReceiverSource& s) override; }; #endif // IMGUIVISITOR_H diff --git a/InfoVisitor.cpp b/InfoVisitor.cpp index b849caa..aee6797 100644 --- a/InfoVisitor.cpp +++ b/InfoVisitor.cpp @@ -44,6 +44,7 @@ #include "PatternSource.h" #include "DeviceSource.h" #include "NetworkSource.h" +#include "SrtReceiverSource.h" #include "MultiFileSource.h" #include "SessionCreator.h" #include "SessionVisitor.h" @@ -92,8 +93,8 @@ void InfoVisitor::visit(MediaPlayer &mp) std::ostringstream oss; if (brief_) { oss << SystemToolkit::filename(mp.filename()) << std::endl; - oss << mp.width() << " x " << mp.height() << ", "; - oss << mp.media().codec_name.substr(0, mp.media().codec_name.find_first_of(" (,")); + oss << mp.media().codec_name.substr(0, mp.media().codec_name.find_first_of(" (,")) << ", "; + oss << mp.width() << " x " << mp.height(); if (!mp.isImage()) oss << ", " << std::fixed << std::setprecision(1) << mp.frameRate() << " fps"; } @@ -136,16 +137,19 @@ void InfoVisitor::visit (SessionFileSource& s) return; std::ostringstream oss; - if (brief_) { - oss << SystemToolkit::filename(s.path()) << " ("; - oss << s.session()->numSource() << " sources)" << std::endl; - } - else - oss << s.path() << std::endl; if (s.session()->frame()){ - oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", "; - oss << "RGB"; + if (brief_) { + oss << SystemToolkit::filename(s.path()) << " ("; + oss << s.session()->numSource() << " sources)" << std::endl; + oss << "RGB, "; + oss << s.session()->frame()->width() << " x " << s.session()->frame()->height(); + } + else { + oss << s.path() << std::endl; + oss << "RGB" << std::endl; + oss << s.session()->frame()->width() << " x " << s.session()->frame()->height(); + } } information_ = oss.str(); @@ -160,8 +164,8 @@ void InfoVisitor::visit (SessionGroupSource& s) std::ostringstream oss; oss << s.session()->numSource() << " sources in group" << std::endl; if (s.session()->frame()){ - oss << s.session()->frame()->width() << " x " << s.session()->frame()->height() << ", "; - oss << "RGB"; + oss << "RGB, "; + oss << s.session()->frame()->width() << " x " << s.session()->frame()->height(); } information_ = oss.str(); @@ -174,15 +178,18 @@ void InfoVisitor::visit (RenderSource& s) return; std::ostringstream oss; - if (!brief_) { - oss << "Rendering Output ("; - oss << RenderSource::rendering_provenance_label[s.renderingProvenance()]; - oss << ") " << std::endl; - } if (s.frame()){ - oss << s.frame()->width() << " x " << s.frame()->height() << ", "; - oss << (s.frame()->use_alpha() ? "RGBA" : "RGB"); + if (brief_) { + oss << (s.frame()->use_alpha() ? "RGBA, " : "RGB, "); + oss << s.frame()->width() << " x " << s.frame()->height(); + } + else { + oss << "Rendering Output ("; + oss << RenderSource::rendering_provenance_label[s.renderingProvenance()] << ") " << std::endl; + oss << (s.frame()->use_alpha() ? "RGBA" : "RGB") << std::endl; + oss << s.frame()->width() << " x " << s.frame()->height(); + } } information_ = oss.str(); @@ -195,15 +202,20 @@ void InfoVisitor::visit (CloneSource& s) return; std::ostringstream oss; - if (!brief_) { - oss << "Clone of '" << s.origin()->name() << "'" << std::endl; - oss << CloneSource::cloning_provenance_label[s.cloningProvenance()]; - oss << ", " << (int)(s.delay()*100.0) << "ms delay " << std::endl; - } if (s.frame()){ - oss << s.frame()->width() << " x " << s.frame()->height() << ", "; - oss << (s.frame()->use_alpha() ? "RGBA" : "RGB"); + if (brief_) { + oss << (s.frame()->use_alpha() ? "RGBA, " : "RGB, "); + oss << s.frame()->width() << " x " << s.frame()->height(); + } + else { + if (s.origin()) + oss << "Clone of '" << s.origin()->name() << "' "; + oss << CloneSource::cloning_provenance_label[s.cloningProvenance()] << std::endl; + oss << (s.frame()->use_alpha() ? "RGBA, " : "RGB, "); + oss << (int)(s.delay()*100.0) << " ms delay " << std::endl; + oss << s.frame()->width() << " x " << s.frame()->height(); + } } information_ = oss.str(); @@ -216,12 +228,15 @@ void InfoVisitor::visit (PatternSource& s) return; std::ostringstream oss; - if (!brief_) - oss << Pattern::get(s.pattern()->type()).label << " pattern" << std::endl; - if (s.pattern()) { - oss << s.pattern()->width() << " x " << s.pattern()->height(); - oss << ", RGBA"; + if (brief_) { + oss << "RGBA, " << s.pattern()->width() << " x " << s.pattern()->height(); + } + else { + oss << Pattern::get(s.pattern()->type()).label << " pattern" << std::endl; + oss << "RGBA" << std::endl; + oss << s.pattern()->width() << " x " << s.pattern()->height(); + } } information_ = oss.str(); @@ -241,15 +256,15 @@ void InfoVisitor::visit (DeviceSource& s) float fps = static_cast(best.fps_numerator) / static_cast(best.fps_denominator); if (brief_) { + oss << best.stream << " " << best.format << ", "; oss << best.width << " x " << best.height << ", "; - oss << best.stream << " " << best.format << ", "; oss << std::fixed << std::setprecision(1) << fps << " fps"; } else { oss << s.device() << std::endl; - oss << Device::manager().description( Device::manager().index(s.device())) << std::endl; + oss << Device::manager().description( Device::manager().index(s.device())); + oss << ", " << best.stream << " " << best.format << std::endl; oss << best.width << " x " << best.height << ", "; - oss << best.stream << " " << best.format << ", "; oss << std::fixed << std::setprecision(1) << fps << " fps"; } } @@ -267,15 +282,15 @@ void InfoVisitor::visit (NetworkSource& s) std::ostringstream oss; if (brief_) { - oss << ns->resolution().x << " x " << ns->resolution().y << ", "; oss << NetworkToolkit::stream_protocol_label[ns->protocol()] << std::endl; - oss << "IP " << ns->serverAddress(); + oss << "IP " << ns->serverAddress() << std::endl; + oss << ns->resolution().x << " x " << ns->resolution().y; } else { oss << s.connection() << std::endl; oss << NetworkToolkit::stream_protocol_label[ns->protocol()]; oss << " shared from IP " << ns->serverAddress() << std::endl; - oss << ns->resolution().x << " x " << ns->resolution().y << " "; + oss << ns->resolution().x << " x " << ns->resolution().y; } information_ = oss.str(); @@ -290,17 +305,17 @@ void InfoVisitor::visit (MultiFileSource& s) std::ostringstream oss; if (brief_) { - oss << s.sequence().width << " x " << s.sequence().height << ", "; - oss << s.sequence().codec << std::endl; oss << s.sequence().max - s.sequence().min + 1 << " images ["; - oss << s.sequence().min << " - " << s.sequence().max << "]"; + oss << s.sequence().min << " - " << s.sequence().max << "]" << std::endl; + oss << s.sequence().codec << ", "; + oss << s.sequence().width << " x " << s.sequence().height; } else { oss << s.sequence().location << " ["; oss << s.sequence().min << " - " << s.sequence().max << "]" << std::endl; - oss << s.sequence().width << " x " << s.sequence().height << ", "; - oss << s.sequence().codec << " ("; - oss << s.sequence().max - s.sequence().min + 1 << " images)"; + oss << s.sequence().max - s.sequence().min + 1 << " "; + oss << s.sequence().codec << " images" << std::endl; + oss << s.sequence().width << " x " << s.sequence().height << ", " << s.framerate() << " fps"; } information_ = oss.str(); @@ -316,12 +331,39 @@ void InfoVisitor::visit (GenericStreamSource& s) if (s.stream()) { std::string src_element = s.gstElements().front(); - if (brief_) + if (brief_) { src_element = src_element.substr(0, src_element.find(" ")); - - oss << "gstreamer '" << src_element << "'" << std::endl; - oss << s.stream()->width() << " x " << s.stream()->height(); - oss << ", RGBA"; + oss << "gstreamer '" << src_element << "'" << std::endl; + oss << "RGBA, " << s.stream()->width() << " x " << s.stream()->height(); + } + else { + oss << "gstreamer '" << src_element << "'" << std::endl; + oss << "RGBA" << std::endl; + oss << s.stream()->width() << " x " << s.stream()->height(); + } + } + else + oss << "Undefined"; + + information_ = oss.str(); + current_id_ = s.id(); +} + +void InfoVisitor::visit (SrtReceiverSource& s) +{ + if (current_id_ == s.id()) + return; + + std::ostringstream oss; + + if (s.stream()) { + if (brief_) + oss << s.uri() << std::endl; + else { + oss << "SRT Receiver " << s.uri() << std::endl; + oss << "H264 (" << s.stream()->decoderName() << ")" << std::endl; + oss << s.stream()->width() << " x " << s.stream()->height(); + } } else oss << "Undefined"; diff --git a/InfoVisitor.h b/InfoVisitor.h index 84981c2..5f7feb2 100644 --- a/InfoVisitor.h +++ b/InfoVisitor.h @@ -36,6 +36,7 @@ public: void visit (NetworkSource& s) override; void visit (MultiFileSource& s) override; void visit (GenericStreamSource& s) override; + void visit (SrtReceiverSource& s) override; }; #endif // INFOVISITOR_H diff --git a/MediaPlayer.cpp b/MediaPlayer.cpp index 1ea2c65..025d9bc 100644 --- a/MediaPlayer.cpp +++ b/MediaPlayer.cpp @@ -560,13 +560,15 @@ bool MediaPlayer::isImage() const std::string MediaPlayer::decoderName() { - // decoder_name_ not initialized - if (decoder_name_.empty()) { - // try to know if it is a hardware decoder - decoder_name_ = GstToolkit::used_gpu_decoding_plugins(pipeline_); - // nope, then it is a sofware decoder - if (decoder_name_.empty()) - decoder_name_ = "software"; + if (pipeline_) { + // decoder_name_ not initialized + if (decoder_name_.empty()) { + // try to know if it is a hardware decoder + decoder_name_ = GstToolkit::used_gpu_decoding_plugins(pipeline_); + // nope, then it is a sofware decoder + if (decoder_name_.empty()) + decoder_name_ = "software"; + } } return decoder_name_; diff --git a/Mixer.cpp b/Mixer.cpp index 6226099..1065be9 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -50,6 +50,8 @@ #include "MultiFileSource.h" #include "StreamSource.h" #include "NetworkSource.h" +#include "SrtReceiverSource.h" + #include "ActionManager.h" #include "MixingGroup.h" #include "Streamer.h" @@ -355,7 +357,6 @@ Source * Mixer::createSourceDevice(const std::string &namedevice) return s; } - Source * Mixer::createSourceNetwork(const std::string &nameconnection) { // ready to create a source @@ -368,6 +369,17 @@ Source * Mixer::createSourceNetwork(const std::string &nameconnection) return s; } +Source * Mixer::createSourceSrt(const std::string &ip, const std::string &port) +{ + // ready to create a source + SrtReceiverSource *s = new SrtReceiverSource; + s->setConnection(ip, port); + + // propose a new name based on address + s->setName(s->uri()); + + return s; +} Source * Mixer::createSourceGroup() { diff --git a/Mixer.h b/Mixer.h index 835bca3..0867c3d 100644 --- a/Mixer.h +++ b/Mixer.h @@ -55,6 +55,7 @@ public: Source * createSourcePattern(uint pattern, glm::ivec2 res); Source * createSourceDevice (const std::string &namedevice); Source * createSourceNetwork(const std::string &nameconnection); + Source * createSourceSrt (const std::string &ip, const std::string &port); Source * createSourceGroup (); // operations on sources diff --git a/NetworkToolkit.cpp b/NetworkToolkit.cpp index 11228be..94479b7 100644 --- a/NetworkToolkit.cpp +++ b/NetworkToolkit.cpp @@ -103,8 +103,8 @@ const std::vector NetworkToolkit::stream_send_pipeline { const std::vector NetworkToolkit::stream_receive_pipeline { "udpsrc port=XXXX caps=\"application/x-rtp,media=(string)video,encoding-name=(string)RAW,sampling=(string)RGB,width=(string)WWWW,height=(string)HHHH\" ! rtpvrawdepay ! queue max-size-buffers=10", - "udpsrc port=XXXX caps=\"application/x-rtp,media=(string)video,encoding-name=(string)JPEG\" ! rtpjpegdepay ! queue max-size-buffers=10 ! jpegdec", - "udpsrc port=XXXX caps=\"application/x-rtp,media=(string)video,encoding-name=(string)H264\" ! rtph264depay ! queue max-size-buffers=10 ! avdec_h264", + "udpsrc port=XXXX caps=\"application/x-rtp,media=(string)video,encoding-name=(string)JPEG\" ! rtpjpegdepay ! decodebin", + "udpsrc port=XXXX caps=\"application/x-rtp,media=(string)video,encoding-name=(string)H264\" ! rtph264depay ! decodebin", "shmsrc socket-path=XXXX ! video/x-raw, format=RGB, framerate=30/1 ! queue max-size-buffers=10", }; diff --git a/PatternSource.cpp b/PatternSource.cpp index 11a044a..f424f32 100644 --- a/PatternSource.cpp +++ b/PatternSource.cpp @@ -50,7 +50,7 @@ std::vector Pattern::patterns_ = { { "Blue", "videotestsrc", "videotestsrc pattern=blue", false, false }, { "Color bars", "videotestsrc", "videotestsrc pattern=smpte100", false, false }, { "RGB grid", "videotestsrc", "videotestsrc pattern=colors", false, false }, - { "SMPTE test pattern", "videotestsrc", "videotestsrc pattern=smpte", true, false }, + { "SMPTE test", "videotestsrc", "videotestsrc pattern=smpte", true, false }, { "Television snow", "videotestsrc", "videotestsrc pattern=snow", true, false }, { "Blink", "videotestsrc", "videotestsrc pattern=blink", true, false }, { "Fresnel zone plate", "videotestsrc", "videotestsrc pattern=zone-plate kx2=XXX ky2=YYY kt=4", true, false }, @@ -67,7 +67,7 @@ std::vector Pattern::patterns_ = { { "Point Grid", "frei0r-src-test-pat-g", "frei0r-src-test-pat-g type=0.4", false, false }, { "Ruler", "frei0r-src-test-pat-g", "frei0r-src-test-pat-g type=0.9", false, false }, { "RGB noise", "frei0r-filter-rgbnoise", "videotestsrc pattern=black ! frei0r-filter-rgbnoise noise=0.6", true, false }, - { "Philips test pattern", "frei0r-src-test-pat-b", "frei0r-src-test-pat-b type=0.7 ", false, false } + { "Philips test", "frei0r-src-test-pat-b", "frei0r-src-test-pat-b type=0.7 ", false, false } }; diff --git a/Settings.cpp b/Settings.cpp index 1f0638a..c693379 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -112,6 +112,8 @@ void Settings::Save(uint64_t runtime) applicationNode->SetAttribute("pannel_history_mode", application.pannel_current_session_mode); applicationNode->SetAttribute("stream_protocol", application.stream_protocol); applicationNode->SetAttribute("broadcast_port", application.broadcast_port); + applicationNode->SetAttribute("custom_connect_ip", application.custom_connect_ip.c_str()); + applicationNode->SetAttribute("custom_connect_port", application.custom_connect_port.c_str()); pRoot->InsertEndChild(applicationNode); // Widgets @@ -266,7 +268,6 @@ void Settings::Save(uint64_t runtime) controlConfNode->SetAttribute("osc_port_receive", application.control.osc_port_receive); controlConfNode->SetAttribute("osc_port_send", application.control.osc_port_send); - // First save : create filename if (settingsFilename.empty()) settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS); @@ -357,6 +358,16 @@ void Settings::Load() applicationNode->QueryIntAttribute("pannel_history_mode", &application.pannel_current_session_mode); applicationNode->QueryIntAttribute("stream_protocol", &application.stream_protocol); applicationNode->QueryIntAttribute("broadcast_port", &application.broadcast_port); + const char *ip = applicationNode->Attribute("custom_connect_ip"); + if (ip) + application.custom_connect_ip = std::string(ip); + else + application.custom_connect_ip = "127.0.0.1"; + const char *p = applicationNode->Attribute("custom_connect_port"); + if (p) + application.custom_connect_port = std::string(p); + else + application.custom_connect_port = "8888"; } // Widgets diff --git a/Settings.h b/Settings.h index da06ecf..729acfa 100644 --- a/Settings.h +++ b/Settings.h @@ -225,6 +225,8 @@ struct Application bool accept_connections; int stream_protocol; int broadcast_port; + std::string custom_connect_ip; + std::string custom_connect_port; // Settings of widgets WidgetsConfig widget; @@ -276,7 +278,9 @@ struct Application show_tooptips = true; accept_connections = false; stream_protocol = 0; - broadcast_port = 8888; + broadcast_port = 7070; + custom_connect_ip = "127.0.0.1"; + custom_connect_port = "8888"; pannel_current_session_mode = 0; current_view = 1; current_workspace= 1; diff --git a/Source.h b/Source.h index 863c645..839ac81 100644 --- a/Source.h +++ b/Source.h @@ -24,6 +24,7 @@ #define ICON_SOURCE_RENDER 0, 2 #define ICON_SOURCE_CLONE 9, 2 #define ICON_SOURCE_GSTREAMER 16, 16 +#define ICON_SOURCE_SRT 14, 5 #define ICON_SOURCE 13, 11 class SourceCallback; diff --git a/SrtReceiverSource.cpp b/SrtReceiverSource.cpp new file mode 100644 index 0000000..fadbb9f --- /dev/null +++ b/SrtReceiverSource.cpp @@ -0,0 +1,56 @@ + +#include "Log.h" +#include "Decorations.h" +#include "Visitor.h" + +#include "SrtReceiverSource.h" + +SrtReceiverSource::SrtReceiverSource(uint64_t id) : StreamSource(id) +{ + // create stream + stream_ = new Stream; + + // set symbol + symbol_ = new Symbol(Symbol::SHARE, glm::vec3(0.75f, 0.75f, 0.01f)); + symbol_->scale_.y = 1.5f; +} + + +void SrtReceiverSource::setConnection(const std::string &ip, const std::string &port) +{ + // TODO add check on wellformed IP and PORT + ip_ = ip; + port_ = port; + Log::Notify("Creating Source SRT receiving from '%s'", uri().c_str()); + + std::string description = "srtsrc uri=" + uri() + " ! tsdemux ! decodebin ! videoconvert"; + + // open gstreamer + stream_->open(description); + stream_->play(true); + + // will be ready after init and one frame rendered + ready_ = false; +} + +std::string SrtReceiverSource::uri() const +{ + return std::string("srt://") + ip_ + ":" + port_; +} + +void SrtReceiverSource::accept(Visitor& v) +{ + Source::accept(v); + if (!failed()) + v.visit(*this); +} + +glm::ivec2 SrtReceiverSource::icon() const +{ + return glm::ivec2(ICON_SOURCE_SRT); +} + +std::string SrtReceiverSource::info() const +{ + return std::string("SRT receiver from '") + uri() + "'"; +} diff --git a/SrtReceiverSource.h b/SrtReceiverSource.h new file mode 100644 index 0000000..db11b1f --- /dev/null +++ b/SrtReceiverSource.h @@ -0,0 +1,32 @@ +#ifndef SRTRECEIVERSOURCE_H +#define SRTRECEIVERSOURCE_H + +#include "StreamSource.h" + +class SrtReceiverSource : public StreamSource +{ + std::string ip_; + std::string port_; + +public: + SrtReceiverSource(uint64_t id = 0); + + // Source interface + void accept (Visitor& v) override; + + // StreamSource interface + Stream *stream() const override { return stream_; } + + // specific interface + void setConnection(const std::string &ip, const std::string &port); + std::string ip() const { return ip_; } + std::string port() const { return port_; } + std::string uri() const; + + glm::ivec2 icon() const override; + std::string info() const override; + +}; + + +#endif // SRTRECEIVERSOURCE_H diff --git a/Stream.cpp b/Stream.cpp index e93c732..3514a87 100644 --- a/Stream.cpp +++ b/Stream.cpp @@ -29,6 +29,7 @@ #include "Visitor.h" #include "SystemToolkit.h" #include "BaseToolkit.h" +#include "GstToolkit.h" #include "Stream.h" @@ -54,6 +55,7 @@ Stream::Stream() single_frame_ = false; live_ = false; failed_ = false; + decoder_name_ = ""; // start index in frame_ stack write_index_ = 0; @@ -87,6 +89,22 @@ void Stream::accept(Visitor& v) { v.visit(*this); } +std::string Stream::decoderName() +{ + if (pipeline_) { + // decoder_name_ not initialized + if (decoder_name_.empty()) { + // try to know if it is a hardware decoder + decoder_name_ = GstToolkit::used_gpu_decoding_plugins(pipeline_); + // nope, then it is a sofware decoder + if (decoder_name_.empty()) + decoder_name_ = "software"; + } + } + + return decoder_name_; +} + guint Stream::texture() const { if (textureindex_ == 0) diff --git a/Stream.h b/Stream.h index c09af83..8420090 100644 --- a/Stream.h +++ b/Stream.h @@ -137,6 +137,12 @@ public: * Must be called in OpenGL context * */ guint texture() const; + /** + * Get the name of the decoder used, + * return 'software' if no hardware decoder is used + * NB: perform request on pipeline on first call + * */ + std::string decoderName(); /** * Accept visitors * Used for saving session file @@ -165,6 +171,7 @@ protected: std::atomic opened_; std::atomic failed_; bool enabled_; + std::string decoder_name_; // fps counter struct TimeCounter { diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 7a8d788..f56bbcf 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -83,6 +83,7 @@ using namespace std; #include "PatternSource.h" #include "DeviceSource.h" #include "NetworkSource.h" +#include "SrtReceiverSource.h" #include "StreamSource.h" #include "PickingVisitor.h" #include "ImageShader.h" @@ -5094,16 +5095,23 @@ void Navigator::RenderNewPannel() for (int d = 0; d < Device::manager().numDevices(); ++d){ std::string namedev = Device::manager().name(d); if (ImGui::Selectable( namedev.c_str() )) { - + custom_connected = false; new_source_preview_.setSource( Mixer::manager().createSourceDevice(namedev), namedev); } } for (int d = 1; d < Connection::manager().numHosts(); ++d){ std::string namehost = Connection::manager().info(d).name; if (ImGui::Selectable( namehost.c_str() )) { + custom_connected = false; new_source_preview_.setSource( Mixer::manager().createSourceNetwork(namehost), namehost); } } + + if ( ImGui::Selectable("Custom SRT") ) { + new_source_preview_.setSource(); + custom_connected = true; + } + ImGui::EndCombo(); } @@ -5112,7 +5120,33 @@ void Navigator::RenderNewPannel() ImGuiToolkit::HelpToolTip("Create a source getting images from connected devices or machines;\n" ICON_FA_CARET_RIGHT " webcams or frame grabbers\n" ICON_FA_CARET_RIGHT " screen capture\n" - ICON_FA_CARET_RIGHT " stream from connected vimix"); + ICON_FA_CARET_RIGHT " stream shared by vimix"); + + if (custom_connected) { + + ImGui::Text("\nCustom network SRT stream:"); + + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + ImGuiToolkit::InputText("IP", &Settings::application.custom_connect_ip, ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsDecimal); + + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + ImGuiToolkit::InputText("Port", &Settings::application.custom_connect_port, ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsDecimal); + + static char bufurl[32]; + ImFormatString(bufurl, IM_ARRAYSIZE(bufurl), "srt://%s:%s", + Settings::application.custom_connect_ip.c_str(), + Settings::application.custom_connect_port.c_str() ); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.8f)); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + ImGui::InputText("##url", bufurl, IM_ARRAYSIZE(bufurl), ImGuiInputTextFlags_ReadOnly); + ImGui::PopStyleColor(1); + + ImGui::SameLine(0); ImGuiToolkit::Indication("URL for connecting to a stream on Secure Reliable Transport (SRT) protocol", ICON_FA_GLOBE); + + if ( ImGui::Button("Try to connect", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ) { + new_source_preview_.setSource( Mixer::manager().createSourceSrt(Settings::application.custom_connect_ip, Settings::application.custom_connect_port), bufurl); + } + } } @@ -5892,7 +5926,7 @@ void Navigator::RenderMainPannelSettings() ImGui::Text("Stream"); char msg[256]; - sprintf(msg, "Port for broadcasting on Secure Reliable Transport (SRT) protocol\n" + ImFormatString(msg, IM_ARRAYSIZE(msg),"Port for broadcasting on Secure Reliable Transport (SRT) protocol\n" "You can e.g. connect to:\n srt://%s:%d", NetworkToolkit::host_ips()[1].c_str(), Settings::application.broadcast_port); ImGuiToolkit::Indication(msg, ICON_FA_GLOBE); diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index a684841..33a2804 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -140,6 +140,7 @@ class Navigator bool selected_button[NAV_COUNT]; int pattern_type; bool custom_pipeline; + bool custom_connected; void clearButtonSelection(); void applyButtonSelection(int index); diff --git a/VideoBroadcast.cpp b/VideoBroadcast.cpp index 3313150..9fc93ec 100644 --- a/VideoBroadcast.cpp +++ b/VideoBroadcast.cpp @@ -194,3 +194,4 @@ std::string VideoBroadcast::info() const return ret.str(); } + diff --git a/VideoBroadcast.h b/VideoBroadcast.h index 8cc6980..4bd92e2 100644 --- a/VideoBroadcast.h +++ b/VideoBroadcast.h @@ -3,6 +3,7 @@ #include "NetworkToolkit.h" #include "FrameGrabber.h" +#include "StreamSource.h" #define BROADCAST_FPS 30 @@ -10,7 +11,7 @@ class VideoBroadcast : public FrameGrabber { public: - VideoBroadcast(int port = 8888); + VideoBroadcast(int port = 7070); virtual ~VideoBroadcast() {} static bool available(); @@ -31,4 +32,5 @@ private: static std::string h264_encoder_; }; + #endif // VIDEOBROADCAST_H diff --git a/Visitor.h b/Visitor.h index e54a6fc..03a82a1 100644 --- a/Visitor.h +++ b/Visitor.h @@ -31,6 +31,7 @@ class MediaSource; class PatternSource; class DeviceSource; class GenericStreamSource; +class SrtReceiverSource; class SessionFileSource; class SessionGroupSource; class RenderSource; @@ -73,6 +74,7 @@ public: virtual void visit (Source&) {} virtual void visit (MediaSource&) {} virtual void visit (NetworkSource&) {} + virtual void visit (SrtReceiverSource&) {} virtual void visit (GenericStreamSource&) {} virtual void visit (DeviceSource&) {} virtual void visit (PatternSource&) {}