BambuStudio/src/slic3r/GUI/wxMediaCtrl3.cpp

290 lines
7.8 KiB
C++

#include "wxMediaCtrl3.h"
#include "AVVideoDecoder.hpp"
#include "I18N.hpp"
#include "libslic3r/Utils.hpp"
#include <boost/log/trivial.hpp>
#include <wx/dcclient.h>
#ifdef __WIN32__
#include <versionhelpers.h>
#include <wx/msw/registry.h>
#include <shellapi.h>
#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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lk(m_mutex);
m_url.reset();
m_frame = wxImage(m_idle_image);
NotifyStopped();
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<std::mutex> lk(m_mutex);
m_frame = wxImage(m_idle_image);
assert(m_frame.IsOk());
Refresh();
}
}
wxMediaState wxMediaCtrl3::GetState()
{
std::unique_lock<std::mutex> lk(m_mutex);
return m_state;
}
int wxMediaCtrl3::GetLastError()
{
std::unique_lock<std::mutex> lk(m_mutex);
return m_error;
}
wxSize wxMediaCtrl3::GetVideoSize()
{
std::unique_lock<std::mutex> 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();
if (size.x <= 0 || size.y <= 0)
return;
std::unique_lock<std::mutex> 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;
auto size3 = (size - size2) / 2;
if (size2.x != size.x && size2.y != size.y) {
double scale = 1.;
if (size.x * size2.y > size.y * size2.x) {
size3 = {size.x * size2.y / size.y, size2.y};
scale = double(size.y) / size2.y;
} else {
size3 = {size2.x, size.y * size2.x / size.x};
scale = double(size.x) / size2.x;
}
dc.SetUserScale(scale, scale);
size3 = (size3 - size2) / 2;
}
dc.DrawBitmap(m_frame, size3.x, size3.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);
std::unique_lock<std::mutex> lk(m_mutex);
adjust_frame_size(m_frame_size, m_video_size, GetSize());
Refresh();
}
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<std::mutex> 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<wxURI> url;
std::unique_lock<std::mutex> 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) {
auto frame_size = m_frame_size;
lk.unlock();
decoder.decode(sample);
#ifdef _WIN32
wxBitmap bm;
decoder.toWxBitmap(bm, frame_size);
#else
wxImage bm;
decoder.toWxImage(bm, frame_size);
#endif
lk.lock();
if (m_url != url) {
error = 1;
break;
}
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_frame_size = wxDefaultSize;
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);
}