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_tpg.h>
23// PaintEngine is needed for the cached _cachedBgPixel member regardless of
24// FreeType; fastfont.h used to pull it in transitively, so include it
25// directly now that the FastFont include is feature-gated.
26#include <promeki/paintengine.h>
27#if PROMEKI_ENABLE_FREETYPE
28#include <promeki/fastfont.h>
29#endif
30#include <promeki/motionband.h>
31
32PROMEKI_NAMESPACE_BEGIN
33
34class UncompressedVideoPayload;
35
101class VideoTestPattern {
102 public:
104 using Pattern = VideoPattern;
105
107 VideoTestPattern();
108
110 ~VideoTestPattern();
111
112 VideoTestPattern(const VideoTestPattern &) = delete;
113 VideoTestPattern &operator=(const VideoTestPattern &) = delete;
114
116 Pattern pattern() const { return _pattern; }
117
127 void setPattern(Pattern pattern);
128
130 const Color &solidColor() const { return _solidColor; }
131
137 void setSolidColor(const Color &color);
138
139 // ---- Burn-in configuration ----
140
142 bool burnEnabled() const { return _burnEnabled; }
143
153 void setBurnEnabled(bool val) { _burnEnabled = val; }
154
156 const String &burnFontFilename() const { return _burnFontFilename; }
157
162 void setBurnFontFilename(const String &path);
163
171 int burnFontSize() const { return _burnFontSize; }
172
179 void setBurnFontSize(int px);
180
182 const Color &burnTextColor() const { return _burnTextColor; }
183
185 void setBurnTextColor(const Color &c);
186
188 const Color &burnBackgroundColor() const { return _burnBackgroundColor; }
189
191 void setBurnBackgroundColor(const Color &c);
192
194 bool burnDrawBackground() const { return _burnDrawBackground; }
195
206 void setBurnDrawBackground(bool val) { _burnDrawBackground = val; }
207
209 BurnPosition burnPosition() const { return _burnPosition; }
210
212 void setBurnPosition(BurnPosition pos) { _burnPosition = pos; }
213
219 int burnTopReserved() const { return _burnTopReserved; }
220
236 void setBurnTopReserved(int lines) { _burnTopReserved = (lines > 0) ? lines : 0; }
237
257 SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
258 createPayload(const ImageDesc &desc, double motionOffset = 0.0,
259 const Timecode &currentTimecode = Timecode()) const;
260
271 Error applyBurn(UncompressedVideoPayload &inout, const String &burnText) const;
272
278 void render(UncompressedVideoPayload &img, double motionOffset = 0.0) const;
279
298 void setAllocator(MediaIOAllocator::Ptr allocator);
299
303 MediaIOAllocator::Ptr allocator() const { return _allocator; }
304
305 // ---- Motion band ----
306
320 MotionBand &motionBand() { return _motionBand; }
321
323 const MotionBand &motionBand() const { return _motionBand; }
324
334 Error applyMotionBand(UncompressedVideoPayload &inout, uint64_t frameCount) const {
335 return _motionBand.apply(inout, frameCount);
336 }
337
338 private:
339 // Background pattern state
340 VideoPattern _pattern = VideoPattern::ColorBars;
341 Color _solidColor;
342
343 // Burn-in config
344 bool _burnEnabled = false;
345 String _burnFontFilename;
346 int _burnFontSize = 36;
347 // Effective font size actually passed to FastFont.
348 // Equals _burnFontSize when that is > 0; when
349 // _burnFontSize is 0 (auto) it is computed from the
350 // rendered image height at renderBurn() time. Tracked
351 // separately so image-size changes can trigger a font
352 // reconfigure via _burnFontConfigDirty.
353 mutable int _burnEffectiveFontSize = 36;
354 Color _burnTextColor = Color::White;
355 Color _burnBackgroundColor = Color::Black;
356 bool _burnDrawBackground = true;
357 BurnPosition _burnPosition = BurnPosition::BottomCenter;
358 int _burnTopReserved = 0;
359
360 // Generic image cache: a small fixed array of slots
361 // shared by all patterns. Slot meanings are local to
362 // each pattern's branch in create() — for example
363 // AvSync uses slot 0 for "white marker" and slot 1 for
364 // "black non-marker", while a static pattern uses slot
365 // 0 for its pre-rendered background. Patterns are
366 // mutually exclusive at any given time, so reusing the
367 // same slot index across patterns is fine.
368 //
369 // Any change to _pattern, _solidColor, or the
370 // requested ImageDesc dumps the entire cache; the next
371 // create() call rebuilds whatever it needs. Two
372 // renderSolid() calls (the most expensive thing the
373 // current patterns can do on a rebuild) are essentially
374 // free, so a simpler dump-on-change policy is the right
375 // tradeoff over per-key invalidation.
376 static constexpr int CacheSlotCount = 2;
377 mutable SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
378 _cachedPayloads[CacheSlotCount];
379 mutable size_t _cacheW = 0;
380 mutable size_t _cacheH = 0;
381 mutable int _cachePixelFormatId = 0;
382
383 // Optional allocator override. Null = use
384 // MediaIOAllocator::defaultAllocator() so freshly-
385 // constructed VideoTestPatterns behave exactly as
386 // before the allocator framework existed.
387 MediaIOAllocator::Ptr _allocator;
388
389 // Embedded motion band — parallel to burn-in. Owns
390 // its own pre-rendered cache of N band frames in the
391 // target pixel format and stamps the appropriate one
392 // into the frame from applyMotionBand().
393 MotionBand _motionBand;
394
395 // Burn font — lazily constructed the first time burn
396 // actually runs, because FastFont needs a PaintEngine
397 // (and thus a pixel format) at construction time. Only
398 // present when FreeType is compiled in; without it
399 // @ref applyBurn returns @c Error::FontUnavailable.
400#if PROMEKI_ENABLE_FREETYPE
401 mutable FastFont::UPtr _burnFont;
402#endif
403 mutable bool _burnFontConfigDirty = true;
404
405 // Cached @ref PaintEngine::Pixel for @ref _burnBackgroundColor.
406 // applyBurn() runs a single fillRect per frame; building
407 // that pixel via @c PaintEngine::createPixel(Color) goes
408 // through a Color::convert into the target colour model
409 // (sRGB→Rec.709 YCbCr Limited on NV12 / YUV outputs),
410 // which is non-trivial. We cache it and invalidate
411 // whenever the configured bg colour changes (see
412 // @ref setBurnBackgroundColor) or the target pixel format
413 // changes (detected via @ref _cachedBgPixelFormat).
414 mutable PaintEngine::Pixel _cachedBgPixel;
415 mutable PixelFormat _cachedBgPixelFormat;
416
417 bool isStaticPattern() const;
418
435 template <typename Builder>
436 SharedPtr<UncompressedVideoPayload, true, UncompressedVideoPayload>
437 cachedPayload(int slotIndex, const ImageDesc &desc, Builder &&build) const {
438 if (_cacheW != desc.width() || _cacheH != desc.height() ||
439 _cachePixelFormatId != static_cast<int>(desc.pixelFormat().id())) {
440 invalidatePayloadCache();
441 _cacheW = desc.width();
442 _cacheH = desc.height();
443 _cachePixelFormatId = static_cast<int>(desc.pixelFormat().id());
444 }
445 auto &slot = _cachedPayloads[slotIndex];
446 if (!slot.isValid()) {
447 // Route allocation through the
448 // installed allocator (typically
449 // SystemCow when wired by TpgMediaIO).
450 // Null _allocator falls back to the
451 // process-wide default — no behaviour
452 // change for callers that never wire
453 // one up.
454 slot = _allocator.isValid() ? _allocator->allocateVideoPayload(desc)
455 : UncompressedVideoPayload::allocate(desc);
456 if (slot.isValid()) {
457 build(slot);
458 // Seal the cached payload so
459 // subsequent ensureExclusive()
460 // detaches a SystemCow-backed
461 // buffer cheaply (MAP_PRIVATE
462 // clone, not full-frame memcpy).
463 // Default backends no-op.
464 (void)slot->data().seal();
465 }
466 }
467 return slot;
468 }
469
471 void invalidatePayloadCache() const;
472
473 void applyBurnFontConfig() const;
474 void computeBurnPosition(int frameW, int frameH, int textW, int totalH, int ascender, int &x,
475 int &y) const;
476
489 ImageDesc rgbScratchDesc(const ImageDesc &target) const;
490
491 void renderColorBars(UncompressedVideoPayload &img, double offset, bool full) const;
492 void renderRamp(UncompressedVideoPayload &img, double offset) const;
493 void renderGrid(UncompressedVideoPayload &img, double offset) const;
494 void renderCrosshatch(UncompressedVideoPayload &img, double offset) const;
495 void renderCheckerboard(UncompressedVideoPayload &img, double offset) const;
496 void renderZonePlate(UncompressedVideoPayload &img, double phase) const;
497 void renderNoise(UncompressedVideoPayload &img) const;
498 void renderSolid(UncompressedVideoPayload &img, const Color &color) const;
499 void renderColorChecker(UncompressedVideoPayload &img) const;
500 void renderSMPTE219(UncompressedVideoPayload &img) const;
501 void renderMultiBurst(UncompressedVideoPayload &img) const;
502 void renderLimitRange(UncompressedVideoPayload &img) const;
503 void renderCircularZone(UncompressedVideoPayload &img, double phase) const;
504 void renderAlignment(UncompressedVideoPayload &img) const;
505 void renderSDIPathological(UncompressedVideoPayload &img, bool isEQ) const;
506};
507
508PROMEKI_NAMESPACE_END
509
510#endif // PROMEKI_ENABLE_PROAV