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