From 07acf0e850d62d6c54cec498c675cc8f82d83d64 Mon Sep 17 00:00:00 2001 From: Daniel Thor Kristjansson Date: Mon, 23 Jul 2012 17:03:57 -0400 Subject: [PATCH] Fixes #10872. Use desired start and end times. The recording quality measurement needs to know when a recording was supposed to start and end to know how much is actually missing from the recording. We used to use the scheduled start and end times for this, but sometimes a recording is intentionally started late or ended early. This captures that by tracking the 'desired' start and end point. This is initially set by the scheduler using and pre and post roll values, and if the user intentionally stops a recording early that updates the desired end time as well. --- .../recorders/NuppelVideoRecorder.cpp | 6 +- .../libmythtv/recorders/NuppelVideoRecorder.h | 2 +- .../libs/libmythtv/recorders/dtvrecorder.cpp | 4 +- mythtv/libs/libmythtv/recorders/dtvrecorder.h | 2 +- .../libs/libmythtv/recorders/recorderbase.cpp | 4 +- .../libs/libmythtv/recorders/recorderbase.h | 10 ++-- mythtv/libs/libmythtv/recordinginfo.cpp | 10 ++++ mythtv/libs/libmythtv/recordinginfo.h | 11 ++++ mythtv/libs/libmythtv/recordingquality.cpp | 55 +++++++++++++++---- mythtv/libs/libmythtv/recordingquality.h | 4 +- mythtv/libs/libmythtv/tv_rec.cpp | 7 ++- 11 files changed, 87 insertions(+), 28 deletions(-) diff --git a/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.cpp b/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.cpp index 6fc9b80fbe8..e9a12464b25 100644 --- a/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.cpp +++ b/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.cpp @@ -2806,8 +2806,8 @@ void NuppelVideoRecorder::doWriteThread(void) } } -void NuppelVideoRecorder::SetNextRecording(const ProgramInfo *progInf, - RingBuffer *rb) +void NuppelVideoRecorder::SetNextRecording( + const RecordingInfo *progInf, RingBuffer *rb) { // First we do some of the time consuming stuff we can do now SavePositionMap(true); @@ -2819,7 +2819,7 @@ void NuppelVideoRecorder::SetNextRecording(const ProgramInfo *progInf, QMutexLocker locker(&nextRingBufferLock); nextRecording = NULL; if (progInf) - nextRecording = new ProgramInfo(*progInf); + nextRecording = new RecordingInfo(*progInf); nextRingBuffer = rb; } diff --git a/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.h b/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.h index 67b7d67b526..33cc2a705a9 100644 --- a/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.h +++ b/mythtv/libs/libmythtv/recorders/NuppelVideoRecorder.h @@ -101,7 +101,7 @@ class MTV_PUBLIC NuppelVideoRecorder : public V4LRecorder, public CC608Input void SetVideoFilters(QString &filters); void SetTranscoding(bool value) { transcoding = value; }; - void SetNextRecording(const ProgramInfo*, RingBuffer*); + void SetNextRecording(const RecordingInfo*, RingBuffer*); void ResetForNewFile(void); void FinishRecording(void); void StartNewFile(void); diff --git a/mythtv/libs/libmythtv/recorders/dtvrecorder.cpp b/mythtv/libs/libmythtv/recorders/dtvrecorder.cpp index 32991d6dfa9..a9aa50625c9 100644 --- a/mythtv/libs/libmythtv/recorders/dtvrecorder.cpp +++ b/mythtv/libs/libmythtv/recorders/dtvrecorder.cpp @@ -688,7 +688,7 @@ bool DTVRecorder::FindOtherKeyframes(const TSPacket *tspacket) } // documented in recorderbase.h -void DTVRecorder::SetNextRecording(const ProgramInfo *progInf, RingBuffer *rb) +void DTVRecorder::SetNextRecording(const RecordingInfo *progInf, RingBuffer *rb) { LOG(VB_RECORD, LOG_INFO, LOC + QString("SetNextRecord(0x%1, 0x%2)") .arg((uint64_t)progInf,0,16).arg((uint64_t)rb,0,16)); @@ -706,7 +706,7 @@ void DTVRecorder::SetNextRecording(const ProgramInfo *progInf, RingBuffer *rb) nextRecording = NULL; if (progInf) - nextRecording = new ProgramInfo(*progInf); + nextRecording = new RecordingInfo(*progInf); nextRingBuffer = rb; nextRingBufferLock.unlock(); diff --git a/mythtv/libs/libmythtv/recorders/dtvrecorder.h b/mythtv/libs/libmythtv/recorders/dtvrecorder.h index cc9eb1f4118..b50bb54028c 100644 --- a/mythtv/libs/libmythtv/recorders/dtvrecorder.h +++ b/mythtv/libs/libmythtv/recorders/dtvrecorder.h @@ -50,7 +50,7 @@ class DTVRecorder : void Initialize(void) {;} int GetVideoFd(void) { return _stream_fd; } - virtual void SetNextRecording(const ProgramInfo*, RingBuffer*); + virtual void SetNextRecording(const RecordingInfo*, RingBuffer*); virtual void SetStreamData(MPEGStreamData* sd); MPEGStreamData *GetStreamData(void) const { return _stream_data; } diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.cpp b/mythtv/libs/libmythtv/recorders/recorderbase.cpp index 84731c77499..df5a6ddb319 100644 --- a/mythtv/libs/libmythtv/recorders/recorderbase.cpp +++ b/mythtv/libs/libmythtv/recorders/recorderbase.cpp @@ -82,7 +82,7 @@ void RecorderBase::SetRingBuffer(RingBuffer *rbuf) weMadeBuffer = false; } -void RecorderBase::SetRecording(const ProgramInfo *pginfo) +void RecorderBase::SetRecording(const RecordingInfo *pginfo) { if (pginfo) LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRecording(0x%1) title(%2)") @@ -92,7 +92,7 @@ void RecorderBase::SetRecording(const ProgramInfo *pginfo) ProgramInfo *oldrec = curRecording; if (pginfo) - curRecording = new ProgramInfo(*pginfo); + curRecording = new RecordingInfo(*pginfo); else curRecording = NULL; diff --git a/mythtv/libs/libmythtv/recorders/recorderbase.h b/mythtv/libs/libmythtv/recorders/recorderbase.h index d124dbd4993..7dca9c7ba40 100644 --- a/mythtv/libs/libmythtv/recorders/recorderbase.h +++ b/mythtv/libs/libmythtv/recorders/recorderbase.h @@ -20,10 +20,10 @@ class FireWireDBOptions; class GeneralDBOptions; class RecordingProfile; +class RecordingInfo; class DVBDBOptions; class RecorderBase; class ChannelBase; -class ProgramInfo; class RingBuffer; class TVRec; @@ -63,7 +63,7 @@ class MTV_PUBLIC RecorderBase : public QRunnable * * \sa ChannelNameChanged(const QString&) */ - void SetRecording(const ProgramInfo *pginfo); + void SetRecording(const RecordingInfo *pginfo); /** \brief Tells recorder to use an externally created ringbuffer. * @@ -116,7 +116,7 @@ class MTV_PUBLIC RecorderBase : public QRunnable * * This calls TVRec::RingBufferChanged() when the switch happens. */ - virtual void SetNextRecording(const ProgramInfo*, RingBuffer*) = 0; + virtual void SetNextRecording(const RecordingInfo*, RingBuffer*) = 0; /** \brief This is called between SetOptionsFromProfile() and * run() to initialize any devices, etc. @@ -276,7 +276,7 @@ class MTV_PUBLIC RecorderBase : public QRunnable uint m_videoWidth; double m_frameRate; - ProgramInfo *curRecording; + RecordingInfo *curRecording; // For handling pausing + stop recording mutable QMutex pauseLock; // also used for request_recording and recording @@ -294,7 +294,7 @@ class MTV_PUBLIC RecorderBase : public QRunnable // For RingBuffer switching QMutex nextRingBufferLock; RingBuffer *nextRingBuffer; - ProgramInfo *nextRecording; + RecordingInfo *nextRecording; // Seektable support MarkTypes positionMapType; diff --git a/mythtv/libs/libmythtv/recordinginfo.cpp b/mythtv/libs/libmythtv/recordinginfo.cpp index 0fc9e02ea42..39003e88445 100644 --- a/mythtv/libs/libmythtv/recordinginfo.cpp +++ b/mythtv/libs/libmythtv/recordinginfo.cpp @@ -110,6 +110,8 @@ RecordingInfo::RecordingInfo( oldrecstatus(_oldrecstatus), savedrecstatus(rsUnknown), future(_future), + desiredrecstartts(_recstartts), + desiredrecendts(_recendts), record(NULL) { hostname = _hostname; @@ -203,6 +205,8 @@ RecordingInfo::RecordingInfo( oldrecstatus(rsUnknown), savedrecstatus(rsUnknown), future(false), + desiredrecstartts(_recstartts), + desiredrecendts(_recendts), record(NULL) { recpriority = _recpriority; @@ -379,6 +383,8 @@ void RecordingInfo::clone(const RecordingInfo &other, oldrecstatus = other.oldrecstatus; savedrecstatus = other.savedrecstatus; future = other.future; + desiredrecstartts = other.desiredrecstartts; + desiredrecendts = other.desiredrecendts; } } @@ -403,6 +409,8 @@ void RecordingInfo::clone(const ProgramInfo &other, oldrecstatus = rsUnknown; savedrecstatus = rsUnknown; future = false; + desiredrecstartts = QDateTime(); + desiredrecendts = QDateTime(); } void RecordingInfo::clear(void) @@ -415,6 +423,8 @@ void RecordingInfo::clear(void) oldrecstatus = rsUnknown; savedrecstatus = rsUnknown; future = false; + desiredrecstartts = QDateTime(); + desiredrecendts = QDateTime(); } diff --git a/mythtv/libs/libmythtv/recordinginfo.h b/mythtv/libs/libmythtv/recordinginfo.h index 4c8378adcc6..7650e368b41 100644 --- a/mythtv/libs/libmythtv/recordinginfo.h +++ b/mythtv/libs/libmythtv/recordinginfo.h @@ -1,6 +1,9 @@ #ifndef _RECORDING_INFO_H_ #define _RECORDING_INFO_H_ +#include +#include + #include "mythtvexp.h" #include "programinfo.h" @@ -206,6 +209,12 @@ class MTV_PUBLIC RecordingInfo : public ProgramInfo void ApplyRecordRecPriorityChange(int); void ToggleRecord(void); + // Used in determining start and end for RecordingQuality determination + void SetDesiredStartTime(const QDateTime &dt) { desiredrecstartts = dt; } + void SetDesiredEndTime(const QDateTime &dt) { desiredrecendts = dt; } + QDateTime GetDesiredStartTime(void) const { return desiredrecstartts; } + QDateTime GetDesiredEndTime(void) const { return desiredrecendts; } + // these five can be moved to programinfo void AddHistory(bool resched = true, bool forcedup = false, bool future = false);//pi @@ -231,6 +240,8 @@ class MTV_PUBLIC RecordingInfo : public ProgramInfo RecStatusType oldrecstatus; RecStatusType savedrecstatus; bool future; + QDateTime desiredrecstartts; + QDateTime desiredrecendts; private: mutable class RecordingRule *record; diff --git a/mythtv/libs/libmythtv/recordingquality.cpp b/mythtv/libs/libmythtv/recordingquality.cpp index f3f79d1fb7b..253b4089b4d 100644 --- a/mythtv/libs/libmythtv/recordingquality.cpp +++ b/mythtv/libs/libmythtv/recordingquality.cpp @@ -3,26 +3,28 @@ using namespace std; #include "recordingquality.h" #include "mythcorecontext.h" +#include "recordinginfo.h" #include "mythmiscutil.h" #include "mythlogging.h" -#include "programinfo.h" static void merge_overlapping(RecordingGaps &gaps); -static double score_gaps(const ProgramInfo*, const RecordingGaps&); +static double score_gaps(const RecordingInfo&, const RecordingGaps&); +static QDateTime get_start(const RecordingInfo&); +static QDateTime get_end(const RecordingInfo&); RecordingQuality::RecordingQuality( - const ProgramInfo *pi, const RecordingGaps &rg, + const RecordingInfo *ri, const RecordingGaps &rg, const QDateTime &first, const QDateTime &latest) : m_continuity_error_count(0), m_packet_count(0), m_overall_score(1.0), m_recording_gaps(rg) { - if (!pi) + if (!ri) return; - m_program_key = pi->MakeUniqueKey(); + m_program_key = ri->MakeUniqueKey(); // trim start - QDateTime start = pi->GetScheduledStartTime(); + QDateTime start = get_start(*ri); while (!m_recording_gaps.empty() && m_recording_gaps.first().GetStart() < start) { @@ -34,7 +36,7 @@ RecordingQuality::RecordingQuality( } // trim end - QDateTime end = pi->GetScheduledEndTime(); + QDateTime end = get_end(*ri); while (!m_recording_gaps.empty() && m_recording_gaps.back().GetEnd() > end) { @@ -58,7 +60,13 @@ RecordingQuality::RecordingQuality( stable_sort(m_recording_gaps.begin(), m_recording_gaps.end()); merge_overlapping(m_recording_gaps); - m_overall_score = score_gaps(pi, m_recording_gaps); + m_overall_score = score_gaps(*ri, m_recording_gaps); + + LOG(VB_RECORD, LOG_INFO, + QString("RecordingQuality() start(%1) end(%2) score(%3)") + .arg(MythDate::toString(start, MythDate::ISODate)) + .arg(MythDate::toString(end, MythDate::ISODate)) + .arg(m_overall_score)); } void RecordingQuality::AddTSStatistics( @@ -137,15 +145,15 @@ static void merge_overlapping(RecordingGaps &gaps) } } -static double score_gaps(const ProgramInfo *pi, const RecordingGaps &gaps) +static double score_gaps(const RecordingInfo &ri, const RecordingGaps &gaps) { RecordingGaps::const_iterator it = gaps.begin(); if (it == gaps.end()) return 1.0; - QDateTime start = pi->GetScheduledStartTime(); + QDateTime start = get_start(ri); - double program_length = start.secsTo(pi->GetScheduledEndTime()); + double program_length = start.secsTo(get_end(ri)); if (program_length < 1.0) return 0.0; @@ -190,3 +198,28 @@ static double score_gaps(const ProgramInfo *pi, const RecordingGaps &gaps) return (score > 0.0) ? score : 0.0; } +static QDateTime get_start(const RecordingInfo &ri) +{ + if (ri.GetDesiredStartTime().isValid()) + { + return (ri.GetScheduledStartTime() > ri.GetDesiredStartTime()) ? + ri.GetScheduledStartTime() : ri.GetDesiredStartTime(); + } + else + { + return ri.GetScheduledStartTime(); + } +} + +static QDateTime get_end(const RecordingInfo &ri) +{ + if (ri.GetDesiredEndTime().isValid()) + { + return (ri.GetScheduledEndTime() < ri.GetDesiredEndTime()) ? + ri.GetScheduledEndTime() : ri.GetDesiredEndTime(); + } + else + { + return ri.GetScheduledEndTime(); + } +} diff --git a/mythtv/libs/libmythtv/recordingquality.h b/mythtv/libs/libmythtv/recordingquality.h index e7f13618ace..056bbb09fd9 100644 --- a/mythtv/libs/libmythtv/recordingquality.h +++ b/mythtv/libs/libmythtv/recordingquality.h @@ -7,7 +7,7 @@ #include "mythtvexp.h" -class ProgramInfo; +class RecordingInfo; class RecordingGap { @@ -33,7 +33,7 @@ class MTV_PUBLIC RecordingQuality { public: RecordingQuality( - const ProgramInfo*, const RecordingGaps&, + const RecordingInfo*, const RecordingGaps&, const QDateTime &firstData, const QDateTime &latestData); void AddTSStatistics(int continuity_error_count, int packet_count); diff --git a/mythtv/libs/libmythtv/tv_rec.cpp b/mythtv/libs/libmythtv/tv_rec.cpp index a47c082a056..3124412532b 100644 --- a/mythtv/libs/libmythtv/tv_rec.cpp +++ b/mythtv/libs/libmythtv/tv_rec.cpp @@ -693,6 +693,8 @@ void TVRec::SetRecordingStatus( /** \fn TVRec::StopRecording(bool killFile) * \brief Changes from a recording state to kState_None. + * \note For the sake of recording quality measurements this is + * treated as the desired end point of the recording. * \sa StartRecording(const ProgramInfo *rec), FinishRecording() */ void TVRec::StopRecording(bool killFile) @@ -702,6 +704,8 @@ void TVRec::StopRecording(bool killFile) QMutexLocker lock(&stateChangeLock); if (killFile) SetFlags(kFlagKillRec); + else if (curRecording) + curRecording->SetDesiredEndTime(MythDate::current(true)); ChangeState(RemoveRecording(GetState())); // wait for state change to take effect WaitForEventThreadSleep(); @@ -4145,8 +4149,9 @@ void TVRec::TuningRestartRecorder(void) { recorder->SetRingBuffer(ringBuffer); ProgramInfo *progInfo = tvchain->GetProgramAt(-1); - recorder->SetRecording(progInfo); + RecordingInfo recinfo(*progInfo); delete progInfo; + recorder->SetRecording(&recinfo); } recorder->Reset();