MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
IOManager.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "CameraReader.hpp"
4#include "ImageWriter.hpp"
5#include "SoundFileWriter.hpp"
6#include "VideoFileReader.hpp"
7#include "VideoFileWriter.hpp"
8
9#include <future>
10
11namespace MayaFlux::Core {
12class VKImage;
13struct GlobalStreamInfo;
14}
15
17class MeshNetwork;
18}
19
20namespace MayaFlux::Kakshya {
21class SignalSourceContainer;
22class VideoFileContainer;
23class SoundFileContainer;
24class CameraContainer;
25class AudioOutputContainer;
26}
27
28namespace MayaFlux::Buffers {
29class VideoContainerBuffer;
30class SoundContainerBuffer;
31class TextureBuffer;
32class TextBuffer;
33class MeshBuffer;
34class BufferManager;
35}
36
38class IOService;
39class AudioBackendService;
40}
41
42namespace MayaFlux::IO {
43
44using TextureResolver = std::function<std::shared_ptr<Core::VKImage>(const std::string& path)>;
45
46class ImageReader;
47class ModelReader;
48
56
57/**
58 * @struct VideoLoadResult
59 * @brief Result of load_video() when audio extraction is requested via
60 * VideoReadOptions::EXTRACT_AUDIO.
61 *
62 * @c audio is nullptr if the file has no audio track or EXTRACT_AUDIO was
63 * not set in the options passed to load_video().
64 */
66 std::shared_ptr<Kakshya::VideoFileContainer> video;
67 std::shared_ptr<Kakshya::SoundFileContainer> audio;
68};
69
70/**
71 * @class IOManager
72 * @brief Optional orchestration layer for IO reader lifetime and IOService dispatch.
73 *
74 * MayaFlux IO readers are fully self-sufficient — VideoFileReader, SoundFileReader,
75 * and ImageReader can all be used standalone without this class. IOManager exists
76 * to solve problems that only arise at scale:
77 *
78 * - Globally unique reader_ids across concurrent VideoFileReader instances, so
79 * IOService::request_decode routes to the correct decode thread.
80 * - A single IOService registration backed by a keyed dispatch map, rather than
81 * each reader overwriting the previous registration.
82 * - Shared ownership of VideoFileReader instances whose background decode thread
83 * must outlive the scope in which they were created.
84 * - High-level load/hook functions that collapse the full open→load→processor
85 * setup→buffer wiring sequence into single calls, mirroring what Depot does
86 * for audio and eliminating the boilerplate currently required for video.
87 *
88 * VideoFileReader detects the IOService registration generically via BackendRegistry
89 * and skips its own registration — no IOManager-specific knowledge in the reader.
90 *
91 * Levels of integration (all valid):
92 * 1. VideoFileReader standalone, single stream — self-registers, self-ids.
93 * 2. IOManager constructed directly — multi-stream safe, no Engine required.
94 * 3. Engine constructs IOManager in Init() — fully orchestrated, zero user wiring.
95 */
96class MAYAFLUX_API IOManager {
97public:
98 /**
99 * @brief Construct IOManager and register the IOService into BackendRegistry.
100 *
101 * Must be constructed before any VideoFileReader::load_into_container() call
102 * that should participate in managed dispatch.
103 */
104 IOManager(Core::GlobalStreamInfo& stream_info, uint32_t frame_rate, const std::shared_ptr<Buffers::BufferManager>& buffer_manager);
105
106 /**
107 * @brief Unregisters IOService, releases all owned readers, clears stored buffers.
108 */
109 ~IOManager();
110
111 IOManager(const IOManager&) = delete;
112 IOManager& operator=(const IOManager&) = delete;
113 IOManager(IOManager&&) = delete;
115
116 // ─────────────────────────────────────────────────────────────────────────
117 // Video — load
118 // ─────────────────────────────────────────────────────────────────────────
119
120 /**
121 * @brief Load a video file into a VideoFileContainer.
122 *
123 * Performs can_read check, opens the
124 * file with metadata extraction, creates the container, registers the reader
125 * with IOManager (assigning a globally unique reader_id), calls
126 * setup_io_service(), loads into container, and configures the default
127 * FrameAccessProcessor with auto_advance enabled.
128 *
129 * Audio is NOT extracted. Use load_video(path, options) for that.
130 *
131 * @param filepath Path to the video file.
132 * @return Loaded VideoFileContainer, or nullptr on failure.
133 */
134 [[nodiscard]] std::shared_ptr<Kakshya::VideoFileContainer> load_video(const std::string& filepath);
135
136 /**
137 * @brief Load a video file into a VideoFileContainer.
138 *
139 * Performs can_read check, opens with
140 * metadata and region extraction, registers the reader (assigning a globally
141 * unique reader_id), calls setup_io_service(), loads into container, and
142 * configures the default FrameAccessProcessor with auto_advance enabled.
143 *
144 * Passing VideoReadOptions::EXTRACT_AUDIO also extracts the embedded
145 * SoundFileContainer, configures its ContiguousAccessProcessor identically
146 * to load_audio_file() in Depot, and populates VideoLoadResult::audio.
147 *
148 * @param filepath Path to the video file.
149 * @param config LoadConfig struct containing options and target dimensions.
150 * @return VideoLoadResult containing video container and optional audio container.
151 */
152 [[nodiscard]] VideoLoadResult load_video(
153 const std::string& filepath, LoadConfig config);
154
155 // ─────────────────────────────────────────────────────────────────────────
156 // Video — hook
157 // ─────────────────────────────────────────────────────────────────────────
158
159 /**
160 * @brief Wire a VideoFileContainer to the graphics buffer system.
161 *
162 * Creates a VideoContainerBuffer via BufferManager with GRAPHICS_BACKEND token,
163 * stores it keyed by container pointer, and returns it.
164 *
165 * @param container Loaded VideoFileContainer to wire.
166 * @return Created VideoContainerBuffer, or nullptr on failure.
167 */
168 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
169 hook_video_container_to_buffer(
170 const std::shared_ptr<Kakshya::VideoFileContainer>& container);
171
172 // ─────────────────────────────────────────────────────────────────────────
173 // Video — retrieve
174 // ─────────────────────────────────────────────────────────────────────────
175
176 /**
177 * @brief Retrieve the VideoContainerBuffer created for a container.
178 * @param container Container previously passed to hook_video_container_to_buffer().
179 * @return Stored buffer, or nullptr if not found.
180 */
181 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
182 get_video_buffer(
183 const std::shared_ptr<Kakshya::VideoFileContainer>& container) const;
184
185 // ─────────────────────────────────────────────────────────────────────────
186 // Audio — load
187 // ─────────────────────────────────────────────────────────────────────────
188
189 /**
190 * @brief Load an audio file into a SoundFileContainer.
191 *
192 * Performs can_read check, opens the file, creates and populates the
193 * container, and configures the default ContiguousAccessProcessor
194 * with auto_advance enabled.
195 *
196 * @param filepath Path to the audio file.
197 * @param config LoadConfig struct containing audio read options.
198 * @return Loaded SoundFileContainer, or nullptr on failure.
199 */
200 [[nodiscard]] std::shared_ptr<Kakshya::SoundFileContainer> load_audio(const std::string& filepath, LoadConfig config = {});
201
202 /**
203 * @brief Load an audio file into a fully resident, size-bounded DynamicSoundStream.
204 *
205 * Applies the engine sample rate, ROW_MAJOR layout, and the engine buffer
206 * size as the CursorAccessProcessor block size. The result is ready for use
207 * with StreamSliceProcessor without any further configuration.
208 *
209 * @param filepath Path to the audio file.
210 * @param max_frames Upper bound on frame count. 0 defaults to 5 s at the
211 * engine sample rate inside SoundFileReader::load_bounded.
212 * @param truncate If true, silently truncate files exceeding max_frames.
213 * @return Configured DynamicSoundStream, or nullptr on failure.
214 */
215 [[nodiscard]] std::shared_ptr<Kakshya::DynamicSoundStream> load_audio_bounded(
216 const std::string& filepath,
217 uint64_t max_frames = 0,
218 bool truncate = false);
219
220 // ─────────────────────────────────────────────────────────────────────────
221 // Audio — write
222 // ─────────────────────────────────────────────────────────────────────────
223
224 /**
225 * @brief Construct an open SoundFileWriter the caller drives manually.
226 *
227 * Allocates, opens, and returns a writer ready to accept write() calls.
228 * IOManager does not track this writer; the caller is responsible for
229 * calling close() when done.
230 *
231 * @param filepath Output file path. Extension determines container format.
232 * @param channels Number of channels in all submitted data.
233 * @param sample_rate PCM sample rate in Hz.
234 * @param codec_id Encoder override; AV_CODEC_ID_NONE = container default.
235 * @return Open SoundFileWriter, or nullptr if open() failed.
236 */
237 [[nodiscard]] std::shared_ptr<SoundFileWriter>
238 create_writer(const std::string& filepath,
239 uint32_t channels,
240 uint32_t sample_rate = 48000,
241 AVCodecID codec_id = AV_CODEC_ID_NONE);
242
243 /**
244 * @brief Write a SoundStreamContainer to file in one shot.
245 *
246 * Opens a writer, posts the container's full data, closes, and stores
247 * the encode future in m_save_tasks. Non-blocking; returns immediately.
248 * Container metadata (sample_rate, channels) drives the encoder parameters.
249 *
250 * @param container Source container; any SoundStreamContainer child.
251 * @param filepath Output file path.
252 * @param codec_id Encoder override; AV_CODEC_ID_NONE = container default.
253 */
254 void write(const std::shared_ptr<Kakshya::SoundStreamContainer>& container,
255 const std::string& filepath,
256 AVCodecID codec_id = AV_CODEC_ID_NONE);
257
258 /**
259 * @brief Begin continuous capture of live audio output to a file.
260 *
261 * Registers an observer with AudioBackendService. Each output cycle the
262 * observer drives an AudioOutputContainer and posts its processed_data
263 * to a SoundFileWriter. Returns an opaque capture id for use with
264 * stop_capture().
265 *
266 * Returns 0 and logs an error if AudioBackendService is unavailable.
267 *
268 * @param filepath Output file path.
269 * @param codec_id Encoder override; AV_CODEC_ID_NONE = container default.
270 * @return Capture handle; pass to stop_capture() to finalise.
271 */
272 [[nodiscard]] uint32_t capture_output(const std::string& filepath,
273 AVCodecID codec_id = AV_CODEC_ID_NONE);
274
275 /**
276 * @brief Stop a running capture and finalise the file.
277 *
278 * Unregisters the AudioBackendService observer, calls writer->close(),
279 * and stores the encode future in m_save_tasks. Non-blocking.
280 *
281 * @param capture_id Handle returned by capture_output().
282 */
283 void stop_capture(uint32_t capture_id);
284
285 // ─────────────────────────────────────────────────────────────────────────
286 // Audio — hook
287 // ─────────────────────────────────────────────────────────────────────────
288
289 /**
290 * @brief Wire a SoundFileContainer to the audio buffer system.
291 *
292 * Creates per-channel SoundContainerBuffers with AUDIO_BACKEND token,
293 * stores them keyed by container pointer, and returns them.
294 *
295 * @param container Loaded SoundFileContainer to wire.
296 * @return Per-channel SoundContainerBuffers, or empty on failure.
297 */
298 [[nodiscard]] std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
299 hook_audio_container_to_buffers(
300 const std::shared_ptr<Kakshya::SoundFileContainer>& container);
301
302 // ─────────────────────────────────────────────────────────────────────────
303 // Audio — retrieve
304 // ─────────────────────────────────────────────────────────────────────────
305
306 /**
307 * @brief Retrieve the SoundContainerBuffers created for a container.
308 * @param container Container previously passed to hook_audio_container_to_buffers().
309 * @return Stored buffer vector, or empty if not found.
310 */
311 [[nodiscard]] std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
312 get_audio_buffers(
313 const std::shared_ptr<Kakshya::SoundFileContainer>& container) const;
314
315 /**
316 * @brief Retrieve the SoundFileContainer extracted from a video file.
317 * @param container VideoFileContainer whose audio was extracted during load_video().
318 * @return Extracted SoundFileContainer, or nullptr if not found.
319 */
320 [[nodiscard]] std::shared_ptr<Kakshya::SoundFileContainer>
321 get_extracted_audio(const std::shared_ptr<Kakshya::VideoFileContainer>& container) const;
322
323 // ─────────────────────────────────────────────────────────────────────────
324 // Camera — open
325 // ─────────────────────────────────────────────────────────────────────────
326
327 /**
328 * @brief Open a camera device and create a CameraContainer.
329 *
330 * Constructs a CameraReader, opens the device, creates the container,
331 * configures its FrameAccessProcessor (auto_advance disabled), assigns
332 * a globally unique reader_id, registers the reader for IOService
333 * dispatch, and wires the container's IOService callback via
334 * CameraContainer::setup_io().
335 *
336 * After this call, the normal processing pipeline drives frame pulls:
337 * CameraContainer::process_default() → IOService::request_frame(reader_id)
338 * → dispatch_frame_request() → CameraReader::pull_frame_all()
339 * → decode thread → pull_frame() → mutable_frame_ptr() write → READY.
340 *
341 * @param config Device and resolution configuration.
342 * @return Opened CameraContainer, or nullptr on failure.
343 */
344 [[nodiscard]] std::shared_ptr<Kakshya::CameraContainer>
345 open_camera(const CameraConfig& config);
346
347 // ─────────────────────────────────────────────────────────────────────────
348 // Camera — hook
349 // ─────────────────────────────────────────────────────────────────────────
350
351 /**
352 * @brief Wire a CameraContainer to the graphics buffer system.
353 *
354 * Creates a VideoContainerBuffer via BufferManager with GRAPHICS_BACKEND
355 * token, stores it keyed by container pointer, and returns it. Identical
356 * to hook_video_container_to_buffer() but accepts CameraContainer.
357 *
358 * @param container Opened CameraContainer from open_camera().
359 * @return Created VideoContainerBuffer, or nullptr on failure.
360 */
361 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
362 hook_camera_to_buffer(const std::shared_ptr<Kakshya::CameraContainer>& container);
363
364 // ─────────────────────────────────────────────────────────────────────────
365 // Camera — retrieve
366 // ─────────────────────────────────────────────────────────────────────────
367
368 /**
369 * @brief Retrieve the VideoContainerBuffer created for a camera container.
370 */
371 [[nodiscard]] std::shared_ptr<Buffers::VideoContainerBuffer>
372 get_camera_buffer(const std::shared_ptr<Kakshya::CameraContainer>& container) const;
373
374 // ─────────────────────────────────────────────────────────────────────────
375 // Video reader management
376 // ─────────────────────────────────────────────────────────────────────────
377
378 /**
379 * @brief Assign a globally unique reader_id and take ownership of a reader.
380 *
381 * Called internally by load_video(). Advanced users who open a
382 * VideoFileReader manually can call this before load_into_container() to
383 * participate in managed dispatch and lifetime tracking.
384 *
385 * Calls reader->set_reader_id(id) before returning.
386 *
387 * @param reader Fully-opened VideoFileReader.
388 * @return Globally unique reader_id assigned to this reader.
389 */
390 [[nodiscard]] uint64_t register_video_reader(std::shared_ptr<VideoFileReader> reader);
391
392 /**
393 * @brief Release ownership of the reader identified by reader_id.
394 *
395 * When the last shared_ptr to the reader is released its decode thread
396 * is joined. Safe to call from a container state-change callback.
397 * No-op with a warning if reader_id is unknown.
398 *
399 * @param reader_id Id returned by register_video_reader().
400 */
401 void release_video_reader(uint64_t reader_id);
402
403 // ─────────────────────────────────────────────────────────────────────────
404 // Video — write
405 // ─────────────────────────────────────────────────────────────────────────
406
407 /**
408 * @brief Construct an open VideoFileWriter the caller drives manually.
409 *
410 * Allocates and opens the writer. IOManager does not track this writer;
411 * the caller is responsible for calling close() or stop_recording() when
412 * done.
413 *
414 * @param filepath Output file path. Extension determines container.
415 * @param width Frame width in pixels.
416 * @param height Frame height in pixels.
417 * @param frame_rate Average frame rate in frames per second.
418 * @param src_pixel_format AVPixelFormat of submitted pixel data.
419 * @param codec_id Encoder override; AV_CODEC_ID_NONE = container default.
420 * @return Open VideoFileWriter, or nullptr if open() failed.
421 */
422 [[nodiscard]] std::shared_ptr<VideoFileWriter>
423 create_writer(const std::string& filepath,
424 uint32_t width,
425 uint32_t height,
426 double frame_rate,
427 AVPixelFormat src_pixel_format,
428 AVCodecID codec_id = AV_CODEC_ID_NONE);
429
430 /**
431 * @brief Begin continuous capture of a window's rendered frames to a file.
432 *
433 * Delegates to VideoFileWriter::record(). The encoder is opened lazily on
434 * the first delivered frame so pixel format and dimensions come from the
435 * live swapchain. Returns an opaque capture id for use with stop_capture().
436 *
437 * Returns 0 if the window is null or record() fails.
438 *
439 * @param window Window whose swapchain frames will be captured.
440 * @param filepath Output file path. Extension determines container format.
441 * @param frame_rate Nominal frame rate written into the container header.
442 * @param codec_id Encoder override; AV_CODEC_ID_NONE = container default.
443 * @return Capture handle; pass to stop_capture() to finalise.
444 */
445 [[nodiscard]] uint32_t capture_window(
446 const std::shared_ptr<Core::Window>& window,
447 const std::string& filepath,
448 double frame_rate,
449 AVCodecID codec_id = AV_CODEC_ID_NONE);
450
451 /**
452 * @brief Stop the active window capture and finalise the file.
453 *
454 * Convenience overload that resolves the capture id by window pointer.
455 * No-op with a warning if no capture is active for this window.
456 *
457 * @param window Window previously passed to capture_window().
458 */
459 void stop_capture(const std::shared_ptr<Core::Window>& window);
460
461 /**
462 * @brief Returns all active video capture IDs (capture_window()).
463 */
464 [[nodiscard]] std::vector<uint32_t> get_video_capture_ids() const;
465
466 /**
467 * @brief Returns all VideoFileWriters created via create_writer().
468 */
469 [[nodiscard]] std::vector<std::shared_ptr<VideoFileWriter>> get_video_writers() const;
470
471 // ─────────────────────────────────────────────────────────────────────────
472 // Image — load
473 // ─────────────────────────────────────────────────────────────────────────
474
475 /**
476 * @brief Load an image file into a TextureBuffer.
477 *
478 * Opens the file via ImageReader, decodes to RGBA8, and creates a
479 * TextureBuffer containing the pixel data. The reader is retained
480 * for the lifetime of IOManager.
481 *
482 * @param filepath Path to the image file (PNG, JPG, BMP, TGA, etc.).
483 * @return TextureBuffer with pixel data, or nullptr on failure.
484 */
485 [[nodiscard]] std::shared_ptr<Buffers::TextureBuffer>
486 load_image(const std::string& filepath);
487
488 // ─────────────────────────────────────────────────────────────────────────
489 // Mesh — load
490 // ─────────────────────────────────────────────────────────────────────────
491
492 /**
493 * @brief Load all meshes from a 3D model file into MeshBuffer instances.
494 *
495 * Opens the file via ModelReader, extracts all aiMesh entries as MeshData,
496 * constructs one MeshBuffer per mesh, calls setup_processors() on each,
497 * and returns them. setup_rendering() is left to the caller.
498 *
499 * If resolver is null, the default resolver is used: paths resolved
500 * relative to the model file's directory via ImageReader::load_texture.
501 * Unresolvable textures are logged and skipped.
502 *
503 * @param filepath Path to the model file (glTF, FBX, OBJ, PLY, etc.).
504 * @param resolver Optional texture resolver.
505 * @return One MeshBuffer per mesh in scene order, or empty on failure.
506 */
507 [[nodiscard]] std::vector<std::shared_ptr<Buffers::MeshBuffer>>
508 load_mesh(
509 const std::string& filepath,
510 TextureResolver resolver = nullptr);
511
512 /**
513 * @brief Load a 3D model file as a MeshNetwork.
514 *
515 * One MeshSlot per aiMesh. Per-slot diffuse textures resolved via resolver.
516 * Null resolver uses the default: ImageReader::load_texture relative to the
517 * model file's directory.
518 *
519 * @param filepath Path to the model file.
520 * @param resolver Optional texture resolver.
521 * @return Populated MeshNetwork, or nullptr on failure.
522 */
523 [[nodiscard]] std::shared_ptr<Nodes::Network::MeshNetwork>
524 load_mesh_network(
525 const std::string& filepath,
526 TextureResolver resolver = nullptr);
527
528 // ─────────────────────────────────────────────────────────────────────────
529 // Image — save
530 // ─────────────────────────────────────────────────────────────────────────
531
532 /**
533 * @brief Save image data to disk asynchronously.
534 *
535 * The source is downloaded from the GPU on the calling thread (must have
536 * command queue access) and the encode + disk flush is dispatched to a
537 * worker. Returns once the download completes and the encode task is
538 * queued; encode/flush failures are logged but do not block the caller.
539 *
540 * For synchronous semantics (e.g. shutdown flush, tests) use the
541 * IO::save_image / IO::save_texture_buffer free functions in
542 * ImageExport.hpp.
543 *
544 * The extension of @p filepath selects the writer via
545 * ImageWriterRegistry. Format-variant compatibility is the writer's
546 * responsibility (PNG wants uint8, EXR wants float, etc.).
547 *
548 * @return True if the download succeeded and the encode task was queued.
549 */
550 bool save_image(
551 const std::shared_ptr<Core::VKImage>& image,
552 const std::string& filepath,
553 const IO::ImageWriteOptions& options = {});
554
555 bool save_image(
556 const std::shared_ptr<Buffers::TextureBuffer>& buffer,
557 const std::string& filepath,
558 const IO::ImageWriteOptions& options = {});
559
560 bool save_image(
561 const std::shared_ptr<Buffers::TextBuffer>& buffer,
562 const std::string& filepath,
563 const IO::ImageWriteOptions& options = {});
564
565 /**
566 * @brief Save an already-downloaded ImageData to disk asynchronously.
567 *
568 * For callers that have an ImageData in hand (e.g. from
569 * IO::download_image) and want to avoid the GPU download step. Purely
570 * CPU-bound from here, so no thread restrictions on the caller.
571 */
572 bool save_image(
573 IO::ImageData data,
574 const std::string& filepath,
575 const IO::ImageWriteOptions& options = {});
576
577 /**
578 * @brief Wait for all in-flight save operations to complete.
579 *
580 * Called automatically by the destructor. Invoke explicitly to flush
581 * pending saves before a checkpoint.
582 */
583 void wait_for_pending_saves();
584
585 // ─────────────────────────────────────────────────────────────────────────
586 // Image — save
587 // ─────────────────────────────────────────────────────────────────────────
588
589 /**
590 * @brief Returns all active video reader IDs.
591 */
592 [[nodiscard]] std::vector<uint64_t> get_video_reader_ids() const;
593
594 /**
595 * @brief Returns the video reader assigned to the given ID, or nullptr.
596 */
597 [[nodiscard]] std::shared_ptr<VideoFileReader> get_video_reader(uint64_t id) const;
598
599 /**
600 * @brief Returns all active camera reader IDs.
601 */
602 [[nodiscard]] std::vector<uint64_t> get_camera_reader_ids() const;
603
604 /**
605 * @brief Returns the camera reader assigned to the given ID, or nullptr.
606 */
607 [[nodiscard]] std::shared_ptr<CameraReader> get_camera_reader(uint64_t id) const;
608
609 /**
610 * @brief Returns all retained audio readers.
611 */
612 [[nodiscard]] std::vector<std::shared_ptr<SoundFileReader>> get_audio_readers() const { return m_audio_readers; };
613
614 /**
615 * @brief Returns all retained image readers.
616 */
617 [[nodiscard]] std::vector<std::shared_ptr<ImageReader>> get_image_readers() const { return m_image_readers; };
618
619 /**
620 * @brief Returns all retained model readers.
621 */
622 [[nodiscard]] std::vector<std::shared_ptr<ModelReader>> get_model_readers() const { return m_model_readers; };
623
624 /**
625 * @brief Returns all active audio capture IDs.
626 */
627 [[nodiscard]] std::vector<uint32_t> get_audio_capture_ids() const;
628
629 /**
630 * @brief Returns all registered SoundFileWriters created via create_writer() or write().
631 */
632 [[nodiscard]] std::vector<std::shared_ptr<SoundFileWriter>> get_sound_writers() const { return m_writers; };
633
634private:
636 uint32_t m_frame_rate;
637
638 /**
639 * @brief IOService::request_decode target — shared-lock lookup + signal_decode().
640 * Non-blocking. Safe from any thread.
641 */
642 void dispatch_decode_request(uint64_t reader_id);
643
644 /**
645 * @brief IOService::request_frame target — shared-lock lookup + pull_frame_all().
646 * Non-blocking. Safe from any thread.
647 */
648 void dispatch_frame_request(uint64_t reader_id);
649
650 void configure_frame_processor(
651 const std::shared_ptr<Kakshya::VideoFileContainer>& container);
652
653 void configure_audio_processor(
654 const std::shared_ptr<Kakshya::SoundFileContainer>& container);
655
656 // ── Audio capture ──────────────────────────────────────────────────────
657
659 std::shared_ptr<Kakshya::AudioOutputContainer> container;
660 std::shared_ptr<SoundFileWriter> writer;
661 uint32_t observer_id {};
662 };
663
664 mutable std::mutex m_audio_captures_mutex;
665 std::unordered_map<uint32_t, AudioCaptureState> m_audio_captures;
666
667 // ── Audio capture ──────────────────────────────────────────────────────
668
670 std::shared_ptr<VideoFileWriter> writer;
671 uint32_t capture_id {};
672 };
673
674 mutable std::mutex m_video_captures_mutex;
675 std::unordered_map<uint32_t, VideoCaptureState> m_video_captures;
676 std::atomic<uint32_t> m_next_video_capture_id { 1 };
677
678 std::vector<std::shared_ptr<VideoFileWriter>> m_video_writers;
679
680 // ── readers ──────────────────────────────────────────────────────
681
682 std::atomic<uint64_t> m_next_reader_id { 1 };
683 mutable std::shared_mutex m_readers_mutex;
684 mutable std::shared_mutex m_camera_mutex;
685 std::unordered_map<uint64_t, std::shared_ptr<VideoFileReader>> m_video_readers;
686 std::unordered_map<uint64_t, std::shared_ptr<CameraReader>> m_camera_readers;
687
688 std::vector<std::shared_ptr<SoundFileReader>> m_audio_readers;
689
690 std::vector<std::shared_ptr<ImageReader>> m_image_readers;
691
692 std::vector<std::shared_ptr<ModelReader>> m_model_readers;
693
694 std::vector<std::shared_ptr<SoundFileWriter>> m_writers;
695
696 // ── Stored buffers ─────────────────────────────────────────────────────
697
698 mutable std::shared_mutex m_buffers_mutex;
700 std::vector<std::future<bool>> m_save_tasks;
701
702 std::unordered_map<
703 std::shared_ptr<Kakshya::VideoFileContainer>,
704 std::shared_ptr<Buffers::VideoContainerBuffer>>
706
707 std::unordered_map<
708 std::shared_ptr<Kakshya::CameraContainer>,
709 std::shared_ptr<Buffers::VideoContainerBuffer>>
711
712 std::unordered_map<
713 std::shared_ptr<Kakshya::VideoFileContainer>,
714 std::shared_ptr<Kakshya::SoundFileContainer>>
716
717 std::unordered_map<
718 std::shared_ptr<Kakshya::SoundFileContainer>,
719 std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>>
721
722 std::shared_ptr<Buffers::BufferManager> m_buffer_manager;
723
724 // ── IOService ──────────────────────────────────────────────────────────
725
726 std::shared_ptr<Registry::Service::IOService> m_io_service;
727 Registry::Service::AudioBackendService* m_audio_backend_service {};
728};
729
730} // namespace MayaFlux::IO
IO::ImageData image
Definition Decoder.cpp:57
uint32_t width
Definition Decoder.cpp:59
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::unordered_map< uint32_t, AudioCaptureState > m_audio_captures
std::mutex m_save_tasks_mutex
std::vector< std::shared_ptr< VideoFileWriter > > m_video_writers
std::vector< std::future< bool > > m_save_tasks
std::vector< std::shared_ptr< SoundFileReader > > get_audio_readers() const
Returns all retained audio readers.
Core::GlobalStreamInfo & m_stream_info
std::vector< std::shared_ptr< ImageReader > > get_image_readers() const
Returns all retained image readers.
std::vector< std::shared_ptr< SoundFileWriter > > get_sound_writers() const
Returns all registered SoundFileWriters created via create_writer() or write().
std::vector< std::shared_ptr< SoundFileReader > > m_audio_readers
IOManager & operator=(IOManager &&)=delete
std::vector< std::shared_ptr< SoundFileWriter > > m_writers
std::shared_mutex m_camera_mutex
std::mutex m_video_captures_mutex
std::vector< std::shared_ptr< ModelReader > > m_model_readers
IOManager(IOManager &&)=delete
std::shared_mutex m_buffers_mutex
std::shared_ptr< Buffers::BufferManager > m_buffer_manager
std::unordered_map< uint32_t, VideoCaptureState > m_video_captures
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::mutex m_audio_captures_mutex
std::unordered_map< uint64_t, std::shared_ptr< VideoFileReader > > m_video_readers
std::vector< std::shared_ptr< ModelReader > > get_model_readers() const
Returns all retained model 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:96
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.
std::function< std::shared_ptr< Core::VKImage >(const std::string &)> TextureResolver
Callable that maps a raw material texture path to a GPU image.
Definition Depot.hpp:25
bool save_image(const std::shared_ptr< Buffers::TextureBuffer > &buffer, const std::string &suggested_name)
Present a native save-file dialog filtered to image formats and save buffer to the chosen path via IO...
Definition Depot.cpp:207
Comprehensive configuration for digital audio stream processing.
Platform-specific FFmpeg input format string for camera devices.
std::shared_ptr< Kakshya::AudioOutputContainer > container
std::shared_ptr< SoundFileWriter > writer
std::shared_ptr< VideoFileWriter > writer
Configuration for image writing.
FileReadOptions file_options
Definition IOManager.hpp:50
AudioReadOptions audio_options
Definition IOManager.hpp:51
VideoReadOptions video_options
Definition IOManager.hpp:52
std::shared_ptr< Kakshya::SoundFileContainer > audio
Definition IOManager.hpp:67
std::shared_ptr< Kakshya::VideoFileContainer > video
Definition IOManager.hpp:66
Result of load_video() when audio extraction is requested via VideoReadOptions::EXTRACT_AUDIO.
Definition IOManager.hpp:65
Backend audio subsystem service interface.