NEW: management camera video stream in device panel

Change-Id: I39634af900071cc5c01a88100457880a513e416f
This commit is contained in:
chunmao.guo 2022-09-19 09:50:34 +08:00 committed by Lane.Wei
parent 1068baf10e
commit 4d4fcc1780
24 changed files with 334 additions and 56 deletions

View File

@ -0,0 +1,8 @@
<svg width="39" height="22" viewBox="0 0 39 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="19.9073" cy="9.2257" r="7.39563" stroke="#323A3D" stroke-width="1.2"/>
<path d="M24.0288 9.09192C23.7729 8.18432 23.2243 7.38668 22.4683 6.82303C21.7123 6.25937 20.7913 5.96127 19.8484 5.97507C18.9055 5.98887 17.9936 6.3138 17.2545 6.89934C16.5153 7.48489 15.9903 8.29824 15.7611 9.21294" stroke="#323A3D" stroke-width="1.2"/>
<path d="M21.9439 9.82928C21.7571 9.42705 21.4553 9.08923 21.0766 8.85853C20.6978 8.62784 20.2592 8.51463 19.8161 8.53322C19.3731 8.55182 18.9455 8.70138 18.5874 8.963C18.2293 9.22462 17.9569 9.58655 17.8045 10.003" stroke="#323A3D" stroke-width="1.2"/>
<path d="M22.9088 16.041L23.4284 15.741L23.158 15.2727L22.6642 15.4931L22.9088 16.041ZM16.9023 16.0484L17.1456 15.4999L16.6524 15.2813L16.3827 15.7484L16.9023 16.0484ZM14.9023 19.5125L14.3827 19.2125L13.8631 20.1125H14.9023V19.5125ZM24.913 19.5125V20.1125H25.9523L25.4327 19.2125L24.913 19.5125ZM22.6642 15.4931C21.8198 15.8701 20.8838 16.0799 19.8972 16.0799V17.2799C21.0556 17.2799 22.158 17.0332 23.1534 16.5889L22.6642 15.4931ZM19.8972 16.0799C18.9166 16.0799 17.986 15.8726 17.1456 15.4999L16.6591 16.5969C17.6498 17.0362 18.7458 17.2799 19.8972 17.2799V16.0799ZM15.422 19.8125L17.422 16.3484L16.3827 15.7484L14.3827 19.2125L15.422 19.8125ZM24.913 18.9125H14.9023V20.1125H24.913V18.9125ZM22.3892 16.341L24.3934 19.8125L25.4327 19.2125L23.4284 15.741L22.3892 16.341Z" fill="#323A3D"/>
<line x1="13.8891" y1="19.5699" x2="25.6521" y2="19.5699" stroke="#323A3D" stroke-width="1.2" stroke-linecap="round"/>
<circle cx="19.9102" cy="11.7305" r="1.28516" fill="#323A3D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,8 @@
<svg width="39" height="22" viewBox="0 0 39 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="19.9073" cy="9.2257" r="7.39563" stroke="#323A3D" stroke-width="1.5"/>
<path d="M24.0288 9.09192C23.7729 8.18432 23.2243 7.38668 22.4683 6.82303C21.7123 6.25937 20.7913 5.96127 19.8484 5.97507C18.9055 5.98887 17.9936 6.3138 17.2545 6.89934C16.5153 7.48489 15.9903 8.29824 15.7611 9.21294" stroke="#323A3D" stroke-width="1.5"/>
<path d="M21.9439 9.82928C21.7571 9.42705 21.4553 9.08923 21.0766 8.85853C20.6978 8.62784 20.2592 8.51463 19.8161 8.53322C19.3731 8.55182 18.9455 8.70138 18.5874 8.963C18.2293 9.22462 17.9569 9.58655 17.8045 10.003" stroke="#323A3D" stroke-width="1.5"/>
<path d="M22.9088 16.041L23.5583 15.666L23.2203 15.0806L22.6031 15.3562L22.9088 16.041ZM16.9023 16.0484L17.2064 15.3628L16.59 15.0895L16.2528 15.6734L16.9023 16.0484ZM14.9023 19.5125L14.2528 19.1375L13.6033 20.2625H14.9023V19.5125ZM24.913 19.5125V20.2625H26.2121L25.5626 19.1375L24.913 19.5125ZM22.6031 15.3562C21.7775 15.7247 20.8623 15.9299 19.8972 15.9299V17.4299C21.0771 17.4299 22.2003 17.1786 23.2145 16.7259L22.6031 15.3562ZM19.8972 15.9299C18.9379 15.9299 18.028 15.7271 17.2064 15.3628L16.5983 16.734C17.6078 17.1816 18.7245 17.4299 19.8972 17.4299V15.9299ZM15.5519 19.8875L17.5519 16.4234L16.2528 15.6734L14.2528 19.1375L15.5519 19.8875ZM24.913 18.7625H14.9023V20.2625H24.913V18.7625ZM22.2593 16.416L24.2635 19.8875L25.5626 19.1375L23.5583 15.666L22.2593 16.416Z" fill="#323A3D"/>
<line x1="14.0391" y1="19.4199" x2="25.5021" y2="19.4199" stroke="#323A3D" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="19.9102" cy="11.7305" r="1.28516" fill="#323A3D"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,8 @@
<svg width="39" height="23" viewBox="0 0 39 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="19.5714" cy="9.27844" r="7.39563" stroke="#323A3D" stroke-width="1.5"/>
<path d="M23.6928 9.14465C23.4369 8.23705 22.8884 7.43942 22.1324 6.87576C21.3764 6.3121 20.4554 6.014 19.5125 6.0278C18.5696 6.0416 17.6577 6.36654 16.9185 6.95208C16.1794 7.53762 15.6544 8.35097 15.4252 9.26567" stroke="#FF6F00" stroke-width="1.5"/>
<path d="M21.6079 9.88202C21.4212 9.47979 21.1194 9.14196 20.7406 8.91127C20.3619 8.68057 19.9233 8.56736 19.4802 8.58596C19.0371 8.60455 18.6095 8.75412 18.2514 9.01574C17.8934 9.27735 17.6209 9.63928 17.4685 10.0557" stroke="#FF6F00" stroke-width="1.5"/>
<path d="M22.5728 16.0938L23.2224 15.7187L22.8844 15.1334L22.2671 15.4089L22.5728 16.0938ZM16.5664 16.1011L16.8704 15.4155L16.254 15.1422L15.9169 15.7261L16.5664 16.1011ZM14.5664 19.5652L13.9169 19.1902L13.2674 20.3152H14.5664V19.5652ZM24.5771 19.5652V20.3152H25.8762L25.2266 19.1902L24.5771 19.5652ZM22.2671 15.4089C21.4416 15.7774 20.5264 15.9827 19.5613 15.9827V17.4827C20.7412 17.4827 21.8643 17.2313 22.8786 16.7786L22.2671 15.4089ZM19.5613 15.9827C18.602 15.9827 17.6921 15.7799 16.8704 15.4155L16.2624 16.7868C17.2718 17.2344 18.3886 17.4827 19.5613 17.4827V15.9827ZM15.2159 19.9402L17.2159 16.4761L15.9169 15.7261L13.9169 19.1902L15.2159 19.9402ZM24.5771 18.8152H14.5664V20.3152H24.5771V18.8152ZM21.9233 16.4688L23.9276 19.9402L25.2266 19.1902L23.2224 15.7187L21.9233 16.4688Z" fill="#323A3D"/>
<line x1="13.7031" y1="19.4727" x2="25.1661" y2="19.4727" stroke="#323A3D" stroke-width="1.5" stroke-linecap="round"/>
<circle cx="19.5742" cy="11.7832" r="1.28516" fill="#FF6F00"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,8 @@
<svg width="39" height="23" viewBox="0 0 39 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="19.9073" cy="9.27844" r="7.39563" stroke="#323A3D" stroke-width="1.2"/>
<path d="M24.0288 9.14465C23.7729 8.23705 23.2243 7.43942 22.4683 6.87576C21.7123 6.3121 20.7913 6.014 19.8484 6.0278C18.9055 6.0416 17.9936 6.36654 17.2545 6.95208C16.5153 7.53762 15.9903 8.35097 15.7611 9.26567" stroke="#FF6F00" stroke-width="1.2"/>
<path d="M21.9439 9.88202C21.7571 9.47979 21.4553 9.14196 21.0766 8.91127C20.6978 8.68057 20.2592 8.56736 19.8161 8.58596C19.3731 8.60455 18.9455 8.75412 18.5874 9.01574C18.2293 9.27735 17.9569 9.63928 17.8045 10.0557" stroke="#FF6F00" stroke-width="1.2"/>
<path d="M22.9088 16.0938L23.4284 15.7937L23.158 15.3254L22.6642 15.5459L22.9088 16.0938ZM16.9023 16.1011L17.1456 15.5527L16.6524 15.334L16.3827 15.8011L16.9023 16.1011ZM14.9023 19.5652L14.3827 19.2652L13.8631 20.1652H14.9023V19.5652ZM24.913 19.5652V20.1652H25.9523L25.4327 19.2652L24.913 19.5652ZM22.6642 15.5459C21.8198 15.9228 20.8838 16.1327 19.8972 16.1327V17.3327C21.0556 17.3327 22.158 17.086 23.1534 16.6416L22.6642 15.5459ZM19.8972 16.1327C18.9166 16.1327 17.986 15.9253 17.1456 15.5527L16.6591 16.6496C17.6498 17.0889 18.7458 17.3327 19.8972 17.3327V16.1327ZM15.422 19.8652L17.422 16.4011L16.3827 15.8011L14.3827 19.2652L15.422 19.8652ZM24.913 18.9652H14.9023V20.1652H24.913V18.9652ZM22.3892 16.3938L24.3934 19.8652L25.4327 19.2652L23.4284 15.7937L22.3892 16.3938Z" fill="#323A3D"/>
<line x1="13.8891" y1="19.6227" x2="25.6521" y2="19.6227" stroke="#323A3D" stroke-width="1.2" stroke-linecap="round"/>
<circle cx="19.9102" cy="11.7832" r="1.28516" fill="#FF6F00"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -70,6 +70,7 @@ enum PrinterFunction {
FUNC_REMOTE_TUNNEL,
FUNC_LOCAL_TUNNEL,
FUNC_PRINT_WITHOUT_SD,
FUNC_VIRTUAL_CAMERA,
FUNC_MAX
};

View File

@ -141,12 +141,12 @@ bool DownloadProgressDialog::Show(bool show)
{
if (show) {
m_simplebook_status->SetSelection(0);
m_upgrade_job = std::make_shared<UpgradeNetworkJob>(m_status_bar);
m_upgrade_job = make_job(m_status_bar);
m_upgrade_job->set_event_handle(this);
m_status_bar->set_progress(0);
Bind(EVT_UPGRADE_NETWORK_SUCCESS, [this](wxCommandEvent& evt) {
m_status_bar->change_button_label(_L("Finish"));
wxGetApp().restart_networking();
on_finish();
m_status_bar->set_cancel_callback_fina(
[this]() {
this->Close();
@ -205,4 +205,8 @@ void DownloadProgressDialog::on_dpi_changed(const wxRect &suggested_rect) {}
void DownloadProgressDialog::update_release_note(std::string release_note, std::string version) {}
std::shared_ptr<UpgradeNetworkJob> DownloadProgressDialog::make_job(std::shared_ptr<ProgressIndicator> pri) { return std::make_shared<UpgradeNetworkJob>(pri); }
void DownloadProgressDialog::on_finish() { wxGetApp().restart_networking(); }
}} // namespace Slic3r::GUI

View File

@ -50,7 +50,9 @@ public:
std::shared_ptr<UpgradeNetworkJob> m_upgrade_job { nullptr };
wxPanel * m_panel_download;
protected:
virtual std::shared_ptr<UpgradeNetworkJob> make_job(std::shared_ptr<ProgressIndicator> pri);
virtual void on_finish();
};

View File

@ -1257,13 +1257,13 @@ std::string GUI_App::get_http_url(std::string country_code)
return url;
}
std::string GUI_App::get_plugin_url(std::string country_code)
std::string GUI_App::get_plugin_url(std::string name, std::string country_code)
{
std::string url = get_http_url(country_code);
std::string curr_version = SLIC3R_VERSION;
std::string using_version = curr_version.substr(0, 9) + "00";
url += (boost::format("?slicer/plugins/cloud=%1%") % using_version).str();
url += (boost::format("?slicer/%1%/cloud=%2%") % name % using_version).str();
//url += (boost::format("?slicer/plugins/cloud=%1%") % "01.01.00.00").str();
return url;
}
@ -1283,7 +1283,7 @@ static std::string decode(std::string const& extra, std::string const& path = {}
return Slic3r::decode_path(path.c_str());
}
int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
int GUI_App::download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
{
int result = 0;
// get country_code
@ -1294,12 +1294,12 @@ int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
BOOST_LOG_TRIVIAL(info) << "[download_plugin]: enter";
m_networking_cancel_update = false;
// get temp path
fs::path target_file_path = (fs::temp_directory_path() / "network_plugin.zip");
fs::path target_file_path = (fs::temp_directory_path() / package_name);
fs::path tmp_path = target_file_path;
tmp_path += format(".%1%%2%", get_current_pid(), ".tmp");
// get_url
std::string url = get_plugin_url(app_config->get_country_code());
std::string url = get_plugin_url(name, app_config->get_country_code());
std::string download_url;
Slic3r::Http http_url = Slic3r::Http::get(url);
BOOST_LOG_TRIVIAL(info) << "[download_plugin]: check the plugin from " << url;
@ -1418,16 +1418,16 @@ int GUI_App::download_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
return result;
}
int GUI_App::install_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
int GUI_App::install_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
{
bool cancel = false;
std::string target_file_path = (fs::temp_directory_path() / "network_plugin.zip").string();
std::string target_file_path = (fs::temp_directory_path() / package_name).string();
BOOST_LOG_TRIVIAL(info) << "[install_plugin] enter";
// get plugin folder
std::string data_dir_str = data_dir();
boost::filesystem::path data_dir_path(data_dir_str);
auto plugin_folder = data_dir_path / "plugins";
auto plugin_folder = data_dir_path / name;
//auto plugin_folder = boost::filesystem::path(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()) / "plugins";
auto backup_folder = plugin_folder/"backup";
if (!boost::filesystem::exists(plugin_folder)) {
@ -1534,7 +1534,8 @@ int GUI_App::install_plugin(InstallProgressFn pro_fn, WasCancelledFn cancel_fn)
if (pro_fn)
pro_fn(InstallStatusInstallCompleted, 100, cancel);
app_config->set_str("app", "installed_networking", "1");
if (name == "plugins")
app_config->set_str("app", "installed_networking", "1");
BOOST_LOG_TRIVIAL(info) << "[install_plugin] success";
return 0;
}

View File

@ -534,9 +534,9 @@ public:
void associate_files(std::wstring extend);
void disassociate_files(std::wstring extend);
#endif // __WXMSW__
std::string get_plugin_url(std::string country_code);
int download_plugin(InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr);
int install_plugin(InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr);
std::string get_plugin_url(std::string name, std::string country_code);
int download_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr);
int install_plugin(std::string name, std::string package_name, InstallProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr);
std::string get_http_url(std::string country_code);
bool is_compatibility_version();
bool check_networking_version();

View File

@ -16,7 +16,8 @@ wxDEFINE_EVENT(EVT_INSTALL_NETWORK_FAILED, wxCommandEvent);
UpgradeNetworkJob::UpgradeNetworkJob(std::shared_ptr<ProgressIndicator> pri)
: Job{std::move(pri)}
{
;
name = "plugins";
package_name = "networking_plugins.zip";
}
void UpgradeNetworkJob::on_exception(const std::exception_ptr &eptr)
@ -56,7 +57,7 @@ void UpgradeNetworkJob::process()
BOOST_LOG_TRIVIAL(info) << "[UpgradeNetworkJob process]: enter";
// get temp path
fs::path target_file_path = (fs::temp_directory_path() / "network_plugin.zip");
fs::path target_file_path = (fs::temp_directory_path() / package_name);
fs::path tmp_path = target_file_path;
auto path_str = tmp_path.string() + wxString::Format(".%d%s", get_current_pid(), ".tmp").ToStdString();
tmp_path = fs::path(path_str);
@ -67,7 +68,7 @@ void UpgradeNetworkJob::process()
return was_canceled();
};
int curr_percent = 0;
result = wxGetApp().download_plugin(
result = wxGetApp().download_plugin(name, package_name,
[this, &curr_percent](int state, int percent, bool &cancel) {
if (state == InstallStatusNormal) {
update_status(percent, _L("Downloading"));
@ -95,7 +96,9 @@ void UpgradeNetworkJob::process()
return;
}
result = wxGetApp().install_plugin([this](int state, int percent, bool&cancel) {
result = wxGetApp().install_plugin(
name, package_name,
[this](int state, int percent, bool &cancel) {
if (state == InstallStatusInstallCompleted) {
update_status(percent, _L("Finish"));
} else {

View File

@ -29,6 +29,9 @@ class UpgradeNetworkJob : public Job
InstallProgressFn pro_fn { nullptr };
protected:
std::string name;
std::string package_name;
void on_exception(const std::exception_ptr &) override;
public:
UpgradeNetworkJob(std::shared_ptr<ProgressIndicator> pri);

View File

@ -125,13 +125,7 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent)
m_button_year->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked);
m_button_month->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked);
m_button_all->Bind(wxEVT_COMMAND_BUTTON_CLICKED, time_button_clicked);
{
wxCommandEvent e(wxEVT_CHECKBOX);
auto b = m_button_all;
e.SetEventObject(b);
b->GetEventHandler()->ProcessEvent(e);
}
m_button_all->SetValue(true);
// File type
auto type_button_clicked = [this](wxEvent &e) {
@ -143,26 +137,12 @@ MediaFilePanel::MediaFilePanel(wxWindow * parent)
return;
m_image_grid->SetFileType(type);
m_last_type = type;
{
wxCommandEvent e(wxEVT_CHECKBOX);
e.SetEventObject(m_button_timelapse);
m_button_timelapse->GetEventHandler()->ProcessEvent(e);
}
{
wxCommandEvent e(wxEVT_CHECKBOX);
e.SetEventObject(m_button_video);
m_button_video->GetEventHandler()->ProcessEvent(e);
}
m_button_timelapse->SetValue(!m_button_timelapse->GetValue());
m_button_video->SetValue(!m_button_video->GetValue());
};
m_button_video->Bind(wxEVT_COMMAND_BUTTON_CLICKED, type_button_clicked);
m_button_timelapse->Bind(wxEVT_COMMAND_BUTTON_CLICKED, type_button_clicked);
{
wxCommandEvent e(wxEVT_CHECKBOX);
auto b = m_button_timelapse;
e.SetEventObject(b);
b->GetEventHandler()->ProcessEvent(e);
}
m_button_timelapse->SetValue(true);
// File management
m_button_management->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) {
@ -310,15 +290,12 @@ void MediaFilePanel::modeChanged(wxCommandEvent& e1)
if (m_last_mode == mode)
return;
::Button* buttons[] = {m_button_all, m_button_month, m_button_year};
wxCommandEvent e(wxEVT_CHECKBOX);
auto b = buttons[m_last_mode];
b->SetFont(Label::Body_14);
e.SetEventObject(b);
b->GetEventHandler()->ProcessEvent(e);
b->SetValue(false);
b = buttons[mode];
b->SetFont(Label::Head_14);
e.SetEventObject(b);
b->GetEventHandler()->ProcessEvent(e);
b->SetValue(true);
m_last_mode = mode;
}

View File

@ -5,6 +5,21 @@
#include "GUI_App.hpp"
#include "libslic3r/AppConfig.hpp"
#include "I18N.hpp"
#include "MsgDialog.hpp"
#include "DownloadProgressDialog.hpp"
#undef pid_t
#include <boost/process.hpp>
#ifdef __WIN32__
#include <boost/process/windows.hpp>
#elif __APPLE__
#include <sys/ipc.h>
#include <sys/shm.h>
#else
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#endif
namespace Slic3r {
namespace GUI {
@ -21,8 +36,7 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl2 *media_ctrl, const w
m_label_status = new Label(this, "", LB_HYPERLINK);
m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto & e) { TogglePlay(); });
m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { TogglePlay(); });
m_button_play->Bind(wxEVT_RIGHT_UP, [this](auto & e) { m_media_ctrl->Play(); });
m_label_status->Bind(wxEVT_LEFT_UP, [this](auto &e) {
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/faq/live-view", L"en");
@ -90,6 +104,12 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj)
}
m_machine = machine;
m_failed_retry = 0;
std::string stream_url;
if (get_stream_url(&stream_url)) {
m_streaming = boost::algorithm::contains(stream_url, "device=" + m_machine);
} else {
m_streaming = false;
}
if (m_last_state != MEDIASTATE_IDLE)
Stop();
if (m_next_retry.IsValid())
@ -217,6 +237,127 @@ void MediaPlayCtrl::TogglePlay()
}
}
struct detach_process
#ifdef __WIN32__
: public ::boost::process::detail::windows::handler_base_ext
#else
: public ::boost::process::detail::posix::handler_base_ext
#endif
{
#ifdef __WIN32__
template<class Executor> void on_setup(Executor &exec) const {
exec.creation_flags |= ::boost::winapi::CREATE_NO_WINDOW_;
}
#endif
};
void MediaPlayCtrl::ToggleStream()
{
std::string file_url = data_dir() + "/cameratools/url.txt";
if (m_streaming) {
boost::nowide::ofstream file(file_url);
file.close();
m_streaming = false;
return;
} else if (!boost::filesystem::exists(file_url)) {
boost::nowide::ofstream file(file_url);
file.close();
}
std::string url;
if (!get_stream_url(&url)) {
// create stream pipeline
#ifdef __WIN32__
std::string file_source = data_dir() + "\\cameratools\\bambu_source.exe";
std::string file_ffmpeg = data_dir() + "\\cameratools\\ffmpeg.exe";
std::string file_ff_cfg = data_dir() + "\\cameratools\\ffmpeg.cfg";
#else
std::string file_source = data_dir() + "/cameratools/bambu_source";
std::string file_ffmpeg = data_dir() + "/cameratools/ffmpeg";
std::string file_ff_cfg = data_dir() + "/cameratools/ffmpeg.cfg";
#endif
if (!boost::filesystem::exists(file_source) || !boost::filesystem::exists(file_ffmpeg) || !boost::filesystem::exists(file_ff_cfg)) {
auto res = MessageDialog(this, _L("Virtual Camera Tools is required for this task!\nDo you want to install them?"), _L("Error"),
wxOK | wxCANCEL).ShowModal();
if (res == wxID_OK) {
// download tools
struct DownloadProgressDialog2 : DownloadProgressDialog
{
MediaPlayCtrl *ctrl;
DownloadProgressDialog2(MediaPlayCtrl *ctrl) : DownloadProgressDialog(_L("Downloading Virtual Camera Tools")), ctrl(ctrl) {}
struct UpgradeNetworkJob2 : UpgradeNetworkJob
{
UpgradeNetworkJob2(std::shared_ptr<ProgressIndicator> pri) : UpgradeNetworkJob(pri) {
name = "cameratools";
package_name = "camera_tools.zip";
}
};
std::shared_ptr<UpgradeNetworkJob> make_job(std::shared_ptr<ProgressIndicator> pri) override
{ return std::make_shared<UpgradeNetworkJob2>(pri); }
void on_finish() override
{
wxGetApp().CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); });
EndModal(wxID_CLOSE);
}
};
DownloadProgressDialog2 dlg(this);
dlg.ShowModal();
}
return;
}
wxString url = L"bambu:///camera/" + from_u8(file_url);
url.Replace("\\", "/");
url = wxURI(url).BuildURI();
std::string configs;
try {
boost::filesystem::load_string_file(file_ff_cfg, configs);
} catch (...) {}
std::vector<std::string> configss;
boost::algorithm::split(configss, configs, boost::algorithm::is_any_of("\r\n"));
configss.erase(std::remove(configss.begin(), configss.end(), std::string()), configss.end());
boost::process::pipe intermediate;
boost::process::child process_source(file_source, url.data().AsInternal(), boost::process::std_out > intermediate, detach_process(),
boost::process::start_dir(boost::filesystem::path(data_dir()) / "plugins"));
boost::process::child process_ffmpeg(file_ffmpeg, configss, boost::process::std_in < intermediate, detach_process());
process_source.detach();
process_ffmpeg.detach();
}
if (!url.empty() && wxGetApp().app_config->get("not_show_vcamera_stop_prev") != "1") {
MessageDialog dlg(this, _L("Another virtual camera is running.\nBambu Studio supports only a single virtual camera.\nDo you want to stop this virtual camera?"), _L("Warning"),
wxYES | wxCANCEL | wxICON_INFORMATION);
dlg.show_dsa_button();
auto res = dlg.ShowModal();
if (dlg.get_checkbox_state())
wxGetApp().app_config->set("not_show_vcamera_stop_prev", "1");
if (res == wxID_CANCEL) return;
}
NetworkAgent *agent = wxGetApp().getAgent();
if (!agent) return;
agent->get_camera_url(m_machine, [this, m = m_machine](std::string url) {
BOOST_LOG_TRIVIAL(info) << "camera_url: " << url;
CallAfter([this, m, url] {
if (m != m_machine || url.empty()) return;
std::string file_url = data_dir() + "/cameratools/url.txt";
boost::nowide::ofstream file(file_url);
auto url2 = encode_path((url + "&device=" + m).c_str());
file.write(url2.c_str(), url2.size());
file.close();
m_streaming = true;
if (wxGetApp().app_config->get("not_show_vcamera_wiki") != "1") {
MessageDialog dlg(this, _L("Virtual camera is started.\nPress 'OK' to navigate the guide page of 'Streaming video of Bambu Printer'."), _L("Information"),
wxOK | wxCANCEL | wxICON_INFORMATION);
dlg.show_dsa_button();
auto res = dlg.ShowModal();
if (dlg.get_checkbox_state())
wxGetApp().app_config->set("not_show_vcamera_wiki", "1");
if (res == wxID_OK) {
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
wxLaunchDefaultBrowser(url);
}
}
});
});
}
void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink)
{
auto msg = wxString::Format(msg2, m_failed_code);
@ -236,6 +377,8 @@ void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink)
Layout();
}
bool MediaPlayCtrl::IsStreaming() const { return m_streaming; }
void MediaPlayCtrl::media_proc()
{
boost::unique_lock lock(m_mutex);
@ -270,6 +413,54 @@ void MediaPlayCtrl::media_proc()
}
}
bool MediaPlayCtrl::get_stream_url(std::string *url)
{
#ifdef __WIN32__
HANDLE shm = ::OpenFileMapping(FILE_MAP_READ, FALSE, L"bambu_stream_url");
if (shm == NULL) return false;
if (url) {
char *addr = (char *) MapViewOfFile(shm, FILE_MAP_READ, 0, 0, 0);
if (addr) {
*url = addr;
UnmapViewOfFile(addr);
url = nullptr;
}
}
CloseHandle(shm);
#elif __APPLE__
std::string file_url = data_dir() + "/url.txt";
key_t key = ::ftok(file_url.c_str(), 1000);
int shm = ::shmget(key, 1024, 0);
if (shm == -1) return false;
struct shmid_ds ds;
::shmctl(shm, IPC_STAT, &ds);
if (ds.shm_nattch == 0) {
return false;
}
if (url) {
char *addr = (char *) ::shmat(shm, nullptr, 0);
if (addr != (void*) -1) {
*url = addr;
::shmdt(addr);
url = nullptr;
}
}
#else
int shm = ::shm_open("bambu_stream_url", O_RDONLY, 0);
if (shm == -1) return false;
if (url) {
char *addr = (char *) ::mmap(nullptr, 1024, PROT_READ, MAP_SHARED, shm, 0);
if (addr != MAP_FAILED) {
*url = addr;
::munmap(addr, 1024);
url = nullptr;
}
}
::close(shm);
#endif
return url == nullptr;
}
void MediaPlayCtrl::onStateChanged(wxMediaEvent& event)
{
auto last_state = m_last_state;

View File

@ -35,6 +35,10 @@ public:
void SetMachineObject(MachineObject * obj);
bool IsStreaming() const;
void ToggleStream();
protected:
void onStateChanged(wxMediaEvent & event);
@ -49,6 +53,8 @@ protected:
private:
void media_proc();
bool get_stream_url(std::string * url = nullptr);
private:
static constexpr wxMediaState MEDIASTATE_IDLE = (wxMediaState) 3;
static constexpr wxMediaState MEDIASTATE_INITIALIZING = (wxMediaState) 4;
@ -71,11 +77,12 @@ private:
boost::condition_variable m_cond;
boost::thread m_thread;
bool m_streaming = false;
int m_failed_retry = 0;
int m_failed_code = 0;
wxDateTime m_next_retry;
::Button * m_button_play;
::Button *m_button_play;
::Label * m_label_status;
};

View File

@ -566,7 +566,7 @@ void NetworkTestDialog::start_test_oss_download()
tmp_path += (boost::format(".%1%%2%") % get_current_pid() % ".tmp").str();
// get_url
std::string url = wxGetApp().get_plugin_url(app_config->get_country_code());
std::string url = wxGetApp().get_plugin_url("plugins", app_config->get_country_code());
std::string download_url;
Slic3r::Http http_url = Slic3r::Http::get(url);
update_status(-1, "[test_oss_download]: url=" + url);

View File

@ -8318,7 +8318,7 @@ int GUI::Plater::close_with_confirm(std::function<bool(bool)> second_check)
auto result = MessageDialog(static_cast<wxWindow*>(this), _L("The current project has unsaved changes, save it before continue?"),
wxString(SLIC3R_APP_FULL_NAME) + " - " + _L("Save"), wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE).ShowModal();
if (result == wxID_CANCEL)
return result;
return result;
else if (result == wxID_YES) {
result = save_project();
if (result == wxID_CANCEL)

View File

@ -235,13 +235,19 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page()
m_recording_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));
m_recording_button->SetBackgroundColour(STATUS_TITLE_BG);
m_vcamera_button = new CameraItem(m_panel_monitoring_title, "vcamera_off_normal", "vcamera_on_normal", "vcamera_off_hover", "vcamera_on_hover");
m_vcamera_button->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));
m_vcamera_button->SetBackgroundColour(STATUS_TITLE_BG);
m_timelapse_button->SetToolTip(_L("Timelapse"));
m_recording_button->SetToolTip(_L("Video"));
m_vcamera_button->SetToolTip(_L("Virtual Camera"));
bSizer_monitoring_title->Add(m_bitmap_sdcard_off_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
bSizer_monitoring_title->Add(m_bitmap_sdcard_on_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
bSizer_monitoring_title->Add(m_timelapse_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
bSizer_monitoring_title->Add(m_recording_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
bSizer_monitoring_title->Add(m_vcamera_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
bSizer_monitoring_title->Add(FromDIP(13), 0, 0);
@ -1029,6 +1035,7 @@ void StatusBasePanel::upodate_camera_state(bool recording, bool timelapse, bool
//recording
m_recording_button->set_switch(recording);
m_vcamera_button->set_switch(m_media_play_ctrl->IsStreaming());
//timelapse
m_timelapse_button->set_switch(timelapse);
@ -1083,6 +1090,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co
//m_bitmap_thumbnail->Connect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_enter), NULL, this);
//m_bitmap_thumbnail->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this);
m_recording_button->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_recording), NULL, this);
m_vcamera_button->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_vcamera), NULL, this);
m_project_task_panel->Connect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this);
m_button_pause_resume->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
@ -1123,6 +1131,7 @@ StatusPanel::~StatusPanel()
//m_bitmap_thumbnail->Disconnect(wxEVT_ENTER_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_enter), NULL, this);
//m_bitmap_thumbnail->Disconnect(wxEVT_LEAVE_WINDOW, wxMouseEventHandler(StatusPanel::on_thumbnail_leave), NULL, this);
m_recording_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_recording), NULL, this);
m_vcamera_button->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::on_switch_vcamera), NULL, this);
m_button_pause_resume->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this);
m_button_abort->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this);
m_button_clean->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_clean), NULL, this);
@ -1355,6 +1364,12 @@ void StatusPanel::update(MachineObject *obj)
m_recording_button->Hide();
}
if (obj->is_function_supported(PrinterFunction::FUNC_VIRTUAL_CAMERA) && obj->has_ipcam) {
m_vcamera_button->Show();
} else {
m_vcamera_button->Hide();
}
if (obj->is_function_supported(PrinterFunction::FUNC_CHAMBER_TEMP)) {
m_tempCtrl_frame->Enable();
} else {
@ -2489,6 +2504,14 @@ void StatusPanel::on_switch_recording(wxMouseEvent &event)
obj->command_ipcam_record(!value);
}
void StatusPanel::on_switch_vcamera(wxMouseEvent &event)
{
//if (!obj) return;
//bool value = m_recording_button->get_switch_status();
//obj->command_ipcam_record(!value);
m_media_play_ctrl->ToggleStream();
}
void StatusPanel::on_camera_enter(wxMouseEvent& event)
{
if (obj) {
@ -2578,6 +2601,7 @@ void StatusPanel::set_default()
m_timelapse_button->Show();
m_recording_button->Show();
m_vcamera_button->Show();
m_tempCtrl_frame->Show();
m_options_btn->Show();
@ -2652,6 +2676,7 @@ void StatusPanel::msw_rescale()
m_timelapse_button->SetMinSize(wxSize(38, 24));
m_recording_button->SetMinSize(wxSize(38, 24));
m_vcamera_button->SetMinSize(wxSize(38, 24));
m_bitmap_sdcard_off_img->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));
m_bitmap_sdcard_on_img->SetMinSize(wxSize(FromDIP(38), FromDIP(24)));

View File

@ -91,6 +91,7 @@ protected:
CameraItem *m_timelapse_button;
CameraItem *m_recording_button;
CameraItem *m_vcamera_button;
wxBitmap m_bitmap_camera;
wxBitmap m_bitmap_sdcard_state_on;
@ -319,6 +320,7 @@ protected:
void on_thumbnail_enter(wxMouseEvent &event);
void on_thumbnail_leave(wxMouseEvent &event);
void on_switch_recording(wxMouseEvent &event);
void on_switch_vcamera(wxMouseEvent &event);
void on_camera_enter(wxMouseEvent &event);
void on_camera_leave(wxMouseEvent& event);
void on_auto_leveling(wxCommandEvent &event);

View File

@ -309,12 +309,14 @@ void DownPluginFrame::OnScriptResponseMessage(wxCommandEvent &WXUNUSED(evt))
int DownPluginFrame::DownloadPlugin()
{
return wxGetApp().download_plugin([this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }, nullptr);
return wxGetApp().download_plugin(
"plugins", "network_plugin.zip", [this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); }, nullptr);
}
int DownPluginFrame::InstallPlugin()
{
return wxGetApp().install_plugin([this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); });
return wxGetApp().install_plugin(
"plugins", "network_plugin.zip", [this](int status, int percent, bool &cancel) { return ShowPluginStatus(status, percent, cancel); });
}
int DownPluginFrame::ShowPluginStatus(int status, int percent, bool &cancel)

View File

@ -1509,6 +1509,7 @@ bool GuideFrame::LoadFile(std::string jPath, std::string &sContent)
int GuideFrame::DownloadPlugin()
{
return wxGetApp().download_plugin(
"plugins", "network_plugin.zip",
[this](int status, int percent, bool& cancel) {
return ShowPluginStatus(status, percent, cancel);
}
@ -1517,7 +1518,7 @@ int GuideFrame::DownloadPlugin()
int GuideFrame::InstallPlugin()
{
return wxGetApp().install_plugin(
return wxGetApp().install_plugin("plugins", "network_plugin.zip",
[this](int status, int percent, bool &cancel) {
return ShowPluginStatus(status, percent, cancel);
}

View File

@ -127,6 +127,14 @@ bool Button::Enable(bool enable)
void Button::SetCanFocus(bool canFocus) { this->canFocus = canFocus; }
void Button::SetValue(bool state)
{
if (GetValue() == state) return;
state_handler.set_state(state ? StateHandler::Checked : 0, StateHandler::Checked);
}
bool Button::GetValue() const { return state_handler.states() & StateHandler::Checked; }
void Button::Rescale()
{
if (this->active_icon.bmp().IsOk())

View File

@ -48,6 +48,10 @@ public:
void SetCanFocus(bool canFocus) override;
void SetValue(bool state);
bool GetValue() const;
void Rescale();
protected:

View File

@ -69,6 +69,19 @@ void StateHandler::update_binds()
for (auto &c : children_) c->update_binds();
}
void StateHandler::set_state(int state, int mask)
{
if (states_ & mask == state & mask) return;
int old = states_;
states_ = states_ & ~mask | state & mask;
if (old != states_ && (old | states2_) != (states_ | states2_)) {
if (parent_)
parent_->changed(states_ | states2_);
else
owner_->Refresh();
}
}
StateHandler::StateHandler(StateHandler *parent, wxWindow *owner)
: StateHandler(owner)
{

View File

@ -41,6 +41,8 @@ public:
int states() const { return states_ | states2_; }
void set_state(int state, int mask);
private:
StateHandler(StateHandler * parent, wxWindow *owner);