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
16
17namespace MayaFlux::Kakshya {
18
19// =========================================================================
20// Construction
21// =========================================================================
22
24 uint32_t height,
26 double frame_rate)
27 : m_width(width)
28 , m_height(height)
29 , m_channels(Portal::Graphics::TextureLoom::get_channel_count(format))
30 , m_bpp(Portal::Graphics::TextureLoom::get_bytes_per_pixel(format))
31 , m_format(format)
32 , m_frame_rate(frame_rate)
33{
34 if (!format_has_variant_storage(format)) {
35 error<std::invalid_argument>(
38 std::source_location::current(),
39 "VideoStreamContainer: format has no DataVariant element type "
40 "(packed depth). Use DEPTH16 or DEPTH32F for CPU-resident range data");
41 }
42
43 m_processing_chain = std::make_shared<DataProcessingChain>();
48
49 if (width > 0 && height > 0)
51}
52
53// =========================================================================
54// Dimensions
55// =========================================================================
56
58{
61 { m_num_frames,
62 static_cast<uint64_t>(m_height),
63 static_cast<uint64_t>(m_width),
64 static_cast<uint64_t>(m_channels) },
66}
67
69{
70 const size_t element_size = storage_element_size(m_format);
71 if (element_size == 2)
72 return typeid(uint16_t);
73 if (element_size == 4)
74 return typeid(float);
75 return typeid(uint8_t);
76}
77
78std::vector<DataDimension> VideoStreamContainer::get_dimensions() const
79{
81}
82
87
89{
90 return static_cast<uint64_t>(m_width) * m_height * m_channels;
91}
92
94{
95 return m_num_frames;
96}
97
102
103std::optional<DataDimension::ValueRange> VideoStreamContainer::component_range() const
104{
106 auto it = std::ranges::find_if(m_structure.dimensions,
107 [role](const DataDimension& d) { return d.role == role; });
108 if (it != m_structure.dimensions.end())
109 return it->value_range;
110 }
111 return std::nullopt;
112}
113
114// =========================================================================
115// Ring buffer setup
116// =========================================================================
117
118void VideoStreamContainer::setup_ring(uint64_t total_frames,
119 uint32_t ring_capacity,
120 uint32_t width,
121 uint32_t height,
123 double frame_rate,
124 uint32_t refill_threshold,
125 uint64_t reader_id)
126{
127 {
129
130 m_width = width;
132 m_format = format;
138
139 m_frame_rate = frame_rate;
140 m_total_source_frames = total_frames;
141 m_ring_capacity = ring_capacity;
142 m_num_frames = total_frames;
143 m_cache_head.store(0, std::memory_order_relaxed);
144 m_refill_threshold = refill_threshold;
145 m_io_reader_id = reader_id;
146
149
150 const size_t frame_bytes = get_frame_byte_size();
151
152 m_data.resize(1);
153 auto& pixels = m_data[0].emplace<std::vector<uint8_t>>();
154 pixels.resize(frame_bytes * ring_capacity, 0);
155
156 m_slot_frame = std::vector<std::atomic<uint64_t>>(ring_capacity);
157 for (auto& sf : m_slot_frame)
158 sf.store(UINT64_MAX, std::memory_order_relaxed);
159
160 m_ready_queue.reset();
162 }
163
164 if (m_float_frame_cache.size() != ring_capacity) {
165 m_float_frame_cache.resize(ring_capacity);
166 m_float_frame_dirty = std::vector<std::atomic<bool>>(ring_capacity);
167
168 for (auto& flag : m_float_frame_dirty)
169 flag.store(true, std::memory_order_relaxed);
170 }
171
173}
174
175// =========================================================================
176// Ring write API
177// =========================================================================
178
179uint8_t* VideoStreamContainer::mutable_slot_ptr(uint64_t frame_index)
180{
181 if (m_ring_capacity == 0 || frame_index >= m_total_source_frames || m_data.empty())
182 return nullptr;
183
184 auto [ptr, bytes] = variant_bytes_mutable(m_data[0]);
185 if (!ptr)
186 return nullptr;
187
188 const size_t frame_bytes = get_frame_byte_size();
189 const size_t offset = slot_for(frame_index) * frame_bytes;
190 if (offset + frame_bytes > bytes)
191 return nullptr;
192
193 return ptr + offset;
194}
195
196void VideoStreamContainer::commit_frame(uint64_t frame_index)
197{
198 if (m_ring_capacity == 0)
199 return;
200
201 m_slot_frame[slot_for(frame_index)].store(frame_index, std::memory_order_release);
202 (void)m_ready_queue.push(frame_index);
203
204 advance_cache_head(frame_index);
205}
206
208{
209 for (auto& sf : m_slot_frame)
210 sf.store(UINT64_MAX, std::memory_order_relaxed);
211
212 m_ready_queue.reset();
213 std::atomic_thread_fence(std::memory_order_release);
214}
215
216bool VideoStreamContainer::is_frame_available(uint64_t frame_index) const
217{
218 if (m_ring_capacity == 0)
219 return false;
220
221 return m_slot_frame[slot_for(frame_index)].load(std::memory_order_acquire) == frame_index;
222}
223
224// =========================================================================
225// Frame access
226// =========================================================================
227
229{
230 return static_cast<size_t>(m_width) * m_height * m_bpp;
231}
232
234{
235 return static_cast<size_t>(m_width) * m_height * m_channels;
236}
237
238std::span<const uint8_t> VideoStreamContainer::get_frame_pixels(uint64_t frame_index) const
239{
240 const size_t frame_bytes = get_frame_byte_size();
241 if (frame_bytes == 0 || frame_index >= m_num_frames)
242 return {};
243
244 if (m_ring_capacity == 0) {
245 std::span<const uint8_t> result;
246 seqlock_read_void(m_data_lock, 8, [&] {
247 if (m_data.empty())
248 return;
249
250 auto [ptr, bytes] = variant_bytes(m_data[0]);
251 if (!ptr)
252 return;
253
254 const size_t offset = frame_index * frame_bytes;
255 if (offset + frame_bytes > bytes)
256 return;
257
258 result = { ptr + offset, frame_bytes };
259 });
260 return result;
261 }
262
263 const uint32_t slot = slot_for(frame_index);
264 if (m_slot_frame[slot].load(std::memory_order_acquire) == frame_index) {
265 if (m_data.empty())
266 return {};
267
268 auto [ptr, bytes] = variant_bytes(m_data[0]);
269 if (!ptr)
270 return {};
271
272 const size_t offset = static_cast<size_t>(slot) * frame_bytes;
273 if (offset + frame_bytes > bytes)
274 return {};
275
276 return { ptr + offset, frame_bytes };
277 }
278
279 return {};
280}
281
282uint64_t VideoStreamContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
283{
284 return coordinates_to_linear(coordinates, m_structure.dimensions);
285}
286
287std::vector<uint64_t> VideoStreamContainer::linear_index_to_coordinates(uint64_t linear_index) const
288{
289 return linear_to_coordinates(linear_index, m_structure.dimensions);
290}
291
292// =========================================================================
293// Region management
294// =========================================================================
295
296std::vector<DataVariant> VideoStreamContainer::get_region_data(const Region& region) const
297{
298 std::optional<std::vector<DataVariant>> result;
299 seqlock_read_void(m_data_lock, 8, [&] {
300 if (m_data.empty())
301 return;
302
303 auto [ptr, bytes] = variant_bytes(m_data[0]);
304 if (!ptr || bytes == 0)
305 return;
306
307 const size_t element_size = storage_element_size(m_format);
308
309 try {
310 if (element_size == 2) {
311 const std::span<const uint16_t> src {
312 reinterpret_cast<const uint16_t*>(ptr), bytes / sizeof(uint16_t)
313 };
314 result = { extract_nd_region<uint16_t>(src, region, m_structure.dimensions) };
315 } else if (element_size == 4) {
316 const std::span<const float> src {
317 reinterpret_cast<const float*>(ptr), bytes / sizeof(float)
318 };
319 result = { extract_nd_region<float>(src, region, m_structure.dimensions) };
320 } else {
321 const std::span<const uint8_t> src { ptr, bytes };
322 result = { extract_nd_region<uint8_t>(src, region, m_structure.dimensions) };
323 }
324 } catch (const std::exception& e) {
326 "VideoStreamContainer::get_region_data extraction failed: {}", e.what());
327 }
328 });
329
330 return result.value_or(std::vector<DataVariant> {});
331}
332
333void VideoStreamContainer::set_region_data(const Region& /*region*/, const std::vector<DataVariant>& /*data*/)
334{
336 "VideoStreamContainer::set_region_data — write path not yet implemented");
337}
338
339std::vector<DataVariant> VideoStreamContainer::get_region_group_data(const RegionGroup& /*group*/) const
340{
341 std::optional<std::vector<DataVariant>> result;
342 seqlock_read_void(m_data_lock, 8, [&] {
343 result = m_data;
344 });
345 return result.value_or(std::vector<DataVariant> {});
346}
347
348std::vector<DataVariant> VideoStreamContainer::get_segments_data(const std::vector<RegionSegment>& /*segments*/) const
349{
350 std::optional<std::vector<DataVariant>> result;
351 seqlock_read_void(m_data_lock, 8, [&] {
352 result = m_data;
353 });
354 return result.value_or(std::vector<DataVariant> {});
355}
356
362
364{
365 static const RegionGroup empty;
366 std::optional<RegionGroup> result;
367 seqlock_read_void(m_region_lock, 8, [&] {
368 auto it = m_region_groups.find(name);
369 result = (it != m_region_groups.end()) ? it->second : empty;
370 });
371 return result.value_or(empty);
372}
373
374std::unordered_map<std::string, RegionGroup> VideoStreamContainer::get_all_region_groups() const
375{
376 std::optional<std::unordered_map<std::string, RegionGroup>> result;
377 seqlock_read_void(m_region_lock, 8, [&] {
378 result = m_region_groups;
379 });
380 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
381}
382
388
389bool VideoStreamContainer::is_region_loaded(const Region& /*region*/) const { return true; }
390void VideoStreamContainer::load_region(const Region& /*region*/) { }
392
393// =========================================================================
394// Read position and looping
395// =========================================================================
396
397void VideoStreamContainer::set_read_position(const std::vector<uint64_t>& position)
398{
399 if (!position.empty())
400 m_read_position.store(position[0]);
401}
402
403void VideoStreamContainer::update_read_position_for_channel(size_t /*channel*/, uint64_t frame)
404{
405 m_read_position.store(frame);
406
408 return;
409
410 const uint64_t head = m_cache_head.load(std::memory_order_acquire);
411 const uint64_t buffered = (head > frame) ? (head - frame) : 0;
412
413 if (buffered < m_refill_threshold && m_io_service->request_decode)
415}
416
417const std::vector<uint64_t>& VideoStreamContainer::get_read_position() const
418{
419 thread_local std::vector<uint64_t> pos(1);
420 pos[0] = m_read_position.load();
421 return pos;
422}
423
424void VideoStreamContainer::advance_read_position(const std::vector<uint64_t>& frames)
425{
426 if (!frames.empty())
427 m_read_position.fetch_add(frames[0]);
428}
429
431{
432 if (is_looping())
433 return false;
434
435 uint64_t total = (m_ring_capacity > 0) ? m_total_source_frames : m_num_frames;
436 return total == 0 || m_read_position.load() >= total;
437}
438
443
445{
446 return static_cast<uint64_t>(m_frame_rate);
447}
448
449uint64_t VideoStreamContainer::time_to_position(double time) const
450{
451 if (m_frame_rate <= 0.0)
452 return 0;
453 return static_cast<uint64_t>(time * m_frame_rate);
454}
455
456double VideoStreamContainer::position_to_time(uint64_t position) const
457{
458 if (m_frame_rate <= 0.0)
459 return 0.0;
460 return static_cast<double>(position) / m_frame_rate;
461}
462
466
468{
469 return has_data() && m_num_frames > 0;
470}
471
473{
474 uint64_t pos = m_read_position.load();
475 return { pos < m_num_frames ? m_num_frames - pos : 0 };
476}
477
478uint64_t VideoStreamContainer::read_sequential(std::span<double> output, uint64_t count)
479{
480 std::ranges::fill(output, 0.0);
481 uint64_t pos = m_read_position.load();
482 uint64_t advanced = std::min(count, m_num_frames > pos ? m_num_frames - pos : 0UL);
483 m_read_position.store(pos + advanced);
484 return advanced;
485}
486
487uint64_t VideoStreamContainer::peek_sequential(std::span<double> output, uint64_t /*count*/, uint64_t /*offset*/) const
488{
489 std::ranges::fill(output, 0.0);
490 return 0;
491}
492
493// =========================================================================
494// Clear and raw access
495// =========================================================================
496
498{
499 {
501 std::ranges::for_each(m_data, [](auto& v) {
502 std::visit([](auto& vec) { vec.clear(); }, v);
503 });
504 m_num_frames = 0;
505 m_read_position.store(0);
507 }
509}
510
512{
513 if (m_ring_capacity > 0)
514 return nullptr;
515
516 if (m_data.empty())
517 return nullptr;
518
519 auto [ptr, bytes] = variant_bytes(m_data[0]);
520 return bytes > 0 ? static_cast<const void*>(ptr) : nullptr;
521}
522
524{
525 if (m_ring_capacity > 0)
526 return m_total_source_frames > 0;
527
528 bool result = false;
529 seqlock_read_void(m_data_lock, 8, [&] {
530 if (m_data.empty())
531 return;
532 result = std::visit([](const auto& vec) { return !vec.empty(); }, m_data[0]);
533 });
534 return result;
535}
536
537// =========================================================================
538// Processing state
539// =========================================================================
540
542{
543 ProcessingState old = m_processing_state.exchange(new_state);
544 if (old != new_state)
545 notify_state_change(new_state);
546}
547
549{
550 seqlock_read_void(m_cb_lock, 8, [&] {
552 m_state_callback(shared_from_this(), new_state);
553 });
554}
555
557 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback)
558{
560 m_state_callback = std::move(callback);
561}
562
568
570 void* output, size_t count, uint64_t start_frame,
571 uint64_t num_frames, const std::type_info& type) const
572{
573 if (!output)
574 return;
575
576 const size_t element_size = storage_element_size(m_format);
577
578 if (type == typeid(uint8_t) && element_size == 1) {
579 get_frames_typed_as(std::span<uint8_t>(static_cast<uint8_t*>(output), count),
580 start_frame, num_frames);
581 return;
582 }
583 if (type == typeid(uint16_t) && element_size == 2) {
584 get_frames_typed_as(std::span<uint16_t>(static_cast<uint16_t*>(output), count),
585 start_frame, num_frames);
586 return;
587 }
588 if (type == typeid(float) && element_size == 4) {
589 get_frames_typed_as(std::span<float>(static_cast<float*>(output), count),
590 start_frame, num_frames);
591 return;
592 }
593
594 error<std::runtime_error>(
596 std::source_location::current(),
597 "VideoStreamContainer::get_frames_impl: requested type does not match storage");
598}
599
601{
602 auto bytes = get_frame_pixels(frame_index);
603 if (bytes.empty())
604 return { std::span<const uint8_t> {} };
605
606 const size_t elements = get_frame_element_count();
607 const size_t element_size = storage_element_size(m_format);
608
609 if (element_size == 2) {
610 return { std::span<const uint16_t>(
611 reinterpret_cast<const uint16_t*>(bytes.data()), elements) };
612 }
613 if (element_size == 4) {
614 return { std::span<const float>(
615 reinterpret_cast<const float*>(bytes.data()), elements) };
616 }
617 return { std::span<const uint8_t>(bytes.data(), elements) };
618}
619
620template <typename T>
622 uint64_t start_frame, uint64_t num_frames) const
623{
624 const size_t elements_per_frame = get_frame_element_count();
625 const size_t required = static_cast<size_t>(num_frames) * elements_per_frame;
626
627 if (output.size() < required) {
628 error<std::runtime_error>(
631 std::source_location::current(),
632 "VideoStreamContainer::get_frames_typed_as: output buffer too small ({} < {})",
633 output.size(), required);
634 }
635
636 const size_t frame_bytes = get_frame_byte_size();
637
638 for (uint64_t i = 0; i < num_frames; ++i) {
639 auto bytes = get_frame_pixels(start_frame + i);
640 if (bytes.size() < frame_bytes)
641 continue;
642 std::memcpy(output.data() + i * elements_per_frame, bytes.data(), frame_bytes);
643 }
644}
645
646template void VideoStreamContainer::get_frames_typed_as<uint8_t>(
647 std::span<uint8_t>, uint64_t, uint64_t) const;
648template void VideoStreamContainer::get_frames_typed_as<uint16_t>(
649 std::span<uint16_t>, uint64_t, uint64_t) const;
650template void VideoStreamContainer::get_frames_typed_as<float>(
651 std::span<float>, uint64_t, uint64_t) const;
652
654{
655 auto state = get_processing_state();
656 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
657}
658
667
669{
670 auto processor = std::make_shared<FrameAccessProcessor>();
671 set_default_processor(processor);
672}
673
682
683void VideoStreamContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
684{
685 auto old = m_default_processor;
686 m_default_processor = processor;
687 if (old)
688 old->on_detach(shared_from_this());
689 if (processor)
690 processor->on_attach(shared_from_this());
691}
692
693std::shared_ptr<DataProcessor> VideoStreamContainer::get_default_processor() const
694{
695 return m_default_processor;
696}
697
698std::shared_ptr<DataProcessingChain> VideoStreamContainer::get_processing_chain()
699{
701 m_processing_chain = std::make_shared<DataProcessingChain>();
702
703 return m_processing_chain;
704}
705
706// =========================================================================
707// Reader tracking
708// =========================================================================
709
710uint32_t VideoStreamContainer::register_dimension_reader(uint32_t /*dimension_index*/)
711{
712 return m_registered_readers.fetch_add(1, std::memory_order_relaxed);
713}
714
716{
717 if (m_registered_readers.load(std::memory_order_relaxed) > 0)
718 m_registered_readers.fetch_sub(1, std::memory_order_relaxed);
719}
720
722{
723 return m_registered_readers.load(std::memory_order_acquire) > 0;
724}
725
726void VideoStreamContainer::mark_dimension_consumed(uint32_t /*dimension_index*/, uint32_t /*reader_id*/)
727{
728 m_consumed_readers.fetch_add(1, std::memory_order_release);
729}
730
732{
733 return m_consumed_readers.load(std::memory_order_acquire)
734 >= m_registered_readers.load(std::memory_order_acquire);
735}
736
737// =========================================================================
738// Data access
739// =========================================================================
740
742{
744 "VideoStreamContainer stores interleaved pixels; channel_data returns the full surface");
745
746 if (m_data.empty()) {
747 static DataVariant empty_variant = std::vector<uint8_t>();
748 return { empty_variant, m_structure.dimensions, m_structure.modality };
749 }
750
752}
753
755{
756 if (m_data.empty())
757 return {};
759}
760
762 const std::vector<uint64_t>& coords, void* out, const std::type_info& type) const
763{
764 if (coords.size() < 4 || m_data.empty())
765 return;
766
767 const uint64_t frame = coords[0];
768 const uint64_t y = coords[1];
769 const uint64_t x = coords[2];
770 const uint64_t c = coords[3];
771
772 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
773 return;
774
775 const size_t slot = (m_ring_capacity == 0) ? frame : slot_for(frame);
776 if (m_ring_capacity != 0
777 && m_slot_frame[slot].load(std::memory_order_acquire) != frame)
778 return;
779
780 const size_t idx = slot * get_frame_element_count()
781 + (y * m_width + x) * m_channels
782 + c;
783
784 if (type == typeid(float)) {
785 *static_cast<float*>(out) = static_cast<float>(
787 return;
788 }
789 if (type == typeid(double)) {
790 *static_cast<double*>(out) = read_normalized_at(m_data[0], m_format, component_range(), idx);
791 return;
792 }
793 if (type == typeid(uint8_t) && storage_element_size(m_format) == 1) {
794 if (const auto* v = std::get_if<std::vector<uint8_t>>(&m_data[0]); v && idx < v->size())
795 *static_cast<uint8_t*>(out) = (*v)[idx];
796 return;
797 }
798 if (type == typeid(uint16_t) && storage_element_size(m_format) == 2) {
799 if (const auto* v = std::get_if<std::vector<uint16_t>>(&m_data[0]); v && idx < v->size())
800 *static_cast<uint16_t*>(out) = (*v)[idx];
801 }
802}
803
805 const std::vector<uint64_t>& coords, const void* in, const std::type_info& type)
806{
807 if (coords.size() < 4 || m_data.empty())
808 return;
809
810 const uint64_t frame = coords[0];
811 const uint64_t y = coords[1];
812 const uint64_t x = coords[2];
813 const uint64_t c = coords[3];
814
815 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
816 return;
817
818 const size_t slot = (m_ring_capacity == 0) ? frame : slot_for(frame);
819 const size_t idx = slot * get_frame_element_count()
820 + (y * m_width + x) * m_channels
821 + c;
822
823 if (type == typeid(float)) {
825 static_cast<double>(*static_cast<const float*>(in)));
826 return;
827 }
828 if (type == typeid(double)) {
830 *static_cast<const double*>(in));
831 return;
832 }
833 if (type == typeid(uint8_t) && storage_element_size(m_format) == 1) {
834 if (auto* v = std::get_if<std::vector<uint8_t>>(&m_data[0]); v && idx < v->size())
835 (*v)[idx] = *static_cast<const uint8_t*>(in);
836 return;
837 }
838 if (type == typeid(uint16_t) && storage_element_size(m_format) == 2) {
839 if (auto* v = std::get_if<std::vector<uint16_t>>(&m_data[0]); v && idx < v->size())
840 (*v)[idx] = *static_cast<const uint16_t*>(in);
841 }
842}
843
844std::span<const float> VideoStreamContainer::processed_frame_as_float(uint64_t frame_index) const
845{
846 if (frame_index >= m_processed_data.size())
847 return {};
848
849 if (frame_index >= m_float_frame_dirty.size()) {
850 const size_t old_size = m_float_frame_dirty.size();
851 const size_t new_size = frame_index + 1;
852 auto new_dirty = std::vector<std::atomic<bool>>(new_size);
853
854 for (size_t i = 0; i < old_size; ++i) {
855 new_dirty[i].store(m_float_frame_dirty[i].load(std::memory_order_relaxed),
856 std::memory_order_relaxed);
857 }
858
859 for (size_t i = old_size; i < new_size; ++i)
860 new_dirty[i].store(true, std::memory_order_relaxed);
861 m_float_frame_dirty = std::move(new_dirty);
862 m_float_frame_cache.resize(new_size);
863 }
864
865 if (!m_float_frame_dirty[frame_index].load(std::memory_order_acquire))
866 return { m_float_frame_cache[frame_index] };
867
868 auto result = as_normalised_float(m_processed_data[frame_index], m_float_frame_cache[frame_index]);
869 if (!result.empty())
870 m_float_frame_dirty[frame_index].store(false, std::memory_order_release);
871
872 return result;
873}
874
876{
877 if (slot_index >= m_float_frame_dirty.size())
878 return;
879 m_float_frame_dirty[slot_index].store(true, std::memory_order_release);
880}
881
883{
884 if (m_processed_data.size() > m_float_frame_dirty.size()) {
886 m_float_frame_dirty = std::vector<std::atomic<bool>>(m_processed_data.size());
887 }
888 for (auto& flag : m_float_frame_dirty)
889 flag.store(true, std::memory_order_release);
890}
891
892} // namespace MayaFlux::Kakshya
#define MF_WARN(comp, ctx,...)
const std::vector< float > * pixels
Definition Decoder.cpp:65
vk::PhysicalDeviceType type
Definition VKDevice.cpp:146
std::string name
Definition VKDevice.cpp:143
size_t count
const uint8_t * ptr
std::shared_ptr< Core::VKImage > output
float offset
uint32_t width
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.
VideoStreamContainer(uint32_t width=0, uint32_t height=0, Portal::Graphics::ImageFormat format=Portal::Graphics::ImageFormat::RGBA8, double frame_rate=0.0)
Construct a VideoStreamContainer with specified parameters.
std::shared_ptr< DataProcessingChain > m_processing_chain
DataSpanVariant get_frame_typed(uint64_t frame_index) const
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.
std::optional< DataDimension::ValueRange > component_range() const
Value range declared on the component dimension, if any.
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::type_index value_element_type() const override
Runtime query for the native scalar element type of this container.
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.
size_t get_frame_element_count() const
Elements in one frame: width * height * channels.
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 a byte 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 get_frames_typed_as(std::span< T > output, uint64_t start_frame, uint64_t num_frames) const
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
Total byte size of one frame: width * height * bytes_per_pixel.
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.
void setup_ring(uint64_t total_frames, uint32_t ring_capacity, uint32_t width, uint32_t height, Portal::Graphics::ImageFormat format, double frame_rate, uint32_t refill_threshold, uint64_t reader_id=0)
Allocate m_data[0] as a ring of ring_capacity frames.
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.
RAII guard that brackets a Seqlock write region.
Definition SeqLock.hpp:136
static size_t get_bytes_per_pixel(ImageFormat format)
Get bytes per pixel for a format.
static uint32_t get_channel_count(ImageFormat format)
Get the number of color channels for a given format.
Portal-level texture creation and management.
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
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
Definition NDData.hpp:657
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::pair< const uint8_t *, size_t > variant_bytes(const DataVariant &v)
Get a pointer to the raw bytes of a DataVariant and its size.
Definition DataUtils.cpp:95
void write_normalized_at(DataVariant &v, ImageFormat format, const std::optional< DataDimension::ValueRange > &range, size_t elem_index, double value)
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_DEPTH
[frames, height, width, components] - streaming range data
@ 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.
bool is_depth_format(ImageFormat format)
size_t storage_element_size(ImageFormat format)
MemoryLayout
Memory layout for multi-dimensional data.
Definition NDData.hpp:65
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
bool format_has_variant_storage(ImageFormat format)
double read_normalized_at(const DataVariant &v, ImageFormat format, const std::optional< DataDimension::ValueRange > &range, size_t elem_index)
std::pair< uint8_t *, size_t > variant_bytes_mutable(DataVariant &v)
Get a mutable pointer to the raw bytes of a DataVariant and its size.
ImageFormat
User-friendly image format enum.
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 constexpr DomainSpec Graphics
Domain constant for Graphics domain.
Definition Creator.hpp:318
static ContainerDataStructure image_interleaved()
Create structure for interleaved image data.
@ DEPTH
Distance from the observation point (depth, disparity, range)
@ CHANNEL
Parallel streams (audio channels, color channels)
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:155
Minimal dimension descriptor focusing on structure only.
Definition NDData.hpp:229
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