FastLED 3.9.3
Loading...
Searching...
No Matches
video.cpp
1#include "fx/video.h"
2
3#include "crgb.h"
4#include "fx/detail/data_stream.h"
5#include "fx/frame.h"
6#include "fx/video/frame_interpolator.h"
7
8#ifdef __EMSCRIPTEN__
9#define DEBUG_IO_STREAM 1
10#else
11#define DEBUG_IO_STREAM 0
12#endif
13
14#if DEBUG_IO_STREAM
15#include <iostream> // ok include
16using namespace std;
17#define DBG(X) (X)
18#else
19#define DBG(X)
20#endif
21
22#include "namespace.h"
23
24FASTLED_NAMESPACE_BEGIN
25
26FASTLED_SMART_REF(DataStream);
27FASTLED_SMART_REF(FrameInterpolator);
28FASTLED_SMART_REF(Frame);
29
30class VideoImpl : public Referent {
31 public:
32 // frameHistoryCount is the number of frames to keep in the buffer after
33 // draw. This allows for time based effects like syncing video speed to
34 // audio triggers.
35 VideoImpl(size_t pixelsPerFrame, float fpsVideo,
36 size_t frameHistoryCount = 0);
37 ~VideoImpl();
38 // Api
39 void begin(FileHandleRef h);
40 void beginStream(ByteStreamRef s);
41 bool draw(uint32_t now, CRGB *leds, uint8_t *alpha = nullptr);
42 void end();
43 bool rewind();
44 // internal use
45 bool draw(uint32_t now, Frame *frame);
46 bool full() const;
47 FrameRef popOldest();
48 void pushNewest(FrameRef frame);
49
50 private:
51 bool updateBufferIfNecessary(uint32_t now);
52 uint32_t mPixelsPerFrame = 0;
53 DataStreamRef mStream;
54 FrameInterpolatorRef mInterpolator;
55};
56
57VideoImpl::VideoImpl(size_t pixelsPerFrame, float fpsVideo,
58 size_t nFramesInBuffer)
59 : mPixelsPerFrame(pixelsPerFrame),
60 mInterpolator(
61 FrameInterpolatorRef::New(MAX(1, nFramesInBuffer), fpsVideo)) {}
62
63VideoImpl::~VideoImpl() { end(); }
64
65void VideoImpl::begin(FileHandleRef h) {
66 end();
67 // Removed setStartTime call
68 mStream = DataStreamRef::New(mPixelsPerFrame);
69 mStream->begin(h);
70}
71
72void VideoImpl::beginStream(ByteStreamRef bs) {
73 end();
74 mStream = DataStreamRef::New(mPixelsPerFrame);
75 // Removed setStartTime call
76 mStream->beginStream(bs);
77}
78
79void VideoImpl::end() {
80 mInterpolator->clear();
81 // Removed resetFrameCounter and setStartTime calls
82 mStream.reset();
83}
84
85void VideoImpl::pushNewest(FrameRef frame) {
86 mInterpolator->push_front(frame, frame->getTimestamp());
87}
88
89bool VideoImpl::full() const { return mInterpolator->getFrames()->full(); }
90
91FrameRef VideoImpl::popOldest() {
92 FrameRef frame;
93 mInterpolator->pop_back(&frame);
94 return frame;
95}
96
97bool VideoImpl::draw(uint32_t now, Frame *frame) {
98 if (!mStream) {
99 return false;
100 }
101 updateBufferIfNecessary(now);
102 if (!frame) {
103 return false;
104 }
105 return mInterpolator->draw(now, frame);
106}
107
108bool VideoImpl::draw(uint32_t now, CRGB *leds, uint8_t *alpha) {
109 if (!mStream) {
110 return false;
111 }
112 bool ok = updateBufferIfNecessary(now);
113 if (!ok) {
114 // paint black
115 memset(leds, 0, mPixelsPerFrame * sizeof(CRGB));
116 if (alpha) {
117 memset(alpha, 0, mPixelsPerFrame);
118 }
119 return false;
120 }
121 mInterpolator->draw(now, leds, alpha);
122 return true;
123}
124
125bool VideoImpl::updateBufferIfNecessary(uint32_t now) {
126 // get the number of frames according to the time elapsed
127 uint32_t precise_timestamp;
128 // At most, update one frame. That way if the user forgets to call draw and
129 // then sends a really old timestamp, we don't update the buffer too much.
130 bool needs_frame = mInterpolator->needsFrame(now, &precise_timestamp);
131 if (!needs_frame) {
132 return true;
133 }
134 // if we dropped frames (because of time manipulation) just set
135 // the frame counter to the current frame number + 1
136 // read the frame from the stream
137 FrameRef frame;
138 if (mInterpolator->full()) {
139 if (!mInterpolator->popOldest(&frame)) {
140 DBG(cout << "popOldest failed" << endl);
141 return false;
142 }
143 } else {
144 frame = FrameRef::New(mPixelsPerFrame, false);
145 }
146 if (mStream->readFrame(frame.get())) {
147 if (mInterpolator->pushNewest(frame, now)) {
148 // we have a new frame
149 mInterpolator->incrementFrameCounter();
150 }
151 return true;
152 } else {
153 DBG(cout << "readFrame failed" << endl);
154 // Something went wrong so put the frame back in the buffer.
155 mInterpolator->push_front(frame, frame->getTimestamp());
156 return false;
157 }
158}
159
160bool VideoImpl::rewind() {
161 if (!mStream || !mStream->rewind()) {
162 return false;
163 }
164 mInterpolator->clear();
165 return true;
166}
167
168Video::Video() = default;
169Video::Video(FileHandleRef h, size_t pixelsPerFrame, float fps, size_t frameHistoryCount) {
170 begin(h, pixelsPerFrame, fps, frameHistoryCount);
171}
172Video::Video(ByteStreamRef s, size_t pixelsPerFrame, float fps, size_t frameHistoryCount) {
173 beginStream(s, pixelsPerFrame, fps, frameHistoryCount);
174}
175Video::~Video() = default;
176Video::Video(const Video &) = default;
177Video &Video::operator=(const Video &) = default;
178
179void Video::begin(FileHandleRef h, size_t pixelsPerFrame, float fps,
180 size_t frameHistoryCount) {
181 mImpl.reset();
182 mImpl = VideoImplRef::New(pixelsPerFrame, fps, frameHistoryCount);
183 mImpl->begin(h);
184}
185
186void Video::beginStream(ByteStreamRef bs, size_t pixelsPerFrame, float fps,
187 size_t frameHistoryCount) {
188 mImpl.reset();
189 mImpl = VideoImplRef::New(pixelsPerFrame, fps, frameHistoryCount);
190 mImpl->beginStream(bs);
191}
192
193bool Video::draw(uint32_t now, CRGB *leds, uint8_t *alpha) {
194 if (!mImpl) {
195 return false;
196 }
197 bool ok = mImpl->draw(now, leds, alpha);
198 if (!ok) {
199 // Interpret not being able to draw as a finished signal.
200 mFinished = true;
201 }
202 return ok;
203}
204
205bool Video::draw(uint32_t now, Frame *frame) {
206 if (!mImpl) {
207 return false;
208 }
209 return mImpl->draw(now, frame);
210}
211
212void Video::end() {
213 if (mImpl) {
214 mImpl->end();
215 }
216}
217
218bool Video::finished() {
219 if (!mImpl) {
220 return true;
221 }
222 return mFinished;
223}
224
225bool Video::rewind() {
226 if (!mImpl) {
227 return false;
228 }
229 return mImpl->rewind();
230}
231
232VideoFx::VideoFx(Video video, XYMap xymap) : FxGrid(xymap), mVideo(video) {}
233
235 if (!mFrame) {
236 mFrame = FrameRef::New(mXyMap.getTotal(), false);
237 }
238 bool ok = mVideo.draw(context.now, mFrame.get());
239 if (!ok) {
240 mVideo.rewind();
241 ok = mVideo.draw(context.now, mFrame.get());
242 if (!ok) {
243 return; // Can't draw or rewind
244 }
245 }
246 if (!mFrame) {
247 return; // Can't draw without a frame
248 }
249
250 const CRGB *src_pixels = mFrame->rgb();
251 CRGB *dst_pixels = context.leds;
252 size_t dst_pos = 0;
253 for (uint16_t w = 0; w < mXyMap.getWidth(); w++) {
254 for (uint16_t h = 0; h < mXyMap.getHeight(); h++) {
255 const size_t index = mXyMap.mapToIndex(w, h);
256 if (index < mFrame->size()) {
257 dst_pixels[dst_pos++] = src_pixels[index];
258 }
259 }
260 }
261}
262
263const char * VideoFx::fxName(int) const { return "video"; }
264
265FASTLED_NAMESPACE_END
Definition frame.h:18
Definition fx2d.h:16
void draw(DrawContext context) override
Definition video.cpp:234
Definition video.h:23
Definition xymap.h:39
Representation of an RGB pixel (Red, Green, Blue)
Definition crgb.h:39