首页 > 解决方案 > 如何将 libvlcpp 嵌入到 wx(v3) 面板/框架中

问题描述

我想使用 wx(版本 3 及更高版本)和 libvlcpp 制作简单的跨平台播放器,但我不知道如何将 libvlcpp 窗口绑定到 wx 中的面板(即框架内)中。

 main_window *frame = new main_window(nullptr,wxID_ANY,wxT("Player"));

VLC::Instance instance = VLC::Instance(0, nullptr);
VLC::Media media = VLC::Media(instance, "/home/projects/vlc_test/ap.mp4", VLC::Media::FromPath);
VLC::MediaPlayer mp = VLC::MediaPlayer(media);
// frame->SetExtraStyle(4);
mp.setHwnd(frame->m_panel_vlc->GetHandle());  // this is not working !!!, btw m_panel_vlc is a public member

while(mp.play()){}

vlc_stop(mp); // a wraper for stopping

frame->Show(true); 

一些输出:

[h264 @ 0x7f6b087ffd00] [0000000002454130] main input error: input control fifo overflow, trashing type=0
thread_get_buffer() failed
[0000000002454130] main input error: input control fifo overflow, trashing type=0
[h264 @ 0x7f6b087ffd00] decode_slice_header error
[h264 @ 0x7f6b087ffd00] no frame!
[0000000002454130] main input error: input control fifo overflow, trashing type=0```

标签: c++wxwidgetslibvlc

解决方案


这是使用 vlcpp 绘制到 wxWidgets 窗口的完整示例(至少对于 Windows)。它基于libvlc 示例,但我将其更改为使用 vlcpp 并使用一些更现代的 wxWidgets 功能。

#include <wx/wx.h>
#include <wx/filename.h>

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

#include <vlcpp/vlc.hpp>

#include <climits>

#define TIMELINE_MAX (INT_MAX-9)
#define VOLUME_MAX 100

wxDEFINE_EVENT(vlcEVT_POS,wxThreadEvent);
wxDEFINE_EVENT(vlcEVT_END,wxThreadEvent);

static wxEvtHandler* gs_handler = NULL;

void OnPositionChanged_VLC(float f)
{
    if ( gs_handler )
    {
        wxThreadEvent* event = new wxThreadEvent(vlcEVT_POS);
        event->SetPayload<float>(f);
        wxQueueEvent(gs_handler,event);
    }
}

void OnEndReached_VLC()
{
    if ( gs_handler )
    {
        wxThreadEvent* event = new wxThreadEvent(vlcEVT_END);
        wxQueueEvent(gs_handler,event);
    }
}

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

private:
    // Event handlers
    void OnPositionChanged(wxThreadEvent& event);
    void OnEndReached(wxThreadEvent& event);

    void OnOpen(wxCommandEvent& event);
    void OnPlayPause(wxCommandEvent& event);
    void OnStop(wxCommandEvent& event);
    void OnPositionChanged_USR(wxCommandEvent& event);
    void OnVolumeChanged(wxCommandEvent& event);
    void OnVolumeClicked(wxMouseEvent& event);
    void OnTimelineClicked(wxMouseEvent& event);
    void OnRendererWinCreated(wxWindowCreateEvent& event);

    // Helper functions.
    void Play();
    void Pause();
    void Stop();
    void SetTimeline(float value);
    void BindTimeline();
    void UnbindTimeline();

    // Video player controls.
    wxButton* m_playPauseButton;
    wxButton* m_stopButton;
    wxSlider* m_timeline;
    wxSlider* m_volumeSlider;
    wxWindow* m_playerWidget;

    // VLC objects
    VLC::Instance m_vlc;
    VLC::MediaPlayer m_player;
};

MainWindow::MainWindow(const wxString& title) : wxFrame(NULL, wxID_ANY, title)
{
    // Setup menubar.
    wxMenuBar* menubar = new wxMenuBar;
    wxMenu*file = new wxMenu;
    file->Append(wxID_OPEN, "&Open");
    menubar->Append(file, "&File");
    SetMenuBar(menubar);
    Bind(wxEVT_MENU, &MainWindow::OnOpen, this, wxID_OPEN, wxID_OPEN);

    // Create the main background panel for the frame.
    wxPanel* bgPanel = new wxPanel(this, wxID_ANY);

    // Create the window the VLC will draw to.
    m_playerWidget = new wxWindow(bgPanel, wxID_ANY);

    // Create the timeline slider.
    m_timeline = new wxSlider(bgPanel, wxID_ANY, 0, 0, TIMELINE_MAX);

    // Create play button, the stop button, and the volume slider.
    m_playPauseButton = new wxButton(bgPanel, wxID_ANY, "Play");
    m_stopButton = new wxButton(bgPanel, wxID_ANY, "Stop");
    m_volumeSlider = new wxSlider(bgPanel, wxID_ANY, VOLUME_MAX, 0, VOLUME_MAX);


    // Set the video window black and disable the timeline and buttons.
    m_playerWidget->SetBackgroundColour(*wxBLACK);
    m_timeline->Enable(false);
    m_playPauseButton->Enable(false);
    m_stopButton->Enable(false);


    // Use sizers to arrange the controls on the frame.
    wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
    vbox->Add(m_playerWidget, wxSizerFlags(1).Expand().Border(wxALL));
    vbox->Add(m_timeline, wxSizerFlags(0).Expand().Border(wxLEFT|wxRIGHT|wxBOTTOM));

    wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
    hbox->Add(m_playPauseButton, wxSizerFlags(0).Border(wxLEFT|wxRIGHT|wxBOTTOM));
    hbox->Add(m_stopButton,wxSizerFlags(0).Border(wxRIGHT|wxBOTTOM));
    hbox->AddStretchSpacer();
    hbox->Add(m_volumeSlider,wxSizerFlags(0).Border(wxRIGHT|wxBOTTOM));
    vbox->Add(hbox,wxSizerFlags(0).Expand());

    bgPanel->SetSizer(vbox);
    Layout();


    // Bind event handlers for the wxWidgets controls.
    m_playPauseButton->Bind(wxEVT_BUTTON, &MainWindow::OnPlayPause, this);
    m_stopButton->Bind(wxEVT_BUTTON, &MainWindow::OnStop, this);
    m_volumeSlider->Bind(wxEVT_SLIDER,&MainWindow::OnVolumeChanged,this);

    BindTimeline();
    m_timeline->Bind(wxEVT_LEFT_UP, &MainWindow::OnTimelineClicked,this);
    m_volumeSlider->Bind(wxEVT_LEFT_UP, &MainWindow::OnVolumeClicked,this);

    // Bind the events that will be thrown from VLC callbacks.
    Bind(vlcEVT_POS, &MainWindow::OnPositionChanged, this);
    Bind(vlcEVT_END, &MainWindow::OnEndReached, this);


    // Set up the VLC objects.
    m_vlc = VLC::Instance(0, nullptr);
    m_player = VLC::MediaPlayer(m_vlc);

#ifdef __WXGTK__
    // On GTK+, we have to wait until the window is actually created before we
    // can tell VLC to use it for output. So wait for the window create event.
    m_playerWidget->Bind(wxEVT_CREATE, &MainWindow::OnRendererWinCreated, this);
#elif defined(__WXMSW__)
    m_player.setHwnd(m_playerWidget->GetHandle());
#endif

    // Get the player's event manager and register to callbacks.
    VLC::MediaPlayerEventManager& eventManager = m_player.eventManager();
    eventManager.onPositionChanged(OnPositionChanged_VLC);
    eventManager.onEndReached(OnEndReached_VLC);

    // Set this frame to a global variable so that it can be used with the
    // VLC callbacks.
    gs_handler = this;
}

void MainWindow::OnRendererWinCreated(wxWindowCreateEvent& event)
{
#ifdef __WXGTK__
    m_player.setXwindow(gdk_x11_window_get_xid(gtk_widget_get_window(m_playerWidget->GetHandle())));

    m_playerWidget->Unbind(wxEVT_CREATE,&MainWindow::OnRendererWinCreated,this);
#endif
}

void MainWindow::OnPositionChanged(wxThreadEvent& event)
{
    float factor = event.GetPayload<float>();
    SetTimeline(factor);
}

void MainWindow::OnEndReached(wxThreadEvent& event)
{
    Stop();
}

void MainWindow::OnOpen(wxCommandEvent& event)
{
    wxFileDialog openFileDialog(this, "Choose File");

    if (openFileDialog.ShowModal() == wxID_CANCEL)
    {
        return;
    }
    else
    {
        wxFileName filename = wxFileName::FileName(openFileDialog.GetPath());
        filename.MakeRelativeTo();
        std::string s2(filename.GetFullPath().utf8_str());

        VLC::Media media(m_vlc, s2, VLC::Media::FromPath);
        m_player.setMedia(media);
        Play();
    }
}

void MainWindow::OnPlayPause(wxCommandEvent& event)
{
    if ( m_player.isPlaying () )
    {
        Pause();
    }
    else
    {
        Play();
    }
}

void MainWindow::OnStop(wxCommandEvent& event)
{
    Stop();
}

void MainWindow::OnPositionChanged_USR(wxCommandEvent& event)
{
    m_player.setPosition((float) event.GetInt() / (float) TIMELINE_MAX);
}

void MainWindow::OnVolumeChanged(wxCommandEvent& event)
{
    m_player.setVolume(m_volumeSlider->GetValue());
}

void MainWindow::OnVolumeClicked(wxMouseEvent& event)
{
    wxSize size = m_volumeSlider->GetSize();
    float position = (float) event.GetX() / (float) size.GetWidth();
    int volume = static_cast<int>(position*VOLUME_MAX);
    m_volumeSlider->SetValue(volume);
    m_player.setVolume(volume);
    event.Skip();
}

void MainWindow::OnTimelineClicked(wxMouseEvent& event)
{
    wxSize size = m_timeline->GetSize();
    float position = (float) event.GetX() / (float) size.GetWidth();
    m_player.setPosition(position);
    SetTimeline(position);
    event.Skip();
}

void MainWindow::Play()
{;
    m_player.play();
    m_playPauseButton->SetLabel("Pause");
    m_playPauseButton->Enable(true);
    m_stopButton->Enable(true);
    m_timeline->Enable(true);
}

void MainWindow::Pause()
{
    m_player.pause();
    m_playPauseButton->SetLabel("Play");
}

void MainWindow::Stop()
{
    Pause();
#if LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)
    m_player.stopAsync();
#else
    m_player.stop();
#endif
    m_stopButton->Enable(false);
    SetTimeline(0.0);
    m_timeline->Enable(false);
}

void MainWindow::SetTimeline(float value) {
    if ( value < 0.0 )
    {
        value = 0.0;
    }
    if ( value > 1.0 )
    {
        value = 1.0;
    }

    UnbindTimeline();
    m_timeline->SetValue((int) (value * TIMELINE_MAX));
    BindTimeline();
}

void MainWindow::BindTimeline()
{
    m_timeline->Bind(wxEVT_SLIDER, &MainWindow::OnPositionChanged_USR, this);
}

void MainWindow::UnbindTimeline()
{
    m_timeline->Unbind(wxEVT_SLIDER, &MainWindow::OnPositionChanged_USR, this);
}

class MyApp : public wxApp {
    public:
        virtual bool OnInit();
};

bool MyApp::OnInit() {
    MainWindow* mainWindow = new MainWindow("wxWidgets libVLC demo");
    mainWindow->Show();
    return true;
}

wxIMPLEMENT_APP(MyApp);

在 linux mint 上使用 wxGTK3,它看起来像这样:

在此处输入图像描述

我从来没有使用过 vlcpp,所以我不是这里的专家。但它不允许您将数据对象传递给回调。使用 libvlc 的 c 接口,您可以使用此数据对象传递 wxWidgets 事件处理程序,然后在回调中使用该事件处理程序将 vlc 事件传递给 wxWidgets。由于 vlcpp 不允许您传递数据对象,因此我不得不为 wxWidgets 事件处理程序使用一个丑陋的全局变量。

除非我遗漏了某些东西并且有一种方法可以为 vlc 回调传递数据对象,否则这看起来像是 vlcpp api 中的一个真正重大缺陷。


推荐阅读