mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-14 00:52:04 +00:00
Linux: use GTK sink for Wayland Bambu liveview (#13432)
* Use GTK sink for Wayland liveview Keep native Wayland sessions on the GTK backend and use a GTK widget based GStreamer sink for Bambu liveview instead of the Wayland video overlay path, which can render black on NVIDIA/Hyprland. Keep the existing wxMediaCtrl path for X11 and continue preferring software H.264 decoding while demoting GL and hardware decoder paths that caused liveview crashes. * Narrow Linux liveview fix to native Wayland * Tighten native Wayland liveview setup * Tighten Wayland liveview teardown and rank setup * Package GStreamer gtksink for Wayland liveview --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
@@ -35,12 +35,13 @@ finish-args:
|
|||||||
|
|
||||||
modules:
|
modules:
|
||||||
|
|
||||||
# JPEG codec for the liveview
|
# JPEG codec and GTK video sink (Wayland liveview) for the liveview
|
||||||
- name: gst-plugins-good
|
- name: gst-plugins-good
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
config-opts:
|
config-opts:
|
||||||
- -Dauto_features=disabled
|
- -Dauto_features=disabled
|
||||||
- -Djpeg=enabled
|
- -Djpeg=enabled
|
||||||
|
- -Dgtk3=enabled
|
||||||
- -Ddoc=disabled
|
- -Ddoc=disabled
|
||||||
- -Dexamples=disabled
|
- -Dexamples=disabled
|
||||||
- -Dtests=disabled
|
- -Dtests=disabled
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export REQUIRED_DEV_PACKAGES=(
|
|||||||
gettext
|
gettext
|
||||||
git
|
git
|
||||||
glew
|
glew
|
||||||
|
gst-plugins-good
|
||||||
gstreamer
|
gstreamer
|
||||||
gstreamermm
|
gstreamermm
|
||||||
gtk3
|
gtk3
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export REQUIRED_DEV_PACKAGES=(
|
|||||||
gettext
|
gettext
|
||||||
git
|
git
|
||||||
glew
|
glew
|
||||||
|
gst-plugins-good
|
||||||
gstreamer
|
gstreamer
|
||||||
gtk3
|
gtk3
|
||||||
libmspack
|
libmspack
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ REQUIRED_DEV_PACKAGES=(
|
|||||||
g++
|
g++
|
||||||
gettext
|
gettext
|
||||||
git
|
git
|
||||||
|
gstreamer1.0-gtk3
|
||||||
libcurl4-openssl-dev
|
libcurl4-openssl-dev
|
||||||
libdbus-1-dev
|
libdbus-1-dev
|
||||||
libglew-dev
|
libglew-dev
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ REQUIRED_DEV_PACKAGES=(
|
|||||||
gettext
|
gettext
|
||||||
git
|
git
|
||||||
gstreamer1-devel
|
gstreamer1-devel
|
||||||
|
gstreamer1-plugins-good-gtk
|
||||||
gstreamermm-devel
|
gstreamermm-devel
|
||||||
gtk3-devel
|
gtk3-devel
|
||||||
libmspack-devel
|
libmspack-devel
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ REQUIRED_DEV_PACKAGES=(
|
|||||||
media-libs/glew
|
media-libs/glew
|
||||||
media-libs/gst-plugins-base:1.0
|
media-libs/gst-plugins-base:1.0
|
||||||
media-libs/gstreamer:1.0
|
media-libs/gstreamer:1.0
|
||||||
|
media-plugins/gst-plugins-gtk:1.0
|
||||||
net-misc/curl
|
net-misc/curl
|
||||||
net-misc/wget
|
net-misc/wget
|
||||||
sys-apps/dbus
|
sys-apps/dbus
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ REQUIRED_DEV_PACKAGES=(
|
|||||||
gettext
|
gettext
|
||||||
git
|
git
|
||||||
gstreamer-devel
|
gstreamer-devel
|
||||||
|
gstreamer-plugins-good-gtk
|
||||||
gtk3-devel
|
gtk3-devel
|
||||||
libmspack-devel
|
libmspack-devel
|
||||||
libquadmath-devel
|
libquadmath-devel
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ static std::map<int, std::string> error_messages = {
|
|||||||
{100, L("The player is not loaded, please click \"play\" button to retry.")},
|
{100, L("The player is not loaded, please click \"play\" button to retry.")},
|
||||||
{101, L("The player is not loaded, please click \"play\" button to retry.")},
|
{101, L("The player is not loaded, please click \"play\" button to retry.")},
|
||||||
{102, L("The player is not loaded, please click \"play\" button to retry.")},
|
{102, L("The player is not loaded, please click \"play\" button to retry.")},
|
||||||
{103, L("The player is not loaded, please click \"play\" button to retry.")}
|
{103, L("The player is not loaded, please click \"play\" button to retry.")},
|
||||||
|
{104, L("The player is not loaded because the GStreamer GTK video sink is missing or failed to initialize.")}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -838,6 +839,12 @@ void wxMediaCtrl2::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
|||||||
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
wxWindow::DoSetSize(x, y, width, height, sizeFlags);
|
||||||
#else
|
#else
|
||||||
wxMediaCtrl::DoSetSize(x, y, width, height, sizeFlags);
|
wxMediaCtrl::DoSetSize(x, y, width, height, sizeFlags);
|
||||||
|
#endif
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_gtk_video_window) {
|
||||||
|
const wxSize client_size = GetClientSize();
|
||||||
|
m_gtk_video_window->SetSize(0, 0, client_size.GetWidth(), client_size.GetHeight());
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (sizeFlags & wxSIZE_USE_EXISTING) return;
|
if (sizeFlags & wxSIZE_USE_EXISTING) return;
|
||||||
wxSize size = m_video_size;
|
wxSize size = m_video_size;
|
||||||
@@ -853,4 +860,3 @@ void wxMediaCtrl2::DoSetSize(int x, int y, int width, int height, int sizeFlags)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
#include "libslic3r/Time.hpp"
|
#include "libslic3r/Time.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
|
#include "LinuxDisplayBackend.hpp"
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <string>
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
#include <winuser.h>
|
#include <winuser.h>
|
||||||
#include <versionhelpers.h>
|
#include <versionhelpers.h>
|
||||||
@@ -13,6 +15,78 @@
|
|||||||
#ifdef __LINUX__
|
#ifdef __LINUX__
|
||||||
#include "Printer/gstbambusrc.h"
|
#include "Printer/gstbambusrc.h"
|
||||||
#include <gst/gst.h> // main gstreamer header
|
#include <gst/gst.h> // main gstreamer header
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <wx/nativewin.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool ensure_gstreamer_initialized_for_liveview()
|
||||||
|
{
|
||||||
|
GError* error = nullptr;
|
||||||
|
if (!gst_init_check(nullptr, nullptr, &error)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "wxMediaCtrl2: gst_init_check failed before native Wayland liveview setup"
|
||||||
|
<< (error ? std::string(": ") + error->message : std::string());
|
||||||
|
if (error)
|
||||||
|
g_error_free(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_gstreamer_feature_available(const char* feature)
|
||||||
|
{
|
||||||
|
if (!ensure_gstreamer_initialized_for_liveview())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GstElementFactory* factory = gst_element_factory_find(feature);
|
||||||
|
if (!factory)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gst_object_unref(factory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_gstreamer_feature_rank(const char* feature, guint rank)
|
||||||
|
{
|
||||||
|
GstElementFactory* factory = gst_element_factory_find(feature);
|
||||||
|
if (!factory)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gst_plugin_feature_set_rank(GST_PLUGIN_FEATURE(factory), rank);
|
||||||
|
gst_object_unref(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure_wayland_gstreamer_liveview_path()
|
||||||
|
{
|
||||||
|
static bool configured = false;
|
||||||
|
if (configured)
|
||||||
|
return;
|
||||||
|
configured = true;
|
||||||
|
|
||||||
|
if (!ensure_gstreamer_initialized_for_liveview())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Prefer software decode for Bambu liveview on Wayland/NVIDIA, where
|
||||||
|
// zero-copy GL/DMABUF display paths can be fragile. Keep hardware
|
||||||
|
// decoders available as lower-ranked fallbacks for VAAPI/NVDEC/V4L2-only
|
||||||
|
// installations instead of passing preflight and then blocking autoplug.
|
||||||
|
set_gstreamer_feature_rank("avdec_h264", GST_RANK_PRIMARY + 300);
|
||||||
|
set_gstreamer_feature_rank("openh264dec", GST_RANK_PRIMARY + 100);
|
||||||
|
set_gstreamer_feature_rank("nvh264dec", GST_RANK_MARGINAL);
|
||||||
|
set_gstreamer_feature_rank("vaapih264dec", GST_RANK_MARGINAL);
|
||||||
|
set_gstreamer_feature_rank("vah264dec", GST_RANK_MARGINAL);
|
||||||
|
set_gstreamer_feature_rank("v4l2h264dec", GST_RANK_MARGINAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
|
||||||
|
#ifdef __LINUX__
|
||||||
|
extern "C" int gst_bambu_last_error;
|
||||||
|
|
||||||
class WXDLLIMPEXP_MEDIA
|
class WXDLLIMPEXP_MEDIA
|
||||||
wxGStreamerMediaBackend : public wxMediaBackendCommonBase
|
wxGStreamerMediaBackend : public wxMediaBackendCommonBase
|
||||||
{
|
{
|
||||||
@@ -25,6 +99,15 @@ wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
|||||||
|
|
||||||
wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent)
|
wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent)
|
||||||
{
|
{
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
m_native_wayland = Slic3r::GUI::is_running_on_wayland();
|
||||||
|
if (m_native_wayland && is_gstreamer_feature_available("gtksink"))
|
||||||
|
configure_wayland_gstreamer_liveview_path();
|
||||||
|
else if (m_native_wayland) {
|
||||||
|
m_gtk_sink_error = _L("Native Wayland liveview requires the GStreamer GTK video sink. Please install the gtksink plugin for GStreamer, then restart OrcaSlicer.");
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: native Wayland liveview disabled because GStreamer gtksink is unavailable";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
auto hModExe = GetModuleHandle(NULL);
|
auto hModExe = GetModuleHandle(NULL);
|
||||||
// BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl2: GetModuleHandle " << hModExe;
|
// BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl2: GetModuleHandle " << hModExe;
|
||||||
@@ -39,20 +122,202 @@ wxMediaCtrl2::wxMediaCtrl2(wxWindow *parent)
|
|||||||
*AmdPowerXpressRequestHighPerformance = 0;
|
*AmdPowerXpressRequestHighPerformance = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
wxMediaCtrl::Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxMEDIACTRLPLAYERCONTROLS_NONE);
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_native_wayland)
|
||||||
|
wxControl::Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
wxMediaCtrl::Create(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxMEDIACTRLPLAYERCONTROLS_NONE);
|
||||||
#ifdef __LINUX__
|
#ifdef __LINUX__
|
||||||
/* Register only after we have created the wxMediaCtrl, since only then are we guaranteed to have fired up Gstreamer's plugin registry. */
|
|
||||||
auto playbin = reinterpret_cast<wxGStreamerMediaBackend *>(m_imp)->m_playbin;
|
|
||||||
g_object_set (G_OBJECT (playbin),
|
|
||||||
"audio-sink", NULL,
|
|
||||||
NULL);
|
|
||||||
gstbambusrc_register();
|
gstbambusrc_register();
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
if (m_native_wayland && m_gtk_sink_error.empty())
|
||||||
|
m_use_gtk_sink = CreateGtkSinkPlayer();
|
||||||
|
if (m_native_wayland && !m_use_gtk_sink && m_gtk_sink_error.empty())
|
||||||
|
m_gtk_sink_error = _L("Failed to initialize the native Wayland GStreamer video sink. Please check your GStreamer GTK plugin installation.");
|
||||||
|
#endif
|
||||||
|
if (!m_use_gtk_sink && m_imp) {
|
||||||
|
auto playbin = reinterpret_cast<wxGStreamerMediaBackend *>(m_imp)->m_playbin;
|
||||||
|
g_object_set(G_OBJECT(playbin),
|
||||||
|
"audio-sink", nullptr,
|
||||||
|
nullptr);
|
||||||
|
} else if (!m_use_gtk_sink) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: wxMediaCtrl backend is unavailable";
|
||||||
|
}
|
||||||
Bind(wxEVT_MEDIA_LOADED, [this](auto & e) {
|
Bind(wxEVT_MEDIA_LOADED, [this](auto & e) {
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
|
wxMediaEvent event(wxEVT_MEDIA_STATECHANGED);
|
||||||
|
event.SetId(0);
|
||||||
|
event.SetEventObject(this);
|
||||||
|
wxPostEvent(this, event);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxMediaCtrl2::~wxMediaCtrl2()
|
||||||
|
{
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
DestroyGtkSinkPlayer();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
bool wxMediaCtrl2::CreateGtkSinkPlayer()
|
||||||
|
{
|
||||||
|
GstElement *playbin = gst_element_factory_make("playbin", "orca-wayland-gtk-playbin");
|
||||||
|
if (!playbin)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
GError *error = nullptr;
|
||||||
|
GstElement *video_sink = gst_parse_bin_from_description(
|
||||||
|
"videoconvert ! videoscale ! video/x-raw,format=BGRx ! gtksink name=orca_wayland_gtksink sync=false",
|
||||||
|
TRUE,
|
||||||
|
&error);
|
||||||
|
if (!video_sink) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: failed to create gtksink video bin"
|
||||||
|
<< (error ? std::string(": ") + error->message : std::string());
|
||||||
|
if (error)
|
||||||
|
g_error_free(error);
|
||||||
|
gst_object_unref(playbin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GstElement *gtk_sink = gst_bin_get_by_name(GST_BIN(video_sink), "orca_wayland_gtksink");
|
||||||
|
if (!gtk_sink) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: failed to find gtksink in video bin";
|
||||||
|
gst_object_unref(video_sink);
|
||||||
|
gst_object_unref(playbin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget *gtk_widget = nullptr;
|
||||||
|
g_object_get(G_OBJECT(gtk_sink), "widget", >k_widget, nullptr);
|
||||||
|
if (!gtk_widget) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: gtksink did not expose a GtkWidget";
|
||||||
|
gst_object_unref(gtk_sink);
|
||||||
|
gst_object_unref(video_sink);
|
||||||
|
gst_object_unref(playbin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_show(gtk_widget);
|
||||||
|
m_gtk_video_window = new wxNativeWindow(this, wxID_ANY, gtk_widget);
|
||||||
|
m_gtk_video_window->Show();
|
||||||
|
g_object_unref(gtk_widget);
|
||||||
|
|
||||||
|
g_object_set(G_OBJECT(playbin),
|
||||||
|
"video-sink", video_sink,
|
||||||
|
"audio-sink", nullptr,
|
||||||
|
nullptr);
|
||||||
|
gst_object_unref(video_sink);
|
||||||
|
|
||||||
|
m_gtk_playbin = playbin;
|
||||||
|
m_gtk_sink = gtk_sink;
|
||||||
|
|
||||||
|
GstBus *bus = gst_element_get_bus(playbin);
|
||||||
|
m_gtk_bus_watch_id = gst_bus_add_watch(bus, [](GstBus *, GstMessage *message, gpointer data) -> gboolean {
|
||||||
|
auto *self = static_cast<wxMediaCtrl2 *>(data);
|
||||||
|
if (!self || !self->m_gtk_playbin)
|
||||||
|
return G_SOURCE_REMOVE;
|
||||||
|
|
||||||
|
switch (GST_MESSAGE_TYPE(message)) {
|
||||||
|
case GST_MESSAGE_ERROR:
|
||||||
|
{
|
||||||
|
GError *error = nullptr;
|
||||||
|
gchar *debug = nullptr;
|
||||||
|
gst_message_parse_error(message, &error, &debug);
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "wxMediaCtrl2: gtksink pipeline error"
|
||||||
|
<< (error ? std::string(": ") + error->message : std::string())
|
||||||
|
<< (debug ? std::string(" debug: ") + debug : std::string());
|
||||||
|
if (error)
|
||||||
|
g_error_free(error);
|
||||||
|
if (debug)
|
||||||
|
g_free(debug);
|
||||||
|
|
||||||
|
self->m_error = gst_bambu_last_error ? gst_bambu_last_error : 2;
|
||||||
|
self->m_loaded = false;
|
||||||
|
self->m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
self->PostGtkSinkStateEvent(self->GetId());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GST_MESSAGE_EOS:
|
||||||
|
self->m_loaded = false;
|
||||||
|
self->m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
self->PostGtkSinkStateEvent(self->GetId());
|
||||||
|
break;
|
||||||
|
case GST_MESSAGE_STATE_CHANGED:
|
||||||
|
if (GST_MESSAGE_SRC(message) == GST_OBJECT(self->m_gtk_playbin)) {
|
||||||
|
GstState old_state;
|
||||||
|
GstState new_state;
|
||||||
|
GstState pending_state;
|
||||||
|
gst_message_parse_state_changed(message, &old_state, &new_state, &pending_state);
|
||||||
|
|
||||||
|
if (new_state == GST_STATE_PLAYING) {
|
||||||
|
self->m_loaded = true;
|
||||||
|
self->m_gtk_state = wxMEDIASTATE_PLAYING;
|
||||||
|
self->PostGtkSinkStateEvent();
|
||||||
|
} else if (new_state == GST_STATE_PAUSED && old_state < GST_STATE_PAUSED) {
|
||||||
|
// Treat only upward READY/NULL -> PAUSED as load completion.
|
||||||
|
// PLAYING -> PAUSED is a normal teardown step before NULL.
|
||||||
|
self->m_loaded = true;
|
||||||
|
self->m_gtk_state = wxMEDIASTATE_PAUSED;
|
||||||
|
self->PostGtkSinkStateEvent();
|
||||||
|
} else if (new_state <= GST_STATE_READY && old_state >= GST_STATE_PAUSED) {
|
||||||
|
self->m_loaded = false;
|
||||||
|
self->m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
self->PostGtkSinkStateEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return G_SOURCE_CONTINUE;
|
||||||
|
}, this);
|
||||||
|
gst_object_unref(bus);
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl2: using GTK native Wayland video sink";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxMediaCtrl2::DestroyGtkSinkPlayer()
|
||||||
|
{
|
||||||
|
if (m_gtk_bus_watch_id) {
|
||||||
|
g_source_remove(m_gtk_bus_watch_id);
|
||||||
|
m_gtk_bus_watch_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_gtk_playbin) {
|
||||||
|
gst_element_set_state(m_gtk_playbin, GST_STATE_NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_gtk_video_window) {
|
||||||
|
m_gtk_video_window->Destroy();
|
||||||
|
m_gtk_video_window = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_gtk_playbin) {
|
||||||
|
gst_object_unref(m_gtk_playbin);
|
||||||
|
m_gtk_playbin = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_gtk_sink) {
|
||||||
|
gst_object_unref(m_gtk_sink);
|
||||||
|
m_gtk_sink = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxMediaCtrl2::PostGtkSinkStateEvent(int id)
|
||||||
|
{
|
||||||
|
wxMediaEvent event(wxEVT_MEDIA_STATECHANGED);
|
||||||
|
event.SetId(id);
|
||||||
|
event.SetEventObject(this);
|
||||||
|
wxPostEvent(this, event);
|
||||||
|
}
|
||||||
|
#endif // defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
|
||||||
#define CLSID_BAMBU_SOURCE L"{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}"
|
#define CLSID_BAMBU_SOURCE L"{233E64FB-2041-4A6C-AFAB-FF9BCF83E7AA}"
|
||||||
|
|
||||||
void wxMediaCtrl2::Load(wxURI url)
|
void wxMediaCtrl2::Load(wxURI url)
|
||||||
@@ -178,6 +443,15 @@ void wxMediaCtrl2::Load(wxURI url)
|
|||||||
if (!factory) {
|
if (!factory) {
|
||||||
factory = gst_element_factory_find("vaapih264dec");
|
factory = gst_element_factory_find("vaapih264dec");
|
||||||
}
|
}
|
||||||
|
if (!factory) {
|
||||||
|
factory = gst_element_factory_find("vah264dec");
|
||||||
|
}
|
||||||
|
if (!factory) {
|
||||||
|
factory = gst_element_factory_find("nvh264dec");
|
||||||
|
}
|
||||||
|
if (!factory) {
|
||||||
|
factory = gst_element_factory_find("v4l2h264dec");
|
||||||
|
}
|
||||||
if (!factory) {
|
if (!factory) {
|
||||||
hasplugins = 0;
|
hasplugins = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -199,30 +473,103 @@ void wxMediaCtrl2::Load(wxURI url)
|
|||||||
#endif
|
#endif
|
||||||
m_error = 0;
|
m_error = 0;
|
||||||
m_loaded = false;
|
m_loaded = false;
|
||||||
wxMediaCtrl::Load(url);
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_use_gtk_sink && m_gtk_playbin) {
|
||||||
#ifdef __WXGTK3__
|
const std::string uri = std::string(url.BuildURI().ToUTF8().data());
|
||||||
|
gst_element_set_state(m_gtk_playbin, GST_STATE_NULL);
|
||||||
|
g_object_set(G_OBJECT(m_gtk_playbin), "uri", uri.c_str(), nullptr);
|
||||||
|
m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
GstStateChangeReturn state = gst_element_set_state(m_gtk_playbin, GST_STATE_PAUSED);
|
||||||
|
if (state == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
m_error = gst_bambu_last_error ? gst_bambu_last_error : 2;
|
||||||
|
PostGtkSinkStateEvent(GetId());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_imp) {
|
||||||
|
m_error = m_native_wayland && !m_gtk_sink_error.empty() ? 104 : 100;
|
||||||
|
m_loaded = false;
|
||||||
|
if (m_native_wayland && !m_gtk_sink_error.empty() && !m_gtk_sink_error_notified) {
|
||||||
|
m_gtk_sink_error_notified = true;
|
||||||
|
const wxString message = m_gtk_sink_error;
|
||||||
|
CallAfter([message] {
|
||||||
|
wxMessageBox(message, _L("Error"), wxOK);
|
||||||
|
});
|
||||||
|
}
|
||||||
wxMediaEvent event(wxEVT_MEDIA_STATECHANGED);
|
wxMediaEvent event(wxEVT_MEDIA_STATECHANGED);
|
||||||
event.SetId(GetId());
|
event.SetId(GetId());
|
||||||
event.SetEventObject(this);
|
event.SetEventObject(this);
|
||||||
wxPostEvent(this, event);
|
wxPostEvent(this, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
wxMediaCtrl::Load(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxMediaCtrl2::Play() { wxMediaCtrl::Play(); }
|
void wxMediaCtrl2::Play()
|
||||||
|
{
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_use_gtk_sink && m_gtk_playbin) {
|
||||||
|
GstStateChangeReturn state = gst_element_set_state(m_gtk_playbin, GST_STATE_PLAYING);
|
||||||
|
if (state == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
m_error = gst_bambu_last_error ? gst_bambu_last_error : 2;
|
||||||
|
m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
PostGtkSinkStateEvent(GetId());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_imp) {
|
||||||
|
m_error = m_native_wayland && !m_gtk_sink_error.empty() ? 104 : 100;
|
||||||
|
if (m_native_wayland && !m_gtk_sink_error.empty() && !m_gtk_sink_error_notified) {
|
||||||
|
m_gtk_sink_error_notified = true;
|
||||||
|
const wxString message = m_gtk_sink_error;
|
||||||
|
CallAfter([message] {
|
||||||
|
wxMessageBox(message, _L("Error"), wxOK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PostGtkSinkStateEvent(GetId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
wxMediaCtrl::Play();
|
||||||
|
}
|
||||||
|
|
||||||
void wxMediaCtrl2::Stop()
|
void wxMediaCtrl2::Stop()
|
||||||
{
|
{
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_use_gtk_sink && m_gtk_playbin) {
|
||||||
|
gst_element_set_state(m_gtk_playbin, GST_STATE_NULL);
|
||||||
|
m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
m_loaded = false;
|
||||||
|
PostGtkSinkStateEvent(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_imp)
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
wxMediaCtrl::Stop();
|
wxMediaCtrl::Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __LINUX__
|
wxMediaState wxMediaCtrl2::GetState()
|
||||||
extern "C" int gst_bambu_last_error;
|
{
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
if (m_use_gtk_sink && m_gtk_playbin)
|
||||||
|
return m_gtk_state;
|
||||||
|
if (!m_imp)
|
||||||
|
return wxMEDIASTATE_STOPPED;
|
||||||
#endif
|
#endif
|
||||||
|
return wxMediaCtrl::GetState();
|
||||||
|
}
|
||||||
|
|
||||||
int wxMediaCtrl2::GetLastError() const
|
int wxMediaCtrl2::GetLastError() const
|
||||||
{
|
{
|
||||||
#ifdef __LINUX__
|
#ifdef __LINUX__
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
if (m_use_gtk_sink && m_error)
|
||||||
|
return m_error;
|
||||||
|
#endif
|
||||||
|
if (m_error)
|
||||||
|
return m_error;
|
||||||
return gst_bambu_last_error;
|
return gst_bambu_last_error;
|
||||||
#else
|
#else
|
||||||
return m_error;
|
return m_error;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
|
|
||||||
wxDECLARE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
wxDECLARE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
||||||
|
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
typedef struct _GstElement GstElement;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __WXMAC__
|
#ifdef __WXMAC__
|
||||||
|
|
||||||
class wxMediaCtrl2 : public wxWindow
|
class wxMediaCtrl2 : public wxWindow
|
||||||
@@ -59,6 +63,7 @@ class wxMediaCtrl2 : public wxMediaCtrl
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
wxMediaCtrl2(wxWindow *parent);
|
wxMediaCtrl2(wxWindow *parent);
|
||||||
|
~wxMediaCtrl2();
|
||||||
|
|
||||||
void Load(wxURI url);
|
void Load(wxURI url);
|
||||||
|
|
||||||
@@ -68,6 +73,8 @@ public:
|
|||||||
|
|
||||||
void SetIdleImage(wxString const & image);
|
void SetIdleImage(wxString const & image);
|
||||||
|
|
||||||
|
wxMediaState GetState();
|
||||||
|
|
||||||
int GetLastError() const;
|
int GetLastError() const;
|
||||||
|
|
||||||
wxSize GetVideoSize() const;
|
wxSize GetVideoSize() const;
|
||||||
@@ -84,6 +91,21 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#if defined(__LINUX__) && defined(__WXGTK__)
|
||||||
|
bool CreateGtkSinkPlayer();
|
||||||
|
void DestroyGtkSinkPlayer();
|
||||||
|
void PostGtkSinkStateEvent(int id = 0);
|
||||||
|
|
||||||
|
bool m_native_wayland = false;
|
||||||
|
bool m_use_gtk_sink = false;
|
||||||
|
wxString m_gtk_sink_error;
|
||||||
|
bool m_gtk_sink_error_notified = false;
|
||||||
|
GstElement *m_gtk_playbin = nullptr;
|
||||||
|
GstElement *m_gtk_sink = nullptr;
|
||||||
|
unsigned int m_gtk_bus_watch_id = 0;
|
||||||
|
wxWindow *m_gtk_video_window = nullptr;
|
||||||
|
wxMediaState m_gtk_state = wxMEDIASTATE_STOPPED;
|
||||||
|
#endif
|
||||||
wxString m_idle_image;
|
wxString m_idle_image;
|
||||||
int m_error = 0;
|
int m_error = 0;
|
||||||
bool m_loaded = false;
|
bool m_loaded = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user