Files
panopainter/src/app_core/document_recording.h

165 lines
4.5 KiB
C++

#pragma once
#include "app_core/app_dialog.h"
#include "foundation/result.h"
#include <cstddef>
#include <limits>
#include <string_view>
namespace pp::app {
enum class RecordingStartAction {
start_thread,
no_op_already_running,
};
enum class RecordingStopAction {
stop_thread,
no_op_not_running,
};
struct RecordingClearPlan {
bool stop_running_recording = false;
bool delete_recorded_files = false;
int frame_count_after_clear = 0;
};
struct RecordingExportPlan {
std::size_t frame_count = 0;
int progress_total = 0;
};
struct RecordingWorkerIterationPlan {
bool continue_running = true;
bool encode_frame = false;
bool clear_dirty_stroke = false;
bool update_frame_label = false;
};
class RecordingServices {
public:
virtual ~RecordingServices() = default;
virtual void start_thread() = 0;
virtual void stop_thread() = 0;
virtual void delete_recorded_files() = 0;
virtual void set_frame_count(int frame_count) = 0;
virtual void update_frame_label() = 0;
virtual void begin_export(int progress_total) = 0;
virtual void write_mp4(std::string_view path) = 0;
virtual void end_export() = 0;
};
[[nodiscard]] constexpr RecordingStartAction plan_recording_start(bool is_running) noexcept
{
return is_running
? RecordingStartAction::no_op_already_running
: RecordingStartAction::start_thread;
}
[[nodiscard]] constexpr RecordingStopAction plan_recording_stop(bool is_running) noexcept
{
return is_running
? RecordingStopAction::stop_thread
: RecordingStopAction::no_op_not_running;
}
[[nodiscard]] constexpr RecordingClearPlan plan_recording_clear(
bool is_running,
bool platform_deletes_recorded_files) noexcept
{
return {
is_running,
platform_deletes_recorded_files,
0,
};
}
[[nodiscard]] constexpr RecordingExportPlan plan_recording_export(std::size_t frame_count) noexcept
{
const auto max_progress_total = static_cast<std::size_t>(std::numeric_limits<int>::max());
return {
frame_count,
frame_count > max_progress_total ? std::numeric_limits<int>::max() : static_cast<int>(frame_count),
};
}
[[nodiscard]] inline AppProgressDialogPlan plan_recording_export_progress_dialog(
const RecordingExportPlan& plan)
{
return plan_app_progress_dialog("Exporting MP4 movie", plan.progress_total);
}
[[nodiscard]] constexpr RecordingWorkerIterationPlan plan_recording_worker_iteration(
bool is_running_after_wake,
bool has_encoder,
bool has_canvas_document) noexcept
{
const bool encode = is_running_after_wake && has_encoder && has_canvas_document;
return {
.continue_running = is_running_after_wake,
.encode_frame = encode,
.clear_dirty_stroke = encode,
.update_frame_label = encode,
};
}
[[nodiscard]] inline pp::foundation::Status execute_recording_start_action(
RecordingStartAction action,
RecordingServices& services)
{
switch (action) {
case RecordingStartAction::start_thread:
services.start_thread();
return pp::foundation::Status::success();
case RecordingStartAction::no_op_already_running:
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown recording start action");
}
[[nodiscard]] inline pp::foundation::Status execute_recording_stop_action(
RecordingStopAction action,
RecordingServices& services)
{
switch (action) {
case RecordingStopAction::stop_thread:
services.stop_thread();
return pp::foundation::Status::success();
case RecordingStopAction::no_op_not_running:
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown recording stop action");
}
[[nodiscard]] inline pp::foundation::Status execute_recording_clear_plan(
const RecordingClearPlan& plan,
RecordingServices& services)
{
if (plan.stop_running_recording) {
services.stop_thread();
}
if (plan.delete_recorded_files) {
services.delete_recorded_files();
}
services.set_frame_count(plan.frame_count_after_clear);
services.update_frame_label();
return pp::foundation::Status::success();
}
[[nodiscard]] inline pp::foundation::Status execute_recording_export_plan(
const RecordingExportPlan& plan,
RecordingServices& services,
std::string_view path)
{
services.begin_export(plan.progress_total);
services.write_mp4(path);
services.end_export();
return pp::foundation::Status::success();
}
}