MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
IOManager.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace MayaFlux::Kakshya {
7class SignalSourceContainer;
8class VideoFileContainer;
9class SoundFileContainer;
10class CameraContainer;
11}
12
13namespace MayaFlux::Buffers {
14class VideoContainerBuffer;
15class SoundContainerBuffer;
16class TextureBuffer;
17class BufferManager;
18}
19
21class IOService;
22}
23
24namespace MayaFlux::IO {
25
26class ImageReader;
27
35
36/**
37 * @struct VideoLoadResult
38 * @brief Result of load_video() when audio extraction is requested via
39 * VideoReadOptions::EXTRACT_AUDIO.
40 *
41 * @c audio is nullptr if the file has no audio track or EXTRACT_AUDIO was
42 * not set in the options passed to load_video().
43 */
45 std::shared_ptr<Kakshya::VideoFileContainer> video;
46 std::shared_ptr<Kakshya::SoundFileContainer> audio;
47};
48
49/**
50 * @class IOManager
51 * @brief Optional orchestration layer for IO reader lifetime and IOService dispatch.
52 *
53 * MayaFlux IO readers are fully self-sufficient — VideoFileReader, SoundFileReader,
54 * and ImageReader can all be used standalone without this class. IOManager exists
55 * to solve problems that only arise at scale:
56 *
57 * - Globally unique reader_ids across concurrent VideoFileReader instances, so
58 * IOService::request_decode routes to the correct decode thread.
59 * - A single IOService registration backed by a keyed dispatch map, rather than
60 * each reader overwriting the previous registration.
61 * - Shared ownership of VideoFileReader instances whose background decode thread
62 * must outlive the scope in which they were created.
63 * - High-level load/hook functions that collapse the full open→load→processor
64 * setup→buffer wiring sequence into single calls, mirroring what Depot does
65 * for audio and eliminating the boilerplate currently required for video.
66 *
67 * VideoFileReader detects the IOService registration generically via BackendRegistry
68 * and skips its own registration — no IOManager-specific knowledge in the reader.
69 *
70 * Levels of integration (all valid):
71 * 1. VideoFileReader standalone, single stream — self-registers, self-ids.
72 * 2. IOManager constructed directly — multi-stream safe, no Engine required.
73 * 3. Engine constructs IOManager in Init() — fully orchestrated, zero user wiring.
74 */
75class MAYAFLUX_API IOManager {
76public:
77 /**
78 * @brief Construct IOManager and register the IOService into BackendRegistry.
79 *
80 * Must be constructed before any VideoFileReader::load_into_container() call
81 * that should participate in managed dispatch.
82 */
83 IOManager(uint64_t sample_rate, uint32_t buffer_size, uint32_t frame_rate, const std::shared_ptr<Buffers::BufferManager>& buffer_manager);
84
85 /**
86 * @brief Unregisters IOService, releases all owned readers, clears stored buffers.
87 */
88 ~IOManager();
89
90 IOManager(const IOManager&) = delete;
91 IOManager& operator=(const IOManager&) = delete;
92 IOManager(IOManager&&) = delete;
94
95 // ─────────────────────────────────────────────────────────────────────────
96 // Video — load
97 // ─────────────────────────────────────────────────────────────────────────
98
99 /**
100 * @brief Load a video file into a VideoFileContainer.
101 *
102 * Performs can_read check, opens the
103 * file with metadata extraction, creates the container, registers the reader
104 * with IOManager (assigning a globally unique reader_id), calls
105 * setup_io_service(), loads into container, and configures the default
106 * FrameAccessProcessor with auto_advance enabled.
107 *
108 * Audio is NOT extracted. Use load_video(path, options) for that.
109 *
110 * @param filepath Path to the video file.
111 * @return Loaded VideoFileContainer, or nullptr on failure.
112 */
113 [[nodiscard]] std::shared_ptr<Kakshya::VideoFileContainer> load_video(const std::string& filepath);
114
115 /**
116 * @brief Load a video file into a VideoFileContainer.
117 *
118 * Performs can_read check, opens with
119 * metadata and region extraction, registers the reader (assigning a globally
120 * unique reader_id), calls setup_io_service(), loads into container, and
121 * configures the default FrameAccessProcessor with auto_advance enabled.
122 *
123 * Passing VideoReadOptions::EXTRACT_AUDIO also extracts the embedded
124 * SoundFileContainer, configures its ContiguousAccessProcessor identically
125 * to load_audio_file() in Depot, and populates VideoLoadResult::audio.
126 *
127 * @param filepath Path to the video file.
128 * @param config LoadConfig struct containing options and target dimensions.
129 * @return VideoLoadResult containing video container and optional audio container.
130 */
131 [[nodiscard]] VideoLoadResult load_video(
132 const std::string& filepath, LoadConfig config);
133
134 // ─────────────────────────────────────────────────────────────────────────
135 // Video — hook
136 // ─────────────────────────────────────────────────────────────────────────
137
138 /**
139 * @brief Wire a VideoFileContainer to the graphics buffer system.
140 *
141 * Creates a VideoContainerBuffer via BufferManager with GRAPHICS_BACKEND token,
142 * stores it keyed by container pointer, and returns it.
143 *
144 * @param container Loaded VideoFileContainer to wire.
145 * @return Created VideoContainerBuffer, or nullptr on failure.
146 */
147 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
148 hook_video_container_to_buffer(
149 const std::shared_ptr<Kakshya::VideoFileContainer>& container);
150
151 // ─────────────────────────────────────────────────────────────────────────
152 // Video — retrieve
153 // ─────────────────────────────────────────────────────────────────────────
154
155 /**
156 * @brief Retrieve the VideoContainerBuffer created for a container.
157 * @param container Container previously passed to hook_video_container_to_buffer().
158 * @return Stored buffer, or nullptr if not found.
159 */
160 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
161 get_video_buffer(
162 const std::shared_ptr<Kakshya::VideoFileContainer>& container) const;
163
164 // ─────────────────────────────────────────────────────────────────────────
165 // Audio — load
166 // ─────────────────────────────────────────────────────────────────────────
167
168 /**
169 * @brief Load an audio file into a SoundFileContainer.
170 *
171 * Performs can_read check, opens the file, creates and populates the
172 * container, and configures the default ContiguousAccessProcessor
173 * with auto_advance enabled.
174 *
175 * @param filepath Path to the audio file.
176 * @param config LoadConfig struct containing audio read options.
177 * @return Loaded SoundFileContainer, or nullptr on failure.
178 */
179 [[nodiscard]] std::shared_ptr<Kakshya::SoundFileContainer> load_audio(const std::string& filepath, LoadConfig config = {});
180
181 // ─────────────────────────────────────────────────────────────────────────
182 // Audio — hook
183 // ─────────────────────────────────────────────────────────────────────────
184
185 /**
186 * @brief Wire a SoundFileContainer to the audio buffer system.
187 *
188 * Creates per-channel SoundContainerBuffers with AUDIO_BACKEND token,
189 * stores them keyed by container pointer, and returns them.
190 *
191 * @param container Loaded SoundFileContainer to wire.
192 * @return Per-channel SoundContainerBuffers, or empty on failure.
193 */
194 [[nodiscard]] std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
195 hook_audio_container_to_buffers(
196 const std::shared_ptr<Kakshya::SoundFileContainer>& container);
197
198 // ─────────────────────────────────────────────────────────────────────────
199 // Audio — retrieve
200 // ─────────────────────────────────────────────────────────────────────────
201
202 /**
203 * @brief Retrieve the SoundContainerBuffers created for a container.
204 * @param container Container previously passed to hook_audio_container_to_buffers().
205 * @return Stored buffer vector, or empty if not found.
206 */
207 [[nodiscard]] std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
208 get_audio_buffers(
209 const std::shared_ptr<Kakshya::SoundFileContainer>& container) const;
210
211 /**
212 * @brief Retrieve the SoundFileContainer extracted from a video file.
213 * @param container VideoFileContainer whose audio was extracted during load_video().
214 * @return Extracted SoundFileContainer, or nullptr if not found.
215 */
216 [[nodiscard]] std::shared_ptr<Kakshya::SoundFileContainer>
217 get_extracted_audio(const std::shared_ptr<Kakshya::VideoFileContainer>& container) const;
218
219 // ─────────────────────────────────────────────────────────────────────────
220 // Camera — open
221 // ─────────────────────────────────────────────────────────────────────────
222
223 /**
224 * @brief Open a camera device and create a CameraContainer.
225 *
226 * Constructs a CameraReader, opens the device, creates the container,
227 * configures its FrameAccessProcessor (auto_advance disabled), assigns
228 * a globally unique reader_id, registers the reader for IOService
229 * dispatch, and wires the container's IOService callback via
230 * CameraContainer::setup_io().
231 *
232 * After this call, the normal processing pipeline drives frame pulls:
233 * CameraContainer::process_default() → IOService::request_frame(reader_id)
234 * → dispatch_frame_request() → CameraReader::pull_frame_all()
235 * → decode thread → pull_frame() → mutable_frame_ptr() write → READY.
236 *
237 * @param config Device and resolution configuration.
238 * @return Opened CameraContainer, or nullptr on failure.
239 */
240 [[nodiscard]] std::shared_ptr<Kakshya::CameraContainer>
241 open_camera(const CameraConfig& config);
242
243 // ─────────────────────────────────────────────────────────────────────────
244 // Camera — hook
245 // ─────────────────────────────────────────────────────────────────────────
246
247 /**
248 * @brief Wire a CameraContainer to the graphics buffer system.
249 *
250 * Creates a VideoContainerBuffer via BufferManager with GRAPHICS_BACKEND
251 * token, stores it keyed by container pointer, and returns it. Identical
252 * to hook_video_container_to_buffer() but accepts CameraContainer.
253 *
254 * @param container Opened CameraContainer from open_camera().
255 * @return Created VideoContainerBuffer, or nullptr on failure.
256 */
257 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
258 hook_camera_to_buffer(const std::shared_ptr<Kakshya::CameraContainer>& container);
259
260 // ─────────────────────────────────────────────────────────────────────────
261 // Camera — retrieve
262 // ─────────────────────────────────────────────────────────────────────────
263
264 /**
265 * @brief Retrieve the VideoContainerBuffer created for a camera container.
266 */
267 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
268 get_camera_buffer(const std::shared_ptr<Kakshya::CameraContainer>& container) const;
269
270 // ─────────────────────────────────────────────────────────────────────────
271 // Video reader management
272 // ─────────────────────────────────────────────────────────────────────────
273
274 /**
275 * @brief Assign a globally unique reader_id and take ownership of a reader.
276 *
277 * Called internally by load_video(). Advanced users who open a
278 * VideoFileReader manually can call this before load_into_container() to
279 * participate in managed dispatch and lifetime tracking.
280 *
281 * Calls reader->set_reader_id(id) before returning.
282 *
283 * @param reader Fully-opened VideoFileReader.
284 * @return Globally unique reader_id assigned to this reader.
285 */
286 [[nodiscard]] uint64_t register_video_reader(std::shared_ptr<VideoFileReader> reader);
287
288 /**
289 * @brief Release ownership of the reader identified by reader_id.
290 *
291 * When the last shared_ptr to the reader is released its decode thread
292 * is joined. Safe to call from a container state-change callback.
293 * No-op with a warning if reader_id is unknown.
294 *
295 * @param reader_id Id returned by register_video_reader().
296 */
297 void release_video_reader(uint64_t reader_id);
298
299 // ─────────────────────────────────────────────────────────────────────────
300 // Image — load
301 // ─────────────────────────────────────────────────────────────────────────
302
303 /**
304 * @brief Load an image file into a TextureBuffer.
305 *
306 * Opens the file via ImageReader, decodes to RGBA8, and creates a
307 * TextureBuffer containing the pixel data. The reader is retained
308 * for the lifetime of IOManager.
309 *
310 * @param filepath Path to the image file (PNG, JPG, BMP, TGA, etc.).
311 * @return TextureBuffer with pixel data, or nullptr on failure.
312 */
313 [[nodiscard]] std::shared_ptr<Buffers::TextureBuffer>
314 load_image(const std::string& filepath);
315
316private:
318
320
321 uint32_t m_frame_rate;
322
323 /**
324 * @brief IOService::request_decode target — shared-lock lookup + signal_decode().
325 * Non-blocking. Safe from any thread.
326 */
327 void dispatch_decode_request(uint64_t reader_id);
328
329 /**
330 * @brief IOService::request_frame target — shared-lock lookup + pull_frame_all().
331 * Non-blocking. Safe from any thread.
332 */
333 void dispatch_frame_request(uint64_t reader_id);
334
335 void configure_frame_processor(
336 const std::shared_ptr<Kakshya::VideoFileContainer>& container);
337
338 void configure_audio_processor(
339 const std::shared_ptr<Kakshya::SoundFileContainer>& container);
340
341 // ── readers ──────────────────────────────────────────────────────
342
343 std::atomic<uint64_t> m_next_reader_id { 1 };
344 mutable std::shared_mutex m_readers_mutex;
345 mutable std::shared_mutex m_camera_mutex;
346 std::unordered_map<uint64_t, std::shared_ptr<VideoFileReader>> m_video_readers;
347 std::unordered_map<uint64_t, std::shared_ptr<CameraReader>> m_camera_readers;
348
349 std::vector<std::shared_ptr<SoundFileReader>> m_audio_readers;
350
351 std::vector<std::shared_ptr<ImageReader>> m_image_readers;
352
353 // ── Stored buffers ─────────────────────────────────────────────────────
354
355 mutable std::shared_mutex m_buffers_mutex;
356
357 std::unordered_map<
358 std::shared_ptr<Kakshya::VideoFileContainer>,
359 std::shared_ptr<Buffers::VideoContainerBuffer>>
361
362 std::unordered_map<
363 std::shared_ptr<Kakshya::CameraContainer>,
364 std::shared_ptr<Buffers::VideoContainerBuffer>>
366
367 std::unordered_map<
368 std::shared_ptr<Kakshya::VideoFileContainer>,
369 std::shared_ptr<Kakshya::SoundFileContainer>>
371
372 std::unordered_map<
373 std::shared_ptr<Kakshya::SoundFileContainer>,
374 std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>>
376
377 std::shared_ptr<Buffers::BufferManager> m_buffer_manager;
378
379 // ── IOService ──────────────────────────────────────────────────────────
380
381 std::shared_ptr<Registry::Service::IOService> m_io_service;
382};
383
384} // namespace MayaFlux::IO
std::unordered_map< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Kakshya::SoundFileContainer > > m_extracted_audio
std::unordered_map< std::shared_ptr< Kakshya::CameraContainer >, std::shared_ptr< Buffers::VideoContainerBuffer > > m_camera_buffers
std::unordered_map< uint64_t, std::shared_ptr< CameraReader > > m_camera_readers
std::unordered_map< std::shared_ptr< Kakshya::SoundFileContainer >, std::vector< std::shared_ptr< Buffers::SoundContainerBuffer > > > m_audio_buffers
IOManager(const IOManager &)=delete
std::vector< std::shared_ptr< SoundFileReader > > m_audio_readers
IOManager & operator=(IOManager &&)=delete
std::shared_mutex m_camera_mutex
IOManager(IOManager &&)=delete
std::shared_mutex m_buffers_mutex
std::shared_ptr< Buffers::BufferManager > m_buffer_manager
std::vector< std::shared_ptr< ImageReader > > m_image_readers
std::shared_ptr< Registry::Service::IOService > m_io_service
std::shared_mutex m_readers_mutex
std::unordered_map< uint64_t, std::shared_ptr< VideoFileReader > > m_video_readers
IOManager & operator=(const IOManager &)=delete
std::unordered_map< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Buffers::VideoContainerBuffer > > m_video_buffers
Optional orchestration layer for IO reader lifetime and IOService dispatch.
Definition IOManager.hpp:75
AudioReadOptions
Audio-specific reading options.
@ DEINTERLEAVE
Output planar (per-channel) doubles instead of interleaved.
FileReadOptions
Generic options for file reading behavior.
@ EXTRACT_METADATA
Extract file metadata.
@ EXTRACT_REGIONS
Extract semantic regions (format-specific)
VideoReadOptions
Video-specific reading options.
Platform-specific FFmpeg input format string for camera devices.
FileReadOptions file_options
Definition IOManager.hpp:29
AudioReadOptions audio_options
Definition IOManager.hpp:30
VideoReadOptions video_options
Definition IOManager.hpp:31
std::shared_ptr< Kakshya::SoundFileContainer > audio
Definition IOManager.hpp:46
std::shared_ptr< Kakshya::VideoFileContainer > video
Definition IOManager.hpp:45
Result of load_video() when audio extraction is requested via VideoReadOptions::EXTRACT_AUDIO.
Definition IOManager.hpp:44