#include "wxMediaCtrl3.h" #include "AVVideoDecoder.hpp" #include "I18N.hpp" #include "libslic3r/Utils.hpp" #ifdef __WIN32__ #include #include #include #endif //wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent); BEGIN_EVENT_TABLE(wxMediaCtrl3, wxWindow) // catch paint events EVT_PAINT(wxMediaCtrl3::paintEvent) END_EVENT_TABLE() struct StaticBambuLib : BambuLib { static StaticBambuLib &get(BambuLib *); }; wxMediaCtrl3::wxMediaCtrl3(wxWindow *parent) : wxWindow(parent, wxID_ANY) , BambuLib(StaticBambuLib::get(this)) , m_thread([this] { PlayThread(); }) { SetBackgroundColour("#000001ff"); } wxMediaCtrl3::~wxMediaCtrl3() { { std::unique_lock lk(m_mutex); m_url.reset(new wxURI); m_frame = wxImage(m_idle_image); m_cond.notify_all(); } m_thread.join(); } void wxMediaCtrl3::Load(wxURI url) { std::unique_lock lk(m_mutex); m_video_size = wxDefaultSize; m_error = 0; m_url.reset(new wxURI(url)); m_cond.notify_all(); } void wxMediaCtrl3::Play() { std::unique_lock lk(m_mutex); if (m_state != wxMEDIASTATE_PLAYING) { m_state = wxMEDIASTATE_PLAYING; wxMediaEvent event(wxEVT_MEDIA_STATECHANGED); event.SetId(GetId()); event.SetEventObject(this); wxPostEvent(this, event); } } void wxMediaCtrl3::Stop() { std::unique_lock lk(m_mutex); m_url.reset(); m_frame = wxImage(m_idle_image); m_cond.notify_all(); Refresh(); } void wxMediaCtrl3::SetIdleImage(wxString const &image) { if (m_idle_image == image) return; m_idle_image = image; if (m_url == nullptr) { std::unique_lock lk(m_mutex); m_frame = wxImage(m_idle_image); assert(m_frame.IsOk()); Refresh(); } } wxMediaState wxMediaCtrl3::GetState() { std::unique_lock lk(m_mutex); return m_state; } int wxMediaCtrl3::GetLastError() { std::unique_lock lk(m_mutex); return m_error; } wxSize wxMediaCtrl3::GetVideoSize() { std::unique_lock lk(m_mutex); return m_video_size; } wxSize wxMediaCtrl3::DoGetBestSize() const { return {-1, -1}; } static void adjust_frame_size(wxSize & frame, wxSize const & video, wxSize const & window) { if (video.x * window.y < video.y * window.x) frame = { video.x * window.y / video.y, window.y }; else frame = { window.x, video.y * window.x / video.x }; } void wxMediaCtrl3::paintEvent(wxPaintEvent &evt) { wxPaintDC dc(this); auto size = GetSize(); std::unique_lock lk(m_mutex); if (!m_frame.IsOk()) return; auto size2 = m_frame.GetSize(); if (size2.x != m_frame_size.x && size2.y == m_frame_size.y) size2.x = m_frame_size.x; if (size2.x != size.x && size2.y != size.y) { auto scale = std::min(double(size.x) / size2.x, double(size.y) / size2.y); dc.SetUserScale(scale, scale); adjust_frame_size(size2, size2, size); } size2 = (size - size2) / 2; dc.DrawBitmap(m_frame, size2.x, size2.y); } void wxMediaCtrl3::DoSetSize(int x, int y, int width, int height, int sizeFlags) { wxWindow::DoSetSize(x, y, width, height, sizeFlags); if (sizeFlags & wxSIZE_USE_EXISTING) return; wxMediaCtrl_OnSize(this, m_video_size, width, height); } void wxMediaCtrl3::bambu_log(void *ctx, int level, tchar const *msg2) { #ifdef _WIN32 wxString msg(msg2); #else wxString msg = wxString::FromUTF8(msg2); #endif if (level == 1) { if (msg.EndsWith("]")) { int n = msg.find_last_of('['); if (n != wxString::npos) { long val = 0; wxMediaCtrl3 *ctrl = (wxMediaCtrl3 *) ctx; if (msg.SubString(n + 1, msg.Length() - 2).ToLong(&val)) { std::unique_lock lk(ctrl->m_mutex); ctrl->m_error = (int) val; } } } else if (msg.Contains("stat_log")) { wxCommandEvent evt(EVT_MEDIA_CTRL_STAT); wxMediaCtrl3 *ctrl = (wxMediaCtrl3 *) ctx; evt.SetEventObject(ctrl); evt.SetString(msg.Mid(msg.Find(' ') + 1)); wxPostEvent(ctrl, evt); } } BOOST_LOG_TRIVIAL(info) << msg.ToUTF8().data(); } void wxMediaCtrl3::PlayThread() { using namespace std::chrono_literals; std::shared_ptr url; std::unique_lock lk(m_mutex); while (true) { m_cond.wait(lk, [this, &url] { return m_url != url; }); url = m_url; if (url == nullptr) continue; if (!url->HasScheme()) break; lk.unlock(); Bambu_Tunnel tunnel = nullptr; int error = Bambu_Create(&tunnel, m_url->BuildURI().ToUTF8()); if (error == 0) { Bambu_SetLogger(tunnel, &wxMediaCtrl3::bambu_log, this); error = Bambu_Open(tunnel); if (error == 0) error = Bambu_would_block; } lk.lock(); while (error == int(Bambu_would_block)) { m_cond.wait_for(lk, 100ms); if (m_url != url) { error = 1; break; } lk.unlock(); error = Bambu_StartStream(tunnel, true); lk.lock(); } Bambu_StreamInfo info; if (error == 0) error = Bambu_GetStreamInfo(tunnel, 0, &info); AVVideoDecoder decoder; if (error == 0) { decoder.open(info); m_video_size = { info.format.video.width, info.format.video.height }; adjust_frame_size(m_frame_size, m_video_size, GetSize()); NotifyStopped(); } Bambu_Sample sample; while (error == 0) { lk.unlock(); error = Bambu_ReadSample(tunnel, &sample); lk.lock(); while (error == int(Bambu_would_block)) { m_cond.wait_for(lk, 100ms); if (m_url != url) { error = 1; break; } lk.unlock(); error = Bambu_ReadSample(tunnel, &sample); lk.lock(); } if (error == 0) { if (m_url != url) { error = 1; break; } lk.unlock(); wxBitmap bm; decoder.decode(sample); decoder.toWxBitmap(bm, m_frame_size); lk.lock(); if (bm.IsOk()) m_frame = bm; CallAfter([this] { Refresh(); }); } } if (tunnel) { lk.unlock(); Bambu_Close(tunnel); Bambu_Destroy(tunnel); tunnel = nullptr; lk.lock(); } if (m_url == url) m_error = error; m_video_size = wxDefaultSize; NotifyStopped(); } } void wxMediaCtrl3::NotifyStopped() { m_state = wxMEDIASTATE_STOPPED; wxMediaEvent event(wxEVT_MEDIA_STATECHANGED); event.SetId(GetId()); event.SetEventObject(this); wxPostEvent(this, event); }