libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
videotestpattern.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/error.h>
16#include <promeki/result.h>
17#include <promeki/color.h>
18#include <promeki/imagedesc.h>
21#include <promeki/timecode.h>
22#include <promeki/enums.h>
23#include <promeki/fastfont.h>
24#include <promeki/motionband.h>
25
26PROMEKI_NAMESPACE_BEGIN
27
28class UncompressedVideoPayload;
29
95class VideoTestPattern {
96 public:
98 using Pattern = VideoPattern;
99
101 VideoTestPattern();
102
104 ~VideoTestPattern();
105
106 VideoTestPattern(const VideoTestPattern &) = delete;
107 VideoTestPattern &operator=(const VideoTestPattern &) = delete;
108
110 Pattern pattern() const { return _pattern; }
111
121 void setPattern(Pattern pattern);
122
124 const Color &solidColor() const { return _solidColor; }
125
131 void setSolidColor(const Color &color);
132
133 // ---- Burn-in configuration ----
134
136 bool burnEnabled() const { return _burnEnabled; }
137
147 void setBurnEnabled(bool val) { _burnEnabled = val; }
148
150 const String &burnFontFilename() const { return _burnFontFilename; }
151
156 void setBurnFontFilename(const String &path);
157
165 int burnFontSize() const { return _burnFontSize; }
166
173 void setBurnFontSize(int px);
174
176 const Color &burnTextColor() const { return _burnTextColor; }
177
179 void setBurnTextColor(const Color &c);
180
182 const Color &burnBackgroundColor() const { return _burnBackgroundColor; }
183
185 void setBurnBackgroundColor(const Color &c);
186
188 bool burnDrawBackground() const { return _burnDrawBackground; }
189
200 void setBurnDrawBackground(bool val) { _burnDrawBackground = val; }
201
203 BurnPosition burnPosition() const { return _burnPosition; }
204
206 void setBurnPosition(BurnPosition pos) { _burnPosition = pos; }
207
213 int burnTopReserved() const { return _burnTopReserved; }
214
230 void setBurnTopReserved(int lines) { _burnTopReserved = (lines > 0) ? lines : 0; }
231
251 SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
252 createPayload(const ImageDesc &desc, double motionOffset = 0.0,
253 const Timecode &currentTimecode = Timecode()) const;
254
265 Error applyBurn(UncompressedVideoPayload &inout, const String &burnText) const;
266
272 void render(UncompressedVideoPayload &img, double motionOffset = 0.0) const;
273
292 void setAllocator(MediaIOAllocator::Ptr allocator);
293
297 MediaIOAllocator::Ptr allocator() const { return _allocator; }
298
299 // ---- Motion band ----
300
314 MotionBand &motionBand() { return _motionBand; }
315
317 const MotionBand &motionBand() const { return _motionBand; }
318
328 Error applyMotionBand(UncompressedVideoPayload &inout, uint64_t frameCount) const {
329 return _motionBand.apply(inout, frameCount);
330 }
331
332 private:
333 // Background pattern state
334 VideoPattern _pattern = VideoPattern::ColorBars;
335 Color _solidColor;
336
337 // Burn-in config
338 bool _burnEnabled = false;
339 String _burnFontFilename;
340 int _burnFontSize = 36;
341 // Effective font size actually passed to FastFont.
342 // Equals _burnFontSize when that is > 0; when
343 // _burnFontSize is 0 (auto) it is computed from the
344 // rendered image height at renderBurn() time. Tracked
345 // separately so image-size changes can trigger a font
346 // reconfigure via _burnFontConfigDirty.
347 mutable int _burnEffectiveFontSize = 36;
348 Color _burnTextColor = Color::White;
349 Color _burnBackgroundColor = Color::Black;
350 bool _burnDrawBackground = true;
351 BurnPosition _burnPosition = BurnPosition::BottomCenter;
352 int _burnTopReserved = 0;
353
354 // Generic image cache: a small fixed array of slots
355 // shared by all patterns. Slot meanings are local to
356 // each pattern's branch in create() — for example
357 // AvSync uses slot 0 for "white marker" and slot 1 for
358 // "black non-marker", while a static pattern uses slot
359 // 0 for its pre-rendered background. Patterns are
360 // mutually exclusive at any given time, so reusing the
361 // same slot index across patterns is fine.
362 //
363 // Any change to _pattern, _solidColor, or the
364 // requested ImageDesc dumps the entire cache; the next
365 // create() call rebuilds whatever it needs. Two
366 // renderSolid() calls (the most expensive thing the
367 // current patterns can do on a rebuild) are essentially
368 // free, so a simpler dump-on-change policy is the right
369 // tradeoff over per-key invalidation.
370 static constexpr int CacheSlotCount = 2;
371 mutable SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
372 _cachedPayloads[CacheSlotCount];
373 mutable size_t _cacheW = 0;
374 mutable size_t _cacheH = 0;
375 mutable int _cachePixelFormatId = 0;
376
377 // Optional allocator override. Null = use
378 // MediaIOAllocator::defaultAllocator() so freshly-
379 // constructed VideoTestPatterns behave exactly as
380 // before the allocator framework existed.
381 MediaIOAllocator::Ptr _allocator;
382
383 // Embedded motion band — parallel to burn-in. Owns
384 // its own pre-rendered cache of N band frames in the
385 // target pixel format and stamps the appropriate one
386 // into the frame from applyMotionBand().
387 MotionBand _motionBand;
388
389 // Burn font — lazily constructed the first time burn
390 // actually runs, because FastFont needs a PaintEngine
391 // (and thus a pixel format) at construction time.
392 mutable FastFont::UPtr _burnFont;
393 mutable bool _burnFontConfigDirty = true;
394
395 // Cached @ref PaintEngine::Pixel for @ref _burnBackgroundColor.
396 // applyBurn() runs a single fillRect per frame; building
397 // that pixel via @c PaintEngine::createPixel(Color) goes
398 // through a Color::convert into the target colour model
399 // (sRGB→Rec.709 YCbCr Limited on NV12 / YUV outputs),
400 // which is non-trivial. We cache it and invalidate
401 // whenever the configured bg colour changes (see
402 // @ref setBurnBackgroundColor) or the target pixel format
403 // changes (detected via @ref _cachedBgPixelFormat).
404 mutable PaintEngine::Pixel _cachedBgPixel;
405 mutable PixelFormat _cachedBgPixelFormat;
406
407 bool isStaticPattern() const;
408
425 template <typename Builder>
426 SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
427 cachedPayload(int slotIndex, const ImageDesc &desc, Builder &&build) const {
428 if (_cacheW != desc.width() || _cacheH != desc.height() ||
429 _cachePixelFormatId != static_cast<int>(desc.pixelFormat().id())) {
430 invalidatePayloadCache();
431 _cacheW = desc.width();
432 _cacheH = desc.height();
433 _cachePixelFormatId = static_cast<int>(desc.pixelFormat().id());
434 }
435 auto &slot = _cachedPayloads[slotIndex];
436 if (!slot.isValid()) {
437 // Route allocation through the
438 // installed allocator (typically
439 // SystemCow when wired by TpgMediaIO).
440 // Null _allocator falls back to the
441 // process-wide default — no behaviour
442 // change for callers that never wire
443 // one up.
444 slot = _allocator.isValid() ? _allocator->allocateVideoPayload(desc)
445 : UncompressedVideoPayload::allocate(desc);
446 if (slot.isValid()) {
447 build(slot);
448 // Seal the cached payload so
449 // subsequent ensureExclusive()
450 // detaches a SystemCow-backed
451 // buffer cheaply (MAP_PRIVATE
452 // clone, not full-frame memcpy).
453 // Default backends no-op.
454 (void)slot->data().seal();
455 }
456 }
457 return slot;
458 }
459
461 void invalidatePayloadCache() const;
462
463 void applyBurnFontConfig() const;
464 void computeBurnPosition(int frameW, int frameH, int textW, int totalH, int ascender, int &x,
465 int &y) const;
466
479 ImageDesc rgbScratchDesc(const ImageDesc &target) const;
480
481 void renderColorBars(UncompressedVideoPayload &img, double offset, bool full) const;
482 void renderRamp(UncompressedVideoPayload &img, double offset) const;
483 void renderGrid(UncompressedVideoPayload &img, double offset) const;
484 void renderCrosshatch(UncompressedVideoPayload &img, double offset) const;
485 void renderCheckerboard(UncompressedVideoPayload &img, double offset) const;
486 void renderZonePlate(UncompressedVideoPayload &img, double phase) const;
487 void renderNoise(UncompressedVideoPayload &img) const;
488 void renderSolid(UncompressedVideoPayload &img, const Color &color) const;
489 void renderColorChecker(UncompressedVideoPayload &img) const;
490 void renderSMPTE219(UncompressedVideoPayload &img) const;
491 void renderMultiBurst(UncompressedVideoPayload &img) const;
492 void renderLimitRange(UncompressedVideoPayload &img) const;
493 void renderCircularZone(UncompressedVideoPayload &img, double phase) const;
494 void renderAlignment(UncompressedVideoPayload &img) const;
495 void renderSDIPathological(UncompressedVideoPayload &img, bool isEQ) const;
496};
497
498PROMEKI_NAMESPACE_END
499
500#endif // PROMEKI_ENABLE_PROAV