11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
25PROMEKI_NAMESPACE_BEGIN
60 PROMEKI_DATATYPE(Timecode, DataTypeTimecode, 2)
62 using DigitType = uint8_t;
63 using FlagsType = uint32_t;
66 Error writeToStream(DataStream &s) const;
68 template <uint32_t V> static Result<Timecode> readFromStream(DataStream &s);
102 DropFrame = 0x00000001,
103 FirstField = 0x00000002
118 Mode(
const VtcFormat *format) : _format(format), _valid(format != nullptr) {}
139 Mode(uint32_t fps, uint32_t flags) : _valid(true) {
140 if (fps == 0)
return;
141 const bool wantDf = (flags & DropFrame) != 0;
146 for (
int i = 0; i < VTC_STANDARD_FORMATS_COUNT; ++i) {
147 const VtcFormat *f = VTC_STANDARD_FORMATS[i];
148 if (vtc_format_fps(f) != fps)
continue;
149 if (vtc_format_is_drop_frame(f) != wantDf)
continue;
150 if (vtc_format_is_ntsc(f))
continue;
157 for (
int i = 0; i < VTC_STANDARD_FORMATS_COUNT; ++i) {
158 const VtcFormat *f = VTC_STANDARD_FORMATS[i];
159 if (vtc_format_fps(f) != fps)
continue;
160 if (vtc_format_is_drop_frame(f) != wantDf)
continue;
164 uint32_t vtcFlags = 0;
165 if (wantDf) vtcFlags |= VTC_FORMAT_FLAG_DROP_FRAME;
166 _format = vtc_format_find_or_create(fps, 1, fps, vtcFlags);
171 Mode(TimecodeType type) : _valid(true) {
173 case NDF24: _format = &VTC_FORMAT_24;
break;
174 case NDF25: _format = &VTC_FORMAT_25;
break;
175 case NDF30: _format = &VTC_FORMAT_30_NDF;
break;
176 case DF30: _format = &VTC_FORMAT_29_97_DF;
break;
177 case NDF48: _format = &VTC_FORMAT_48;
break;
178 case NDF50: _format = &VTC_FORMAT_50;
break;
179 case NDF60: _format = &VTC_FORMAT_60;
break;
180 case DF60: _format = &VTC_FORMAT_59_94_DF;
break;
181 case NDF72: _format = &VTC_FORMAT_72;
break;
182 case NDF96: _format = &VTC_FORMAT_96;
break;
183 case NDF100: _format = &VTC_FORMAT_100;
break;
184 case NDF120: _format = &VTC_FORMAT_120_30X4;
break;
185 case DF120: _format = &VTC_FORMAT_119_88_DF;
break;
186 case NDF120_24x5: _format = &VTC_FORMAT_120_24X5;
break;
190 bool operator==(
const Mode &other)
const {
191 if (_format == other._format)
return _valid == other._valid;
192 if (_format ==
nullptr || other._format ==
nullptr) {
194 return _valid == other._valid && _format == other._format;
196 return _format->tc_fps == other._format->tc_fps &&
197 vtc_format_is_drop_frame(_format) ==
198 vtc_format_is_drop_frame(other._format);
201 bool operator!=(
const Mode &other)
const {
return !(*
this == other); }
204 uint32_t fps()
const {
return _format ? vtc_format_fps(_format) : 0; }
206 bool isValid()
const {
return _valid; }
208 bool isDropFrame()
const {
return _format ? vtc_format_is_drop_frame(_format) : false; }
210 bool hasFormat()
const {
return _format !=
nullptr; }
222 uint32_t framesPerSuperFrame()
const {
223 return _format ? vtc_format_hfr_n(_format) + 1u : 1u;
237 uint32_t superFrameRate()
const {
238 return _format ? _format->tc_fps : 0u;
242 const VtcFormat *vtcFormat()
const {
return _format; }
245 const VtcFormat *_format =
nullptr;
260 static Timecode fromFrameNumber(
const Mode &mode,
const FrameNumber &frameNumber);
283 static Result<Timecode> fromString(
const String &str);
286 Timecode() =
default;
289 Timecode(
const Mode &md) : _mode(md) {}
295 Timecode(DigitType h, DigitType m, DigitType s, DigitType f)
296 : _mode(Mode(0u, 0u)), _hour(h), _min(m), _sec(s), _frame(f) {}
303 Timecode(
const Mode &md, DigitType h, DigitType m, DigitType s, DigitType f)
304 : _mode(md), _hour(h), _min(m), _sec(s), _frame(f) {}
307 Timecode(
const String &str) {
308 auto [tc, err] = fromString(str);
309 if (err.isOk()) *
this = tc;
312 bool operator==(
const Timecode &other)
const {
313 return _mode == other._mode && _hour == other._hour && _min == other._min &&
314 _sec == other._sec && _frame == other._frame &&
315 _colorFrame == other._colorFrame && _userbits == other._userbits;
318 bool operator!=(
const Timecode &other)
const {
return !(*
this == other); }
337 bool operator>(
const Timecode &other)
const {
338 if (compareByDigits(other))
return digitTuple() > other.digitTuple();
339 return toFrameNumber() > other.toFrameNumber();
342 bool operator<(
const Timecode &other)
const {
343 if (compareByDigits(other))
return digitTuple() < other.digitTuple();
344 return toFrameNumber() < other.toFrameNumber();
347 bool operator>=(
const Timecode &other)
const {
348 if (compareByDigits(other))
return digitTuple() >= other.digitTuple();
349 return toFrameNumber() >= other.toFrameNumber();
352 bool operator<=(
const Timecode &other)
const {
353 if (compareByDigits(other))
return digitTuple() <= other.digitTuple();
354 return toFrameNumber() <= other.toFrameNumber();
358 bool isValid()
const {
return _mode.isValid(); }
360 bool isDropFrame()
const {
return _mode.isDropFrame(); }
362 bool isFirstField()
const {
return _flags & FirstField; }
364 uint32_t fps()
const {
return _mode.fps(); }
366 Mode mode()
const {
return _mode; }
371 void setMode(
const Mode &md) {
377 Timecode &operator++();
379 Timecode operator++(
int) {
380 Timecode ret = *
this;
385 Timecode &operator--();
387 Timecode operator--(
int) {
388 Timecode ret = *
this;
398 void set(DigitType h, DigitType m, DigitType s, DigitType f) {
406 DigitType hour()
const {
return _hour; }
408 DigitType min()
const {
return _min; }
410 DigitType sec()
const {
return _sec; }
412 DigitType frame()
const {
return _frame; }
422 bool colorFrame()
const {
return _colorFrame; }
424 void setColorFrame(
bool on) { _colorFrame = on; }
427 const TimecodeUserbits &userbits()
const {
return _userbits; }
429 void setUserbits(
const TimecodeUserbits &ub) { _userbits = ub; }
441 uint32_t superFrameIndex()
const {
442 const uint32_t n = _mode.framesPerSuperFrame();
443 return n > 0u ?
static_cast<uint32_t
>(_frame) / n : static_cast<uint32_t>(_frame);
456 uint32_t subFrameIndex()
const {
457 const uint32_t n = _mode.framesPerSuperFrame();
458 return n > 0u ?
static_cast<uint32_t
>(_frame) % n : 0u;
463 bool isHfr()
const {
return _mode.framesPerSuperFrame() > 1u; }
469 bool isSuperFrameBoundary()
const {
return subFrameIndex() == 0u; }
472 const VtcFormat *vtcFormat()
const {
return _mode.vtcFormat(); }
475 operator String()
const {
return toString(); }
490 String toString()
const {
return toFormatString().first(); }
518 Result<String> toFormatString(
const VtcStringFormat *fmt = &VTC_STR_FMT_SMPTE)
const;
528 FrameNumber toFrameNumber()
const;
549 Result<Duration> toRuntime(
const FrameRate &rate)
const;
591 uint64_t toBcd64(TimecodePackFormat fmt = TimecodePackFormat::Vitc)
const;
633 static Result<Timecode> fromBcd64(uint64_t bcd, TimecodePackFormat fmt,
const Mode &mode);
636 static Result<Timecode> fromBcd64(uint64_t bcd) {
637 return fromBcd64(bcd, TimecodePackFormat::Vitc, Mode());
641 static Result<Timecode> fromBcd64(uint64_t bcd,
const Mode &mode) {
642 return fromBcd64(bcd, TimecodePackFormat::Vitc, mode);
646 VtcTimecode toVtc()
const;
647 void fromVtc(
const VtcTimecode &vtc);
653 using DigitTuple = Array<DigitType, 4>;
654 DigitTuple digitTuple()
const {
return DigitTuple(_hour, _min, _sec, _frame); }
660 bool compareByDigits(
const Timecode &other)
const {
661 if (_mode == other._mode)
return true;
662 return !_mode.hasFormat() || !other._mode.hasFormat();
666 FlagsType _flags = 0;
670 DigitType _frame = 0;
671 bool _colorFrame =
false;
672 TimecodeUserbits _userbits;
705template <>
struct std::formatter<promeki::Timecode> {
713 Style _style = Style::Smpte;
714 std::formatter<std::string_view> _base;
716 constexpr auto parse(std::format_parse_context &ctx) {
717 auto it = ctx.begin();
718 auto end = ctx.end();
724 auto tryKeyword = [&](
const char *kw, Style s) {
726 while (*kw && p != end && *p == *kw) {
730 if (*kw == 0 && (p == end || *p ==
'}' || *p ==
':')) {
738 if (!tryKeyword(
"smpte-fps", Style::SmpteWithFps) &&
739 !tryKeyword(
"smpte-space", Style::SmpteSpaceFps) && !tryKeyword(
"smpte", Style::Smpte) &&
740 !tryKeyword(
"field", Style::Field)) {
748 if (it != end && *it ==
':') ++it;
753 return _base.parse(ctx);
756 template <
typename FormatContext>
auto format(
const promeki::Timecode &tc, FormatContext &ctx)
const {
757 const VtcStringFormat *fmt = &VTC_STR_FMT_SMPTE;
759 case Style::Smpte: fmt = &VTC_STR_FMT_SMPTE;
break;
760 case Style::SmpteWithFps: fmt = &VTC_STR_FMT_SMPTE_WITH_FPS;
break;
761 case Style::SmpteSpaceFps: fmt = &VTC_STR_FMT_SMPTE_SPACE_FPS;
break;
762 case Style::Field: fmt = &VTC_STR_FMT_FIELD;
break;
764 auto [s, err] = tc.toFormatString(fmt);
766 return _base.format(std::string_view(s.cstr(), s.byteCount()), ctx);