libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
framesync.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_PROAV
13#include <promeki/namespace.h>
14#include <promeki/string.h>
15#include <promeki/frame.h>
16#include <promeki/audiodesc.h>
20#include <promeki/framecount.h>
21#include <promeki/framenumber.h>
22#include <promeki/framerate.h>
23#include <promeki/duration.h>
24#include <promeki/error.h>
25#include <promeki/result.h>
26#include <promeki/mutex.h>
28#include <promeki/list.h>
30#include <promeki/atomic.h>
31#include <promeki/clock.h>
32
33PROMEKI_NAMESPACE_BEGIN
34
35class SyntheticClock;
36
91class FrameSync {
92 public:
97 enum class InputOverflowPolicy {
102 DropOldest,
103
108 Block,
109 };
110
119 struct PullResult {
121 Frame frame;
122
124 FrameNumber frameIndex{0};
125
128 FrameCount framesRepeated{0};
129
132 FrameCount framesDropped{0};
133
135 Duration error;
136 };
137
139 FrameSync();
140
145 explicit FrameSync(const String &name);
146
148 ~FrameSync();
149
150 FrameSync(const FrameSync &) = delete;
151 FrameSync &operator=(const FrameSync &) = delete;
152 FrameSync(FrameSync &&) = delete;
153 FrameSync &operator=(FrameSync &&) = delete;
154
156 void setName(const String &name) { _name = name; }
157
159 const String &name() const { return _name; }
160
168 void setTargetFrameRate(const FrameRate &fps);
169
171 const FrameRate &targetFrameRate() const { return _targetFrameRate; }
172
180 void setTargetAudioDesc(const AudioDesc &desc);
181
183 const AudioDesc &targetAudioDesc() const { return _targetAudioDesc; }
184
192 void setClock(const Clock::Ptr &clock);
193
195 Clock::Ptr clock() const { return _clock; }
196
207 void setInputQueueCapacity(int capacity);
208
210 int inputQueueCapacity() const { return _queueCapacity; }
211
218 void setInputOverflowPolicy(InputOverflowPolicy policy);
219
221 InputOverflowPolicy inputOverflowPolicy() const { return _overflowPolicy; }
222
230 void reset();
231
242 void reset(int64_t originNs);
243
255 Error pushFrame(const Frame &frame);
256
265 void pushEndOfStream();
266
288 Result<PullResult> pullFrame(bool blockOnEmpty = true);
289
293 void interrupt();
294
305 void clearInterrupt();
306
324 void resetSourceRateEstimator();
325
326 // ---- Stats ----
327
329 FrameCount framesIn() const { return FrameCount(_framesIn.value()); }
330
332 FrameCount framesOut() const { return FrameCount(_framesOut.value()); }
333
335 FrameCount framesRepeated() const { return FrameCount(_framesRepeated.value()); }
336
338 FrameCount framesDropped() const { return FrameCount(_framesDropped.value()); }
339
341 FrameCount overflowDrops() const { return FrameCount(_overflowDrops.value()); }
342
344 Duration accumulatedError() const { return Duration::fromNanoseconds(_accumulatedErrorNs); }
345
347 double currentResampleRatio() const { return _currentResampleRatio; }
348
350 double currentSourceAudioRate() const { return _sourceAudioRateHz; }
351
355 double currentSourceVideoRate() const { return _sourceVideoRateHz; }
356
357 private:
358 struct QueuedFrame {
359 Frame frame;
360 int64_t videoTsNs = 0; // source video timestamp
361 bool hasVideoTs = false;
362 int64_t audioTsNs = 0; // first audio timestamp
363 bool hasAudioTs = false;
364 };
365
366 // Initial setup done under the mutex on demand.
367 void ensureInitialised();
368 void resetLocked(bool setExplicitOrigin, int64_t originNs);
369
370 // Pull-path helpers (all assume _mutex is NOT held).
371 void selectVideo(int64_t sourceTimeNs, int64_t nextSourceTimeNs, VideoPayload::Ptr &outVideo,
372 int64_t &outRepeated, int64_t &outDropped);
373 PcmAudioPayload::Ptr produceAudio(int64_t targetSamples);
374 void updateSourceAudioRate(const PcmAudioPayload &audio, int64_t audioTsNs);
375 void updateSourceVideoRate(int64_t videoTsNs);
376
377 // Periodic debug log.
378 void periodicDebugLog(int64_t nowNs);
379
380 // Configuration.
381 String _name;
382 FrameRate _targetFrameRate;
383 AudioDesc _targetAudioDesc;
384 Clock::Ptr _clock;
385 SyntheticClock *_syntheticClock = nullptr; // cached downcast
386 int _queueCapacity = 8;
387 InputOverflowPolicy _overflowPolicy = InputOverflowPolicy::DropOldest;
388
389 // Shared state (all guarded by _mutex unless noted).
390 mutable Mutex _mutex;
391 WaitCondition _cv;
392
393 // Input queue.
394 List<QueuedFrame> _queue;
395 bool _eos = false;
396 bool _interrupted = false;
397
398 // Timeline state.
399 bool _started = false;
400 bool _explicitOrigin = false;
401 int64_t _originNs = 0;
402 int64_t _framePeriodNs = 0;
403 FrameCount _frameCount{0};
404
405 // Source-origin anchoring — captured on first pushed frame.
406 bool _sourceOriginValid = false;
407 int64_t _sourceVideoOriginNs = 0;
408 int64_t _sourceAudioOriginNs = 0;
409
410 // Held "current" video payload (used for repeats when
411 // no new input qualifies for this pull). Carries the
412 // source payload with its original metadata.
413 VideoPayload::Ptr _heldVideo;
414 int64_t _heldVideoSourceTsNs = 0;
415 bool _hasHeldVideo = false;
416
417 // FrameSyncDrop/FrameSyncRepeat metadata state.
418 // _pendingFrameSyncDrops accumulates input drops across
419 // pulls that emit a repeat — the spec requires
420 // FrameSyncDrop to be zero on repeat outputs, so any
421 // drops that occur while the output is stuck on a
422 // repeat are deferred to the next fresh emit.
423 // _frameSyncRepeatIndex is the position of the current
424 // output within a repeat sequence (0 on fresh emit;
425 // 1, 2, ... on successive repeats).
426 int64_t _pendingFrameSyncDrops = 0;
427 int64_t _frameSyncRepeatIndex = 0;
428
429 // Audio resampler pipeline.
430 AudioResampler::UPtr _resampler;
431 List<PcmAudioPayload::Ptr> _audioInput; // pending input audio, FIFO
432 int64_t _audioSamplesConsumed = 0; // of current front audio
433
434 // Rate tracking.
435 double _sourceAudioRateHz = 0.0; // LPF'd source rate
436 double _sourceVideoRateHz = 0.0; // LPF'd source video rate
437 double _currentResampleRatio = 1.0;
438 int64_t _lastAudioTsForRateNs = 0;
439 int64_t _lastAudioTsSamples = 0; // samples covered by last push
440 int64_t _lastVideoTsForRateNs = 0;
441
442 // Error / logging.
443 int64_t _accumulatedErrorNs = 0;
444 int64_t _lastPeriodicLogNs = 0;
445 FrameCount _frameCountAtLastLog{0};
446 FrameCount _lastEmitFrameCount = FrameCount::unknown(); // debug only
447
448 // PLL-style deadline bias. The per-pull measured
449 // wake-error feeds this LPF; the next deadline is
450 // shifted earlier by this amount so any systematic
451 // bias (sleep latency, clock interpolation rate
452 // mismatch, per-pull work time) self-corrects.
453 int64_t _deadlineBiasNs = 0;
454
455 // Atomic stats (readable from any thread without lock).
456 Atomic<int64_t> _framesIn;
457 Atomic<int64_t> _framesOut;
458 Atomic<int64_t> _framesRepeated;
459 Atomic<int64_t> _framesDropped;
460 Atomic<int64_t> _overflowDrops;
461};
462
463PROMEKI_NAMESPACE_END
464
465#endif // PROMEKI_ENABLE_PROAV