MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
IOManager.cpp
Go to the documentation of this file.
1#include "IOManager.hpp"
2
4
7
9
11
18
26
30
34
38
41
42#include "Image/EXRWriter.hpp"
43#include "Image/ImageExport.hpp"
45
47#include "Model/ModelExport.hpp"
48#include "Model/ModelReader.hpp"
49
50#include "Volume/VDBWriter.hpp"
51
53
54extern "C" {
55#include <libavdevice/avdevice.h>
56}
57
58namespace MayaFlux::IO {
59
60IOManager::IOManager(Core::GlobalStreamInfo& stream_info, uint32_t frame_rate, const std::shared_ptr<Buffers::BufferManager>& buffer_manager, const std::shared_ptr<Vruta::TaskScheduler>& scheduler)
61 : m_stream_info(stream_info)
62 , m_frame_rate(frame_rate)
63 , m_buffer_manager(buffer_manager)
64 , m_scheduler(scheduler)
65 , m_save_tasks([](std::exception_ptr ep) {
66 try {
67 std::rethrow_exception(std::move(ep));
68 } catch (...) {
69 try {
71 std::source_location::current(), "save task failed");
72 }
73 // NOLINTNEXTLINE(bugprone-empty-catch)
74 catch (...) {
75 }
76 }
77 })
78{
79 m_io_service = std::make_shared<Registry::Service::IOService>();
80
81 m_io_service->request_decode = [this](uint64_t reader_id) {
82 dispatch_decode_request(reader_id);
83 };
84
87 [svc = m_io_service]() -> void* { return svc.get(); });
88
89 m_io_service->request_frame = [this](uint64_t reader_id) {
90 dispatch_frame_request(reader_id);
91 };
92
97
99}
100
105
107{
108 if (m_stop_done.exchange(true)) {
109 return;
110 }
111
112 {
113 std::vector<uint32_t> ids;
114 {
115 std::lock_guard lock(m_audio_captures_mutex);
116 ids.reserve(m_audio_captures.size());
117 for (const auto& [id, _] : m_audio_captures)
118 ids.push_back(id);
119 }
120 for (auto id : ids)
122 }
123
124 {
125 std::vector<uint32_t> ids;
126 {
127 std::lock_guard lock(m_video_captures_mutex);
128 ids.reserve(m_video_captures.size());
129 for (const auto& [id, _] : m_video_captures)
130 ids.push_back(id);
131 }
132 for (auto id : ids)
134 }
135
136 {
137 std::vector<uint32_t> ids;
138 {
139 std::lock_guard lock(m_volume_captures_mutex);
140 ids.reserve(m_volume_captures.size());
141 for (const auto& [id, _] : m_volume_captures)
142 ids.push_back(id);
143 }
144 for (auto id : ids)
146 }
147
148 {
149 std::vector<uint32_t> ids;
150 {
151 std::lock_guard lock(m_spatial_captures_mutex);
152 ids.reserve(m_spatial_captures.size());
153 for (const auto& [id, _] : m_spatial_captures)
154 ids.push_back(id);
155 }
156 for (auto id : ids)
158 }
159
160 for (auto& w : m_writers) {
161 if (w->is_open()) {
162 auto fut = w->close();
163 fut.wait();
164 }
165 }
166
168
170}
171
173{
174 if (m_shutdown_done.exchange(true)) {
175 return;
176 }
177
178 stop();
179
181
184
185 m_io_service.reset();
186
187 {
188 decltype(m_video_readers) video_readers;
189 {
190 std::unique_lock lock(m_readers_mutex);
191 video_readers = std::exchange(m_video_readers, {});
192 }
193
194 decltype(m_camera_readers) camera_readers;
195 decltype(m_camera_buffers) camera_buffers;
196 {
197 std::unique_lock lock(m_camera_mutex);
198 camera_readers = std::exchange(m_camera_readers, {});
199 camera_buffers = std::exchange(m_camera_buffers, {});
200 }
201
202 decltype(m_video_buffers) video_buffers;
203 decltype(m_audio_buffers) audio_buffers;
204 decltype(m_extracted_audio) extracted_audio;
205 {
206 std::unique_lock lock(m_buffers_mutex);
207 video_buffers = std::exchange(m_video_buffers, {});
208 audio_buffers = std::exchange(m_audio_buffers, {});
209 extracted_audio = std::exchange(m_extracted_audio, {});
210 }
211
212 decltype(m_writers) writers;
213 {
214 std::lock_guard lock(m_writers_mutex);
215 writers = std::exchange(m_writers, {});
216 }
217
218 decltype(m_video_writers) video_writers;
219 {
220 std::lock_guard lock(m_video_captures_mutex);
221 video_writers = std::exchange(m_video_writers, {});
222 }
223
224 decltype(m_spatial_caches) spatial_caches;
225 {
226 std::lock_guard lock(m_spatial_captures_mutex);
227 spatial_caches = std::exchange(m_spatial_caches, {});
229 }
230
231 auto loaded_volumes = std::exchange(m_loaded_volumes, {});
232 auto audio_readers = std::exchange(m_audio_readers, {});
233 auto image_readers = std::exchange(m_image_readers, {});
234 auto model_readers = std::exchange(m_model_readers, {});
235
236 video_readers.clear();
237 camera_readers.clear();
238 }
239
240 MF_INFO(Journal::Component::IO, Journal::Context::Init, "IOManager shutdown complete");
241}
242
243std::shared_ptr<Kakshya::VideoFileContainer>
244IOManager::load_video(const std::string& filepath)
245{
246 auto [video, audio] = load_video(filepath, {});
247 return std::move(video);
248}
249
251IOManager::load_video(const std::string& filepath, VideoLoadConfig config)
252{
253 auto reader = std::make_shared<IO::VideoFileReader>();
254
255 if (!reader->can_read(filepath)) {
257 "Cannot read video file: {}", filepath);
258 return {};
259 }
260
261 reader->set_video_options(config.video_options);
262 reader->set_audio_options(config.audio_options);
263 reader->set_target_dimensions(config.target_width, config.target_height);
264
266 reader->set_target_sample_rate(m_stream_info.sample_rate);
267 }
268
269 if (!reader->open(filepath, config.file_options)) {
271 "Failed to open video file: {}", reader->get_last_error());
272 return {};
273 }
274
275 const uint64_t reader_id = register_video_reader(reader);
276 reader->setup_io_service(reader_id);
277
278 auto video_container = std::dynamic_pointer_cast<Kakshya::VideoFileContainer>(reader->create_container());
279
280 if (!video_container) {
282 "Failed to create video container from: {}", filepath);
283 release_video_reader(reader_id);
284 return {};
285 }
286
287 if (!reader->load_into_container(video_container)) {
289 "Failed to load video data: {}", reader->get_last_error());
290 release_video_reader(reader_id);
291 return {};
292 }
293
294 configure_frame_processor(video_container);
295
296 std::shared_ptr<Kakshya::SoundFileContainer> audio_container;
297
299 audio_container = reader->get_audio_container();
300 if (audio_container) {
301 configure_audio_processor(audio_container);
302 m_extracted_audio[video_container] = audio_container;
303 } else {
305 "No audio track found in: {}", filepath);
306 }
307 }
308
310 "Loaded video: {}", filepath);
311
312 return { std::move(video_container), std::move(audio_container) };
313}
314
315uint64_t IOManager::register_video_reader(std::shared_ptr<IO::VideoFileReader> reader)
316{
317 if (!reader) {
319 "IOManager::register_video_reader called with null reader");
320 return 0;
321 }
322
323 const uint64_t id = m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
324 reader->set_reader_id(id);
325
326 {
327 std::unique_lock lock(m_readers_mutex);
328 m_video_readers.emplace(id, std::move(reader));
329 }
330
332 "IOManager: registered VideoFileReader id={}", id);
333
334 return id;
335}
336
337void IOManager::release_video_reader(uint64_t reader_id)
338{
339 std::unique_lock lock(m_readers_mutex);
340 auto it = m_video_readers.find(reader_id);
341
342 if (it == m_video_readers.end()) {
344 "IOManager::release_video_reader: unknown id={}", reader_id);
345 return;
346 }
347
348 m_video_readers.erase(it);
349
351 "IOManager: released VideoFileReader id={}", reader_id);
352}
353
355{
356 std::shared_lock lock(m_readers_mutex);
357 auto it = m_video_readers.find(reader_id);
358
359 if (it == m_video_readers.end()) {
361 "IOManager: dispatch_decode_request unknown reader_id={}", reader_id);
362 return;
363 }
364
365 it->second->signal_decode();
366}
367
368void IOManager::dispatch_frame_request(uint64_t reader_id)
369{
370 std::shared_lock lock(m_camera_mutex);
371 auto it = m_camera_readers.find(reader_id);
372
373 if (it == m_camera_readers.end()) {
375 "IOManager: dispatch_frame_request unknown reader_id={}", reader_id);
376 return;
377 }
378
379 it->second->pull_frame_all();
380}
381
382uint64_t IOManager::register_camera_source(std::shared_ptr<CameraSource> source)
383{
384 if (!source) {
386 "IOManager::register_camera_source called with null source");
387 return 0;
388 }
389
390 const uint64_t id = m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
391
392 {
393 std::unique_lock lock(m_camera_mutex);
394 m_camera_readers.emplace(id, std::move(source));
395 }
396
398 "IOManager: registered CameraSource id={}", id);
399
400 return id;
401}
402
403void IOManager::release_camera_source(uint64_t reader_id)
404{
405 std::unique_lock lock(m_camera_mutex);
406 auto it = m_camera_readers.find(reader_id);
407
408 if (it == m_camera_readers.end()) {
410 "IOManager::release_camera_source: unknown id={}", reader_id);
411 return;
412 }
413
414 m_camera_readers.erase(it);
415
417 "IOManager: released CameraSource id={}", reader_id);
418}
419
420std::shared_ptr<Kakshya::SoundFileContainer> IOManager::load_audio(const std::string& filepath, VideoLoadConfig config)
421{
422 auto reader = std::make_shared<IO::SoundFileReader>();
423
424 if (!reader->can_read(filepath)) {
425 MF_ERROR(Journal::Component::API, Journal::Context::FileIO, "Cannot read file: {}", filepath);
426 return nullptr;
427 }
428
429 reader->set_target_sample_rate(m_stream_info.sample_rate);
430 reader->set_audio_options(config.audio_options);
431
432 if (!reader->open(filepath, config.file_options)) {
433 MF_ERROR(Journal::Component::API, Journal::Context::FileIO, "Failed to open file: {}", reader->get_last_error());
434 return nullptr;
435 }
436
437 auto container = reader->create_container();
438 auto sound_container = std::dynamic_pointer_cast<Kakshya::SoundFileContainer>(container);
439 if (!sound_container) {
440 MF_ERROR(Journal::Component::API, Journal::Context::Runtime, "Failed to create sound container");
441 return nullptr;
442 }
443
444 if (!reader->load_into_container(sound_container)) {
445 MF_ERROR(Journal::Component::API, Journal::Context::Runtime, "Failed to load audio data: {}", reader->get_last_error());
446 return nullptr;
447 }
448
449 configure_audio_processor(sound_container);
450
451 m_audio_readers.push_back(std::move(reader));
452
453 return sound_container;
454}
455
456std::shared_ptr<Kakshya::DynamicSoundStream> IOManager::load_audio_bounded(
457 const std::string& filepath,
458 uint64_t max_frames,
459 bool truncate)
460{
461 auto reader = std::make_shared<IO::SoundFileReader>();
462
463 if (!reader->can_read(filepath)) {
465 "IOManager::load_bounded: unsupported format '{}'", filepath);
466 return nullptr;
467 }
468
469 reader->set_target_sample_rate(m_stream_info.sample_rate);
470
471 auto stream = reader->load_bounded(filepath, max_frames, truncate);
472 if (!stream) {
474 "IOManager::load_bounded: failed for '{}'", filepath);
475 return nullptr;
476 }
477
478 stream->set_memory_layout(Kakshya::MemoryLayout::ROW_MAJOR);
479
480 m_audio_readers.push_back(std::move(reader));
481
482 return stream;
483}
484
485// ─────────────────────────────────────────────────────────────────────────
486
487std::shared_ptr<SoundFileWriter>
488IOManager::create_writer(const std::string& filepath,
489 uint32_t channels,
490 uint32_t sample_rate,
491 AVCodecID codec_id)
492{
493 auto writer = std::make_shared<SoundFileWriter>();
494 if (!writer->open(filepath, channels, sample_rate, codec_id)) {
496 "create_writer: open failed for '{}': {}", filepath, writer->last_error());
497 return nullptr;
498 }
499
500 {
501 std::lock_guard lock(m_writers_mutex);
502 m_writers.push_back(writer);
503 }
504 return writer;
505}
506
507void IOManager::write(const std::shared_ptr<Kakshya::SoundStreamContainer>& container,
508 const std::string& filepath,
509 AVCodecID codec_id)
510{
511 if (!container) {
513 "write: null container");
514 return;
515 }
516
517 auto writer = std::make_shared<SoundFileWriter>();
518 if (!writer->open(filepath,
519 container->get_num_channels(),
520 container->get_sample_rate(),
521 codec_id)) {
523 "write: open failed for '{}': {}", filepath, writer->last_error());
524 return;
525 }
526
527 writer->write(container->get_data());
528 track_save_task(writer->close());
529}
530
531uint32_t IOManager::capture_output(const std::string& filepath, AVCodecID codec_id)
532{
533
537 }
538
540 error<std::runtime_error>(Journal::Component::IO, Journal::Context::FileIO, std::source_location::current(),
541 "capture_output: AudioBackendService unavailable");
542 }
543
544 auto ct = std::make_shared<Kakshya::AudioOutputContainer>(m_stream_info);
545 ct->create_default_processor();
546
547 auto writer = std::make_shared<SoundFileWriter>();
548 if (!writer->open(filepath,
551 codec_id)) {
553 "capture_output: writer open failed for '{}': {}", filepath, writer->last_error());
554 return 0;
555 }
556
558 [ct, writer](const double*, uint32_t) {
559 ct->process_default();
560 const auto& vt = ct->get_processed_data();
561 if (!vt.empty())
562 writer->write(vt);
563 });
564
565 {
566 std::lock_guard lock(m_writers_mutex);
567 m_writers.push_back(writer);
568 }
569
570 std::lock_guard lock(m_audio_captures_mutex);
571 m_audio_captures.emplace(obs_id, AudioCaptureState { .container = ct, .writer = writer, .observer_id = obs_id });
572 return obs_id;
573}
574
575void IOManager::stop_audio_capture(uint32_t capture_id)
576{
577 AudioCaptureState state;
578 {
579 std::lock_guard lock(m_audio_captures_mutex);
580 auto it = m_audio_captures.find(capture_id);
581 if (it == m_audio_captures.end()) {
583 "stop_audio_capture: unknown capture_id={}", capture_id);
584 return;
585 }
586 state = std::move(it->second);
587 m_audio_captures.erase(it);
588 }
589
592 if (svc)
593 svc->unregister_output_observer(state.observer_id);
594
595 track_save_task(state.writer->close());
596}
597
598std::vector<uint32_t> IOManager::get_audio_capture_ids() const
599{
600 std::lock_guard lock(m_audio_captures_mutex);
601 std::vector<uint32_t> ids;
602 ids.reserve(m_audio_captures.size());
603 for (const auto& [id, _] : m_audio_captures)
604 ids.push_back(id);
605 return ids;
606}
607
608std::shared_ptr<VideoFileWriter>
609IOManager::create_writer(const std::string& filepath,
610 uint32_t width,
611 uint32_t height,
612 double frame_rate,
613 AVPixelFormat src_pixel_format,
614 AVCodecID codec_id)
615{
616 auto writer = std::make_shared<VideoFileWriter>();
617 if (!writer->open(filepath, width, height, frame_rate, src_pixel_format, codec_id)) {
619 "create_writer: open failed for '{}': {}", filepath, writer->last_error());
620 return nullptr;
621 }
622 return writer;
623}
624
626 const std::shared_ptr<Core::Window>& window,
627 const std::string& filepath,
628 double frame_rate,
629 AVCodecID codec_id)
630{
631 if (!window) {
633 "capture_window: null window");
634 return 0;
635 }
636
637 auto writer = std::make_shared<VideoFileWriter>();
638 if (!writer->record(window, filepath, frame_rate, codec_id)) {
640 "capture_window: record failed for '{}': {}", filepath, writer->last_error());
641 return 0;
642 }
643
644 const uint32_t id = m_next_video_capture_id.fetch_add(1, std::memory_order_relaxed);
645
646 {
647 std::lock_guard lock(m_video_captures_mutex);
648 m_video_captures.emplace(id, VideoCaptureState { .writer = writer, .capture_id = id });
649 m_video_writers.push_back(writer);
650 }
651
652 return id;
653}
654
655void IOManager::stop_video_capture(uint32_t capture_id)
656{
657 VideoCaptureState state;
658 {
659 std::lock_guard lock(m_video_captures_mutex);
660 auto it = m_video_captures.find(capture_id);
661 if (it == m_video_captures.end()) {
663 "stop_video_capture: unknown capture_id={}", capture_id);
664 return;
665 }
666 state = std::move(it->second);
667 m_video_captures.erase(it);
668 }
669
670 track_save_task(state.writer->stop_recording());
671}
672
673void IOManager::stop_capture(const std::shared_ptr<Core::Window>& window)
674{
675 if (!window) {
677 "stop_capture(window): null window");
678 return;
679 }
680
681 uint32_t found_id = 0;
682 {
683 std::lock_guard lock(m_video_captures_mutex);
684 for (const auto& [id, state] : m_video_captures) {
685 if (state.writer->capture_window() == window) {
686 found_id = id;
687 break;
688 }
689 }
690 }
691
692 if (found_id == 0) {
694 "stop_capture(window): no active capture for window '{}'",
695 window->get_create_info().title);
696 return;
697 }
698
699 stop_video_capture(found_id);
700}
701
702std::vector<uint32_t> IOManager::get_video_capture_ids() const
703{
704 std::lock_guard lock(m_video_captures_mutex);
705 std::vector<uint32_t> ids;
706 ids.reserve(m_video_captures.size());
707 for (const auto& [id, _] : m_video_captures)
708 ids.push_back(id);
709 return ids;
710}
711
712std::vector<std::shared_ptr<VideoFileWriter>> IOManager::get_video_writers() const
713{
714 std::lock_guard lock(m_video_captures_mutex);
715 return m_video_writers;
716}
717
718std::shared_ptr<Kakshya::CameraContainer>
720{
721 static std::once_flag s_avdevice_init;
722 std::call_once(s_avdevice_init, [] { avdevice_register_all(); });
723
724 auto reader = std::make_shared<FFmpegCameraReader>();
725
726 if (!reader->open(config)) {
728 "open_camera: failed — {}", reader->last_error());
729 return nullptr;
730 }
731
732 auto container = reader->create_container();
733 if (!container) {
735 "open_camera: failed to create container — {}",
736 reader->last_error());
737 return nullptr;
738 }
739
740 container->create_default_processor();
741 reader->set_container(container);
742
743 const uint64_t rid = register_camera_source(reader);
745
747 "open_camera: reader_id={} device='{}' {}x{} @{:.1f}fps",
748 rid, config.device_name,
749 reader->width(), reader->height(), reader->frame_rate());
750
751 return container;
752}
753
754std::shared_ptr<Buffers::TextureBuffer>
755IOManager::load_image(const std::string& filepath, int desired_channels,
756 std::optional<Kakshya::Region> region)
757{
758 auto reader = std::make_shared<IO::ImageReader>(desired_channels, std::move(region));
759
760 if (!reader->open(filepath)) {
762 "Failed to open image: {}", filepath);
763 return nullptr;
764 }
765
766 auto texture_buffer = reader->create_texture_buffer();
767
768 if (!texture_buffer) {
770 "Failed to create texture buffer from: {}", filepath);
771 return nullptr;
772 }
773
774 m_image_readers.push_back(std::move(reader));
775
777 "Loaded image: {} ({}x{})",
778 std::filesystem::path(filepath).filename().string(),
779 texture_buffer->get_width(),
780 texture_buffer->get_height());
781
782 return texture_buffer;
783}
784
785std::shared_ptr<CompositeReader>
787 const std::string& filepath,
788 std::optional<Kakshya::CompositeLayout> layout)
789{
790 auto delimited = std::make_shared<DelimitedTextReader>();
791 if (!delimited->can_read(filepath)) {
793 "IOManager::open_composite_reader: unsupported format '{}'", filepath);
794 return nullptr;
795 }
796
797 if (layout && !delimited->set_layout(std::move(*layout))) {
799 "IOManager::open_composite_reader: failed to set layout for '{}'", filepath);
800 return nullptr;
801 }
802
803 if (!delimited->open(filepath)) {
805 "IOManager::open_composite_reader: failed to open '{}' because {}",
806 filepath, delimited->get_last_error());
807 return nullptr;
808 }
809
810 return delimited;
811}
812
813std::shared_ptr<Kakshya::CompositeContainer>
815 const std::string& filepath,
816 std::optional<Kakshya::CompositeLayout> layout,
817 size_t batch_size)
818{
819 auto reader = open_composite_reader(filepath, std::move(layout));
820 if (!reader)
821 return nullptr;
822
823 auto container = reader->create_composite_container(batch_size);
824 if (!container) {
826 "IOManager::load_composite: failed to read '{}' because {}",
827 filepath, reader->get_last_error());
828 return nullptr;
829 }
830
832 "IOManager::load_composite: loaded '{}'",
833 std::filesystem::path(filepath).filename().string());
834
835 return container;
836}
837
838std::shared_ptr<CompositeWriter>
840 const std::string& filepath,
841 const Kakshya::CompositeLayout& layout)
842{
843 auto writer = std::make_shared<DelimitedTextWriter>();
844 if (!writer->can_write(filepath)) {
846 "IOManager::open_composite_writer: unsupported format '{}'", filepath);
847 return nullptr;
848 }
849
850 if (!writer->open(filepath, layout)) {
852 "IOManager::open_composite_writer: failed to open '{}' because {}",
853 filepath, writer->get_last_error());
854 return nullptr;
855 }
856
857 return writer;
858}
859
861 const std::shared_ptr<Kakshya::CompositeContainer>& container,
862 const std::string& filepath)
863{
864 if (!container) {
866 "IOManager::save_composite: null container");
867 return false;
868 }
869
870 const auto& array = container->get_data();
871 auto writer = open_composite_writer(filepath, array.layout());
872 if (!writer)
873 return false;
874
875 const auto rows = array.slice(0, array.size());
876 const bool written = rows && writer->write_rows(*rows);
877 const std::string write_error = !rows
878 ? "could not access the container's elements"
879 : writer->get_last_error();
880 const bool closed = writer->close();
881
882 if (!written || !closed) {
884 "IOManager::save_composite: failed to write '{}' because {}",
885 filepath, written ? writer->get_last_error() : write_error);
886 return false;
887 }
888
890 "IOManager::save_composite: wrote '{}'",
891 std::filesystem::path(filepath).filename().string());
892
893 return true;
894}
895
896std::vector<std::shared_ptr<Buffers::MeshBuffer>>
897IOManager::load_mesh(const std::string& filepath, TextureResolver resolver)
898{
899 auto reader = std::make_shared<ModelReader>();
900
901 if (!reader->can_read(filepath)) {
903 "IOManager::load_mesh: unsupported format '{}'", filepath);
904 return {};
905 }
906
907 if (!reader->open(filepath)) {
909 "IOManager::load_mesh: failed to open '{}' — {}",
910 filepath, reader->get_last_error());
911 return {};
912 }
913
914 if (!resolver)
915 resolver = Detail::make_default_resolver(filepath);
916
917 auto buffers = reader->create_mesh_buffers(resolver);
918 reader->close();
919
920 if (buffers.empty()) {
922 "IOManager::load_mesh: no meshes in '{}'", filepath);
923 return {};
924 }
925
927 "IOManager::load_mesh: {} mesh(es) from '{}'",
928 buffers.size(),
929 std::filesystem::path(filepath).filename().string());
930
931 return buffers;
932}
933
934std::shared_ptr<Nodes::Network::MeshNetwork>
935IOManager::load_mesh_network(const std::string& filepath, TextureResolver resolver)
936{
937 auto reader = std::make_shared<ModelReader>();
938
939 if (!reader->can_read(filepath)) {
941 "IOManager::load_mesh_network: unsupported format '{}'", filepath);
942 return nullptr;
943 }
944
945 if (!reader->open(filepath)) {
947 "IOManager::load_mesh_network: failed to open '{}' — {}",
948 filepath, reader->get_last_error());
949 return nullptr;
950 }
951
952 if (!resolver)
953 resolver = Detail::make_default_resolver(filepath);
954
955 auto net = reader->create_mesh_network(resolver);
956 reader->close();
957
958 if (!net) {
960 "IOManager::load_mesh_network: no network from '{}'", filepath);
961 return nullptr;
962 }
963
965 "IOManager::load_mesh_network: {} slots from '{}'",
966 net->slot_count(),
967 std::filesystem::path(filepath).filename().string());
968
969 return net;
970}
971
972void IOManager::track_save_task(std::future<bool> fut)
973{
974 m_save_tasks.submit(std::move(fut));
975}
976
978 const std::shared_ptr<Buffers::MeshBuffer>& mesh_buffer,
979 const std::string& filepath,
980 const IO::ModelWriteOptions& options)
981{
982 track_save_task(std::async(std::launch::async,
983 [mesh_buffer, filepath, options]() -> bool {
984 return IO::save_mesh(mesh_buffer, filepath, options);
985 }));
986
987 return true;
988}
989
991 const std::vector<Kakshya::MeshData>& meshes,
992 const std::string& filepath,
993 const IO::ModelWriteOptions& options)
994{
995 track_save_task(std::async(std::launch::async,
996 [meshes, filepath, options]() -> bool {
997 return Detail::execute_mesh_write(filepath, meshes, options);
998 }));
999
1000 return true;
1001}
1002
1004 const std::shared_ptr<Nodes::Network::MeshNetwork>& network,
1005 const std::string& filepath,
1006 const IO::ModelWriteOptions& options)
1007{
1008 track_save_task(std::async(std::launch::async,
1009 [network, filepath, options]() -> bool {
1010 return IO::save_mesh(network, filepath, options);
1011 }));
1012
1013 return true;
1014}
1015
1017 const std::shared_ptr<Buffers::ComputeMeshBuffer>& buffer,
1018 const std::string& filepath,
1019 const IO::ModelWriteOptions& options)
1020{
1021 if (!buffer) {
1023 "save_mesh: null buffer");
1024 return false;
1025 }
1026
1027 track_save_task(std::async(std::launch::async,
1028 [buffer, filepath, options]() -> bool {
1029 return IO::save_mesh(buffer, filepath, options);
1030 }));
1031
1032 return true;
1033}
1034
1036 const std::shared_ptr<Buffers::ComputeMeshBuffer>& buffer,
1037 const std::string& path_pattern,
1038 const IO::ModelWriteOptions& options)
1039{
1040 if (!buffer) {
1042 "save_mesh_snapshot: null buffer");
1043 return false;
1044 }
1045
1046 track_save_task(std::async(std::launch::async,
1047 [buffer, path_pattern, options]() -> bool {
1048 return Detail::execute_mesh_snapshot(buffer, path_pattern, options);
1049 }));
1050
1051 return true;
1052}
1053
1055 const std::shared_ptr<Kakshya::VideoFileContainer>& container) const
1056{
1058}
1059
1061 const std::shared_ptr<Kakshya::SoundFileContainer>& container) const
1062{
1064}
1065
1066std::shared_ptr<Buffers::VideoContainerBuffer>
1068 const std::shared_ptr<Kakshya::VideoStreamContainer>& container)
1069{
1070 if (!container) {
1072 "hook_video_container_to_buffer: null container");
1073 return nullptr;
1074 }
1075
1076 if (const auto dynamic = std::dynamic_pointer_cast<Kakshya::DynamicVideoStream>(container);
1077 dynamic && !dynamic->get_default_processor()) {
1078 dynamic->create_default_processor();
1079 }
1080
1081 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
1082
1083 if (!stream_container) {
1085 "hook_video_container_to_buffer: container is not a VideoStreamContainer");
1086 return nullptr;
1087 }
1088
1089 auto video_buffer = Detail::create_video_container_buffer(m_buffer_manager, stream_container);
1090
1091 {
1092 std::unique_lock lock(m_buffers_mutex);
1093 m_video_buffers[container] = video_buffer;
1094 }
1095
1097 "Hooked VideoFileContainer to VideoContainerBuffer ({}x{})",
1098 video_buffer->get_width(), video_buffer->get_height());
1099
1100 return video_buffer;
1101}
1102
1103std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
1105 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
1106{
1107 if (!container) {
1109 "hook_audio_container_to_buffers: null container");
1110 return {};
1111 }
1112
1113 MF_TRACE(
1116 "Setting up audio playback for {} channels...",
1117 container->get_num_channels());
1118
1119 auto created_buffers = Detail::create_audio_container_buffers(m_buffer_manager, container);
1120
1121 for (size_t channel = 0; channel < created_buffers.size(); ++channel) {
1122 MF_INFO(
1125 "✓ Created buffer for channel {}",
1126 channel);
1127 }
1128
1129 m_audio_buffers[container] = created_buffers;
1130
1131 return created_buffers;
1132}
1133
1134std::shared_ptr<Buffers::VideoContainerBuffer>
1136 const std::shared_ptr<Kakshya::CameraContainer>& container)
1137{
1138 if (!container) {
1140 "hook_camera_to_buffer: null container");
1141 return nullptr;
1142 }
1143
1144 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
1145 if (!stream_container) {
1147 "hook_camera_to_buffer: container is not a StreamContainer");
1148 return nullptr;
1149 }
1150
1151 auto video_buffer = Detail::create_video_container_buffer(m_buffer_manager, stream_container);
1152
1153 if (!video_buffer) {
1155 "hook_camera_to_buffer: failed to create VideoContainerBuffer");
1156 return nullptr;
1157 }
1158
1159 {
1160 std::unique_lock lock(m_camera_mutex);
1161 m_camera_buffers[container] = video_buffer;
1162 }
1163
1165 "Hooked CameraContainer to VideoContainerBuffer ({}x{})",
1166 video_buffer->get_width(), video_buffer->get_height());
1167
1168 return video_buffer;
1169}
1170
1171std::shared_ptr<Buffers::VideoContainerBuffer>
1173 const std::shared_ptr<Kakshya::VideoStreamContainer>& container) const
1174{
1175 std::shared_lock lock(m_buffers_mutex);
1176 auto it = m_video_buffers.find(container);
1177 return it != m_video_buffers.end() ? it->second : nullptr;
1178}
1179
1180std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
1182 const std::shared_ptr<Kakshya::SoundFileContainer>& container) const
1183{
1184 std::shared_lock lock(m_buffers_mutex);
1185 auto it = m_audio_buffers.find(container);
1186 return it != m_audio_buffers.end() ? it->second : std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>> {};
1187}
1188
1189std::shared_ptr<Kakshya::SoundFileContainer>
1190IOManager::get_extracted_audio(const std::shared_ptr<Kakshya::VideoFileContainer>& container) const
1191{
1192 std::shared_lock lock(m_buffers_mutex);
1193 auto it = m_extracted_audio.find(container);
1194 return it != m_extracted_audio.end() ? it->second : nullptr;
1195}
1196
1197std::shared_ptr<Buffers::VideoContainerBuffer>
1199 const std::shared_ptr<Kakshya::CameraContainer>& container) const
1200{
1201 std::shared_lock lock(m_camera_mutex);
1202 auto it = m_camera_buffers.find(container);
1203 return it != m_camera_buffers.end() ? it->second : nullptr;
1204}
1205
1207 const std::shared_ptr<Core::VKImage>& image,
1208 const std::string& filepath,
1209 const IO::ImageWriteOptions& options)
1210{
1211 if (!image) {
1213 "save_image: null image");
1214 return false;
1215 }
1216
1217 track_save_task(std::async(std::launch::async,
1218 [image, filepath, options]() -> bool {
1219 auto data = IO::download_image(image);
1220 if (!data) {
1222 "save_image task: download failed for '{}'", filepath);
1223 return false;
1224 }
1225
1226 return Detail::execute_image_write(filepath, *data, options);
1227 }));
1228
1229 return true;
1230}
1231
1233 const std::shared_ptr<Buffers::TextureBuffer>& buffer,
1234 const std::string& filepath,
1235 const IO::ImageWriteOptions& options)
1236{
1237 if (!buffer) {
1239 "save_image: null buffer");
1240 return false;
1241 }
1242 auto image = buffer->get_gpu_texture();
1243 if (!image) {
1245 "save_image: buffer has no GPU texture");
1246 return false;
1247 }
1248 return save_image(image, filepath, options);
1249}
1250
1252 const std::shared_ptr<Buffers::TextBuffer>& buffer,
1253 const std::string& filepath,
1254 const IO::ImageWriteOptions& options)
1255{
1256 return save_image(
1257 std::static_pointer_cast<Buffers::TextureBuffer>(buffer),
1258 filepath,
1259 options);
1260}
1261
1263 IO::ImageData data,
1264 const std::string& filepath,
1265 const IO::ImageWriteOptions& options)
1266{
1267 track_save_task(std::async(std::launch::async,
1268 [data = std::move(data),
1269 filepath,
1270 options]() -> bool {
1271 return Detail::execute_image_write(filepath, data, options);
1272 }));
1273
1274 return true;
1275}
1276
1277std::shared_ptr<Buffers::VolumeGridBuffer>
1278IOManager::load_volume(const std::string& filepath, const IO::VolumeReadOptions& options)
1279{
1280 IO::VolumeReader reader;
1281
1282 if (!reader.can_read(filepath)) {
1284 "IOManager::load_volume: unsupported format '{}'", filepath);
1285 return nullptr;
1286 }
1287
1288 auto data = reader.load(filepath, options);
1289 if (!data) {
1291 "IOManager::load_volume: failed to load '{}' — {}",
1292 filepath, reader.get_last_error());
1293 return nullptr;
1294 }
1295
1297
1298 if (!buffer) {
1300 "IOManager::load_volume: failed to construct or upload VolumeGridBuffer for '{}'", filepath);
1301 return nullptr;
1302 }
1303
1304 m_loaded_volumes.push_back(buffer);
1305
1307 "IOManager::load_volume: {} field(s) from '{}'",
1308 data->fields.size(),
1309 std::filesystem::path(filepath).filename().string());
1310
1311 return buffer;
1312}
1313
1316 const std::string& filepath,
1317 const IO::VolumeWriteOptions& options)
1318{
1319 track_save_task(std::async(std::launch::async,
1320 [data = std::move(data),
1321 filepath,
1322 options]() -> bool {
1323 return Detail::execute_volume_write(filepath, data, options);
1324 }));
1325
1326 return true;
1327}
1328
1330 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
1331 const std::string& filepath,
1332 const IO::VolumeWriteOptions& options,
1333 const std::vector<std::string>& field_names)
1334{
1335 if (!volume) {
1337 "save_volume: null volume");
1338 return false;
1339 }
1340
1341 track_save_task(std::async(std::launch::async,
1342 [volume, filepath, options, field_names]() -> bool {
1343 auto data = IO::download_volume(volume, field_names);
1344 if (!data) {
1346 "save_volume task: download failed for '{}'", filepath);
1347 return false;
1348 }
1349
1350 return Detail::execute_volume_write(filepath, *data, options);
1351 }));
1352
1353 return true;
1354}
1355
1357 const std::shared_ptr<Buffers::VolumeGridBuffer>& volume,
1358 const std::string& path_pattern,
1359 const std::vector<std::string>& field_names,
1360 const IO::VolumeWriteOptions& options,
1361 uint32_t max_frames,
1362 uint64_t frame_interval)
1363{
1364 if (!volume) {
1366 "capture_volume: null volume");
1367 return 0;
1368 }
1369
1370 if (!m_scheduler) {
1372 "capture_volume: TaskScheduler unavailable");
1373 return 0;
1374 }
1375
1376 auto capture = Detail::create_volume_capture(
1377 *m_scheduler, volume, path_pattern, field_names, options,
1378 [this](Kakshya::VolumeData&& data,
1379 const std::string& path,
1380 const IO::VolumeWriteOptions& opts) {
1381 return save_volume(std::move(data), path, opts);
1382 },
1383 max_frames, frame_interval);
1384
1385 const uint32_t id = m_next_volume_capture_id.fetch_add(1, std::memory_order_relaxed);
1386
1387 std::lock_guard lock(m_volume_captures_mutex);
1388 m_volume_captures.emplace(id, std::move(capture));
1389 return id;
1390}
1391
1392void IOManager::stop_volume_capture(uint32_t capture_id)
1393{
1394 std::unique_ptr<IO::VolumeCapture> capture;
1395 {
1396 std::lock_guard lock(m_volume_captures_mutex);
1397 auto it = m_volume_captures.find(capture_id);
1398 if (it == m_volume_captures.end()) {
1400 "stop_volume_capture: unknown capture_id={}", capture_id);
1401 return;
1402 }
1403 capture = std::move(it->second);
1404 m_volume_captures.erase(it);
1405 }
1406}
1407
1408std::vector<uint32_t> IOManager::get_volume_capture_ids() const
1409{
1410 std::lock_guard lock(m_volume_captures_mutex);
1411 std::vector<uint32_t> ids;
1412 ids.reserve(m_volume_captures.size());
1413 for (const auto& [id, _] : m_volume_captures)
1414 ids.push_back(id);
1415 return ids;
1416}
1417
1419 const std::string& filepath,
1420 std::vector<IO::SpatialCaptureSource> sources,
1421 uint32_t max_frames,
1422 uint64_t frame_interval)
1423{
1424 if (sources.empty()) {
1426 "capture_spatial: no sources");
1427 return 0;
1428 }
1429
1430 if (!m_scheduler) {
1432 "capture_spatial: TaskScheduler unavailable");
1433 return 0;
1434 }
1435
1436 std::shared_ptr<IO::SpatialCache> cache;
1437 {
1438 std::lock_guard lock(m_spatial_captures_mutex);
1439 auto it = m_spatial_caches.find(filepath);
1440 if (it != m_spatial_caches.end()) {
1441 cache = it->second;
1442 } else {
1443 auto new_cache = std::make_shared<IO::SpatialCache>();
1444 if (!new_cache->open(filepath)) {
1446 "capture_spatial: failed to open '{}': {}", filepath, new_cache->get_last_error());
1447 return 0;
1448 }
1449 cache = new_cache;
1450 m_spatial_caches.emplace(filepath, cache);
1451 }
1452 ++m_spatial_cache_refcounts[filepath];
1453 }
1454
1455 auto capture = Detail::create_spatial_capture(*m_scheduler, cache, std::move(sources), max_frames, frame_interval);
1456
1457 const uint32_t id = m_next_spatial_capture_id.fetch_add(1, std::memory_order_relaxed);
1458
1459 std::lock_guard lock(m_spatial_captures_mutex);
1460 m_spatial_captures.emplace(id, SpatialCaptureEntry { .capture = std::move(capture), .filepath = filepath });
1461 return id;
1462}
1463
1464void IOManager::stop_spatial_capture(uint32_t capture_id)
1465{
1466 std::unique_ptr<IO::SpatialCapture> capture;
1467 std::shared_ptr<IO::SpatialCache> cache_to_close;
1468 {
1469 std::lock_guard lock(m_spatial_captures_mutex);
1470 auto it = m_spatial_captures.find(capture_id);
1471 if (it == m_spatial_captures.end()) {
1473 "stop_spatial_capture: unknown capture_id={}", capture_id);
1474 return;
1475 }
1476 capture = std::move(it->second.capture);
1477 const std::string filepath = std::move(it->second.filepath);
1478 m_spatial_captures.erase(it);
1479
1480 auto ref_it = m_spatial_cache_refcounts.find(filepath);
1481 if (ref_it != m_spatial_cache_refcounts.end() && --ref_it->second <= 0) {
1482 m_spatial_cache_refcounts.erase(ref_it);
1483 auto cache_it = m_spatial_caches.find(filepath);
1484 if (cache_it != m_spatial_caches.end()) {
1485 cache_to_close = cache_it->second;
1486 m_spatial_caches.erase(cache_it);
1487 }
1488 }
1489 }
1490
1491 capture.reset();
1492 if (cache_to_close) {
1493 cache_to_close->close();
1494 }
1495}
1496
1497std::vector<uint32_t> IOManager::get_spatial_capture_ids() const
1498{
1499 std::lock_guard lock(m_spatial_captures_mutex);
1500 std::vector<uint32_t> ids;
1501 ids.reserve(m_spatial_captures.size());
1502 for (const auto& [id, _] : m_spatial_captures)
1503 ids.push_back(id);
1504 return ids;
1505}
1506
1508 std::vector<IO::SpatialCaptureSource> sources,
1509 const std::string& path_pattern)
1510{
1511 track_save_task(std::async(std::launch::async,
1512 [sources = std::move(sources), path_pattern]() -> bool {
1513 return Detail::execute_spatial_snapshot(path_pattern, sources);
1514 }));
1515 return true;
1516}
1517
1522
1523std::vector<uint64_t> IOManager::get_video_reader_ids() const
1524{
1525 std::shared_lock lock(m_readers_mutex);
1526 std::vector<uint64_t> ids;
1527 ids.reserve(m_video_readers.size());
1528 for (const auto& [id, _] : m_video_readers)
1529 ids.push_back(id);
1530 return ids;
1531}
1532
1533std::shared_ptr<VideoFileReader> IOManager::get_video_reader(uint64_t id) const
1534{
1535 std::shared_lock lock(m_readers_mutex);
1536 auto it = m_video_readers.find(id);
1537 return it != m_video_readers.end() ? it->second : nullptr;
1538}
1539
1540std::vector<uint64_t> IOManager::get_camera_reader_ids() const
1541{
1542 std::shared_lock lock(m_camera_mutex);
1543 std::vector<uint64_t> ids;
1544 ids.reserve(m_camera_readers.size());
1545 for (const auto& [id, _] : m_camera_readers)
1546 ids.push_back(id);
1547 return ids;
1548}
1549
1550std::shared_ptr<CameraSource> IOManager::get_camera_reader(uint64_t id) const
1551{
1552 std::shared_lock lock(m_camera_mutex);
1553 auto it = m_camera_readers.find(id);
1554 return it != m_camera_readers.end() ? it->second : nullptr;
1555}
1556
1557} // namespace MayaFlux::IO
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:43
Core::GlobalStreamInfo stream
Definition Config.cpp:40
IO::ImageData image
Definition Decoder.cpp:47
uint32_t rows
std::shared_ptr< NetworkGeometryBuffer > buffer
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
std::vector< uint64_t > ids
tvdb_value_type_t vt
uint32_t channels
uint32_t width
uint32_t height
static void register_with_registry()
Register this writer with the ModelWriterRegistry.
static void register_with_registry()
Register this writer with the ImageWriterRegistry.
Definition EXRWriter.cpp:61
std::unordered_map< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Kakshya::SoundFileContainer > > m_extracted_audio
void track_save_task(std::future< bool > fut)
Retain a save task in m_save_tasks and opportunistically reclaim finished ones.
std::shared_ptr< Kakshya::VideoFileContainer > load_video(const std::string &filepath)
Load a video file into a VideoFileContainer.
uint64_t register_camera_source(std::shared_ptr< CameraSource > source)
Assign a globally unique reader_id and take ownership of a CameraSource that is already open.
std::unordered_map< std::shared_ptr< Kakshya::CameraContainer >, std::shared_ptr< Buffers::VideoContainerBuffer > > m_camera_buffers
void configure_audio_processor(const std::shared_ptr< Kakshya::SoundFileContainer > &container) const
bool save_composite(const std::shared_ptr< Kakshya::CompositeContainer > &container, const std::string &filepath)
Save a complete CompositeContainer in a supported format.
std::unordered_map< uint32_t, SpatialCaptureEntry > m_spatial_captures
std::shared_ptr< SoundFileWriter > create_writer(const std::string &filepath, uint32_t channels, uint32_t sample_rate=48000, AVCodecID codec_id=AV_CODEC_ID_NONE)
Construct an open SoundFileWriter the caller drives manually.
std::unordered_map< std::string, int > m_spatial_cache_refcounts
Closes+erases a cache at zero.
std::unordered_map< std::shared_ptr< Kakshya::SoundFileContainer >, std::vector< std::shared_ptr< Buffers::SoundContainerBuffer > > > m_audio_buffers
bool save_image(const std::shared_ptr< Core::VKImage > &image, const std::string &filepath, const IO::ImageWriteOptions &options={})
Save image data to disk asynchronously.
std::atomic< bool > m_shutdown_done
std::vector< uint32_t > get_volume_capture_ids() const
Returns all active volume capture IDs.
std::unordered_map< uint32_t, AudioCaptureState > m_audio_captures
bool save_spatial_snapshot(std::vector< IO::SpatialCaptureSource > sources, const std::string &path_pattern)
Save every source once, as a single time sample, with a millisecond epoch timestamp spliced into the ...
std::vector< uint64_t > get_video_reader_ids() const
Returns all active video reader IDs.
std::shared_ptr< Kakshya::CameraContainer > open_camera(const CameraConfig &config)
Open an FFmpeg camera device and create a CameraContainer.
Registry::Service::AudioBackendService * m_audio_backend_service
std::unordered_map< std::shared_ptr< Kakshya::VideoStreamContainer >, std::shared_ptr< Buffers::VideoContainerBuffer > > m_video_buffers
std::vector< uint32_t > get_spatial_capture_ids() const
Returns all active spatial capture IDs.
std::vector< std::shared_ptr< VideoFileWriter > > m_video_writers
std::vector< std::shared_ptr< Buffers::SoundContainerBuffer > > hook_audio_container_to_buffers(const std::shared_ptr< Kakshya::SoundFileContainer > &container)
Wire a SoundFileContainer to the audio buffer system.
void release_camera_source(uint64_t reader_id)
Release ownership of the camera source identified by reader_id.
uint32_t capture_spatial(const std::string &filepath, std::vector< IO::SpatialCaptureSource > sources, uint32_t max_frames=0, uint64_t frame_interval=1)
Begin (or join) recording time-sampled streams into an Alembic archive.
std::shared_ptr< Nodes::Network::MeshNetwork > load_mesh_network(const std::string &filepath, TextureResolver resolver=nullptr)
Load a 3D model file as a MeshNetwork.
void stop_spatial_capture(uint32_t capture_id)
Stop a running spatial capture and release it.
Core::GlobalStreamInfo & m_stream_info
std::vector< uint32_t > get_video_capture_ids() const
Returns all active video capture IDs (capture_window()).
void stop_audio_capture(uint32_t capture_id)
Stop a running audio capture and finalise the file.
void dispatch_frame_request(uint64_t reader_id)
IOService::request_frame target — shared-lock lookup + pull_frame_all().
std::vector< uint64_t > get_camera_reader_ids() const
Returns all active camera reader IDs.
std::shared_ptr< Kakshya::DynamicSoundStream > load_audio_bounded(const std::string &filepath, uint64_t max_frames=0, bool truncate=false)
Load an audio file into a fully resident, size-bounded DynamicSoundStream.
uint32_t capture_window(const std::shared_ptr< Core::Window > &window, const std::string &filepath, double frame_rate, AVCodecID codec_id=AV_CODEC_ID_NONE)
Begin continuous capture of a window's rendered frames to a file.
std::vector< std::shared_ptr< SoundFileReader > > m_audio_readers
std::vector< std::shared_ptr< Buffers::SoundContainerBuffer > > get_audio_buffers(const std::shared_ptr< Kakshya::SoundFileContainer > &container) const
Retrieve the SoundContainerBuffers created for a container.
std::mutex m_volume_captures_mutex
std::atomic< uint32_t > m_next_spatial_capture_id
void configure_frame_processor(const std::shared_ptr< Kakshya::VideoFileContainer > &container) const
bool save_mesh(const std::shared_ptr< Buffers::MeshBuffer > &mesh_buffer, const std::string &filepath, const IO::ModelWriteOptions &options={})
Save a MeshBuffer's mesh data to disk asynchronously.
std::shared_ptr< CompositeReader > open_composite_reader(const std::string &filepath, std::optional< Kakshya::CompositeLayout > layout=std::nullopt)
Open a schema-bearing reader selected by file format.
std::vector< std::shared_ptr< VideoFileWriter > > get_video_writers() const
Returns all VideoFileWriters created via create_writer().
void stop_volume_capture(uint32_t capture_id)
Stop a running volume capture and release it.
void stop_capture(const std::shared_ptr< Core::Window > &window)
Stop the active window capture and finalise the file.
void stop_video_capture(uint32_t capture_id)
Stop a running video capture and finalise the file.
std::vector< std::shared_ptr< SoundFileWriter > > m_writers
std::mutex m_spatial_captures_mutex
std::shared_ptr< Buffers::VolumeGridBuffer > load_volume(const std::string &filepath, const IO::VolumeReadOptions &options={})
Load a volume file into a newly constructed VolumeGridBuffer.
std::shared_mutex m_camera_mutex
std::vector< std::shared_ptr< Buffers::MeshBuffer > > load_mesh(const std::string &filepath, TextureResolver resolver=nullptr)
Load all meshes from a 3D model file into MeshBuffer instances.
std::shared_ptr< Kakshya::SoundFileContainer > load_audio(const std::string &filepath, VideoLoadConfig config={})
Load an audio file into a SoundFileContainer.
std::shared_ptr< CameraSource > get_camera_reader(uint64_t id) const
Returns the camera source assigned to the given ID, or nullptr.
std::shared_ptr< Buffers::VideoContainerBuffer > get_video_buffer(const std::shared_ptr< Kakshya::VideoStreamContainer > &container) const
Retrieve the VideoContainerBuffer created for a container.
std::mutex m_video_captures_mutex
std::atomic< bool > m_stop_done
std::vector< std::shared_ptr< ModelReader > > m_model_readers
std::atomic< uint32_t > m_next_volume_capture_id
std::vector< uint32_t > get_audio_capture_ids() const
Returns all active audio capture IDs.
std::shared_ptr< VideoFileReader > get_video_reader(uint64_t id) const
Returns the video reader assigned to the given ID, or nullptr.
std::shared_ptr< Kakshya::SoundFileContainer > get_extracted_audio(const std::shared_ptr< Kakshya::VideoFileContainer > &container) const
Retrieve the SoundFileContainer extracted from a video file.
bool save_mesh_snapshot(const std::shared_ptr< Buffers::ComputeMeshBuffer > &buffer, const std::string &path_pattern, const IO::ModelWriteOptions &options={})
Save a ComputeMeshBuffer with a millisecond epoch timestamp spliced into the path,...
std::shared_ptr< Buffers::VideoContainerBuffer > get_camera_buffer(const std::shared_ptr< Kakshya::CameraContainer > &container) const
Retrieve the VideoContainerBuffer created for a camera container.
std::unordered_map< std::string, std::shared_ptr< IO::SpatialCache > > m_spatial_caches
void release_video_reader(uint64_t reader_id)
Release ownership of the reader identified by reader_id.
std::vector< std::shared_ptr< Buffers::VolumeGridBuffer > > m_loaded_volumes
void dispatch_decode_request(uint64_t reader_id)
IOService::request_decode target — shared-lock lookup + signal_decode().
void shutdown()
Unregisters IOService and releases readers, writers, buffers and caches.
std::shared_mutex m_buffers_mutex
std::shared_ptr< Buffers::VideoContainerBuffer > hook_video_container_to_buffer(const std::shared_ptr< Kakshya::VideoStreamContainer > &container)
Wire a video container to the graphics buffer system.
uint32_t capture_output(const std::string &filepath, AVCodecID codec_id=AV_CODEC_ID_NONE)
Begin continuous capture of live audio output to a file.
void write(const std::shared_ptr< Kakshya::SoundStreamContainer > &container, const std::string &filepath, AVCodecID codec_id=AV_CODEC_ID_NONE)
Write a SoundStreamContainer to file in one shot.
std::shared_ptr< CompositeWriter > open_composite_writer(const std::string &filepath, const Kakshya::CompositeLayout &layout)
Open a Composite writer selected by the output file format.
std::shared_ptr< Kakshya::CompositeContainer > load_composite(const std::string &filepath, std::optional< Kakshya::CompositeLayout > layout=std::nullopt, size_t batch_size=1)
Load a complete Composite file into an owning container.
uint64_t register_video_reader(std::shared_ptr< VideoFileReader > reader)
Assign a globally unique reader_id and take ownership of a reader.
void wait_for_pending_saves()
Wait for all in-flight save operations to complete.
std::shared_ptr< Buffers::TextureBuffer > load_image(const std::string &filepath, int desired_channels=4, std::optional< Kakshya::Region > region=std::nullopt)
Load an image file into a TextureBuffer.
std::atomic< uint32_t > m_next_video_capture_id
std::shared_ptr< Buffers::VideoContainerBuffer > hook_camera_to_buffer(const std::shared_ptr< Kakshya::CameraContainer > &container)
Wire a CameraContainer to the graphics buffer system.
~IOManager()
Calls shutdown() if it has not already run.
void stop()
Finalizes all in-flight output while consumers are still running.
bool save_volume(Kakshya::VolumeData data, const std::string &filepath, const IO::VolumeWriteOptions &options={})
Save an already-downloaded VolumeData to disk asynchronously.
std::shared_ptr< Buffers::BufferManager > m_buffer_manager
std::unordered_map< uint32_t, VideoCaptureState > m_video_captures
std::atomic< uint64_t > m_next_reader_id
std::unordered_map< uint64_t, std::shared_ptr< CameraSource > > m_camera_readers
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< uint32_t, std::unique_ptr< IO::VolumeCapture > > m_volume_captures
std::unordered_map< uint64_t, std::shared_ptr< VideoFileReader > > m_video_readers
uint32_t capture_volume(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const std::string &path_pattern, const std::vector< std::string > &field_names={}, const IO::VolumeWriteOptions &options={}, uint32_t max_frames=0, uint64_t frame_interval=1)
Begin recording a numbered .vdb sequence from a volume.
std::shared_ptr< Vruta::TaskScheduler > m_scheduler
IOManager(Core::GlobalStreamInfo &stream_info, uint32_t frame_rate, const std::shared_ptr< Buffers::BufferManager > &buffer_manager, const std::shared_ptr< Vruta::TaskScheduler > &scheduler)
Construct IOManager and register the IOService into BackendRegistry.
Definition IOManager.cpp:60
Parallel::AsyncGroup< bool > m_save_tasks
static void register_with_registry()
Register this writer with the ImageWriterRegistry.
static void register_with_registry()
Register this writer with the VolumeWriterRegistry.
bool can_read(const std::string &filepath) const override
Check if a file can be read by this reader.
std::string get_last_error() const override
Get the last error message.
std::optional< Kakshya::VolumeData > load(const std::string &filepath, const VolumeReadOptions &options={})
Load every selected grid from a file in one call.
tinyvdb-backed loader for volumetric grid files.
Shared field description and byte layout for a CompositeArray.
Definition Composite.hpp:52
void drain()
Blocks until every currently submitted future completes.
void submit(std::future< T > fut)
Retains fut and opportunistically reclaims completed work.
Interface * get_service()
Query for a backend service.
void register_service(ServiceFactory factory)
Register a backend service capability.
static BackendRegistry & instance()
Get the global registry instance.
void unregister_service()
Unregister a service.
void configure_audio_container(const std::shared_ptr< Kakshya::SoundFileContainer > &container, uint32_t buffer_size)
bool execute_volume_write(const std::string &filepath, const Kakshya::VolumeData &data, const VolumeWriteOptions &options)
bool execute_mesh_snapshot(const std::shared_ptr< Buffers::ComputeMeshBuffer > &buffer, const std::string &path_pattern, const ModelWriteOptions &options)
std::vector< std::shared_ptr< Buffers::SoundContainerBuffer > > create_audio_container_buffers(const std::shared_ptr< Buffers::BufferManager > &buffer_manager, const std::shared_ptr< Kakshya::SoundFileContainer > &container)
void configure_video_container(const std::shared_ptr< Kakshya::VideoFileContainer > &container, uint32_t m_frame_rate)
bool execute_image_write(const std::string &filepath, const ImageData &data, const ImageWriteOptions &options)
bool execute_spatial_snapshot(const std::string &path_pattern, const std::vector< SpatialCaptureSource > &sources)
std::shared_ptr< Buffers::VolumeGridBuffer > create_volume_grid_buffer(const std::shared_ptr< Buffers::BufferManager > &buffer_manager, const Kakshya::VolumeData &volume_data)
std::unique_ptr< SpatialCapture > create_spatial_capture(Vruta::TaskScheduler &scheduler, const std::shared_ptr< SpatialCache > &cache, std::vector< SpatialCaptureSource > sources, uint32_t max_frames, uint64_t frame_interval)
std::unique_ptr< VolumeCapture > create_volume_capture(Vruta::TaskScheduler &scheduler, const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const std::string &path_pattern, const std::vector< std::string > &field_names, const VolumeWriteOptions &options, std::function< bool(Kakshya::VolumeData &&, const std::string &, const VolumeWriteOptions &)> save_fn, uint32_t max_frames, uint64_t frame_interval)
void configure_camera_container(const std::shared_ptr< Kakshya::CameraContainer > &container, uint64_t reader_id)
TextureResolver make_default_resolver(const std::string &filepath)
bool execute_mesh_write(const std::string &filepath, const std::vector< Kakshya::MeshData > &meshes, const ModelWriteOptions &options)
std::shared_ptr< Buffers::VideoContainerBuffer > create_video_container_buffer(const std::shared_ptr< Buffers::BufferManager > &buffer_manager, const std::shared_ptr< Kakshya::StreamContainer > &container)
std::optional< ImageData > download_image(const std::shared_ptr< Core::VKImage > &image)
Download pixel data from a GPU-resident VKImage into host ImageData.
std::pair< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Kakshya::SoundFileContainer > > VideoLoadResult
Result of load_video(path, config): the video container, then its audio.
Definition IOManager.hpp:90
bool save_mesh(const std::shared_ptr< Buffers::MeshBuffer > &buffer, const std::string &filepath, const ModelWriteOptions &options)
Save a MeshBuffer's current mesh data to disk via ModelWriterRegistry.
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:31
std::optional< Kakshya::VolumeData > download_volume(const std::shared_ptr< Buffers::VolumeGridBuffer > &volume, const std::vector< std::string > &field_names)
Download named fields from a GPU-resident volume into host VolumeData.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ FileIO
Filesystem I/O operations.
@ Init
Engine/subsystem initialization.
@ AsyncIO
Async I/O operations ( network, streaming)
@ Runtime
General runtime operations (default fallback)
void error_rethrow(Component component, Context context, std::source_location location=std::source_location::current(), std::string_view additional_context="")
Catch and log an exception, then rethrow it.
@ Core
Core engine, backend, subsystems.
@ IO
Networking, file handling, streaming.
@ API
MayaFlux/API Wrapper and convenience functions.
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
uint32_t channels
Number of discrete channels in this set.
uint32_t buffer_size
Number of samples per processing block.
uint32_t sample_rate
Number of samples processed per second (Hz)
ChannelConfig output
Configuration for output signal channels.
Comprehensive configuration for digital audio stream processing.
std::string device_name
Platform device string.
Configuration for opening a camera device via FFmpeg.
std::shared_ptr< Kakshya::AudioOutputContainer > container
std::shared_ptr< SoundFileWriter > writer
std::unique_ptr< IO::SpatialCapture > capture
std::shared_ptr< VideoFileWriter > writer
Configuration for image writing.
Configuration for model writing.
VideoReadOptions video_options
Definition IOManager.hpp:76
AudioReadOptions audio_options
Definition IOManager.hpp:75
Configuration for volume reading.
Configuration for volume writing.
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55
A lattice and every field sampled over it, held in host memory.
std::function< uint32_t(std::function< void(const double *, uint32_t)>)> register_output_observer
Register a per-cycle output observer.
Backend audio subsystem service interface.
std::function< void(uint64_t reader_id)> request_frame
Request the identified camera reader to pull the next frame.
Definition IOService.hpp:44
Backend IO streaming service interface.
Definition IOService.hpp:18