MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VideoFileWriter.hpp
Go to the documentation of this file.
1#pragma once
2
4
5#include <future>
6
7extern "C" {
8#include <libavcodec/avcodec.h>
9#include <libavutil/pixfmt.h>
10}
11
12namespace MayaFlux::Core {
13class Window;
14}
15
16namespace MayaFlux::Kakshya {
17class TextureContainer;
18class VideoStreamContainer;
19}
20
21namespace MayaFlux::Buffers {
22class VKBuffer;
23class TextureBuffer;
24}
25
26namespace MayaFlux::IO {
27
28/**
29 * @class VideoFileWriter
30 * @brief Asynchronous video file encoder with a lock-free work queue.
31 *
32 * Accepts pixel data from multiple source types and encodes it to a file on a
33 * dedicated worker thread. The public API is fully non-blocking: every write
34 * call copies the pixel data, posts a work item to an SPSC queue, and returns
35 * immediately. The worker thread owns all FFmpeg state and is the only thread
36 * that touches it.
37 *
38 * Supported input paths:
39 * - record() / stop_recording(): swapchain readback via DisplayService frame
40 * observer; encoder opened lazily on first frame.
41 * - Raw packed uint8_t pixels (span): caller supplies dimensions matching open().
42 * - TextureContainer: reads pixel_bytes(layer); dimensions from container.
43 * - VideoStreamContainer / CameraContainer: reads get_frame_pixels(); always RGBA.
44 * - TextureBuffer: CPU pixels via get_pixel_data() when present, otherwise
45 * GPU download via download_data_async() on the worker thread.
46 *
47 * Source dimensions may differ from the encoder output dimensions; SwsContext
48 * rescales automatically. The format passed to open() must match the source
49 * pixel layout for all write() calls on that instance.
50 *
51 * close() returns future<bool>; caller must .get() before process exit or the
52 * container trailer will not be written.
53 */
54class MAYAFLUX_API VideoFileWriter {
55public:
58
63
64 // =========================================================================
65 // Lifecycle
66 // =========================================================================
67
68 bool open(const std::string& filepath,
69 uint32_t width,
70 uint32_t height,
71 double frame_rate,
72 AVPixelFormat src_pixel_format,
73 AVCodecID explicit_codec = AV_CODEC_ID_NONE);
74
75 std::future<bool> close();
76
77 [[nodiscard]] bool is_open() const { return m_open.load(std::memory_order_acquire); }
78
79 // =========================================================================
80 // Screen capture
81 // =========================================================================
82
83 bool record(const std::shared_ptr<Core::Window>& window,
84 const std::string& filepath,
85 double frame_rate,
86 AVCodecID codec_id = AV_CODEC_ID_NONE);
87
88 std::future<bool> stop_recording();
89
90 [[nodiscard]] bool is_recording() const
91 {
92 return m_observer_id.load(std::memory_order_acquire) != 0;
93 }
94
95 [[nodiscard]] std::shared_ptr<Core::Window> capture_window() const { return m_capture_window; }
96
97 // =========================================================================
98 // Write
99 // =========================================================================
100
101 void write(const uint8_t* pixels, size_t size);
102 void write(std::span<const uint8_t> pixels);
103
104 void write(const std::shared_ptr<Kakshya::TextureContainer>& container,
105 uint32_t layer = 0);
106
107 void write(const std::shared_ptr<Kakshya::VideoStreamContainer>& container,
108 uint64_t frame_index = 0);
109
110 void write(const std::shared_ptr<Buffers::TextureBuffer>& buffer);
111
112 // =========================================================================
113 // Error
114 // =========================================================================
115
116 [[nodiscard]] std::string last_error() const;
117
118private:
119 struct RawFrame {
120 std::vector<uint8_t> pixels;
121 uint32_t width {};
122 uint32_t height {};
123 };
124
125 struct DownloadCmd {
126 std::shared_ptr<Buffers::TextureBuffer> buffer;
127 };
128
129 struct CloseCmd { };
130
131 using WorkItem = std::variant<RawFrame, DownloadCmd, CloseCmd>;
132
133 static constexpr size_t k_queue_capacity = 512;
134 std::unique_ptr<Memory::LockFreeQueue<WorkItem, k_queue_capacity>> m_queue;
135
136 std::thread m_worker;
137 std::atomic<bool> m_open { false };
138 std::atomic<bool> m_closing { false };
139 std::promise<bool> m_close_promise;
140
141 mutable std::mutex m_error_mutex;
142 std::string m_last_error;
143
144 uint32_t m_width {};
145 uint32_t m_height {};
146 AVPixelFormat m_src_fmt { AV_PIX_FMT_BGRA };
147
148 std::shared_ptr<Core::Window> m_capture_window;
149 std::shared_ptr<Buffers::VKBuffer> m_staging_buffer;
150 std::atomic<uint32_t> m_observer_id { 0 };
151 std::atomic<bool> m_capture_opened { false };
153 double m_capture_frame_rate {};
154 AVCodecID m_capture_codec_id { AV_CODEC_ID_NONE };
155 bool m_capture_did_enable { false };
156
157 void worker_loop(const std::string& filepath,
158 uint32_t width,
159 uint32_t height,
160 double frame_rate,
161 AVPixelFormat src_fmt,
162 AVCodecID codec_id);
163
164 void set_error(std::string msg);
165 bool post(const WorkItem& item);
166};
167
168} // namespace MayaFlux::IO
uint32_t width
Definition Decoder.cpp:66
const std::vector< float > * pixels
Definition Decoder.cpp:65
uint32_t height
std::shared_ptr< Buffers::VKBuffer > m_staging_buffer
VideoFileWriter(const VideoFileWriter &)=delete
std::shared_ptr< Core::Window > m_capture_window
std::variant< RawFrame, DownloadCmd, CloseCmd > WorkItem
VideoFileWriter & operator=(VideoFileWriter &&)=delete
VideoFileWriter & operator=(const VideoFileWriter &)=delete
std::shared_ptr< Core::Window > capture_window() const
std::unique_ptr< Memory::LockFreeQueue< WorkItem, k_queue_capacity > > m_queue
VideoFileWriter(VideoFileWriter &&)=delete
std::promise< bool > m_close_promise
Asynchronous video file encoder with a lock-free work queue.
std::shared_ptr< Buffers::TextureBuffer > buffer