diff --git a/CMakeLists.txt b/CMakeLists.txt index 069ba891..e282c4b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_C_STANDARD 17) + # --- NUCLEAR STATIC OVERRIDES (MUST BE AT THE TOP) --- # These must run before ANY include(FetchContent) set(BUILD_SHARED_LIBS OFF CACHE BOOL "Global Static" FORCE) diff --git a/README.md b/README.md index fdef91e3..ac4f6417 100644 --- a/README.md +++ b/README.md @@ -21,15 +21,15 @@ Tool for control multiple GoPro Cameras, The design is for above 100 cameras con - [x] Preset changer - [x] Monitor cameras information - [x] Modify GoPro Camera Setting - - [ ] Custom preset apply pipeline - - [ ] Sync custom preset setting to all + - [x] Custom preset apply pipeline + - [x] Sync custom preset setting to all * WebCam - [x] Change to WebCam Mode * Preview - [x] Display selected GoPro camera - [x] Change setting while in preview Popup window * Media - - [ ] One click pull latest media file + - [x] One click pull latest media file - [ ] Media Browser for download and modify and delete etc... ## Screenshot diff --git a/package/deb_master_amd64 b/package/deb_master_amd64 index 41e1f952..a5cab6c4 100644 --- a/package/deb_master_amd64 +++ b/package/deb_master_amd64 @@ -1,5 +1,5 @@ Package: go-pro-control-master -Version: 0.1.3 +Version: 1.0.6 Section: shells Priority: optional Architecture: amd64 diff --git a/package/deb_server_amd64 b/package/deb_server_amd64 index a7a8a8f8..06aa0d35 100644 --- a/package/deb_server_amd64 +++ b/package/deb_server_amd64 @@ -1,5 +1,5 @@ Package: go-pro-control-server -Version: 0.1.3 +Version: 1.0.6 Section: shells Priority: optional Architecture: amd64 diff --git a/package/deb_server_arm64 b/package/deb_server_arm64 index 7b4d1437..016efb48 100644 --- a/package/deb_server_arm64 +++ b/package/deb_server_arm64 @@ -1,5 +1,5 @@ Package: go-pro-control-server -Version: 0.1.3 +Version: 1.0.6 Section: shells Priority: optional Architecture: arm64 diff --git a/package/win_master_x64.iss b/package/win_master_x64.iss index 663cf21a..5028bc0c 100755 --- a/package/win_master_x64.iss +++ b/package/win_master_x64.iss @@ -3,7 +3,7 @@ ; Non-commercial use only #define MyAppName "GoPro Master" -#define MyAppVersion "0.1.3" +#define MyAppVersion "1.0.6" #define MyAppPublisher "Elly" #define MyAppURL "https://github.com/Elly2018/GoPro_Controller" #define MyAppExeName "go-pro-master.exe" diff --git a/package/win_server_x64.iss b/package/win_server_x64.iss index 90db7aeb..4424e059 100755 --- a/package/win_server_x64.iss +++ b/package/win_server_x64.iss @@ -3,7 +3,7 @@ ; Non-commercial use only #define MyAppName "GoPro Server" -#define MyAppVersion "0.1.3" +#define MyAppVersion "1.0.6" #define MyAppPublisher "Elly" #define MyAppURL "https://github.com/Elly2018/GoPro_Controller" #define MyAppExeName "go-pro-server.exe" diff --git a/src/common/camera_code.h b/src/common/camera_code.h index 488f9cc7..34398037 100644 --- a/src/common/camera_code.h +++ b/src/common/camera_code.h @@ -322,6 +322,7 @@ const static int32_t GOPRO_MEDIA_STATUS_IDS[] = { // Lookup functions inline const int32_t GET_SETTING_SIZE_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_SIZE; case BURST_OUTPUT_ID : return BURST_OUTPUT_SIZE; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_SIZE; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_SIZE; @@ -393,6 +394,7 @@ inline const int32_t GET_SETTING_SIZE_BY_ID(int32_t x) { inline const int32_t GET_SETTING_AVA_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_AVA; case BURST_OUTPUT_ID : return BURST_OUTPUT_AVA; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_AVA; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_AVA; @@ -464,6 +466,7 @@ inline const int32_t GET_SETTING_AVA_BY_ID(int32_t x) { inline const char* GET_SETTING_NAME_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_NAME; case BURST_OUTPUT_ID : return BURST_OUTPUT_NAME; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_NAME; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_NAME; @@ -535,6 +538,7 @@ inline const char* GET_SETTING_NAME_BY_ID(int32_t x) { inline const char** GET_SETTING_STRING_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_STRING; case BURST_OUTPUT_ID : return BURST_OUTPUT_STRING; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_STRING; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_STRING; @@ -606,6 +610,7 @@ inline const char** GET_SETTING_STRING_BY_ID(int32_t x) { inline const int32_t* GET_SETTING_VALUE_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_VALUE; case BURST_OUTPUT_ID : return BURST_OUTPUT_VALUE; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_VALUE; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_VALUE; @@ -677,6 +682,7 @@ inline const int32_t* GET_SETTING_VALUE_BY_ID(int32_t x) { inline const int32_t* GET_SETTING_SUPPORT_BY_ID(int32_t x) { switch(x) { + case DENOISE_ID : return DENOISE_SUPPORT; case BURST_OUTPUT_ID : return BURST_OUTPUT_SUPPORT; case PHOTO_BURST_RATE_ID : return PHOTO_BURST_RATE_SUPPORT; case ISO_MIN_BURST_ID : return ISO_MIN_BURST_SUPPORT; diff --git a/src/common/camera_other.h b/src/common/camera_other.h index c16d125e..3233d0b0 100644 --- a/src/common/camera_other.h +++ b/src/common/camera_other.h @@ -3,9 +3,9 @@ #define CAMERA_OTHER #include +#pragma region GoPro Mode #define GOPRO_MODE_SIZE 9 #define GOPRO_MODE_NAME "Webcam Mode" - const static char* GOPRO_MODE_STRING[] = { "Video", "Photo Burst", @@ -17,7 +17,6 @@ const static char* GOPRO_MODE_STRING[] = { "Timelapse Video", "Timelapse Night Video", }; - const static int32_t GOPRO_MODE_VALUE[] = { 0, // Video 65538, // Photo Burst @@ -29,6 +28,20 @@ const static int32_t GOPRO_MODE_VALUE[] = { 131073, // Timelapse Video 131074, // Timelapse Night Video }; +#pragma endregion + +#pragma region Media Download Type +#define MEDIA_DOWNLOAD_TYPE_SIZE 3 +#define MEDIA_DOWNLOAD_TYPE_NAME "Media Download Type" +const static char* MEDIA_DOWNLOAD_TYPE_STRING[] = { + "All", + "Front Chars", + "Back Chars" +}; +const static int32_t MEDIA_DOWNLOAD_TYPE_VALUE[] = { + 0, 1, 2 +}; +#pragma endregion #define WEBCAM_START_RES_SIZE 3 #define WEBCAM_START_RES_NAME "Webcam Resolution" diff --git a/src/common/camera_setting.h b/src/common/camera_setting.h index 23e9873f..cc5cc5e7 100644 --- a/src/common/camera_setting.h +++ b/src/common/camera_setting.h @@ -2172,21 +2172,21 @@ const static int32_t SHARPNESS_SUPPORT[] = { #define DENOISE_ID 198 #define DENOISE_SIZE 3 #define DENOISE_NAME "Denoise" -#define DENOISE_AVA MODEL_MAX2_ALL +#define DENOISE_AVA MODEL_MAX2|MODEL_13 const static char* DENOISE_STRING[] = { "High", "Midium", - "Low" + "Low", }; const static int32_t DENOISE_VALUE[] = { 2, // High 1, // Midium - 1, // Low + 0, // Low }; const static int32_t DENOISE_SUPPORT[] = { - MODEL_MAX2_ALL, // High - MODEL_MAX2_ALL, // Midium - MODEL_MAX2_ALL, // Low + MODEL_MAX2|MODEL_13, // High + MODEL_MAX2|MODEL_13, // Midium + MODEL_MAX2|MODEL_13, // Low }; #pragma endregio diff --git a/src/common/iphelper.h b/src/common/iphelper.h index 0414cc4c..0ec1aeaa 100644 --- a/src/common/iphelper.h +++ b/src/common/iphelper.h @@ -206,6 +206,11 @@ inline size_t write_callback_pure(void* contents, size_t size, size_t nmemb, std return size * nmemb; } +inline size_t write_callback_char(void* contents, size_t size, size_t nmemb, std::vector* userp) { + userp->insert(userp->end(), static_cast(contents), static_cast(contents) + size * nmemb); + return size * nmemb; +} + inline std::string GetRemoteIPBySerial(std::string serial){ if(serial.size() < 3){ std::cerr << "Serial string must be at least 3" << "\n"; @@ -257,6 +262,26 @@ inline std::string exec(std::string cmd, int64_t timeout = 1500L, int64_t connec return result; } +inline std::vector exec_byte(std::string cmd, int64_t timeout = 1500L, int64_t connection_timeout = 1000L){ + CURL* curl = curl_easy_init(); + std::vector result = std::vector(); + + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, cmd.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_char); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, connection_timeout); + CURLcode res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + }else{ + std::cerr << "[Error] iphelper.h, Curl init failed" << std::endl; + } + + return std::move(result); +} + inline std::vector execs(std::vector cmds) { CURLM* curlm = curl_multi_init(); CURL* curl = NULL; diff --git a/src/master/GoProMaster.cpp b/src/master/GoProMaster.cpp index 4d19a3b7..509db87e 100644 --- a/src/master/GoProMaster.cpp +++ b/src/master/GoProMaster.cpp @@ -13,6 +13,8 @@ namespace fs = std::filesystem; +extern std::shared_ptr global_state; + GoProMaster::GoProMaster() { t1 = std::thread(&GoProMaster::update, this); std::cout << "GoProMaster created" << std::endl; @@ -25,7 +27,7 @@ GoProMaster::~GoProMaster() { t1.join(); } for (auto& s : servers) { - s->client.close(); + s->client->close(); std::lock_guard lock(camera_mtx); cleanCameraFromServer(s->ip); } @@ -40,9 +42,10 @@ std::string GoProMaster::addServer(const std::string& ip) { } auto conn = std::make_shared(); + conn->client = std::make_shared(); conn->ip = ip; - conn->client.onopen = [conn, this]() { + conn->client->onopen = [conn, this]() { std::cout << "Connected to server: " << conn->ip << std::endl; conn->connected = true; { @@ -52,12 +55,12 @@ std::string GoProMaster::addServer(const std::string& ip) { ImGui::InsertNotification(toast); } }; - conn->client.onmessage = [conn, this](const std::string& msg) { + conn->client->onmessage = [conn, this](const std::string& msg) { std::thread([=]() { processMessage(conn->ip, msg); }).detach(); }; - conn->client.onclose = [conn, this]() { + conn->client->onclose = [conn, this]() { if(conn->connected){ std::cout << "Disconnected from server: " << conn->ip << std::endl; conn->connected = false; @@ -85,7 +88,7 @@ void GoProMaster::reconnectAll() { std::cout << "Connecting to " << s->ip << "..." << std::endl; // Assuming ws://ip:9090 based on server implementation std::string url = "ws://" + s->ip + ":9090"; - s->client.open(url.c_str()); + s->client->open(url.c_str()); } } } @@ -94,7 +97,7 @@ void GoProMaster::disconnectAll(){ for (auto& s : servers) { if (s->connected) { std::cout << "Disconnect to " << s->ip << "..." << std::endl; - s->client.close(); + s->client->close(); } } } @@ -114,7 +117,7 @@ void GoProMaster::reconnect(const std::string& ip){ if(s->ip == ip && !s->connected){ std::cout << "Connecting to " << s->ip << "..." << std::endl; std::string url = "ws://" + s->ip + ":9090"; - s->client.open(url.c_str()); + s->client->open(url.c_str()); break; } } @@ -124,7 +127,7 @@ void GoProMaster::disconnect(const std::string& ip){ for(auto& s : servers){ if(s->ip == ip && s->connected){ std::cout << "Disconnecting to " << s->ip << "..." << std::endl; - s->client.close(); + s->client->close(); break; } } @@ -149,7 +152,7 @@ void GoProMaster::command_only(const std::string command, std::string target){ for(auto s : servers){ if(s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); } } } @@ -163,7 +166,7 @@ void GoProMaster::command_only(const std::string server, const std::string comma for(auto s : servers){ if(s->ip == server && s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); break; } } @@ -179,7 +182,7 @@ void GoProMaster::command_with_value(const std::string command, std::string targ for(auto s : servers){ if(s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); } } } @@ -193,7 +196,7 @@ void GoProMaster::query_only(const std::string command, std::string target){ for(auto s : servers){ if(s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); } } } @@ -207,7 +210,7 @@ void GoProMaster::query_only(const std::string server, const std::string command for(auto s : servers){ if(s->ip == server && s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); break; } } @@ -222,7 +225,7 @@ void GoProMaster::webcam_only(const std::string command, std::string target){ for(auto s : servers){ if(s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); } } } @@ -236,7 +239,7 @@ void GoProMaster::webcam_only(const std::string server, const std::string comman for(auto s : servers){ if(s->ip == server && s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); break; } } @@ -255,7 +258,7 @@ void GoProMaster::preview_start(std::string server, std::string target){ for(auto s : servers){ if(s->ip == server && s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); break; } } @@ -270,7 +273,7 @@ void GoProMaster::preview_end(std::string server, std::string target){ for(auto s : servers){ if((s->ip == server || server.size() == 0) && s->connected){ - s->client.send(data.dump()); + s->client->send(data.dump()); break; } } @@ -280,24 +283,47 @@ void GoProMaster::media_only(const std::string command, std::string target){ } -void GoProMaster::download_last_media(const std::string dir, bool put_finish){ +void GoProMaster::download_last_media(const std::string ip, const DownloadMediaParameters params){ + if(downloading_media_flag > 0) return; std::thread([=](){ - if(put_finish){ - downloading_last_media_flag = 2; + if(params.put_finish){ + downloading_media_flag = 2; }else{ - downloading_last_media_flag = 1; + downloading_media_flag = 1; } - downloading_last_media_total = 0; - downloading_last_media_done = 0; + downloading_media_total = 0; + downloading_media_done = 0; for(auto& s : cameras){ if(!s->connected) continue; - - std::string filename = s->name + fs::path(s->last_media).extension().string(); + if(ip.size() == 0 || s->ip != ip) continue; + std::string ext = fs::path(s->last_media).extension().string(); + std::string filename = s->name; if(filename.size() == 0 || s->name.size() == 0) { std::cerr << "[download_last_media] filename size is 0, we just skip..." << std::endl; continue; } + size_t filename_size = filename.size(); + std::cout << "[download_last_media] \ttype: " << params.type << std::endl; + std::cout << "[download_last_media] \tfilename: " << filename.c_str() << std::endl; + if(params.c_count > 0){ + std::string ccc = ""; + for(int32_t i = 0; i < params.c_count && i < filename_size; i++){ + if(params.type == 1){ + ccc += filename.at(0); + filename.erase(filename.begin()); + } + if(params.type == 2){ + ccc += filename.at(filename.size() - 1); + filename.erase(filename.begin() + filename.size() - 1); + } + } + if(params.type == 2){ + ccc.reserve(); + } + filename = ccc; + } + filename += ext; bool islocal = s->server == "127.0.0.1"; json data = json::object(); @@ -307,67 +333,139 @@ void GoProMaster::download_last_media(const std::string dir, bool put_finish){ data["value"]["item"] = s->name; data["value"]["ip"] = s->ip; data["value"]["local"] = islocal; - data["value"]["dir"] = dir; + data["value"]["dir"] = params.dir; data["value"]["filename"] = filename; for(auto ss : servers){ if(s->server == ss->ip && ss->connected){ - ss->client.send(data.dump()); - downloading_last_media_total++; + ss->client->send(data.dump()); + downloading_media_total++; break; } } } - if(downloading_last_media_total == 0){ - downloading_last_media_flag = 0; + if(downloading_media_total == 0){ + downloading_media_flag = 0; } }).detach(); } -void GoProMaster::download_last_media(const std::string ip, const std::string dir, bool put_finish){ +void GoProMaster::download_all_media(const std::string server, const std::string ip, const std::string folder, std::vector media_list){ + if(downloading_media_flag > 0) return; std::thread([=](){ - if(put_finish){ - downloading_last_media_flag = 2; - }else{ - downloading_last_media_flag = 1; - } - downloading_last_media_total = 0; - downloading_last_media_done = 0; + downloading_media_flag = 1; + downloading_media_total = 0; + downloading_media_done = 0; - for(auto& s : cameras){ - if(!s->connected) continue; - if(s->ip != ip) continue; - std::string filename = s->name + fs::path(s->last_media).extension().string(); - if(filename.size() == 0 || s->name.size() == 0) { - std::cerr << "[download_last_media] filename size is 0, we just skip..." << std::endl; - continue; + int32_t index = findCamera(server, ip); + if(index >= 0){ + const CameraInfo s = getCamera_Clone(index); + std::string ext = fs::path(s.last_media).extension().string(); + bool islocal = s.server == "127.0.0.1"; + + json data = json::object(); + data["key"] = "media"; + data["value"] = json::object(); + data["value"]["name"] = "d_all"; + data["value"]["item"] = s.name; + data["value"]["ip"] = s.ip; + data["value"]["local"] = islocal; + data["value"]["dir"] = folder; + data["value"]["filenames"] = json::array(); + + for(auto& m : media_list){ + data["value"]["filenames"].push_back(m.filename); } - bool islocal = s->server == "127.0.0.1"; + + for(auto ss : servers){ + if(s.server == ss->ip && ss->connected){ + ss->client->send(data.dump()); + downloading_media_total++; + break; + } + } + } + if(downloading_media_total == 0){ + downloading_media_flag = 0; + } + }).detach(); +} + +void GoProMaster::download_single_media(const std::string server, const std::string ip, const std::string filepath, MediaInfo media){ + if(downloading_media_flag > 0) return; + std::thread([=](){ + downloading_media_flag = 1; + downloading_media_total = 0; + downloading_media_done = 0; + + int32_t index = findCamera(server, ip); + if(index >= 0){ + const CameraInfo s = getCamera_Clone(index); + std::string ext = fs::path(s.last_media).extension().string(); + bool islocal = s.server == "127.0.0.1"; + fs::path p(filepath); json data = json::object(); data["key"] = "media"; data["value"] = json::object(); - data["value"]["name"] = "url"; - data["value"]["item"] = s->name; - data["value"]["ip"] = s->ip; + data["value"]["name"] = "d_single"; + data["value"]["item"] = s.name; + data["value"]["ip"] = s.ip; data["value"]["local"] = islocal; - data["value"]["dir"] = dir; - data["value"]["filename"] = filename; + data["value"]["dir"] = p.parent_path().string(); + data["value"]["filename"] = p.filename().string(); for(auto ss : servers){ - if(s->server == ss->ip && ss->connected){ - ss->client.send(data.dump()); - downloading_last_media_total++; + if(s.server == ss->ip && ss->connected){ + ss->client->send(data.dump()); + downloading_media_total++; break; } } } - if(downloading_last_media_total == 0){ - downloading_last_media_flag = 0; + if(downloading_media_total == 0){ + downloading_media_flag = 0; } }).detach(); } +void GoProMaster::get_media_info(const std::string server, const std::string ip, const std::string path){ + json data = json::object(); + data["key"] = "media"; + data["value"] = json::object(); + data["value"]["name"] = "info"; + data["value"]["path"] = path; + data["value"]["target"] = ip; + + for(auto s : servers){ + if(s->ip != server) continue; + bool islocal = s->ip == "127.0.0.1"; + data["value"]["local"] = islocal; + if((s->ip == server || server.size() == 0) && s->connected){ + s->client->send(data.dump()); + break; + } + } +} + +void GoProMaster::get_media_list(const std::string server, const std::string ip){ + json data = json::object(); + data["key"] = "media"; + data["value"] = json::object(); + data["value"]["name"] = "list"; + data["value"]["target"] = ip; + + for(auto s : servers){ + if(s->ip != server) continue; + bool islocal = s->ip == "127.0.0.1"; + data["value"]["local"] = islocal; + if((s->ip == server || server.size() == 0) && s->connected){ + s->client->send(data.dump()); + break; + } + } +} + void GoProMaster::presetSwitch(const std::string server, const std::string target, int32_t mode) { std::thread([=](){ for (auto& s : servers) { @@ -379,7 +477,7 @@ void GoProMaster::presetSwitch(const std::string server, const std::string targe get_status["value"]["name"] = "load"; get_status["value"]["mode"] = mode; get_status["value"]["target"] = target; - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } }).detach(); } @@ -413,7 +511,7 @@ void GoProMaster::apply(const std::string& ip, const std::string& target, const get_status["value"]["target"] = target; get_status["value"]["id"] = id; get_status["value"]["value"] = std::to_string(value); - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } }).detach(); } @@ -428,27 +526,40 @@ void GoProMaster::applyAll(const std::string& ip, const json& res){ get_status["value"]["source"] = ip; get_status["value"]["name"] = "setall"; get_status["value"]["value"] = res; - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } }).detach(); } -void GoProMaster::quickApplyAll(const std::shared_ptr& target){ - int32_t model = InspectorWindow::_get_current_model(target->hw); +void GoProMaster::quickApplyAll(const CameraInfo& target){ + int32_t model = InspectorWindow::_get_current_model(target.hw); json root = json::object(); json _set = json::object(); json _status = json::object(); - if(getSettingsFromCamera(*target, _set) && getStatusFromCamera(*target, _status)){ + if(getSettingsFromCamera(target, _set) && getStatusFromCamera(target, _status)){ // Execute the apply logic here int32_t p = _status[std::to_string(PRESET_ID)].get(); root["model"] = model; // Added a model field for mark it's supported root["preset"] = p; root["setting"] = _set; std::cout << "trying apply all, preset: " << p << std::endl; - applyAll(target->ip, root); + applyAll(target.ip, root); } } +void GoProMaster::stopApplyAll(const CameraInfo& target){ + std::thread([=](){ + for (auto& s : servers) { + if (!s->connected) continue; + json get_status = json::object(); + get_status["key"] = "query"; + get_status["value"] = json::object(); + get_status["value"]["name"] = "setall_cancel"; + s->client->send(get_status.dump()); + } + }).detach(); +} + bool GoProMaster::directoryExists(const std::string& path) { if (fs::exists(path) && fs::is_directory(path)) { return true; @@ -456,6 +567,10 @@ bool GoProMaster::directoryExists(const std::string& path) { return false; } +void GoProMaster::registerCameraMediaListFeedback(camera_media_list_feedback v){ + _camera_media_list_feedback = v; +} + void GoProMaster::registerCameraSettingFeedback(camera_setting_feedback v){ _camera_setting_feedback = v; } @@ -536,6 +651,7 @@ bool GoProMaster::remove_preset(const std::string name){ } return false; } + std::vector GoProMaster::get_preset_names(){ if(!(*preset_ptr)["data"].is_array()) { (*preset_ptr)["data"] = json::array(); @@ -560,16 +676,67 @@ const std::vector GoProMaster::getCameras_Clone() { { std::lock_guard lock(camera_mtx); for(auto& i : getCameras()){ - buffer.push_back(*i.get()); + CameraInfo sc = CameraInfo(); + sc.hw = json::parse(i->hw.dump()); + sc.state = json::parse(i->state.dump()); + sc.ip = i->ip; + sc.last_media = i->last_media; + sc.name = i->name; + sc.serial = i->serial; + sc.connected = i->connected; + sc.server = i->server; + buffer.push_back(sc); } } return buffer; } +const CameraInfo GoProMaster::getCamera_Clone(int32_t index) { + if(index < 0 || index >= cameras.size()) return CameraInfo(); + CameraInfo sc = CameraInfo(); + auto i = getCameras().at(index).get(); + sc.hw = i->hw; + sc.ip = i->ip; + sc.last_media = i->last_media; + sc.name = i->name; + sc.serial = i->serial; + sc.connected = i->connected; + sc.state = i->state; + sc.server = i->server; + return sc; +} + const std::vector>& GoProMaster::getServers() const { return servers; } +const std::vector GoProMaster::getServers_Clone() { + std::vector buffer = std::vector(); + { + std::lock_guard lock(server_mtx); + for(auto& i : getServers()){ + ServerConnection sc = ServerConnection(); + sc.ip = i->ip; + sc.connected = i->connected; + sc.last_message = i->last_message; + sc.client = i->client; + buffer.push_back(sc); + } + } + return buffer; +} + +const ServerConnection GoProMaster::getServer_Clone(int32_t index) { + if(index < 0 || index >= servers.size()) return ServerConnection(); + ServerConnection sc = ServerConnection(); + auto i = getServers().at(index).get(); + sc.ip = i->ip; + sc.connected = i->connected; + sc.last_message = i->last_message; + sc.client = i->client; + return sc; +} + void GoProMaster::update(){ while (!done) { for (auto& s : servers) { @@ -580,7 +747,7 @@ void GoProMaster::update(){ get_status["value"] = json::object(); get_status["value"]["name"] = "ip"; ipQueryFinish.insert_or_assign(s->ip, true); - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } for (auto& s : servers) { @@ -591,7 +758,7 @@ void GoProMaster::update(){ get_status["value"] = json::object(); get_status["value"]["name"] = "getall"; stateQueryFinish.insert_or_assign(s->ip, true); - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } for (auto& s : servers) { @@ -602,7 +769,7 @@ void GoProMaster::update(){ get_status["value"] = json::object(); get_status["value"]["name"] = "lastmedia"; mediaQueryFinish.insert_or_assign(s->ip, true); - s->client.send(get_status.dump()); + s->client->send(get_status.dump()); } std::lock_guard lock(locate_mtx); @@ -674,10 +841,12 @@ void GoProMaster::processMessage(const std::string& server, const std::string& m replaceCameraFromServer(server, ips); for(int32_t i = 0; i < ips.size(); i++){ - int32_t index = findCamera(ips[i]); - cameras[index]->serial = serial[i]; - if(names.count(ips[i])){ - cameras[index]->name = names.at(ips[i]); + int32_t index = findCamera(server, ips[i]); + if(index != -1){ + cameras[index]->serial = serial[i]; + if(names.count(ips[i])){ + cameras[index]->name = names.at(ips[i]); + } } } @@ -692,6 +861,10 @@ void GoProMaster::processMessage(const std::string& server, const std::string& m std::cerr << "query:get, return value should be array" << std::endl; return; } + if(key == "query:get" && data["value"]["data"].size() == 0){ + std::cout << "Camera response null state from server: " << server << std::endl; + return; + } /** * In case you're confuse here... * The data is like this @@ -720,7 +893,10 @@ void GoProMaster::processMessage(const std::string& server, const std::string& m continue; } std::string ip_ref = ip.value()["ip"].get(); - int32_t found = findCamera(ip_ref); + if(ip_ref.size() == 0){ + continue; + } + int32_t found = findCamera(server, ip_ref); CameraInfo _cam; if(found == -1){ auto cam = std::make_shared(); @@ -844,18 +1020,86 @@ void GoProMaster::processMessage(const std::string& server, const std::string& m size_t size = requests::downloadFile(urls.c_str(), path_target.c_str(), [&urls, &path_target](size_t received_bytes, size_t total_bytes){ std::cout << "[last_media] download " << urls << " => " << path_target << " " << received_bytes << " / " << total_bytes << std::endl; }); - downloading_last_media_done++; - if(downloading_last_media_done == downloading_last_media_total){ - if(downloading_last_media_flag == 2){ + downloading_media_done++; + if(downloading_media_done == downloading_media_total){ + if(downloading_media_flag == 2){ std::string finish_file = dir + "/" + "finish.txt"; FILE* f = fopen(finish_file.c_str(), "wb"); fclose(f); } - downloading_last_media_total = 0; - downloading_last_media_done = 0; - downloading_last_media_flag = 0; + downloading_media_total = 0; + downloading_media_done = 0; + downloading_media_flag = 0; } } + } + else if(key == "media:info"){ + + } + else if(key == "media:list"){ + std::vector media_list = std::vector(); + if(!data["value"]["data"].is_array()){ + std::cerr << "Invalid message from " << server << ": " << msg << std::endl; + std::cerr << "media:list, return value should be array" << std::endl; + return; + } + + for(auto ip = data["value"]["data"].begin(); ip != data["value"]["data"].end(); ++ip){ + std::string source = ""; + if(ip.value()["ip"].is_string()){ + source = ip.value()["ip"].get(); + } else continue; + if(ip.value()["status"].is_object()){ + if(ip.value()["status"]["media"].is_array()){ + auto mediaarr = ip.value()["status"]["media"]; + for(auto ip2 = mediaarr.begin(); ip2 != mediaarr.end(); ++ip2){ + std::string d = ""; + if(ip2.value()["d"].is_string()){ + d = ip2.value()["d"].get(); + } else continue; + if(ip2.value()["fs"].is_array()){ + auto fsarr = ip2.value()["fs"]; + for(auto ip3 = fsarr.begin(); ip3 != fsarr.end(); ++ip3){ + MediaInfo info = MediaInfo(); + if(ip3.value()["n"].is_string()){ + info.filename = d; + info.filename += "/"; + info.filename += ip3.value()["n"].get(); + } else continue; + if(ip3.value()["s"].is_number()){ + info.size = ip3.value()["s"].get(); + } + if(ip3.value()["cre"].is_number()){ + info.created = ip3.value()["cre"].get(); + } + if(ip3.value()["mod"].is_number()){ + info.modified = ip3.value()["mod"].get(); + } + media_list.push_back(info); + } + } + } + } + } + } + + if(_camera_media_list_feedback != NULL) + _camera_media_list_feedback(media_list); + } + else if(key == "media:thumbnail"){ + if(!data["value"]["data"].is_string()){ + std::cerr << "Invalid message from " << server << ": " << msg << std::endl; + std::cerr << "media:thumbnail, return value should be string" << std::endl; + return; + } + + std::vector raw_data = decodeBase64(data["value"]["data"].get()); + } + else if(key == "media:d_single"){ + + } + else if(key == "media:d_all"){ + } else{ std::cerr << "Invalid message from " << server << ": " << msg << std::endl; @@ -869,7 +1113,7 @@ void GoProMaster::processMessage(const std::string& server, const std::string& m void GoProMaster::sendToAll(const std::string& msg) { for (auto& s : servers) { if (s->connected) { - s->client.send(msg); + s->client->send(msg); } } } @@ -897,13 +1141,14 @@ void GoProMaster::replaceCameraFromServer(const std::string server, const std::v } return false; }); + cameras.erase(it, cameras.end()); // Append part for (const auto& new_ip : ips) { bool exists = false; for (const auto& existing_cam : cameras) { - if (existing_cam && existing_cam->ip == new_ip) { + if (existing_cam && existing_cam->ip == new_ip && existing_cam->server == server) { exists = true; break; } @@ -981,9 +1226,13 @@ bool GoProMaster::getStatusFromCamera(CameraInfo target, json& res){ } std::string GoProMaster::getBarInfo(const std::shared_ptr &c){ - json obj = c->state; + return getBarInfo(*c); +} + +std::string GoProMaster::getBarInfo(const CameraInfo &c){ + json obj = c.state; bool find = false; - std::string result = c->name + " " + c->serial + " " + c->ip + " "; + std::string result = c.name + " " + c.serial + " " + c.ip + " "; if(obj["settings"].is_object()){ if(obj["settings"]["2"].is_number()){ int32_t vr = obj["settings"]["2"].get(); @@ -1008,21 +1257,12 @@ std::string GoProMaster::getBarInfo(const std::shared_ptr &c){ find = true; } } - if(!find) return c->ip + " ..."; + if(!find) return c.ip + " ..."; return result; } -int32_t GoProMaster::findCamera(const std::string ip){ - int32_t index = 0; - for(const auto& c : cameras){ - if(c){ - if(c->ip == ip){ - return index; - } - } - ++index; - } - return -1; +size_t GoProMaster::getServerCount(){ + return servers.size(); } int32_t GoProMaster::findServer(const std::string ip){ @@ -1038,6 +1278,7 @@ int32_t GoProMaster::findServer(const std::string ip){ int32_t GoProMaster::findCamera(const std::string server, const std::string ip){ int32_t index = 0; + //std::lock_guard lock(camera_mtx); for(const auto& c : cameras){ if(c->server == server && c->ip == ip){ return index; @@ -1045,4 +1286,27 @@ int32_t GoProMaster::findCamera(const std::string server, const std::string ip){ ++index; } return -1; -} \ No newline at end of file +} + +std::vector GoProMaster::decodeBase64(const std::string& input) { + static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + std::vector T(256, -1); + for (int32_t i = 0; i < 64; i++) T[base64_chars[i]] = i; + + std::vector out; + int32_t val = 0, valb = -8; + for (u_char c : input) { + if (T[c] == -1) continue; // Skip non-base64 characters + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back(static_cast((val >> valb) & 0xFF)); + valb -= 8; + } + } + return out; +} diff --git a/src/master/GoProMaster.h b/src/master/GoProMaster.h index 39c93730..358ebb1b 100644 --- a/src/master/GoProMaster.h +++ b/src/master/GoProMaster.h @@ -14,9 +14,11 @@ #include #include "../common/iphelper.h" #include "../common/camera_code.h" +#include "data/state.h" #include "data/camera_info.h" #include "data/server_connection.h" +typedef void (*camera_media_list_feedback)(std::vector media_list); typedef void (*camera_setting_feedback)(std::string ip, json setting); typedef void (*camera_status_feedback)(std::string ip, json status); typedef void (*camera_hw_feedback)(std::string ip, json hw); @@ -24,6 +26,13 @@ typedef void (*camera_log_feedback)(std::string key, std::string value); typedef void (*camera_preset_save)(); typedef void (*camera_apply_all_feedback)(); +struct DownloadMediaParameters { + std::string dir; + bool put_finish; + int32_t type; + int32_t c_count; +}; + /// /// GoPro Master Worker /// Use this hub stuff to control multiple websocket server or camera @@ -88,18 +97,23 @@ class GoProMaster { void preview_start(std::string server, std::string target); void preview_end(std::string server, std::string target); void media_only(const std::string command, std::string target = ""); - void download_last_media(const std::string dir, bool put_finish); - void download_last_media(const std::string ip, const std::string dir, bool put_finish); + void download_last_media(const std::string ip, const DownloadMediaParameters params); + void download_all_media(const std::string server, const std::string ip, const std::string folder, std::vector media_list); + void download_single_media(const std::string server, const std::string ip, const std::string filepath, MediaInfo media); + void get_media_info(const std::string server, const std::string ip, const std::string path); + void get_media_list(const std::string server, const std::string ip); void presetSwitch(const std::string server, const std::string target, int32_t mode); void locate(const std::string server, const std::string target); int32_t haslocate(const std::string server, const std::string target); void apply(const std::string& ip, const std::string& target, const int32_t id, const int32_t value); void applyAll(const std::string& ip, const json& res); - void quickApplyAll(const std::shared_ptr& target); + void quickApplyAll(const CameraInfo& target); + void stopApplyAll(const CameraInfo& target); bool directoryExists(const std::string& path); + void registerCameraMediaListFeedback(camera_media_list_feedback v); /// /// Register the feedback event /// Called when fetch inspector setting data @@ -126,6 +140,7 @@ class GoProMaster { */ std::mutex camera_mtx; std::mutex locate_mtx; + std::mutex server_mtx; // ---------------------------------------------------------- // @@ -140,10 +155,13 @@ class GoProMaster { * Get current camera record (Clone, For thread optimization) */ const std::vector getCameras_Clone(); + const CameraInfo getCamera_Clone(int32_t index); /** * Get current websocket server record */ const std::vector>& getServers() const; + const std::vector getServers_Clone(); + const ServerConnection getServer_Clone(int32_t index); private: /** * All cameras record for master @@ -173,6 +191,7 @@ class GoProMaster { */ std::unordered_map stateQueryFinish = std::unordered_map(); std::unordered_map mediaQueryFinish = std::unordered_map(); + camera_media_list_feedback _camera_media_list_feedback = NULL; camera_setting_feedback _camera_setting_feedback = NULL; camera_status_feedback _camera_status_feedback = NULL; camera_hw_feedback _camera_hw_feedback = NULL; @@ -189,9 +208,9 @@ class GoProMaster { * 1: On (no finish txt) * 2: On (with finish txt) */ - std::atomic_char32_t downloading_last_media_flag = 0; - std::atomic_char32_t downloading_last_media_total; - std::atomic_char32_t downloading_last_media_done; + std::atomic_char32_t downloading_media_flag = 0; + std::atomic_char32_t downloading_media_total; + std::atomic_char32_t downloading_media_done; /** * The background thread for fetch update from all websocket server and update etc... @@ -219,8 +238,10 @@ class GoProMaster { bool getSettingsFromCamera(CameraInfo target, json& res); bool getStatusFromCamera(CameraInfo target, json& res); std::string getBarInfo(const std::shared_ptr &c); + std::string getBarInfo(const CameraInfo &c); - int32_t findCamera(const std::string ip); + size_t getServerCount(); int32_t findServer(const std::string ip); int32_t findCamera(const std::string server, const std::string ip); + std::vector decodeBase64(const std::string& input); }; diff --git a/src/master/data/camera_info.h b/src/master/data/camera_info.h index 558ae38f..abf12384 100644 --- a/src/master/data/camera_info.h +++ b/src/master/data/camera_info.h @@ -29,6 +29,6 @@ struct CameraInfo { /** * The json states */ - json state; - json hw; + json state = json::object(); + json hw = json::object(); }; \ No newline at end of file diff --git a/src/master/data/server_connection.h b/src/master/data/server_connection.h index 997d35e9..ad423e97 100644 --- a/src/master/data/server_connection.h +++ b/src/master/data/server_connection.h @@ -19,7 +19,7 @@ struct ServerConnection { /** * The libhv websocket client */ - hv::WebSocketClient client; + std::shared_ptr client; /** * Current connection state */ diff --git a/src/master/data/state.h b/src/master/data/state.h index a6a745cd..cb87ef83 100644 --- a/src/master/data/state.h +++ b/src/master/data/state.h @@ -6,6 +6,8 @@ */ #pragma once #include +#include +#include #include using json = nlohmann::json; @@ -17,8 +19,17 @@ enum class InspectorObjectType { Camera }; +struct MediaInfo { + std::string filename; + uint32_t created; + uint32_t modified; + size_t size; +}; + struct GlobalState { + SDL_Renderer* m_renderer; bool done; + int32_t applying_all_count = 0; bool applying_all; // Selection std::string websocket_server_selection; @@ -28,6 +39,8 @@ struct GlobalState { // Current select camera setting std::string current_camera_name = ""; std::string current_download_location = ""; + std::mutex media_list_mtx; + std::vector current_media_list = std::vector(); json current_setting_items; bool current_setting_items_bind = false; json current_status_items; diff --git a/src/master/main.cpp b/src/master/main.cpp index 0131bddd..4b04b4bf 100644 --- a/src/master/main.cpp +++ b/src/master/main.cpp @@ -45,7 +45,8 @@ std::shared_ptr start_webcam_popwin; std::shared_ptr preview_popwin; std::shared_ptr add_preset_popwin; std::shared_ptr preset_manager_popwin; -std::shared_ptr pop_windows_array[6]; +std::shared_ptr media_browser_popwin; +std::shared_ptr pop_windows_array[7]; // All the window flags ExecutionType execution_type = ExecutionType::SetAll; @@ -82,6 +83,10 @@ void background_worker(){ preset_manager_popwin->trigger(true); std::cout << "Detect preset_manager popup" << std::endl; } + else if(cmd == "media_browser"){ + media_browser_popwin->trigger(true); + std::cout << "Detect media_browser popup" << std::endl; + } } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } @@ -118,7 +123,15 @@ void hwGetterFeedback(std::string ip, json hw){ } void applyAllFeedback(){ - global_state->applying_all = false; + global_state->applying_all_count++; + if(global_state->applying_all_count >= master->getServerCount()){ + global_state->applying_all = false; + } +} + +void updateMediaList(std::vector data){ + std::lock_guard lock(global_state->media_list_mtx); + global_state->current_media_list = data; } void updateServerList(){ @@ -176,6 +189,7 @@ int main(int, char**) SDL_GL_MakeCurrent(window, gl_context); } SDL_Renderer* renderer = SDL_CreateRenderer(window, NULL); + global_state->m_renderer = renderer; servers = std::make_shared(loadServerList()); gui = std::make_shared(loadGUI()); @@ -192,7 +206,9 @@ int main(int, char**) WIN_INIT2(preview_popwin, PreviewPopup, renderer, pop_windows_array, 3); WIN_INIT(add_preset_popwin, AddPresetPopup, pop_windows_array, 4); WIN_INIT(preset_manager_popwin, PresetManagerPopup, pop_windows_array, 5); + WIN_INIT(media_browser_popwin, MediaBrowserPopup, pop_windows_array, 6); // Register event for master + master->registerCameraMediaListFeedback(updateMediaList); master->registerCameraSettingFeedback(settingGetterFeedback); master->registerCameraStatusFeedback(statusGetterFeedback); master->registerCameraHWFeedback(hwGetterFeedback); diff --git a/src/master/popup/add_camera_popwin.cpp b/src/master/popup/add_camera_popwin.cpp index cc324be2..5b271e75 100644 --- a/src/master/popup/add_camera_popwin.cpp +++ b/src/master/popup/add_camera_popwin.cpp @@ -17,29 +17,27 @@ AddCameraPopup::~AddCameraPopup(){ void AddCameraPopup::trigger(bool value){ BasePopWindow::trigger(value); if(value){ - std::lock_guard lock(master->camera_mtx); - const std::vector>& refs = master->getServers(); + const std::vector refs = master->getServers_Clone(); if(refs.size() > 0){ - server_ip_buf = refs[0]->ip; + server_ip_buf = refs[0].ip; } } } void AddCameraPopup::render(){ if(ImGui::BeginPopupModal(title.c_str(), NULL, wp_flag)){ - std::lock_guard lock(master->camera_mtx); - const std::vector>& all_servers = master->getServers(); + const std::vector all_servers = master->getServers_Clone(); bool updated = false; if(ImGui::BeginCombo("Server IP", server_ip_buf.c_str())){ for (int n = 0; n < all_servers.size(); n++) { - std::string option = all_servers.at(n)->ip; - bool is_selected = all_servers[n]->ip == server_ip_buf; + std::string option = all_servers.at(n).ip; + bool is_selected = all_servers.at(n).ip == server_ip_buf; option += "##PopupServerOption"; - ImGui::BeginDisabled(!all_servers[n]->connected); + ImGui::BeginDisabled(!all_servers.at(n).connected); if (ImGui::Selectable(option.c_str(), is_selected)) { - server_ip_buf = all_servers.at(n)->ip; + server_ip_buf = all_servers.at(n).ip; } ImGui::EndDisabled(); if (is_selected) @@ -63,11 +61,11 @@ void AddCameraPopup::render(){ error = "Server does not exist."; pass = false; } - if(!all_servers[index]->connected){ + if(!all_servers.at(index).connected){ error = "Server is disconnected."; pass = false; } - if(master->findCamera(GetRemoteIPBySerial(camera_serial_buf)) != -1){ + if(master->findCamera(server_ip_buf, GetRemoteIPBySerial(camera_serial_buf)) != -1){ error = "Camera already added."; pass = false; } diff --git a/src/master/popup/add_preset_popwin.cpp b/src/master/popup/add_preset_popwin.cpp index ac7913e1..f405934d 100644 --- a/src/master/popup/add_preset_popwin.cpp +++ b/src/master/popup/add_preset_popwin.cpp @@ -45,7 +45,7 @@ void AddPresetPopup::save_preset(){ json status = json::object(); json setting = json::object(); json data = json::object(); - int32_t t = master->findCamera(state->current_camera_item); + int32_t t = master->findCamera(state->current_camera_server, state->current_camera_item); if(t < 0) return; std::lock_guard lock(master->camera_mtx); const std::shared_ptr& c = master->getCameras().at(t); diff --git a/src/master/popup/media_browser_popwin.cpp b/src/master/popup/media_browser_popwin.cpp new file mode 100644 index 00000000..b76a8d79 --- /dev/null +++ b/src/master/popup/media_browser_popwin.cpp @@ -0,0 +1,210 @@ +#include "media_browser_popwin.h" +#include + +MediaBrowserPopup::MediaBrowserPopup( + std::shared_ptr _setting, + std::shared_ptr _state, + std::shared_ptr _master +) + : BasePopWindow(_setting, _state, _master) { + title = "Media Browser##Popup"; +} + + +MediaBrowserPopup::~MediaBrowserPopup(){ + +} + +void MediaBrowserPopup::trigger(bool value){ + BasePopWindow::trigger(value); + if(value){ + selected = ""; + master->get_media_list(state->current_camera_server, state->current_camera_item); + } +} + +void MediaBrowserPopup::render(){ + ImGuiIO& io = ImGui::GetIO(); + ImGuiStyle& style = ImGui::GetStyle(); + ImVec2 display_size = io.DisplaySize; + ImVec2 unit = ImVec2(display_size.x / 12.0f, display_size.y / 12.0f); + float unit_width; + float unit_height; + + ImGui::SetNextWindowPos(ImVec2(unit.x * 1.0F, unit.y * 1.0F), wp_cond); + ImGui::SetNextWindowSize(ImVec2(unit.x * 10.0F, unit.y * 10.0F), wp_cond); + + unit_width = (unit.x * 10.0F) / 2.0F - style.ItemSpacing.x; + unit_height = unit.y * 9.0F; + + if(ImGui::BeginPopupModal(title.c_str(), NULL, wp_flag)){ + { + int32_t s = master->findCamera(state->current_camera_server, state->current_camera_item); + if(s >= 0) { + const CameraInfo ci = master->getCamera_Clone(s); + ImGui::BeginChild("Body##Media_Browser_ChildWin_Body", ImVec2(unit.x * 10.0F - (style.ItemSpacing.x * 2), unit.y * 7.5F)); + { + draw_body(ci); + } + ImGui::EndChild(); + ImGui::BeginChild("Header##Media_Browser_ChildWin_Header", ImVec2(unit.x * 10.0F - (style.ItemSpacing.x * 2), unit.y * 1.6F)); + { + draw_header(ci); + } + ImGui::EndChild(); + } + } + ImGui::EndPopup(); + } +} + +void MediaBrowserPopup::draw_header(const CameraInfo& c){ + bool is_enable = c.ip.size() > 0; + bool have_select = selected.size() > 0; + ImGui::Dummy(ImVec2(10, 10)); + ImGui::Dummy(ImVec2(10, 10)); + ImGui::SameLine(); + + if(ImGui::Button("Cancel")){ + trigger(false); + } + ImGui::SameLine(); + ImGui::BeginDisabled(!have_select); + if(ImGui::Button("Copy URL")){ + + } + ImGui::SameLine(); + if(ImGui::Button("Download")){ + + } + ImGui::SameLine(); + if(ImGui::Button("Download To")){ + + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if(ImGui::Button("Download All To")){ + + } + ImGui::SameLine(); + ImGui::BeginDisabled(!have_select); + if(ImGui::Button("Delete")){ + + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if(ImGui::Button("Delete All")){ + + } + + ImGui::Dummy(ImVec2(10, 10)); + + ImGui::Dummy(ImVec2(10, 10)); + ImGui::SameLine(); + ImGui::Text("Camera: %s", c.name.c_str()); +} + +void MediaBrowserPopup::draw_body(const CameraInfo& c){ + if(c.ip.size() == 0) return; + std::lock_guard lock(state->media_list_mtx); + std::vector media_list = state->current_media_list; + for(int i = 0; i < media_list.size(); i++){ + const MediaInfo& mi = media_list.at(i); + if(draw_item(mi)){ + selected = mi.filename; + } + } +} + +bool MediaBrowserPopup::draw_item(const MediaInfo& mi){ + return ImGui::Selectable(mi.filename.c_str(), selected == mi.filename); +} + +void MediaBrowserPopup::open_dialog_for_download_all_folder_selection(){ + SDL_Window* win = SDL_GL_GetCurrentWindow(); + SDL_ShowOpenFolderDialog([](void* userdata, const char* const* filelist, int filter){ + if (!filelist) { + SDL_Log("Error opening file dialog: %s", SDL_GetError()); + } else if (!*filelist) { + SDL_Log("User canceled the dialog."); + } else { + std::string path = ""; + while (*filelist) { + SDL_Log("Selected file: %s", *filelist); + path = *filelist; + filelist++; + } + MediaBrowserPopup* self = static_cast(userdata); + self->download_all_folder_callback(path); + } + }, this, win, NULL, false); +} + +void MediaBrowserPopup::open_dialog_for_download_file_selection(){ + +} + +void MediaBrowserPopup::download_all_folder_callback(const std::string folder){ + int32_t s = master->findCamera(state->current_camera_server, state->current_camera_item); + if(s >= 0) { + const CameraInfo ci = master->getCamera_Clone(s); + master->download_all_media(ci.server, ci.ip, folder, state->current_media_list); + } +} + +void MediaBrowserPopup::download_file_callback(const std::string fullpath){ + int32_t s = master->findCamera(state->current_camera_server, state->current_camera_item); + if(s >= 0) { + const CameraInfo ci = master->getCamera_Clone(s); + + for(int32_t i = 0; i < state->current_media_list.size(); i++){ + const MediaInfo& mi = state->current_media_list.at(i); + if(mi.filename == selected){ + master->download_single_media(ci.server, ci.ip, fullpath, mi); + break; + } + } + } +} + +void MediaBrowserPopup::add_thumbnail(const std::string filename, const std::vector rawData, const std::pair resolution){ + thumbnail_rawData.insert_or_assign(filename, std::make_pair(rawData, resolution)); +} + +void MediaBrowserPopup::convert_rawData_to_texture(){ + for(auto& i : thumbnail_rawData){ + const std::string& filename = i.first; + const std::vector& rawData = i.second.first; + const std::pair& resolution = i.second.second; + + SDL_Texture* texture = SDL_CreateTexture(state->m_renderer, + SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_STATIC, + resolution.first, resolution.second); + if (texture) { + // 2. Upload the raw pixel data + // Pitch is the number of bytes in a row (width * 4 bytes for RGBA) + SDL_UpdateTexture(texture, nullptr, rawData.data(), resolution.first * 4); + // 3. Set filtering (Scaling mode) + // SDL_SCALEMODE_LINEAR is the equivalent to GL_LINEAR + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_LINEAR); + + // 4. Store in your map + // In SDL3 + Dear ImGui, ImTextureID is typically just the SDL_Texture pointer + thumbnail_textures.insert_or_assign(filename, (ImTextureID)texture); + } + } + thumbnail_rawData.clear(); +} + +void MediaBrowserPopup::clean_thumbnail(){ + thumbnail_rawData.clear(); +} + +void MediaBrowserPopup::clean_textures(){ + for(auto& i : thumbnail_textures){ + ImTextureID textureID = i.second; + SDL_DestroyTexture((SDL_Texture*)textureID); + } + thumbnail_textures.clear(); +} \ No newline at end of file diff --git a/src/master/popup/media_browser_popwin.h b/src/master/popup/media_browser_popwin.h new file mode 100644 index 00000000..c48df7b6 --- /dev/null +++ b/src/master/popup/media_browser_popwin.h @@ -0,0 +1,34 @@ +#pragma once +#include "base_pop_window.h" +#include +#include + +class MediaBrowserPopup : public BasePopWindow { +public: + MediaBrowserPopup( + std::shared_ptr _setting, + std::shared_ptr _state, + std::shared_ptr _master); + ~MediaBrowserPopup(); + + virtual void trigger(bool value) override; + virtual void render() override; +protected: + virtual void draw_header(const CameraInfo& c); + virtual void draw_body(const CameraInfo& c); + + virtual bool draw_item(const MediaInfo& mi); + virtual void open_dialog_for_download_all_folder_selection(); + virtual void open_dialog_for_download_file_selection(); + virtual void download_all_folder_callback(const std::string folder); + virtual void download_file_callback(const std::string fullpath); + + virtual void add_thumbnail(const std::string filename, const std::vector rawData, const std::pair resolution); + virtual void convert_rawData_to_texture(); + virtual void clean_thumbnail(); + virtual void clean_textures(); +private: + std::string selected = ""; + std::unordered_map, std::pair>> thumbnail_rawData; + std::unordered_map thumbnail_textures; +}; \ No newline at end of file diff --git a/src/master/popup/popwins.h b/src/master/popup/popwins.h index 5a82afb5..3b5aadee 100644 --- a/src/master/popup/popwins.h +++ b/src/master/popup/popwins.h @@ -10,4 +10,5 @@ #include "scan_camera_popwin.h" #include "start_webcam_popwin.h" #include "add_preset_popwin.h" -#include "preset_manager_popwin.h" \ No newline at end of file +#include "preset_manager_popwin.h" +#include "media_browser_popwin.h" \ No newline at end of file diff --git a/src/master/popup/preview/decode.cpp b/src/master/popup/preview/decode.cpp index eb8f1e95..71e64f28 100644 --- a/src/master/popup/preview/decode.cpp +++ b/src/master/popup/preview/decode.cpp @@ -16,18 +16,17 @@ void PreviewPopup::update_decoder(){ } { - std::lock_guard lock(master->camera_mtx); - s = master->findCamera(state->preview_ip); + s = master->findCamera(state->preview_server, state->preview_ip); if(s == -1){ std::cout << "[Preview Decoder] Cannot find camera: " << state->preview_ip << std::endl; trying = false; return; } - const std::shared_ptr& c = master->getCameras().at(s); - model = _get_current_model(c->hw); + const CameraInfo c = master->getCamera_Clone(s); + model = _get_current_model(c.hw); json buffer_setting = json::object(); - master->getSettingsFromCamera(*c, buffer_setting); + master->getSettingsFromCamera(c, buffer_setting); } if(gl_texture != 0){ diff --git a/src/master/popup/preview/preview_popwin.cpp b/src/master/popup/preview/preview_popwin.cpp index 704adc63..67294ec2 100644 --- a/src/master/popup/preview/preview_popwin.cpp +++ b/src/master/popup/preview/preview_popwin.cpp @@ -48,11 +48,11 @@ void PreviewPopup::set_window_data(json data){ } } -void PreviewPopup::register_setting_drawer(std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> caller){ +void PreviewPopup::register_setting_drawer(std::function& state, std::shared_ptr& master, const CameraInfo& c)> caller){ setting_drawer = caller; } -void PreviewPopup::register_protune_drawer(std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> caller){ +void PreviewPopup::register_protune_drawer(std::function& state, std::shared_ptr& master, const CameraInfo& c)> caller){ protune_drawer = caller; } @@ -115,7 +115,7 @@ void PreviewPopup::_draw_rotation_button(){ void PreviewPopup::_draw_camera_selection(){ int32_t s = -1; std::lock_guard lock(master->camera_mtx); - s = master->findCamera(state->preview_ip); + s = master->findCamera(state->preview_server, state->preview_ip); if(s != -1){ const std::shared_ptr& c = master->getCameras().at(s); std::string display_name = c->name; @@ -162,10 +162,9 @@ void PreviewPopup::_draw_bottom_button(){ void PreviewPopup::_draw_setting(){ if(setting_drawer != NULL){ int32_t s = -1; - std::lock_guard lock(master->camera_mtx); - s = master->findCamera(state->preview_ip); + s = master->findCamera(state->preview_server, state->preview_ip); if(s != -1){ - const std::shared_ptr& c = master->getCameras().at(s); + const CameraInfo c = master->getCamera_Clone(s); if(ImGui::Button("Quick Apply All##Preview_Popwin_Action")){ state->applying_all = true; master->quickApplyAll(c); diff --git a/src/master/popup/preview_popwin.h b/src/master/popup/preview_popwin.h index a06d929f..efa37819 100644 --- a/src/master/popup/preview_popwin.h +++ b/src/master/popup/preview_popwin.h @@ -53,8 +53,8 @@ class PreviewPopup : public BasePopWindow { json get_window_data() override; void set_window_data(json data) override; - void register_setting_drawer(std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> caller); - void register_protune_drawer(std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> caller); + void register_setting_drawer(std::function& state, std::shared_ptr& master, const CameraInfo& c)> caller); + void register_protune_drawer(std::function& state, std::shared_ptr& master, const CameraInfo& c)> caller); virtual void trigger(bool value) override; virtual void update_decoder(); @@ -70,8 +70,8 @@ class PreviewPopup : public BasePopWindow { void _draw_setting(); private: - std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> setting_drawer; - std::function& state, std::shared_ptr& master, const std::shared_ptr& c)> protune_drawer; + std::function& state, std::shared_ptr& master, const CameraInfo& c)> setting_drawer; + std::function& state, std::shared_ptr& master, const CameraInfo& c)> protune_drawer; cv::VideoCapture cap; std::string pipeline; std::queue frame_queue; diff --git a/src/master/windows/camera_list.h b/src/master/windows/camera_list.h index 591df14a..b2fe580e 100644 --- a/src/master/windows/camera_list.h +++ b/src/master/windows/camera_list.h @@ -45,15 +45,15 @@ class CameraListWindow : public BaseWindow { void set_window_data(json data) override; virtual void render() override; - virtual void draw_line(const std::shared_ptr& c); - virtual void draw_group_state(const std::shared_ptr& c); - virtual void draw_group_header(const std::shared_ptr& c); - virtual void item_event(const std::shared_ptr& c); - void onClick(const std::shared_ptr& c); + virtual void draw_line(const CameraInfo& c); + virtual void draw_group_state(const CameraInfo& c); + virtual void draw_group_header(const CameraInfo& c); + virtual void item_event(const CameraInfo& c); + void onClick(const CameraInfo& c); private: ImVec2 get_rect_size(); - std::vector> get_filtering_result(); + std::vector get_filtering_result(); std::string get_filter_string(FilterType type); std::string get_sort_string(SortType type); std::string toTimeCode(int32_t timer); diff --git a/src/master/windows/camera_list/camera_list.cpp b/src/master/windows/camera_list/camera_list.cpp index a13cc159..7b502ac9 100644 --- a/src/master/windows/camera_list/camera_list.cpp +++ b/src/master/windows/camera_list/camera_list.cpp @@ -60,7 +60,6 @@ void CameraListWindow::render(){ ImGui::Begin(title.c_str(), &enable, w_flag); { ImVec2 window_size = ImGui::GetWindowSize(); - std::lock_guard lock(master->camera_mtx); changed = ImGui::SliderInt("Item Size##Camera_List_Size", &size, 0, 15); changed = changed || ImGui::InputText("Search", &search); @@ -110,10 +109,10 @@ void CameraListWindow::render(){ float width = ImGui::GetWindowSize().x; // Total space size int32_t limit = static_cast((width / rect_size.x) - 0.5f); int32_t counter = 0; - std::vector> ciss = get_filtering_result(); + std::vector ciss = get_filtering_result(); ImGui::Text("Cal: %f/%f, Total: %d, Line: %d", width, rect_size.x, size, limit); for(const auto& c : ciss){ - if(c){ + if(c.ip.size() > 0){ try{ if(size == 0) draw_line(c); else { @@ -141,10 +140,10 @@ void CameraListWindow::render(){ } } -void CameraListWindow::draw_line(const std::shared_ptr& c){ - bool selected = c->ip == state->current_camera_item && c->server == state->current_camera_server; +void CameraListWindow::draw_line(const CameraInfo& c){ + bool selected = c.ip == state->current_camera_item && c.server == state->current_camera_server; std::string plusStatus = master->getBarInfo(c); - std::string plusID = plusStatus + "##CameraList_" + c->ip; + std::string plusID = plusStatus + "##CameraList_" + c.ip; if(ImGui::Selectable(plusID.c_str(), selected)){ // User select interaction onClick(c); @@ -152,60 +151,60 @@ void CameraListWindow::draw_line(const std::shared_ptr& c){ item_event(c); } -void CameraListWindow::item_event(const std::shared_ptr& c){ +void CameraListWindow::item_event(const CameraInfo& c){ if(ImGui::IsItemClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()){ onClick(c); } if(ImGui::IsItemClicked(ImGuiMouseButton_Right) && ImGui::IsItemHovered()){ ImGui::OpenPopupOnItemClick(); } - if(ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered() && c->connected){ - master->preview_start(c->server, c->ip); - state->preview_ip = c->ip; - state->preview_server = c->server; + if(ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered() && c.connected){ + master->preview_start(c.server, c.ip); + state->preview_ip = c.ip; + state->preview_server = c.server; state->command_sender("preview_start"); } - if(ImGui::BeginPopupContextItem((title + "##Popup_Menu" + c->ip).c_str())){ - ImGui::BeginDisabled(!c->connected); + if(ImGui::BeginPopupContextItem((title + "##Popup_Menu" + c.ip).c_str())){ + ImGui::BeginDisabled(!c.connected); if (ImGui::Selectable("Connect")){ - master->command_only(c->server, "usb_on", c->ip); + master->command_only(c.server, "usb_on", c.ip); } if (ImGui::Selectable("Disconnect")){ - master->command_only(c->server, "usb_off", c->ip); + master->command_only(c.server, "usb_off", c.ip); } ImGui::Separator(); if (ImGui::Selectable("Reboot")) { - master->command_only(c->server, "reboot", c->ip); + master->command_only(c.server, "reboot", c.ip); } if (ImGui::Selectable("Shutdown")) { - master->command_only(c->server, "shutdown", c->ip); + master->command_only(c.server, "shutdown", c.ip); } if (ImGui::Selectable("Preview")) { - master->preview_start(c->server, c->ip); - state->preview_ip = c->ip; - state->preview_server = c->server; + master->preview_start(c.server, c.ip); + state->preview_ip = c.ip; + state->preview_server = c.server; state->command_sender("preview_start"); } ImGui::EndDisabled(); if (ImGui::Selectable("Delete")) { - master->command_only(c->server, "delete", c->ip); + master->command_only(c.server, "delete", c.ip); } ImGui::EndPopup(); } } -void CameraListWindow::onClick(const std::shared_ptr& c){ +void CameraListWindow::onClick(const CameraInfo& c){ state->current_setting_items_bind = false; - state->current_camera_item = c->ip; - state->current_camera_server = c->server; - state->current_camera_name = c->name; - std::cout << "Select camera: " << c->server << " " << c->ip << std::endl; - master->query_only(c->server, "get", c->ip); + state->current_camera_item = c.ip; + state->current_camera_server = c.server; + state->current_camera_name = c.name; + std::cout << "Select camera: " << c.server << " " << c.ip << std::endl; + master->query_only(c.server, "get", c.ip); //current_setting_items_bind = master.getSettingsFromCamera(*c, current_setting_items); } @@ -213,24 +212,24 @@ ImVec2 CameraListWindow::get_rect_size(){ return ImVec2(10 * (size + 10) + 20, 10 * (size + 10) + 20); } -std::vector> CameraListWindow::get_filtering_result(){ - auto buffer = master->getCameras(); - auto filtered = std::vector>(); +std::vector CameraListWindow::get_filtering_result(){ + auto buffer = master->getCameras_Clone(); + auto filtered = std::vector(); for(auto& c : buffer){ if(search.size() > 0){ - bool ip_contain = c->ip.find(search) != std::string::npos; - bool name_contain = c->name.find(search) != std::string::npos; - bool serial_contain = c->serial.find(search) != std::string::npos; + bool ip_contain = c.ip.find(search) != std::string::npos; + bool name_contain = c.name.find(search) != std::string::npos; + bool serial_contain = c.serial.find(search) != std::string::npos; if(!ip_contain && !name_contain && !serial_contain) continue; } if(filter == FilterType::Connect){ - if(c->connected == filter_connect){ + if(c.connected == filter_connect){ filtered.push_back(c); } }else if(filter == FilterType::Server){ - if(c->server == filter_ip){ + if(c.server == filter_ip){ filtered.push_back(c); } }else { @@ -239,12 +238,12 @@ std::vector> CameraListWindow::get_filtering_result( } if (sort == SortType::Name){ - std::sort(filtered.begin(), filtered.end(), [](const std::shared_ptr& a, const std::shared_ptr& b){ - return a->name < b->name; + std::sort(filtered.begin(), filtered.end(), [](const CameraInfo& a, const CameraInfo& b){ + return a.name < b.name; }); }else if (sort == SortType::IP){ - std::sort(filtered.begin(), filtered.end(), [](const std::shared_ptr& a, const std::shared_ptr& b){ - return a->ip < b->ip; + std::sort(filtered.begin(), filtered.end(), [](const CameraInfo& a, const CameraInfo& b){ + return a.ip < b.ip; }); } diff --git a/src/master/windows/camera_list/display_header.cpp b/src/master/windows/camera_list/display_header.cpp index 9f2c0d00..e32d96e3 100644 --- a/src/master/windows/camera_list/display_header.cpp +++ b/src/master/windows/camera_list/display_header.cpp @@ -1,17 +1,17 @@ #include "../camera_list.h" -void CameraListWindow::draw_group_header(const std::shared_ptr& c){ +void CameraListWindow::draw_group_header(const CameraInfo& c){ json status = json::object(); json setting = json::object(); int preset = 0; - master->getStatusFromCamera(*c, status); - master->getSettingsFromCamera(*c, setting); + master->getStatusFromCamera(c, status); + master->getSettingsFromCamera(c, setting); ImGui::BeginGroup(); ImGuiStyle& style = ImGui::GetStyle(); float fontsize = ImGui::GetFontSize(); - std::string id = c->ip + "##Grid_Item_ID"; - std::string pid = c->ip + "##Grid_Popup_Item_ID"; + std::string id = c.ip + "##Grid_Item_ID"; + std::string pid = c.ip + "##Grid_Popup_Item_ID"; ImGui::PushID(id.c_str()); ImDrawList* draw_list = ImGui::GetWindowDrawList(); @@ -37,11 +37,11 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ // Drawing outline { uint32_t col = col_red; - if(c->connected) col = col_greed; + if(c.connected) col = col_greed; draw_list->AddRect(image_pos + ImVec2(5, 5), image_pos_max - ImVec2(5, 5), col, 2.0F, 0, 2.0F); } // Drawing Battery - if(c->connected){ + if(c.connected){ bool batteryHave = false; bool batteryCharging = false; int precentage = 0; @@ -108,7 +108,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ } } // Drawing SD card - if(c->connected){ + if(c.connected){ bool sdHave = false; int64_t remaining = 0; uint32_t col_inner_color; @@ -157,7 +157,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ // Center text { std::string shutter_speed; - std::string camera_title = c->name; + std::string camera_title = c.name; std::string iso_setting; std::string white_balance_setting; float spacing = 0.75F; @@ -174,7 +174,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ center.y + ( camera_title_size.y / -2.0F ) + (rect_size_unit.y * (spacing * -3.0F)) ); draw_list->AddText(camera_title_min, col_white, camera_title.c_str()); - if(c->connected) { + if(c.connected) { int32_t SHUTTER_SPEED_ID = SHUTTER_SPEED_PHOTO_ID; int32_t ISO_MIN_ID = ISO_MIN_PHOTO_ID; int32_t ISO_MAX_ID = ISO_MAX_PHOTO_ID; @@ -231,7 +231,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ } } // Record Time - if(c->connected){ + if(c.connected){ ImVec2 frame_padding = ImVec2(10, 10); ImVec2 center = ImVec2( image_pos.x + (rect_size.x / 2.0F), @@ -253,7 +253,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ draw_list->AddText(record_time_min, col_white, record_time.c_str()); } // Preset mode - if(c->connected){ + if(c.connected){ int32_t preset; std::string preset_text; if(status[std::to_string(PRESET_ID)].is_number_integer()){ @@ -279,7 +279,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ ), col_white, preset_text.c_str()); } // Setting - if(c->connected){ + if(c.connected){ float spacing = 0.75F; float word_spacing = 10; std::string res; @@ -335,7 +335,7 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ ImGui::PopID(); ImGui::Dummy(rect_size); - bool is_select = state->current_camera_item == c->ip && state->current_camera_server == c->server; + bool is_select = state->current_camera_item == c.ip && state->current_camera_server == c.server; if(ImGui::IsItemHovered()){ std::string displayText = ""; displayText += "name: "; @@ -343,28 +343,28 @@ void CameraListWindow::draw_group_header(const std::shared_ptr& c){ displayText += "\n"; displayText += "server: "; - displayText += c->server; + displayText += c.server; displayText += "\n"; displayText += "ip: "; - displayText += c->ip; + displayText += c.ip; displayText += "\n"; displayText += "serial: "; - if(c->hw["serial_number"].is_string()){ - displayText += c->hw["serial_number"].get(); + if(c.hw.is_object() && c.hw["serial_number"].is_string()){ + displayText += c.hw["serial_number"].get(); } displayText += "\n"; displayText += "model: "; - if(c->hw["model_name"].is_string()){ - displayText += c->hw["model_name"].get(); + if(c.hw.is_object() && c.hw["model_name"].is_string()){ + displayText += c.hw["model_name"].get(); } displayText += "\n"; displayText += "firmware: "; - if(c->hw["firmware_version"].is_string()){ - displayText += c->hw["firmware_version"].get(); + if(c.hw.is_object() && c.hw["firmware_version"].is_string()){ + displayText += c.hw["firmware_version"].get(); } displayText += "\n"; ImGui::SetItemTooltip("%s", displayText.c_str()); diff --git a/src/master/windows/camera_list/display_state.cpp b/src/master/windows/camera_list/display_state.cpp index c7d6608b..0fa96cf2 100644 --- a/src/master/windows/camera_list/display_state.cpp +++ b/src/master/windows/camera_list/display_state.cpp @@ -1,426 +1,430 @@ #include "../camera_list.h" -void CameraListWindow::draw_group_state(const std::shared_ptr& c){ +void CameraListWindow::draw_group_state(const CameraInfo& c){ json status = json::object(); json setting = json::object(); int32_t preset = 0; - master->getStatusFromCamera(*c, status); - master->getSettingsFromCamera(*c, setting); + master->getStatusFromCamera(c, status); + master->getSettingsFromCamera(c, setting); ImGui::BeginGroup(); - ImGuiStyle& style = ImGui::GetStyle(); - float fontsize = ImGui::GetFontSize(); - std::string id = c->ip + "##Grid_Item_ID"; - std::string pid = c->ip + "##Grid_Popup_Item_ID"; - ImGui::PushID(id.c_str()); - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - ImVec2 image_pos = ImGui::GetCursorScreenPos(); - ImVec2 rect_size = get_rect_size(); - ImVec2 rect_size_unit = ImVec2(rect_size.x / 12.0F, rect_size.y / 12.0F); - - ImVec2 image_pos_max = image_pos + rect_size; - uint32_t col_white = IM_COL32(255, 255, 255, 255); - uint32_t col_grey = IM_COL32(210, 210, 210, 255); - uint32_t col_grey_light = IM_COL32(210, 210, 210, 50); - uint32_t col_red = IM_COL32(230, 10, 10, 255); - uint32_t col_orange = IM_COL32(230, 230, 10, 255); - uint32_t col_orange_dark = IM_COL32(80, 80, 10, 255); - uint32_t col_greed = IM_COL32(10, 230, 10, 255); - - // Get preset - { - if(status[std::to_string(PRESET_ID)].is_number_integer()){ - preset = status[std::to_string(PRESET_ID)].get(); + { + ImGuiStyle& style = ImGui::GetStyle(); + float fontsize = ImGui::GetFontSize(); + std::string id = c.ip + "##Grid_Item_ID"; + std::string pid = c.ip + "##Grid_Popup_Item_ID"; + ImGui::PushID(id.c_str()); + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImVec2 image_pos = ImGui::GetCursorScreenPos(); + ImVec2 rect_size = get_rect_size(); + ImVec2 rect_size_unit = ImVec2(rect_size.x / 12.0F, rect_size.y / 12.0F); + + ImVec2 image_pos_max = image_pos + rect_size; + uint32_t col_white = IM_COL32(255, 255, 255, 255); + uint32_t col_grey = IM_COL32(210, 210, 210, 255); + uint32_t col_grey_light = IM_COL32(210, 210, 210, 50); + uint32_t col_red = IM_COL32(230, 10, 10, 255); + uint32_t col_orange = IM_COL32(230, 230, 10, 255); + uint32_t col_orange_dark = IM_COL32(80, 80, 10, 255); + uint32_t col_greed = IM_COL32(10, 230, 10, 255); + + // Get preset + { + if(status[std::to_string(PRESET_ID)].is_number_integer()){ + preset = status[std::to_string(PRESET_ID)].get(); + } } - } - // Drawing outline - { - uint32_t col = col_red; - if(c->connected) col = col_greed; - draw_list->AddRect(image_pos + ImVec2(5, 5), image_pos_max - ImVec2(5, 5), col, 2.0F, 0, 2.0F); - } - // Drawing Battery - if(c->connected){ - bool batteryHave = false; - bool batteryCharging = false; - int precentage = 0; - uint32_t col_inner_color; - if(status[std::to_string(BATTERY_PRESENT_ID)].is_number()){ - batteryHave = status[std::to_string(BATTERY_PRESENT_ID)].get() == 1; - } - if(status[std::to_string(INTERNAL_BATTERY_PERCENTAGE_ID)].is_number()){ - precentage = status[std::to_string(INTERNAL_BATTERY_PERCENTAGE_ID)].get(); - } - if(status[std::to_string(INTERNAL_BATTERY_BARS_ID)].is_number()){ - batteryCharging = status[std::to_string(INTERNAL_BATTERY_BARS_ID)].get() == 4; + // Drawing outline + { + uint32_t col = col_red; + if(c.connected) col = col_greed; + draw_list->AddRect(image_pos + ImVec2(5, 5), image_pos_max - ImVec2(5, 5), col, 2.0F, 0, 2.0F); } - ImVec2 battery_text_size; - std::string battery_text; - if(batteryHave){ - if(precentage <= 25){ - col_inner_color = col_orange; + // Drawing Battery + if(c.connected){ + bool batteryHave = false; + bool batteryCharging = false; + int precentage = 0; + uint32_t col_inner_color; + if(status[std::to_string(BATTERY_PRESENT_ID)].is_number()){ + batteryHave = status[std::to_string(BATTERY_PRESENT_ID)].get() == 1; + } + if(status[std::to_string(INTERNAL_BATTERY_PERCENTAGE_ID)].is_number()){ + precentage = status[std::to_string(INTERNAL_BATTERY_PERCENTAGE_ID)].get(); + } + if(status[std::to_string(INTERNAL_BATTERY_BARS_ID)].is_number()){ + batteryCharging = status[std::to_string(INTERNAL_BATTERY_BARS_ID)].get() == 4; + } + ImVec2 battery_text_size; + std::string battery_text; + if(batteryHave){ + if(precentage <= 25){ + col_inner_color = col_orange; + }else{ + col_inner_color = col_greed; + } + battery_text = std::to_string(precentage) + "%"; }else{ - col_inner_color = col_greed; + col_inner_color = col_red; + battery_text = "X"; + } + battery_text_size = ImGui::CalcTextSize(battery_text.c_str()); + ImVec2 bettery_size = ImVec2(rect_size_unit.x * 2.2, rect_size_unit.y * 1.2F); + ImVec2 frame_padding = ImVec2(10, 10); + ImVec2 inner_padding = ImVec2(2, 2); + int32_t spacing = 15; + + // Drawing Battery outline + ImVec2 outter_min = image_pos + ImVec2(rect_size.x - (frame_padding.x + bettery_size.x), frame_padding.y); + ImVec2 outter_max = image_pos + ImVec2(rect_size.x - frame_padding.x, frame_padding.y + bettery_size.y); + draw_list->AddRectFilled( + outter_min, + outter_max, + col_grey); + + // Drawing Battery inline + ImVec2 inner_min = outter_min + inner_padding; + ImVec2 inner_max = outter_max - inner_padding; + float w = inner_max.y - inner_min.y; + float wc = w * (precentage / 100.0F); + float wr = w - wc; + inner_max.x -= wr; + draw_list->AddRectFilled( + inner_min, + inner_max, + col_inner_color); + + // Drawing Battery text + ImVec2 text_pos = ImVec2(outter_max.x - battery_text_size.x, outter_max.y); + draw_list->AddText(text_pos, col_white, battery_text.c_str()); + + if(batteryCharging){ + std::string charging_text = "C=>"; + ImVec2 charging_text_size = ImGui::CalcTextSize(charging_text.c_str()); + ImVec2 charging_text_pos = ImVec2(inner_min.x, + ((inner_max.y + inner_min.y) / 2.0F) - (charging_text_size.y / 2.0F) + ); + draw_list->AddText(charging_text_pos, col_orange_dark, charging_text.c_str()); } - battery_text = std::to_string(precentage) + "%"; - }else{ - col_inner_color = col_red; - battery_text = "X"; - } - battery_text_size = ImGui::CalcTextSize(battery_text.c_str()); - ImVec2 bettery_size = ImVec2(rect_size_unit.x * 2.2, rect_size_unit.y * 1.2F); - ImVec2 frame_padding = ImVec2(10, 10); - ImVec2 inner_padding = ImVec2(2, 2); - int32_t spacing = 15; - - // Drawing Battery outline - ImVec2 outter_min = image_pos + ImVec2(rect_size.x - (frame_padding.x + bettery_size.x), frame_padding.y); - ImVec2 outter_max = image_pos + ImVec2(rect_size.x - frame_padding.x, frame_padding.y + bettery_size.y); - draw_list->AddRectFilled( - outter_min, - outter_max, - col_grey); - - // Drawing Battery inline - ImVec2 inner_min = outter_min + inner_padding; - ImVec2 inner_max = outter_max - inner_padding; - float w = inner_max.y - inner_min.y; - float wc = w * (precentage / 100.0F); - float wr = w - wc; - inner_max.x -= wr; - draw_list->AddRectFilled( - inner_min, - inner_max, - col_inner_color); - - // Drawing Battery text - ImVec2 text_pos = ImVec2(outter_max.x - battery_text_size.x, outter_max.y); - draw_list->AddText(text_pos, col_white, battery_text.c_str()); - - if(batteryCharging){ - std::string charging_text = "C=>"; - ImVec2 charging_text_size = ImGui::CalcTextSize(charging_text.c_str()); - ImVec2 charging_text_pos = ImVec2(inner_min.x, - ((inner_max.y + inner_min.y) / 2.0F) - (charging_text_size.y / 2.0F) - ); - draw_list->AddText(charging_text_pos, col_orange_dark, charging_text.c_str()); - } - } - // Drawing SD card - if(c->connected){ - bool sdHave = false; - int64_t remaining = 0; - uint32_t col_inner_color; - if(status[std::to_string(PRIMARY_STORAGE_ID)].is_number()){ - int b = status[std::to_string(PRIMARY_STORAGE_ID)].get() == 1; - if(b == 1) sdHave = true; - } - if(status[std::to_string(SD_CARD_REMAINING_ID)].is_number()){ - remaining = status[std::to_string(SD_CARD_REMAINING_ID)].get(); - } - ImVec2 sd_text_size; - std::string sd_text; - if(sdHave){ - col_inner_color = col_greed; - sd_text = bytesToGbString(remaining); - }else{ - col_inner_color = col_red; - sd_text = "X"; } - sd_text_size = ImGui::CalcTextSize(sd_text.c_str()); - ImVec2 sd_size = ImVec2(rect_size_unit.x * 2.2, rect_size_unit.y * 1.2F); - ImVec2 frame_padding = ImVec2(10, 10); - ImVec2 inner_padding = ImVec2(2, 2); - int32_t spacing = 15; - - // Drawing SD outline - ImVec2 outter_min = image_pos + frame_padding; - ImVec2 outter_max = image_pos + frame_padding + sd_size; - draw_list->AddRectFilled( - outter_min, - outter_max, - col_grey); - - // Drawing SD inline - ImVec2 inner_min = outter_min + inner_padding; - ImVec2 inner_max = outter_max - inner_padding; - draw_list->AddRectFilled( - inner_min, - inner_max, - col_inner_color); - - // Drawing SD text - ImVec2 text_pos = ImVec2(outter_min.x, outter_max.y); - draw_list->AddText(text_pos, col_white, sd_text.c_str()); - } - // Center text - { - std::string shutter_speed; - std::string ev_setting; - std::string camera_title = c->name; - std::string iso_setting; - std::string white_balance_setting; - std::string sharpness_setting; - std::string color_setting; - float spacing = 0.75F; - - ImVec2 camera_title_size = ImGui::CalcTextSize(camera_title.c_str()); - - // Draw Title in center - ImVec2 center = ImVec2( - image_pos.x + (rect_size.x / 2.0F), - image_pos.y + (rect_size.y / 2.0F) - ); - ImVec2 camera_title_min = ImVec2( - center.x + ( camera_title_size.x / -2.0F ), - center.y + ( camera_title_size.y / -2.0F ) + (rect_size_unit.y * (spacing * -3.0F)) - ); - draw_list->AddText(camera_title_min, col_white, camera_title.c_str()); - if(c->connected) { - int32_t SHUTTER_SPEED_ID = 0; - int32_t ISO_MIN_ID = 0; - int32_t ISO_MAX_ID = 0; - if(preset == 65538){ // Burst - SHUTTER_SPEED_ID = SHUTTER_SPEED_PHOTO_ID; - ISO_MIN_ID = ISO_MIN_BURST_ID; - ISO_MAX_ID = ISO_MAX_BURST_ID; + // Drawing SD card + if(c.connected){ + bool sdHave = false; + int64_t remaining = 0; + uint32_t col_inner_color; + if(status[std::to_string(PRIMARY_STORAGE_ID)].is_number()){ + int b = status[std::to_string(PRIMARY_STORAGE_ID)].get() == 1; + if(b == 1) sdHave = true; } - else if(preset == 65536){ // photo - SHUTTER_SPEED_ID = SHUTTER_SPEED_PHOTO_ID; - ISO_MIN_ID = ISO_MIN_PHOTO_ID; - ISO_MAX_ID = ISO_MAX_PHOTO_ID; + if(status[std::to_string(SD_CARD_REMAINING_ID)].is_number()){ + remaining = status[std::to_string(SD_CARD_REMAINING_ID)].get(); } - else if(preset == 0){ // video - SHUTTER_SPEED_ID = SHUTTER_SPEED_VIDEO_ID; - ISO_MIN_ID = ISO_MIN_VIDEO_ID; - ISO_MAX_ID = ISO_MAX_VIDEO_ID; + ImVec2 sd_text_size; + std::string sd_text; + if(sdHave){ + col_inner_color = col_greed; + sd_text = bytesToGbString(remaining); + }else{ + col_inner_color = col_red; + sd_text = "X"; } - else{ // Timelapse + sd_text_size = ImGui::CalcTextSize(sd_text.c_str()); + ImVec2 sd_size = ImVec2(rect_size_unit.x * 2.2, rect_size_unit.y * 1.2F); + ImVec2 frame_padding = ImVec2(10, 10); + ImVec2 inner_padding = ImVec2(2, 2); + int32_t spacing = 15; + + // Drawing SD outline + ImVec2 outter_min = image_pos + frame_padding; + ImVec2 outter_max = image_pos + frame_padding + sd_size; + draw_list->AddRectFilled( + outter_min, + outter_max, + col_grey); - } + // Drawing SD inline + ImVec2 inner_min = outter_min + inner_padding; + ImVec2 inner_max = outter_max - inner_padding; + draw_list->AddRectFilled( + inner_min, + inner_max, + col_inner_color); + + // Drawing SD text + ImVec2 text_pos = ImVec2(outter_min.x, outter_max.y); + draw_list->AddText(text_pos, col_white, sd_text.c_str()); + } + // Center text + { + std::string shutter_speed; + std::string ev_setting; + std::string camera_title = c.name; + std::string iso_setting; + std::string white_balance_setting; + std::string sharpness_setting; + std::string color_setting; + float spacing = 0.75F; + + ImVec2 camera_title_size = ImGui::CalcTextSize(camera_title.c_str()); + + // Draw Title in center + ImVec2 center = ImVec2( + image_pos.x + (rect_size.x / 2.0F), + image_pos.y + (rect_size.y / 2.0F) + ); + ImVec2 camera_title_min = ImVec2( + center.x + ( camera_title_size.x / -2.0F ), + center.y + ( camera_title_size.y / -2.0F ) + (rect_size_unit.y * (spacing * -3.0F)) + ); + draw_list->AddText(camera_title_min, col_white, camera_title.c_str()); + if(c.connected) { + int32_t SHUTTER_SPEED_ID = 0; + int32_t ISO_MIN_ID = 0; + int32_t ISO_MAX_ID = 0; + if(preset == 65538){ // Burst + SHUTTER_SPEED_ID = SHUTTER_SPEED_PHOTO_ID; + ISO_MIN_ID = ISO_MIN_BURST_ID; + ISO_MAX_ID = ISO_MAX_BURST_ID; + } + else if(preset == 65536){ // photo + SHUTTER_SPEED_ID = SHUTTER_SPEED_PHOTO_ID; + ISO_MIN_ID = ISO_MIN_PHOTO_ID; + ISO_MAX_ID = ISO_MAX_PHOTO_ID; + } + else if(preset == 0){ // video + SHUTTER_SPEED_ID = SHUTTER_SPEED_VIDEO_ID; + ISO_MIN_ID = ISO_MIN_VIDEO_ID; + ISO_MAX_ID = ISO_MAX_VIDEO_ID; + } + else{ // Timelapse + + } - if(setting[std::to_string(EXPOSURE_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(EXPOSURE_ID)].get(); - ev_setting = EXPOSURE_STRING[re]; - } - if(setting[std::to_string(SHUTTER_SPEED_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(SHUTTER_SPEED_ID)].get(); - if(preset == 0){ // Video - shutter_speed = SHUTTER_SPEED_VIDEO_STRING[re]; - }else { - shutter_speed = SHUTTER_SPEED_PHOTO_STRING[re]; + if(setting[std::to_string(EXPOSURE_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(EXPOSURE_ID)].get(); + ev_setting = EXPOSURE_STRING[re]; } - if(shutter_speed == "Auto"){ - shutter_speed = "S: " + shutter_speed + ", " + ev_setting; - }else{ - shutter_speed = "S: " + shutter_speed; + if(setting[std::to_string(SHUTTER_SPEED_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(SHUTTER_SPEED_ID)].get(); + if(preset == 0){ // Video + shutter_speed = SHUTTER_SPEED_VIDEO_STRING[re]; + }else { + shutter_speed = SHUTTER_SPEED_PHOTO_STRING[re]; + } + if(shutter_speed == "Auto"){ + shutter_speed = "S: " + shutter_speed + ", " + ev_setting; + }else{ + shutter_speed = "S: " + shutter_speed; + } } - } - if(setting[std::to_string(ISO_MIN_ID)].is_number() && setting[std::to_string(ISO_MAX_ID)].is_number()){ - int32_t iso_min = setting[std::to_string(ISO_MIN_ID)].get(); - int32_t iso_max = setting[std::to_string(ISO_MAX_ID)].get(); - std::string iso_min_text = "ISO MIN"; - std::string iso_max_text = "ISO MAX"; - if(preset == 0){ - iso_min_text = ISO_MIN_VIDEO_STRING[iso_min]; - iso_max_text = ISO_MAX_VIDEO_STRING[iso_max]; - }else{ - iso_min_text = ISO_MIN_PHOTO_STRING[iso_min]; - iso_max_text = ISO_MAX_PHOTO_STRING[iso_max]; + if(setting[std::to_string(ISO_MIN_ID)].is_number() && setting[std::to_string(ISO_MAX_ID)].is_number()){ + int32_t iso_min = setting[std::to_string(ISO_MIN_ID)].get(); + int32_t iso_max = setting[std::to_string(ISO_MAX_ID)].get(); + std::string iso_min_text = "ISO MIN"; + std::string iso_max_text = "ISO MAX"; + if(preset == 0){ + iso_min_text = ISO_MIN_VIDEO_STRING[iso_min]; + iso_max_text = ISO_MAX_VIDEO_STRING[iso_max]; + }else{ + iso_min_text = ISO_MIN_PHOTO_STRING[iso_min]; + iso_max_text = ISO_MAX_PHOTO_STRING[iso_max]; + } + iso_setting = "I: " + iso_min_text + " " + iso_max_text; + } + if(setting[std::to_string(SHARPNESS_ID)].is_number()){ + int32_t sharpness_bal = setting[std::to_string(SHARPNESS_ID)].get(); + sharpness_setting = SHARPNESS_STRING[sharpness_bal]; + } + if(setting[std::to_string(COLOR_ID)].is_number()){ + int32_t color_bal = setting[std::to_string(COLOR_ID)].get(); + color_setting = COLOR_STRING[color_bal]; } - iso_setting = "I: " + iso_min_text + " " + iso_max_text; + if(setting[std::to_string(WHITE_BALANCE_ID)].is_number()){ + int32_t white_bal = setting[std::to_string(WHITE_BALANCE_ID)].get(); + white_balance_setting = WHITE_BALANCE_STRING[white_bal]; + white_balance_setting += ", " + sharpness_setting + ", " + color_setting; + } + + ImVec2 shutter_speed_size = ImGui::CalcTextSize(shutter_speed.c_str()); + ImVec2 shutter_speed_min = ImVec2( + center.x + ( shutter_speed_size.x / -2.0F ), + center.y + ( shutter_speed_size.y / -2.0F ) + (rect_size_unit.y * (spacing * -1.0F)) + ); + draw_list->AddText(shutter_speed_min, col_white, shutter_speed.c_str()); + + ImVec2 iso_setting_size = ImGui::CalcTextSize(iso_setting.c_str()); + ImVec2 iso_setting_min = ImVec2( + center.x + ( iso_setting_size.x / -2.0F ), + center.y + ( iso_setting_size.y / -2.0F ) + (rect_size_unit.y * (spacing * 1.0F)) + ); + draw_list->AddText(iso_setting_min, col_white, iso_setting.c_str()); + + ImVec2 white_balance_setting_size = ImGui::CalcTextSize(white_balance_setting.c_str()); + ImVec2 white_balance_setting_min = ImVec2( + center.x + ( white_balance_setting_size.x / -2.0F ), + center.y + ( white_balance_setting_size.y / -2.0F ) + (rect_size_unit.y * (spacing * 3.0F)) + ); + draw_list->AddText(white_balance_setting_min, col_white, white_balance_setting.c_str()); + } + } + // Record Time + if(c.connected){ + ImVec2 frame_padding = ImVec2(10, 10); + ImVec2 center = ImVec2( + image_pos.x + (rect_size.x / 2.0F), + image_pos.y + (rect_size.y / 2.0F) + ); + + std::string record_time; + bool record_time_red = false; + + if(status[std::to_string(VIDEO_ENCODING_DURATION_ID)].is_number_integer()){ + int32_t re = status[std::to_string(VIDEO_ENCODING_DURATION_ID)].get(); + record_time_red = re > 0; + record_time = toTimeCode(re); + } + + ImVec2 record_time_size = ImGui::CalcTextSize(record_time.c_str()); + ImVec2 record_time_min = ImVec2( + center.x + ( record_time_size.x / -2.0F ), + image_pos.y + frame_padding.y + ); + draw_list->AddText(record_time_min, record_time_red ? col_red : col_white, record_time.c_str()); + } + // Preset mode + if(c.connected){ + int32_t preset; + std::string preset_text; + if(status[std::to_string(PRESET_ID)].is_number_integer()){ + preset = status[std::to_string(PRESET_ID)].get(); } - if(setting[std::to_string(SHARPNESS_ID)].is_number()){ - int32_t sharpness_bal = setting[std::to_string(SHARPNESS_ID)].get(); - sharpness_setting = SHARPNESS_STRING[sharpness_bal]; + + if(preset == GOPRO_MODE_VALUE[0]) preset_text = "V"; + else if(preset == GOPRO_MODE_VALUE[1]) preset_text = "PB"; + else if(preset == GOPRO_MODE_VALUE[2]) preset_text = "PS"; + else if(preset == GOPRO_MODE_VALUE[3]) preset_text = "T"; + else if(preset == GOPRO_MODE_VALUE[4]) preset_text = "TT"; + else if(preset == GOPRO_MODE_VALUE[5]) preset_text = "TLP"; + else if(preset == GOPRO_MODE_VALUE[6]) preset_text = "TLT"; + else if(preset == GOPRO_MODE_VALUE[7]) preset_text = "TV"; + else if(preset == GOPRO_MODE_VALUE[8]) preset_text = "TNV"; + else preset_text = std::to_string(preset); + + ImVec2 frame_padding = ImVec2(10, 10); + ImVec2 preset_text_size = ImGui::CalcTextSize(preset_text.c_str()); + draw_list->AddText(ImVec2( + image_pos.x + frame_padding.x, + (image_pos.y + rect_size.y) - (frame_padding.y + preset_text_size.y) + ), col_white, preset_text.c_str()); + } + // Setting + if(c.connected){ + float spacing = 0.75F; + float word_spacing = 10; + std::string res; + std::string fps; + std::string profile; + std::string videolen; + + if(setting[std::to_string(VIDEO_RESOLUTION_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(VIDEO_RESOLUTION_ID)].get(); + res = VIDEO_RESOLUTION_STRING[re]; } - if(setting[std::to_string(COLOR_ID)].is_number()){ - int32_t color_bal = setting[std::to_string(COLOR_ID)].get(); - color_setting = COLOR_STRING[color_bal]; + if(setting[std::to_string(FRAMES_PER_SECOND_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(FRAMES_PER_SECOND_ID)].get(); + fps = FRAMES_PER_SECOND_STRING[re]; } - if(setting[std::to_string(WHITE_BALANCE_ID)].is_number()){ - int32_t white_bal = setting[std::to_string(WHITE_BALANCE_ID)].get(); - white_balance_setting = WHITE_BALANCE_STRING[white_bal]; - white_balance_setting += ", " + sharpness_setting + ", " + color_setting; + if(setting[std::to_string(PROFILES_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(PROFILES_ID)].get(); + profile = PROFILES_STRING[re]; + } + if(preset == 0){ + if(setting[std::to_string(VIDEO_LENS_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(VIDEO_LENS_ID)].get(); + videolen = VIDEO_LENS_STRING[re]; + while(videolen.size() > 1){ + videolen.pop_back(); + } + } + }else{ + if(setting[std::to_string(PHOTO_LENS_ID)].is_number_integer()){ + int32_t re = setting[std::to_string(PHOTO_LENS_ID)].get(); + videolen = PHOTO_LENS_STRING[re]; + while(videolen.size() > 1){ + videolen.pop_back(); + } + } } - ImVec2 shutter_speed_size = ImGui::CalcTextSize(shutter_speed.c_str()); - ImVec2 shutter_speed_min = ImVec2( - center.x + ( shutter_speed_size.x / -2.0F ), - center.y + ( shutter_speed_size.y / -2.0F ) + (rect_size_unit.y * (spacing * -1.0F)) - ); - draw_list->AddText(shutter_speed_min, col_white, shutter_speed.c_str()); + ImVec2 res_text_size = ImGui::CalcTextSize(res.c_str()); + ImVec2 fps_text_size = ImGui::CalcTextSize(fps.c_str()); + ImVec2 profile_text_size = ImGui::CalcTextSize(profile.c_str()); + ImVec2 videolen_text_size = ImGui::CalcTextSize(videolen.c_str()); + ImVec2 frame_padding = ImVec2(5, 5); - ImVec2 iso_setting_size = ImGui::CalcTextSize(iso_setting.c_str()); - ImVec2 iso_setting_min = ImVec2( - center.x + ( iso_setting_size.x / -2.0F ), - center.y + ( iso_setting_size.y / -2.0F ) + (rect_size_unit.y * (spacing * 1.0F)) + ImVec2 corner = image_pos + rect_size; + ImVec2 profile_pos = ImVec2( + corner.x - (frame_padding.x + profile_text_size.x), + corner.y - (frame_padding.y + profile_text_size.y + ((rect_size_unit.y * 2.0F) * spacing)) ); - draw_list->AddText(iso_setting_min, col_white, iso_setting.c_str()); + draw_list->AddText(profile_pos, col_white, profile.c_str()); - ImVec2 white_balance_setting_size = ImGui::CalcTextSize(white_balance_setting.c_str()); - ImVec2 white_balance_setting_min = ImVec2( - center.x + ( white_balance_setting_size.x / -2.0F ), - center.y + ( white_balance_setting_size.y / -2.0F ) + (rect_size_unit.y * (spacing * 3.0F)) + ImVec2 framing_pos = ImVec2( + corner.x - (frame_padding.x + videolen_text_size.x), + corner.y - (frame_padding.y + videolen_text_size.y) ); - draw_list->AddText(white_balance_setting_min, col_white, white_balance_setting.c_str()); - } - } - // Record Time - if(c->connected){ - ImVec2 frame_padding = ImVec2(10, 10); - ImVec2 center = ImVec2( - image_pos.x + (rect_size.x / 2.0F), - image_pos.y + (rect_size.y / 2.0F) - ); - - std::string record_time; - - if(status[std::to_string(VIDEO_ENCODING_DURATION_ID)].is_number_integer()){ - int32_t re = status[std::to_string(VIDEO_ENCODING_DURATION_ID)].get(); - record_time = toTimeCode(re); - } + draw_list->AddText(framing_pos, col_white, videolen.c_str()); - ImVec2 record_time_size = ImGui::CalcTextSize(record_time.c_str()); - ImVec2 record_time_min = ImVec2( - center.x + ( record_time_size.x / -2.0F ), - image_pos.y + frame_padding.y - ); - draw_list->AddText(record_time_min, col_white, record_time.c_str()); - } - // Preset mode - if(c->connected){ - int32_t preset; - std::string preset_text; - if(status[std::to_string(PRESET_ID)].is_number_integer()){ - preset = status[std::to_string(PRESET_ID)].get(); - } + ImVec2 fps_pos = framing_pos - ImVec2(word_spacing + fps_text_size.x, 0); + draw_list->AddText(fps_pos, col_white, fps.c_str()); - if(preset == GOPRO_MODE_VALUE[0]) preset_text = "V"; - else if(preset == GOPRO_MODE_VALUE[1]) preset_text = "PB"; - else if(preset == GOPRO_MODE_VALUE[2]) preset_text = "PS"; - else if(preset == GOPRO_MODE_VALUE[3]) preset_text = "T"; - else if(preset == GOPRO_MODE_VALUE[4]) preset_text = "TT"; - else if(preset == GOPRO_MODE_VALUE[5]) preset_text = "TLP"; - else if(preset == GOPRO_MODE_VALUE[6]) preset_text = "TLT"; - else if(preset == GOPRO_MODE_VALUE[7]) preset_text = "TV"; - else if(preset == GOPRO_MODE_VALUE[8]) preset_text = "TNV"; - else preset_text = std::to_string(preset); - - ImVec2 frame_padding = ImVec2(10, 10); - ImVec2 preset_text_size = ImGui::CalcTextSize(preset_text.c_str()); - draw_list->AddText(ImVec2( - image_pos.x + frame_padding.x, - (image_pos.y + rect_size.y) - (frame_padding.y + preset_text_size.y) - ), col_white, preset_text.c_str()); - } - // Setting - if(c->connected){ - float spacing = 0.75F; - float word_spacing = 10; - std::string res; - std::string fps; - std::string profile; - std::string videolen; - - if(setting[std::to_string(VIDEO_RESOLUTION_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(VIDEO_RESOLUTION_ID)].get(); - res = VIDEO_RESOLUTION_STRING[re]; - } - if(setting[std::to_string(FRAMES_PER_SECOND_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(FRAMES_PER_SECOND_ID)].get(); - fps = FRAMES_PER_SECOND_STRING[re]; + ImVec2 res_pos = fps_pos - ImVec2(word_spacing + res_text_size.x, 0); + draw_list->AddText(res_pos, col_white, res.c_str()); } - if(setting[std::to_string(PROFILES_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(PROFILES_ID)].get(); - profile = PROFILES_STRING[re]; - } - if(preset == 0){ - if(setting[std::to_string(VIDEO_LENS_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(VIDEO_LENS_ID)].get(); - videolen = VIDEO_LENS_STRING[re]; - while(videolen.size() > 1){ - videolen.pop_back(); - } + + ImGui::PopID(); + ImGui::Dummy(rect_size); + bool is_select = state->current_camera_item == c.ip && state->current_camera_server == c.server; + if(ImGui::IsItemHovered()){ + std::string displayText = ""; + displayText += "name: "; + displayText += state->current_camera_name; + displayText += "\n"; + + displayText += "server: "; + displayText += c.server; + displayText += "\n"; + + displayText += "ip: "; + displayText += c.ip; + displayText += "\n"; + + displayText += "serial: "; + if(c.hw.is_object() && c.hw.contains("serial_number") && c.hw["serial_number"].is_string()){ + displayText += c.hw["serial_number"].get(); } - }else{ - if(setting[std::to_string(PHOTO_LENS_ID)].is_number_integer()){ - int32_t re = setting[std::to_string(PHOTO_LENS_ID)].get(); - videolen = PHOTO_LENS_STRING[re]; - while(videolen.size() > 1){ - videolen.pop_back(); - } - } - } + displayText += "\n"; - ImVec2 res_text_size = ImGui::CalcTextSize(res.c_str()); - ImVec2 fps_text_size = ImGui::CalcTextSize(fps.c_str()); - ImVec2 profile_text_size = ImGui::CalcTextSize(profile.c_str()); - ImVec2 videolen_text_size = ImGui::CalcTextSize(videolen.c_str()); - ImVec2 frame_padding = ImVec2(5, 5); - - ImVec2 corner = image_pos + rect_size; - ImVec2 profile_pos = ImVec2( - corner.x - (frame_padding.x + profile_text_size.x), - corner.y - (frame_padding.y + profile_text_size.y + ((rect_size_unit.y * 2.0F) * spacing)) - ); - draw_list->AddText(profile_pos, col_white, profile.c_str()); - - ImVec2 framing_pos = ImVec2( - corner.x - (frame_padding.x + videolen_text_size.x), - corner.y - (frame_padding.y + videolen_text_size.y) - ); - draw_list->AddText(framing_pos, col_white, videolen.c_str()); - - ImVec2 fps_pos = framing_pos - ImVec2(word_spacing + fps_text_size.x, 0); - draw_list->AddText(fps_pos, col_white, fps.c_str()); - - ImVec2 res_pos = fps_pos - ImVec2(word_spacing + res_text_size.x, 0); - draw_list->AddText(res_pos, col_white, res.c_str()); - } - - ImGui::PopID(); - ImGui::Dummy(rect_size); - bool is_select = state->current_camera_item == c->ip && state->current_camera_server == c->server; - if(ImGui::IsItemHovered()){ - std::string displayText = ""; - displayText += "name: "; - displayText += state->current_camera_name; - displayText += "\n"; - - displayText += "server: "; - displayText += c->server; - displayText += "\n"; - - displayText += "ip: "; - displayText += c->ip; - displayText += "\n"; - - displayText += "serial: "; - if(c->hw["serial_number"].is_string()){ - displayText += c->hw["serial_number"].get(); - } - displayText += "\n"; + displayText += "model: "; + if(c.hw.is_object() && c.hw.contains("model_name") && c.hw["model_name"].is_string()){ + displayText += c.hw["model_name"].get(); + } + displayText += "\n"; - displayText += "model: "; - if(c->hw["model_name"].is_string()){ - displayText += c->hw["model_name"].get(); + displayText += "firmware: "; + if(c.hw.is_object() && c.hw.contains("firmware_version") && c.hw["firmware_version"].is_string()){ + displayText += c.hw["firmware_version"].get(); + } + displayText += "\n"; + ImGui::SetItemTooltip("%s", displayText.c_str()); + draw_list->AddRect(image_pos, image_pos_max, col_grey, 2.0F, 0, 5.0F); } - displayText += "\n"; - - displayText += "firmware: "; - if(c->hw["firmware_version"].is_string()){ - displayText += c->hw["firmware_version"].get(); + else if(is_select){ + draw_list->AddRect(image_pos, image_pos_max, col_grey_light, 2.0F, 0, 5.0F); } - displayText += "\n"; - ImGui::SetItemTooltip("%s", displayText.c_str()); - draw_list->AddRect(image_pos, image_pos_max, col_grey, 2.0F, 0, 5.0F); - } - else if(is_select){ - draw_list->AddRect(image_pos, image_pos_max, col_grey_light, 2.0F, 0, 5.0F); + item_event(c); } - item_event( c); ImGui::EndGroup(); } diff --git a/src/master/windows/inspector.h b/src/master/windows/inspector.h index 1107fd93..b5d01807 100644 --- a/src/master/windows/inspector.h +++ b/src/master/windows/inspector.h @@ -23,8 +23,8 @@ class InspectorWindow : public BaseWindow { virtual void draw_system(); virtual void draw_setting(); virtual void draw_protune(); - static void global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c); - static void global_draw_protune(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c); + static void global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c); + static void global_draw_protune(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c); virtual void draw_status(); virtual void draw_hardware(); virtual void draw_network(); @@ -41,8 +41,8 @@ class InspectorWindow : public BaseWindow { protected: virtual void _draw_setting(std::vector& ordered); virtual void _draw_status(std::vector& ordered); - static void _global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c, std::vector& ordered); - static bool _global_draw_setting_item(int32_t i, std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c, std::vector& ordered); + static void _global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c, std::vector& ordered); + static bool _global_draw_setting_item(int32_t i, std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c, std::vector& ordered); static bool conditional_filter(const std::shared_ptr& state, int32_t mymodel, int32_t setting_id); static bool conditional_filter_option(const std::shared_ptr& state, int32_t mymodel, int32_t setting_id, int32_t value_index); @@ -66,4 +66,11 @@ class InspectorWindow : public BaseWindow { bool put_finish; bool should_disabled; bool applying_all_last; + /// + /// 0: None + /// 1: Front Characters + /// 2: Back Characters + /// + int32_t media_name_rule_type = 0; + int32_t media_name_character_count = 0; }; diff --git a/src/master/windows/inspector/command.cpp b/src/master/windows/inspector/command.cpp index d19bdea0..49c1b4e0 100644 --- a/src/master/windows/inspector/command.cpp +++ b/src/master/windows/inspector/command.cpp @@ -5,7 +5,7 @@ void InspectorWindow::draw_command_local(){ ImGui::Text("Single Camera Control"); - int32_t current_camera = master->findCamera(state->current_camera_item); + int32_t current_camera = master->findCamera(state->current_camera_server, state->current_camera_item); bool should_disabled = state->current_camera_item.size() < 10 || current_camera == -1; ImGui::BeginDisabled(should_disabled); @@ -16,7 +16,7 @@ void InspectorWindow::draw_command_local(){ ImVec2 full_button_size = ImVec2(size.x - style.ItemSpacing.x, 0); if(current_camera != -1) { - CameraInfo info = *(master->getCameras().at(current_camera)); + CameraInfo info = master->getCamera_Clone(current_camera); if(ImGui::Button("Record", button_2size)) master->command_only(info.server, "shutter_on", info.ip); ImGui::SameLine(); if(ImGui::Button("Stop", button_2size)) master->command_only(info.server, "shutter_off", info.ip); diff --git a/src/master/windows/inspector/filter.cpp b/src/master/windows/inspector/filter.cpp index 168fb844..2df305ae 100644 --- a/src/master/windows/inspector/filter.cpp +++ b/src/master/windows/inspector/filter.cpp @@ -216,7 +216,7 @@ bool InspectorWindow::conditional_filter_option(const std::shared_ptrcurrent_download_location; data["setting_order"] = json::object(); data["status_order"] = json::object(); @@ -74,6 +76,15 @@ void InspectorWindow::set_window_data(json data) { if(data["create_date_folder"].is_boolean()){ create_date_folder = data["create_date_folder"].get(); } + if(data["media_name_rule_type"].is_number()){ + media_name_rule_type = data["media_name_rule_type"].get(); + } + if(data["media_name_character_count"].is_number()){ + media_name_character_count = data["media_name_character_count"].get(); + } + if(data["put_finish"].is_number()){ + put_finish = data["put_finish"].get(); + } if(data["current_download_location"].is_string()){ state->current_download_location = data["current_download_location"].get(); } @@ -98,11 +109,10 @@ void InspectorWindow::set_window_data(json data) { void InspectorWindow::render(){ ImGui::Begin("Inspector", &enable, w_flag); { - std::lock_guard lock(master->camera_mtx); - int32_t s = master->findCamera(state->current_camera_item); + int32_t s = master->findCamera(state->current_camera_server, state->current_camera_item); if(s != -1){ - auto& c = master->getCameras().at(s); - should_disabled = !c->connected || state->current_camera_item.size() < 10 || s == -1 || !state->current_setting_items_bind; + CameraInfo c = master->getCamera_Clone(s); + should_disabled = !c.connected || state->current_camera_item.size() < 10 || s == -1 || !state->current_setting_items_bind; } draw_header(); @@ -138,12 +148,21 @@ void InspectorWindow::render(){ ImGui::EndDisabled(); ImGui::SameLine(); - ImGui::BeginDisabled(should_disabled || state->applying_all); - if(ImGui::Button("Quick Apply All##Inspector_Bar_Item")){ - if(s != -1){ - const std::shared_ptr& c = master->getCameras().at(s); - master->quickApplyAll(c); - state->applying_all = true; + ImGui::BeginDisabled(should_disabled); + if(state->applying_all){ + if(ImGui::Button("Stop Apply All##Inspector_Bar_Item")){ + if(s != -1){ + const CameraInfo c = master->getCamera_Clone(s); + master->stopApplyAll(c); + } + } + }else{ + if(ImGui::Button("Quick Apply All##Inspector_Bar_Item")){ + if(s != -1){ + const CameraInfo c = master->getCamera_Clone(s); + master->quickApplyAll(c); + state->applying_all = true; + } } } ImGui::EndDisabled(); diff --git a/src/master/windows/inspector/media.cpp b/src/master/windows/inspector/media.cpp index 29c5961d..9cc2fd9c 100644 --- a/src/master/windows/inspector/media.cpp +++ b/src/master/windows/inspector/media.cpp @@ -38,11 +38,17 @@ void InspectorWindow::draw_media_global(){ ImVec2 button_size = ImVec2(size.x / 2.0F - style.ItemSpacing.x, 0); ImVec2 button3_size = ImVec2(size.x / 3.0F - style.ItemSpacing.x, 0); - int32_t camera_ip = master->findCamera(state->current_camera_item); + DownloadMediaParameters params; + params.dir = state->current_download_location; + params.put_finish = put_finish; + params.type = media_name_rule_type; + params.c_count = media_name_character_count; + + int32_t camera_ip = master->findCamera(state->current_camera_server, state->current_camera_item); if(ImGui::InputText("Media Download", &state->current_download_location)){ state->update_server(); } - if(ImGui::Button("All Download", button_size)){ + if(ImGui::Button("All Download", button3_size)){ std::string buffer = state->current_download_location; while(buffer.size() > 0 && buffer.at(buffer.size() - 1) == '/'){ buffer.pop_back(); @@ -53,12 +59,13 @@ void InspectorWindow::draw_media_global(){ buffer.append("/" + date); } fs::create_directories(buffer); - master->download_last_media(buffer, put_finish); + params.dir = buffer; + master->download_last_media("", params); } } if(ImGui::IsItemHovered()) ImGui::SetTooltip("Download all exist camera instances"); ImGui::SameLine(); - if(ImGui::Button("Single Download", button_size)){ + if(ImGui::Button("Single Download", button3_size)){ std::string buffer = state->current_download_location; while(buffer.size() > 0 && buffer.at(buffer.size() - 1) == '/'){ buffer.pop_back(); @@ -69,9 +76,14 @@ void InspectorWindow::draw_media_global(){ buffer.append("/" + date); } fs::create_directories(buffer); - master->download_last_media(state->current_camera_item, buffer, put_finish); + params.dir = buffer; + master->download_last_media(state->current_camera_item, params); } } + ImGui::SameLine(); + if(ImGui::Button("Media Browser", button3_size)){ + state->command_sender("media_browser"); + } if(ImGui::IsItemHovered()) ImGui::SetTooltip("Download current select camera instance"); if(ImGui::Button("Open Home", button3_size)){ @@ -94,6 +106,28 @@ void InspectorWindow::draw_media_global(){ state->update_server(); } + const char* selection = MEDIA_DOWNLOAD_TYPE_STRING[media_name_rule_type]; + if(ImGui::BeginCombo("Media Name Rule", selection)){ + for(int32_t i = 0; i < MEDIA_DOWNLOAD_TYPE_SIZE; i++){ + bool selected = (media_name_rule_type == i); + if(ImGui::Selectable(MEDIA_DOWNLOAD_TYPE_STRING[i], selected)){ + media_name_rule_type = i; + state->update_server(); + } + if(selected){ + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + + if(media_name_rule_type > 0){ + if(ImGui::InputInt("Character Count", &media_name_character_count, 1, 5)){ + if(media_name_character_count < 1) media_name_character_count = 1; + state->update_server(); + } + } + if(ImGui::IsItemHovered()) ImGui::SetTooltip("Open file explorer for path select directory"); if(camera_ip >= 0){ std::shared_ptr t = master->getCameras()[camera_ip]; diff --git a/src/master/windows/inspector/setting.cpp b/src/master/windows/inspector/setting.cpp index fc255cc8..48453671 100644 --- a/src/master/windows/inspector/setting.cpp +++ b/src/master/windows/inspector/setting.cpp @@ -35,9 +35,9 @@ void InspectorWindow::draw_protune(){ } } -void InspectorWindow::global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c){ +void InspectorWindow::global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c){ json status = json::object(); - master->getStatusFromCamera(*c, status); + master->getStatusFromCamera(c, status); if (status[std::to_string(PRESET_ID)].is_number()) { int32_t preset = status[std::to_string(PRESET_ID)].get(); if(preset == 0){ @@ -48,9 +48,9 @@ void InspectorWindow::global_draw_setting(std::shared_ptr& state, s } } -void InspectorWindow::global_draw_protune(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c){ +void InspectorWindow::global_draw_protune(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c){ json status = json::object(); - master->getStatusFromCamera(*c, status); + master->getStatusFromCamera(c, status); if (status[std::to_string(PRESET_ID)].is_number()) { int32_t preset = status[std::to_string(PRESET_ID)].get(); if(preset == 0){ @@ -62,9 +62,9 @@ void InspectorWindow::global_draw_protune(std::shared_ptr& state, s } void InspectorWindow::_draw_setting(std::vector& ordered){ - int32_t current = master->findCamera(state->current_camera_item); + int32_t current = master->findCamera(state->current_camera_server, state->current_camera_item); if(current < 0) return; - auto& c = master->getCameras().at(current); + const CameraInfo c = master->getCamera_Clone(current); int32_t move_from = -1, move_to = -1; for(int32_t i = 0; i < ordered.size(); i++){ int32_t id = ordered[i]; @@ -105,23 +105,23 @@ void InspectorWindow::_draw_setting(std::vector& ordered){ } } -void InspectorWindow::_global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c, std::vector& ordered){ +void InspectorWindow::_global_draw_setting(std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c, std::vector& ordered){ for(int32_t i = 0; i < ordered.size(); i++){ _global_draw_setting_item(i, state, master, c, ordered); } } -bool InspectorWindow::_global_draw_setting_item(int32_t i, std::shared_ptr& state, std::shared_ptr& master, const std::shared_ptr& c, std::vector& ordered){ +bool InspectorWindow::_global_draw_setting_item(int32_t i, std::shared_ptr& state, std::shared_ptr& master, const CameraInfo& c, std::vector& ordered){ int32_t id = ordered[i]; std::string name = GET_SETTING_NAME_BY_ID(id); size_t size = GET_SETTING_SIZE_BY_ID(id); int32_t ava = GET_SETTING_AVA_BY_ID(id); - int32_t model_enum = _get_current_model(c->hw); + int32_t model_enum = _get_current_model(c.hw); json setting = json::object(); json status = json::object(); - master->getSettingsFromCamera(*c, setting); - master->getStatusFromCamera(*c, status); + master->getSettingsFromCamera(c, setting); + master->getStatusFromCamera(c, status); if((model_enum&ava) == 0){ return false; @@ -170,7 +170,7 @@ bool InspectorWindow::_global_draw_setting_item(int32_t i, std::shared_ptrapply("", c->ip, id, values_id[n]); + master->apply("", c.ip, id, values_id[n]); } if (is_selected) ImGui::SetItemDefaultFocus(); // You may set the initial focus when opening the combo (scrolling + for keyboard navigation support) diff --git a/src/master/windows/websocket_server.cpp b/src/master/windows/websocket_server.cpp index 2680b469..bd1af7d4 100644 --- a/src/master/windows/websocket_server.cpp +++ b/src/master/windows/websocket_server.cpp @@ -15,6 +15,7 @@ WebsocketWindow::WebsocketWindow( title = "Websocket Dashboard"; } + WebsocketWindow::~WebsocketWindow(){ } @@ -85,17 +86,17 @@ void WebsocketWindow::render(){ ImGui::TableSetupColumn("Last Message"); ImGui::TableHeadersRow(); - for (const auto& s : master->getServers()) { + for (const auto& s : master->getServers_Clone()) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::Text("%s", s->ip.c_str()); + ImGui::Text("%s", s.ip.c_str()); ImGui::TableSetColumnIndex(1); - if (s->connected) + if (s.connected) ImGui::TextColored(ImVec4(0,1,0,1), "Connected"); else ImGui::TextColored(ImVec4(1,0,0,1), "Disconnected"); ImGui::TableSetColumnIndex(2); - ImGui::Text("%s", s->last_message.c_str()); + ImGui::Text("%s", s.last_message.c_str()); } ImGui::EndTable(); } diff --git a/src/server/GoProController.h b/src/server/GoProController.h index 33017e60..dc3dee12 100644 --- a/src/server/GoProController.h +++ b/src/server/GoProController.h @@ -222,6 +222,7 @@ class GoProController { /// }] /// std::string setSettingAll(const std::string source, const std::string target, int32_t preset, json value); + void setSettingCancelAll(); #pragma endregion #pragma region Webcam part of calls @@ -329,6 +330,10 @@ class GoProController { /// - target /// std::string getFetchURL(std::string target_ip, bool is_local); + std::string getSingleFetchURL(std::string target_ip, const std::string filename, bool is_local); + std::vector> getAllFetchURL(std::string target_ip, std::vector filenames, bool is_local); + std::string getThumbnailData(std::string target_ip, std::string path, bool is_local); + std::string getMediaInfoData(std::string target_ip, std::string path, bool is_local); #pragma endregion #pragma endregion @@ -404,12 +409,18 @@ class GoProController { // Utility calls SingleResponse _getSingleResponse(std::string target, std::string suffix); std::vector _getAllResponse(std::vector targets, std::string suffix); - + std::string base64_encode(const std::vector& data); + int32_t _get_current_model(json hwinfo); #pragma endregion private: #pragma region private variable + /// + /// State of current server + /// Is applying something + /// + std::atomic applying_cancel = false; /// /// The handle for mdns service manager /// diff --git a/src/server/controller/media.cpp b/src/server/controller/media.cpp index 0e705bc3..1c8b6ea8 100644 --- a/src/server/controller/media.cpp +++ b/src/server/controller/media.cpp @@ -159,4 +159,131 @@ std::string GoProController::getFetchURL(std::string target_ip, bool is_local){ std::cerr << ex.what() << std::endl; return ""; } -} \ No newline at end of file +} + +std::string GoProController::getSingleFetchURL(std::string target_ip, const std::string filename, bool is_local){ + std::cout << "Http GET /single_media " << target_ip << ", " << is_local << std::endl; + + if (target_ip.empty()) { + std::cerr << "[single_media] " << target_ip << " Missing ip parameter" << std::endl; + return ""; + } + + try{ + std::string gopro_url = "http://" + target_ip + ":8080/videos/DCIM/" + filename + "?download=true"; + if(is_local){ + std::cout << "[last_media] return value: " << target_ip << " => " << gopro_url << std::endl; + return gopro_url; + }else{ + int32_t t = 0; + std::string download_path = "temp.download"; + while(fs::exists("res/" + download_path)){ + download_path = "temp.download" + std::to_string(t); + t++; + } + std::cout << "[last_media] try download " << gopro_url.c_str() << std::endl; +#ifdef SERVER_MEDIA_DOWNLOAD_LOG + auto start = std::chrono::high_resolution_clock::now(); +#endif + size_t size = requests::downloadFile(gopro_url.c_str(), ("res/" + download_path).c_str(), [&target_ip, &start](size_t received_bytes, size_t total_bytes){ +#ifdef SERVER_MEDIA_DOWNLOAD_LOG + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end - start; + if(elapsed.count() >= SERVER_MEDIA_DOWNLOAD_PERIOD){ + start = end; + std::cout << "[last_media] download " << target_ip << " " << received_bytes << " / " << total_bytes << std::endl; + } +#endif + }); + std::cout << "[last_media] return value: " << target_ip << " => " << download_path << std::endl; + return download_path; + } + }catch(const std::exception& ex){ + std::cerr << ex.what() << std::endl; + return ""; + } +} + +std::vector> GoProController::getAllFetchURL(std::string target_ip, std::vector filenames, bool is_local){ + std::cout << "Http GET /all_media " << target_ip << ", " << is_local << std::endl; + + if (target_ip.empty()) { + std::cerr << "[all_media] " << target_ip << " Missing ip parameter" << std::endl; + return std::vector>(); + } + + try{ + std::vector> results = std::vector>(); + for(auto filename : filenames){ + std::string gopro_url = "http://" + target_ip + ":8080/videos/DCIM/" + filename + "?download=true"; + if(is_local){ + std::cout << "[last_media] return value: " << target_ip << " => " << gopro_url << std::endl; + results.push_back(std::make_pair(filename, gopro_url)); + }else{ + int32_t t = 0; + std::string download_path = "temp.download"; + while(fs::exists("res/" + download_path)){ + download_path = "temp.download" + std::to_string(t); + t++; + } + std::cout << "[last_media] try download " << gopro_url.c_str() << std::endl; + #ifdef SERVER_MEDIA_DOWNLOAD_LOG + auto start = std::chrono::high_resolution_clock::now(); + #endif + size_t size = requests::downloadFile(gopro_url.c_str(), ("res/" + download_path).c_str(), [&target_ip, &start](size_t received_bytes, size_t total_bytes){ + #ifdef SERVER_MEDIA_DOWNLOAD_LOG + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end - start; + if(elapsed.count() >= SERVER_MEDIA_DOWNLOAD_PERIOD){ + start = end; + std::cout << "[last_media] download " << target_ip << " " << received_bytes << " / " << total_bytes << std::endl; + } + #endif + }); + std::cout << "[last_media] return value: " << target_ip << " => " << download_path << std::endl; + results.push_back(std::make_pair(filename, download_path)); + } + } + return results; + }catch(const std::exception& ex){ + std::cerr << ex.what() << std::endl; + return std::vector>();; + } +} + +std::string GoProController::getThumbnailData(std::string target_ip, std::string path, bool is_local){ + std::cout << "Http GET /thumbnail " << target_ip << ", " << is_local << std::endl; + + if (target_ip.empty()) { + std::cerr << "[thumbnail] " << target_ip << " Missing ip parameter" << std::endl; + return ""; + } + + try{ + const std::vector res = exec_byte("http://" + target_ip + ":8080/gopro/media/screennail?path=" + path); + std::string result = base64_encode(res); + return result; + } + catch(const std::exception& ex){ + std::cerr << ex.what() << std::endl; + return ""; + } +} + +std::string GoProController::getMediaInfoData(std::string target_ip, std::string path, bool is_local){ + std::cout << "Http GET /media_info " << target_ip << ", " << is_local << std::endl; + + if (target_ip.empty()) { + std::cerr << "[media_info] " << target_ip << " Missing ip parameter" << std::endl; + return ""; + } + + try{ + std::string result = exec("http://" + target_ip + ":8080/gopro/media/info?path=" + path); + return result; + } + catch(const std::exception& ex){ + std::cerr << ex.what() << std::endl; + return ""; + } +} diff --git a/src/server/controller/query.cpp b/src/server/controller/query.cpp index 08dd9e4e..62c3e1ec 100644 --- a/src/server/controller/query.cpp +++ b/src/server/controller/query.cpp @@ -119,10 +119,12 @@ std::string GoProController::setSetting(std::string target, int32_t ID, std::str std::string GoProController::setSettingAll(const std::string source, const std::string target, int32_t preset, json value){ json arr = json::array(); json res = json::object(); + applying_cancel = false; std::string address = ""; if(target.size() > 0){ // Apply to single target std::vector results = _setSetting(target, preset, value); for(int32_t i = 0; i < results.size(); i++){ + if(applying_cancel) continue; address = results[i].first; bool vaild = json::accept(results[i].second); if(vaild){ @@ -147,6 +149,7 @@ std::string GoProController::setSettingAll(const std::string source, const std:: std::vector results = _setAllSetting(buffer, preset, value); std::cout << "[LOG] next step of setSettingAll" << std::endl; for(int32_t i = 0; i < results.size(); i++){ + if(applying_cancel) continue; address = results[i].first; bool vaild = json::accept(results[i].second); if(vaild){ @@ -165,3 +168,6 @@ std::string GoProController::setSettingAll(const std::string source, const std:: return arr.dump(); } +void GoProController::setSettingCancelAll() { + applying_cancel = true; +} \ No newline at end of file diff --git a/src/server/main.cpp b/src/server/main.cpp index 102a6a4b..3e105090 100644 --- a/src/server/main.cpp +++ b/src/server/main.cpp @@ -24,10 +24,7 @@ #include "hv/hsocket.h" #include "GoProController.h" -/// -/// All the master websocket instances -/// -std::vector hosts = std::vector(); + /// /// Main worker, HERO is here /// @@ -236,6 +233,10 @@ void QueryAction(const WebSocketChannelPtr& channel, json j){ } channel->send(getPacket("query:set", r)); } + else if(name == "setall_cancel"){ + controller.setSettingCancelAll(); + channel->send(getPacket("query:setall_cancel", r)); + } else if(name == "setall"){ resultText = controller.setSettingAll(source, target, preset, jvalue); if(json::accept(resultText)){ @@ -336,9 +337,11 @@ void MediaAction(const WebSocketChannelPtr& channel, json j){ std::string target = ""; std::string name = ""; std::string item = ""; + std::string path = ""; std::string ip = ""; std::string dir = ""; std::string filename = ""; + std::vector filenames = std::vector(); bool local = true; json r = json::object(); @@ -351,6 +354,9 @@ void MediaAction(const WebSocketChannelPtr& channel, json j){ if(j["item"].is_string()){ item = j["item"].get(); } + if(j["path"].is_string()){ + path = j["path"].get(); + } if(j["ip"].is_string()){ ip = j["ip"].get(); } @@ -360,11 +366,19 @@ void MediaAction(const WebSocketChannelPtr& channel, json j){ if(j["filename"].is_string()){ filename = j["filename"].get(); } + if(j["filenames"].is_array()){ + for(size_t i = 0; i < j["filenames"].size(); i++){ + if(j["filenames"][i].is_string()){ + filenames.push_back(j["filenames"][i].get()); + } + } + } if(j["local"].is_boolean()){ local = j["local"].get(); } if(name == "lastmedia"){ + controller.keep_alive(""); resultText = controller.getLastMedia(target); if(json::accept(resultText)){ r["data"] = json::parse(resultText); @@ -372,8 +386,7 @@ void MediaAction(const WebSocketChannelPtr& channel, json j){ r["data"] = json::array(); } channel->send(getPacket("media:lastmedia", r)); - } - else if(name == "url"){ + }else if(name == "url"){ // Download the media one at the time... thanks std::lock_guard lock(download_mtx); r["local"] = local; @@ -382,12 +395,50 @@ void MediaAction(const WebSocketChannelPtr& channel, json j){ r["filename"] = filename; r["path"] = controller.getFetchURL(ip, local); channel->send(getPacket("media:url", r)); + }else if(name == "list"){ + resultText = controller.getMediaList(target); + if(json::accept(resultText)){ + r["data"] = json::parse(resultText); + }else{ + r["data"] = json::array(); + } + channel->send(getPacket("media:list", r)); + }else if(name == "thumbnail"){ + r["local"] = local; + r["data"] = controller.getThumbnailData(ip, path, local); + channel->send(getPacket("media:thumbnail", r)); + }else if(name == "info"){ + r["local"] = local; + r["data"] = controller.getMediaInfoData(ip, path, local); + channel->send(getPacket("media:info", r)); + }else if(name == "d_single"){ + std::lock_guard lock(download_mtx); + r["local"] = local; + r["item"] = item; + r["dir"] = dir; + r["filename"] = filename; + r["path"] = controller.getSingleFetchURL(ip, filename, local); + channel->send(getPacket("media:d_single", r)); + }else if(name == "d_all"){ + std::lock_guard lock(download_mtx); + std::vector> results = controller.getAllFetchURL(ip, filenames, local); + r["local"] = local; + r["item"] = item; + r["dir"] = dir; + r["filenames"] = filenames; + r["paths"] = json::array(); + for(size_t i = 0; i < results.size(); i++){ + json buffer = json::object(); + buffer["filename"] = results.at(i).first; + buffer["path"] = results.at(i).second; + r["paths"].push_back(buffer); + } + channel->send(getPacket("media:d_all", r)); }else{ channel->send(getPacket("media:unknown", r)); } } - void PreviewAction(const WebSocketChannelPtr& channel, json j){ std::string target = ""; std::string name = ""; @@ -422,7 +473,6 @@ void WebsocketServer(){ ws.onopen = [&](const WebSocketChannelPtr& channel, const HttpRequestPtr& req) { std::lock_guard lock(broadcast_mtx); printf("Client connected: %s\n", channel->peeraddr().c_str()); - hosts.push_back(&channel); int32_t f = -1; for(int32_t i = 0; i < broadcast_addrs.size(); i++){ @@ -487,14 +537,6 @@ void WebsocketServer(){ ws.onclose = [&](const WebSocketChannelPtr& channel) { std::lock_guard lock(broadcast_mtx); printf("Client disconnected: %s\n", channel->peeraddr().c_str()); - for(int32_t i = 0; i < hosts.size(); i++){ - bool find = std::strcmp(hosts[i]->get()->peeraddr().c_str(), - channel->peeraddr().c_str()); - if(find){ - hosts.erase(hosts.begin() + i); - break; - } - } int32_t f = -1; for(int32_t i = 0; i < broadcast_addrs.size(); i++){ @@ -595,8 +637,10 @@ int main() { controller.update(); + t3.join(); t2.join(); t1.join(); return 0; } + diff --git a/src/server/private/private.cpp b/src/server/private/private.cpp index b926be28..d6b8c8f2 100644 --- a/src/server/private/private.cpp +++ b/src/server/private/private.cpp @@ -85,3 +85,23 @@ std::vector GoProController::_getAllResponse(std::vector& data) { + static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + std::string out; + int val = 0, valb = -6; + for (u_char c : data) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back(base64_chars[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) out.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]); + while (out.size() % 4) out.push_back('='); + return out; +} diff --git a/src/server/private/webcam.cpp b/src/server/private/webcam.cpp index d6b7fb33..cb8255d9 100644 --- a/src/server/private/webcam.cpp +++ b/src/server/private/webcam.cpp @@ -62,7 +62,6 @@ void GoProController::_webcamOff(std::string target){ _getSingleResponse(target, "/gopro/webcam/stop"); } - std::pair GoProController::_webcamStatus(std::string target){ return _getSingleResponse(target, "/gopro/webcam/status"); }