diff --git a/CMakeLists.txt b/CMakeLists.txt index fe4dc6e..71036de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,9 +138,8 @@ endif() ##### Preprocessor options ##### -# add_definitions(-DUSE_GST_OPENGL_SYNC_HANDLER) +add_definitions(-DUSE_GST_OPENGL_SYNC_HANDLER) # add_definitions(-DUSE_GL_BUFFER_SUBDATA) -# add_definitions(-DIGNORE_GST_BUS_MESSAGE) ##### ##### Dependencies diff --git a/src/FrameGrabber.cpp b/src/FrameGrabber.cpp index 7f95889..aaf9632 100644 --- a/src/FrameGrabber.cpp +++ b/src/FrameGrabber.cpp @@ -496,14 +496,10 @@ void FrameGrabber::addFrame (GstBuffer *buffer, GstCaps *caps) // if initialization succeeded if (initialized_) { -#ifdef IGNORE_GST_BUS_MESSAGE - // avoid filling up bus with messages - gst_bus_set_flushing(gst_element_get_bus(pipeline_), true); -#else // set message handler for the pipeline's bus gst_bus_set_sync_handler(gst_element_get_bus(pipeline_), FrameGrabber::signal_handler, this, NULL); -#endif + // attach EOS detector GstPad *pad = gst_element_get_static_pad (gst_bin_get_by_name (GST_BIN (pipeline_), "sink"), "sink"); gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, FrameGrabber::callback_event_probe, this, NULL); diff --git a/src/MediaPlayer.cpp b/src/MediaPlayer.cpp index fceb5cf..4076a18 100644 --- a/src/MediaPlayer.cpp +++ b/src/MediaPlayer.cpp @@ -46,6 +46,7 @@ #endif #ifdef USE_GST_OPENGL_SYNC_HANDLER +#include #include "RenderingManager.h" #endif @@ -89,6 +90,11 @@ MediaPlayer::MediaPlayer() pbo_index_ = 0; pbo_next_index_ = 0; +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // try to use GLMemory for zero-copy GPU textures + use_gl_memory_ = Settings::application.render.gst_glmemory_texturing; +#endif + // OpenGL texture textureindex_ = 0; } @@ -341,6 +347,27 @@ GstBusSyncReply MediaPlayer::signal_handler(GstBus *, GstMessage *msg, gpointer error->message); g_error_free(error); } +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // setup OpenGL contexts for GStreamer elements from global Rendering opengl + else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_NEED_CONTEXT) { + const gchar* contextType; + gst_message_parse_context_type(msg, &contextType); + + if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE) && Rendering::manager().global_display) { + GstContext *displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); + gst_context_set_gl_display(displayContext, Rendering::manager().global_display); + gst_element_set_context(GST_ELEMENT(msg->src), displayContext); + gst_context_unref (displayContext); + } + if (!g_strcmp0(contextType, "gst.gl.app_context") && Rendering::manager().global_gl_context) { + GstContext *appContext = gst_context_new("gst.gl.app_context", TRUE); + GstStructure* structure = gst_context_writable_structure(appContext); + gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, Rendering::manager().global_gl_context, nullptr); + gst_element_set_context(GST_ELEMENT(msg->src), appContext); + gst_context_unref (appContext); + } + } +#endif // drop all messages to avoid filling up the stack gst_message_unref (msg); @@ -441,12 +468,43 @@ void MediaPlayer::execute_open() // instruct the sink to send samples synched in time gst_base_sink_set_sync (GST_BASE_SINK(sink), true); - // instruct sink to use the required caps - GstCaps *caps = gst_caps_new_simple ("video/x-raw", - "format", G_TYPE_STRING, "RGBA", - "width", G_TYPE_INT, media_.width, - "height", G_TYPE_INT, media_.height, - NULL); + // Configure appsink caps + // When using glsinkbin, appsink must accept GLMemory caps + GstCaps *caps = nullptr; + +#ifdef USE_GST_OPENGL_SYNC_HANDLER + if (media_.isimage) + use_gl_memory_ = false; // disable GLMemory for images to avoid issues with some plugins + + if (use_gl_memory_) { + // Create caps that accept BOTH GLMemory and system memory + // This allows glsinkbin to link properly + caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "RGBA", + NULL); + + // Add GLMemory variant + GstCaps *gl_caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "RGBA", + NULL); + GstCapsFeatures *gl_features = gst_caps_features_new("memory:GLMemory", NULL); + gst_caps_set_features(gl_caps, 0, gl_features); + + // Append GLMemory caps as preferred option + gst_caps_append(gl_caps, caps); + caps = gl_caps; + } + else +#endif + { + // Standard CPU caps with dimensions + caps = gst_caps_new_simple("video/x-raw", + "format", G_TYPE_STRING, "RGBA", + "width", G_TYPE_INT, media_.width, + "height", G_TYPE_INT, media_.height, + NULL); + } + gst_app_sink_set_caps (GST_APP_SINK(sink), caps); gst_caps_unref (caps); @@ -454,6 +512,10 @@ void MediaPlayer::execute_open() gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 5); gst_app_sink_set_drop (GST_APP_SINK(sink), true); + // set message handler for the pipeline's bus + bus_ = gst_element_get_bus(pipeline_); + gst_bus_set_sync_handler(bus_, MediaPlayer::signal_handler, this, NULL); + // set the callbacks GstAppSinkCallbacks callbacks; #if GST_VERSION_MINOR > 18 && GST_VERSION_MAJOR > 0 @@ -474,14 +536,30 @@ void MediaPlayer::execute_open() gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); - // set playbin sink - g_object_set ( G_OBJECT (pipeline_), "video-sink", sink, NULL); + // Wrap appsink with glsinkbin for automatic GLMemory handling + GstElement *video_sink = sink; // Default to appsink #ifdef USE_GST_OPENGL_SYNC_HANDLER - // capture bus signals to force a unique opengl context for all GST elements - Rendering::LinkPipeline(GST_PIPELINE (pipeline_)); + if (use_gl_memory_) { + // Try to create glsinkbin to automatically handle GL upload and conversion + GstElement *glsinkbin = gst_element_factory_make("glsinkbin", "glsink"); + + if (glsinkbin) { + // glsinkbin wraps our appsink and automatically inserts: + // - glupload (for GLMemory) + // - glcolorconvert (for NV12/I420 -> RGBA conversion in shaders) + g_object_set(G_OBJECT(glsinkbin), "sink", sink, NULL); + video_sink = glsinkbin; + } + else { + use_gl_memory_ = false; + } + } #endif + // set playbin sink (either glsinkbin wrapper or direct appsink) + g_object_set ( G_OBJECT (pipeline_), "video-sink", video_sink, NULL); + // set to desired state (PLAY or PAUSE) GstStateChangeReturn ret = gst_element_set_state (GST_ELEMENT(pipeline_), desired_state_); if (ret == GST_STATE_CHANGE_FAILURE) { @@ -497,15 +575,6 @@ void MediaPlayer::execute_open() timeline_.setEnd(d); } - bus_ = gst_element_get_bus(pipeline_); -#ifdef IGNORE_GST_BUS_MESSAGE - // avoid filling up bus with messages - gst_bus_set_flushing(bus_, true); -#else - // set message handler for the pipeline's bus - gst_bus_set_sync_handler(bus_, MediaPlayer::signal_handler, this, NULL); -#endif - // all good Log::Info("MediaPlayer %s Opened '%s' (%s %d x %d, %d kbps)", std::to_string(id_).c_str(), SystemToolkit::filename(uri_).c_str(), media_.codec_name.c_str(), @@ -660,11 +729,6 @@ void MediaPlayer::execute_open() gst_object_unref (sink); gst_caps_unref (caps); -#ifdef USE_GST_OPENGL_SYNC_HANDLER - // capture bus signals to force a unique opengl context for all GST elements - Rendering::LinkPipeline(GST_PIPELINE (pipeline_)); -#endif - // set to desired state (PLAY or PAUSE) GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_); if (ret == GST_STATE_CHANGE_FAILURE) { @@ -681,13 +745,8 @@ void MediaPlayer::execute_open() } bus_ = gst_element_get_bus(pipeline_); -#ifdef IGNORE_GST_BUS_MESSAGE - // avoid filling up bus with messages - gst_bus_set_flushing(bus_, true); -#else // set message handler for the pipeline's bus gst_bus_set_sync_handler(bus_, MediaPlayer::signal_handler, this, NULL); -#endif // all good Log::Info("MediaPlayer %s Opened '%s' (%s %d x %d)", std::to_string(id_).c_str(), @@ -753,15 +812,14 @@ void MediaPlayer::pipeline_terminate( GstElement *p, GstBus *b ) if (ret == GST_STATE_CHANGE_ASYNC) gst_element_get_state(p, NULL, NULL, 1000000); -#ifndef IGNORE_GST_BUS_MESSAGE - // empty pipeline bus (if used) + // empty pipeline bus GstMessage *msg = NULL; do { if (msg) gst_message_unref (msg); msg = gst_bus_timed_pop_filtered(b, 1000000, GST_MESSAGE_ANY); } while (msg != NULL); -#endif + // unref bus gst_object_unref( GST_OBJECT(b) ); @@ -1159,11 +1217,49 @@ void MediaPlayer::init_texture(guint index) // fill texture frame with frame at given index if (frame_[index].buffer) { - GstMapInfo map; - gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height, - GL_RGBA, GL_UNSIGNED_BYTE, map.data); - gst_buffer_unmap (frame_[index].buffer, &map); + +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // Try GLMemory fast path first + bool gl_memory_used = false; + if ( use_gl_memory_ ) { + GstMemory *mem = gst_buffer_peek_memory(frame_[index].buffer, 0); + + if (mem && gst_is_gl_memory(mem)) { + GstGLMemory *gl_mem = (GstGLMemory*) mem; + guint gst_tex_id = gst_gl_memory_get_texture_id(gl_mem); + + if (gst_tex_id > 0) { + // Copy from GStreamer GL texture to our texture using FBO + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gst_tex_id, 0); + + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + media_.width, media_.height); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + gl_memory_used = true; + } + } else { + // Not GLMemory - disable optimization + use_gl_memory_ = false; + } + } + + // Fallback to CPU path if GLMemory not available + if (!gl_memory_used) +#endif + { + GstMapInfo map; + gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, media_.width, media_.height, + GL_RGBA, GL_UNSIGNED_BYTE, map.data); + gst_buffer_unmap (frame_[index].buffer, &map); + } } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -1171,24 +1267,34 @@ void MediaPlayer::init_texture(guint index) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // initialize decoderName once (forced update) + decoder_name_ = ""; + // use Pixel Buffer Objects only for performance needs of videos if ( !isImage() ) { - // set pbo image size - pbo_size_ = media_.height * media_.width * 4; +#ifdef USE_GST_OPENGL_SYNC_HANDLER + if (use_gl_memory_) + Log::Info("MediaPlayer %s Uses %s decoding and OpenGL GLMemory texturing (zero-copy).", std::to_string(id_).c_str(), decoderName().c_str()); + else +#endif + { + // set pbo image size + pbo_size_ = media_.height * media_.width * 4; - // create pixel buffer objects, - if (pbo_[0]) - glDeleteBuffers(2, pbo_); - glGenBuffers(2, pbo_); + // create pixel buffer objects, + if (pbo_[0]) + glDeleteBuffers(2, pbo_); + glGenBuffers(2, pbo_); - // should be good to go - pbo_index_ = 0; - pbo_next_index_ = 1; + // should be good to go + pbo_index_ = 0; + pbo_next_index_ = 1; - // initialize decoderName once (forced update) - decoder_name_ = ""; - Log::Info("MediaPlayer %s Uses %s decoding and OpenGL PBO texturing.", std::to_string(id_).c_str(), decoderName().c_str()); + Log::Info("MediaPlayer %s Uses %s decoding and OpenGL PBO texturing.", std::to_string(id_).c_str(), decoderName().c_str()); + } } + else + Log::Info("MediaPlayer %s Uses %s decoding and standard OpenGL texturing.", std::to_string(id_).c_str(), decoderName().c_str()); glBindTexture(GL_TEXTURE_2D, 0); } @@ -1204,6 +1310,49 @@ void MediaPlayer::fill_texture(guint index) init_texture(index); } else if (!isImage()) { + +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // Try GLMemory fast path first (zero-copy GPU texture) + if (use_gl_memory_ && frame_[index].buffer) { + GstMemory *mem = gst_buffer_peek_memory(frame_[index].buffer, 0); + + if (mem && gst_is_gl_memory(mem)) { + // FAST PATH: Direct GL texture extraction from GStreamer + GstGLMemory *gl_mem = (GstGLMemory*) mem; + guint gst_tex_id = gst_gl_memory_get_texture_id(gl_mem); + + if (gst_tex_id > 0) { + // Use FBO to copy from GStreamer's texture to our texture + // This ensures we maintain ownership and texture lifecycle + GLuint fbo; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gst_tex_id, 0); + + glBindTexture(GL_TEXTURE_2D, textureindex_); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, + media_.width, media_.height); + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + + // Success - no need for CPU path + return; + } + } + else { + // First buffer is not GLMemory - disable GLMemory optimization for this source + // This happens with images, some network streams, or when glupload fails + use_gl_memory_ = false; + Log::Info("MediaPlayer %s GLMemory not available, falling back to CPU transfer.", + std::to_string(id_).c_str()); + } + } +#endif + + // FALLBACK: CPU path (standard PBO or direct upload) // Use GST mapping to access pointer to RGBA data GstMapInfo map; gst_buffer_map(frame_[index].buffer, &map, GST_MAP_READ); @@ -1413,11 +1562,9 @@ void MediaPlayer::update() if (need_loop && desired_state_ == GST_STATE_PLAYING) // avoid repeated call execute_loop_command(); -#ifndef IGNORE_GST_BUS_MESSAGE GstMessage *msg = gst_bus_pop_filtered(bus_, GST_MESSAGE_ANY); if (msg != NULL) gst_message_unref(msg); -#endif force_update_ = false; } @@ -1731,6 +1878,21 @@ GstFlowReturn MediaPlayer::callback_new_preroll (GstAppSink *sink, gpointer p) // send frames to media player only if ready MediaPlayer *m = static_cast(p); if (m && m->opened_) { + +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // Debug: Log negotiated caps on first frame to verify GLMemory + if (m->use_gl_memory_) { + GstCaps *caps = gst_sample_get_caps(sample); + if (caps) { + // Check if GLMemory was actually negotiated + GstCapsFeatures *features = gst_caps_get_features(caps, 0); + if (!features || !gst_caps_features_contains(features, "memory:GLMemory")) { + m->use_gl_memory_ = false; + } + } + } +#endif + // fill frame from buffer if ( !m->fill_frame(buf, MediaPlayer::PREROLL) ) ret = GST_FLOW_ERROR; diff --git a/src/MediaPlayer.h b/src/MediaPlayer.h index 7da9bcc..b35c874 100644 --- a/src/MediaPlayer.h +++ b/src/MediaPlayer.h @@ -18,7 +18,7 @@ class Visitor; #define MAX_PLAY_SPEED 20.0 #define MIN_PLAY_SPEED 0.1 -#define N_VFRAME 5 +#define N_VFRAME 15 struct MediaInfo { @@ -403,6 +403,11 @@ private: guint pbo_index_, pbo_next_index_; guint pbo_size_; +#ifdef USE_GST_OPENGL_SYNC_HANDLER + // for GLMemory optimization + bool use_gl_memory_; +#endif + // gst pipeline control void execute_open(); void execute_play_command(bool on); diff --git a/src/Navigator.cpp b/src/Navigator.cpp index c6ec0ae..81be214 100644 --- a/src/Navigator.cpp +++ b/src/Navigator.cpp @@ -3353,6 +3353,7 @@ void Navigator::RenderMainPannelSettings() static bool vsync = (Settings::application.render.vsync > 0); static bool multi = (Settings::application.render.multisampling > 0); static bool gpu = Settings::application.render.gpu_decoding; + static bool glmemory = Settings::application.render.gst_glmemory_texturing; static bool audio = Settings::application.accept_audio; bool change = false; // hardware support deserves more explanation @@ -3371,13 +3372,14 @@ void Navigator::RenderMainPannelSettings() change |= ImGuiToolkit::ButtonSwitch( "Audio (experimental)", &audio); #ifndef NDEBUG + change |= ImGuiToolkit::ButtonSwitch( "Gst-GLMemory texturing", &glmemory); change |= ImGuiToolkit::ButtonSwitch( "Vertical synchronization", &vsync); change |= ImGuiToolkit::ButtonSwitch( "Multisample antialiasing", &multi); #endif if (change) { need_restart = ( vsync != (Settings::application.render.vsync > 0) || multi != (Settings::application.render.multisampling > 0) || - gpu != Settings::application.render.gpu_decoding || + glmemory != Settings::application.render.gst_glmemory_texturing || audio != Settings::application.accept_audio ); } @@ -3386,6 +3388,7 @@ void Navigator::RenderMainPannelSettings() if (ImGui::Button( ICON_FA_POWER_OFF " Quit & restart to apply", ImVec2(ImGui::GetContentRegionAvail().x - 50, 0))) { Settings::application.render.vsync = vsync ? 1 : 0; Settings::application.render.multisampling = multi ? 3 : 0; + Settings::application.render.gst_glmemory_texturing = glmemory; Settings::application.render.gpu_decoding = gpu; Settings::application.accept_audio = audio; if (UserInterface::manager().TryClose()) diff --git a/src/RenderingManager.cpp b/src/RenderingManager.cpp index a93385e..2e1555c 100644 --- a/src/RenderingManager.cpp +++ b/src/RenderingManager.cpp @@ -51,6 +51,10 @@ #include #endif +#ifdef USE_GST_OPENGL_SYNC_HANDLER +#include +#endif + // standalone image loader #include @@ -168,59 +172,6 @@ void inhibitScreensaver (bool) {} #endif -#ifdef USE_GST_OPENGL_SYNC_HANDLER - -GLFW_EXPOSE_NATIVE_X11 -#include - -// -// Discarded because not working under OSX - kept in case it would become useful -// -// Linking pipeline to the rendering instance ensures the opengl contexts -// created by gstreamer inside plugins (e.g. glsinkbin) is the same -// -static GstGLContext *global_gl_context = NULL; -static GstGLDisplay *global_display = NULL; - -static GstBusSyncReply bus_sync_handler( GstBus *, GstMessage * msg, gpointer ) -{ - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_NEED_CONTEXT) { - const gchar* contextType; - gst_message_parse_context_type(msg, &contextType); - - if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) { - GstContext *displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE); - gst_context_set_gl_display(displayContext, global_display); - gst_element_set_context(GST_ELEMENT(msg->src), displayContext); - gst_context_unref (displayContext); - - g_info ("Managed %s\n", contextType); - } - if (!g_strcmp0(contextType, "gst.gl.app_context")) { - GstContext *appContext = gst_context_new("gst.gl.app_context", TRUE); - GstStructure* structure = gst_context_writable_structure(appContext); - gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, global_gl_context, nullptr); - gst_element_set_context(GST_ELEMENT(msg->src), appContext); - gst_context_unref (appContext); - - g_info ("Managed %s\n", contextType); - } - } - - gst_message_unref (msg); - - return GST_BUS_DROP; -} - -void Rendering::LinkPipeline( GstPipeline *pipeline ) -{ - // capture bus signals to force a unique opengl context for all GST elements - GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); - gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) bus_sync_handler, pipeline, NULL); - gst_object_unref (m_bus); -} -#endif - bool openGLExtensionAvailable(const char *extensionname) { diff --git a/src/RenderingManager.h b/src/RenderingManager.h index 65fe284..06c85e6 100644 --- a/src/RenderingManager.h +++ b/src/RenderingManager.h @@ -170,8 +170,9 @@ public: static bool shouldHaveEnoughMemory(glm::vec3 resolution, int flags); #ifdef USE_GST_OPENGL_SYNC_HANDLER - // for opengl pipeline in gstreamer - static void LinkPipeline( GstPipeline *pipeline ); + // for opengl pipeline in gstreamer + GstGLContext *global_gl_context; + GstGLDisplay *global_display; #endif protected: @@ -202,6 +203,8 @@ private: Screenshot screenshot_; bool request_screenshot_; + + }; diff --git a/src/Settings.cpp b/src/Settings.cpp index 80a538a..b7db817 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -211,6 +211,7 @@ void Settings::Save(uint64_t runtime, const std::string &filename) RenderNode->SetAttribute("vsync", application.render.vsync); RenderNode->SetAttribute("multisampling", application.render.multisampling); RenderNode->SetAttribute("gpu_decoding", application.render.gpu_decoding); + RenderNode->SetAttribute("gst_glmemory_texturing", application.render.gst_glmemory_texturing); RenderNode->SetAttribute("ratio", application.render.ratio); RenderNode->SetAttribute("res", application.render.res); RenderNode->SetAttribute("custom_width", application.render.custom_width); @@ -567,6 +568,7 @@ void Settings::Load(const std::string &filename) rendernode->QueryIntAttribute("vsync", &application.render.vsync); rendernode->QueryIntAttribute("multisampling", &application.render.multisampling); rendernode->QueryBoolAttribute("gpu_decoding", &application.render.gpu_decoding); + rendernode->QueryBoolAttribute("gst_glmemory_texturing", &application.render.gst_glmemory_texturing); rendernode->QueryIntAttribute("ratio", &application.render.ratio); rendernode->QueryIntAttribute("res", &application.render.res); rendernode->QueryIntAttribute("custom_width", &application.render.custom_width); diff --git a/src/Settings.h b/src/Settings.h index df4288f..e5f1f32 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -197,6 +197,7 @@ struct RenderConfig float fading; bool gpu_decoding; bool gpu_decoding_available; + bool gst_glmemory_texturing; RenderConfig() { disabled = false; @@ -209,6 +210,7 @@ struct RenderConfig fading = 0.0; gpu_decoding = true; gpu_decoding_available = false; + gst_glmemory_texturing = true; } }; diff --git a/src/Stream.cpp b/src/Stream.cpp index 51c93a7..44757f4 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -333,13 +333,9 @@ void Stream::execute_open() gst_base_sink_set_sync (GST_BASE_SINK(sink), !live_); bus_ = gst_element_get_bus(pipeline_); -#ifdef IGNORE_GST_BUS_MESSAGE - // avoid filling up bus with messages - gst_bus_set_flushing(bus_, true); -#else + // set message handler for the pipeline's bus gst_bus_set_sync_handler(bus_, stream_signal_handler, this, NULL); -#endif // all good Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_); @@ -392,7 +388,6 @@ void Stream::pipeline_terminate( GstElement *p, GstBus *b ) if (ret == GST_STATE_CHANGE_ASYNC) gst_element_get_state (p, NULL, NULL, 1000000); -#ifndef IGNORE_GST_BUS_MESSAGE // empty pipeline bus (if used) GstMessage *msg = NULL; do { @@ -400,7 +395,7 @@ void Stream::pipeline_terminate( GstElement *p, GstBus *b ) gst_message_unref (msg); msg = gst_bus_timed_pop_filtered(b, 1000000, GST_MESSAGE_ANY); } while (msg != NULL); -#endif + // unref bus gst_object_unref( GST_OBJECT(b) ); @@ -796,11 +791,9 @@ void Stream::update() play(false); } -#ifndef IGNORE_GST_BUS_MESSAGE GstMessage *msg = gst_bus_pop_filtered(bus_, GST_MESSAGE_ANY); if (msg != NULL) gst_message_unref(msg); -#endif } double Stream::updateFrameRate() const