32 , m_frame_rate(frame_rate)
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");
62 static_cast<uint64_t>(m_height),
63 static_cast<uint64_t>(m_width),
64 static_cast<uint64_t>(m_channels) },
71 if (element_size == 2)
72 return typeid(uint16_t);
73 if (element_size == 4)
75 return typeid(uint8_t);
109 return it->value_range;
119 uint32_t ring_capacity,
124 uint32_t refill_threshold,
153 auto&
pixels =
m_data[0].emplace<std::vector<uint8_t>>();
154 pixels.resize(frame_bytes * ring_capacity, 0);
156 m_slot_frame = std::vector<std::atomic<uint64_t>>(ring_capacity);
158 sf.store(UINT64_MAX, std::memory_order_relaxed);
169 flag.store(
true, std::memory_order_relaxed);
190 if (
offset + frame_bytes > bytes)
210 sf.store(UINT64_MAX, std::memory_order_relaxed);
213 std::atomic_thread_fence(std::memory_order_release);
245 std::span<const uint8_t> result;
254 const size_t offset = frame_index * frame_bytes;
255 if (
offset + frame_bytes > bytes)
263 const uint32_t slot =
slot_for(frame_index);
264 if (
m_slot_frame[slot].load(std::memory_order_acquire) == frame_index) {
272 const size_t offset =
static_cast<size_t>(slot) * frame_bytes;
273 if (
offset + frame_bytes > bytes)
298 std::optional<std::vector<DataVariant>> result;
304 if (!
ptr || bytes == 0)
310 if (element_size == 2) {
311 const std::span<const uint16_t> src {
312 reinterpret_cast<const uint16_t*
>(
ptr), bytes /
sizeof(uint16_t)
315 }
else if (element_size == 4) {
316 const std::span<const float> src {
317 reinterpret_cast<const float*
>(
ptr), bytes /
sizeof(
float)
321 const std::span<const uint8_t> src {
ptr, bytes };
324 }
catch (
const std::exception& e) {
326 "VideoStreamContainer::get_region_data extraction failed: {}", e.what());
330 return result.value_or(std::vector<DataVariant> {});
336 "VideoStreamContainer::set_region_data — write path not yet implemented");
341 std::optional<std::vector<DataVariant>> result;
345 return result.value_or(std::vector<DataVariant> {});
350 std::optional<std::vector<DataVariant>> result;
354 return result.value_or(std::vector<DataVariant> {});
366 std::optional<RegionGroup> result;
371 return result.value_or(empty);
376 std::optional<std::unordered_map<std::string, RegionGroup>> result;
380 return result.value_or(std::unordered_map<std::string, RegionGroup> {});
399 if (!position.empty())
410 const uint64_t head =
m_cache_head.load(std::memory_order_acquire);
411 const uint64_t buffered = (head > frame) ? (head - frame) : 0;
413 if (buffered < m_refill_threshold && m_io_service->request_decode)
419 thread_local std::vector<uint64_t> pos(1);
480 std::ranges::fill(
output, 0.0);
489 std::ranges::fill(
output, 0.0);
501 std::ranges::for_each(
m_data, [](
auto& v) {
502 std::visit([](
auto& vec) { vec.clear(); }, v);
520 return bytes > 0 ?
static_cast<const void*
>(
ptr) :
nullptr;
532 result = std::visit([](
const auto& vec) {
return !vec.empty(); },
m_data[0]);
544 if (old != new_state)
557 std::function<
void(
const std::shared_ptr<SignalSourceContainer>&,
ProcessingState)> callback)
571 uint64_t num_frames,
const std::type_info&
type)
const
578 if (
type ==
typeid(uint8_t) && element_size == 1) {
580 start_frame, num_frames);
583 if (
type ==
typeid(uint16_t) && element_size == 2) {
585 start_frame, num_frames);
588 if (
type ==
typeid(
float) && element_size == 4) {
590 start_frame, num_frames);
594 error<std::runtime_error>(
596 std::source_location::current(),
597 "VideoStreamContainer::get_frames_impl: requested type does not match storage");
604 return { std::span<const uint8_t> {} };
609 if (element_size == 2) {
610 return { std::span<const uint16_t>(
611 reinterpret_cast<const uint16_t*
>(bytes.data()), elements) };
613 if (element_size == 4) {
614 return { std::span<const float>(
615 reinterpret_cast<const float*
>(bytes.data()), elements) };
617 return { std::span<const uint8_t>(bytes.data(), elements) };
622 uint64_t start_frame, uint64_t num_frames)
const
625 const size_t required =
static_cast<size_t>(num_frames) * elements_per_frame;
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 ({} < {})",
638 for (uint64_t i = 0; i < num_frames; ++i) {
640 if (bytes.size() < frame_bytes)
642 std::memcpy(
output.data() + i * elements_per_frame, bytes.data(), frame_bytes);
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;
670 auto processor = std::make_shared<FrameAccessProcessor>();
688 old->on_detach(shared_from_this());
690 processor->on_attach(shared_from_this());
744 "VideoStreamContainer stores interleaved pixels; channel_data returns the full surface");
747 static DataVariant empty_variant = std::vector<uint8_t>();
762 const std::vector<uint64_t>& coords,
void* out,
const std::type_info&
type)
const
764 if (coords.size() < 4 ||
m_data.empty())
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];
777 &&
m_slot_frame[slot].load(std::memory_order_acquire) != frame)
784 if (
type ==
typeid(
float)) {
785 *
static_cast<float*
>(out) =
static_cast<float>(
789 if (
type ==
typeid(
double)) {
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];
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];
805 const std::vector<uint64_t>& coords,
const void* in,
const std::type_info&
type)
807 if (coords.size() < 4 ||
m_data.empty())
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];
823 if (
type ==
typeid(
float)) {
825 static_cast<double>(*
static_cast<const float*
>(in)));
828 if (
type ==
typeid(
double)) {
830 *
static_cast<const double*
>(in));
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);
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);
851 const size_t new_size = frame_index + 1;
852 auto new_dirty = std::vector<std::atomic<bool>>(new_size);
854 for (
size_t i = 0; i < old_size; ++i) {
856 std::memory_order_relaxed);
859 for (
size_t i = old_size; i < new_size; ++i)
860 new_dirty[i].
store(
true, std::memory_order_relaxed);
889 flag.store(
true, std::memory_order_release);
#define MF_WARN(comp, ctx,...)
const std::vector< float > * pixels
vk::PhysicalDeviceType type
std::shared_ptr< Core::VKImage > output
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
std::vector< DataVariant > m_processed_data
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 ®ion, 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.
std::atomic< uint32_t > m_consumed_readers
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::atomic< uint32_t > m_registered_readers
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 ®ion) 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 ®ion) 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_float_frame_cache()
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.
Memory::Seqlock m_cb_lock
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.
Memory::Seqlock m_data_lock
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 ®ion) override
Set the loop region using a Region.
void unload_region(const Region ®ion) override
Unload a region from memory.
std::atomic< uint64_t > m_cache_head
Highest frame index committed by the decode thread.
std::vector< DataVariant > m_data
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.
Portal::Graphics::ImageFormat m_format
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.
ContainerDataStructure m_structure
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.
uint64_t m_total_source_frames
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.
std::atomic< uint64_t > m_read_position
bool has_active_readers() const override
Check if any dimensions currently have active readers.
bool is_region_loaded(const Region ®ion) 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.
Memory::Seqlock m_region_lock
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.
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.
typename detail::span_const_from_vector_variant< DataVariant >::type DataSpanVariant
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.
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.
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.
@ 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.
@ 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.
static constexpr DomainSpec Graphics
Domain constant for Graphics domain.
std::vector< DataDimension > dimensions
static ContainerDataStructure image_interleaved()
Create structure for interleaved image data.
MemoryLayout memory_layout
@ 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.
Minimal dimension descriptor focusing on structure only.
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.
std::function< void(uint64_t reader_id)> request_decode
Request the identified reader to decode the next batch of frames.
Backend IO streaming service interface.