arduino-audio-tools
Loading...
Searching...
No Matches
PacedVideoOutput.h
Go to the documentation of this file.
1#pragma once
2#include <atomic>
3#include <string.h>
8
9#ifdef __linux__
11#else
13#endif
14
15namespace audio_tools {
16
63 public:
73 PacedVideoOutput(VideoOutput& target, float fps = 0,
74 uint32_t schedulingDelayMs = 0)
75 : p_target(&target), scheduling_delay_ms(schedulingDelayMs) {
76 setFps(fps);
77 }
78
81 void setFps(float fps) { frame_period_ms = fps > 0 ? 1000.0f / fps : 0; }
82
89 void setAudioClock(TimeSource& clock) { p_clock = &clock; }
90
100 void setSchedulingDelayMs(uint32_t delayMs) { scheduling_delay_ms = delayMs; }
101
109 void setCatchUpThresholdFrames(float frames) {
111 }
112
122 void setIgnorePFrames(bool active) { ignore_p_frames = active; }
123 bool ignorePFrames() const { return ignore_p_frames; }
124
133 void setResyncThresholdMs(uint32_t ms) { resync_threshold_ms = ms; }
134
143 void setResyncQueueFillFraction(float fraction) {
145 }
146
154 void setMaxQueuedIFrames(int count) { max_queued_i_frames = count; }
155
158 void setTaskParameters(uint32_t stackSizeWords, uint8_t priority,
159 int core = -1) {
160 task_stack_size = stackSizeWords;
161 task_priority = priority;
162 task_core = core;
163 }
164
173 void setQueueBytes(size_t bytes) { queue_bytes = bytes > 0 ? bytes : 1; }
174
181 void setQueueUsePSRAM(bool flag) { queue_use_psram = flag; }
182
188 bool begin() {
189 frame_index = 0;
190 frame_count = 0;
195 logged_drop_burst = false;
196 awaiting_keyframe = false;
198 i_frame_count = 0;
199 p_frame_count = 0;
202 start_set = false;
203 input_start_set = false;
204 output_start_set = false;
205 have_header = false;
211 } else {
212 queue.reset();
213 }
214 task.create("PacedVideoOutput", task_stack_size, task_priority,
215 task_core);
216 bool ok = task.begin([this]() { taskLoop(); });
218 LOGI(
219 "PacedVideoOutput: %s (fps=%.2f, clock=%s, scheduling delay=%u ms, "
220 "queue=%u bytes, stack=%u words, priority=%u)",
221 ok ? "render task started" : "render task failed to start",
222 frame_period_ms > 0 ? 1000.0f / frame_period_ms : 0.0f,
223 p_clock != nullptr ? "external audio" : "wall",
224 (unsigned)scheduling_delay_ms, (unsigned)queue.size(),
225 (unsigned)task_stack_size, (unsigned)task_priority);
226 return ok;
227 }
228
230 void end() {
231 task.end();
232 task_started = false;
233 LOGI(
234 "PacedVideoOutput: render task stopped (%u frames rendered - %u "
235 "I / %u P, %u P dropped / %u I dropped, input=%.2f fps, "
236 "output=%.2f fps)",
237 (unsigned)frame_count, (unsigned)i_frame_count.load(),
238 (unsigned)p_frame_count.load(), (unsigned)dropped_frame_count,
240 }
241
262 size_t write(const uint8_t* data, size_t len) override {
263 if (len == 0) return 0;
264 if (!task_started) begin();
265 if (!input_start_set) {
267 input_start_set = true;
268 }
269 uint32_t this_frame = frame_index;
270 uint32_t target_ms = (uint32_t)((double)frame_index * frame_period_ms);
271 frame_index++;
272 bool is_key = isKeyFrame(data, len);
273
274 // See setIgnorePFrames() - unconditional, independent of queue/
275 // lateness state, and checked before the header/queue-space bookkeeping
276 // below since a dropped-here frame never touches any of that. Counts
277 // into droppedFrameCount() too (same externally visible effect as any
278 // other dropped P-frame - never rendered), with ignoredFrameCount()
279 // as the more specific breakdown of how many were dropped for this
280 // reason rather than backlog.
281 if (ignore_p_frames && !is_key) {
284 frame_count++;
285 return len;
286 }
287
288 FrameHeader header;
289 header.size = (uint32_t)len;
290 header.target_ms = target_ms;
291 header.is_key = is_key ? 1 : 0;
292 size_t needed = sizeof(header) + len;
293
294 // decode+render isn't keeping up right now. A non-keyframe is
295 // dropped outright instead of enqueued (see droppedFrameCount()) to
296 // catch back up without blocking this call, which would otherwise
297 // stall the caller's own thread (typically a demuxer's dispatch
298 // loop, delaying audio too). Two triggers: the queue is actually
299 // full, or the render task's most recently rendered frame was
300 // already more than setCatchUpThresholdFrames() frame periods late.
301 // Keyframes are never dropped here (losing one would desync every
302 // dependent P-frame) - if the queue can't fit one, this still blocks
303 // below.
304 bool queue_full = (size_t)queue.availableForWrite() < needed;
305 bool falling_behind =
306 frame_period_ms > 0 &&
307 render_lateness_ms.load() >
309 if (!is_key && (queue_full || falling_behind)) {
311 frame_count++;
312 // Rate-limited to one line per unbroken run of drops - this can
313 // trigger on every P-frame in a row for a long stretch, and
314 // logging each one individually is expensive enough (Serial I/O on
315 // this thread) to measurably worsen the backlog it's trying to
316 // fix.
317 if (!logged_drop_burst) {
318 LOGW(
319 "PacedVideoOutput: dropping P frames starting at #%u - %s "
320 "(%d/%u bytes free, need %u, render %d ms behind)",
321 (unsigned)this_frame,
322 queue_full ? "render queue full" : "catching up",
323 queue.availableForWrite(), (unsigned)queue.size(),
324 (unsigned)needed, (int)render_lateness_ms.load());
325 logged_drop_burst = true;
326 }
327 return len;
328 }
329 logged_drop_burst = false;
330
331 bool logged_wait = false;
332 while ((size_t)queue.availableForWrite() < needed) {
333 if (!logged_wait) {
334 LOGW(
335 "PacedVideoOutput: write() blocked on frame #%u - render "
336 "queue full (%d/%u bytes free, need %u)",
337 (unsigned)this_frame, queue.availableForWrite(),
338 (unsigned)queue.size(), (unsigned)needed);
339 logged_wait = true;
340 }
341 delay(1);
342 }
343 queue.writeArray((const uint8_t*)&header, sizeof(header));
344 queue.writeArray(data, len);
345 if (is_key) queued_i_frame_count++;
346 LOGD(
347 "PacedVideoOutput: queued frame #%u (%s, %u bytes, target=%u ms, "
348 "%d bytes free)",
349 (unsigned)this_frame, is_key ? "I" : "P", (unsigned)len,
350 (unsigned)target_ms, queue.availableForWrite());
351 frame_count++;
352 return len;
353 }
354
358 void flush() override {}
359
360 void setSkipRender(bool skip) override { p_target->setSkipRender(skip); }
361
365 uint32_t frameCount() const { return frame_count; }
366
375 uint32_t droppedFrameCount() const { return dropped_frame_count; }
376
380 uint32_t ignoredFrameCount() const { return ignored_frame_count; }
381
386 uint32_t droppedIFrameCount() const { return dropped_i_frame_count; }
387
391 int queuedIFrameCount() const { return queued_i_frame_count.load(); }
392
397 size_t queuedBytes() { return (size_t)queue.available(); }
398
402 size_t queueCapacityBytes() { return queue.size(); }
403
406 uint32_t frameCountI() const { return i_frame_count; }
408 uint32_t frameCountP() const { return p_frame_count; }
409
412 float avgIFrameMs() const {
413 uint32_t n = i_frame_count;
414 return n > 0 ? (float)i_frame_total_ms.load() / n : 0.0f;
415 }
418 float avgPFrameMs() const {
419 uint32_t n = p_frame_count;
420 return n > 0 ? (float)p_frame_total_ms.load() / n : 0.0f;
421 }
424 float avgFrameMs() const {
425 uint32_t n = i_frame_count + p_frame_count;
426 return n > 0 ? (float)(i_frame_total_ms + p_frame_total_ms) / n : 0.0f;
427 }
428
435 float inputFPS() {
436 if (!input_start_set) return 0.0f;
437 uint32_t elapsed = clockMs() - input_start_ms;
438 return elapsed > 0 ? (1000.0f * frame_count) / elapsed : 0.0f;
439 }
440
448 float outputFPS() {
449 if (!output_start_set) return 0.0f;
450 uint32_t elapsed = millis() - output_start_ms.load();
451 uint32_t count = i_frame_count + p_frame_count;
452 return elapsed > 0 ? (1000.0f * count) / elapsed : 0.0f;
453 }
454
455 protected:
457 TimeSource* p_clock = nullptr;
459 uint32_t scheduling_delay_ms = 0; // see setSchedulingDelayMs()
460 float catch_up_threshold_frames = 1.0f; // see setCatchUpThresholdFrames()
461 bool ignore_p_frames = false; // see setIgnorePFrames()
462 uint32_t resync_threshold_ms = 2000; // see setResyncThresholdMs()
463 float resync_queue_fill_fraction = 0.8f; // see setResyncQueueFillFraction()
464 int max_queued_i_frames = 4; // see setMaxQueuedIFrames()
465 uint64_t frame_index = 0;
466 uint32_t frame_count = 0;
467 uint32_t dropped_frame_count = 0; // see droppedFrameCount()
468 uint32_t dropped_i_frame_count = 0; // consumer-thread-only; see droppedIFrameCount()
469 uint32_t ignored_frame_count = 0; // write()-thread-only; see ignoredFrameCount()
470 // Keyframes currently queued, not yet consumed - incremented by
471 // write(), decremented by taskLoop()/drainQueueKeepingLastKeyframe();
472 // atomic since producer and consumer both touch it. See
473 // queuedIFrameCount().
474 std::atomic<int32_t> queued_i_frame_count{0};
475 bool logged_drop_burst = false; // write()-thread-only; see write()
476 uint32_t start_ms = 0;
477 bool start_set = false;
478 bool task_started = false;
479 // True from the moment a resync fires until a real keyframe renders -
480 // consumer-thread-only. A resync can leave the decoder's
481 // reference-frame state unreliable, so P-frames are discarded until a
482 // self-contained keyframe resets it.
483 bool awaiting_keyframe = false;
484 // How many ms late (or early) the most recently rendered frame was -
485 // written by taskLoop(), read by write() (its proactive drop check),
486 // hence atomic. A heuristic: stale by one frame's processing time by
487 // the time write() reads it, same as output_start_ms below.
488 std::atomic<int32_t> render_lateness_ms{0};
489
493 struct FrameHeader {
494 uint32_t size = 0;
495 uint32_t target_ms = 0;
496 uint8_t is_key = 0;
497 };
503 size_t queue_bytes = 32 * 1024; // desired capacity - see setQueueBytes()
504 size_t queue_bytes_allocated = 0; // what 'queue' was last resize()d to
505 bool queue_use_psram = false; // see setQueueUsePSRAM()
506
507 // Consumer-side-only framing state (see taskLoop()): a header already
508 // pulled out of 'queue' whose payload isn't fully written yet. Never
509 // touched by write()/the caller thread.
510 bool have_header = false;
515
517 uint32_t task_stack_size = 4096;
518 uint8_t task_priority = 2;
519 int task_core = -1;
520
521 // Per-frame-type render-time stats - written only from taskLoop(),
522 // read via the getters above. Plain atomic (single writer; not for
523 // contended updates) - the only shared state left outside 'queue'
524 // itself.
525 std::atomic<uint32_t> i_frame_count{0};
526 std::atomic<uint32_t> p_frame_count{0};
527 std::atomic<uint64_t> i_frame_total_ms{0};
528 std::atomic<uint64_t> p_frame_total_ms{0};
529
530 // inputFPS()/outputFPS() anchors, set once and never moved - averaging
531 // runs "since the very start" up to now. Deliberately different
532 // clocks: input_start_ms is clockMs() (see inputFPS()),
533 // output_start_ms is always wall-clock millis() (see outputFPS()).
534 // input_* is caller-thread-only; output_* is written by taskLoop(),
535 // hence atomic.
536 uint32_t input_start_ms = 0;
537 bool input_start_set = false;
538 std::atomic<uint32_t> output_start_ms{0};
539 std::atomic<bool> output_start_set{false};
540
544 uint32_t clockMs() { return p_clock != nullptr ? p_clock->playbackTime() : millis(); }
545
551 bool isKeyFrame(const uint8_t* data, size_t len) {
552 return p_target->isKeyFrame(data, len);
553 }
554
557 static constexpr int kDrainYieldEvery = 32;
558
584 bool drainQueueKeepingLastKeyframe(uint32_t& out_target_ms) {
585 bool found_key = false;
586 int count = 0;
587 while (true) {
588 if (!have_header) {
589 if ((size_t)queue.available() < sizeof(FrameHeader)) break;
590 queue.readArray((uint8_t*)&current_header, sizeof(FrameHeader));
591 have_header = true;
592 }
593 if ((size_t)queue.available() < current_header.size) break;
594
596 if (found_key) dropped_i_frame_count++; // superseded by this one
599 out_target_ms = current_header.target_ms;
600 found_key = true;
602 } else {
603 uint8_t scratch[256];
604 size_t remaining = current_header.size;
605 while (remaining > 0) {
606 size_t chunk = remaining < sizeof(scratch) ? remaining : sizeof(scratch);
607 size_t got = (size_t)queue.readArray(scratch, chunk);
608 if (got == 0) break; // shouldn't happen; avoids ever spinning forever
609 remaining -= got;
610 }
612 }
613 have_header = false;
614 if (++count % kDrainYieldEvery == 0) delay(1);
615 }
616 return found_key;
617 }
618
624 void taskLoop() {
625 if (!have_header) {
626 if ((size_t)queue.available() < sizeof(FrameHeader)) {
627 delay(1);
628 return;
629 }
630 queue.readArray((uint8_t*)&current_header, sizeof(FrameHeader));
631 have_header = true;
632 }
633 // The header may already be visible before its payload fully is -
634 // write() writes them as two separate writeArray() calls - so just
635 // wait for the rest; have_header stays true across calls in the
636 // meantime.
637 if ((size_t)queue.available() < current_header.size) {
638 delay(1);
639 return;
640 }
643 uint32_t target_ms = current_header.target_ms;
644 bool is_key = current_header.is_key != 0;
645 have_header = false;
646 // Leaving the queue for rendering (not a discard - see
647 // drainQueueKeepingLastKeyframe() for that side of this same counter).
648 if (is_key) queued_i_frame_count--;
649
650 // Still waiting for a keyframe after a previous resync - skip
651 // straight past any P-frame without considering its schedule,
652 // reaching the next keyframe as fast as the queue allows.
653 if (awaiting_keyframe && !is_key) {
655 frame_count++;
656 return;
657 }
658
659 if (!start_set) {
660 start_ms = clockMs();
661 start_set = true;
662 LOGI("PacedVideoOutput: playback anchored at %u ms (%s clock)",
663 (unsigned)start_ms, p_clock != nullptr ? "external audio" : "wall");
664 }
665 // scheduling_delay_ms (see setSchedulingDelayMs()) only ever pushes
666 // this later, correcting for the audio output's own buffering
667 // latency between "accepted by write()" and "actually audible".
668 uint32_t scheduled_ms = start_ms + target_ms + scheduling_delay_ms;
669 while ((int32_t)(scheduled_ms - clockMs()) > 0) {
670 delay(1);
671 }
672 // Captured right as the wait loop exits, before write()/flush() - a
673 // measure of how far behind schedule this frame already was when we
674 // started rendering it, separate from processMs (how long rendering
675 // itself then took).
676 int32_t lateness_ms = (int32_t)(clockMs() - scheduled_ms);
677 // Dropping non-keyframes alone can only prevent the backlog from
678 // growing, not shrink it - the producer can't deliver frames faster
679 // than real time. Past resync_threshold_ms, give up on the skipped
680 // content and jump the schedule anchor forward, making *this* frame
681 // "on time" - a visible jump instead of an ever-growing lag.
682 //
683 // Three independent triggers, since they can diverge: lateness_ms
684 // only reflects the frame we're about to render - dropped P-frames
685 // can keep that low even while newer bytes keep piling up in the
686 // queue, since overall render throughput can still trail the arrival
687 // rate. queue fill catches that directly, but only once
688 // substantially full; queued keyframe count (setMaxQueuedIFrames())
689 // catches it earlier still.
690 bool lateness_resync =
691 resync_threshold_ms > 0 && lateness_ms > (int32_t)resync_threshold_ms;
692 bool queue_resync =
694 (float)queue.available() >=
696 bool iframe_resync = max_queued_i_frames > 0 &&
698 if (lateness_resync || queue_resync || iframe_resync) {
699 // Keep the freshest keyframe found in the backlog instead of
700 // discarding it too (see drainQueueKeepingLastKeyframe()).
701 uint32_t new_target_ms = target_ms;
702 bool found_fresher_key = drainQueueKeepingLastKeyframe(new_target_ms);
703 LOGW(
704 "PacedVideoOutput: resyncing (%s) - %s instead of trying to "
705 "catch up",
706 lateness_resync
707 ? "lateness"
708 : (queue_resync ? "queue fill" : "I-frame backlog"),
709 found_fresher_key ? "jumping to the newest available keyframe"
710 : "jumping the schedule forward");
711 if (found_fresher_key) {
712 // Supersede whatever was already dequeued.
713 if (is_key) {
715 } else {
717 }
718 target_ms = new_target_ms;
719 is_key = true;
720 awaiting_keyframe = false;
721 } else {
722 // A resync can leave the decoder's reference state unreliable,
723 // so require a real (self-contained) keyframe before resuming -
724 // discard any P-frame in between (see the check below) - unless
725 // the frame we already have is one.
726 awaiting_keyframe = true;
727 }
728 // Recomputed for whichever frame we're actually about to render.
729 start_ms = clockMs() - target_ms - scheduling_delay_ms;
730 scheduled_ms = start_ms + target_ms + scheduling_delay_ms; // == clockMs() now
731 lateness_ms = 0;
732 }
733 if (awaiting_keyframe) {
734 if (!is_key) {
736 frame_count++;
738 return;
739 }
740 awaiting_keyframe = false;
741 }
742 // Published for write()'s proactive drop check (see
743 // setCatchUpThresholdFrames()) before doing the possibly-slow render
744 // call, so a caller blocked in write() sees it as soon as possible
745 // rather than only after this frame finishes too.
746 render_lateness_ms = lateness_ms;
747 uint32_t processStart = millis();
749 p_target->flush();
750 uint32_t processMs = millis() - processStart;
751 // See VideoOutput::hadOutput() - true for every synchronous decoder
752 // (the common case), but a decoder like MPGDecoder can legitimately
753 // do real decode work here without a picture actually reaching the
754 // screen yet (held back for B-picture display-order reordering), or
755 // emit an earlier held picture instead. Only count/time this call as
756 // a rendered frame when a picture was actually produced - otherwise
757 // outputFPS()/frameCountI()/frameCountP()/avgFrameMs() would
758 // overcount calls that did no real rendering work.
759 if (p_target->hadOutput()) {
760 if (!output_start_set) {
762 output_start_set = true;
763 }
764 if (is_key) {
766 i_frame_total_ms += processMs;
767 } else {
769 p_frame_total_ms += processMs;
770 }
771 LOGD(
772 "PacedVideoOutput: rendered %s frame - scheduled=%u actual=%u "
773 "(%d ms late), process=%u ms",
774 is_key ? "I" : "P", (unsigned)scheduled_ms, (unsigned)clockMs(),
775 (int)lateness_ms, (unsigned)processMs);
776 } else {
777 LOGD(
778 "PacedVideoOutput: decoded %s frame, no picture emitted yet "
779 "(scheduled=%u actual=%u, %d ms late), process=%u ms",
780 is_key ? "I" : "P", (unsigned)scheduled_ms, (unsigned)clockMs(),
781 (int)lateness_ms, (unsigned)processMs);
782 }
783 if (frame_period_ms > 0 && processMs > (uint32_t)frame_period_ms) {
784 LOGW(
785 "PacedVideoOutput: %s frame took %u ms to render - longer than "
786 "the %.1f ms frame period, falling behind",
787 is_key ? "I" : "P", (unsigned)processMs, frame_period_ms);
788 }
789 }
790};
791
792} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
Buffers a small, configurable amount of video (see setQueueBytes()) and renders it frame by frame fro...
Definition PacedVideoOutput.h:62
float avgFrameMs() const
Definition PacedVideoOutput.h:424
void flush() override
Definition PacedVideoOutput.h:358
size_t queue_bytes
Definition PacedVideoOutput.h:503
uint32_t input_start_ms
Definition PacedVideoOutput.h:536
PacedVideoOutput(VideoOutput &target, float fps=0, uint32_t schedulingDelayMs=0)
Definition PacedVideoOutput.h:73
std::atomic< int32_t > render_lateness_ms
Definition PacedVideoOutput.h:488
Vector< uint8_t > frame_buf
Definition PacedVideoOutput.h:514
std::atomic< uint64_t > i_frame_total_ms
Definition PacedVideoOutput.h:527
float resync_queue_fill_fraction
Definition PacedVideoOutput.h:463
float catch_up_threshold_frames
Definition PacedVideoOutput.h:460
bool have_header
Definition PacedVideoOutput.h:510
int task_core
Definition PacedVideoOutput.h:519
RingBufferSPSC< uint8_t > queue
Definition PacedVideoOutput.h:502
bool logged_drop_burst
Definition PacedVideoOutput.h:475
uint32_t droppedIFrameCount() const
Definition PacedVideoOutput.h:386
std::atomic< uint64_t > p_frame_total_ms
Definition PacedVideoOutput.h:528
uint32_t scheduling_delay_ms
Definition PacedVideoOutput.h:459
bool ignore_p_frames
Definition PacedVideoOutput.h:461
void setResyncThresholdMs(uint32_t ms)
Definition PacedVideoOutput.h:133
uint32_t resync_threshold_ms
Definition PacedVideoOutput.h:462
std::atomic< int32_t > queued_i_frame_count
Definition PacedVideoOutput.h:474
bool input_start_set
Definition PacedVideoOutput.h:537
uint32_t start_ms
Definition PacedVideoOutput.h:476
float inputFPS()
Definition PacedVideoOutput.h:435
float avgIFrameMs() const
Definition PacedVideoOutput.h:412
void taskLoop()
Definition PacedVideoOutput.h:624
std::atomic< bool > output_start_set
Definition PacedVideoOutput.h:539
uint32_t frameCount() const
Definition PacedVideoOutput.h:365
bool begin()
Definition PacedVideoOutput.h:188
uint32_t frameCountI() const
Definition PacedVideoOutput.h:406
std::atomic< uint32_t > output_start_ms
Definition PacedVideoOutput.h:538
uint8_t task_priority
Definition PacedVideoOutput.h:518
uint32_t droppedFrameCount() const
Definition PacedVideoOutput.h:375
size_t queue_bytes_allocated
Definition PacedVideoOutput.h:504
size_t write(const uint8_t *data, size_t len) override
Definition PacedVideoOutput.h:262
size_t queueCapacityBytes()
Definition PacedVideoOutput.h:402
uint64_t frame_index
Definition PacedVideoOutput.h:465
TimeSource * p_clock
Definition PacedVideoOutput.h:457
void setMaxQueuedIFrames(int count)
Definition PacedVideoOutput.h:154
std::atomic< uint32_t > p_frame_count
Definition PacedVideoOutput.h:526
Task task
Definition PacedVideoOutput.h:516
bool awaiting_keyframe
Definition PacedVideoOutput.h:483
void setSkipRender(bool skip) override
Definition PacedVideoOutput.h:360
bool task_started
Definition PacedVideoOutput.h:478
bool drainQueueKeepingLastKeyframe(uint32_t &out_target_ms)
Definition PacedVideoOutput.h:584
bool ignorePFrames() const
Definition PacedVideoOutput.h:123
uint32_t frameCountP() const
Number of P-frames (non-key frames) actually rendered so far.
Definition PacedVideoOutput.h:408
VideoOutput * p_target
Definition PacedVideoOutput.h:456
uint32_t ignoredFrameCount() const
Definition PacedVideoOutput.h:380
float frame_period_ms
Definition PacedVideoOutput.h:458
bool isKeyFrame(const uint8_t *data, size_t len)
Definition PacedVideoOutput.h:551
bool start_set
Definition PacedVideoOutput.h:477
void setFps(float fps)
Definition PacedVideoOutput.h:81
void setSchedulingDelayMs(uint32_t delayMs)
Definition PacedVideoOutput.h:100
void end()
Stops the background render task.
Definition PacedVideoOutput.h:230
FrameHeader current_header
Definition PacedVideoOutput.h:511
size_t queuedBytes()
Definition PacedVideoOutput.h:397
int max_queued_i_frames
Definition PacedVideoOutput.h:464
void setQueueUsePSRAM(bool flag)
Definition PacedVideoOutput.h:181
float outputFPS()
Definition PacedVideoOutput.h:448
void setResyncQueueFillFraction(float fraction)
Definition PacedVideoOutput.h:143
void setCatchUpThresholdFrames(float frames)
Definition PacedVideoOutput.h:109
static constexpr int kDrainYieldEvery
Definition PacedVideoOutput.h:557
uint32_t dropped_i_frame_count
Definition PacedVideoOutput.h:468
float avgPFrameMs() const
Definition PacedVideoOutput.h:418
void setAudioClock(TimeSource &clock)
Definition PacedVideoOutput.h:89
uint32_t dropped_frame_count
Definition PacedVideoOutput.h:467
bool queue_use_psram
Definition PacedVideoOutput.h:505
int queuedIFrameCount() const
Definition PacedVideoOutput.h:391
uint32_t clockMs()
Definition PacedVideoOutput.h:544
uint32_t frame_count
Definition PacedVideoOutput.h:466
std::atomic< uint32_t > i_frame_count
Definition PacedVideoOutput.h:525
void setQueueBytes(size_t bytes)
Definition PacedVideoOutput.h:173
uint32_t task_stack_size
Definition PacedVideoOutput.h:517
void setTaskParameters(uint32_t stackSizeWords, uint8_t priority, int core=-1)
Definition PacedVideoOutput.h:158
void setIgnorePFrames(bool active)
Definition PacedVideoOutput.h:122
uint32_t ignored_frame_count
Definition PacedVideoOutput.h:469
Lock-free Single-Producer Single-Consumer ring buffer.
Definition RingBufferSPSC.h:50
size_t size() override
Definition RingBufferSPSC.h:150
int available() override
provides the number of entries that are available to read
Definition RingBufferSPSC.h:113
int availableForWrite() override
provides the number of entries that are available to write
Definition RingBufferSPSC.h:119
int writeArray(const T data[], int len) override
Fills the buffer data.
Definition RingBufferSPSC.h:71
bool resize(size_t capacity) override
Resizes the buffer if supported: returns false if not supported.
Definition RingBufferSPSC.h:137
void setUsePSRAM(bool flag)
Definition RingBufferSPSC.h:135
void reset() override
clears the buffer
Definition RingBufferSPSC.h:127
int readArray(T data[], int len) override
reads multiple values
Definition RingBufferSPSC.h:91
FreeRTOS task.
Definition Task.h:26
bool create(const char *name, int stackSizeWords, int priority=1, int core=-1)
Definition Task.h:54
void end()
suspends the task
Definition Task.h:105
bool begin(std::function< void()> process)
Definition Task.h:94
Interface for classes that can provide time information - two distinct notions of "now",...
Definition AudioTypes.h:580
virtual uint32_t playbackTime()
Definition AudioTypes.h:598
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
void clear()
Definition Vector.h:176
bool resize(size_t newSize, T value)
Definition Vector.h:266
T * data()
Definition Vector.h:316
int size()
Definition Vector.h:178
Abstract class for video playback. This class is used to assemble a complete video frame in memory....
Definition Video.h:210
virtual size_t write(const uint8_t *data, size_t len)=0
virtual bool hadOutput() const
Definition Video.h:255
virtual void setSkipRender(bool skip)
Definition Video.h:224
virtual void flush()
Definition Video.h:216
virtual bool isKeyFrame(const uint8_t *data, size_t len)
Definition Video.h:238
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
void delay(uint32_t ms)
Definition Arduino.h:259
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:260
Definition PacedVideoOutput.h:493
uint8_t is_key
Definition PacedVideoOutput.h:496
uint32_t size
Definition PacedVideoOutput.h:494
uint32_t target_ms
Definition PacedVideoOutput.h:495