MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VideoStreamContainer.cpp
Go to the documentation of this file.
2
11
14
15namespace MayaFlux::Kakshya {
16
17// =========================================================================
18// Construction
19// =========================================================================
20
22 uint32_t height,
23 uint32_t channels,
24 double frame_rate)
25 : m_width(width)
26 , m_height(height)
27 , m_channels(channels)
28 , m_frame_rate(frame_rate)
29{
30 m_processing_chain = std::make_shared<DataProcessingChain>();
33
34 if (width > 0 && height > 0)
36}
37
38// =========================================================================
39// Dimensions
40// =========================================================================
41
43{
47 static_cast<uint64_t>(m_height),
48 static_cast<uint64_t>(m_width),
49 static_cast<uint64_t>(m_channels) },
51}
52
53std::vector<DataDimension> VideoStreamContainer::get_dimensions() const
54{
56}
57
62
64{
65 return static_cast<uint64_t>(m_width) * m_height * m_channels;
66}
67
69{
70 return m_num_frames;
71}
72
77
78// =========================================================================
79// Ring buffer setup
80// =========================================================================
81
82void VideoStreamContainer::setup_ring(uint64_t total_frames,
83 uint32_t ring_capacity,
84 uint32_t width,
85 uint32_t height,
86 uint32_t channels,
87 double frame_rate,
88 uint32_t refill_threshold,
89 uint64_t reader_id)
90{
91 {
93
94 m_width = width;
96 m_channels = channels;
97 m_frame_rate = frame_rate;
98 m_total_source_frames = total_frames;
99 m_ring_capacity = ring_capacity;
100 m_num_frames = total_frames;
101 m_cache_head.store(0, std::memory_order_relaxed);
102 m_refill_threshold = refill_threshold;
103 m_io_reader_id = reader_id;
104
107
108 const size_t frame_bytes = get_frame_byte_size();
109
110 m_data.resize(1);
111 auto& pixels = m_data[0].emplace<std::vector<uint8_t>>();
112 pixels.resize(frame_bytes * ring_capacity, 0);
113
114 m_slot_frame = std::vector<std::atomic<uint64_t>>(ring_capacity);
115 for (auto& sf : m_slot_frame)
116 sf.store(UINT64_MAX, std::memory_order_relaxed);
117
118 m_ready_queue.reset();
120 }
121
122 if (m_float_frame_cache.size() != ring_capacity) {
123 m_float_frame_cache.resize(ring_capacity);
124 m_float_frame_dirty = std::vector<std::atomic<bool>>(ring_capacity);
125
126 for (auto& flag : m_float_frame_dirty)
127 flag.store(true, std::memory_order_relaxed);
128 }
129
131}
132
133// =========================================================================
134// Ring write API
135// =========================================================================
136
137uint8_t* VideoStreamContainer::mutable_slot_ptr(uint64_t frame_index)
138{
139 if (m_ring_capacity == 0 || frame_index >= m_total_source_frames)
140 return nullptr;
141
142 auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
143 if (!pixels)
144 return nullptr;
145
146 return pixels->data() + slot_for(frame_index) * get_frame_byte_size();
147}
148
149void VideoStreamContainer::commit_frame(uint64_t frame_index)
150{
151 if (m_ring_capacity == 0)
152 return;
153
154 m_slot_frame[slot_for(frame_index)].store(frame_index, std::memory_order_release);
155 (void)m_ready_queue.push(frame_index);
156
157 advance_cache_head(frame_index);
158}
159
161{
162 for (auto& sf : m_slot_frame)
163 sf.store(UINT64_MAX, std::memory_order_relaxed);
164
165 m_ready_queue.reset();
166 std::atomic_thread_fence(std::memory_order_release);
167}
168
169bool VideoStreamContainer::is_frame_available(uint64_t frame_index) const
170{
171 if (m_ring_capacity == 0)
172 return false;
173
174 return m_slot_frame[slot_for(frame_index)].load(std::memory_order_acquire) == frame_index;
175}
176
177// =========================================================================
178// Frame access
179// =========================================================================
180
182{
183 return static_cast<size_t>(m_width) * m_height * m_channels;
184}
185
186std::span<const uint8_t> VideoStreamContainer::get_frame_pixels(uint64_t frame_index) const
187{
188 const size_t frame_bytes = get_frame_byte_size();
189 if (frame_bytes == 0 || frame_index >= m_num_frames)
190 return {};
191
192 if (m_ring_capacity == 0) {
193 std::span<const uint8_t> result;
194 seqlock_read_void(m_data_lock, 8, [&] {
195 if (m_data.empty())
196 return;
197 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
198 if (!pixels)
199 return;
200 const size_t offset = frame_index * frame_bytes;
201 if (offset + frame_bytes > pixels->size())
202 return;
203 result = { pixels->data() + offset, frame_bytes };
204 });
205 return result;
206 }
207
208 const uint32_t slot = slot_for(frame_index);
209 if (m_slot_frame[slot].load(std::memory_order_acquire) == frame_index) {
210 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
211 if (!pixels)
212 return {};
213 return { pixels->data() + slot * frame_bytes, frame_bytes };
214 }
215
216 return {};
217}
218
219uint64_t VideoStreamContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
220{
221 return coordinates_to_linear(coordinates, m_structure.dimensions);
222}
223
224std::vector<uint64_t> VideoStreamContainer::linear_index_to_coordinates(uint64_t linear_index) const
225{
226 return linear_to_coordinates(linear_index, m_structure.dimensions);
227}
228
229// =========================================================================
230// Region management
231// =========================================================================
232
233std::vector<DataVariant> VideoStreamContainer::get_region_data(const Region& region) const
234{
235 std::optional<std::vector<DataVariant>> result;
236 seqlock_read_void(m_data_lock, 8, [&] {
237 if (m_data.empty())
238 return;
239
240 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
241 if (!pixels || pixels->empty())
242 return;
243
244 const std::span<const uint8_t> src { pixels->data(), pixels->size() };
245 try {
246 result = { extract_nd_region<uint8_t>(src, region, m_structure.dimensions) };
247 } catch (const std::exception& e) {
249 "VideoStreamContainer::get_region_data extraction failed — {}", e.what());
250 }
251 });
252
253 return result.value_or(std::vector<DataVariant> {});
254}
255
256void VideoStreamContainer::set_region_data(const Region& /*region*/, const std::vector<DataVariant>& /*data*/)
257{
259 "VideoStreamContainer::set_region_data — write path not yet implemented");
260}
261
262std::vector<DataVariant> VideoStreamContainer::get_region_group_data(const RegionGroup& /*group*/) const
263{
264 std::optional<std::vector<DataVariant>> result;
265 seqlock_read_void(m_data_lock, 8, [&] {
266 result = m_data;
267 });
268 return result.value_or(std::vector<DataVariant> {});
269}
270
271std::vector<DataVariant> VideoStreamContainer::get_segments_data(const std::vector<RegionSegment>& /*segments*/) const
272{
273 std::optional<std::vector<DataVariant>> result;
274 seqlock_read_void(m_data_lock, 8, [&] {
275 result = m_data;
276 });
277 return result.value_or(std::vector<DataVariant> {});
278}
279
285
287{
288 static const RegionGroup empty;
289 std::optional<RegionGroup> result;
290 seqlock_read_void(m_region_lock, 8, [&] {
291 auto it = m_region_groups.find(name);
292 result = (it != m_region_groups.end()) ? it->second : empty;
293 });
294 return result.value_or(empty);
295}
296
297std::unordered_map<std::string, RegionGroup> VideoStreamContainer::get_all_region_groups() const
298{
299 std::optional<std::unordered_map<std::string, RegionGroup>> result;
300 seqlock_read_void(m_region_lock, 8, [&] {
301 result = m_region_groups;
302 });
303 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
304}
305
306void VideoStreamContainer::remove_region_group(const std::string& name)
307{
309 m_region_groups.erase(name);
310}
311
312bool VideoStreamContainer::is_region_loaded(const Region& /*region*/) const { return true; }
313void VideoStreamContainer::load_region(const Region& /*region*/) { }
315
316// =========================================================================
317// Read position and looping
318// =========================================================================
319
320void VideoStreamContainer::set_read_position(const std::vector<uint64_t>& position)
321{
322 if (!position.empty())
323 m_read_position.store(position[0]);
324}
325
326void VideoStreamContainer::update_read_position_for_channel(size_t /*channel*/, uint64_t frame)
327{
328 m_read_position.store(frame);
329
331 return;
332
333 const uint64_t head = m_cache_head.load(std::memory_order_acquire);
334 const uint64_t buffered = (head > frame) ? (head - frame) : 0;
335
336 if (buffered < m_refill_threshold && m_io_service->request_decode)
338}
339
340const std::vector<uint64_t>& VideoStreamContainer::get_read_position() const
341{
342 thread_local std::vector<uint64_t> pos(1);
343 pos[0] = m_read_position.load();
344 return pos;
345}
346
347void VideoStreamContainer::advance_read_position(const std::vector<uint64_t>& frames)
348{
349 if (!frames.empty())
350 m_read_position.fetch_add(frames[0]);
351}
352
354{
355 if (is_looping())
356 return false;
357
358 uint64_t total = (m_ring_capacity > 0) ? m_total_source_frames : m_num_frames;
359 return total == 0 || m_read_position.load() >= total;
360}
361
366
368{
369 return static_cast<uint64_t>(m_frame_rate);
370}
371
372uint64_t VideoStreamContainer::time_to_position(double time) const
373{
374 if (m_frame_rate <= 0.0)
375 return 0;
376 return static_cast<uint64_t>(time * m_frame_rate);
377}
378
379double VideoStreamContainer::position_to_time(uint64_t position) const
380{
381 if (m_frame_rate <= 0.0)
382 return 0.0;
383 return static_cast<double>(position) / m_frame_rate;
384}
385
389
391{
392 return has_data() && m_num_frames > 0;
393}
394
396{
397 uint64_t pos = m_read_position.load();
398 return { pos < m_num_frames ? m_num_frames - pos : 0 };
399}
400
401uint64_t VideoStreamContainer::read_sequential(std::span<double> output, uint64_t count)
402{
403 std::ranges::fill(output, 0.0);
404 uint64_t pos = m_read_position.load();
405 uint64_t advanced = std::min(count, m_num_frames > pos ? m_num_frames - pos : 0UL);
406 m_read_position.store(pos + advanced);
407 return advanced;
408}
409
410uint64_t VideoStreamContainer::peek_sequential(std::span<double> output, uint64_t /*count*/, uint64_t /*offset*/) const
411{
412 std::ranges::fill(output, 0.0);
413 return 0;
414}
415
416// =========================================================================
417// Clear and raw access
418// =========================================================================
419
421{
422 {
424 std::ranges::for_each(m_data, [](auto& v) {
425 std::visit([](auto& vec) { vec.clear(); }, v);
426 });
427 m_num_frames = 0;
428 m_read_position.store(0);
430 }
432}
433
435{
436 if (m_ring_capacity > 0)
437 return nullptr;
438
439 if (m_data.empty())
440 return nullptr;
441 const auto* v = std::get_if<std::vector<uint8_t>>(&m_data[0]);
442 return (v && !v->empty()) ? v->data() : nullptr;
443}
444
446{
447 if (m_ring_capacity > 0)
448 return m_total_source_frames > 0;
449
450 bool result = false;
451 seqlock_read_void(m_data_lock, 8, [&] {
452 if (m_data.empty())
453 return;
454 result = std::visit([](const auto& vec) { return !vec.empty(); }, m_data[0]);
455 });
456 return result;
457}
458
459// =========================================================================
460// Processing state
461// =========================================================================
462
464{
465 ProcessingState old = m_processing_state.exchange(new_state);
466 if (old != new_state)
467 notify_state_change(new_state);
468}
469
471{
472 seqlock_read_void(m_cb_lock, 8, [&] {
474 m_state_callback(shared_from_this(), new_state);
475 });
476}
477
479 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback)
480{
482 m_state_callback = std::move(callback);
483}
484
490
492 void* output,
493 size_t count,
494 uint64_t start_frame,
495 uint64_t num_frames,
496 const std::type_info& type) const
497{
498 if (type != typeid(uint8_t)) {
499 error<std::runtime_error>(Journal::Component::Kakshya, Journal::Context::Runtime, std::source_location::current(), "VideoStreamContainer only supports uint8_t");
500 }
501
502 auto* out = static_cast<uint8_t*>(output);
503 get_frames_typed(std::span<uint8_t>(out, count), start_frame, num_frames);
504}
505
506void VideoStreamContainer::get_frames_typed(std::span<uint8_t> output, uint64_t start_frame, uint64_t num_frames) const
507{
508 const size_t frame_size = get_frame_byte_size();
509 const size_t required = static_cast<size_t>(num_frames) * frame_size;
510
511 if (output.size() < required) {
512 error<std::runtime_error>(
515 std::source_location::current(),
516 "VideoStreamContainer::get_frames_typed: output buffer too small ({} < {})",
517 output.size(), required);
518 }
519
520 for (uint64_t i = 0; i < num_frames; ++i) {
521 auto frame = get_frame_typed(start_frame + i);
522 std::ranges::copy(frame, output.begin() + static_cast<size_t>(i) * frame_size);
523 }
524}
525
527{
528 auto state = get_processing_state();
529 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
530}
531
540
542{
543 auto processor = std::make_shared<FrameAccessProcessor>();
544 set_default_processor(processor);
545}
546
555
556void VideoStreamContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
557{
558 auto old = m_default_processor;
559 m_default_processor = processor;
560 if (old)
561 old->on_detach(shared_from_this());
562 if (processor)
563 processor->on_attach(shared_from_this());
564}
565
566std::shared_ptr<DataProcessor> VideoStreamContainer::get_default_processor() const
567{
568 return m_default_processor;
569}
570
571std::shared_ptr<DataProcessingChain> VideoStreamContainer::get_processing_chain()
572{
574 m_processing_chain = std::make_shared<DataProcessingChain>();
575
576 return m_processing_chain;
577}
578
579// =========================================================================
580// Reader tracking
581// =========================================================================
582
583uint32_t VideoStreamContainer::register_dimension_reader(uint32_t /*dimension_index*/)
584{
585 return m_registered_readers.fetch_add(1, std::memory_order_relaxed);
586}
587
589{
590 if (m_registered_readers.load(std::memory_order_relaxed) > 0)
591 m_registered_readers.fetch_sub(1, std::memory_order_relaxed);
592}
593
595{
596 return m_registered_readers.load(std::memory_order_acquire) > 0;
597}
598
599void VideoStreamContainer::mark_dimension_consumed(uint32_t /*dimension_index*/, uint32_t /*reader_id*/)
600{
601 m_consumed_readers.fetch_add(1, std::memory_order_release);
602}
603
605{
606 return m_consumed_readers.load(std::memory_order_acquire)
607 >= m_registered_readers.load(std::memory_order_acquire);
608}
609
610// =========================================================================
611// Data access
612// =========================================================================
613
615{
617 "VideoStreamContainer::channel_data — not meaningful for interleaved pixel data; returning full surface");
618
619 if (m_data.empty()) {
620 static DataVariant empty_variant = std::vector<uint8_t>();
621 return { empty_variant, m_structure.dimensions, DataModality::VIDEO_COLOR };
622 }
623
625}
626
628{
629 if (m_data.empty())
630 return {};
632}
633
635 const std::vector<uint64_t>& coords,
636 void* out,
637 const std::type_info& type) const
638{
639 if (type != typeid(uint8_t) || coords.size() < 4)
640 return;
641
642 const uint64_t frame = coords[0];
643 const uint64_t y = coords[1];
644 const uint64_t x = coords[2];
645 const uint64_t c = coords[3];
646
647 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
648 return;
649
650 auto pixels = get_frame_pixels(frame);
651 if (pixels.empty())
652 return;
653
654 const size_t idx = (y * m_width + x) * m_channels + c;
655 if (idx >= pixels.size())
656 return;
657
658 *static_cast<uint8_t*>(out) = pixels[idx];
659}
660
662 const std::vector<uint64_t>& coords,
663 const void* in,
664 const std::type_info& type)
665{
666 if (type != typeid(uint8_t) || coords.size() < 4 || m_data.empty())
667 return;
668
669 auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
670 if (!pixels)
671 return;
672
673 const uint64_t frame = coords[0];
674 const uint64_t y = coords[1];
675 const uint64_t x = coords[2];
676 const uint64_t c = coords[3];
677
678 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
679 return;
680
681 const size_t idx = (frame * m_height * m_width * m_channels)
682 + (y * m_width * m_channels)
683 + (x * m_channels)
684 + c;
685
686 if (idx >= pixels->size())
687 return;
688
689 (*pixels)[idx] = *static_cast<const uint8_t*>(in);
690}
691
692std::span<const float> VideoStreamContainer::processed_frame_as_float(uint64_t frame_index) const
693{
694 if (frame_index >= m_processed_data.size())
695 return {};
696
697 if (frame_index >= m_float_frame_dirty.size()) {
698 const size_t old_size = m_float_frame_dirty.size();
699 const size_t new_size = frame_index + 1;
700 auto new_dirty = std::vector<std::atomic<bool>>(new_size);
701
702 for (size_t i = 0; i < old_size; ++i) {
703 new_dirty[i].store(m_float_frame_dirty[i].load(std::memory_order_relaxed),
704 std::memory_order_relaxed);
705 }
706
707 for (size_t i = old_size; i < new_size; ++i)
708 new_dirty[i].store(true, std::memory_order_relaxed);
709 m_float_frame_dirty = std::move(new_dirty);
710 m_float_frame_cache.resize(new_size);
711 }
712
713 if (!m_float_frame_dirty[frame_index].load(std::memory_order_acquire))
714 return { m_float_frame_cache[frame_index] };
715
716 auto result = as_normalised_float(m_processed_data[frame_index], m_float_frame_cache[frame_index]);
717 if (!result.empty())
718 m_float_frame_dirty[frame_index].store(false, std::memory_order_release);
719
720 return result;
721}
722
724{
725 if (slot_index >= m_float_frame_dirty.size())
726 return;
727 m_float_frame_dirty[slot_index].store(true, std::memory_order_release);
728}
729
731{
732 if (m_processed_data.size() > m_float_frame_dirty.size()) {
734 m_float_frame_dirty = std::vector<std::atomic<bool>>(m_processed_data.size());
735 }
736 for (auto& flag : m_float_frame_dirty)
737 flag.store(true, std::memory_order_release);
738}
739
740} // namespace MayaFlux::Kakshya
#define MF_WARN(comp, ctx,...)
uint32_t width
Definition Decoder.cpp:66
const std::vector< float > * pixels
Definition Decoder.cpp:65
size_t count
std::shared_ptr< Core::VKImage > output
float offset
uint32_t height
Type-erased accessor for NDData with semantic view construction.
std::vector< uint64_t > get_remaining_frames() const override
Get the number of remaining frames from the current position, per channel.
std::shared_ptr< DataProcessor > m_default_processor
void mark_dimension_consumed(uint32_t dimension_index, uint32_t reader_id) override
Mark a dimension as consumed for the current processing cycle.
uint32_t m_refill_threshold
Trigger refill when (m_cache_head - read_position) drops below this.
std::shared_ptr< DataProcessingChain > get_processing_chain() override
Get the current processing chain for this container.
std::shared_ptr< DataProcessingChain > m_processing_chain
std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> m_state_callback
void set_region_data(const Region &region, const std::vector< DataVariant > &data) override
Set data for a specific region.
uint64_t peek_sequential(std::span< double > output, uint64_t count, uint64_t offset) const override
Peek at data without advancing the read position.
const std::vector< uint64_t > & get_read_position() const override
Get the current read position.
uint8_t * mutable_slot_ptr(uint64_t frame_index)
Mutable pointer into m_data[0] for the decode thread to write into.
void get_value_impl(const std::vector< uint64_t > &coords, void *out, const std::type_info &type) const override
Type-erased single-element read.
bool is_at_end() const override
Check if read position has reached the end of the stream.
void unregister_dimension_reader(uint32_t dimension_index) override
Unregister a reader for a specific dimension.
std::vector< DataVariant > get_segments_data(const std::vector< RegionSegment > &segment) const override
Get data for multiple region segments efficiently.
std::vector< std::atomic< uint64_t > > m_slot_frame
bool is_ready() const override
Check if the stream is ready for reading.
DataAccess channel_data(size_t channel) override
Get channel data with semantic interpretation.
void get_frames_typed(std::span< uint8_t > output, uint64_t start_frame, uint64_t num_frames) const
void get_frames_impl(void *output, size_t count, uint64_t start_frame, uint64_t num_frames, const std::type_info &type) const override
Implementation-specific method to retrieve multiple frames.
std::vector< uint64_t > linear_index_to_coordinates(uint64_t linear_index) const override
Convert linear index to coordinates based on current memory layout.
std::vector< DataDimension > get_dimensions() const override
Get the dimensions describing the structure of the data.
RegionGroup get_region_group(const std::string &name) const override
Get a region group by name.
ProcessingState get_processing_state() const override
Get the current processing state of the container.
uint64_t read_sequential(std::span< double > output, uint64_t count) override
Read data sequentially from the current position.
uint64_t get_num_frames() const override
Get the number of frames in the primary (temporal) dimension.
void update_read_position_for_channel(size_t channel, uint64_t frame) override
Update the read position for a specific channel.
void create_default_processor() override
Create and configure a default processor for this container.
uint32_t register_dimension_reader(uint32_t dimension_index) override
Register a reader for a specific dimension.
Memory::LockFreeQueue< uint64_t, READY_QUEUE_CAPACITY > m_ready_queue
std::vector< DataAccess > all_channel_data() override
Get all channel data as accessors.
bool is_frame_available(uint64_t frame_index) const
Check if a frame is currently valid in the ring.
void unregister_state_change_callback() override
Unregister the state change callback, if any.
void add_region_group(const RegionGroup &group) override
Add a named group of regions to the container.
std::vector< std::atomic< bool > > m_float_frame_dirty
void set_value_impl(const std::vector< uint64_t > &coords, const void *in, const std::type_info &type) override
Type-erased single-element write.
std::vector< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
std::vector< std::vector< float > > m_float_frame_cache
void clear() override
Clear all data in the container.
std::vector< DataVariant > get_region_data(const Region &region) const override
Get data for a specific region.
uint64_t get_total_elements() const override
Get the total number of elements in the container.
bool has_data() const override
Check if the container currently holds any data.
VideoStreamContainer(uint32_t width=0, uint32_t height=0, uint32_t channels=4, double frame_rate=0.0)
Construct a VideoStreamContainer with specified parameters.
uint64_t get_temporal_rate() const override
Get the temporal rate (e.g., sample rate, frame rate) of the stream.
uint64_t time_to_position(double time) const override
Convert from time (seconds) to position units (e.g., frame/sample index).
const void * get_raw_data() const override
Get a raw pointer to the underlying data storage.
std::span< const float > processed_frame_as_float(uint64_t frame_index=0) const
Processed frame at frame_index as a normalised float span.
void load_region(const Region &region) override
Load a region into memory.
std::shared_ptr< DataProcessor > get_default_processor() const override
Get the current default data processor.
void remove_region_group(const std::string &name) override
Remove a region group by name.
void reset_read_position() override
Reset read position to the beginning of the stream.
std::span< const uint8_t > get_frame_pixels(uint64_t frame_index) const
Get raw pixel data for a single frame as uint8_t span.
std::unordered_map< std::string, RegionGroup > m_region_groups
void advance_cache_head(uint64_t frame_index)
Advance the container's view of how many frames have been decoded.
void setup_ring(uint64_t total_frames, uint32_t ring_capacity, uint32_t width, uint32_t height, uint32_t channels, double frame_rate, uint32_t refill_threshold, uint64_t reader_id=0)
Allocate m_data[0] as a ring of ring_capacity frames.
void invalidate_float_frame_cache(uint32_t slot_index=0)
void set_looping(bool enable) override
Enable or disable looping behavior for the stream.
double position_to_time(uint64_t position) const override
Convert from position units (e.g., frame/sample index) to time (seconds).
void notify_state_change(ProcessingState new_state)
uint64_t get_frame_size() const override
Get the number of elements that constitute one "frame".
void set_loop_region(const Region &region) override
Set the loop region using a Region.
void unload_region(const Region &region) override
Unload a region from memory.
std::atomic< uint64_t > m_cache_head
Highest frame index committed by the decode thread.
uint32_t slot_for(uint64_t frame_index) const
void set_default_processor(const std::shared_ptr< DataProcessor > &processor) override
Set the default data processor for this container.
void mark_ready_for_processing(bool ready) override
Mark the container as ready or not ready for processing.
size_t get_frame_byte_size() const
Get the total byte size of one frame (width * height * channels).
void register_state_change_callback(std::function< void(const std::shared_ptr< SignalSourceContainer > &, ProcessingState)> callback) override
Register a callback to be invoked on processing state changes.
void process_default() override
Process the container's data using the default processor.
void invalidate_ring()
Invalidate all ring slots.
void advance_read_position(const std::vector< uint64_t > &frames) override
Advance the read position by a specified amount.
uint64_t coordinates_to_linear_index(const std::vector< uint64_t > &coordinates) const override
Convert coordinates to linear index based on current memory layout.
void set_read_position(const std::vector< uint64_t > &position) override
Set the current read position in the primary temporal dimension per channel.
bool is_ready_for_processing() const override
Check if the container is ready for processing.
bool has_active_readers() const override
Check if any dimensions currently have active readers.
bool is_region_loaded(const Region &region) const override
Check if a region is loaded in memory.
void commit_frame(uint64_t frame_index)
Publish a decoded frame.
std::unordered_map< std::string, RegionGroup > get_all_region_groups() const override
Get all region groups in the container.
void update_processing_state(ProcessingState new_state) override
Update the processing state of the container.
Region get_loop_region() const override
Get the current loop region.
bool all_dimensions_consumed() const override
Check if all active dimensions have been consumed in this cycle.
std::atomic< ProcessingState > m_processing_state
void set_memory_layout(MemoryLayout layout) override
Set the memory layout for this container.
Registry::Service::IOService * m_io_service
bool is_looping() const override
Check if looping is enabled for the stream.
std::span< const uint8_t > get_frame_typed(uint64_t frame_index) const
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:136
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
ProcessingState
Represents the current processing lifecycle state of a container.
@ READY
Container has data loaded and is ready for processing.
@ IDLE
Container is inactive with no data or not ready for processing.
@ PROCESSING
Container is actively being processed.
@ PROCESSED
Container has completed processing and results are available.
uint64_t coordinates_to_linear(const std::vector< uint64_t > &coords, const std::vector< DataDimension > &dimensions)
Convert N-dimensional coordinates to a linear index for interleaved data.
Definition CoordUtils.cpp:8
std::span< const float > as_normalised_float(const DataVariant &variant, std::vector< float > &storage)
Extract a DataVariant holding pixel data as a normalised float span.
Definition DataUtils.cpp:61
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:102
@ VIDEO_COLOR
4D video (time + 2D + color)
std::vector< uint64_t > linear_to_coordinates(uint64_t index, const std::vector< DataDimension > &dimensions)
Convert a linear index to N-dimensional coordinates for interleaved data.
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:65
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
std::shared_ptr< T > store(std::shared_ptr< T > obj)
Transfer ownership of an existing object to the persistent store for process lifetime.
Definition Persist.hpp:28
static ContainerDataStructure image_interleaved()
Create structure for interleaved image data.
static std::vector< DataDimension > create_dimensions(DataModality modality, const std::vector< uint64_t > &shape, MemoryLayout layout=MemoryLayout::ROW_MAJOR)
Create dimension descriptors for a data modality.
Definition NDData.cpp:136
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
Represents a point or span in N-dimensional space.
Definition Region.hpp:73
std::function< void(uint64_t reader_id)> request_decode
Request the identified reader to decode the next batch of frames.
Definition IOService.hpp:31
Backend IO streaming service interface.
Definition IOService.hpp:18