Files
OpenVulkano/openVulkanoCpp/AR/ArRecorder.hpp
2024-12-06 21:15:02 +01:00

205 lines
6.5 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "Math/ByteSize.hpp"
#include "Base/Event.hpp"
#include "Base/Timer.hpp"
#include <atomic>
#include <condition_variable>
#include <filesystem>
#include <mutex>
#include <memory>
#include <string>
#include <thread>
#include <queue>
namespace OpenVulkano
{
class IEventHandler;
class MultiPartArchiveWriter;
}
namespace OpenVulkano::AR
{
class ArSession;
class ArFrame;
enum class RecordingMode
{
/**
* Frames will only be saved when manually requested through ArFrame::Save()
*/
MANUAL,
/**
* Frames will only be saved when the frame is manually requested through ArSession::GetFrame()
*/
FRAME_REQUEST,
/**
* Every new frame will be stored automatically
*/
NEW_FRAME
};
struct RecordingSettings
{
RecordingMode recordingMode = RecordingMode::MANUAL;
std::filesystem::path path;
size_t archiveSize = 2_GiB;
bool downsampleColor = true;
bool highResFramesInSeparateArchive = true;
bool saveHighResFrames = true;
bool asyncRecording = true;
};
class ArRecorder final
{
struct AsyncProcessor final
{
ArRecorder* recorder;
std::thread processingThread;
std::queue<std::shared_ptr<ArFrame>> frameQueue, highResFrameQueue;
std::mutex queueMutex;
std::condition_variable newDataAvailable;
std::atomic_bool requestExit{};
explicit AsyncProcessor(ArRecorder* recorder);
~AsyncProcessor();
void Queue(const std::shared_ptr<ArFrame>& frame, bool highRes);
void Handler();
};
ArSession* m_session;
std::unique_ptr<MultiPartArchiveWriter> m_colorWriter, m_depthWriter, m_confidenceWriter, m_metadataWriter, m_highResWriter;
RecordingSettings m_settings;
uint64_t m_frameCount = 0;
uint32_t m_skippedFrames = 0;
bool m_recording = false, m_persistent = false;
Timer m_timer;
IEventHandler* m_newFrameHandler = nullptr;
AsyncProcessor m_asyncProcessor;
void Write(ArFrame* frame, bool highRes = false);
void WriteMetadata(ArFrame* frame, MultiPartArchiveWriter* metaWriter);
void WriteColorImage(ArFrame* arFrame, MultiPartArchiveWriter* colorWriter, const std::filesystem::path* path, bool highRes) const;
void WriteDepthImage(ArFrame *arFrame, MultiPartArchiveWriter* depthWriter, MultiPartArchiveWriter* confWriter);
void SplitWriters();
void WriteMetadataFile();
public:
ArRecorder(ArSession* session);
~ArRecorder();
void Save(const std::shared_ptr<ArFrame>& frame);
void SaveHighResolution(const std::shared_ptr<ArFrame>& frame);
void SaveToFile(const std::shared_ptr<ArFrame>& frame, const std::filesystem::path& path, bool downsample = false);
/**
* Starts the recording of the owning AR session
*/
void Start();
/**
* Stops the recording of the owning AR session
*/
void Stop();
/**
* Sets the directory into which the AR recording should be stored.
* The path needs to be set to make the recording persistent.
* If path is changed after starting the recording, the already running recording will be moved to the new path.
* @param path The path to be used to store the recording
*/
void SetRecordingPath(const std::string& path)
{
std::filesystem::path p(path);
SetRecordingPath(p);
}
/**
* Sets the directory into which the AR recording should be stored.
* The path needs to be set to make the recording persistent.
* If path is changed after starting the recording, the already running recording will be moved to the new path.
* @param path The path to be used to store the recording
*/
void SetRecordingPath(const std::filesystem::path& path);
/**
* Gets the current recording path
* @return Current recording dir
*/
[[nodiscard]] const std::filesystem::path& GetRecordingPath() const { return m_settings.path; }
/**
* Checks if a path to be used for the recording has been set.
* If no path has been set the recording will be stored in a temporary location and will be deleted once the recording is stopped.
* @return True if a path has been set and the recording will persist after stopping the recording.
*/
[[nodiscard]] bool IsPersistent() const { return m_persistent; }
/**
* Checks if the recording of the AR session is running
* @return True if recording is started
*/
[[nodiscard]] bool IsRecording() const { return m_recording; }
/**
* Sets the used recording mode
* @param mode The mode that should be used to record the current session
*/
void SetRecordingMode(RecordingMode mode);
/**
* Checks the currently used recording mode for the AR session
* @return The currently used recording mode
*/
[[nodiscard]] RecordingMode GetRecordingMode() const { return m_settings.recordingMode; }
/**
* If enabled color images will be reduced to 1/4 of the original resolution (both axes will have half the resolution).
* This option can be used to reduce the used storage space and storage bandwidth during recording.
*
* @param downsample true = downsample images; false = no downsampling
*/
void SetDownsampleColorImages(bool downsample = true) { m_settings.downsampleColor = downsample; }
/**
* Checks if color image downsampling is enabled.
* @return true = downsample images; false = no downsampling
*/
bool GetDownsampleColorImages() const { return m_settings.downsampleColor; }
/**
* If enabled the requested high resolution images will be stored in a separate archive ("highres.tar") instead of the normal ar recording archives.
* @param separateArchive true = using the separate archive file; false = using the normal archive files
*/
void SetHighResFramesInSeparateArchive(bool separateArchive = true) { m_settings.highResFramesInSeparateArchive = separateArchive; }
/**
* Checks if a separate archive is used for the high res frames.
* @return true = using the separate archive file; false = using the normal archive files
*/
bool GetHighResFramesInSeparateArchive() const { return m_settings.highResFramesInSeparateArchive; }
void SetArchivePartMaxFileSize(size_t maxPartSize = 2_GiB) { m_settings.archiveSize = maxPartSize; }
size_t GetArchivePartMaxFileSize() const { return m_settings.archiveSize; }
const RecordingSettings& GetRecordingSettings() const { return m_settings; }
void SetRecordHighResImages(bool recHighRes = true) { m_settings.saveHighResFrames = recHighRes; }
Event<ArRecorder*, bool> OnRecordingStateChanged;
};
}