35#include <libavdevice/avdevice.h>
44 const auto base_dir = std::filesystem::path(filepath).parent_path();
45 return [base_dir](
const std::string& raw) -> std::shared_ptr<Core::VKImage> {
54 : m_stream_info(stream_info)
55 , m_frame_rate(frame_rate)
56 , m_buffer_manager(buffer_manager)
58 m_io_service = std::make_shared<Registry::Service::IOService>();
60 m_io_service->request_decode = [
this](uint64_t reader_id) {
81 std::vector<uint32_t> ids;
94 auto fut = w->close();
123std::shared_ptr<Kakshya::VideoFileContainer>
132 auto reader = std::make_shared<IO::VideoFileReader>();
134 if (!reader->can_read(filepath)) {
136 "Cannot read video file: {}", filepath);
150 "Failed to open video file: {}", reader->get_last_error());
155 reader->setup_io_service(reader_id);
157 auto video_container = std::dynamic_pointer_cast<Kakshya::VideoFileContainer>(reader->create_container());
159 if (!video_container) {
161 "Failed to create video container from: {}", filepath);
166 if (!reader->load_into_container(video_container)) {
168 "Failed to load video data: {}", reader->get_last_error());
176 result.
video = video_container;
179 auto audio_container = reader->get_audio_container();
180 if (audio_container) {
182 result.
audio = audio_container;
186 "No audio track found in: {}", filepath);
191 "Loaded video: {}", filepath);
200 "IOManager::register_video_reader called with null reader");
204 const uint64_t
id =
m_next_reader_id.fetch_add(1, std::memory_order_relaxed);
205 reader->set_reader_id(
id);
213 "IOManager: registered VideoFileReader id={}",
id);
225 "IOManager::release_video_reader: unknown id={}", reader_id);
232 "IOManager: released VideoFileReader id={}", reader_id);
242 "IOManager: dispatch_decode_request unknown reader_id={}", reader_id);
246 it->second->signal_decode();
256 "IOManager: dispatch_frame_request unknown reader_id={}", reader_id);
260 it->second->pull_frame_all();
265 auto reader = std::make_shared<IO::SoundFileReader>();
267 if (!reader->can_read(filepath)) {
280 auto container = reader->create_container();
281 auto sound_container = std::dynamic_pointer_cast<Kakshya::SoundFileContainer>(container);
282 if (!sound_container) {
287 if (!reader->load_into_container(sound_container)) {
296 return sound_container;
300 const std::string& filepath,
304 auto reader = std::make_shared<IO::SoundFileReader>();
306 if (!reader->can_read(filepath)) {
308 "IOManager::load_bounded: unsupported format '{}'", filepath);
314 auto stream = reader->load_bounded(filepath, max_frames, truncate);
317 "IOManager::load_bounded: failed for '{}'", filepath);
330std::shared_ptr<SoundFileWriter>
333 uint32_t sample_rate,
336 auto writer = std::make_shared<SoundFileWriter>();
337 if (!writer->open(filepath, channels, sample_rate, codec_id)) {
339 "create_writer: open failed for '{}': {}", filepath, writer->last_error());
351 const std::string& filepath,
356 "write: null container");
360 auto writer = std::make_shared<SoundFileWriter>();
361 if (!writer->open(filepath,
362 container->get_num_channels(),
363 container->get_sample_rate(),
366 "write: open failed for '{}': {}", filepath, writer->last_error());
370 writer->write(container->get_data());
371 auto fut = writer->close();
376 return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
390 "capture_output: AudioBackendService unavailable");
393 auto ct = std::make_shared<Kakshya::AudioOutputContainer>(
m_stream_info);
394 ct->create_default_processor();
396 auto writer = std::make_shared<SoundFileWriter>();
397 if (!writer->open(filepath,
402 "capture_output: writer open failed for '{}': {}", filepath, writer->last_error());
407 [ct, writer](
const double*, uint32_t) {
408 ct->process_default();
409 const auto& vt = ct->get_processed_data();
432 "stop_capture: unknown capture_id={}", capture_id);
435 state = std::move(it->second);
444 auto fut = state.
writer->close();
449 return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
456 std::vector<uint32_t> ids;
463std::shared_ptr<VideoFileWriter>
468 AVPixelFormat src_pixel_format,
471 auto writer = std::make_shared<VideoFileWriter>();
472 if (!writer->open(filepath,
width, height, frame_rate, src_pixel_format, codec_id)) {
474 "create_writer: open failed for '{}': {}", filepath, writer->last_error());
481 const std::shared_ptr<Core::Window>& window,
482 const std::string& filepath,
488 "capture_window: null window");
492 auto writer = std::make_shared<VideoFileWriter>();
493 if (!writer->record(window, filepath, frame_rate, codec_id)) {
495 "capture_window: record failed for '{}': {}", filepath, writer->last_error());
514 "stop_capture(window): null window");
518 uint32_t found_id = 0;
522 if (state.writer->capture_window() == window) {
531 "stop_capture(window): no active capture for window '{}'",
532 window->get_create_info().title);
542 std::vector<uint32_t> ids;
555std::shared_ptr<Kakshya::CameraContainer>
558 static std::once_flag s_avdevice_init;
559 std::call_once(s_avdevice_init, [] { avdevice_register_all(); });
561 auto reader = std::make_shared<CameraReader>();
563 if (!reader->open(config)) {
565 "open_camera: failed — {}", reader->last_error());
569 auto container = reader->create_container();
572 "open_camera: failed to create container — {}",
573 reader->last_error());
577 container->create_default_processor();
580 reader->set_container(container);
587 container->setup_io(rid);
588 container->mark_ready_for_processing(
true);
591 "open_camera: reader_id={} device='{}' {}x{} @{:.1f}fps",
593 reader->width(), reader->height(), reader->frame_rate());
598std::shared_ptr<Buffers::TextureBuffer>
601 auto reader = std::make_shared<IO::ImageReader>();
603 if (!reader->open(filepath)) {
605 "Failed to open image: {}", filepath);
609 auto texture_buffer = reader->create_texture_buffer();
611 if (!texture_buffer) {
613 "Failed to create texture buffer from: {}", filepath);
620 "Loaded image: {} ({}x{})",
621 std::filesystem::path(filepath).filename().
string(),
622 texture_buffer->get_width(),
623 texture_buffer->get_height());
625 return texture_buffer;
628std::vector<std::shared_ptr<Buffers::MeshBuffer>>
631 auto reader = std::make_shared<ModelReader>();
633 if (!reader->can_read(filepath)) {
635 "IOManager::load_mesh: unsupported format '{}'", filepath);
639 if (!reader->open(filepath)) {
641 "IOManager::load_mesh: failed to open '{}' — {}",
642 filepath, reader->get_last_error());
647 resolver = make_default_resolver(filepath);
649 auto buffers = reader->create_mesh_buffers(resolver);
652 if (buffers.empty()) {
654 "IOManager::load_mesh: no meshes in '{}'", filepath);
659 "IOManager::load_mesh: {} mesh(es) from '{}'",
661 std::filesystem::path(filepath).filename().string());
666std::shared_ptr<Nodes::Network::MeshNetwork>
669 auto reader = std::make_shared<ModelReader>();
671 if (!reader->can_read(filepath)) {
673 "IOManager::load_mesh_network: unsupported format '{}'", filepath);
677 if (!reader->open(filepath)) {
679 "IOManager::load_mesh_network: failed to open '{}' — {}",
680 filepath, reader->get_last_error());
685 resolver = make_default_resolver(filepath);
687 auto net = reader->create_mesh_network(resolver);
692 "IOManager::load_mesh_network: no network from '{}'", filepath);
697 "IOManager::load_mesh_network: {} slots from '{}'",
699 std::filesystem::path(filepath).filename().string());
705 const std::shared_ptr<Kakshya::VideoFileContainer>& container)
707 auto existing = std::dynamic_pointer_cast<Kakshya::FrameAccessProcessor>(
708 container->get_default_processor());
714 "Configured existing FrameAccessProcessor");
716 auto processor = std::make_shared<Kakshya::FrameAccessProcessor>();
718 processor->set_auto_advance(
true);
719 container->set_default_processor(processor);
721 "Created and set FrameAccessProcessor");
726 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
730 const std::vector<uint64_t> output_shape = {
732 container->get_num_channels()
735 auto existing = std::dynamic_pointer_cast<Kakshya::ContiguousAccessProcessor>(
736 container->get_default_processor());
739 existing->set_output_size(output_shape);
742 "Configured existing ContiguousAccessProcessor");
744 auto processor = std::make_shared<Kakshya::ContiguousAccessProcessor>();
745 processor->set_output_size(output_shape);
746 processor->set_auto_advance(
true);
747 container->set_default_processor(processor);
749 "Created and set ContiguousAccessProcessor");
753std::shared_ptr<Buffers::VideoContainerBuffer>
755 const std::shared_ptr<Kakshya::VideoFileContainer>& container)
759 "hook_video_container_to_buffer: null container");
763 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
765 if (!stream_container) {
767 "hook_video_container_to_buffer: container is not a VideoStreamContainer");
781 "Hooked VideoFileContainer to VideoContainerBuffer ({}x{})",
782 video_buffer->get_width(), video_buffer->get_height());
787std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
789 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
793 "hook_audio_container_to_buffers: null container");
797 uint32_t num_channels = container->get_num_channels();
798 std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>> created_buffers;
803 "Setting up audio playback for {} channels...",
806 for (uint32_t channel = 0; channel < num_channels; ++channel) {
815 created_buffers.push_back(std::move(container_buffer));
820 "✓ Created buffer for channel {}",
826 return created_buffers;
829std::shared_ptr<Buffers::VideoContainerBuffer>
831 const std::shared_ptr<Kakshya::CameraContainer>& container)
835 "hook_camera_to_buffer: null container");
839 auto stream_container = std::dynamic_pointer_cast<Kakshya::StreamContainer>(container);
840 if (!stream_container) {
842 "hook_camera_to_buffer: container is not a StreamContainer");
852 "hook_camera_to_buffer: failed to create VideoContainerBuffer");
862 "Hooked CameraContainer to VideoContainerBuffer ({}x{})",
863 video_buffer->get_width(), video_buffer->get_height());
868std::shared_ptr<Buffers::VideoContainerBuffer>
870 const std::shared_ptr<Kakshya::VideoFileContainer>& container)
const
877std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>>
879 const std::shared_ptr<Kakshya::SoundFileContainer>& container)
const
883 return it !=
m_audio_buffers.end() ? it->second : std::vector<std::shared_ptr<Buffers::SoundContainerBuffer>> {};
886std::shared_ptr<Kakshya::SoundFileContainer>
894std::shared_ptr<Buffers::VideoContainerBuffer>
896 const std::shared_ptr<Kakshya::CameraContainer>& container)
const
904 const std::shared_ptr<Core::VKImage>&
image,
905 const std::string& filepath,
910 "save_image: null image");
914 auto fut = std::async(std::launch::async,
915 [
image, filepath, options]() ->
bool {
919 "save_image task: download failed for '{}'", filepath);
926 "save_image task: no writer registered for '{}'", filepath);
930 const bool ok = writer->write(filepath, *data, options);
933 "save_image task: writer failed for '{}': {}",
934 filepath, writer->get_last_error());
937 "save_image task: wrote '{}'", filepath);
946 return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
953 const std::shared_ptr<Buffers::TextureBuffer>& buffer,
954 const std::string& filepath,
959 "save_image: null buffer");
962 auto image = buffer->get_gpu_texture();
965 "save_image: buffer has no GPU texture");
972 const std::shared_ptr<Buffers::TextBuffer>& buffer,
973 const std::string& filepath,
977 std::static_pointer_cast<Buffers::TextureBuffer>(buffer),
984 const std::string& filepath,
987 auto fut = std::async(std::launch::async,
988 [data = std::move(data),
994 "save_image task: no writer registered for '{}'", filepath);
997 const bool ok = writer->write(filepath, data, options);
1000 "save_image task: writer failed for '{}': {}",
1001 filepath, writer->get_last_error());
1004 "save_image task: wrote '{}'", filepath);
1013 return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
1021 std::vector<std::future<bool>> tasks;
1026 for (
auto& f : tasks) {
1034 std::vector<uint64_t> ids;
1051 std::vector<uint64_t> ids;
#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::GlobalStreamInfo stream
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.
static void register_with_registry()
Register this writer with the ImageWriterRegistry.
static std::string resolve_path(const std::string &filepath)
Resolve a filepath against the project source root if not found as-is.
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.
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::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::shared_ptr< CameraReader > get_camera_reader(uint64_t id) const
Returns the camera reader assigned to the given ID, or nullptr.
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::shared_ptr< Buffers::TextureBuffer > load_image(const std::string &filepath)
Load an image file into a TextureBuffer.
std::unordered_map< uint32_t, AudioCaptureState > m_audio_captures
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 a camera device and create a CameraContainer.
Registry::Service::AudioBackendService * m_audio_backend_service
std::mutex m_save_tasks_mutex
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.
std::vector< std::future< bool > > m_save_tasks
std::shared_ptr< Nodes::Network::MeshNetwork > load_mesh_network(const std::string &filepath, TextureResolver resolver=nullptr)
Load a 3D model file as a MeshNetwork.
Core::GlobalStreamInfo & m_stream_info
std::vector< uint32_t > get_video_capture_ids() const
Returns all active video capture IDs (capture_window()).
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.
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::vector< std::shared_ptr< VideoFileWriter > > get_video_writers() const
Returns all VideoFileWriters created via create_writer().
void stop_capture(uint32_t capture_id)
Stop a running capture and finalise the file.
std::vector< std::shared_ptr< SoundFileWriter > > m_writers
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< Buffers::VideoContainerBuffer > hook_video_container_to_buffer(const std::shared_ptr< Kakshya::VideoFileContainer > &container)
Wire a VideoFileContainer to the graphics buffer system.
std::mutex m_video_captures_mutex
std::vector< uint32_t > get_audio_capture_ids() const
Returns all active audio capture IDs.
void configure_audio_processor(const std::shared_ptr< Kakshya::SoundFileContainer > &container)
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.
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
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.
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::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()
Unregisters IOService, releases all owned readers, clears stored buffers.
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::vector< std::shared_ptr< ImageReader > > m_image_readers
IOManager(Core::GlobalStreamInfo &stream_info, uint32_t frame_rate, const std::shared_ptr< Buffers::BufferManager > &buffer_manager)
Construct IOManager and register the IOService into BackendRegistry.
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::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
static std::shared_ptr< Core::VKImage > load_texture(const std::string &path)
Load image directly into GPU texture (static utility)
std::unique_ptr< ImageWriter > create_writer(const std::string &filepath) const
static ImageWriterRegistry & instance()
static void register_with_registry()
Register this writer with the ImageWriterRegistry.
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.
@ AUDIO_BACKEND
Standard audio processing backend configuration.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
std::function< std::shared_ptr< Core::VKImage >(const std::string &)> TextureResolver
Callable that maps a raw material texture path to a GPU image.
std::optional< ImageData > download_image(const std::shared_ptr< Core::VKImage > &image)
Download pixel data from a GPU-resident VKImage into host ImageData.
@ 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.
@ 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.
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
Raw image data loaded from file.
Configuration for image writing.
FileReadOptions file_options
AudioReadOptions audio_options
VideoReadOptions video_options
std::shared_ptr< Kakshya::SoundFileContainer > audio
std::shared_ptr< Kakshya::VideoFileContainer > video
Result of load_video() when audio extraction is requested via VideoReadOptions::EXTRACT_AUDIO.
std::function< uint32_t(std::function< void(const double *, uint32_t)>)> register_output_observer
Register a per-cycle output observer.
std::function< void(uint32_t)> unregister_output_observer
Unregister a previously registered 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.
Backend IO streaming service interface.