diff --git a/src/FrameGrabber.cpp b/src/FrameGrabber.cpp index 8bad4a4..d3915c2 100644 --- a/src/FrameGrabber.cpp +++ b/src/FrameGrabber.cpp @@ -270,7 +270,7 @@ void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer) FrameGrabber::FrameGrabber(): finished_(false), initialized_(false), active_(false), endofstream_(false), accept_buffer_(false), buffering_full_(false), pipeline_(nullptr), src_(nullptr), caps_(nullptr), timer_(nullptr), timer_firstframe_(0), - timestamp_(0), duration_(0), frame_count_(0), buffering_size_(MIN_BUFFER_SIZE), timestamp_on_clock_(false) + timestamp_(0), duration_(0), frame_count_(0), buffering_size_(MIN_BUFFER_SIZE), timestamp_on_clock_(true) { // unique id id_ = BaseToolkit::uniqueId(); diff --git a/src/RenderingManager.cpp b/src/RenderingManager.cpp index b376109..d3900bc 100644 --- a/src/RenderingManager.cpp +++ b/src/RenderingManager.cpp @@ -231,10 +231,15 @@ bool Rendering::init() g_setenv ("GST_PLUGIN_SCANNER", plugins_scanner.c_str(), TRUE); } - std::string plugins_path; + const gchar *c = g_getenv("GST_PLUGIN_PATH"); + std::string plugins_path = c != NULL ? std::string(c) : ""; + std::string local_plugin_path = SystemToolkit::cwd_path() + "gstreamer-1.0"; - if ( SystemToolkit::file_exists(local_plugin_path)) - plugins_path = local_plugin_path; + if ( SystemToolkit::file_exists(local_plugin_path)){ + if (!plugins_path.empty()) + plugins_path += ":"; + plugins_path += local_plugin_path; + } #ifdef GSTREAMER_SHMDATA_PLUGIN std::string shmdata_plugin_path = GSTREAMER_SHMDATA_PLUGIN; diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 77ab3ed..82e8cc7 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -95,6 +95,7 @@ using namespace std; #include "ImageProcessingShader.h" #include "Metronome.h" #include "VideoBroadcast.h" +#include "ShmdataBroadcast.h" #include "MultiFileRecorder.h" #include "TextEditor.h" @@ -3863,12 +3864,13 @@ void OutputPreview::Update() video_recorder_->stop(); } - // verify the video broadcaster is valid (change to nullptr if invalid) + // verify the broadcasters are valid (change to nullptr if invalid) FrameGrabbing::manager().verify( (FrameGrabber**) &video_broadcaster_); + FrameGrabbing::manager().verify( (FrameGrabber**) &shm_broadcaster_); #if defined(LINUX) // verify the frame grabber for webcam emulator is valid - FrameGrabbing::manager().verify(&webcam_emulator_); + FrameGrabbing::manager().verify( (FrameGrabber**) &webcam_emulator_); #endif } @@ -3911,6 +3913,17 @@ void OutputPreview::ToggleBroadcast() } } +void OutputPreview::ToggleSharedMemory() +{ + if (shm_broadcaster_) { + shm_broadcaster_->stop(); + } + else { + shm_broadcaster_ = new ShmdataBroadcast; + FrameGrabbing::manager().add(shm_broadcaster_); + } +} + void OutputPreview::Render() { @@ -4096,41 +4109,69 @@ void OutputPreview::Render() } #endif - if (VideoBroadcast::available()) { - // Broadcasting menu - ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f)); - // Stop broadcast menu (broadcaster already exists) - if (video_broadcaster_) { - if ( ImGui::MenuItem( ICON_FA_SQUARE " Stop Broadcast SRT") ) - video_broadcaster_->stop(); - } - // start broadcast (broadcaster does not exists) - else { - if ( ImGui::MenuItem( ICON_FA_PODCAST " Broadcast SRT") ) { - video_broadcaster_ = new VideoBroadcast(Settings::application.broadcast_port); - FrameGrabbing::manager().add(video_broadcaster_); - } - } - ImGui::PopStyleColor(1); - } - // Stream sharing menu ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f)); - if ( ImGui::MenuItem( ICON_FA_SHARE_ALT_SQUARE " Peer-to-peer sharing", NULL, &Settings::application.accept_connections) ) { + if ( ImGui::MenuItem( ICON_FA_SHARE_ALT_SQUARE " Peer-to-peer sharing", NULL, &Settings::application.accept_connections) ) { Streaming::manager().enable(Settings::application.accept_connections); } ImGui::PopStyleColor(1); - if (Settings::application.accept_connections) - { - std::vector ls = Streaming::manager().listStreams(); - if (ls.size()>0) { - ImGui::Separator(); - ImGui::MenuItem("Connected peers", nullptr, false, false); - for (auto it = ls.begin(); it != ls.end(); ++it) - ImGui::Text(" %s", (*it).c_str() ); + + // list active streams: + std::vector ls = Streaming::manager().listStreams(); + + + // Broadcasting menu + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.9f)); + if (VideoBroadcast::available()) { + bool enabled = (video_broadcaster_!=nullptr); + if ( ImGui::MenuItem( ICON_FA_PODCAST " SRT Broadcast", NULL, &enabled) ) + ToggleBroadcast(); + } + + // Shared Memory menu + if (ShmdataBroadcast::available()) { + bool enabled = (shm_broadcaster_!=nullptr); + if ( ImGui::MenuItem( ICON_FA_PROJECT_DIAGRAM " Shared Memory", NULL, &enabled) ) + ToggleSharedMemory(); + } + ImGui::PopStyleColor(1); + + // Display list of active stream + if (ls.size()>0 || video_broadcaster_ || shm_broadcaster_) { + ImGui::Separator(); + ImGui::MenuItem("Active streaming", nullptr, false, false); + + // First the list of peer 2 peer + for (auto it = ls.begin(); it != ls.end(); ++it) + ImGui::Text(" %s ", (*it).c_str() ); + + // Second the SRT + if (video_broadcaster_) { + ImGui::Text(" %s ", video_broadcaster_->info().c_str()); + + // copy text icon to give user the srt link to connect to + ImVec2 draw_pos = ImGui::GetCursorPos(); + ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -0.8 * ImGui::GetFrameHeight()) ); + char msg[256]; + ImFormatString(msg, IM_ARRAYSIZE(msg), "srt//%s:%d", NetworkToolkit::host_ips()[1].c_str(), Settings::application.broadcast_port ); + if (ImGuiToolkit::IconButton( ICON_FA_COPY, msg)) + ImGui::SetClipboardText(msg); + ImGui::SetCursorPos(draw_pos); } + // Third the SHMdata + if (shm_broadcaster_) { + ImGui::Text(" %s ", shm_broadcaster_->info().c_str()); + + // copy text icon to give user the socket path to connec to + ImVec2 draw_pos = ImGui::GetCursorPos(); + ImGui::SetCursorPos(draw_pos + ImVec2(ImGui::GetContentRegionAvailWidth() - 1.2 * ImGui::GetTextLineHeightWithSpacing(), -0.8 * ImGui::GetFrameHeight()) ); + if (ImGuiToolkit::IconButton( ICON_FA_COPY, shm_broadcaster_->socket_path().c_str())) + ImGui::SetClipboardText(shm_broadcaster_->socket_path().c_str()); + ImGui::SetCursorPos(draw_pos); + } } + ImGui::EndMenu(); } ImGui::EndMenuBar(); @@ -4194,6 +4235,19 @@ void OutputPreview::Render() ImGui::PopStyleColor(1); ImGui::PopFont(); } + // shmdata indicator + if (shm_broadcaster_) + { + ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + imagesize.x - 4.5f * r, draw_pos.y + r)); + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); + if (shm_broadcaster_->busy()) + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.8f)); + else + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_BROADCAST, 0.4f)); + ImGui::Text(ICON_FA_PROJECT_DIAGRAM); + ImGui::PopStyleColor(1); + ImGui::PopFont(); + } // streaming indicator if (Settings::application.accept_connections) { @@ -4239,6 +4293,7 @@ void OutputPreview::Render() float h = 1.f; h += (Settings::application.accept_connections ? 1.f : 0.f); h += (video_broadcaster_ ? 1.f : 0.f); + h += (shm_broadcaster_ ? 1.f : 0.f); draw_list->AddRectFilled(draw_pos, ImVec2(draw_pos.x + imagesize.x, draw_pos.y + h * r), IMGUI_COLOR_OVERLAY); ImGui::SetCursorScreenPos(draw_pos); ImGui::Text(" " ICON_FA_TV " %d x %d px, %.d fps", output->width(), output->height(), int(Mixer::manager().fps()) ); @@ -4248,6 +4303,8 @@ void OutputPreview::Render() Streaming::manager().listStreams().size() ); if (video_broadcaster_) ImGui::Text( " " ICON_FA_PODCAST " %s", video_broadcaster_->info().c_str() ); + if (shm_broadcaster_) + ImGui::Text( " " ICON_FA_PROJECT_DIAGRAM " %s", shm_broadcaster_->info().c_str() ); } ImGui::End(); @@ -5872,6 +5929,7 @@ void ShaderEditor::Render() { filters_.clear(); current_ = nullptr; + _editor.SetText(""); } // if compiling, cannot change source nor do anything else @@ -7800,8 +7858,16 @@ void Navigator::RenderMainPannelSettings() // // Networking preferences // + ImGuiToolkit::Spacing(); ImGui::Text("Stream"); + ImGuiToolkit::Indication("Peer-to-peer sharing on local network\n\n" + "vimix can stream JPEG (default) or H264 (requires less bandwidth but more resources for encoding)", ICON_FA_SHARE_ALT_SQUARE); + ImGui::SameLine(0); + ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + ImGui::Combo("Share P2P", &Settings::application.stream_protocol, "JPEG\0H264\0"); + if (VideoBroadcast::available()) { char msg[256]; ImFormatString(msg, IM_ARRAYSIZE(msg), "Broadcast SRT\n\n" @@ -7819,20 +7885,13 @@ void Navigator::RenderMainPannelSettings() ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); char bufport[7] = ""; sprintf(bufport, "%d", Settings::application.broadcast_port); - ImGui::InputTextWithHint("Broadcast", "7070", bufport, 6, ImGuiInputTextFlags_CharsDecimal); + ImGui::InputTextWithHint("Port SRT", "7070", bufport, 6, ImGuiInputTextFlags_CharsDecimal); if (ImGui::IsItemDeactivatedAfterEdit()){ if ( BaseToolkit::is_a_number(bufport, &Settings::application.broadcast_port)) Settings::application.broadcast_port = CLAMP(Settings::application.broadcast_port, 1029, 49150); } } - ImGuiToolkit::Indication("Peer-to-peer sharing on local network\n\n" - "vimix can stream JPEG (default) or H264 (requires less bandwidth but more resources for encoding)", ICON_FA_SHARE_ALT_SQUARE); - ImGui::SameLine(0); - ImGui::SetCursorPosX(-1.f * IMGUI_RIGHT_ALIGN); - ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - ImGui::Combo("P2P Share", &Settings::application.stream_protocol, "JPEG\0H264\0"); - // // OSC preferences // diff --git a/src/UserInterfaceManager.h b/src/UserInterfaceManager.h index bf356c0..f4ecc95 100644 --- a/src/UserInterfaceManager.h +++ b/src/UserInterfaceManager.h @@ -110,6 +110,7 @@ class FrameBufferImage; class FrameGrabber; class VideoRecorder; class VideoBroadcast; +class ShmdataBroadcast; class SourcePreview { @@ -330,6 +331,7 @@ class OutputPreview : public WorkspaceWindow // frame grabbers VideoRecorder *video_recorder_; VideoBroadcast *video_broadcaster_; + ShmdataBroadcast *shm_broadcaster_; // delayed trigger for recording std::vector< std::future > _video_recorders; @@ -350,6 +352,9 @@ public: void ToggleBroadcast(); inline bool isBroadcasting() const { return video_broadcaster_ != nullptr; } + void ToggleSharedMemory(); + inline bool isSharingMemory() const { return shm_broadcaster_ != nullptr; } + void Render(); void setVisible(bool on); diff --git a/src/VideoBroadcast.cpp b/src/VideoBroadcast.cpp index 2a88a33..564ce29 100644 --- a/src/VideoBroadcast.cpp +++ b/src/VideoBroadcast.cpp @@ -27,9 +27,9 @@ std::vector< std::pair > pipeline_sink_ { }; std::vector< std::pair > pipeline_encoder_ { - {"nvh264enc", "nvh264enc zerolatency=true rc-mode=cbr-ld-hq bitrate=4000 ! video/x-h264, profile=(string)high ! h264parse config-interval=1 ! mpegtsmux ! queue ! "}, - {"vaapih264enc", "vaapih264enc rate-control=cqp init-qp=26 ! video/x-h264, profile=high ! h264parse config-interval=1 ! mpegtsmux ! queue ! "}, - {"x264enc", "x264enc tune=zerolatency ! video/x-h264, profile=high ! mpegtsmux ! "} + {"nvh264enc", "nvh264enc zerolatency=true rc-mode=cbr-ld-hq bitrate=4000 ! "}, + {"vaapih264enc", "vaapih264enc rate-control=cqp init-qp=26 ! "}, + {"x264enc", "x264enc tune=zerolatency ! "} }; bool VideoBroadcast::available() @@ -76,18 +76,21 @@ VideoBroadcast::VideoBroadcast(int port): FrameGrabber(), port_(port), stopped_( std::string VideoBroadcast::init(GstCaps *caps) { + if (!VideoBroadcast::available()) + return std::string("Video Broadcast : Not available (missing SRT or H264)"); + // ignore if (caps == nullptr) return std::string("Video Broadcast : Invalid caps"); - if (!VideoBroadcast::available()) - return std::string("Video Broadcast : Not available (missing SRT or H264)"); - // create a gstreamer pipeline std::string description = "appsrc name=src ! videoconvert ! "; - // complement pipeline with encoder and sink + // complement pipeline with encoder description += VideoBroadcast::h264_encoder_; + description += "video/x-h264, profile=high ! queue ! h264parse config-interval=-1 ! mpegtsmux ! "; + + // complement pipeline with sink description += VideoBroadcast::srt_sink_; // change the placeholder to include the broadcast port @@ -192,11 +195,11 @@ std::string VideoBroadcast::info() const std::ostringstream ret; if (!initialized_) - ret << "Starting SRT"; + ret << "SRT starting.."; else if (active_) - ret << "Broadcasting on SRT (listener mode)"; + ret << "SRT Broadcast on port " << port_; else - ret << "SRT Terminated"; + ret << "SRT terminated"; return ret.str(); } diff --git a/src/defines.h b/src/defines.h index 60f15fa..9797d4e 100644 --- a/src/defines.h +++ b/src/defines.h @@ -82,7 +82,7 @@ #define IMGUI_COLOR_CAPTURE 1.0, 0.55, 0.05 #define IMGUI_COLOR_RECORD 1.0, 0.05, 0.05 #define IMGUI_COLOR_STREAM 0.05, 0.8, 1.0 -#define IMGUI_COLOR_BROADCAST 0.1, 0.9, 0.1 +#define IMGUI_COLOR_BROADCAST 0.1, 0.5, 1.0 #define IMGUI_NOTIFICATION_DURATION 2.5f #define IMGUI_TOOLTIP_TIMEOUT 80