mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-09 07:56:24 -06:00
NEW: management camera video stream in device panel
Change-Id: I39634af900071cc5c01a88100457880a513e416f
This commit is contained in:
parent
1068baf10e
commit
4d4fcc1780
24 changed files with 334 additions and 56 deletions
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue