MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VideoStreamContainer.cpp
Go to the documentation of this file.
2
9
12
13namespace MayaFlux::Kakshya {
14
15// =========================================================================
16// Construction
17// =========================================================================
18
20 uint32_t height,
21 uint32_t channels,
22 double frame_rate)
23 : m_width(width)
24 , m_height(height)
25 , m_channels(channels)
26 , m_frame_rate(frame_rate)
27{
30
31 if (width > 0 && height > 0)
33}
34
35// =========================================================================
36// Dimensions
37// =========================================================================
38
40{
44 static_cast<uint64_t>(m_height),
45 static_cast<uint64_t>(m_width),
46 static_cast<uint64_t>(m_channels) },
48}
49
50std::vector<DataDimension> VideoStreamContainer::get_dimensions() const
51{
53}
54
59
61{
62 return static_cast<uint64_t>(m_width) * m_height * m_channels;
63}
64
66{
67 return m_num_frames;
68}
69
74
75// =========================================================================
76// Ring buffer setup
77// =========================================================================
78
79void VideoStreamContainer::setup_ring(uint64_t total_frames,
80 uint32_t ring_capacity,
81 uint32_t width,
82 uint32_t height,
83 uint32_t channels,
84 double frame_rate,
85 uint32_t refill_threshold,
86 uint64_t reader_id)
87{
88 std::unique_lock data_lock(m_data_mutex);
89
90 m_width = width;
91 m_height = height;
92 m_channels = channels;
93 m_frame_rate = frame_rate;
94 m_total_source_frames = total_frames;
95 m_ring_capacity = ring_capacity;
96 m_num_frames = total_frames;
97 m_cache_head.store(0, std::memory_order_relaxed);
98 m_refill_threshold = refill_threshold;
99 m_io_reader_id = reader_id;
100
103
104 const size_t frame_bytes = get_frame_byte_size();
105
106 m_data.resize(1);
107 auto& pixels = m_data[0].emplace<std::vector<uint8_t>>();
108 pixels.resize(frame_bytes * ring_capacity, 0);
109
110 m_slot_frame = std::vector<std::atomic<uint64_t>>(ring_capacity);
111 for (auto& sf : m_slot_frame)
112 sf.store(UINT64_MAX, std::memory_order_relaxed);
113
114 m_ready_queue.reset();
115
118}
119
120// =========================================================================
121// Ring write API
122// =========================================================================
123
124uint8_t* VideoStreamContainer::mutable_slot_ptr(uint64_t frame_index)
125{
126 if (m_ring_capacity == 0 || frame_index >= m_total_source_frames)
127 return nullptr;
128
129 auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
130 if (!pixels)
131 return nullptr;
132
133 return pixels->data() + slot_for(frame_index) * get_frame_byte_size();
134}
135
136void VideoStreamContainer::commit_frame(uint64_t frame_index)
137{
138 if (m_ring_capacity == 0)
139 return;
140
141 m_slot_frame[slot_for(frame_index)].store(frame_index, std::memory_order_release);
142 (void)m_ready_queue.push(frame_index);
143
144 advance_cache_head(frame_index);
145}
146
148{
149 for (auto& sf : m_slot_frame)
150 sf.store(UINT64_MAX, std::memory_order_relaxed);
151
152 m_ready_queue.reset();
153 std::atomic_thread_fence(std::memory_order_release);
154}
155
156bool VideoStreamContainer::is_frame_available(uint64_t frame_index) const
157{
158 if (m_ring_capacity == 0)
159 return false;
160
161 return m_slot_frame[slot_for(frame_index)].load(std::memory_order_acquire) == frame_index;
162}
163
164// =========================================================================
165// Frame access
166// =========================================================================
167
169{
170 return static_cast<size_t>(m_width) * m_height * m_channels;
171}
172
173std::span<const uint8_t> VideoStreamContainer::get_frame_pixels(uint64_t frame_index) const
174{
175 const size_t frame_bytes = get_frame_byte_size();
176 if (frame_bytes == 0 || frame_index >= m_num_frames)
177 return {};
178
179 if (m_ring_capacity == 0) {
180 std::shared_lock lock(m_data_mutex);
181
182 if (m_data.empty())
183 return {};
184
185 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
186 if (!pixels)
187 return {};
188
189 const size_t offset = frame_index * frame_bytes;
190 if (offset + frame_bytes > pixels->size())
191 return {};
192
193 return { pixels->data() + offset, frame_bytes };
194 }
195
196 uint32_t slot = slot_for(frame_index);
197
198 if (m_slot_frame[slot].load(std::memory_order_acquire) == frame_index) {
199 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
200 if (!pixels)
201 return {};
202 return { pixels->data() + slot * frame_bytes, frame_bytes };
203 }
204
205 return {};
206}
207
208std::span<const double> VideoStreamContainer::get_frame(uint64_t /*frame_index*/) const
209{
210 return {};
211}
212
213void VideoStreamContainer::get_frames(std::span<double> output, uint64_t /*start_frame*/, uint64_t /*num_frames*/) const
214{
215 std::ranges::fill(output, 0.0);
216}
217
218double VideoStreamContainer::get_value_at(const std::vector<uint64_t>& coordinates) const
219{
220 if (coordinates.size() < 4 || m_data.empty())
221 return 0.0;
222
223 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
224 if (!pixels)
225 return 0.0;
226
227 const uint64_t frame = coordinates[0];
228 const uint64_t y = coordinates[1];
229 const uint64_t x = coordinates[2];
230 const uint64_t c = coordinates[3];
231
232 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
233 return 0.0;
234
235 const size_t idx = (frame * m_height * m_width * m_channels)
236 + (y * m_width * m_channels)
237 + (x * m_channels)
238 + c;
239
240 if (idx >= pixels->size())
241 return 0.0;
242
243 return static_cast<double>((*pixels)[idx]) / 255.0;
244}
245
246void VideoStreamContainer::set_value_at(const std::vector<uint64_t>& coordinates, double value)
247{
248 if (coordinates.size() < 4 || m_data.empty())
249 return;
250
251 auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
252 if (!pixels)
253 return;
254
255 const uint64_t frame = coordinates[0];
256 const uint64_t y = coordinates[1];
257 const uint64_t x = coordinates[2];
258 const uint64_t c = coordinates[3];
259
260 if (frame >= m_num_frames || y >= m_height || x >= m_width || c >= m_channels)
261 return;
262
263 const size_t idx = (frame * m_height * m_width * m_channels)
264 + (y * m_width * m_channels)
265 + (x * m_channels)
266 + c;
267
268 if (idx >= pixels->size())
269 return;
270
271 (*pixels)[idx] = static_cast<uint8_t>(std::clamp(value * 255.0, 0.0, 255.0));
272}
273
274uint64_t VideoStreamContainer::coordinates_to_linear_index(const std::vector<uint64_t>& coordinates) const
275{
276 return coordinates_to_linear(coordinates, m_structure.dimensions);
277}
278
279std::vector<uint64_t> VideoStreamContainer::linear_index_to_coordinates(uint64_t linear_index) const
280{
281 return linear_to_coordinates(linear_index, m_structure.dimensions);
282}
283
284// =========================================================================
285// Region management
286// =========================================================================
287
288std::vector<DataVariant> VideoStreamContainer::get_region_data(const Region& region) const
289{
290 std::shared_lock lock(m_data_mutex);
291
292 if (m_data.empty())
293 return {};
294
295 const auto* pixels = std::get_if<std::vector<uint8_t>>(&m_data[0]);
296 if (!pixels || pixels->empty())
297 return {};
298
299 const std::span<const uint8_t> src { pixels->data(), pixels->size() };
300
301 try {
302 return { extract_nd_region<uint8_t>(src, region, m_structure.dimensions) };
303 } catch (const std::exception& e) {
305 "VideoStreamContainer::get_region_data extraction failed — {}", e.what());
306 return {};
307 }
308}
309
310void VideoStreamContainer::set_region_data(const Region& /*region*/, const std::vector<DataVariant>& /*data*/)
311{
313 "VideoStreamContainer::set_region_data — write path not yet implemented");
314}
315
316std::vector<DataVariant> VideoStreamContainer::get_region_group_data(const RegionGroup& /*group*/) const
317{
318 std::shared_lock lock(m_data_mutex);
319 return m_data;
320}
321
322std::vector<DataVariant> VideoStreamContainer::get_segments_data(const std::vector<RegionSegment>& /*segments*/) const
323{
324 std::shared_lock lock(m_data_mutex);
325 return m_data;
326}
327
329{
330 std::lock_guard lock(m_state_mutex);
331 m_region_groups[group.name] = group;
332}
333
334const RegionGroup& VideoStreamContainer::get_region_group(const std::string& name) const
335{
336 static const RegionGroup empty;
337 std::shared_lock lock(m_data_mutex);
338 auto it = m_region_groups.find(name);
339 return it != m_region_groups.end() ? it->second : empty;
340}
341
342std::unordered_map<std::string, RegionGroup> VideoStreamContainer::get_all_region_groups() const
343{
344 std::shared_lock lock(m_data_mutex);
345 return m_region_groups;
346}
347
348void VideoStreamContainer::remove_region_group(const std::string& name)
349{
350 std::lock_guard lock(m_state_mutex);
351 m_region_groups.erase(name);
352}
353
354bool VideoStreamContainer::is_region_loaded(const Region& /*region*/) const { return true; }
355void VideoStreamContainer::load_region(const Region& /*region*/) { }
357
358// =========================================================================
359// Read position and looping
360// =========================================================================
361
362void VideoStreamContainer::set_read_position(const std::vector<uint64_t>& position)
363{
364 if (!position.empty())
365 m_read_position.store(position[0]);
366}
367
368void VideoStreamContainer::update_read_position_for_channel(size_t /*channel*/, uint64_t frame)
369{
370 m_read_position.store(frame);
371
373 return;
374
375 const uint64_t head = m_cache_head.load(std::memory_order_acquire);
376 const uint64_t buffered = (head > frame) ? (head - frame) : 0;
377
378 if (buffered < m_refill_threshold && m_io_service->request_decode)
380}
381
382const std::vector<uint64_t>& VideoStreamContainer::get_read_position() const
383{
384 thread_local std::vector<uint64_t> pos(1);
385 pos[0] = m_read_position.load();
386 return pos;
387}
388
389void VideoStreamContainer::advance_read_position(const std::vector<uint64_t>& frames)
390{
391 if (!frames.empty())
392 m_read_position.fetch_add(frames[0]);
393}
394
396{
397 if (is_looping())
398 return false;
399
400 uint64_t total = (m_ring_capacity > 0) ? m_total_source_frames : m_num_frames;
401 return total == 0 || m_read_position.load() >= total;
402}
403
408
410{
411 return static_cast<uint64_t>(m_frame_rate);
412}
413
414uint64_t VideoStreamContainer::time_to_position(double time) const
415{
416 if (m_frame_rate <= 0.0)
417 return 0;
418 return static_cast<uint64_t>(time * m_frame_rate);
419}
420
421double VideoStreamContainer::position_to_time(uint64_t position) const
422{
423 if (m_frame_rate <= 0.0)
424 return 0.0;
425 return static_cast<double>(position) / m_frame_rate;
426}
427
431
433{
434 return has_data() && m_num_frames > 0;
435}
436
438{
439 uint64_t pos = m_read_position.load();
440 return { pos < m_num_frames ? m_num_frames - pos : 0 };
441}
442
443uint64_t VideoStreamContainer::read_sequential(std::span<double> output, uint64_t count)
444{
445 std::ranges::fill(output, 0.0);
446 uint64_t pos = m_read_position.load();
447 uint64_t advanced = std::min(count, m_num_frames > pos ? m_num_frames - pos : 0UL);
448 m_read_position.store(pos + advanced);
449 return advanced;
450}
451
452uint64_t VideoStreamContainer::peek_sequential(std::span<double> output, uint64_t /*count*/, uint64_t /*offset*/) const
453{
454 std::ranges::fill(output, 0.0);
455 return 0;
456}
457
458// =========================================================================
459// Clear and raw access
460// =========================================================================
461
463{
464 std::unique_lock lock(m_data_mutex);
465
466 std::ranges::for_each(m_data, [](auto& v) {
467 std::visit([](auto& vec) { vec.clear(); }, v);
468 });
469
470 m_num_frames = 0;
471 m_read_position.store(0);
472
475}
476
478{
479 if (m_ring_capacity > 0)
480 return nullptr;
481
482 if (m_data.empty())
483 return nullptr;
484 const auto* v = std::get_if<std::vector<uint8_t>>(&m_data[0]);
485 return (v && !v->empty()) ? v->data() : nullptr;
486}
487
489{
490 if (m_ring_capacity > 0)
491 return m_total_source_frames > 0;
492
493 std::shared_lock lock(m_data_mutex);
494 if (m_data.empty())
495 return false;
496 return std::visit([](const auto& vec) { return !vec.empty(); }, m_data[0]);
497}
498
499// =========================================================================
500// Processing state
501// =========================================================================
502
504{
505 ProcessingState old = m_processing_state.exchange(new_state);
506 if (old != new_state)
507 notify_state_change(new_state);
508}
509
511{
512 std::lock_guard lock(m_state_mutex);
514 m_state_callback(shared_from_this(), new_state);
515}
516
518 std::function<void(const std::shared_ptr<SignalSourceContainer>&, ProcessingState)> callback)
519{
520 std::lock_guard lock(m_state_mutex);
521 m_state_callback = std::move(callback);
522}
523
529
531{
532 auto state = get_processing_state();
533 return has_data() && (state == ProcessingState::READY || state == ProcessingState::PROCESSED);
534}
535
544
546{
547 auto processor = std::make_shared<FrameAccessProcessor>();
548 set_default_processor(processor);
549}
550
559
560void VideoStreamContainer::set_default_processor(const std::shared_ptr<DataProcessor>& processor)
561{
562 auto old = m_default_processor;
563 m_default_processor = processor;
564 if (old)
565 old->on_detach(shared_from_this());
566 if (processor)
567 processor->on_attach(shared_from_this());
568}
569
570std::shared_ptr<DataProcessor> VideoStreamContainer::get_default_processor() const
571{
572 std::lock_guard lock(m_state_mutex);
573 return m_default_processor;
574}
575
576// =========================================================================
577// Reader tracking
578// =========================================================================
579
580uint32_t VideoStreamContainer::register_dimension_reader(uint32_t /*dimension_index*/)
581{
582 return m_registered_readers.fetch_add(1, std::memory_order_relaxed);
583}
584
586{
587 if (m_registered_readers.load(std::memory_order_relaxed) > 0)
588 m_registered_readers.fetch_sub(1, std::memory_order_relaxed);
589}
590
592{
593 return m_registered_readers.load(std::memory_order_acquire) > 0;
594}
595
596void VideoStreamContainer::mark_dimension_consumed(uint32_t /*dimension_index*/, uint32_t /*reader_id*/)
597{
598 m_consumed_readers.fetch_add(1, std::memory_order_release);
599}
600
602{
603 return m_consumed_readers.load(std::memory_order_acquire)
604 >= m_registered_readers.load(std::memory_order_acquire);
605}
606
607// =========================================================================
608// Data access
609// =========================================================================
610
612{
614 "VideoStreamContainer::channel_data — not meaningful for interleaved pixel data; returning full surface");
615
616 if (m_data.empty()) {
617 static DataVariant empty_variant = std::vector<uint8_t>();
618 return { empty_variant, m_structure.dimensions, DataModality::VIDEO_COLOR };
619 }
620
622}
623
625{
626 if (m_data.empty())
627 return {};
629}
630
631} // namespace MayaFlux::Kakshya
#define MF_WARN(comp, ctx,...)
Eigen::Index count
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::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.
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::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.
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 get_frames(std::span< double > output, uint64_t start_frame, uint64_t num_frames) const override
Get multiple frames efficiently.
double get_value_at(const std::vector< uint64_t > &coordinates) const override
Get a single value at the specified coordinates.
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.
void set_value_at(const std::vector< uint64_t > &coordinates, double value) override
Set a single value at the specified coordinates.
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< DataVariant > get_region_group_data(const RegionGroup &group) const override
Get data for multiple regions efficiently.
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.
void load_region(const Region &region) override
Load a region into memory.
const RegionGroup & get_region_group(const std::string &name) const override
Get a region group by name.
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 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.
void lock() override
Acquire a lock for thread-safe access.
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 double > get_frame(uint64_t frame_index) const override
Get a single frame of data efficiently.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ 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:6
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:73
@ 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:36
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
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:108
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:67
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