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

@ -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;