MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FrameAccessProcessor.cpp
Go to the documentation of this file.
2
4
6
7namespace MayaFlux::Kakshya {
8
9// =========================================================================
10// Lifecycle
11// =========================================================================
12
13void FrameAccessProcessor::on_attach(const std::shared_ptr<SignalSourceContainer>& container)
14{
15 if (!container) {
16 return;
17 }
18
19 m_source_container_weak = container;
20
21 try {
22 store_metadata(container);
23 validate();
24
25 m_prepared = true;
26 container->mark_ready_for_processing(true);
27
28 m_last_process_time = std::chrono::steady_clock::now();
30
32 "FrameAccessProcessor attached: {}×{}×{} frames={}, frame_bytes={}, batch={}",
35
36 } catch (const std::exception& e) {
37 m_prepared = false;
39 std::source_location::current(),
40 "Failed to attach FrameAccessProcessor: {}",
41 e.what());
42 }
43}
44
45void FrameAccessProcessor::on_detach(const std::shared_ptr<SignalSourceContainer>& /*container*/)
46{
48 m_prepared = false;
52}
53
54// =========================================================================
55// Metadata
56// =========================================================================
57
58void FrameAccessProcessor::store_metadata(const std::shared_ptr<SignalSourceContainer>& container)
59{
60 m_structure = container->get_structure();
61
62 const auto& dims = m_structure.dimensions;
63
68
70
71 if (auto vc = std::dynamic_pointer_cast<VideoStreamContainer>(container)) {
72 m_looping_enabled = vc->is_looping();
73 m_loop_region = vc->get_loop_region();
74 m_frame_rate = vc->get_frame_rate();
75 m_frame_byte_size = vc->get_frame_byte_size();
76
77 const auto& stream_positions = vc->get_read_position();
78 if (!stream_positions.empty())
79 m_current_frame = stream_positions[0];
80 } else if (auto stream = std::dynamic_pointer_cast<StreamContainer>(container)) {
81 m_looping_enabled = stream->is_looping();
82 m_loop_region = stream->get_loop_region();
83
84 const auto& stream_positions = stream->get_read_position();
85 if (!stream_positions.empty())
86 m_current_frame = stream_positions[0];
87 }
88
89 if (auto vc = std::dynamic_pointer_cast<VideoStreamContainer>(container))
90 m_frame_rate = vc->get_frame_rate();
91}
92
94{
100 std::source_location::current(),
101 "FrameAccessProcessor requires a frame-structured modality "
102 "(VIDEO_COLOR, IMAGE_COLOR, VIDEO_DEPTH, DEPTH_MAP), got {}",
103 static_cast<int>(m_structure.modality));
104 }
105
106 if (m_width == 0 || m_height == 0) {
108 std::source_location::current(),
109 "Frame dimensions cannot be zero ({}×{})",
111 }
112
113 if (m_channels == 0) {
115 std::source_location::current(),
116 "Channel count cannot be zero");
117 }
118
119 if (m_total_frames == 0) {
121 "FrameAccessProcessor: container has zero frames");
122 }
123
126 "FrameAccessProcessor: batch size {} exceeds total frames {}, clamping",
129 }
130
132 "FrameAccessProcessor validated: {}×{}×{}, {} total frames, batch {}",
134}
135
136// =========================================================================
137// Processing
138// =========================================================================
139
140void FrameAccessProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
141{
142 if (!m_prepared) {
144 "FrameAccessProcessor not prepared for processing");
145 return;
146 }
147
148 auto source_container = m_source_container_weak.lock();
149 if (!source_container || source_container.get() != container.get()) {
151 "FrameAccessProcessor: source container mismatch or expired");
152 return;
153 }
154
155 m_is_processing = true;
156 m_last_process_time = std::chrono::steady_clock::now();
157
158 try {
159 uint64_t frames_to_extract = std::min(m_frames_per_batch,
161
162 if (frames_to_extract == 0 && m_looping_enabled && m_total_frames > 0) {
163 m_current_frame = 0;
164 if (!m_loop_region.start_coordinates.empty()) {
166 }
167 frames_to_extract = std::min(m_frames_per_batch, m_total_frames - m_current_frame);
168 }
169
170 if (frames_to_extract == 0) {
171 m_is_processing = false;
172 return;
173 }
174
175 auto video_container = std::dynamic_pointer_cast<VideoStreamContainer>(source_container);
176 if (!video_container) {
178 "FrameAccessProcessor: container is not a VideoStreamContainer");
179 m_is_processing = false;
180 return;
181 }
182
183 const uint64_t byte_count = frames_to_extract * m_frame_byte_size;
184
185 auto& processed_data_vector = container->get_processed_data();
186 processed_data_vector.resize(1);
187
188 auto* dest = std::get_if<std::vector<uint8_t>>(&processed_data_vector[0]);
189 if (!dest) {
190 processed_data_vector[0] = std::vector<uint8_t>();
191 dest = std::get_if<std::vector<uint8_t>>(&processed_data_vector[0]);
192 }
193 dest->resize(byte_count);
194
195 uint8_t* write_ptr = dest->data();
196 bool all_ok = true;
197
198 for (uint64_t i = 0; i < frames_to_extract; ++i) {
199 auto pixels = video_container->get_frame_pixels(m_current_frame + i);
200 if (pixels.empty()) {
201 std::memset(write_ptr, 0, m_frame_byte_size);
202 all_ok = false;
203 } else {
204 std::memcpy(write_ptr, pixels.data(), m_frame_byte_size);
205 }
206 write_ptr += m_frame_byte_size;
207 }
208
209 if (!all_ok) {
211 "FrameAccessProcessor: one or more frames unavailable at frame {}",
213 }
214
215 if (m_auto_advance) {
216 if (!all_ok) {
218 "FrameAccessProcessor: auto-advance enabled but frame data was incomplete. Waiting for next process call without advancing frame.");
219 }
220 if (m_frame_rate > 0.0) {
222 auto frames_to_advance = static_cast<uint64_t>(m_frame_accumulator);
223 if (frames_to_advance > 0) {
224 m_frame_accumulator -= static_cast<double>(frames_to_advance);
225 advance_frame(frames_to_advance);
226 }
227 } else {
228 advance_frame(frames_to_extract);
229 }
230 }
231
232 } catch (const std::exception& e) {
234 "FrameAccessProcessor::process failed: {}", e.what());
235 }
236
237 m_is_processing = false;
238}
239
240// =========================================================================
241// Configuration
242// =========================================================================
243
245{
246 if (count == 0) {
248 "FrameAccessProcessor: batch size cannot be zero, clamping to 1");
249 count = 1;
250 }
252}
253
254// =========================================================================
255// Frame advancement
256// =========================================================================
257
258void FrameAccessProcessor::advance_frame(uint64_t frames_to_advance)
259{
260 uint64_t new_frame = m_current_frame + frames_to_advance;
261
262 if (new_frame >= m_total_frames) {
263 if (m_looping_enabled) {
264 uint64_t loop_start = 0;
265 uint64_t loop_end = m_total_frames - 1;
266
267 if (!m_loop_region.start_coordinates.empty()) {
268 loop_start = m_loop_region.start_coordinates[0];
269 }
270 if (!m_loop_region.end_coordinates.empty()) {
271 loop_end = m_loop_region.end_coordinates[0];
272 }
273
274 if (loop_end > loop_start) {
275 uint64_t loop_length = loop_end - loop_start + 1;
276 uint64_t overflow = new_frame - m_total_frames;
277 new_frame = loop_start + (overflow % loop_length);
278 } else {
279 new_frame = loop_start;
280 }
281 } else {
282 new_frame = m_total_frames > 0 ? m_total_frames - 1 : 0;
283 }
284 }
285
286 m_current_frame = new_frame;
287
288 if (auto stream = std::dynamic_pointer_cast<StreamContainer>(m_source_container_weak.lock())) {
289 stream->update_read_position_for_channel(0, m_current_frame);
290 }
291}
292
293} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Core::GlobalStreamInfo stream
Definition Config.cpp:36
const std::vector< float > * pixels
Definition Decoder.cpp:65
size_t count
void set_frames_per_batch(uint64_t count)
Set the number of frames extracted per process() call.
void process(const std::shared_ptr< SignalSourceContainer > &container) override
Extract the current frame(s) into the container's processed_data.
std::chrono::steady_clock::time_point m_last_process_time
void on_detach(const std::shared_ptr< SignalSourceContainer > &container) override
Detach the processor from its container.
void advance_frame(uint64_t frames_to_advance)
Advance the frame cursor, respecting loop boundaries.
std::weak_ptr< SignalSourceContainer > m_source_container_weak
void on_attach(const std::shared_ptr< SignalSourceContainer > &container) override
Attach the processor to a video container.
void validate()
Validate that the container is suitable for frame-based processing.
double m_frame_accumulator
Sub-frame accumulator for wall-clock-driven advancement.
double m_frame_rate
Cached video frame rate in frames per second.
void store_metadata(const std::shared_ptr< SignalSourceContainer > &container)
Cache dimension metadata and frame geometry from the container.
@ ContainerProcessing
Container operations (Kakshya - file/stream/region processing)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
@ DEPTH_MAP
[height, width, components] - range/disparity image
@ VIDEO_DEPTH
[frames, height, width, components] - streaming range data
@ VIDEO_COLOR
4D video (time + 2D + color)
@ IMAGE_COLOR
2D RGB/RGBA image
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:78
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:75