FastLED 3.9.15
Loading...
Searching...
No Matches
video_impl.cpp.hpp
Go to the documentation of this file.
1
2
4
5#include "fl/fx/time.h"
6#include "fl/stl/assert.h"
7#include "fl/math/math.h"
8#include "fl/log/log.h"
12#include "crgb.h"
13#include "fl/stl/noexcept.h"
14
15namespace fl {
16namespace video {
17
18VideoImpl::VideoImpl(size_t pixelsPerFrame, float fpsVideo,
19 size_t nFramesInBuffer)
22 fl::make_shared<FrameInterpolator>(fl::max(1, nFramesInBuffer), fpsVideo)) {}
23
24void VideoImpl::pause(fl::u32 now) {
25 if (!mTime) {
27 }
28 mTime->pause(now);
29}
30void VideoImpl::resume(fl::u32 now) {
31 if (!mTime) {
33 }
34 mTime->resume(now);
35}
36
39 if (mTime) {
40 mTime->setSpeed(timeScale);
41 }
42}
43
44void VideoImpl::setFade(fl::u32 fadeInTime, fl::u32 fadeOutTime) {
45 mFadeInTime = fadeInTime;
46 mFadeOutTime = fadeOutTime;
47}
48
49bool VideoImpl::needsFrame(fl::u32 now) const {
50 fl::u32 f1, f2;
51 bool out = mFrameInterpolator->needsFrame(now, &f1, &f2);
52 return out;
53}
54
56
63
65 mFrameInterpolator->clear();
66 // Removed resetFrameCounter and setStartTime calls
67 mStream.reset();
68}
69
70bool VideoImpl::full() const { return mFrameInterpolator->getFrames()->full(); }
71
72bool VideoImpl::draw(fl::u32 now, Frame *frame) {
73 return draw(now, frame->rgb());
74}
75
77 if (!mStream) {
78 return -1;
79 }
80 i32 frames = mStream->framesRemaining();
81 if (frames < 0) {
82 return -1; // Stream case, duration unknown
83 }
84 fl::u32 micros_per_frame =
85 mFrameInterpolator->getFrameTracker().microsecondsPerFrame();
86 return (frames * micros_per_frame); // Convert to milliseconds
87}
88
90 if (!mTime) {
92 mTime->setSpeed(mTimeScale);
93 mTime->reset(now);
94 }
95 now = mTime->update(now);
96 if (!mStream) {
97 FL_WARN("no stream");
98 return false;
99 }
100 bool ok = updateBufferIfNecessary(mPrevNow, now);
101 mPrevNow = now;
102 if (!ok) {
103 FL_WARN("updateBufferIfNecessary failed");
104 return false;
105 }
106 mFrameInterpolator->draw(now, leds);
107
108 fl::u32 time = mTime->time();
109 fl::u32 brightness = 255;
110 // Compute fade in/out brightness.
111 if (mFadeInTime || mFadeOutTime) {
112 brightness = 255;
113 if (time <= mFadeInTime) {
114 if (mFadeInTime == 0) {
115 brightness = 255;
116 } else {
117 brightness = time * 255 / mFadeInTime;
118 }
119 } else if (mFadeOutTime) {
120 i32 frames_remaining = mStream->framesRemaining();
121 if (frames_remaining < 0) {
122 // -1 means this is a stream.
123 brightness = 255;
124 } else {
125 FrameTracker &frame_tracker =
126 mFrameInterpolator->getFrameTracker();
127 fl::u32 micros_per_frame =
128 frame_tracker.microsecondsPerFrame();
129 fl::u32 millis_left =
130 (frames_remaining * micros_per_frame) / 1000;
131 if (millis_left < mFadeOutTime) {
132 brightness = millis_left * 255 / mFadeOutTime;
133 }
134 }
135 }
136 }
137 if (brightness < 255) {
138 if (brightness == 0) {
139 for (size_t i = 0; i < mPixelsPerFrame; ++i) {
140 leds[i] = CRGB::Black;
141 }
142 } else {
143 for (size_t i = 0; i < mPixelsPerFrame; ++i) {
144 leds[i].nscale8(brightness);
145 }
146 }
147 }
148 return true;
149}
150
152 FASTLED_ASSERT(mTime, "mTime is null");
153 if (!mStream) {
154 FL_WARN("no stream");
155 return false;
156 }
157 if (mStream->atEnd()) {
158 return false;
159 }
160
161 fl::u32 currFrameNumber = 0;
162 fl::u32 nextFrameNumber = 0;
163 bool needs_frame =
164 mFrameInterpolator->needsFrame(now, &currFrameNumber, &nextFrameNumber);
165 if (!needs_frame) {
166 return true;
167 }
168
169 if (mFrameInterpolator->capacity() == 0) {
170 FL_WARN("capacity == 0");
171 return false;
172 }
173
174 const bool has_current_frame = mFrameInterpolator->has(currFrameNumber);
175 const bool has_next_frame = mFrameInterpolator->has(nextFrameNumber);
176
177 fl::FixedVector<fl::u32, 2> frame_numbers;
178 if (!has_current_frame) {
179 frame_numbers.push_back(currFrameNumber);
180 }
181 size_t capacity = mFrameInterpolator->capacity();
182 if (capacity > 1 && !has_next_frame) {
183 frame_numbers.push_back(nextFrameNumber);
184 }
185
186 for (size_t i = 0; i < frame_numbers.size(); ++i) {
187 FramePtr recycled_frame;
188 if (mFrameInterpolator->full()) {
189 fl::u32 frame_to_erase = 0;
190 bool ok =
191 mFrameInterpolator->get_oldest_frame_number(&frame_to_erase);
192 if (!ok) {
193 FL_WARN("get_oldest_frame_number failed");
194 return false;
195 }
196 recycled_frame = mFrameInterpolator->erase(frame_to_erase);
197 if (!recycled_frame) {
198 FL_WARN("erase failed for frame: " << frame_to_erase);
199 return false;
200 }
201 }
202 fl::u32 frame_to_fetch = frame_numbers[i];
203 if (!recycled_frame) {
204 // Happens when we are not full and we need to allocate a new frame.
205 recycled_frame = fl::make_shared<Frame>(mPixelsPerFrame);
206 }
207
208 if (!mStream->readFrame(recycled_frame.get())) {
209 if (mStream->atEnd()) {
210 if (!mStream->rewind()) {
211 FL_WARN("rewind failed");
212 return false;
213 }
214 mTime->reset(now);
215 frame_to_fetch = 0;
216 if (!mStream->readFrameAt(frame_to_fetch,
217 recycled_frame.get())) {
218 FL_WARN("readFrameAt failed");
219 return false;
220 }
221 } else {
222 // For streaming mode, buffer might not have data yet - this is OK
223 // Producer (effect renderer) will fill buffer on next call
224 // Just return success and we'll try again next frame
225 if (mStream->getType() == PixelStream::kStreaming) {
226 return true; // Gracefully handle empty streaming buffer
227 }
228 FL_WARN("We failed for some other reason");
229 return false;
230 }
231 }
232 bool ok = mFrameInterpolator->insert(frame_to_fetch, recycled_frame);
233 if (!ok) {
234 FL_WARN("insert failed");
235 return false;
236 }
237 }
238 return true;
239}
240
242 fl::u32 currFrameNumber = 0;
243 fl::u32 nextFrameNumber = 0;
244 bool needs_frame =
245 mFrameInterpolator->needsFrame(now, &currFrameNumber, &nextFrameNumber);
246 if (!needs_frame) {
247 return true;
248 }
249 bool has_curr_frame = mFrameInterpolator->has(currFrameNumber);
250 bool has_next_frame = mFrameInterpolator->has(nextFrameNumber);
251 if (has_curr_frame && has_next_frame) {
252 return true;
253 }
254 if (mFrameInterpolator->capacity() == 0) {
255 FL_WARN("capacity == 0");
256 return false;
257 }
258
259 fl::FixedVector<fl::u32, 2> frame_numbers;
260 if (!mFrameInterpolator->has(currFrameNumber)) {
261 frame_numbers.push_back(currFrameNumber);
262 }
263 if (mFrameInterpolator->capacity() > 1 &&
264 !mFrameInterpolator->has(nextFrameNumber)) {
265 frame_numbers.push_back(nextFrameNumber);
266 }
267
268 for (size_t i = 0; i < frame_numbers.size(); ++i) {
269 FramePtr recycled_frame;
270 if (mFrameInterpolator->full()) {
271 fl::u32 frame_to_erase = 0;
272 bool ok = false;
273 if (forward) {
274 ok = mFrameInterpolator->get_oldest_frame_number(
275 &frame_to_erase);
276 if (!ok) {
277 FL_WARN("get_oldest_frame_number failed");
278 return false;
279 }
280 } else {
281 ok = mFrameInterpolator->get_newest_frame_number(
282 &frame_to_erase);
283 if (!ok) {
284 FL_WARN("get_newest_frame_number failed");
285 return false;
286 }
287 }
288 recycled_frame = mFrameInterpolator->erase(frame_to_erase);
289 if (!recycled_frame) {
290 FL_WARN("erase failed for frame: " << frame_to_erase);
291 return false;
292 }
293 }
294 fl::u32 frame_to_fetch = frame_numbers[i];
295 if (!recycled_frame) {
296 // Happens when we are not full and we need to allocate a new frame.
297 recycled_frame = fl::make_shared<Frame>(mPixelsPerFrame);
298 }
299
300 do { // only to use break
301 if (!mStream->readFrameAt(frame_to_fetch, recycled_frame.get())) {
302 if (!forward) {
303 // nothing more we can do, we can't go negative.
304 return false;
305 }
306 if (mStream->atEnd()) {
307 if (!mStream->rewind()) { // Is this still
308 FL_WARN("rewind failed");
309 return false;
310 }
311 mTime->reset(now);
312 frame_to_fetch = 0;
313 if (!mStream->readFrameAt(frame_to_fetch,
314 recycled_frame.get())) {
315 FL_WARN("readFrameAt failed");
316 return false;
317 }
318 break; // we have the frame, so we can break out of the loop
319 }
320 FL_WARN("We failed for some other reason");
321 return false;
322 }
323 break;
324 } while (false);
325
326 bool ok = mFrameInterpolator->insert(frame_to_fetch, recycled_frame);
327 if (!ok) {
328 FL_WARN("insert failed");
329 return false;
330 }
331 }
332 return true;
333}
334
335bool VideoImpl::updateBufferIfNecessary(fl::u32 prev, fl::u32 now) {
336 const bool forward = now >= prev;
337
338 PixelStream::Type type = mStream->getType();
339 switch (type) {
341 return updateBufferFromFile(now, forward);
343 return updateBufferFromStream(now);
344 default:
345 FL_WARN("Unknown type: " << fl::u32(type));
346 return false;
347 }
348}
349
351 if (!mStream || !mStream->rewind()) {
352 return false;
353 }
354 mFrameInterpolator->clear();
355 return true;
356}
357
359 if (!mStream) return false;
360 return mStream->hasEmbeddedScreenMap();
361}
362
364 static const fl::string kEmpty;
365 if (!mStream) return kEmpty;
366 return mStream->embeddedScreenMapJson();
367}
368
369} // namespace video
370} // namespace fl
fl::CRGB leds[NUM_LEDS]
fl::UISlider brightness("Brightness", BRIGHTNESS, 0, 255)
void push_back(const T &value) FL_NOEXCEPT
Definition vector.h:191
constexpr fl::size size() const FL_NOEXCEPT
Definition vector.h:183
fl::span< CRGB > rgb()
Definition frame.h:42
fl::u32 microsecondsPerFrame() const
bool updateBufferFromStream(fl::u32 now)
fl::TimeWarpPtr mTime
Definition video_impl.h:70
void setFade(fl::u32 fadeInTime, fl::u32 fadeOutTime)
size_t pixelsPerFrame() const
Definition video_impl.h:51
bool updateBufferFromFile(fl::u32 now, bool forward)
float timeScale() const
Definition video_impl.h:50
FrameInterpolatorPtr mFrameInterpolator
Definition video_impl.h:69
VideoImpl(size_t pixelsPerFrame, float fpsVideo, size_t frameHistoryCount=0)
void resume(fl::u32 now)
void pause(fl::u32 now)
bool needsFrame(fl::u32 now) const
bool hasEmbeddedScreenMap() const FL_NOEXCEPT
void setTimeScale(float timeScale)
const fl::string & embeddedScreenMapJson() const FL_NOEXCEPT
bool draw(fl::u32 now, fl::span< CRGB > leds)
PixelStreamPtr mStream
Definition video_impl.h:67
bool updateBufferIfNecessary(fl::u32 prev, fl::u32 now)
void begin(fl::filebuf_ptr h)
#define FL_WARN(X)
Definition log.h:276
Centralized logging categories for FastLED hardware interfaces and subsystems.
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
fl::u64 time() FL_NOEXCEPT
Alias for millis64() - returns 64-bit millisecond time.
Definition chrono.h:346
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
Definition shared_ptr.h:414
fl::shared_ptr< filebuf > filebuf_ptr
Definition idecoder.h:15
constexpr T && forward(typename remove_reference< T >::type &t) FL_NOEXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
@ Black
<div style='background:#000000;width:4em;height:4em;'></div>
Definition crgb.h:510