FastLED 3.9.12
Loading...
Searching...
No Matches
video_impl.cpp
1
2
3#include "video_impl.h"
4
5#include "fl/assert.h"
6#include "fl/math_macros.h"
7#include "fl/namespace.h"
8#include "fl/warn.h"
9
10using namespace fl;
11
12namespace fl {
13
14VideoImpl::VideoImpl(size_t pixelsPerFrame, float fpsVideo,
15 size_t nFramesInBuffer)
16 : mPixelsPerFrame(pixelsPerFrame),
17 mFrameInterpolator(
18 FrameInterpolatorPtr::New(MAX(1, nFramesInBuffer), fpsVideo)) {}
19
20void VideoImpl::pause(uint32_t now) {
21 if (!mTime) {
22 mTime = TimeScalePtr::New(now);
23 }
24 mTime->pause(now);
25}
26void VideoImpl::resume(uint32_t now) {
27 if (!mTime) {
28 mTime = TimeScalePtr::New(now);
29 }
30 mTime->resume(now);
31}
32
33void VideoImpl::setTimeScale(float timeScale) {
34 mTimeScale = timeScale;
35 if (mTime) {
36 mTime->setScale(timeScale);
37 }
38}
39
40void VideoImpl::setFade(uint32_t fadeInTime, uint32_t fadeOutTime) {
41 mFadeInTime = fadeInTime;
42 mFadeOutTime = fadeOutTime;
43}
44
45bool VideoImpl::needsFrame(uint32_t now) const {
46 uint32_t f1, f2;
47 bool out = mFrameInterpolator->needsFrame(now, &f1, &f2);
48 return out;
49}
50
51VideoImpl::~VideoImpl() { end(); }
52
53void VideoImpl::begin(FileHandlePtr h) {
54 end();
55 // Removed setStartTime call
56 mStream = PixelStreamPtr::New(mPixelsPerFrame * kSizeRGB8);
57 mStream->begin(h);
58 mPrevNow = 0;
59}
60
61void VideoImpl::beginStream(ByteStreamPtr bs) {
62 end();
63 mStream = PixelStreamPtr::New(mPixelsPerFrame * kSizeRGB8);
64 // Removed setStartTime call
65 mStream->beginStream(bs);
66 mPrevNow = 0;
67}
68
69void VideoImpl::end() {
70 mFrameInterpolator->clear();
71 // Removed resetFrameCounter and setStartTime calls
72 mStream.reset();
73}
74
75bool VideoImpl::full() const { return mFrameInterpolator->getFrames()->full(); }
76
77bool VideoImpl::draw(uint32_t now, Frame *frame) {
78 return draw(now, frame->rgb());
79}
80
81int32_t VideoImpl::durationMicros() const {
82 if (!mStream) {
83 return -1;
84 }
85 int32_t frames = mStream->framesRemaining();
86 if (frames < 0) {
87 return -1; // Stream case, duration unknown
88 }
89 uint32_t micros_per_frame = mFrameInterpolator->getFrameTracker().microsecondsPerFrame();
90 return (frames * micros_per_frame); // Convert to milliseconds
91}
92
93bool VideoImpl::draw(uint32_t now, CRGB *leds) {
94 if (!mTime) {
95 mTime = TimeScalePtr::New(now);
96 mTime->setScale(mTimeScale);
97 mTime->reset(now);
98 }
99 now = mTime->update(now);
100 if (!mStream) {
101 FASTLED_WARN("no stream");
102 return false;
103 }
104 bool ok = updateBufferIfNecessary(mPrevNow, now);
105 mPrevNow = now;
106 if (!ok) {
107 FASTLED_WARN("updateBufferIfNecessary failed");
108 return false;
109 }
110 mFrameInterpolator->draw(now, leds);
111
112 uint32_t time = mTime->time();
113 uint32_t brightness = 255;
114 // Compute fade in/out brightness.
115 if (mFadeInTime || mFadeOutTime) {
116 brightness = 255;
117 if (time <= mFadeInTime) {
118 if (mFadeInTime == 0) {
119 brightness = 255;
120 } else {
121 brightness = time * 255 / mFadeInTime;
122 }
123 } else if (mFadeOutTime) {
124 int32_t frames_remaining = mStream->framesRemaining();
125 if (frames_remaining < 0) {
126 // -1 means this is a stream.
127 brightness = 255;
128 } else {
129 FrameTracker &frame_tracker =
130 mFrameInterpolator->getFrameTracker();
131 uint32_t micros_per_frame =
132 frame_tracker.microsecondsPerFrame();
133 uint32_t millis_left =
134 (frames_remaining * micros_per_frame) / 1000;
135 if (millis_left < mFadeOutTime) {
136 brightness = millis_left * 255 / mFadeOutTime;
137 }
138 }
139 }
140 }
141 if (brightness < 255) {
142 if (brightness == 0) {
143 for (size_t i = 0; i < mPixelsPerFrame; ++i) {
144 leds[i] = CRGB::Black;
145 }
146 } else {
147 for (size_t i = 0; i < mPixelsPerFrame; ++i) {
148 leds[i].nscale8(brightness);
149 }
150 }
151 }
152 return true;
153}
154
155bool VideoImpl::updateBufferFromStream(uint32_t now) {
156 FASTLED_ASSERT(mTime, "mTime is null");
157 if (!mStream) {
158 FASTLED_WARN("no stream");
159 return false;
160 }
161 if (mStream->atEnd()) {
162 return false;
163 }
164
165 uint32_t currFrameNumber = 0;
166 uint32_t nextFrameNumber = 0;
167 bool needs_frame =
168 mFrameInterpolator->needsFrame(now, &currFrameNumber, &nextFrameNumber);
169 if (!needs_frame) {
170 return true;
171 }
172
173 if (mFrameInterpolator->capacity() == 0) {
174 FASTLED_WARN("capacity == 0");
175 return false;
176 }
177
178 const bool has_current_frame = mFrameInterpolator->has(currFrameNumber);
179 const bool has_next_frame = mFrameInterpolator->has(nextFrameNumber);
180
181 fl::FixedVector<uint32_t, 2> frame_numbers;
182 if (!has_current_frame) {
183 frame_numbers.push_back(currFrameNumber);
184 }
185 size_t capacity = mFrameInterpolator->capacity();
186 if (capacity > 1 && !has_next_frame) {
187 frame_numbers.push_back(nextFrameNumber);
188 }
189
190 for (size_t i = 0; i < frame_numbers.size(); ++i) {
191 FramePtr recycled_frame;
192 if (mFrameInterpolator->full()) {
193 uint32_t frame_to_erase = 0;
194 bool ok =
195 mFrameInterpolator->get_oldest_frame_number(&frame_to_erase);
196 if (!ok) {
197 FASTLED_WARN("get_oldest_frame_number failed");
198 return false;
199 }
200 recycled_frame = mFrameInterpolator->erase(frame_to_erase);
201 if (!recycled_frame) {
202 FASTLED_WARN("erase failed for frame: " << frame_to_erase);
203 return false;
204 }
205 }
206 uint32_t frame_to_fetch = frame_numbers[i];
207 if (!recycled_frame) {
208 // Happens when we are not full and we need to allocate a new frame.
209 recycled_frame = FramePtr::New(mPixelsPerFrame);
210 }
211
212 if (!mStream->readFrame(recycled_frame.get())) {
213 if (mStream->atEnd()) {
214 if (!mStream->rewind()) {
215 FASTLED_WARN("rewind failed");
216 return false;
217 }
218 mTime->reset(now);
219 frame_to_fetch = 0;
220 if (!mStream->readFrameAt(frame_to_fetch,
221 recycled_frame.get())) {
222 FASTLED_WARN("readFrameAt failed");
223 return false;
224 }
225 } else {
226 FASTLED_WARN("We failed for some other reason");
227 return false;
228 }
229 }
230 bool ok = mFrameInterpolator->insert(frame_to_fetch, recycled_frame);
231 if (!ok) {
232 FASTLED_WARN("insert failed");
233 return false;
234 }
235 }
236 return true;
237}
238
239bool VideoImpl::updateBufferFromFile(uint32_t now, bool forward) {
240 uint32_t currFrameNumber = 0;
241 uint32_t nextFrameNumber = 0;
242 bool needs_frame =
243 mFrameInterpolator->needsFrame(now, &currFrameNumber, &nextFrameNumber);
244 if (!needs_frame) {
245 return true;
246 }
247 bool has_curr_frame = mFrameInterpolator->has(currFrameNumber);
248 bool has_next_frame = mFrameInterpolator->has(nextFrameNumber);
249 if (has_curr_frame && has_next_frame) {
250 return true;
251 }
252 if (mFrameInterpolator->capacity() == 0) {
253 FASTLED_WARN("capacity == 0");
254 return false;
255 }
256
257 fl::FixedVector<uint32_t, 2> frame_numbers;
258 if (!mFrameInterpolator->has(currFrameNumber)) {
259 frame_numbers.push_back(currFrameNumber);
260 }
261 if (mFrameInterpolator->capacity() > 1 &&
262 !mFrameInterpolator->has(nextFrameNumber)) {
263 frame_numbers.push_back(nextFrameNumber);
264 }
265
266 for (size_t i = 0; i < frame_numbers.size(); ++i) {
267 FramePtr recycled_frame;
268 if (mFrameInterpolator->full()) {
269 uint32_t frame_to_erase = 0;
270 bool ok = false;
271 if (forward) {
272 ok = mFrameInterpolator->get_oldest_frame_number(
273 &frame_to_erase);
274 if (!ok) {
275 FASTLED_WARN("get_oldest_frame_number failed");
276 return false;
277 }
278 } else {
279 ok = mFrameInterpolator->get_newest_frame_number(
280 &frame_to_erase);
281 if (!ok) {
282 FASTLED_WARN("get_newest_frame_number failed");
283 return false;
284 }
285 }
286 recycled_frame = mFrameInterpolator->erase(frame_to_erase);
287 if (!recycled_frame) {
288 FASTLED_WARN("erase failed for frame: " << frame_to_erase);
289 return false;
290 }
291 }
292 uint32_t frame_to_fetch = frame_numbers[i];
293 if (!recycled_frame) {
294 // Happens when we are not full and we need to allocate a new frame.
295 recycled_frame = FramePtr::New(mPixelsPerFrame);
296 }
297
298 do { // only to use break
299 if (!mStream->readFrameAt(frame_to_fetch, recycled_frame.get())) {
300 if (!forward) {
301 // nothing more we can do, we can't go negative.
302 return false;
303 }
304 if (mStream->atEnd()) {
305 if (!mStream->rewind()) { // Is this still
306 FASTLED_WARN("rewind failed");
307 return false;
308 }
309 mTime->reset(now);
310 frame_to_fetch = 0;
311 if (!mStream->readFrameAt(frame_to_fetch,
312 recycled_frame.get())) {
313 FASTLED_WARN("readFrameAt failed");
314 return false;
315 }
316 break; // we have the frame, so we can break out of the loop
317 }
318 FASTLED_WARN("We failed for some other reason");
319 return false;
320 }
321 break;
322 } while (false);
323
324 bool ok = mFrameInterpolator->insert(frame_to_fetch, recycled_frame);
325 if (!ok) {
326 FASTLED_WARN("insert failed");
327 return false;
328 }
329 }
330 return true;
331}
332
333bool VideoImpl::updateBufferIfNecessary(uint32_t prev, uint32_t now) {
334 const bool forward = now >= prev;
335
336 PixelStream::Type type = mStream->getType();
337 switch (type) {
338 case PixelStream::kFile:
339 return updateBufferFromFile(now, forward);
340 case PixelStream::kStreaming:
341 return updateBufferFromStream(now);
342 default:
343 FASTLED_WARN("Unknown type: " << uint32_t(type));
344 return false;
345 }
346}
347
348bool VideoImpl::rewind() {
349 if (!mStream || !mStream->rewind()) {
350 return false;
351 }
352 mFrameInterpolator->clear();
353 return true;
354}
355
356} // namespace fl
Implements the FastLED namespace macros.
Implements a simple red square effect for 2D LED grids.
Definition crgb.h:16
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:54
CRGB & nscale8(uint8_t scaledown)
Scale down a RGB to N/256ths of its current brightness, using "plain math" dimming rules.
Definition crgb.cpp:58
@ Black
Definition crgb.h:496