From 5a8aa134d3fd88857302eb658dd568779f3cb9d5 Mon Sep 17 00:00:00 2001
From: Iiro Rastas <iitara@utu.fi>
Date: Tue, 12 Nov 2019 16:40:20 +0200
Subject: [PATCH] Reimplement 2D video capture

As the previous approach led to low performance, 2D video is now
captured to an ftl file, which removes the need to encode the video in
real time. The ftl file can be converted into a video file with ftl2mkv.
---
 applications/gui/src/camera.cpp               | 37 +++++++++++--------
 applications/gui/src/camera.hpp               |  6 ++-
 applications/gui/src/media_panel.cpp          |  8 ++++
 .../rgbd-sources/include/ftl/rgbd/source.hpp  |  6 ++-
 4 files changed, 38 insertions(+), 19 deletions(-)

diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp
index 3413d23d9..b8a22a2a0 100644
--- a/applications/gui/src/camera.cpp
+++ b/applications/gui/src/camera.cpp
@@ -137,7 +137,13 @@ ftl::gui::Camera::Camera(ftl::gui::Screen *screen, ftl::rgbd::Source *src) : scr
 	sdepth_ = false;
 	ftime_ = (float)glfwGetTime();
 	pause_ = false;
-	videowriter_ = std::nullopt;
+	recording_ = false;
+	fileout_ = new std::ofstream();
+	writer_ = new ftl::codecs::Writer(*fileout_);
+	recorder_ = std::function([this](ftl::rgbd::Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
+		ftl::codecs::StreamPacket s = spkt;
+		writer_->write(s, pkt);
+	});
 
 	channel_ = Channel::Left;
 
@@ -167,7 +173,8 @@ ftl::gui::Camera::Camera(ftl::gui::Screen *screen, ftl::rgbd::Source *src) : scr
 }
 
 ftl::gui::Camera::~Camera() {
-
+	delete writer_;
+	delete fileout_;
 }
 
 ftl::rgbd::Source *ftl::gui::Camera::source() {
@@ -489,12 +496,6 @@ const GLTexture &ftl::gui::Camera::captureFrame() {
 			//imageSize = Vector2f(rgb.cols,rgb.rows);
 			texture1_.update(im1_);
 		}
-
-		if (videowriter_) {
-			cv::Mat image;
-			cv::flip(im1_, image, 0);
-			videowriter_.value().write(image);
-		}
 	}
 
 	return texture1_;
@@ -511,17 +512,21 @@ void ftl::gui::Camera::snapshot() {
 }
 
 void ftl::gui::Camera::toggleVideoRecording() {
-	if (videowriter_) {
-		videowriter_.value().release();
-		videowriter_ = std::nullopt;
+	if (recording_) {
+		src_->removeRawCallback(recorder_);
+		writer_->end();
+		fileout_->close();
 	} else {
 		char timestamp[18];
-		std::time_t t = std::time(NULL);
+		std::time_t t=std::time(NULL);
 		std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
-		videowriter_ = std::optional<cv::VideoWriter>(cv::VideoWriter(std::string(timestamp) + ".avi",
-													cv::VideoWriter::fourcc('M','J','P','G'),
-													screen_->root()->value("fps", 20),
-													cv::Size(width(), height())));
+		fileout_->open(std::string(timestamp) + ".ftl");
+
+		writer_->begin();
+		src_->addRawCallback(recorder_);
+
+		src_->inject(Channel::Calibration, src_->parameters(), Channel::Left, src_->getCapabilities());
+		src_->inject(src_->getPose());
 	}
 }
 
diff --git a/applications/gui/src/camera.hpp b/applications/gui/src/camera.hpp
index 156d8bb35..89cfba056 100644
--- a/applications/gui/src/camera.hpp
+++ b/applications/gui/src/camera.hpp
@@ -2,6 +2,7 @@
 #define _FTL_GUI_CAMERA_HPP_
 
 #include <ftl/rgbd/source.hpp>
+#include <ftl/codecs/writer.hpp>
 #include "gltexture.hpp"
 
 #include <string>
@@ -89,7 +90,10 @@ class Camera {
 	ftl::codecs::Channels channels_;
 	cv::Mat im1_; // first channel (left)
 	cv::Mat im2_; // second channel ("right")
-	std::optional<cv::VideoWriter> videowriter_;
+	bool recording_;
+	std::ofstream *fileout_;
+	ftl::codecs::Writer *writer_;
+	ftl::rgbd::RawCallback recorder_;
 
 	MUTEX mutex_;
 
diff --git a/applications/gui/src/media_panel.cpp b/applications/gui/src/media_panel.cpp
index fa2fa18bd..9a6eb7259 100644
--- a/applications/gui/src/media_panel.cpp
+++ b/applications/gui/src/media_panel.cpp
@@ -76,6 +76,14 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen) : nanogui::Window(screen, ""),
 		}
 	});
 	itembutton = new Button(recordpopup, "3D scene recording");
+	itembutton->setFlags(Button::ToggleButton);
+	itembutton->setChangeCallback([this,recordbutton](bool state) {
+		if (state) {
+			std::cout << "Starting 3D scene recording." << '\n';
+		} else {
+			std::cout << "Finishing 3D scene recording." << '\n';
+		}
+	});
 	itembutton = new Button(recordpopup, "Detailed recording options");
 
 	button = new Button(this, "", ENTYPO_ICON_CONTROLLER_STOP);
diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp
index 748478879..895817359 100644
--- a/components/rgbd-sources/include/ftl/rgbd/source.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp
@@ -31,6 +31,8 @@ class SnapshotReader;
 class VirtualSource;
 class Player;
 
+typedef std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)> RawCallback;
+
 /**
  * RGBD Generic data source configurable entity. This class hides the
  * internal implementation of an RGBD source by providing accessor functions
@@ -189,12 +191,12 @@ class Source : public ftl::Configurable {
 	 * Currently this only works for a net source since other sources don't
 	 * produce raw encoded data.
 	 */
-	void addRawCallback(const std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)> &);
+	void addRawCallback(const RawCallback &);
 
 	/**
 	 * THIS DOES NOT WORK CURRENTLY.
 	 */
-	void removeRawCallback(const std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)> &);
+	void removeRawCallback(const RawCallback &);
 
 	/**
 	 * INTERNAL. Used to send raw data to callbacks.
-- 
GitLab