Skip to content
This repository has been archived by the owner on Feb 9, 2023. It is now read-only.

#9 Add the video feed window #62

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cfg/video_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"stream_ip":
{
"addr": "239.255.123.123",
"addr": "192.168.1.10",
"port": 22202
},
"command_ip":
Expand Down
71 changes: 45 additions & 26 deletions src/basestation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ set(MODULE_HDR
modules/input_config/controller_config.hpp
modules/input_config/controller_calibration_popup.hpp
modules/input_config/controller_binding_popup.hpp
modules/video_feed_viewer.hpp
)
set(MODULE_SRC
modules/console.cpp
modules/video_feed_viewer.cpp
modules/network_settings.cpp
modules/drive_stats.cpp
modules/statusbar.cpp
Expand Down Expand Up @@ -38,34 +40,51 @@ set(WIDGET_SRC
widgets/layouts/simple_column.cpp
)

add_executable(basestation
main.cpp
basestation.hpp
basestation.cpp
basestation_screen.hpp
basestation_screen.cpp
controls/controller_manager.hpp
controls/controller_manager.cpp
controls/controller.hpp
controls/controller.cpp
controls/lua_ctrl_lib.hpp
controls/lua_ctrl_lib.cpp
controls/drive_input.hpp
controls/drive_input.cpp
${MODULE_HDR}
${MODULE_SRC}
${WIDGET_HDR}
${WIDGET_SRC}
)
find_package(PkgConfig)
if (NOT PKG_CONFIG_FOUND)
message(STATUS "basestation: pkg-config unavailable.")
else()
pkg_search_module(PKG_LIBJPEG_TURBO IMPORTED_TARGET libturbojpeg)

if (NOT PKG_LIBJPEG_TURBO_FOUND)
message(STATUS "basestation: libjpeg-turbo unavailable.")
else()

find_package(Boost COMPONENTS program_options REQUIRED)

find_package(Boost COMPONENTS program_options REQUIRED)
add_executable(basestation
main.cpp
basestation.hpp
basestation.cpp
basestation_screen.hpp
basestation_screen.cpp
controls/controller_manager.hpp
controls/controller_manager.cpp
controls/controller.hpp
controls/controller.cpp
controls/lua_ctrl_lib.hpp
controls/lua_ctrl_lib.cpp
controls/drive_input.hpp
controls/drive_input.cpp
video_decoder/decoder.hpp
video_decoder/decoder.cpp
${MODULE_HDR}
${MODULE_SRC}
${WIDGET_HDR}
${WIDGET_SRC}
)

target_include_directories(basestation PUBLIC nanogui roverlua ${CMAKE_CURRENT_SOURCE_DIR} rover_control Boost::headers)
target_link_libraries(basestation nanogui ${NANOGUI_EXTRA_LIBS} roverlua rover_control Boost::program_options)
target_include_directories(basestation PUBLIC nanogui roverlua network rover_control ${CMAKE_CURRENT_SOURCE_DIR} Boost::headers)
target_link_libraries(basestation nanogui ${NANOGUI_EXTRA_LIBS} roverlua rover_control network PkgConfig::PKG_LIBJPEG_TURBO Boost::program_options)

# NanoGUI requires C++17
target_compile_features(basestation PUBLIC cxx_std_17)
# NanoGUI requires C++17
target_compile_features(basestation PUBLIC cxx_std_17)

if (APPLE)
target_link_libraries(basestation "-framework OpenGL")
if (APPLE)
target_link_libraries(basestation "-framework OpenGL")
endif()

endif()
endif()


9 changes: 9 additions & 0 deletions src/basestation/basestation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Basestation::Basestation() : Basestation(boost::property_tree::ptree()) {}
Basestation::Basestation(const boost::property_tree::ptree& config)
: m_subsystem_sender(main_thread_ctx),
m_subsystem_feed(main_thread_ctx),
video_feed_receiver(main_thread_ctx),
m_remote_drive(m_subsystem_sender) {

if (main_instance != nullptr) {
Expand All @@ -35,6 +36,14 @@ Basestation::Basestation(const boost::property_tree::ptree& config)
m_remote_drive.register_listen_handlers(m_subsystem_feed);
m_remote_sensors.register_listen_handlers(m_subsystem_feed);

video_feed_receiver.set_listen_port(22202);
video_feed_receiver.open();

//TODO: Add commands or GUI window to open streams. Also do not use 9 fixed like this...
for (int i = 0; i < 9; i++) {
video_feed_receiver.open_stream(i);
}

Console::add_setup_routine([](Console& new_console) {
new_console.load_library("ctrl", lua_ctrl_lib::open);
new_console.load_library("bs", lua_basestation_lib::open);
Expand Down
10 changes: 9 additions & 1 deletion src/basestation/basestation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
#include <boost/property_tree/ptree.hpp>

#include <network.hpp>

#include <rover_lua.hpp>
#include <basestation_screen.hpp>
#include <controls/controller_manager.hpp>
#include <controls/drive_input.hpp>
#include <stream.hpp>

/*
Container class for the main instance of the base station
Expand Down Expand Up @@ -48,6 +48,9 @@ class Basestation {
inline net::MessageReceiver& subsystem_feed() {
return m_subsystem_feed;
}
inline net::StreamReceiver& video_stream_feed() {
return video_feed_receiver;
}
inline DriveInput& remote_drive() {
return m_remote_drive;
}
Expand Down Expand Up @@ -78,10 +81,15 @@ class Basestation {
static void open(lua_State*);
};

inline void set_video_callback(const std::function<void(int stream, net::Frame& frame)>& handler) {
video_feed_receiver.on_frame_received(handler);
}

private:
boost::asio::io_context main_thread_ctx;
net::MessageSender m_subsystem_sender;
net::MessageReceiver m_subsystem_feed;
net::StreamReceiver video_feed_receiver;
DriveInput m_remote_drive;
rc::Sensor m_remote_sensors;

Expand Down
5 changes: 5 additions & 0 deletions src/basestation/basestation_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <basestation.hpp>

#include <modules/console.hpp>
#include <modules/video_feed_viewer.hpp>
#include <modules/statusbar.hpp>

ScreenPositioning::ScreenPositioning(const nanogui::Vector2i& size, const nanogui::Vector2i& window_pos, int monitor, bool use_fullscreen) :
Expand Down Expand Up @@ -123,6 +124,10 @@ bool BasestationScreen::keyboard_event(int key, int scancode, int action, int mo
} else if (key == GLFW_KEY_N && action == GLFW_PRESS && (mods & GLFW_MOD_CONTROL)) {
Basestation::get().add_screen(new BasestationScreen());
handled = true;
} else if (key == GLFW_KEY_C && action == GLFW_PRESS) {
new VideoFeedViewer(this);
Basestation::get().set_video_callback(VideoFeedViewer::update_frame_STATIC);
handled = true;
}

return handled;
Expand Down
93 changes: 87 additions & 6 deletions src/basestation/modules/network_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ gui::NetworkSettings::NetworkSettings(nanogui::Screen* screen) :
Section: Subsystem Link settings
*/

form->add_group("Subsystem Update Feed");
form->add_group("Subsystem Update Feed (Incoming)");

auto feed_ip_entry = form->add_variable("Feed IP Address", mcast_feed_str);
// This terrifying regex for IP validation was provided by:
Expand Down Expand Up @@ -100,7 +100,7 @@ gui::NetworkSettings::NetworkSettings(nanogui::Screen* screen) :
feed_mcast_mode_box->callback()(subsys_feed_mcast);


subsys_feed_enable = Basestation::get().subsystem_feed().is_multicast();
subsys_feed_enable = Basestation::get().subsystem_feed().opened();
auto feed_enable_box = form->add_variable("Open", subsys_feed_enable);
feed_enable_box->set_callback([this](bool checked) {
try {
Expand All @@ -114,7 +114,11 @@ gui::NetworkSettings::NetworkSettings(nanogui::Screen* screen) :
subsys_feed_enable = Basestation::get().subsystem_feed().opened();
});

form->add_group("Subsystem Device Link");
/*
Section: Outgoing Subsystem Link
*/

form->add_group("Subsystem Device Link (Outgoing)");

auto ip_entry = form->add_variable("Device IP Address", ip_str);

Expand Down Expand Up @@ -180,10 +184,87 @@ gui::NetworkSettings::NetworkSettings(nanogui::Screen* screen) :
});

/*
(TODO) Section: Video Link settings

(This section is a placeholder)
Section: Video Link settings
*/
form->add_group("Video Stream Feed (Incoming)");

auto stream_ip_entry = form->add_variable("Feed IP Address", video_stream_ip_str);
// This terrifying regex for IP validation was provided by:
// https://stackoverflow.com/a/36760050
stream_ip_entry->set_format("^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)(\\.(?!$)|$)){4}$");
stream_ip_entry->set_value(Basestation::get().video_stream_feed().listen_endpoint().address().to_string());
stream_ip_entry->set_fixed_width(text_entry_width);
stream_ip_entry->set_callback([this](const std::string& str) {
auto& feed = Basestation::get().video_stream_feed();
if (str.size() != 0) {
try {
auto ep = feed.listen_endpoint();
ep.address(boost::asio::ip::address_v4::from_string(str));
feed.set_listen_endpoint(ep);
return true;
} catch (const std::exception& err) {
open_error_popup(err);
}
}
video_stream_ip_str = feed.listen_endpoint().address().to_string();
return false;
});

video_stream_port = Basestation::get().video_stream_feed().listen_endpoint().port();

auto stream_port_entry = form->add_variable("Feed Port", video_stream_port);
// Regex: https://3widgets.com/, range: 0 - 65535
stream_port_entry->set_format("(\\d|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])");
stream_port_entry->set_fixed_width(text_entry_width);
stream_port_entry->set_callback([this](int set_port) {
auto& feed = Basestation::get().video_stream_feed();
try {
if (set_port >= 0 && set_port <= std::numeric_limits<uint16_t>().max()) {
auto ep = feed.listen_endpoint();
ep.port(set_port);
feed.set_listen_endpoint(ep);
return true;
}
} catch (const std::exception& e) {
open_error_popup(e);
}
video_stream_port = feed.listen_endpoint().port();
return false;
});

video_stream_multicast = Basestation::get().video_stream_feed().is_multicast();
auto stream_mcast_mode_box = form->add_variable("Multicast", video_stream_multicast);
stream_mcast_mode_box->set_callback([this, stream_ip_entry](bool checked) {
auto& feed = Basestation::get().video_stream_feed();
try {
stream_ip_entry->set_editable(checked);
if (checked) {
stream_ip_entry->set_value(feed.listen_endpoint().address().to_string());
} else {
stream_ip_entry->set_value("");
}
feed.set_multicast(checked);
} catch (const std::exception& err) {
open_error_popup(err);
}
video_stream_multicast = feed.is_multicast();
});
stream_mcast_mode_box->callback()(video_stream_multicast);


video_stream_enable = Basestation::get().video_stream_feed().opened();
auto stream_enable_box = form->add_variable("Open", video_stream_enable);
stream_enable_box->set_callback([this](bool checked) {
try {
if (checked)
Basestation::get().video_stream_feed().open();
else
Basestation::get().video_stream_feed().close();
} catch (const std::exception& err) {
open_error_popup(err);
}
video_stream_enable = Basestation::get().video_stream_feed().opened();
});

set_position(15);
set_visible(true);
Expand Down
4 changes: 4 additions & 0 deletions src/basestation/modules/network_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ class NetworkSettings : public gui::Window {
// Most of these variables are for the form helper widgets but aren't really needed
std::string ip_str;
std::string mcast_feed_str;
std::string video_stream_ip_str;
int mcast_feed_port;
int port;
int video_stream_port;
int interval;
bool enable;
bool subsys_feed_enable;
bool subsys_feed_mcast;
bool video_stream_enable;
bool video_stream_multicast;

int text_entry_width = 150;

Expand Down
7 changes: 7 additions & 0 deletions src/basestation/modules/statusbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <modules/electrical_info.hpp>
#include <modules/drive_stats.hpp>
#include <modules/input_config/controller_config.hpp>
#include <modules/video_feed_viewer.hpp>

gui::Statusbar::Statusbar(nanogui::Widget* parent) : gui::Toolbar(parent) {
{
Expand All @@ -20,6 +21,12 @@ gui::Statusbar::Statusbar(nanogui::Widget* parent) : gui::Toolbar(parent) {
auto wnd = new ControllerConfig(screen(), Basestation::get().controller_manager());
wnd->center();
});
auto video_player_button = new nanogui::ToolButton(left_tray(), FA_VIDEO);
video_player_button->set_flags(nanogui::Button::NormalButton);
video_player_button->set_callback([this] {
auto wnd = new VideoFeedViewer(screen());
wnd->center();
});
}
{
auto drive_button = new nanogui::ToolButton(right_tray(), FA_COGS);
Expand Down
Loading