diff --git a/.dockerignore b/.dockerignore index 90be07e..aca9fe4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ .vscode/ build/ log/ +eSDK_IVS_API/ *.mp4 diff --git a/.gitignore b/.gitignore index 67d81f7..906d61f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ build/ log/ IVS_SDK/ +external/eSDK_IVS_API/ diff --git a/.gitmodules b/.gitmodules index 5a3826c..b42601b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external/spdlog"] path = external/spdlog - url = https://github.com/gabime/spdlog.git \ No newline at end of file + url = https://github.com/gabime/spdlog.git +[submodule "modules/flussonic/external/cpr"] + path = modules/flussonic/external/cpr + url = https://github.com/whoshuu/cpr.git diff --git a/CMakeLists.txt b/CMakeLists.txt index a2b4144..e273be3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,5 +33,7 @@ endif() # Add modules # ---------------------------------------------------------------------------- -add_subdirectory(modules/huawei) + +# add_subdirectory(modules/huawei) +add_subdirectory(modules/flussonic) add_subdirectory(modules/api) diff --git a/Dockerfile b/Dockerfile index 6e25a53..81c884c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,13 +2,19 @@ FROM ubuntu:16.04 WORKDIR /app -RUN apt-get update && apt-get install -y build-essential cmake curl libboost-all-dev && rm -rf /var/lib/apt/lists/* +RUN apt-get update && apt-get install -y \ + build-essential cmake curl libboost-all-dev \ + libssl-dev libcurl4-openssl-dev \ + && rm -rf /var/lib/apt/lists/* ADD . /app -ENV IVS_SDK_PATH=/app/IVS_SDK +ENV IVS_SDK_PATH=/app/external/eSDK_IVS_API -RUN mkdir /app/build && cd /app/build && cmake .. && cmake --build /app/build --target all --config Release -- -j 10 +RUN mkdir /app/build \ + && cd /app/build \ + && cmake -DUSE_SYSTEM_CURL=ON .. \ + && cmake --build /app/build --target all --config Release -- -j 10 EXPOSE 8000 diff --git a/modules/api/CMakeLists.txt b/modules/api/CMakeLists.txt index e7a140a..6eab86d 100644 --- a/modules/api/CMakeLists.txt +++ b/modules/api/CMakeLists.txt @@ -22,9 +22,11 @@ file(GLOB hwivs_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) set(vmsapi_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/external/spdlog/include + ${CPR_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/external ${CMAKE_CURRENT_SOURCE_DIR}/../base ${CMAKE_CURRENT_SOURCE_DIR}/../huawei/include + ${CMAKE_CURRENT_SOURCE_DIR}/../flussonic/include $ENV{IVS_SDK_PATH}/inc ) @@ -35,7 +37,8 @@ target_include_directories(vmsapi PRIVATE ${vmsapi_INCLUDE_DIR}) target_link_libraries(vmsapi ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} - hwivs + ${CPR_LIBRARIES} + flussonic ) target_compile_options(vmsapi PRIVATE -Wall -Wextra -Wunused-parameter) diff --git a/modules/api/src/main.cpp b/modules/api/src/main.cpp index ccb574b..b118945 100644 --- a/modules/api/src/main.cpp +++ b/modules/api/src/main.cpp @@ -1,14 +1,8 @@ -#include "IVS_SDK.h" #include "server.h" int main() { - IVS_SDK_SetLogPath("./log"); - IVS_SDK_Init(); - vms::api::Server server; server.run(); - IVS_SDK_Cleanup(); - return 0; -} +} \ No newline at end of file diff --git a/modules/api/src/server.cpp b/modules/api/src/server.cpp index 3dc4bb5..e81e65e 100644 --- a/modules/api/src/server.cpp +++ b/modules/api/src/server.cpp @@ -20,7 +20,7 @@ api::Server::Server() { void api::Server::run(int port) { _app.port(port).multithreaded().run(); } -std::shared_ptr api::Server::_login( +std::shared_ptr api::Server::_login( const crow::json::rvalue body) { const std::string ip = body["ip"].s(); const std::string username = body["username"].s(); @@ -28,8 +28,8 @@ std::shared_ptr api::Server::_login( const std::string vendor = body["vendor"].s(); try { - auto vms = std::make_shared("./log"); - vms->login(ip, 9900, username, password); + auto vms = std::make_shared("./log"); + vms->login(ip, 8090, username, password); return vms; // return session.login(ip, username, password, vendor); } catch (std::exception) { diff --git a/modules/api/src/server.h b/modules/api/src/server.h index fb567e7..3e7d103 100644 --- a/modules/api/src/server.h +++ b/modules/api/src/server.h @@ -2,6 +2,7 @@ #define SERVER_H #include "crow/crow_all.h" +#include "flussonic.h" #include "hw_ivs.h" #include "session.h" @@ -15,7 +16,7 @@ class Server { void run(int port = 8000); private: - static std::shared_ptr _login( + static std::shared_ptr _login( const crow::json::rvalue body); static void _camera_list(const crow::request &req, crow::response &res); diff --git a/modules/api/src/session.cpp b/modules/api/src/session.cpp index 78c7560..5e4ffe9 100644 --- a/modules/api/src/session.cpp +++ b/modules/api/src/session.cpp @@ -1,7 +1,6 @@ #include #include -#include "hw_ivs.h" #include "session.h" namespace api = vms::api; @@ -38,7 +37,7 @@ std::shared_ptr api::Session::_create_vendor_vms( std::shared_ptr vms{nullptr}; if (vendor == "huawei") { - vms = std::make_shared("./log"); + // vms = std::make_shared("./log"); } return vms; diff --git a/modules/base/vms_interface.h b/modules/base/vms_interface.h index e3a3425..f7210bc 100644 --- a/modules/base/vms_interface.h +++ b/modules/base/vms_interface.h @@ -65,6 +65,11 @@ class VMSInterface { const std::string &start_time, const std::string &end_time) = 0; + virtual std::string download(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) = 0; + virtual std::string live_stream(const std::string &camera_code, const std::string &nvr_code, const std::string &transport) = 0; diff --git a/modules/flussonic/CMakeLists.txt b/modules/flussonic/CMakeLists.txt new file mode 100644 index 0000000..a19a4e5 --- /dev/null +++ b/modules/flussonic/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.0.0) +project(flussonic VERSION 0.1.0) + +add_subdirectory(external/cpr) + +# ---------------------------------------------------------------------------- +# Target definition +# ---------------------------------------------------------------------------- + +file(GLOB flussonic_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + +set(flussonic_INCLUDE_DIR + ${CPR_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/external/spdlog/include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../base +) + +add_library(flussonic ${flussonic_SRC}) + +target_include_directories(flussonic PRIVATE ${flussonic_INCLUDE_DIR}) + +target_link_libraries(flussonic + ${CPR_LIBRARIES} +) + +target_compile_options(flussonic PRIVATE -Wall -Wextra -Wunused-parameter) +set_property(TARGET flussonic PROPERTY CXX_STANDARD 14) diff --git a/modules/flussonic/include/flussonic.h b/modules/flussonic/include/flussonic.h new file mode 100644 index 0000000..1e5c7f9 --- /dev/null +++ b/modules/flussonic/include/flussonic.h @@ -0,0 +1,63 @@ +#ifndef FLUSSONIC_H +#define FLUSSONIC_H + +#include +#include "vms_interface.h" + +namespace vms { +namespace flussonic { +class Flussonic : public VMSInterface { + public: + Flussonic(const std::string &log_path); + + void login(const std::string &ip, unsigned int port, + const std::string &username, const std::string &password) override; + + void logout() override; + + int session_id() override; + + std::vector nvr_list(unsigned int max) override; + + std::vector camera_list(unsigned int max) override; + + std::vector recording_list(const std::string &camera_code, + const std::string &start_time, + const std::string &end_time, + unsigned int max) override; + + std::string playback(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) override; + + std::string download(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) override; + + std::string live_stream(const std::string &camera_code, + const std::string &nvr_code, + const std::string &transport) override; + + std::string create_url(const std::string &_ip, const std::string &camera_code, + const std::string &start_time, + const std::string &end_time); + + std::time_t to_unix_timestamp(const std::string ×tamp); + + bool status_stream(const std::string &_ip, const unsigned int &_port, + const std::string &camera_code); + + bool to_bool(std::string str); + + private: + int _session_id; + bool _logged_in; + std::string _ip, _id, _pass; + unsigned int _port; +}; +} // namespace flussonic +} // namespace vms + +#endif diff --git a/modules/flussonic/src/flussonic.cpp b/modules/flussonic/src/flussonic.cpp new file mode 100644 index 0000000..f491e31 --- /dev/null +++ b/modules/flussonic/src/flussonic.cpp @@ -0,0 +1,160 @@ +#include "flussonic.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spdlog/spdlog.h" + +namespace vms { +namespace flussonic { +auto fls_console = spdlog::stdout_color_mt("flussonic"); + +Flussonic::Flussonic(const std::string &log_path) {} + +void Flussonic::login(const std::string &ip, unsigned int port, + const std::string &username, + const std::string &password) { + fls_console->info("Logging in to Flussonic VMS..."); + _ip = ip; + _port = port; + _logged_in = true; + _id = username; + _pass = password; +} + +void Flussonic::logout() { + fls_console->info("Logging out from Flussonic VMS..."); + _logged_in = false; +} + +int Flussonic::session_id() { return _session_id; } + +std::vector Flussonic::nvr_list(unsigned int max) { + std::vector nvrs; + return nvrs; +} + +std::vector Flussonic::camera_list(unsigned int max) { + std::vector cameras; + return cameras; +} + +std::vector Flussonic::recording_list(const std::string &camera_code, + const std::string &start_time, + const std::string &end_time, + unsigned int max) { + std::vector records; + return records; +} + +std::string Flussonic::playback(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) { + std::string url = create_url(_ip, camera_code, start_time, end_time); + if (status_stream(_ip, _port, camera_code) == true) { + return url; + } else { + throw std::invalid_argument("No available stream"); + } +} + +std::string Flussonic::download(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) { + std::string url = create_url(_ip, camera_code, start_time, end_time); + + auto r = cpr::Get(cpr::Url(url)); + + // Create filename based on url, starting from camera_code + std::size_t pos = url.find(camera_code); + std::string filename = url.substr(pos) + ".mp4"; + filename = boost::replace_all_copy(filename, "/", "_"); + + std::ofstream downloaded_file; + downloaded_file.open(filename); + downloaded_file << r.text; + downloaded_file.close(); + + return url; +} + +std::string Flussonic::live_stream(const std::string &camera_code, + const std::string &nvr_code, + const std::string &transport) { + std::string live_stream_url = "http://" + _ip + ":" + std::to_string(_port) + + "/" + camera_code + "/index.m3u8"; + + return live_stream_url; +} + +std::string Flussonic::create_url(const std::string &_ip, + const std::string &camera_code, + const std::string &start_time, + const std::string &end_time) { + std::string duration = std::to_string(to_unix_timestamp(end_time) - + to_unix_timestamp(start_time)); + + std::string start_time_vid = std::to_string(to_unix_timestamp(start_time)); + + std::string url = _ip + ":" + std::to_string(_port) + "/" + camera_code + + "/" + "archive-" + start_time_vid + "-" + duration + ".mp4"; + return url; +} + +/** + * @brief unixtimestamp converter + * + * @param timestamp timestamp in string format + * @return std::time_t unixtimestamp + */ +std::time_t Flussonic::to_unix_timestamp(const std::string ×tamp) { + std::tm tm; + std::string str = timestamp; + + std::string tahun = str.substr(0, 4); + std::string bulan = str.substr(4, 2); + std::string tanggal = str.substr(6, 2); + + std::string jam = str.substr(8, 2); + std::string menit = str.substr(10, 2); + std::string detik = str.substr(12, 2); + + tm.tm_year = stoi(tahun) - 1900; + tm.tm_mon = stoi(bulan) - 1; + tm.tm_mday = stoi(tanggal); + + tm.tm_hour = stoi(jam); + tm.tm_min = stoi(menit); + tm.tm_sec = stoi(detik); + + time_t time = mktime(&tm); + + return time; +} +bool Flussonic::status_stream(const std::string &_ip, const unsigned int &_port, + const std::string &camera_code) { + std::string url = "http://" + _id + ":" + _pass + "@" + _ip + ":" + + std::to_string(_port) + "/flussonic/api/stream_health/" + + camera_code; + + auto r = cpr::Get(cpr::Url(url)); + + return to_bool(r.text); +} + +bool Flussonic::to_bool(std::string str) { + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + std::istringstream is(str); + bool boolean; + is >> std::boolalpha >> boolean; + return boolean; +} +} // namespace flussonic +} // namespace vms \ No newline at end of file diff --git a/modules/huawei/include/hw_ivs.h b/modules/huawei/include/hw_ivs.h index 7c06c95..1abd21c 100644 --- a/modules/huawei/include/hw_ivs.h +++ b/modules/huawei/include/hw_ivs.h @@ -32,6 +32,11 @@ class HuaweiIVS : public VMSInterface { const std::string &start_time, const std::string &end_time) override; + std::string download(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) override; + std::string live_stream(const std::string &camera_code, const std::string &nvr_code, const std::string &transport) override; diff --git a/modules/huawei/src/hw_ivs.cpp b/modules/huawei/src/hw_ivs.cpp index 638d089..f4f96cd 100644 --- a/modules/huawei/src/hw_ivs.cpp +++ b/modules/huawei/src/hw_ivs.cpp @@ -220,6 +220,13 @@ std::string hwivs::HuaweiIVS::playback(const std::string &camera_code, return std::string(rtsp_url.get()); } +std::string hwivs::HuaweiIVS::download(const std::string &camera_code, + const std::string &nvr_code, + const std::string &start_time, + const std::string &end_time) { + return ""; +} + std::vector get_stream_info(int session_id, const std::string &camera_code) { IVS_CAMERA_STREAM_CFG stream_config = {0};