Go to the documentation of this file.
37 #define LOC ((m_tvrec) ? \
38 QString("DTVRec[%1]: ").arg(m_tvrec->GetInputId()) : \
39 QString("DTVRec(0x%1): ").arg(intptr_t(this),0,16))
93 if (name ==
"recordingtype")
104 if (name ==
"wait_for_seqstart")
106 else if (name ==
"recordmpts")
113 const QString &videodev,
114 const QString& ,
const QString& )
142 LOG(VB_RECORD, LOG_INFO,
LOC +
"ResetForNewFile(void)");
206 LOG(VB_RECORD, LOG_INFO,
LOC +
"Reset(void)");
245 if (atsc && atsc->DesiredMinorChannel())
248 atsc->DesiredMinorChannel());
282 int interval = thresh;
286 .fetchAndStoreRelaxed(thresh * 4 / 5);
291 .fetchAndStoreRelaxed(thresh * 9 / 8);
297 LOG(VB_RECORD, LOG_DEBUG,
LOC +
298 QString(
"Updating timeOfLatestData elapsed(%1) interval(%2)")
299 .arg(elapsed.count()).arg(interval));
324 LOG(VB_GENERAL, LOG_INFO,
LOC +
325 QString(
"BufferedWrite: Writes are failing, "
326 "setting status to %1")
334 const uint8_t *bufptr,
int bytes_left,
int pts_or_dts)
339 bool has_pts = (bufptr[3] & 0x80) != 0;
341 if (((
kExtractPTS == pts_or_dts) && !has_pts) || (offset + 5 > bytes_left))
344 bool has_dts = (bufptr[3] & 0x40) != 0;
349 offset += has_pts ? 5 : 0;
350 if (offset + 5 > bytes_left)
354 return ((uint64_t(bufptr[offset+0] & 0x0e) << 29) |
355 (uint64_t(bufptr[offset+1] ) << 22) |
356 (uint64_t(bufptr[offset+2] & 0xfe) << 14) |
357 (uint64_t(bufptr[offset+3] ) << 7) |
358 (uint64_t(bufptr[offset+4] & 0xfe) >> 1));
362 uint64_t
pts, uint64_t pts_first,
const QDateTime &pts_first_dt)
365 pts += 0x1FFFFFFFFLL;
366 const QDateTime& dt = pts_first_dt;
367 return dt.addMSecs((
pts - pts_first)/90);
421 bool hasFrame =
false;
422 bool hasKeyFrame =
false;
424 uint aspectRatio = 0;
436 const uint8_t *bufptr = tspacket->
data() + tspacket->
AFCOffset();
440 while (bufptr < bufend)
443 int bytes_left = bufend - bufptr;
468 aspectRatio = (bufptr[3] >> 4);
471 height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
472 width = (bufptr[0] <<4) | (bufptr[1]>>4);
480 int ext_type = (bufptr[0] >> 4);
492 int picture_structure = bufptr[2] & 3;
493 int top_field_first = bufptr[3] & (1 << 7);
494 int repeat_first_field = bufptr[3] & (1 << 1);
495 int progressive_frame = bufptr[4] & (1 << 7);
497 LOG(VB_RECORD, LOG_DEBUG,
LOC +
498 QString(
"picture_coding_extension(): (m_progressiveSequence: %1) picture_structure: %2 top_field_first: %3 repeat_first_field: %4 progressive_frame: %5")
500 QString::number(picture_structure , 2),
501 QString::number(top_field_first , 2),
502 QString::number(repeat_first_field , 2),
503 QString::number(progressive_frame , 2)
509 if (picture_structure == 0b00)
513 else if (picture_structure == 0b11)
518 else if (picture_structure < 0b11)
523 if (top_field_first != 0)
525 hasFrame = (picture_structure == 0b01);
529 hasFrame = (picture_structure == 0b10);
536 if (repeat_first_field)
545 else if (progressive_frame)
574 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Music Choice program detected");
580 if (hasFrame && !hasKeyFrame)
594 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString
595 (
"Keyframe @ %1 + %2 = %3")
606 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString
607 (
"Frame @ %1 + %2 = %3")
640 LOG(VB_RECORD, LOG_INFO,
LOC +
641 QString(
"FindMPEG2Keyframes: frame rate = %1")
661 LOG(VB_RECORD, LOG_DEBUG,
662 "Switching from dts tracking to pts tracking." +
663 QString(
"TS count is %1").arg(
m_tsCount[stream_id]));
667 int64_t gap_threshold = 90000;
671 gap_threshold = 2*90000LL;
675 gap_threshold = 8*90000LL;
679 int64_t diff = ts -
m_tsLast[stream_id];
682 if (diff < (10 * -90000LL))
684 diff += 0x1ffffffffLL;
701 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString(
"Inserted gap %1 dur %2")
709 LOG(VB_GENERAL, LOG_INFO,
LOC +
710 QString(
"HandleTimestamps: too much damage, "
711 "setting status to %1")
753 LOG(VB_RECORD, LOG_DEBUG,
754 QString(
"count=%1 m_frameRate=%2 tick_frameRate=%3 "
755 "tick_cnt=%4 tick_base=%5 _total_dur=%6")
767 bool hasKeyFrame =
false;
771 static constexpr uint64_t kMsecPerDay { 24ULL * 60 * 60 * 1000 };
773 uint64_t elapsed = 0;
776 auto expected_frame = (uint64_t) ((
double)elapsed / frame_interval);
779 expected_frame += (uint64_t) ((
double)kMsecPerDay / frame_interval);
813 LOG(VB_RECORD, LOG_INFO,
LOC +
"DSMCC - FindOtherKeyframes() - "
814 "generating initial key-frame");
878 LOG(VB_GENERAL, LOG_ERR,
LOC +
"FindH2645Keyframes: No ringbuffer");
884 LOG(VB_GENERAL, LOG_ERR,
LOC +
"FindH2645Keyframes: m_h2645Parser not present");
896 uint aspectRatio = 0;
902 bool hasFrame =
false;
903 bool hasKeyFrame =
false;
915 LOG(VB_GENERAL, LOG_ERR,
LOC +
916 "PES packet start code may overflow to next TS packet, "
917 "aborting keyframe search");
922 if (tspacket->
data()[i++] != 0x00 ||
923 tspacket->
data()[i++] != 0x00 ||
924 tspacket->
data()[i++] != 0x01)
929 LOG(VB_GENERAL, LOG_ERR,
LOC +
930 "PES start code not found in TS packet with PUSI set");
940 LOG(VB_GENERAL, LOG_ERR,
LOC +
941 "PES packet headers overflow to next TS packet, "
942 "aborting keyframe search");
952 const unsigned char pes_header_length = tspacket->
data()[i + 5];
957 LOG(VB_GENERAL, LOG_ERR,
LOC +
958 "PES packet headers overflow to next TS packet, "
959 "aborting keyframe search");
966 i += 5 + pes_header_length;
970 LOG(VB_RECORD, LOG_DEBUG,
LOC +
"PES synced");
982 i += (bytes_used - 1);
1004 if (hasFrame && !hasKeyFrame &&
1008 LOG(VB_RECORD, LOG_WARNING,
LOC +
1009 QString(
"FindH2645Keyframes: %1 frames without a keyframe.")
1016 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString
1017 (
"Keyframe @ %1 + %2 = %3 AU %4")
1029 LOG(VB_RECORD, LOG_DEBUG,
LOC + QString
1030 (
"Frame @ %1 + %2 = %3 AU %4")
1063 LOG(VB_RECORD, LOG_INFO,
LOC +
1064 QString(
"FindH2645Keyframes: timescale: %1, tick: %2, framerate: %3")
1067 .arg( frameRate.
toDouble() * 1000 ) );
1074 LOG(VB_RECORD, LOG_INFO,
LOC +
1075 QString(
"FindH2645Keyframes: scan type: %1")
1077 "Interlaced" :
"Progressive"));
1094 uint64_t startpos = 0;
1122 const uint8_t *bufstart = buffer;
1123 const uint8_t *bufptr = buffer;
1124 const uint8_t *bufend = buffer + len;
1126 uint aspectRatio = 0;
1132 while (bufptr + skip < bufend)
1134 bool hasFrame =
false;
1135 bool hasKeyFrame =
false;
1137 const uint8_t *
tmp = bufptr;
1149 int pes_packet_length = -1;
1150 if ((bufend - bufptr) >= 2)
1151 pes_packet_length = ((bufptr[0]<<8) | bufptr[1]) + 2 + 6;
1158 pes_packet_length = -1;
1159 if (bufend-bufptr >= 4)
1161 uint frmtypei = (bufptr[1]>>3) & 0x7;
1162 if ((1 <= frmtypei) && (frmtypei <= 5))
1172 pes_packet_length = -1;
1178 pes_packet_length = -1;
1183 aspectRatio = (bufptr[3] >> 4);
1186 height = ((bufptr[1] & 0xf) << 8) | bufptr[2];
1187 width = (bufptr[0] <<4) | (bufptr[1]>>4);
1213 if (hasFrame && !hasKeyFrame)
1243 if (height && width &&
1254 LOG(VB_RECORD, LOG_INFO,
LOC +
1255 QString(
"FindPSKeyFrames: frame rate = %1")
1256 .arg(frameRate.
toDouble() * 1000));
1260 if (hasKeyFrame || hasFrame)
1283 int bytes_skipped = bufend - bufptr;
1284 if (bytes_skipped > 0)
1295 uint64_t rem = (bufend - bufstart);
1299 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
1300 QString(
"idx: %1, rem: %2").arg(idx).arg(rem));
1308 LOG(VB_RECORD, LOG_ERR,
LOC +
"SetPAT(NULL)");
1323 int oldProgNum = progNum;
1325 LOG(VB_GENERAL, LOG_INFO,
LOC +
1326 QString(
"Update desired program found in SPTS PAT from %1 to %2")
1327 .arg(oldProgNum).arg(progNum));
1329 pmtpid = _pat->
FindPID(progNum);
1334 LOG(VB_RECORD, LOG_ERR,
LOC +
1335 QString(
"SetPAT(): Ignoring PAT not containing our desired "
1336 "program (%1)...").arg(progNum));
1340 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"SetPAT(%1 on pid 0x%2)")
1341 .arg(progNum).arg(pmtpid,0,16));
1360 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"SetPMT(%1, %2)").arg(progNum)
1361 .arg(_pmt ==
nullptr ?
"NULL" :
"valid"));
1366 LOG(VB_RECORD, LOG_INFO,
LOC + QString(
"SetPMT(%1)").arg(progNum));
1372 bool has_no_av =
true;
1390 LOG(VB_RECORD, LOG_ERR,
LOC +
"HandleSingleProgramPAT(NULL)");
1409 LOG(VB_RECORD, LOG_ERR,
LOC +
"HandleSingleProgramPMT(NULL)");
1416 uint bestAudioCodec = 0;
1446 if (avcParser !=
nullptr)
1455 LOG(VB_GENERAL, LOG_INFO,
LOC +
"HEVC detected");
1545 const uint pid = tspacket.
PID();
1555 double erate = v * 100.0 /
m_packetCount.fetchAndAddRelaxed(0);
1556 LOG(VB_RECORD, LOG_WARNING,
LOC +
1557 QString(
"PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1558 .arg(pid,0,16).arg(old_cnt,2)
1630 else if (streamType != 0)
1633 LOG(VB_RECORD, LOG_ERR,
LOC +
1634 "ProcessVideoTSPacket: unknown stream type!");
1673 const uint pid = tspacket.
PID();
1683 double erate = v * 100.0 /
m_packetCount.fetchAndAddRelaxed(0);
1684 LOG(VB_RECORD, LOG_WARNING,
LOC +
1685 QString(
"A/V PID 0x%1 discontinuity detected ((%2+1)%16!=%3) %4%")
1687 .arg(erate,5,
'f',2));
1693 LOG(VB_RECORD, LOG_INFO,
LOC +
1694 QString(
"PID 0x%1 Found Payload Start").arg(pid,0,16));
@ VC1Video
SMPTE 421M video codec (aka VC1) in Blu-Ray.
void FindPSKeyFrames(const uint8_t *buffer, uint len) override
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
std::array< uint8_t, 0x1fff+1 > m_pidStatus
MythTimer m_timeOfLatestDataTimer
uint ProgramPID(uint i) const
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
void SetPositionMapType(MarkTypes type)
Set seektable type.
@ MPEGVideoStreamBegin
First MPEG-1/2 video stream (w/ext hdr)
QDateTime m_timeOfLatestData
ProgramMapTable * m_inputPmt
PMT on input side.
void VideoCodecChange(AVCodecID vCodec)
Note a change in video codec.
double toDouble(void) const
bool CheckCC(uint pid, uint new_cnt)
uint StreamCount(void) const
void SetDesiredProgram(int p)
unsigned long long m_framesWrittenCount
std::vector< unsigned char > m_payloadBuffer
void AddMPEGSPListener(MPEGSingleProgramStreamListener *val)
virtual void getFrameRate(FrameRate &result) const =0
static const uint kMaxKeyFrameDistance
If the number of regular frames detected since the last detected keyframe exceeds this value,...
@ MPEG2AudioAmd1
ISO 13818-3/AMD-1 Audio using LATM syntax.
void ResetForNewFile(void) override
QElapsedTimer m_audioTimer
Holds information on a TV Program one might wish to record.
ProgramAssociationTable * m_inputPat
PAT on input side.
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
bool ProcessVideoTSPacket(const TSPacket &tspacket) override
const TSHeader * tsheader() const
bool FindOtherKeyframes(const TSPacket *tspacket)
Non-Audio/Video data.
unsigned int m_videoBytesRemaining
int FindPID(uint pid) const
Locates stream index of pid.
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
void Reset(void) override
Reset the recorder to the startup state.
uint PCRPID(void) const
stream that contains program clock reference.
void HandlePAT(const ProgramAssociationTable *_pat) override
unsigned long long m_lastGopSeen
void stop(void)
Stops timer, next call to isRunning() will return false and any calls to elapsed() or restart() will ...
unsigned int AFCOffset(void) const
uint pictureWidth(void) const
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
virtual void SetStreamData(MPEGStreamData *data)
void BufferedWrite(const TSPacket &tspacket, bool insert=false)
bool IsVideo(uint i, const QString &sistandard) const
Returns true iff the stream at index i is a video stream.
@ MPEGAudioStreamEnd
Last MPEG-1/2 audio stream (w/ext hdr)
std::array< uint64_t, 256 > m_tsCount
uint32_t GetUnitsInTick(void) const
void start(void)
starts measuring elapsed time.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
@ MPEG2Video
ISO 13818-2 & ITU H.262 (aka MPEG-2)
unsigned long long m_framesSeenCount
bool IsDamaged(void) const
uint ProgramCount(void) const
virtual QString GetSIStandard(void) const
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
virtual void SetCAMPMT(const ProgramMapTable *)
std::array< int64_t, 256 > m_tsFirst
QAtomicInt m_timeOfLatestDataPacketInterval
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
static bool IsVideo(uint type)
Returns true iff video is an MPEG1/2/3, H264 or open cable video stream.
@ H265Video
ISO 23008-2 & ITU H.265 (aka HEVC, Ultra HD)
std::array< uint8_t, 0x1fff+1 > m_continuityCounter
bool isNonzero(void) const
static QString toString(RecStatus::Type recstatus, uint id)
Converts "recstatus" into a short (unreadable) string.
static const unsigned char kPayloadStartSeen
@ MPEG1Video
ISO 11172-2 (aka MPEG-1)
virtual bool IsListeningPID(uint pid) const
@ EAC3Audio
A/53 Part 3:2009 6.7.3.
RecordingInfo * m_curRecording
FrameRate m_tdTickFramerate
frm_pos_map_t m_durationMapDelta
void SetStrOption(RecordingProfile *profile, const QString &name)
Convenience function used to set QString options from a profile.
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
void SetDesiredChannel(int major, int minor)
RecStatus::Type GetRecordingStatus(void) const
bool ProcessAVTSPacket(const TSPacket &tspacket)
Common code for processing either audio or video packets.
uint ProgramNumber(uint i) const
unsigned long long m_lastKeyframeSeen
void AspectChange(uint aspect, long long frame)
Note a change in aspect ratio in the recordedmark table.
int m_progressiveSequence
uint64_t keyframeAUstreamOffset(void) const
void HandleKeyframe(int64_t extra)
This save the current frame to the position maps and handles ringbuffer switching.
SCAN_t GetScanType(void) const
static bool IsAudio(uint type)
Returns true iff audio is MPEG1/2, AAC, AC3 or DTS audio stream.
RecordingGaps m_recordingGaps
virtual void ClearStatistics(void)
Used to access the data of a Transport Stream packet.
Encapsulates data about ATSC stream and emits events for most tables.
uint StreamType(uint i) const
#define LOC
DTVRecorder – base class for Digital Televison recorders Copyright 2003-2004 by Brandon Beattie,...
virtual RecordingQuality * GetRecordingQuality(const RecordingInfo *ri) const
Returns a report about the current recordings quality.
Encapsulates data about MPEG stream and emits events for each table.
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
bool onFrameStart(void) const
void ResolutionChange(uint width, uint height, long long frame)
Note a change in video size in the recordedmark table.
bool FindAudioKeyframes(const TSPacket *tspacket)
void AudioCodecChange(AVCodecID aCodec)
Note a change in audio codec.
H2645Parser * m_h2645Parser
void HandleH2645Keyframe(void)
This save the current frame to the position maps and handles ringbuffer switching.
static const std::array< const FrameRate, 16 > frameRateMap
MPEGStreamData * GetStreamData(void) const
frm_pos_map_t m_durationMap
std::array< uint8_t, 0x1fff+1 > m_streamId
void SetTotalFrames(uint64_t total_frames)
Note the total frames in the recordedmark table.
void AddTSStatistics(int continuity_error_count, int packet_count)
@ AC3Audio
A/53 Part 3:2009 6.7.1.
void ClearStatistics(void) override
bool ProcessTSPacket(const TSPacket &tspacket) override
@ MPEG4Video
ISO 14492-2 (aka MPEG-4)
void SetOption(const QString &name, const QString &value) override
Set an specific option.
MythTimer m_recordMptsTimer
@ H264Video
ISO 14492-10 & ITU H.264 (aka MPEG-4-AVC)
frm_pos_map_t m_positionMap
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
AVContainer m_containerFormat
void SetDuration(std::chrono::milliseconds duration)
Note the total duration in the recordedmark table.
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
void FrameRateChange(uint framerate, uint64_t frame)
Note a change in video frame rate in the recordedmark table.
unsigned long long m_lastSeqSeen
int GetNumSetting(const QString &key, int defaultval=0)
bool IsAudio(uint i, const QString &sistandard) const
Returns true iff the stream at index i is an audio stream.
virtual uint32_t addBytes(const uint8_t *bytes, uint32_t byte_count, uint64_t stream_offset)=0
virtual void FinishRecording(void)
frm_pos_map_t m_positionMapDelta
bool start_code_is_valid(uint32_t start_code)
Test whether a start code found by find_start_code() is valid.
unsigned int m_audioBytesRemaining
virtual void StopRecording(void)
StopRecording() signals to the recorder that it should stop recording and exit cleanly.
bool m_hasWrittenOtherKeyframe
void AddMPEGListener(MPEGStreamListener *val)
unsigned int m_otherBytesRemaining
MythMediaBuffer * m_ringBuffer
bool ProcessAudioTSPacket(const TSPacket &tspacket) override
void AddDVBMainListener(DVBMainStreamListener *val)
bool FindMPEG2Keyframes(const TSPacket *tspacket)
Locates the keyframes and saves them to the position map.
void UpdateFramesWritten(void)
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
MPEGStreamData * m_streamData
QDateTime m_timeOfFirstData
static int64_t extract_timestamp(const uint8_t *bufptr, int bytes_left, int pts_or_dts)
int DesiredProgram(void) const
QString m_error
non-empty iff irrecoverable recording error detected
@ MPEG2AACAudio
ISO 13818-7 Audio w/ADTS syntax.
void ClearPositionMap(MarkTypes type) const
virtual field_type getFieldType(void) const =0
QAtomicInt m_timeOfFirstDataIsSet
void addMSecs(std::chrono::milliseconds ms)
Adds an offset to the last call to start() or restart().
This is the abstract base class for supporting recorder hardware.
@ MPEG2ExtensionStartCode
Followed by an extension byte, not documented here.
void HandlePMT(uint progNum, const ProgramMapTable *_pmt) override
This is the coordinating class of the Recorder Subsystem.
@ GOPStartCode
Group of Pictures (GOP) start code.
QAtomicInt m_continuityErrorCount
bool onKeyFrameStart(void) const
AVCodecID m_primaryAudioCodec
@ SequenceStartCode
Sequence (SEQ) start code contains frame size, aspect ratio and fps.
uint aspectRatio(void) const
Computes aspect ratio from picture size and sample aspect ratio.
static QDateTime ts_to_qdatetime(uint64_t pts, uint64_t pts_first, const QDateTime &pts_first_dt)
virtual void SetOption(const QString &name, const QString &value)
Set an specific option.
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
AVCodecID m_primaryVideoCodec
std::vector< TSPacket > m_scratch
QString toString(void) const
void GetAsTSPackets(std::vector< TSPacket > &output, uint cc) const
Returns payload only PESPacket as series of TSPackets.
void SendMythSystemRecEvent(const QString &msg, const RecordingInfo *pginfo)
virtual void AddListeningPID(uint pid, PIDPriority priority=kPIDPriorityNormal)
@ OpenCableVideo
Always MPEG-2??
uint FindPID(uint progNum) const
uint32_t GetTimeScale(void) const
int m_minimumRecordingQuality
static constexpr std::chrono::milliseconds kTimeOfLatestDataIntervalTarget
timeOfLatest update interval target in milliseconds.
QRecursiveMutex m_pidLock
bool FindH2645Keyframes(const TSPacket *tspacket)
uint pictureHeight(void) const
void HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
virtual bool CheckForRingBufferSwitch(void)
If requested, switch to new RingBuffer/ProgramInfo objects.
@ MPEGVideoStreamEnd
Last MPEG-1/2 video stream (w/ext hdr)
uint StreamPID(uint i) const
RecordingQuality * GetRecordingQuality(const RecordingInfo *r) const override
Returns a report about the current recordings quality.
QAtomicInt m_timeOfLatestDataCount
@ PrivSec
ISO 13818-1 private tables & ITU H.222.0.
const MTV_PUBLIC uint8_t * find_start_code_truncated(const uint8_t *p, const uint8_t *end, uint32_t *start_code)
By preserving the start_code value between subsequent calls, the caller can detect start codes across...
virtual void InitStreamData(void)
@ MPEGAudioStreamBegin
First MPEG-1/2 audio stream (w/ext hdr)
void VideoScanChange(SCAN_t scan, uint64_t frame)
Note a change in video scan type in the recordedmark table.
std::array< int64_t, 256 > m_tsLast
std::array< QDateTime, 256 > m_tsFirstDt
QString GetSetting(const QString &key, const QString &defaultval="")
static constexpr unsigned int kSize
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::milliseconds > millisecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
bool stateChanged(void) const
void SetIntOption(RecordingProfile *profile, const QString &name)
Convenience function used to set integer options from a profile.