Render video to wxPanel causes Segmentation fault

Do you have a typical platform dependent issue you're battling with ? Ask it here. Make sure you mention your platform, compiler, and wxWidgets version.
Post Reply
Prezla
In need of some credit
In need of some credit
Posts: 5
Joined: Thu Sep 22, 2022 3:09 am

Render video to wxPanel causes Segmentation fault

Post by Prezla »

Hello,

I'm trying to render gstreamer video to a wxPanel after getting wxEVT_CREATE event but I get segmentation fault under Ubuntu 22. It seems to happen when creating a secondary wxFrame window, the crash is in MyFrame::OnRendererWinCreated() line renderWindow->GetHandle(). Note that the render works fine from MainWindow::OnRendererWinCreated() however I need it to work from MyFrame::OnRendererWinCreated() as I need to draw to wxPanels. Also, this code layout works fine under Windows but not under Ubuntu gtk3. How can I render gstreamer video to wxPanels?

Sample code adapted from internet:

Code: Select all

#include "wx/wx.h"

#ifdef __WXGTK__
    #include <gdk/gdkx.h>
    #include <gtk/gtk.h>
#endif

#include <gst/gst.h>
#include <gst/video/videooverlay.h>
class MainWindow;
class MyFrame : public wxFrame
{
public:
	MyFrame(MainWindow*win, const wxString& title, const wxPoint& pos, const wxSize& size);
	~MyFrame();

private:
	void OnRendererWinCreated(wxWindowCreateEvent&);
		
	guintptr _xid;
	wxPanel* _videoPanel;
	wxWindow* _renderWindow;
};

MyFrame::MyFrame( MainWindow* win, const wxString& title, const wxPoint& pos, const wxSize& size) :
    wxFrame((wxFrame*)win, wxID_ANY, title, pos, size),
      _videoPanel(nullptr)
{
 
    this->SetBackgroundColour(*wxBLACK);
    _videoPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(size.x / 2, size.y / 2));
    _videoPanel->SetBackgroundColour(*wxBLACK);

    _renderWindow = new wxWindow(_videoPanel, wxID_ANY);
    wxBoxSizer* szr1 = new wxBoxSizer(wxVERTICAL);
    szr1->Add(_renderWindow, wxSizerFlags(1).Expand().Border(wxBOTTOM));
    _videoPanel->SetSizer(szr1);
    Layout();

    #ifdef __WXGTK__
    this->Bind(wxEVT_CREATE, &MyFrame::OnRendererWinCreated, this);
    #endif
}

MyFrame::~MyFrame()
{    
}

void MyFrame::OnRendererWinCreated(wxWindowCreateEvent& event)
{
#ifdef __WXGTK__
    // This event is no longer needed.
    this->Unbind(wxEVT_CREATE, &MyFrame::OnRendererWinCreated, this);
    // Get the XID for this window.
    // use export GDK_BACKEND=x11 for Ubuntu 22.04 since wxGLCanvas isn't currently supported on Wayland force GDK to use X11
    //_xid = GDK_WINDOW_XID(gtk_widget_get_window(_videoPanel->GetHandle())); // crashes
    _xid = GDK_WINDOW_XID(gtk_widget_get_window(_renderWindow->GetHandle())); // crashes
    
#endif
}



class MainWindow : public wxFrame
{
public:
    MainWindow(const wxString& title);
    ~MainWindow();

private:
    // Event handlers
    void OnRendererWinCreated(wxWindowCreateEvent&);
    void OnPlay(wxCommandEvent&);
    void OnStop(wxCommandEvent&);

    // Helper function
    void LoadVideo();
    void PlayHelper();

    // wx controls
    wxPanel* m_panel;
    wxWindow* m_renderWindow;
    wxButton* m_playButton;
    wxButton* m_stopButton;

    // GStreamer data
    GstElement* m_pipeline;
    guintptr m_xid;
};

MainWindow::MainWindow(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
{
    MyFrame* frame = new MyFrame( this, "Two Way Audio", wxDefaultPosition, wxSize(1920,1080));
    frame->Show(true);   
    /*// Create the UI widgets.
    wxPanel* bg = new wxPanel(this,wxID_ANY);
    m_panel = bg;
    m_renderWindow = new wxWindow(bg,wxID_ANY);
    m_playButton = new wxButton(bg,wxID_ANY,"Play");
    m_stopButton = new wxButton(bg,wxID_ANY,"Stop");
    m_renderWindow->SetBackgroundColour(*wxBLACK);
    m_playButton->Enable(true);
    m_stopButton->Enable(false);

    // Layout the UI.
    wxBoxSizer* szr1 = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer* szr2 = new wxBoxSizer(wxHORIZONTAL);
    szr2->Add(m_playButton, wxSizerFlags(0).Border(wxLEFT|wxRIGHT|wxBOTTOM));
    szr2->Add(m_stopButton, wxSizerFlags(0).Border(wxRIGHT|wxBOTTOM));

    szr1->Add(m_renderWindow, wxSizerFlags(1).Expand().Border(wxBOTTOM));
    szr1->Add(szr2, wxSizerFlags(0));
    bg->SetSizer(szr1);
    Layout();

    // Set up the event handlers.
#ifdef __WXGTK__
    m_renderWindow->Bind(wxEVT_CREATE, &MainWindow::OnRendererWinCreated, this);
    m_playButton->Enable(false);
#endif
    m_playButton->Bind(wxEVT_BUTTON, &MainWindow::OnPlay, this);
    m_stopButton->Bind(wxEVT_BUTTON, &MainWindow::OnStop, this);
*/
    // Initialize GStreamer.
    m_xid = 0;
    m_pipeline = NULL;
    gst_init(NULL, NULL);
}

MainWindow::~MainWindow()
{
    if ( m_pipeline )
    {
        gst_element_set_state(m_pipeline, GST_STATE_NULL);
        gst_object_unref(m_pipeline);
    }
}

void MainWindow::OnRendererWinCreated(wxWindowCreateEvent&)
{
#ifdef __WXGTK__
    // This event is no longer needed.
    m_renderWindow->Unbind(wxEVT_CREATE,&MainWindow::OnRendererWinCreated,this);

    // Get the XID for this window.
    //m_xid = GDK_WINDOW_XID(gtk_widget_get_window(m_renderWindow->GetHandle()));
    m_xid = GDK_WINDOW_XID(gtk_widget_get_window(m_panel->GetHandle()));

    // We can now load and play the video, so enable the play button.
    m_playButton->Enable(true);
#endif
}

void MainWindow::OnPlay(wxCommandEvent&)
{
    if ( m_pipeline )
    {
        PlayHelper();
    }
    else
    {
        LoadVideo();
    }
}

void MainWindow::OnStop(wxCommandEvent&)
{
    if ( m_pipeline )
    {
        GstStateChangeReturn ret =
            gst_element_set_state(m_pipeline, GST_STATE_PAUSED);

        if ( ret == GST_STATE_CHANGE_FAILURE )
        {
            wxLogWarning("Unable to set the pipeline to the paused state.");
            gst_object_unref(m_pipeline);
            m_pipeline = NULL;
            m_playButton->Enable(true);
            m_stopButton->Enable(false);
        }
        else
        {
            m_playButton->Enable(true);
            m_stopButton->Enable(false);
        }
    }
}

void MainWindow::LoadVideo()
{
    // Create the elements
    GstElement *source = gst_element_factory_make("videotestsrc", "source");
#ifdef __WXGTK__
    GstElement *sink = gst_element_factory_make("xvimagesink", "sink");
    gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(sink), m_xid);
#elif defined __WXMSW__
    GstElement *sink = gst_element_factory_make("d3dvideosink", "sink");
    WXWidget hwnd = m_renderWindow->GetHandle();
    gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(sink),
                                        reinterpret_cast<guintptr>(hwnd));
#endif

    //Create the empty pipeline
    m_pipeline = gst_pipeline_new ("test-pipeline");

    if ( !m_pipeline || !source || !sink )
    {
        wxLogError("Not all elements could be created.");
        return;
    }

    // Build the pipeline
    gst_bin_add_many(GST_BIN(m_pipeline), source, sink, NULL);
    if ( gst_element_link(source, sink) != TRUE )
    {
        wxLogWarning("Elements could not be linked.");
        gst_object_unref(m_pipeline);
        m_pipeline = NULL;
        return;
    }

    // Modify the source's properties
    g_object_set(source, "pattern", 0, NULL);

    PlayHelper();
}

void MainWindow::PlayHelper()
{
    GstStateChangeReturn ret =
        gst_element_set_state(m_pipeline, GST_STATE_PLAYING);

    if ( ret == GST_STATE_CHANGE_FAILURE )
    {
        wxLogWarning("Unable to set the pipeline to the playing state.");
        gst_object_unref(m_pipeline);
        m_pipeline = NULL;
        m_playButton->Enable(true);
        m_stopButton->Enable(false);
    }
    else
    {
        m_playButton->Enable(false);
        m_stopButton->Enable(true);
    }
}

class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        MainWindow* mainWindow = new MainWindow("wxWidgets GStreamer demo");
        mainWindow->Show();
        return true;
    }
};

wxIMPLEMENT_APP(MyApp);

Thanks a bunch!!
User avatar
doublemax
Moderator
Moderator
Posts: 17608
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Render video to wxPanel causes Segmentation fault

Post by doublemax »

the crash is in MyFrame::OnRendererWinCreated() line renderWindow->GetHandle().
Did you confirm that renderWindow is already initialized when this gets called?
Note that the render works fine from MainWindow::OnRendererWinCreated()
There is one significant difference between the two versions. In MyFrame you catch the wxEVT_CREATE event for the frame. In MainWindow you catch the wxEVT_CREATE event for m_renderWindow.
Use the source, Luke!
Prezla
In need of some credit
In need of some credit
Posts: 5
Joined: Thu Sep 22, 2022 3:09 am

Re: Render video to wxPanel causes Segmentation fault

Post by Prezla »

Thank you doublemax, much appreciated. Your analysis was spot on as soon as I used this->Bind(...) to _videoPanel-Bind(...) it worked.
Post Reply