From 8da8f85bc32abb24002fd5fb21253e6b76437223 Mon Sep 17 00:00:00 2001
From: Iiro Rastas <iitara@utu.fi>
Date: Mon, 2 Dec 2019 17:25:01 +0200
Subject: [PATCH] Add recording window functionality

The recording window can now make every type of recording, and can do so
whether the chosen camera is active or not. In the case of virtual
camera recordings, it is necessary to set the chosen camera as the
active camera. The recordings can also be named.
---
 applications/gui/src/camera.cpp        |  7 +--
 applications/gui/src/camera.hpp        |  2 +-
 applications/gui/src/media_panel.cpp   | 73 ++++++++++++++++----------
 applications/gui/src/media_panel.hpp   |  4 ++
 applications/gui/src/record_window.cpp | 37 ++++++++++---
 applications/gui/src/record_window.hpp |  5 +-
 applications/reconstruct/src/main.cpp  |  2 +-
 7 files changed, 84 insertions(+), 46 deletions(-)

diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp
index 38f6c5f77..9d8dac421 100644
--- a/applications/gui/src/camera.cpp
+++ b/applications/gui/src/camera.cpp
@@ -524,11 +524,8 @@ const GLTexture &ftl::gui::Camera::captureFrame() {
 	return texture1_;
 }
 
-void ftl::gui::Camera::snapshot() {
+void ftl::gui::Camera::snapshot(const std::string &filename) {
 	UNIQUE_LOCK(mutex_, lk);
-	char timestamp[18];
-	std::time_t t = std::time(NULL);
-	std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
 	cv::Mat blended;
 	cv::Mat visualized = visualizeActiveChannel();
 	if (!visualized.empty()) {
@@ -539,7 +536,7 @@ void ftl::gui::Camera::snapshot() {
 	}
 	cv::Mat flipped;
 	cv::flip(blended, flipped, 0);
-	cv::imwrite(std::string(timestamp) + ".png", flipped);
+	cv::imwrite(filename, flipped);
 }
 
 void ftl::gui::Camera::startVideoRecording(const std::string &filename) {
diff --git a/applications/gui/src/camera.hpp b/applications/gui/src/camera.hpp
index fe477f624..90914cf59 100644
--- a/applications/gui/src/camera.hpp
+++ b/applications/gui/src/camera.hpp
@@ -52,7 +52,7 @@ class Camera {
 
 	bool thumbnail(cv::Mat &thumb);
 
-	void snapshot();
+	void snapshot(const std::string &filename);
 
 	void startVideoRecording(const std::string &filename);
 
diff --git a/applications/gui/src/media_panel.cpp b/applications/gui/src/media_panel.cpp
index c576f28f6..1a6f288ce 100644
--- a/applications/gui/src/media_panel.cpp
+++ b/applications/gui/src/media_panel.cpp
@@ -47,23 +47,10 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen, ftl::gui::SourceWindow *sourceW
 	recordpopup->setAnchorHeight(150);
 	auto itembutton = new Button(recordpopup, "2D snapshot (.png)");
 	itembutton->setCallback([this,recordbutton]() {
-		screen_->activeCamera()->snapshot();
-		recordbutton->setPushed(false);
-	});
-	itembutton = new Button(recordpopup, "3D snapshot (.ftl)");
-	itembutton->setCallback([this,recordbutton]() {
-		auto tag = screen_->activeCamera()->source()->get<std::string>("uri");
-		if (tag) {
-			auto tagvalue = tag.value();
-			auto configurables = ftl::config::findByTag(tagvalue);
-			if (configurables.size() > 0) {
-				ftl::Configurable *configurable = configurables[0];
-				char timestamp[18];
-				std::time_t t=std::time(NULL);
-				std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
-				configurable->set("3D-snapshot", std::string(timestamp) + ".ftl");
-			}
-		}
+		char timestamp[18];
+		std::time_t t=std::time(NULL);
+		std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
+		screen_->activeCamera()->snapshot(std::string(timestamp) + ".png");
 		recordbutton->setPushed(false);
 	});
 	itembutton = new Button(recordpopup, "Virtual camera recording (.ftl)");
@@ -76,24 +63,26 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen, ftl::gui::SourceWindow *sourceW
 		recordbutton->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
 		recordbutton->setPushed(false);
 	});
+	itembutton = new Button(recordpopup, "3D scene snapshot (.ftl)");
+	itembutton->setCallback([this,recordbutton]() {
+		char timestamp[18];
+		std::time_t t=std::time(NULL);
+		std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
+		snapshot3D(screen_->activeCamera(), std::string(timestamp) + ".ftl");
+		recordbutton->setPushed(false);
+	});
 	itembutton = new Button(recordpopup, "3D scene recording (.ftl)");
 	itembutton->setCallback([this,recordbutton]() {
-		auto tag = screen_->activeCamera()->source()->get<std::string>("uri");
-		if (tag) {
-			auto tagvalue = tag.value();
-			auto configurables = ftl::config::findByTag(tagvalue);
-			if (configurables.size() > 0) {
-				ftl::Configurable *configurable = configurables[0];
-				configurable->set("record", true);
-				recordbutton->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
-				sceneRecording_ = std::optional<ftl::Configurable*>(configurable);
-			}
-		}
+		char timestamp[18];
+		std::time_t t=std::time(NULL);
+		std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
+		startRecording3D(screen_->activeCamera(), std::string(timestamp) + ".ftl");
+		recordbutton->setTextColor(nanogui::Color(1.0f,0.1f,0.1f,1.0f));
 		recordbutton->setPushed(false);
 	});
 	itembutton = new Button(recordpopup, "Detailed recording options");
 	itembutton->setCallback([this,sourceWindow,recordbutton] {
-		auto record_window = new RecordWindow(screen_, sourceWindow->getCameras(), this);
+		auto record_window = new RecordWindow(screen_, screen_, sourceWindow->getCameras(), this);
 		record_window->setTheme(screen_->windowtheme);
 		recordbutton->setPushed(false);
 	});
@@ -308,4 +297,30 @@ void MediaPanel::cameraChanged() {
 void MediaPanel::toggleVirtualCameraRecording(ftl::gui::Camera *camera, const std::string &filename) {
 	camera->startVideoRecording(filename);
 	virtualCameraRecording_ = std::optional<ftl::gui::Camera*>(camera);
+}
+
+void MediaPanel::snapshot3D(ftl::gui::Camera *camera, const std::string &filename) {
+	auto tag = camera->source()->get<std::string>("uri");
+	if (tag) {
+		auto tagvalue = tag.value();
+		auto configurables = ftl::config::findByTag(tagvalue);
+		if (configurables.size() > 0) {
+			ftl::Configurable *configurable = configurables[0];
+			configurable->set("3D-snapshot", filename);
+		}
+	}
+}
+
+void MediaPanel::startRecording3D(ftl::gui::Camera *camera, const std::string &filename) {
+	auto tag = camera->source()->get<std::string>("uri");
+	if (tag) {
+		auto tagvalue = tag.value();
+		auto configurables = ftl::config::findByTag(tagvalue);
+		if (configurables.size() > 0) {
+			ftl::Configurable *configurable = configurables[0];
+			configurable->set("record-name", filename);
+			configurable->set("record", true);
+			sceneRecording_ = std::optional<ftl::Configurable*>(configurable);
+		}
+	}
 }
\ No newline at end of file
diff --git a/applications/gui/src/media_panel.hpp b/applications/gui/src/media_panel.hpp
index b7e5957cc..a71ee8183 100644
--- a/applications/gui/src/media_panel.hpp
+++ b/applications/gui/src/media_panel.hpp
@@ -26,6 +26,10 @@ class MediaPanel : public nanogui::Window {
 
 	void toggleVirtualCameraRecording(ftl::gui::Camera *camera, const std::string &filename);
 
+	void snapshot3D(ftl::gui::Camera *camera, const std::string &filename);
+
+	void startRecording3D(ftl::gui::Camera *camera, const std::string &filename);
+
 	private:
 	ftl::gui::Screen *screen_;
 
diff --git a/applications/gui/src/record_window.cpp b/applications/gui/src/record_window.cpp
index 82b333bc9..50cdf9d3e 100644
--- a/applications/gui/src/record_window.cpp
+++ b/applications/gui/src/record_window.cpp
@@ -1,5 +1,7 @@
 #include "record_window.hpp"
 
+#include "screen.hpp"
+
 #include <ftl/codecs/channels.hpp>
 
 #include <nanogui/layout.h>
@@ -11,7 +13,7 @@
 
 using ftl::gui::RecordWindow;
 
-RecordWindow::RecordWindow(nanogui::Widget *parent, const std::vector<ftl::gui::Camera *> &streams, ftl::gui::MediaPanel *media_panel)
+RecordWindow::RecordWindow(nanogui::Widget *parent, ftl::gui::Screen *screen, const std::vector<ftl::gui::Camera *> &streams, ftl::gui::MediaPanel *media_panel)
         : nanogui::Window(parent, "Recording options") {
     using namespace nanogui;
 
@@ -54,23 +56,42 @@ RecordWindow::RecordWindow(nanogui::Widget *parent, const std::vector<ftl::gui::
     new Label(recording2D, "Select channel (in addition to Left)", "sans-bold");
     auto recordingChannel = recording2D->add<ComboBox>();
     streamSelect->setCallback([this,streams,snapshotChannel,recordingChannel](int ix) {
-        channels_ = std::vector<std::string>();
+        channels_ = std::vector<ftl::codecs::Channel>();
+        channel_names_ = std::vector<std::string>();
         ftl::codecs::Channels availableChannels = streams[ix]->availableChannels();
         for (auto c : availableChannels) {
-            channels_.push_back(ftl::codecs::name(c));
+            channels_.push_back(c);
+            channel_names_.push_back(ftl::codecs::name(c));
         }
-            snapshotChannel->setItems(channels_);
-            recordingChannel->setItems(channels_);
+        snapshotChannel->setItems(channel_names_);
+        recordingChannel->setItems(channel_names_);
     });
 
     Widget *actionButtons = new Widget(this);
     actionButtons->setLayout(new BoxLayout(Orientation::Horizontal));
     auto button = new Button(actionButtons, "Start");
-    button->setCallback([streams,streamSelect,media_panel,fileName]() {
+    button->setCallback([this,streams,streamSelect,screen,media_panel,fileName,tabWidget,snapshot2D,recording2D,snapshot3D,recording3D,snapshotChannel,recordingChannel]() {
         // Check the chosen stream type and channels, then record them.
         auto stream = streams[streamSelect->selectedIndex()];
-        // TODO: If the camera isn't active, no frames are currently received.
-        media_panel->toggleVirtualCameraRecording(stream, fileName->value());
+        auto tab = tabWidget->tab(tabWidget->activeTab());
+        if (tab == snapshot2D) {
+            // If the channel isn't the same as the one that was active
+            // previously, the channel won't have an image.
+            // One possible solution is to set the channels immediately
+            // as they are chosen in the recording window.
+            // Alternatively, 2D snapshots might not need a channel option at all.
+            stream->setChannel(channels_[snapshotChannel->selectedIndex()]);
+            stream->snapshot(fileName->value());
+        } else if (tab == recording2D) {
+            stream->setChannel(channels_[recordingChannel->selectedIndex()]);
+            screen->setActiveCamera(stream);
+            media_panel->toggleVirtualCameraRecording(stream, fileName->value());
+        } else if (tab == snapshot3D) {
+            media_panel->snapshot3D(stream, fileName->value());
+        } else if (tab == recording3D) {
+            media_panel->startRecording3D(stream, fileName->value());
+        }
+        dispose();
     });
     button = new Button(actionButtons, "Cancel");
     button->setCallback([this]() {
diff --git a/applications/gui/src/record_window.hpp b/applications/gui/src/record_window.hpp
index 4396c0456..5a9b28fef 100644
--- a/applications/gui/src/record_window.hpp
+++ b/applications/gui/src/record_window.hpp
@@ -8,11 +8,12 @@ namespace gui {
 
 class RecordWindow : public nanogui::Window {
     public:
-    explicit RecordWindow(nanogui::Widget *parent, const std::vector<ftl::gui::Camera *> &streams, ftl::gui::MediaPanel *media_panel);
+    explicit RecordWindow(nanogui::Widget *parent, ftl::gui::Screen *screen, const std::vector<ftl::gui::Camera *> &streams, ftl::gui::MediaPanel *media_panel);
     ~RecordWindow();
 
     private:
-    std::vector<std::string> channels_;
+    std::vector<ftl::codecs::Channel> channels_;
+    std::vector<std::string> channel_names_;
 };
 
 }
diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index 66d6e3189..3a5807171 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -294,7 +294,7 @@ static void run(ftl::Configurable *root) {
 			char timestamp[18];
 			std::time_t t=std::time(NULL);
 			std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
-			fileout.open(std::string(timestamp) + ".ftl");
+			fileout.open(e.entity->value<std::string>("record-name", std::string(timestamp) + ".ftl"));
 
 			writer.begin();
 			group->addRawCallback(std::function(recorder));
-- 
GitLab