MayaFlux 0.2.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 stream = std::dynamic_pointer_cast<StreamContainer>(container)) {
72 m_looping_enabled = stream->is_looping();
73 m_loop_region = stream->get_loop_region();
74
75 const auto& stream_positions = stream->get_read_position();
76 if (!stream_positions.empty()) {
77 m_current_frame = stream_positions[0];
78 }
79 }
80
81 if (auto vc = std::dynamic_pointer_cast<VideoStreamContainer>(container))
82 m_frame_rate = vc->get_frame_rate();
83}
84
86{
90 std::source_location::current(),
91 "FrameAccessProcessor requires VIDEO_COLOR or IMAGE_COLOR modality, got {}",
92 static_cast<int>(m_structure.modality));
93 }
94
95 if (m_width == 0 || m_height == 0) {
97 std::source_location::current(),
98 "Frame dimensions cannot be zero ({}×{})",
100 }
101
102 if (m_channels == 0) {
104 std::source_location::current(),
105 "Channel count cannot be zero");
106 }
107
108 if (m_total_frames == 0) {
110 "FrameAccessProcessor: container has zero frames");
111 }
112
115 "FrameAccessProcessor: batch size {} exceeds total frames {}, clamping",
118 }
119
121 "FrameAccessProcessor validated: {}×{}×{}, {} total frames, batch {}",
123}
124
125// =========================================================================
126// Processing
127// =========================================================================
128
129void FrameAccessProcessor::process(const std::shared_ptr<SignalSourceContainer>& container)
130{
131 if (!m_prepared) {
133 "FrameAccessProcessor not prepared for processing");
134 return;
135 }
136
137 auto source_container = m_source_container_weak.lock();
138 if (!source_container || source_container.get() != container.get()) {
140 "FrameAccessProcessor: source container mismatch or expired");
141 return;
142 }
143
144 m_is_processing = true;
145 m_last_process_time = std::chrono::steady_clock::now();
146
147 try {
148 uint64_t frames_to_extract = std::min(m_frames_per_batch,
150
151 if (frames_to_extract == 0 && m_looping_enabled && m_total_frames > 0) {
152 m_current_frame = 0;
153 if (!m_loop_region.start_coordinates.empty()) {
155 }
156 frames_to_extract = std::min(m_frames_per_batch, m_total_frames - m_current_frame);
157 }
158
159 if (frames_to_extract == 0) {
160 m_is_processing = false;
161 return;
162 }
163
164 auto video_container = std::dynamic_pointer_cast<VideoStreamContainer>(source_container);
165 if (!video_container) {
167 "FrameAccessProcessor: container is not a VideoStreamContainer");
168 m_is_processing = false;
169 return;
170 }
171
172 const uint64_t byte_count = frames_to_extract * m_frame_byte_size;
173
174 auto& processed_data_vector = container->get_processed_data();
175 processed_data_vector.resize(1);
176
177 auto* dest = std::get_if<std::vector<uint8_t>>(&processed_data_vector[0]);
178 if (!dest) {
179 processed_data_vector[0] = std::vector<uint8_t>();
180 dest = std::get_if<std::vector<uint8_t>>(&processed_data_vector[0]);
181 }
182 dest->resize(byte_count);
183
184 uint8_t* write_ptr = dest->data();
185 bool all_ok = true;
186
187 for (uint64_t i = 0; i < frames_to_extract; ++i) {
188 auto pixels = video_container->get_frame_pixels(m_current_frame + i);
189 if (pixels.empty()) {
190 std::memset(write_ptr, 0, m_frame_byte_size);
191 all_ok = false;
192 } else {
193 std::memcpy(write_ptr, pixels.data(), m_frame_byte_size);
194 }
195 write_ptr += m_frame_byte_size;
196 }
197
198 if (!all_ok) {
200 "FrameAccessProcessor: one or more frames unavailable at frame {}",
202 }
203
204 if (m_auto_advance) {
205 if (!all_ok) {
207 "FrameAccessProcessor: auto-advance enabled but frame data was incomplete. Waiting for next process call without advancing frame.");
208 }
209 if (m_frame_rate > 0.0) {
211 auto frames_to_advance = static_cast<uint64_t>(m_frame_accumulator);
212 if (frames_to_advance > 0) {
213 m_frame_accumulator -= static_cast<double>(frames_to_advance);
214 advance_frame(frames_to_advance);
215 }
216 } else {
217 advance_frame(frames_to_extract);
218 }
219 }
220
221 } catch (const std::exception& e) {
223 "FrameAccessProcessor::process failed: {}", e.what());
224 }
225
226 m_is_processing = false;
227}
228
229// =========================================================================
230// Configuration
231// =========================================================================
232
234{
235 if (count == 0) {
237 "FrameAccessProcessor: batch size cannot be zero, clamping to 1");
238 count = 1;
239 }
241}
242
243// =========================================================================
244// Frame advancement
245// =========================================================================
246
247void FrameAccessProcessor::advance_frame(uint64_t frames_to_advance)
248{
249 uint64_t new_frame = m_current_frame + frames_to_advance;
250
251 if (new_frame >= m_total_frames) {
252 if (m_looping_enabled) {
253 uint64_t loop_start = 0;
254 uint64_t loop_end = m_total_frames - 1;
255
256 if (!m_loop_region.start_coordinates.empty()) {
257 loop_start = m_loop_region.start_coordinates[0];
258 }
259 if (!m_loop_region.end_coordinates.empty()) {
260 loop_end = m_loop_region.end_coordinates[0];
261 }
262
263 if (loop_end > loop_start) {
264 uint64_t loop_length = loop_end - loop_start + 1;
265 uint64_t overflow = new_frame - m_total_frames;
266 new_frame = loop_start + (overflow % loop_length);
267 } else {
268 new_frame = loop_start;
269 }
270 } else {
271 new_frame = m_total_frames > 0 ? m_total_frames - 1 : 0;
272 }
273 }
274
275 m_current_frame = new_frame;
276
277 if (auto stream = std::dynamic_pointer_cast<StreamContainer>(m_source_container_weak.lock())) {
278 stream->update_read_position_for_channel(0, m_current_frame);
279 }
280}
281
282} // namespace MayaFlux::Kakshya
#define MF_INFO(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Eigen::Index 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.
@ 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:72
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:69