diff --git a/CMakeLists.txt b/CMakeLists.txt index 57f8965dd..459cfebb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. # We pick it from environment if it is not defined in another way if(WIN32) + find_package(PkgConfig REQUIRED) if(NOT DEFINED WIN10SDK_PATH) if(DEFINED ENV{WIN10SDK_PATH}) set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") @@ -362,7 +363,7 @@ endif() function(slic3r_remap_configs targets from_Cfg to_Cfg) if(MSVC) string(TOUPPER ${from_Cfg} from_CFG) - + foreach(tgt ${targets}) if(TARGET ${tgt}) set_target_properties(${tgt} PROPERTIES MAP_IMPORTED_CONFIG_${from_CFG} ${to_Cfg}) @@ -503,7 +504,7 @@ add_custom_target(gettext_make_pot COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost -f "${BBL_L18N_DIR}/list.txt" -o "${BBL_L18N_DIR}/BambuStudio.pot" - COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR} + COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMENT "Generate pot file from strings in the source tree" ) @@ -619,6 +620,10 @@ function(bambustudio_copy_dlls target config postfix output_dlls) ${CMAKE_PREFIX_PATH}/bin/occt/TKXDESTEP.dll ${CMAKE_PREFIX_PATH}/bin/occt/TKXSBase.dll ${CMAKE_PREFIX_PATH}/bin/freetype.dll + ${CMAKE_PREFIX_PATH}/bin/avcodec-59.dll + ${CMAKE_PREFIX_PATH}/bin/swresample-4.dll + ${CMAKE_PREFIX_PATH}/bin/swscale-6.dll + ${CMAKE_PREFIX_PATH}/bin/avutil-57.dll DESTINATION ${_out_dir}) set(${output_dlls} @@ -654,11 +659,34 @@ function(bambustudio_copy_dlls target config postfix output_dlls) ${_out_dir}/TKXSBase.dll ${_out_dir}/freetype.dll + ${_out_dir}/avcodec-59.dll + ${_out_dir}/swresample-4.dll + ${_out_dir}/swscale-6.dll + ${_out_dir}/avutil-57.dll PARENT_SCOPE ) - + endfunction() +function(bambustudio_copy_sos target config postfix output_sos) + + set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}") + message ("set out_dir to CMAKE_CURRENT_BINARY_DIR: ${_out_dir}") + + file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so + ${CMAKE_PREFIX_PATH}/lib/libavutil.so + ${CMAKE_PREFIX_PATH}/lib/libswscale.so + ${CMAKE_PREFIX_PATH}/lib/libswresample.so + DESTINATION ${_out_dir}) + + set(${output_dlls} + ${_out_dir}/libavcodec.so + ${_out_dir}/libavutil.so + ${_out_dir}/libswscale.so + ${_out_dir}/libswresample.so + PARENT_SCOPE + ) +endfunction() # libslic3r, BambuStudio GUI and the BambuStudio executable. add_subdirectory(src) diff --git a/resources/images/live_stream_default.png b/resources/images/live_stream_default.png index 9256213aa..c1aa90bf1 100644 Binary files a/resources/images/live_stream_default.png and b/resources/images/live_stream_default.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8aaa71386..c20a0dda3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -82,7 +82,7 @@ if (SLIC3R_GUI) list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat) list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES}) endif () - + # This is an issue in the new wxWidgets cmake build, doesn't deal with librt find_library(LIBRT rt) if(LIBRT) @@ -218,6 +218,16 @@ if (WIN32) else () + if (NOT APPLE) + set(output_sos_Release "") + set(output_sos_Debug "") + add_custom_target(BambuStudioSosCopy ALL DEPENDS BambuStudio) + if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + bambustudio_copy_sos(BambuStudioSosCopy "Debug" "d" output_sos_Debug) + else() + bambustudio_copy_sos(BambuStudioSosCopy "Release" "" output_sos_Release) + endif() + endif() if (APPLE AND NOT CMAKE_MACOSX_BUNDLE) # On OSX, the name of the binary matches the name of the Application. add_custom_command(TARGET BambuStudio POST_BUILD @@ -275,5 +285,9 @@ if (WIN32) endif () install(FILES ${output_dlls_${build_type}} DESTINATION "${CMAKE_INSTALL_PREFIX}") else () + if (APPLE) + else() + install(FILES ${output_sos_${build_type}} DESTINATION "${CMAKE_INSTALL_PREFIX}") + endif() install(TARGETS BambuStudio RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}) endif () diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 9b57201c8..e695acd48 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -546,12 +546,17 @@ if (APPLE) GUI/InstanceCheckMac.h GUI/wxMediaCtrl2.mm GUI/wxMediaCtrl2.h + GUI/wxMediaCtrl3.h ) FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration) else () list(APPEND SLIC3R_GUI_SOURCES + GUI/AVVideoDecoder.cpp + GUI/AVVideoDecoder.hpp GUI/wxMediaCtrl2.cpp GUI/wxMediaCtrl2.h + GUI/wxMediaCtrl3.cpp + GUI/wxMediaCtrl3.h ) endif () @@ -608,6 +613,15 @@ if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () +if (NOT APPLE) + pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET + libavcodec + libswscale + libavutil + ) + target_link_libraries(libslic3r_gui PkgConfig::LIBAV) +endif() + # We need to implement some hacks for wxWidgets and touch the underlying GTK # layer and sub-libraries. This forces us to use the include locations and # link these libraries. diff --git a/src/slic3r/GUI/AVVideoDecoder.cpp b/src/slic3r/GUI/AVVideoDecoder.cpp new file mode 100644 index 000000000..cb9ed33a2 --- /dev/null +++ b/src/slic3r/GUI/AVVideoDecoder.cpp @@ -0,0 +1,126 @@ +#include "AVVideoDecoder.hpp" + +extern "C" +{ + #include + #include +} + +AVVideoDecoder::AVVideoDecoder() +{ + codec_ctx_ = avcodec_alloc_context3(nullptr); +} + +AVVideoDecoder::~AVVideoDecoder() +{ + if (sws_ctx_) + sws_freeContext(sws_ctx_); + if (frame_) + av_frame_free(&frame_); + if (codec_ctx_) + avcodec_free_context(&codec_ctx_); +} + +int AVVideoDecoder::open(Bambu_StreamInfo const &info) +{ + auto codec_id = info.sub_type == AVC1 ? AV_CODEC_ID_H264 : AV_CODEC_ID_MJPEG; + auto codec = avcodec_find_decoder(codec_id); + if (codec == nullptr) { + fprintf(stderr, "Unsupported codec!\n"); + return -1; // Codec not found + } + /* open the coderc */ + if (avcodec_open2(codec_ctx_, codec, nullptr) < 0) { + fprintf(stderr, "could not open codec\n"); + return -1; + } + + // Allocate an AVFrame structure + frame_ = av_frame_alloc(); + if (frame_ == nullptr) + return -1; + + return 0; +} + +int AVVideoDecoder::decode(const Bambu_Sample &sample) +{ + auto pkt = av_packet_alloc(); + int ret = av_new_packet(pkt, sample.size); + if (ret == 0) + memcpy(pkt->data, sample.buffer, size_t(sample.size)); + got_frame_ = avcodec_receive_frame(codec_ctx_, frame_) == 0; + ret = avcodec_send_packet(codec_ctx_, pkt); + return ret; +} + +int AVVideoDecoder::flush() +{ + int ret = avcodec_send_packet(codec_ctx_, nullptr); + got_frame_ = avcodec_receive_frame(codec_ctx_, frame_) == 0; + return ret; +} + +void AVVideoDecoder::close() +{ +} + +bool AVVideoDecoder::toWxImage(wxImage &image, wxSize const &size2) +{ + if (!got_frame_) + return false; + + auto size = size2; + if (!size.IsFullySpecified()) + size = {frame_->width, frame_->height }; + if (size.GetWidth() & 0x0f) + size.SetWidth((size.GetWidth() & ~0x0f) + 0x10); + AVPixelFormat wxFmt = AV_PIX_FMT_RGB24; + sws_ctx_ = sws_getCachedContext(sws_ctx_, + frame_->width, frame_->height, AVPixelFormat(frame_->format), + size.GetWidth(), size.GetHeight(), wxFmt, + SWS_POINT, // SWS_FAST_BILINEAR //SWS_BICUBIC + nullptr, nullptr, nullptr); + uint8_t *data = (uint8_t*)malloc(size.GetWidth() * size.GetHeight() * 3); + if (data == nullptr) + return false; + uint8_t * datas[] = {data }; + int strides[] = {size.GetWidth() * 3}; + int result_h = sws_scale(sws_ctx_, frame_->data, frame_->linesize, 0, frame_->height, datas, strides); + if (result_h != size.GetHeight()) { + delete[] data; + return false; + } + image = wxImage(size.GetWidth(), size.GetHeight(), data); + return true; +} + +bool AVVideoDecoder::toWxBitmap(wxBitmap &bitmap, wxSize const &size2) +{ + if (!got_frame_) + return false; + + auto size = size2; + if (!size.IsFullySpecified()) + size = {frame_->width, frame_->height }; + if (size.GetWidth() & 0x0f) + size.SetWidth((size.GetWidth() & ~0x0f) + 0x10); + AVPixelFormat wxFmt = AV_PIX_FMT_RGB32; + sws_ctx_ = sws_getCachedContext(sws_ctx_, + frame_->width, frame_->height, AVPixelFormat(frame_->format), + size.GetWidth(), size.GetHeight(), wxFmt, + SWS_POINT, // SWS_FAST_BILINEAR //SWS_BICUBIC + nullptr, nullptr, nullptr); + uint8_t *data = (uint8_t*)malloc(size.GetWidth() * size.GetHeight() * 4); + if (data == nullptr) + return false; + uint8_t *datas[] = {data}; + int strides[] = {size.GetWidth() * 4}; + int result_h = sws_scale(sws_ctx_, frame_->data, frame_->linesize, 0, frame_->height, datas, strides); + if (result_h != size.GetHeight()) { + delete[] data; + return false; + } + bitmap = wxBitmap((char *) data, size.GetWidth(), size.GetHeight(), 32); + return true; +} diff --git a/src/slic3r/GUI/AVVideoDecoder.hpp b/src/slic3r/GUI/AVVideoDecoder.hpp new file mode 100644 index 000000000..7c6e5e7e7 --- /dev/null +++ b/src/slic3r/GUI/AVVideoDecoder.hpp @@ -0,0 +1,39 @@ +#ifndef AVVIDEODECODER_HPP +#define AVVIDEODECODER_HPP + +#include "BambuTunnel.h" + +extern "C" { + #include + #include +} +class wxBitmap; + +class AVVideoDecoder +{ +public: + AVVideoDecoder(); + + ~AVVideoDecoder(); + +public: + int open(Bambu_StreamInfo const &info); + + int decode(Bambu_Sample const &sample); + + int flush(); + + void close(); + + bool toWxImage(wxImage &image, wxSize const &size); + + bool toWxBitmap(wxBitmap &bitmap, wxSize const & size); + +private: + AVCodecContext *codec_ctx_ = nullptr; + AVFrame * frame_ = nullptr; + SwsContext * sws_ctx_ = nullptr; + bool got_frame_ = false; +}; + +#endif // AVVIDEODECODER_HPP diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 3c3709573..210e6b543 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -49,6 +49,7 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w SetLabel("MediaPlayCtrl"); SetBackgroundColour(*wxWHITE); m_media_ctrl->Bind(wxEVT_MEDIA_STATECHANGED, &MediaPlayCtrl::onStateChanged, this); + m_media_ctrl->SetIdleImage(from_u8(resources_dir() + "/images/live_stream_default.png")); m_button_play = new Button(this, "", "media_play", wxBORDER_NONE); m_button_play->SetCanFocus(false); @@ -332,7 +333,7 @@ void MediaPlayCtrl::Play() // !m_lan_mode && !m_remote_proto && m_lan_proto == LVL_None (x) if (m_lan_proto <= MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto)) { - Stop(m_lan_proto == MachineObject::LVL_None + Stop(m_lan_proto == MachineObject::LVL_None ? _L("Problem occured. Please update the printer firmware and try again.") : _L("LAN Only Liveview is off. Please turn on the liveview on printer screen.")); return; @@ -366,7 +367,7 @@ void MediaPlayCtrl::Play() url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); CallAfter([this, m, url] { if (m != m_machine) { @@ -436,7 +437,7 @@ void MediaPlayCtrl::Stop(wxString const &msg, wxString const &msg2) auto tunnel = m_url.empty() ? "" : into_u8(wxURI(m_url).GetPath()).substr(1); if (auto n = tunnel.find_first_of("/_"); n != std::string::npos) tunnel = tunnel.substr(0, n); - if (last_state != wxMEDIASTATE_PLAYING && m_failed_code != 0 + if (last_state != wxMEDIASTATE_PLAYING && m_failed_code != 0 && m_last_failed_codes.find(m_failed_code) == m_last_failed_codes.end() && (m_user_triggered || m_failed_retry > 3)) { json j; @@ -604,7 +605,7 @@ void MediaPlayCtrl::ToggleStream() url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url, + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url, {"?uid=", "authkey=", "passwd=", "license=", "token="}); CallAfter([this, m, url] { if (m != m_machine) return; @@ -624,8 +625,8 @@ void MediaPlayCtrl::ToggleStream() }); } -void MediaPlayCtrl::msw_rescale() { - m_button_play->Rescale(); +void MediaPlayCtrl::msw_rescale() { + m_button_play->Rescale(); } void MediaPlayCtrl::jump_to_play() @@ -841,15 +842,15 @@ bool MediaPlayCtrl::start_stream_service(bool *need_install) if (!boost::filesystem::exists(file_dll) || boost::filesystem::last_write_time(file_dll) != boost::filesystem::last_write_time(file_dll2)) boost::filesystem::copy_file(file_dll2, file_dll, boost::filesystem::copy_option::overwrite_if_exists); } - boost::process::child process_source(file_source, file_url2.ToStdWstring(), boost::process::start_dir(tools_dir), - boost::process::windows::create_no_window, + boost::process::child process_source(file_source, file_url2.ToStdWstring(), boost::process::start_dir(tools_dir), + boost::process::windows::create_no_window, boost::process::std_out > intermediate, boost::process::limit_handles); - boost::process::child process_ffmpeg(file_ffmpeg, configss, boost::process::windows::create_no_window, + boost::process::child process_ffmpeg(file_ffmpeg, configss, boost::process::windows::create_no_window, boost::process::std_in < intermediate, boost::process::limit_handles); #else boost::filesystem::permissions(file_source, boost::filesystem::owner_exe | boost::filesystem::add_perms); boost::filesystem::permissions(file_ffmpeg, boost::filesystem::owner_exe | boost::filesystem::add_perms); - boost::process::child process_source(file_source, file_url2.data().AsInternal(), boost::process::start_dir(start_dir), + boost::process::child process_source(file_source, file_url2.data().AsInternal(), boost::process::start_dir(start_dir), boost::process::std_out > intermediate, boost::process::limit_handles); boost::process::child process_ffmpeg(file_ffmpeg, configss, boost::process::std_in < intermediate, boost::process::limit_handles); #endif @@ -914,25 +915,19 @@ static int SecondsSinceLastInput() }} -void wxMediaCtrl2::DoSetSize(int x, int y, int width, int height, int sizeFlags) +void wxMediaCtrl_OnSize(wxWindow * ctrl, wxSize const & videoSize, int width, int height) { -#ifdef __WXMAC__ - wxWindow::DoSetSize(x, y, width, height, sizeFlags); -#else - wxMediaCtrl::DoSetSize(x, y, width, height, sizeFlags); -#endif - if (sizeFlags & wxSIZE_USE_EXISTING) return; - wxSize size = m_video_size; + wxSize size = videoSize; + if (!size.IsFullySpecified()) size = {16, 9}; int maxHeight = (width * size.GetHeight() + size.GetHeight() - 1) / size.GetWidth(); - if (maxHeight != GetMaxHeight()) { - // BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl2::DoSetSize: width: " << width << ", height: " << height << ", maxHeight: " << maxHeight; - SetMaxSize({-1, maxHeight}); - CallAfter([this] { - if (auto p = GetParent()) { + if (maxHeight != ctrl->GetMaxHeight()) { + // BOOST_LOG_TRIVIAL(info) << "wxMediaCtrl_OnSize: width: " << width << ", height: " << height << ", maxHeight: " << maxHeight; + ctrl->SetMaxSize({-1, maxHeight}); + ctrl->CallAfter([ctrl] { + if (auto p = ctrl->GetParent()) { p->Layout(); p->Refresh(); } }); } } - diff --git a/src/slic3r/GUI/MediaPlayCtrl.h b/src/slic3r/GUI/MediaPlayCtrl.h index 4744a674f..877fa0d88 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.h +++ b/src/slic3r/GUI/MediaPlayCtrl.h @@ -8,7 +8,14 @@ #ifndef MediaPlayCtrl_h #define MediaPlayCtrl_h +#define USE_WX_MEDIA_CTRL_2 0 + +#if USE_WX_MEDIA_CTRL_2 #include "wxMediaCtrl2.h" +#define wxMediaCtrl3 wxMediaCtrl2 +#else +#include "wxMediaCtrl3.h" +#endif #include @@ -31,7 +38,7 @@ namespace GUI { class MediaPlayCtrl : public wxPanel { public: - MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize); + MediaPlayCtrl(wxWindow *parent, wxMediaCtrl3 *media_ctrl, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize); ~MediaPlayCtrl(); @@ -73,7 +80,7 @@ private: static constexpr wxMediaState MEDIASTATE_LOADING = (wxMediaState) 5; static constexpr wxMediaState MEDIASTATE_BUFFERING = (wxMediaState) 6; - wxMediaCtrl2 * m_media_ctrl; + wxMediaCtrl3 * m_media_ctrl; wxMediaState m_last_state = MEDIASTATE_IDLE; std::string m_machine; int m_lan_proto = 0; @@ -88,7 +95,7 @@ private: bool m_device_busy = false; bool m_disable_lan = false; wxString m_url; - + std::deque m_tasks; boost::mutex m_mutex; boost::condition_variable m_cond; diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 60f6058e1..15425f387 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -577,7 +577,7 @@ void PrintingTaskPanel::paint(wxPaintEvent&) dc.SetTextForeground(*wxBLACK); dc.DrawBitmap(m_thumbnail_bmp_display, wxPoint(0, 0)); dc.SetFont(Label::Body_12); - + if (m_plate_index >= 0) { wxString plate_id_str = wxString::Format("%d", m_plate_index); dc.DrawText(plate_id_str, wxPoint(4, 4)); @@ -795,7 +795,7 @@ void PrintingTaskPanel::set_plate_index(int plate_idx) } void PrintingTaskPanel::market_scoring_show() -{ +{ m_score_staticline->Show(); m_score_subtask_info->Show(); } @@ -916,7 +916,7 @@ void StatusBasePanel::init_bitmaps() m_bitmap_fan_off = ScalableBitmap(this, "monitor_fan_off", 22); m_bitmap_speed = ScalableBitmap(this, "monitor_speed", 24); m_bitmap_speed_active = ScalableBitmap(this, "monitor_speed_active", 24); - + m_thumbnail_brokenimg = ScalableBitmap(this, "monitor_brokenimg", 120); m_thumbnail_sdcard = ScalableBitmap(this, "monitor_sdcard_thumbnail", 120); //m_bitmap_camera = create_scaled_bitmap("monitor_camera", nullptr, 18); @@ -1012,7 +1012,7 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page() // media_ctrl_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); // media_ctrl_panel->SetBackgroundColour(*wxBLACK); // wxBoxSizer *bSizer_monitoring = new wxBoxSizer(wxVERTICAL); - m_media_ctrl = new wxMediaCtrl2(this); + m_media_ctrl = new wxMediaCtrl3(this); m_media_ctrl->SetMinSize(wxSize(PAGE_MIN_WIDTH, FromDIP(288))); m_media_play_ctrl = new MediaPlayCtrl(this, m_media_ctrl, wxDefaultPosition, wxSize(-1, FromDIP(40))); @@ -1689,7 +1689,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_project_task_panel->get_pause_resume_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); m_project_task_panel->get_abort_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); m_project_task_panel->get_market_scoring_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); - m_project_task_panel->get_market_retry_buttom()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); + m_project_task_panel->get_market_retry_buttom()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); m_project_task_panel->get_clean_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this); m_setting_button->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); @@ -1703,7 +1703,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_switch_lamp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this); m_switch_nozzle_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); // TODO m_switch_printing_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - m_switch_cham_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + m_switch_cham_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_bpButton_xy->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this); // TODO m_bpButton_z_10->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this); m_bpButton_z_1->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_1), NULL, this); @@ -1748,7 +1748,7 @@ StatusPanel::~StatusPanel() m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); - m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); + m_project_task_panel->get_market_retry_buttom()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_retry), NULL, this); m_project_task_panel->get_clean_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_print_error_clean), NULL, this); m_setting_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_camera_enter), NULL, this); @@ -1787,7 +1787,7 @@ StatusPanel::~StatusPanel() if (sdcard_hint_dlg != nullptr) delete sdcard_hint_dlg; - if (m_score_data != nullptr) { + if (m_score_data != nullptr) { delete m_score_data; } } @@ -1811,7 +1811,7 @@ void StatusPanel::init_scaled_buttons() m_bpButton_e_down_10->SetCornerRadius(FromDIP(12)); } -void StatusPanel::on_market_scoring(wxCommandEvent &event) { +void StatusPanel::on_market_scoring(wxCommandEvent &event) { if (obj && obj->is_makeworld_subtask() && obj->rating_info && obj->rating_info->request_successful) { // model is mall model and has rating_id BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_market_scoring" ; if (m_score_data && m_score_data->rating_id == obj->rating_info->rating_id) { // current score data for model is same as mall model @@ -1820,7 +1820,7 @@ void StatusPanel::on_market_scoring(wxCommandEvent &event) { int ret = m_score_dlg.ShowModal(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": old data"; - if (ret == wxID_OK) { + if (ret == wxID_OK) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": old data is upload"; m_score_data->rating_id = -1; m_project_task_panel->set_star_count_dirty(false); @@ -1842,11 +1842,11 @@ void StatusPanel::on_market_scoring(wxCommandEvent &event) { std::string comment = obj->rating_info->content; if (!comment.empty()) { m_score_dlg.set_comment(comment); } - + std::vector images_json_array; images_json_array = obj->rating_info->image_url_paths; if (!images_json_array.empty()) m_score_dlg.set_cloud_bitmap(images_json_array); - + int ret = m_score_dlg.ShowModal(); if (ret == wxID_OK) { @@ -1881,11 +1881,11 @@ void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event) if (obj->can_resume()) { BOOST_LOG_TRIVIAL(info) << "monitor: resume current print task dev_id =" << obj->dev_id; obj->command_task_resume(); - } + } else { BOOST_LOG_TRIVIAL(info) << "monitor: pause current print task dev_id =" << obj->dev_id; obj->command_task_pause(); - } + } if (m_print_error_dlg) { m_print_error_dlg->on_hide(); }if (m_print_error_dlg_no_action) { @@ -1902,7 +1902,7 @@ void StatusPanel::on_subtask_abort(wxCommandEvent &event) abort_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent &e) { if (obj) { BOOST_LOG_TRIVIAL(info) << "monitor: stop current print task dev_id =" << obj->dev_id; - obj->command_task_abort(); + obj->command_task_abort(); } }); } @@ -2050,7 +2050,7 @@ void StatusPanel::update(MachineObject *obj) calibration_dlg->update_machine_obj(obj); calibration_dlg->update_cali(obj); } - + if (obj->is_support_first_layer_inspect @@ -2576,10 +2576,15 @@ void StatusPanel::update_ams(MachineObject *obj) m_ams_control->show_auto_refill(false); } else { +<<<<<<< HEAD (26a4fc ENH: refresh_agora_url callback) +======= + + m_ams_control->SetAmsModel(ams_mode, ams_mode); +>>>>>>> CHANGE (d6c7f0 NEW: reimpl wxMediaCtrl from ffmpeg) m_ams_control->SetAmsModel(ams_mode, ams_mode); show_ams_group(true); - m_ams_control->show_auto_refill(true); + m_ams_control->show_auto_refill(true); } @@ -2643,7 +2648,7 @@ void StatusPanel::update_ams(MachineObject *obj) m_ams_control->SetExtruder(obj->is_filament_at_extruder(), true, obj->m_ams_id, obj->vt_tray.get_color()); } else { m_ams_control->SetExtruder(obj->is_filament_at_extruder(), false, obj->m_ams_id, m_ams_control->GetCanColour(obj->m_ams_id, obj->m_tray_id)); - + } if (obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) { @@ -2696,7 +2701,7 @@ void StatusPanel::update_ams(MachineObject *obj) }else{ m_ams_control->SetFilamentStep(FilamentStep::STEP_PUSH_NEW_FILAMENT, FilamentStepType::STEP_TYPE_LOAD); } - + } else { m_ams_control->SetFilamentStep(FilamentStep::STEP_PUSH_NEW_FILAMENT, FilamentStepType::STEP_TYPE_UNLOAD); @@ -2736,7 +2741,7 @@ void StatusPanel::update_ams(MachineObject *obj) } else { m_ams_control->SetFilamentStep(FilamentStep::STEP_IDLE, FilamentStepType::STEP_TYPE_LOAD); } - + for (auto ams_it = obj->amsList.begin(); ams_it != obj->amsList.end(); ams_it++) { std::string ams_id = ams_it->first; @@ -2783,7 +2788,7 @@ void StatusPanel::update_ams(MachineObject *obj) }else { is_curr_tray_selected = true; } - + update_ams_control_state(is_curr_tray_selected); } @@ -2839,7 +2844,7 @@ void StatusPanel::update_ams_control_state(bool is_curr_tray_selected) if (!obj->is_filament_at_extruder()) { enable[ACTION_BTN_UNLOAD] = false; } - + if (obj->ams_exist_bits == 0) { if (obj->is_in_printing()) { if (!obj->can_resume()) { @@ -2857,7 +2862,7 @@ void StatusPanel::update_ams_control_state(bool is_curr_tray_selected) } } } - + } else { if (obj->is_in_printing() /*&& obj->can_resume() && obj->m_tray_now != std::to_string(VIRTUAL_TRAY_ID) */) { @@ -2876,7 +2881,7 @@ void StatusPanel::update_ams_control_state(bool is_curr_tray_selected) else if (!m_ams_control->GetCurrentCan(m_ams_control->GetCurentAms()).empty()) { enable[ACTION_BTN_LOAD] = false; enable[ACTION_BTN_UNLOAD] = false; - } + } } else if (obj->m_tray_now == std::to_string(VIRTUAL_TRAY_ID)) { if (m_ams_control->GetCurentAms() == std::to_string(VIRTUAL_TRAY_ID)) { @@ -2892,7 +2897,7 @@ void StatusPanel::update_ams_control_state(bool is_curr_tray_selected) enable[ACTION_BTN_LOAD] = false; enable[ACTION_BTN_UNLOAD] = false; } - } + } } } @@ -2952,14 +2957,14 @@ void StatusPanel::update_basic_print_data(bool def) void StatusPanel::update_model_info() { auto get_subtask_fn = [this](BBLModelTask* subtask) { - CallAfter([this, subtask]() { + CallAfter([this, subtask]() { if (obj && obj->subtask_id_ == subtask->task_id) { obj->set_modeltask(subtask); } }); }; - + if (wxGetApp().getAgent() && obj) { BBLSubTask* curr_task = obj->get_subtask(); if (curr_task) { @@ -3024,7 +3029,7 @@ void StatusPanel::update_subtask(MachineObject *obj) if (calib_bitmap != nullptr) m_project_task_panel->set_thumbnail_img(*calib_bitmap); } - + if (obj->is_support_layer_num) { m_project_task_panel->update_layers_num(true); } @@ -3127,7 +3132,7 @@ void StatusPanel::update_subtask(MachineObject *obj) m_project_task_panel->get_request_failed_panel()->Hide(); } // update printing stage - + m_project_task_panel->update_left_time(obj->mc_left_time); if (obj->subtask_) { m_project_task_panel->update_stage_value(obj->get_curr_stage(), obj->subtask_->task_progress); @@ -3243,7 +3248,7 @@ void StatusPanel::reset_printing_values() m_project_task_panel->update_left_time(NA_STR); m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %s"), NA_STR)); update_calib_bitmap(); - + task_thumbnail_state = ThumbnailState::PLACE_HOLDER; m_start_loading_thumbnail = false; m_load_sdcard_thumbnail = false; @@ -3313,7 +3318,7 @@ bool StatusPanel::check_axis_z_at_home(MachineObject* obj) } void StatusPanel::on_axis_ctrl_z_up_10(wxCommandEvent &event) -{ +{ if (obj) { obj->command_axis_control("Z", 1.0, -10.0f, 900); if (!check_axis_z_at_home(obj)) @@ -3478,7 +3483,7 @@ void StatusPanel::on_ams_load_curr() std::string curr_ams_id = m_ams_control->GetCurentAms(); std::string curr_can_id = m_ams_control->GetCurrentCan(curr_ams_id); - + update_filament_step(); //virtual tray if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_ID)) == 0) { @@ -3853,7 +3858,7 @@ void StatusPanel::on_ams_guide(wxCommandEvent& event) else { ams_wiki_url = "https://wiki.bambulab.com/en/software/bambu-studio/use-ams-on-bambu-studio"; } - + wxLaunchDefaultBrowser(ams_wiki_url); } @@ -3991,7 +3996,7 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) speed_dismiss_time = boost::posix_time::microsec_clock::universal_time(); } }); - + m_ams_control->Bind(EVT_CLEAR_SPEED_CONTROL, [this, popUp](auto& e) { if (m_showing_speed_popup) { if (popUp && popUp->IsShown()) { @@ -4378,8 +4383,8 @@ void StatusPanel::msw_rescale() m_calibration_btn->Rescale(); m_options_btn->SetMinSize(wxSize(-1, FromDIP(26))); - m_options_btn->Rescale(); - + m_options_btn->Rescale(); + m_parts_btn->SetMinSize(wxSize(-1, FromDIP(26))); m_parts_btn->Rescale(); @@ -4420,11 +4425,11 @@ ScoreDialog::ScoreDialog(wxWindow *parent, ScoreData *score_data) , m_upload_status_code(StatusCode::CODE_NUMBER) { m_tocken.reset(new int(0)); - + wxBoxSizer *m_main_sizer = get_main_sizer(score_data->local_to_url_image, score_data->comment_text); m_image_url_paths = score_data->image_url_paths; - + this->SetSizer(m_main_sizer); Fit(); @@ -4440,16 +4445,16 @@ void ScoreDialog::on_dpi_changed(const wxRect &suggested_rect) {} void ScoreDialog::OnBitmapClicked(wxMouseEvent &event) { wxStaticBitmap *clickedBitmap = dynamic_cast(event.GetEventObject()); - if (m_image.find(clickedBitmap) != m_image.end()) { + if (m_image.find(clickedBitmap) != m_image.end()) { if (!m_image[clickedBitmap].is_selected) { - for (auto panel : m_image[clickedBitmap].image_broad) { + for (auto panel : m_image[clickedBitmap].image_broad) { panel->Show(); } m_image[clickedBitmap].is_selected = true; m_selected_image_list.insert(clickedBitmap); } else { - for (auto panel : m_image[clickedBitmap].image_broad) { - panel->Hide(); + for (auto panel : m_image[clickedBitmap].image_broad) { + panel->Hide(); } m_image[clickedBitmap].is_selected = false; m_selected_image_list.erase(clickedBitmap); @@ -4466,9 +4471,9 @@ void ScoreDialog::OnBitmapClicked(wxMouseEvent &event) } std::set > ScoreDialog::add_need_upload_imgs() -{ +{ std::set> need_upload_images; - for (auto bitmap : m_image) { + for (auto bitmap : m_image) { if (!bitmap.second.is_uploaded) { wxString &local_image_path = bitmap.second.local_image_url; if (!local_image_path.empty()) { need_upload_images.insert(std::make_pair(bitmap.first, local_image_path)); } @@ -4488,7 +4493,7 @@ std::pair ScoreDialog::create_local_thu cur_image_msg.local_image_url = local_path; cur_image_msg.img_url_paths = ""; cur_image_msg.is_uploaded = false; - + wxStaticBitmap *imageCtrl = new wxStaticBitmap(this, wxID_ANY, wxBitmap(wxImage(local_path, wxBITMAP_TYPE_ANY).Rescale(FromDIP(80), FromDIP(60))), wxDefaultPosition, wxDefaultSize, 0); imageCtrl->Bind(wxEVT_LEFT_DOWN, &ScoreDialog::OnBitmapClicked, this); @@ -4553,7 +4558,7 @@ void ScoreDialog::update_static_bitmap(wxStaticBitmap* static_bitmap, wxImage im } wxBoxSizer *ScoreDialog::create_broad_sizer(wxStaticBitmap *bitmap, ImageMsg& cur_image_msg) -{ +{ // tb: top and bottom lr: left and right auto m_image_tb_broad = new wxBoxSizer(wxVERTICAL); auto line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); @@ -4600,7 +4605,7 @@ void ScoreDialog::init() { SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); } -wxBoxSizer *ScoreDialog::get_score_sizer() { +wxBoxSizer *ScoreDialog::get_score_sizer() { wxBoxSizer *score_sizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText *static_score_text = new wxStaticText(this, wxID_ANY, _L("Rate"), wxDefaultPosition, wxDefaultSize, 0); static_score_text->Wrap(-1); @@ -4720,18 +4725,18 @@ wxBoxSizer *ScoreDialog::get_photo_btn_sizer() { for (int i = 0; i < filePaths.GetCount(); i++) { //It's ugly, but useful bool is_repeat = false; for (auto image : m_image) { - if (filePaths[i] == image.second.local_image_url) { + if (filePaths[i] == image.second.local_image_url) { is_repeat = true; continue; } } if (!is_repeat) { local_path.push_back(std::make_pair(filePaths[i], "")); - if (local_path.size() + m_image.size() > m_photo_nums) { - break; + if (local_path.size() + m_image.size() > m_photo_nums) { + break; } } - + } load_photo(local_path); @@ -4858,7 +4863,7 @@ wxBoxSizer *ScoreDialog::get_button_sizer() } } progress_dialog->Hide(); - if (progress_dialog) { + if (progress_dialog) { delete progress_dialog; progress_dialog = nullptr; } @@ -4998,7 +5003,7 @@ wxBoxSizer *ScoreDialog::get_main_sizer(const std::vectorAdd(m_photo_sizer, 0, wxEXPAND | wxTOP, FromDIP(8)); m_image_sizer = new wxGridSizer(5, FromDIP(5), FromDIP(5)); - if (!images.empty()) { + if (!images.empty()) { load_photo(images); } m_main_sizer->Add(m_image_sizer, 0, wxEXPAND | wxLEFT, FromDIP(24)); @@ -5010,7 +5015,7 @@ wxBoxSizer *ScoreDialog::get_main_sizer(const std::vectorGetValue(); score_data.image_url_paths = m_image_url_paths; for (auto img : m_image) { score_data.local_to_url_image.push_back(std::make_pair(img.second.local_image_url, img.second.img_url_paths)); } - + return score_data; } void ScoreDialog::set_comment(std::string comment) { - if (m_comment_text) { + if (m_comment_text) { m_comment_text->SetValue(wxString::FromUTF8(comment)); } } void ScoreDialog::set_cloud_bitmap(std::vector cloud_bitmaps) -{ +{ m_image_url_paths = cloud_bitmaps; for (std::string &url : cloud_bitmaps) { if (std::string::npos == url.find(m_model_id)) continue; diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 27b1d0ba7..435084b08 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -13,7 +13,6 @@ #include #include #include -#include "wxMediaCtrl2.h" #include "MediaPlayCtrl.h" #include "AMSSetting.hpp" #include "Calibration.hpp" @@ -91,11 +90,11 @@ public: void set_cloud_bitmap(std::vector cloud_bitmaps); protected: - enum StatusCode { - UPLOAD_PROGRESS = 0, - UPLOAD_EXIST_ISSUE, + enum StatusCode { + UPLOAD_PROGRESS = 0, + UPLOAD_EXIST_ISSUE, UPLOAD_IMG_FAILED, - CODE_NUMBER + CODE_NUMBER }; std::shared_ptr m_tocken; @@ -113,7 +112,7 @@ protected: { wxString local_image_url; //local image path std::string img_url_paths; // oss url path - vector image_broad; + vector image_broad; bool is_selected; bool is_uploaded; // load wxBoxSizer * image_tb_broad = nullptr; @@ -148,7 +147,7 @@ protected: std::set> add_need_upload_imgs(); std::pair create_local_thumbnail(wxString &local_path); std::pair create_oss_thumbnail(std::string &oss_path); - + }; class PrintingTaskPanel : public wxPanel @@ -157,7 +156,7 @@ public: PrintingTaskPanel(wxWindow* parent, PrintingTaskType type); ~PrintingTaskPanel(); void create_panel(wxWindow* parent); - + private: MachineObject* m_obj; @@ -232,7 +231,7 @@ public: void set_plate_index(int plate_idx = -1); void market_scoring_show(); void market_scoring_hide(); - + public: ScalableButton* get_abort_button() {return m_button_abort;}; ScalableButton* get_pause_resume_button() {return m_button_pause_resume;}; @@ -313,7 +312,7 @@ protected: wxStaticBitmap *m_bitmap_static_use_weight; - wxMediaCtrl2 * m_media_ctrl; + wxMediaCtrl3 * m_media_ctrl; MediaPlayCtrl * m_media_play_ctrl; Label * m_staticText_printing; @@ -340,7 +339,7 @@ protected: /* TempInput */ wxBoxSizer * m_misc_ctrl_sizer; - StaticBox* m_fan_panel; + StaticBox* m_fan_panel; StaticLine * m_line_nozzle; TempInput* m_tempCtrl_nozzle; int m_temp_nozzle_timeout{ 0 }; @@ -406,7 +405,7 @@ protected: virtual void on_bed_temp_kill_focus(wxFocusEvent &event) { event.Skip(); } virtual void on_bed_temp_set_focus(wxFocusEvent &event) { event.Skip(); } virtual void on_nozzle_temp_kill_focus(wxFocusEvent &event) { event.Skip(); } - virtual void on_nozzle_temp_set_focus(wxFocusEvent &event) { event.Skip(); } + virtual void on_nozzle_temp_set_focus(wxFocusEvent &event) { event.Skip(); } virtual void on_nozzle_fan_switch(wxCommandEvent &event) { event.Skip(); } virtual void on_printing_fan_switch(wxCommandEvent &event) { event.Skip(); } virtual void on_axis_ctrl_z_up_10(wxCommandEvent &event) { event.Skip(); } diff --git a/src/slic3r/GUI/wxMediaCtrl2.cpp b/src/slic3r/GUI/wxMediaCtrl2.cpp index 60b026733..82876cf33 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.cpp +++ b/src/slic3r/GUI/wxMediaCtrl2.cpp @@ -1,6 +1,6 @@ #include "wxMediaCtrl2.h" #include "I18N.hpp" -#include "GUI_App.hpp" +#include "libslic3r/Utils.hpp" #ifdef __WIN32__ #include #include @@ -61,8 +61,8 @@ void wxMediaCtrl2::Load(wxURI url) if (!notified) CallAfter([] { auto res = wxMessageBox(_L("Windows Media Player is required for this task! Do you want to enable 'Windows Media Player' for your operation system?"), _L("Error"), wxOK | wxCANCEL); if (res == wxOK) { - wxString url = IsWindows10OrGreater() - ? "ms-settings:optionalfeatures?activationSource=SMC-Article-14209" + wxString url = IsWindows10OrGreater() + ? "ms-settings:optionalfeatures?activationSource=SMC-Article-14209" : "https://support.microsoft.com/en-au/windows/get-windows-media-player-81718e0d-cfce-25b1-aee3-94596b658287"; wxExecute("cmd /c start " + url, wxEXEC_HIDE_CONSOLE); } @@ -77,7 +77,7 @@ void wxMediaCtrl2::Load(wxURI url) { wxRegKey key11(wxRegKey::HKCU, L"SOFTWARE\\Classes\\CLSID\\" CLSID_BAMBU_SOURCE L"\\InProcServer32"); wxRegKey key12(wxRegKey::HKCR, L"CLSID\\" CLSID_BAMBU_SOURCE L"\\InProcServer32"); - wxString path = key11.Exists() ? key11.QueryDefaultValue() + wxString path = key11.Exists() ? key11.QueryDefaultValue() : key12.Exists() ? key12.QueryDefaultValue() : wxString{}; wxRegKey key2(wxRegKey::HKCR, "bambu"); wxString clsid; @@ -144,14 +144,14 @@ void wxMediaCtrl2::Load(wxURI url) #ifdef __WXGTK3__ GstElementFactory *factory; int hasplugins = 1; - + factory = gst_element_factory_find("h264parse"); if (!factory) { hasplugins = 0; } else { gst_object_unref(factory); } - + factory = gst_element_factory_find("openh264dec"); if (!factory) { factory = gst_element_factory_find("avdec_h264"); @@ -164,7 +164,7 @@ void wxMediaCtrl2::Load(wxURI url) } else { gst_object_unref(factory); } - + if (!hasplugins) { CallAfter([] { wxMessageBox(_L("Your system is missing H.264 codecs for GStreamer, which are required to play video. (Try installing the gstreamer1.0-plugins-bad or gstreamer1.0-libav packages, then restart Bambu Studio?)"), _L("Error"), wxOK); @@ -194,8 +194,9 @@ void wxMediaCtrl2::Play() { wxMediaCtrl::Play(); } void wxMediaCtrl2::Stop() { - wxMediaCtrl::Stop(); -} + wxMediaCtrl::Stop(); } + +void wxMediaCtrl2::SetIdleImage(wxString const &image) {} #ifdef __LINUX__ extern "C" int gst_bambu_last_error; @@ -230,6 +231,13 @@ wxSize wxMediaCtrl2::DoGetBestSize() const return {-1, -1}; } +void wxMediaCtrl2::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); +} + #ifdef __WIN32__ WXLRESULT wxMediaCtrl2::MSWWindowProc(WXUINT nMsg, diff --git a/src/slic3r/GUI/wxMediaCtrl2.h b/src/slic3r/GUI/wxMediaCtrl2.h index 1b510e211..09e721bda 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.h +++ b/src/slic3r/GUI/wxMediaCtrl2.h @@ -13,13 +13,15 @@ wxDECLARE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent); +void wxMediaCtrl_OnSize(wxWindow * ctrl, wxSize const & videoSize, int width, int height); + #ifdef __WXMAC__ class wxMediaCtrl2 : public wxWindow { public: wxMediaCtrl2(wxWindow * parent); - + ~wxMediaCtrl2(); void Load(wxURI url); @@ -41,8 +43,8 @@ public: protected: void DoSetSize(int x, int y, int width, int height, int sizeFlags) override; - static void bambu_log(void const * ctx, int level, char const * msg); - + static void bambu_log(void const *ctx, int level, char const *msg); + void NotifyStopped(); private: diff --git a/src/slic3r/GUI/wxMediaCtrl2.mm b/src/slic3r/GUI/wxMediaCtrl2.mm index ce40b0dd0..b99dda578 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.mm +++ b/src/slic3r/GUI/wxMediaCtrl2.mm @@ -140,6 +140,10 @@ void wxMediaCtrl2::Stop() NotifyStopped(); } +void wxMediaCtrl2::SetIdleImage(wxString const &image) +{ +} + void wxMediaCtrl2::NotifyStopped() { if (m_state != wxMEDIASTATE_STOPPED) { @@ -168,3 +172,10 @@ wxSize wxMediaCtrl2::GetVideoSize() const return {0, 0}; } } + +void wxMediaCtrl2::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); +} diff --git a/src/slic3r/GUI/wxMediaCtrl3.cpp b/src/slic3r/GUI/wxMediaCtrl3.cpp new file mode 100644 index 000000000..302af5fe2 --- /dev/null +++ b/src/slic3r/GUI/wxMediaCtrl3.cpp @@ -0,0 +1,267 @@ +#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(); +}; + +wxMediaCtrl3::wxMediaCtrl3(wxWindow *parent) + : wxWindow(parent, wxID_ANY) + , BambuLib(StaticBambuLib::get()) + , m_thread([this] { PlayThread(); }) +{ + SetBackgroundColour(*wxBLACK); +} + +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); +} diff --git a/src/slic3r/GUI/wxMediaCtrl3.h b/src/slic3r/GUI/wxMediaCtrl3.h new file mode 100644 index 000000000..108c039f4 --- /dev/null +++ b/src/slic3r/GUI/wxMediaCtrl3.h @@ -0,0 +1,83 @@ +// +// wxMediaCtrl3.h +// libslic3r_gui +// +// Created by cmguo on 2024/6/22. +// + +#ifndef wxMediaCtrl3_h +#define wxMediaCtrl3_h + +#include "wx/uri.h" +#include "wx/mediactrl.h" + +wxDECLARE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent); + +void wxMediaCtrl_OnSize(wxWindow * ctrl, wxSize const & videoSize, int width, int height); + +#ifdef __WXMAC__ + +#include "wxMediaCtrl2.h" +#define wxMediaCtrl3 wxMediaCtrl2 + +#else + +#define BAMBU_DYNAMIC +#include + +class AVVideoDecoder; + +class wxMediaCtrl3 : public wxWindow, BambuLib +{ +public: + wxMediaCtrl3(wxWindow *parent); + + ~wxMediaCtrl3(); + + void Load(wxURI url); + + void Play(); + + void Stop(); + + void SetIdleImage(wxString const & image); + + wxMediaState GetState(); + + int GetLastError(); + + wxSize GetVideoSize(); + +protected: + DECLARE_EVENT_TABLE() + + void paintEvent(wxPaintEvent &evt); + + wxSize DoGetBestSize() const override; + + void DoSetSize(int x, int y, int width, int height, int sizeFlags) override; + + static void bambu_log(void *ctx, int level, tchar const *msg); + + void PlayThread(); + + void NotifyStopped(); + +private: + wxString m_idle_image; + wxMediaState m_state = wxMEDIASTATE_STOPPED; + int m_error = 0; + wxSize m_video_size = wxDefaultSize; + wxSize m_frame_size = wxDefaultSize; + wxBitmap m_frame; + wxImage m_frame2; + + std::shared_ptr m_url; + std::mutex m_mutex; + std::condition_variable m_cond; + std::thread m_thread; +}; + +#endif + +#endif /* wxMediaCtrl3_h */