MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
IOManager.cpp
Go to the documentation of this file.
1#include "IOManager.hpp"
2
6
12
16
18
20
21extern "C" {
22#include <libavdevice/avdevice.h>
23}
24
25namespace MayaFlux::IO {
26
27IOManager::IOManager(uint64_t sample_rate, uint32_t buffer_size, uint32_t frame_rate, const std::shared_ptr<Buffers::BufferManager>& buffer_manager)
28 : m_sample_rate(sample_rate)
29 , m_buffer_size(buffer_size)
30 , m_frame_rate(frame_rate)
31 , m_buffer_manager(buffer_manager)
32{
33 m_io_service = std::make_shared<Registry::Service::IOService>();
34
35 m_io_service->request_decode = [this](uint64_t reader_id) {
36 dispatch_decode_request(reader_id);
37 };
38
41 [svc = m_io_service]() -> void* { return svc.get(); });
42
43 m_io_service->request_frame = [this](uint64_t reader_id) {
44 dispatch_frame_request(reader_id);
45 };
46
48}
49
51{
54
55 m_io_service.reset();
56
57 {
58 std::unique_lock lock(m_readers_mutex);
59 m_video_readers.clear();
60 }
61
62 {
63 std::unique_lock lock(m_camera_mutex);
64 m_camera_readers.clear();
65 }
66
67 {
68 std::unique_lock lock(m_buffers_mutex);
69 m_video_buffers.clear();
70 m_audio_buffers.clear();
71 }
72
74}
75
76std::shared_ptr<Kakshya::VideoFileContainer>
77IOManager::load_video(const std::string& filepath)
78{
79 return load_video(filepath, {}).video;
80}
81
83IOManager::load_video(const std::string& filepath, LoadConfig config)
84{
85 auto reader = std::make_shared<IO::VideoFileReader>();
86
87 if (!reader->can_read(filepath)) {
89 "Cannot read video file: {}", filepath);
90 return {};
91 }
92
93 reader->set_video_options(config.video_options);
94 reader->set_audio_options(config.audio_options);
95 reader->set_target_dimensions(config.target_width, config.target_height);
96
98 reader->set_target_sample_rate(m_sample_rate);
99 }
100
101 if (!reader->open(filepath, config.file_options)) {
103 "Failed to open video file: {}", reader->get_last_error());
104 return {};
105 }
106
107 const uint64_t reader_id = register_video_reader(reader);
108 reader->setup_io_service(reader_id);
109
110 auto video_container = std::dynamic_pointer_cast<Kakshya::VideoFileContainer>(reader->create_container());
111
112 if (!video_container) {
114 "Failed to create video container from: {}", filepath);
115 release_video_reader(reader_id);
116 return {};
117 }
118
119 if (!reader->load_into_container(video_container)) {
121 "Failed to load video data: {}", reader->get_last_error());
122 release_video_reader(reader_id);
123 return {};
124 }
125
126 configure_frame_processor(video_container);
127
128 VideoLoadResult result;
129 result.video = video_container;
130
132 auto audio_container = reader->get_audio_container();
133 if (audio_container) {
134 configure_audio_processor(audio_container);
135 result.audio = audio_container;
136 m_extracted_audio[video_container] = audio_container;
137 } else {
139 "No audio track found in: {}", filepath);
140 }
141 }
142
144 "Loaded video: {}", filepath);
145
146 return result;
147}
148
149uint64_t IOManager::register_video_reader(std::shared_ptr<IO::VideoFileReader> reader)
150{
151 if (!reader) {
153 "IOManager::register_video_reader called with null reader");
154 return 0;
155 }
156
157 const uint64_t id = m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
158 reader->set_reader_id(id);
159
160 {
161 std::unique_lock lock(m_readers_mutex);
162 m_video_readers.emplace(id, std::move(reader));
163 }
164
166 "IOManager: registered VideoFileReader id={}", id);
167
168 return id;
169}
170
171void IOManager::release_video_reader(uint64_t reader_id)
172{
173 std::unique_lock lock(m_readers_mutex);
174 auto it = m_video_readers.find(reader_id);
175
176 if (it == m_video_readers.end()) {
178 "IOManager::release_video_reader: unknown id={}", reader_id);
179 return;
180 }
181
182 m_video_readers.erase(it);
183
185 "IOManager: released VideoFileReader id={}", reader_id);
186}
187
189{
190 std::shared_lock lock(m_readers_mutex);
191 auto it = m_video_readers.find(reader_id);
192
193 if (it == m_video_readers.end()) {
195 "IOManager: dispatch_decode_request unknown reader_id={}", reader_id);
196 return;
197 }
198
199 it->second->signal_decode();
200}
201
202void IOManager::dispatch_frame_request(uint64_t reader_id)
203{
204 std::shared_lock lock(m_camera_mutex);
205 auto it = m_camera_readers.find(reader_id);
206
207 if (it == m_camera_readers.end()) {
209 "IOManager: dispatch_frame_request unknown reader_id={}", reader_id);
210 return;
211 }
212
213 it->second->pull_frame_all();
214}
215
216std::shared_ptr<Kakshya::SoundFileContainer> IOManager::load_audio(const std::string& filepath, LoadConfig config)
217{
218 auto reader = std::make_shared<IO::SoundFileReader>();
219
220 if (!reader->can_read(filepath)) {
221 MF_ERROR(Journal::Component::API, Journal::Context::FileIO, "Cannot read file: {}", filepath);
222 return nullptr;
223 }
224
225 reader->set_target_sample_rate(m_sample_rate);
226 reader->set_audio_options(config.audio_options);
227
228 if (!reader->open(filepath, config.file_options)) {
229 MF_ERROR(Journal::Component::API, Journal::Context::FileIO, "Failed to open file: {}", reader->get_last_error());
230 return nullptr;
231 }
232
233 auto container = reader->create_container();
234 auto sound_container = std::dynamic_pointer_cast<Kakshya::SoundFileContainer>(container);
235 if (!sound_container) {
236 MF_ERROR(Journal::Component::API, Journal::Context::Runtime, "Failed to create sound container");
237 return nullptr;
238 }
239
240 if (!reader->load_into_container(sound_container)) {
241 MF_ERROR(Journal::Component::API, Journal::Context::Runtime, "Failed to load audio data: {}", reader->get_last_error());
242 return nullptr;
243 }
244
245 configure_audio_processor(sound_container);
246
247 m_audio_readers.push_back(std::move(reader));
248
249 return sound_container;
250}
251
252std::shared_ptr<Kakshya::CameraContainer>
254{
255 static std::once_flag s_avdevice_init;
256 std::call_once(s_avdevice_init, [] { avdevice_register_all(); });
257
258 auto reader = std::make_shared<CameraReader>();
259
260 if (!reader->open(config)) {
262 "open_camera: failed — {}", reader->last_error());
263 return nullptr;
264 }
265
266 auto container = reader->create_container();
267 if (!container) {
269 "open_camera: failed to create container — {}",
270 reader->last_error());
271 return nullptr;
272 }
273
274 container->create_default_processor();
275
276 uint64_t rid = m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
277 reader->set_container(container);
278
279 {
280 std::unique_lock lock(m_camera_mutex);
281 m_camera_readers[rid] = reader;
282 }
283
284 container->setup_io(rid);
285 container->mark_ready_for_processing(true);
286
288 "open_camera: reader_id={} device='{}' {}x{} @{:.1f}fps",
289 rid, config.device_name,
290 reader->width(), reader->height(), reader->frame_rate());
291
292 return container;
293}
294
295std::shared_ptr<Buffers::TextureBuffer>
296IOManager::load_image(const std::string& filepath)
297{
298 auto reader = std::make_shared<IO::ImageReader>();
299
300 if (!reader->open(filepath)) {
302 "Failed to open image: {}", filepath);
303 return nullptr;
304 }
305
306 auto texture_buffer = reader->create_texture_buffer();
307
308 if (!texture_buffer) {
310 "Failed to create texture buffer from: {}", filepath);
311 return nullptr;
312 }
313
314 m_image_readers.push_back(std::move(reader));
315
317 "Loaded image: {} ({}x{})",
318 std::filesystem::path(filepath).filename().string(),
319 texture_buffer->get_width(),
320 texture_buffer->get_height());
321
322 return texture_buffer;
323}
324
326 const std::shared_ptr<Kakshya::VideoFileContainer>& container)
327{
328 auto existing = std::dynamic_pointer_cast<Kakshya::FrameAccessProcessor>(
329 container->get_default_processor());
330
331 if (existing) {
332 existing->set_global_fps(m_frame_rate);
333 existing->set_auto_advance(true);
335 "Configured existing FrameAccessProcessor");
336 } else {
337 auto processor = std::make_shared<Kakshya::FrameAccessProcessor>();
338 processor->set_global_fps(m_frame_rate);
339 processor->set_auto_advance(true);
340 container->set_default_processor(processor);
342 "Created and set FrameAccessProcessor");
343 }
344}
345
347 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
348{
349 container->set_memory_layout(Kakshya::MemoryLayout::ROW_MAJOR);
350
351 const std::vector<uint64_t> output_shape = {
353 container->get_num_channels()
354 };
355
356 auto existing = std::dynamic_pointer_cast<Kakshya::ContiguousAccessProcessor>(
357 container->get_default_processor());
358
359 if (existing) {
360 existing->set_output_size(output_shape);
361 existing->set_auto_advance(true);
363 "Configured existing ContiguousAccessProcessor");
364 } else {
365 auto processor = std::make_shared<Kakshya::ContiguousAccessProcessor>();
366 processor->set_output_size(output_shape);
367 processor->set_auto_advance(true);
368 container->set_default_processor(processor);
370 "Created and set ContiguousAccessProcessor");
371 }
372}
373
374std::shared_ptr<Buffers::VideoContainerBuffer>
376 const std::shared_ptr<Kakshya::VideoFileContainer>& container)
377{
378 if (!container) {
380 "hook_video_container_to_buffer: null container");
381 return nullptr;
382 }
383
384 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
385
386 if (!stream_container) {
388 "hook_video_container_to_buffer: container is not a VideoStreamContainer");
389 return nullptr;
390 }
391
392 auto video_buffer = m_buffer_manager->create_graphics_buffer<Buffers::VideoContainerBuffer>(
394 stream_container);
395
396 {
397 std::unique_lock lock(m_buffers_mutex);
398 m_video_buffers[container] = video_buffer;
399 }
400
402 "Hooked VideoFileContainer to VideoContainerBuffer ({}x{})",
403 video_buffer->get_width(), video_buffer->get_height());
404
405 return video_buffer;
406}
407
408std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
410 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
411{
412 if (!container) {
414 "hook_audio_container_to_buffers: null container");
415 return {};
416 }
417
418 uint32_t num_channels = container->get_num_channels();
419 std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>> created_buffers;
420
421 MF_TRACE(
424 "Setting up audio playback for {} channels...",
425 num_channels);
426
427 for (uint32_t channel = 0; channel < num_channels; ++channel) {
428 auto container_buffer = m_buffer_manager->create_audio_buffer<MayaFlux::Buffers::SoundContainerBuffer>(
430 channel,
431 container,
432 channel);
433
434 container_buffer->initialize();
435
436 created_buffers.push_back(std::move(container_buffer));
437
438 MF_INFO(
441 "✓ Created buffer for channel {}",
442 channel);
443 }
444
445 m_audio_buffers[container] = created_buffers;
446
447 return created_buffers;
448}
449
450std::shared_ptr<Buffers::VideoContainerBuffer>
452 const std::shared_ptr<Kakshya::CameraContainer>& container)
453{
454 if (!container) {
456 "hook_camera_to_buffer: null container");
457 return nullptr;
458 }
459
460 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
461 if (!stream_container) {
463 "hook_camera_to_buffer: container is not a StreamContainer");
464 return nullptr;
465 }
466
467 auto video_buffer = m_buffer_manager->create_graphics_buffer<Buffers::VideoContainerBuffer>(
469 stream_container);
470
471 if (!video_buffer) {
473 "hook_camera_to_buffer: failed to create VideoContainerBuffer");
474 return nullptr;
475 }
476
477 {
478 std::unique_lock lock(m_camera_mutex);
479 m_camera_buffers[container] = video_buffer;
480 }
481
483 "Hooked CameraContainer to VideoContainerBuffer ({}x{})",
484 video_buffer->get_width(), video_buffer->get_height());
485
486 return video_buffer;
487}
488
489std::shared_ptr<Buffers::VideoContainerBuffer>
491 const std::shared_ptr<Kakshya::VideoFileContainer>& container) const
492{
493 std::shared_lock lock(m_buffers_mutex);
494 auto it = m_video_buffers.find(container);
495 return it != m_video_buffers.end() ? it->second : nullptr;
496}
497
498std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
500 const std::shared_ptr<Kakshya::SoundFileContainer>& container) const
501{
502 std::shared_lock lock(m_buffers_mutex);
503 auto it = m_audio_buffers.find(container);
504 return it != m_audio_buffers.end() ? it->second : std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>> {};
505}
506
507std::shared_ptr<Kakshya::SoundFileContainer>
508IOManager::get_extracted_audio(const std::shared_ptr<Kakshya::VideoFileContainer>& container) const
509{
510 std::shared_lock lock(m_buffers_mutex);
511 auto it = m_extracted_audio.find(container);
512 return it != m_extracted_audio.end() ? it->second : nullptr;
513}
514
515std::shared_ptr<Buffers::VideoContainerBuffer>
517 const std::shared_ptr<Kakshya::CameraContainer>& container) const
518{
519 std::shared_lock lock(m_camera_mutex);
520 auto it = m_camera_buffers.find(container);
521 return it != m_camera_buffers.end() ? it->second : nullptr;
522}
523
524} // 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,...)
Cycle Behavior: The for_cycles(N) configuration controls how many times the capture operation execute...
void initialize()
Initialize the buffer after construction.
AudioBuffer implementation backed by a StreamContainer.
TextureBuffer implementation backed by a VideoStreamContainer.
std::unordered_map< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Kakshya::SoundFileContainer > > m_extracted_audio
std::shared_ptr< Kakshya::VideoFileContainer > load_video(const std::string &filepath)
Load a video file into a VideoFileContainer.
Definition IOManager.cpp:77
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
std::shared_ptr< Buffers::TextureBuffer > load_image(const std::string &filepath)
Load an image file into a TextureBuffer.
std::shared_ptr< Kakshya::CameraContainer > open_camera(const CameraConfig &config)
Open a camera device and create a CameraContainer.
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 dispatch_frame_request(uint64_t reader_id)
IOService::request_frame target — shared-lock lookup + pull_frame_all().
void configure_frame_processor(const std::shared_ptr< Kakshya::VideoFileContainer > &container)
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::shared_mutex m_camera_mutex
std::shared_ptr< Buffers::VideoContainerBuffer > hook_video_container_to_buffer(const std::shared_ptr< Kakshya::VideoFileContainer > &container)
Wire a VideoFileContainer to the graphics buffer system.
void configure_audio_processor(const std::shared_ptr< Kakshya::SoundFileContainer > &container)
std::shared_ptr< Kakshya::SoundFileContainer > get_extracted_audio(const std::shared_ptr< Kakshya::VideoFileContainer > &container) const
Retrieve the SoundFileContainer extracted from a video file.
std::shared_ptr< Buffers::VideoContainerBuffer > get_video_buffer(const std::shared_ptr< Kakshya::VideoFileContainer > &container) const
Retrieve the VideoContainerBuffer created for a container.
std::shared_ptr< Buffers::VideoContainerBuffer > get_camera_buffer(const std::shared_ptr< Kakshya::CameraContainer > &container) const
Retrieve the VideoContainerBuffer created for a camera container.
void release_video_reader(uint64_t reader_id)
Release ownership of the reader identified by reader_id.
void dispatch_decode_request(uint64_t reader_id)
IOService::request_decode target — shared-lock lookup + signal_decode().
std::shared_mutex m_buffers_mutex
uint64_t register_video_reader(std::shared_ptr< VideoFileReader > reader)
Assign a globally unique reader_id and take ownership of a reader.
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()
Unregisters IOService, releases all owned readers, clears stored buffers.
Definition IOManager.cpp:50
std::shared_ptr< Buffers::BufferManager > m_buffer_manager
std::atomic< uint64_t > m_next_reader_id
IOManager(uint64_t sample_rate, uint32_t buffer_size, uint32_t frame_rate, const std::shared_ptr< Buffers::BufferManager > &buffer_manager)
Construct IOManager and register the IOService into BackendRegistry.
Definition IOManager.cpp:27
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
std::shared_ptr< Kakshya::SoundFileContainer > load_audio(const std::string &filepath, LoadConfig config={})
Load an audio file into a SoundFileContainer.
std::unordered_map< std::shared_ptr< Kakshya::VideoFileContainer >, std::shared_ptr< Buffers::VideoContainerBuffer > > m_video_buffers
void register_service(ServiceFactory factory)
Register a backend service capability.
static BackendRegistry & instance()
Get the global registry instance.
void unregister_service()
Unregister a service.
@ AUDIO_BACKEND
Standard audio processing backend configuration.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ 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)
@ Core
Core engine, backend, subsystems.
@ API
MayaFlux/API Wrapper and convenience functions.
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
std::string device_name
Platform device string.
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
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