From acfb59c6b4c30f71089f6d160a25f5c7ca72b302 Mon Sep 17 00:00:00 2001
From: Iiro Rastas <iitara@utu.fi>
Date: Tue, 10 Dec 2019 17:27:23 +0200
Subject: [PATCH] Fix reconstruction snapshots

Reconstruction snapshots are now fixed. The snapshot is taken from the
next complete IFrame, so reconstruction snapshots cannot be taken while
the reconstruction is paused.
---
 applications/reconstruct/src/main.cpp         | 87 +++++++++++--------
 .../codecs/include/ftl/codecs/writer.hpp      |  1 +
 components/codecs/src/writer.cpp              |  4 +
 3 files changed, 57 insertions(+), 35 deletions(-)

diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index 0f45f363e..e1e7adcd8 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -45,6 +45,9 @@
 #include <ftl/cuda/normals.hpp>
 #include <ftl/registration.hpp>
 
+#include <ftl/codecs/h264.hpp>
+#include <ftl/codecs/hevc.hpp>
+
 #include <cuda_profiler_api.h>
 
 #ifdef WIN32
@@ -269,25 +272,55 @@ static void run(ftl::Configurable *root) {
 	std::ofstream fileout;
 	ftl::codecs::Writer writer(fileout);
 
+	std::ofstream snapshotout;
+	ftl::codecs::Writer snapshotwriter(snapshotout);
+
 	root->set("record", false);
 
-	int64_t timestamp = 0;
-	std::vector<std::pair<ftl::codecs::StreamPacket, ftl::codecs::Packet>> currentFrame, completeFrame;
+	int64_t timestamp = -1;
+	bool writingSnapshot = false;
+	std::unordered_set<int64_t> precedingFrames, followingFrames;
 	// Add a recording callback to all reconstruction scenes
 	for (size_t i=0; i<sources.size(); ++i) {
-		sources[i]->addRawCallback([&writer,&groups,&timestamp,&currentFrame,&completeFrame,i](ftl::rgbd::Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
+		sources[i]->addRawCallback([&writer,&groups,&snapshotout,&snapshotwriter,&timestamp,&writingSnapshot,&precedingFrames,&followingFrames,i](ftl::rgbd::Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) {
 			ftl::codecs::StreamPacket s = spkt;
 
 			// Patch stream ID to match order in group
 			s.streamID = i;
 			writer.write(s, pkt);
 
-			if (s.timestamp > timestamp) {
-				timestamp = s.timestamp;
-				completeFrame = currentFrame;
-				currentFrame = std::vector<std::pair<ftl::codecs::StreamPacket, ftl::codecs::Packet>>({ std::make_pair(s, pkt) });
-			} else if (s.timestamp == timestamp) {
-				currentFrame.push_back(std::make_pair(s, pkt));
+			if (snapshotwriter.active()) {
+				// The frame that is captured is the next IFrame, unless that
+				// IFrame is one of the first two frames seen. In this case a
+				// part of the frame might already have been missed, so the
+				// IFrame after that one is captured instead.
+				if (precedingFrames.size() >= 2) {
+					bool isIFrame = false;
+					switch (pkt.codec) {
+						case ftl::codecs::codec_t::H264:
+							isIFrame = ftl::codecs::h264::isIFrame(pkt.data);
+							break;
+						case ftl::codecs::codec_t::HEVC:
+							isIFrame = ftl::codecs::hevc::isIFrame(pkt.data);
+					}
+
+					if (isIFrame && precedingFrames.count(s.timestamp) == 0) {
+						timestamp = s.timestamp;
+						writingSnapshot = true;
+						snapshotwriter.write(s, pkt);
+					} else if (writingSnapshot && s.timestamp > timestamp) {
+						followingFrames.insert(s.timestamp);
+					}
+
+					// Keep looking for packets of the captured frame until
+					// packets from two following frames have been seen.
+					if (followingFrames.size() >= 2) {
+						snapshotwriter.end();
+						snapshotout.close();
+					}
+				} else {
+					precedingFrames.insert(s.timestamp);
+				}
 			}
 		});
 	}
@@ -315,33 +348,17 @@ static void run(ftl::Configurable *root) {
 		}
 	});
 
-	std::ofstream snapshotout;
-	ftl::codecs::Writer snapshotwriter(snapshotout);
-
-	root->on("3D-snapshot", [&snapshotout,&snapshotwriter,&sources,&currentFrame](const ftl::config::Event &e) {
-		char timestamp[18];
-		std::time_t t=std::time(NULL);
-		std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
-		snapshotout.open(e.entity->value<std::string>("3D-snapshot", std::string(timestamp) + ".ftl"));
-
-		snapshotwriter.begin();
-
-		for (size_t i=0; i<sources.size(); ++i) {
-			auto packet_pair = sources[i]->make_packet(Channel::Calibration, sources[i]->parameters(), Channel::Left, sources[i]->getCapabilities());
-			packet_pair.first.streamID = i;
-			snapshotwriter.write(packet_pair.first, packet_pair.second);
-
-			packet_pair = sources[i]->make_packet(sources[i]->getPose());
-			packet_pair.first.streamID = i;
-			snapshotwriter.write(packet_pair.first, packet_pair.second);
-		}
-
-		for (auto &p : currentFrame) {
-			snapshotwriter.write(p.first, p.second);
+	root->on("3D-snapshot", [&snapshotout,&snapshotwriter,&writingSnapshot,&precedingFrames,&followingFrames](const ftl::config::Event &e) {
+		if (!snapshotwriter.active()) {
+			char timestamp[18];
+			std::time_t t=std::time(NULL);
+			std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t));
+			snapshotout.open(e.entity->value<std::string>("3D-snapshot", std::string(timestamp) + ".ftl"));
+			writingSnapshot = false;
+			precedingFrames.clear();
+			followingFrames.clear();
+			snapshotwriter.begin();
 		}
-
-		snapshotwriter.end();
-		snapshotout.close();
 	});
 
 	// -------------------------------------------------------------------------
diff --git a/components/codecs/include/ftl/codecs/writer.hpp b/components/codecs/include/ftl/codecs/writer.hpp
index 94d82f1ec..c1a0ba6c3 100644
--- a/components/codecs/include/ftl/codecs/writer.hpp
+++ b/components/codecs/include/ftl/codecs/writer.hpp
@@ -19,6 +19,7 @@ class Writer {
 	bool begin();
 	bool write(const ftl::codecs::StreamPacket &, const ftl::codecs::Packet &);
 	bool end();
+	bool active();
 
 	private:
 	std::ostream *stream_;
diff --git a/components/codecs/src/writer.cpp b/components/codecs/src/writer.cpp
index 1e3841a8e..dba5853ac 100644
--- a/components/codecs/src/writer.cpp
+++ b/components/codecs/src/writer.cpp
@@ -46,3 +46,7 @@ bool Writer::write(const ftl::codecs::StreamPacket &s, const ftl::codecs::Packet
 	(*stream_).write(buffer.data(), buffer.size());
 	return true;
 }
+
+bool Writer::active() {
+	return active_;
+}
\ No newline at end of file
-- 
GitLab