bool Dvr::UnDeleteRecording(int RecordedId, int chanid, const QDateTime &recstarttsRaw) { if ((RecordedId <= 0) && (chanid <= 0 || !recstarttsRaw.isValid())) throw QString("Recorded ID or Channel ID and StartTime appears invalid."); RecordingInfo ri; if (RecordedId > 0) ri = RecordingInfo(RecordedId); else ri = RecordingInfo(chanid, recstarttsRaw.toUTC()); if (ri.GetChanID() && ri.HasPathname()) { QString cmd = QString("UNDELETE_RECORDING %1 %2") .arg(ri.GetChanID()) .arg(ri.GetRecordingStartTime(MythDate::ISODate)); MythEvent me(cmd); gCoreContext->dispatch(me); return true; } return false; }
DTC::CutList* Dvr::GetRecordedCommBreak ( int RecordedId, int chanid, const QDateTime &recstarttsRaw, const QString &offsettype ) { int marktype; if ((RecordedId <= 0) && (chanid <= 0 || !recstarttsRaw.isValid())) throw QString("Recorded ID or Channel ID and StartTime appears invalid."); RecordingInfo ri; if (RecordedId > 0) ri = RecordingInfo(RecordedId); else ri = RecordingInfo(chanid, recstarttsRaw.toUTC()); DTC::CutList* pCutList = new DTC::CutList(); if (offsettype == "Position") marktype = 1; else if (offsettype == "Duration") marktype = 2; else marktype = 0; FillCommBreak(pCutList, &ri, marktype); return pCutList; }
bool Dvr::SetSavedBookmark( int RecordedId, int chanid, const QDateTime &recstarttsRaw, const QString &offsettype, long Offset ) { if ((RecordedId <= 0) && (chanid <= 0 || !recstarttsRaw.isValid())) throw QString("Recorded ID or Channel ID and StartTime appears invalid."); if (Offset < 0) throw QString("Offset must be >= 0."); RecordingInfo ri; if (RecordedId > 0) ri = RecordingInfo(RecordedId); else ri = RecordingInfo(chanid, recstarttsRaw.toUTC()); uint64_t position; bool isend=true; if (offsettype.toLower() == "position"){ if (!ri.QueryPositionKeyFrame(&position, Offset, isend)) return false; } else if (offsettype.toLower() == "duration"){ if (!ri.QueryDurationKeyFrame(&position, Offset, isend)) return false; } else position = Offset; ri.SaveBookmark(position); return true; }
long Dvr::GetSavedBookmark( int RecordedId, int chanid, const QDateTime &recstarttsRaw, const QString &offsettype ) { if ((RecordedId <= 0) && (chanid <= 0 || !recstarttsRaw.isValid())) throw QString("Recorded ID or Channel ID and StartTime appears invalid."); RecordingInfo ri; if (RecordedId > 0) ri = RecordingInfo(RecordedId); else ri = RecordingInfo(chanid, recstarttsRaw.toUTC()); uint64_t offset; bool isend=true; uint64_t position = ri.QueryBookmark(); if (offsettype.toLower() == "position"){ ri.QueryKeyFramePosition(&offset, position, isend); return offset; } else if (offsettype.toLower() == "duration"){ ri.QueryKeyFrameDuration(&offset, position, isend); return offset; } else return position; }
bool Dvr::AddDontRecordSchedule(int nChanId, const QDateTime &dStartTime, bool bNeverRecord) { bool bResult = true; if (nChanId <= 0 || !dStartTime.isValid()) throw QString("Program does not exist."); ProgramInfo *pi = LoadProgramFromProgram(nChanId, dStartTime.toUTC()); if (!pi) throw QString("Program does not exist."); // Why RecordingInfo instead of ProgramInfo? Good question ... RecordingInfo recInfo = RecordingInfo(*pi); delete pi; if (bNeverRecord) { recInfo.ApplyNeverRecord(); } else recInfo.ApplyRecordStateChange(kDontRecord); return bResult; }
void cRecorder::Action(void) { cTimeMs t(MAXBROKENTIMEOUT); bool InfoWritten = false; bool FirstIframeSeen = false; while (Running()) { int r; uchar *b = ringBuffer->Get(r); if (b) { int Count = frameDetector->Analyze(b, r); if (Count) { if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame break; if (frameDetector->Synced()) { if (!InfoWritten) { cRecordingInfo RecordingInfo(recordingName); if (RecordingInfo.Read()) { if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(RecordingInfo.FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(RecordingInfo.FramesPerSecond(), frameDetector->FramesPerSecond())) { RecordingInfo.SetFramesPerSecond(frameDetector->FramesPerSecond()); RecordingInfo.Write(); Recordings.UpdateByName(recordingName); } } InfoWritten = true; cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName); } if (FirstIframeSeen || frameDetector->IndependentFrame()) { FirstIframeSeen = true; // start recording with the first I-frame if (!NextFile()) break; if (index && frameDetector->NewFrame()) index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize); if (frameDetector->IndependentFrame()) { recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE); fileSize += TS_SIZE; int Index = 0; while (uchar *pmt = patPmtGenerator.GetPmt(Index)) { recordFile->Write(pmt, TS_SIZE); fileSize += TS_SIZE; } } if (recordFile->Write(b, Count) < 0) { LOG_ERROR_STR(fileName->Name()); break; } fileSize += Count; t.Set(MAXBROKENTIMEOUT); } } ringBuffer->Del(Count); } } if (t.TimedOut()) { esyslog("ERROR: video data stream broken"); ShutdownHandler.RequestEmergencyExit(); t.Set(MAXBROKENTIMEOUT); } } }
bool Dvr::ReactivateRecording(int RecordedId) { if (RecordedId <= 0) throw QString("Recorded ID is invalid."); RecordingInfo ri = RecordingInfo(RecordedId); ri.ReactivateRecording(); return true; }
DTC::CutList* Dvr::GetRecordedSeek ( int RecordedId, const QString &offsettype ) { MarkTypes marktype; if (RecordedId <= 0) throw QString("Recorded ID appears invalid."); RecordingInfo ri; ri = RecordingInfo(RecordedId); DTC::CutList* pCutList = new DTC::CutList(); if (offsettype == "BYTES") marktype = MARK_GOP_BYFRAME; else if (offsettype == "DURATION") marktype = MARK_DURATION_MS; else throw QString("Type must be 'BYTES' or 'DURATION'."); FillSeek(pCutList, &ri, marktype); return pCutList; }
bool Dvr::StopRecording(int RecordedId) { if (RecordedId <= 0) throw QString("Recorded ID is invalid."); RecordingInfo ri = RecordingInfo(RecordedId); if (ri.GetChanID()) { QString cmd = QString("STOP_RECORDING %1 %2") .arg(ri.GetChanID()) .arg(ri.GetRecordingStartTime(MythDate::ISODate)); MythEvent me(cmd); gCoreContext->dispatch(me); return true; } else throw QString("RecordID %1 not found").arg(RecordedId); return false; }
/** \fn MythSystemEventHandler::SubstituteMatches(const QStringList &tokens, QString &command) * \brief Substitutes %MATCH% variables in given command line. * \sa ProgramInfo::SubstituteMatches(QString &str) * * Subsitutes values for %MATCH% type variables in given command string. * Some of these matches come from the tokens list passed in and some * may come from a ProgramInfo if a chanid and starttime are specified * in the tokens list. * * \param tokens Const QStringList containing token list passed with event. * \param command Command line containing %MATCH% variables to be substituted. */ void MythSystemEventHandler::SubstituteMatches(const QStringList &tokens, QString &command) { if (command.isEmpty()) return; LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: BEFORE: %1") .arg(command)); QString args; uint chanid = 0; QDateTime recstartts; QString sender; QStringList::const_iterator it = tokens.begin(); ++it; command.replace(QString("%EVENTNAME%"), *it); ++it; while (it != tokens.end()) { if (!args.isEmpty()) args += " "; args += *it; // Check for some token names that we substitute one for one as // %MATCH% type variables. if ((*it == "CARDID") || (*it == "COMMAND") || (*it == "RECSTATUS") || (*it == "HOSTNAME") || (*it == "SECS") || (*it == "SENDER") || (*it == "PATH")) { QString token = *it; if (++it == tokens.end()) break; if (token == "SENDER") sender = *it; // The following string is broken up on purpose to indicate // what we're replacing is the token surrounded by percent signs command.replace(QString("%" "%1" "%").arg(token), *it); if (!args.isEmpty()) args += " "; args += *it; } // Remember any chanid and starttime so we can lookup info about // the recording from the database. if (*it == "CHANID") { if (++it == tokens.end()) break; chanid = (*it).toUInt(); if (!args.isEmpty()) args += " "; args += *it; } if (*it == "STARTTIME") { if (++it == tokens.end()) break; recstartts = MythDate::fromString(*it); if (!args.isEmpty()) args += " "; args += *it; } ++it; } command.replace(QString("%ARGS%"), args); ProgramInfo pginfo(chanid, recstartts); bool pginfo_loaded = pginfo.GetChanID(); if (!pginfo_loaded) { RecordingInfo::LoadStatus status; pginfo = RecordingInfo(chanid, recstartts, false, 0, &status); pginfo_loaded = RecordingInfo::kFoundProgram == status; } if (pginfo_loaded) { pginfo.SubstituteMatches(command); } else { command.replace(QString("%CHANID%"), QString::number(chanid)); command.replace(QString("%STARTTIME%"), MythDate::toString(recstartts, MythDate::kFilename)); command.replace(QString("%STARTTIMEISO%"), recstartts.toString(Qt::ISODate)); } command.replace(QString("%VERBOSELEVEL%"), QString("%1").arg(verboseMask)); LOG(VB_FILE, LOG_DEBUG, LOC + QString("SubstituteMatches: AFTER : %1") .arg(command)); }
bool cRecorder::NextFile(void) { if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame #ifdef USE_HARDLINKCUTTER if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) { #else if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) { #endif /* HARDLINKCUTTER */ recordFile = fileName->NextFile(); fileSize = 0; } } return recordFile != NULL; } void cRecorder::Activate(bool On) { if (On) Start(); else Cancel(3); } void cRecorder::Receive(uchar *Data, int Length) { if (Running()) { int p = ringBuffer->Put(Data, Length); if (p != Length && Running()) ringBuffer->ReportOverflow(Length - p); } } void cRecorder::Action(void) { time_t t = time(NULL); bool InfoWritten = false; bool FirstIframeSeen = false; #ifdef USE_LIVEBUFFER double fps = DEFAULTFRAMESPERSECOND; #endif /*USE_LIVEBUFFER*/ while (Running()) { int r; uchar *b = ringBuffer->Get(r); if (b) { int Count = frameDetector->Analyze(b, r); if (Count) { if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame break; if (frameDetector->Synced()) { #ifdef USE_LIVEBUFFER if(index && (frameDetector->FramesPerSecond() != fps)) { fps = frameDetector->FramesPerSecond(); index->SetFramesPerSecond(fps); } // if #endif /*USE_LIVEBUFFER*/ if (!InfoWritten) { cRecordingInfo RecordingInfo(recordingName); if (RecordingInfo.Read()) { if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(RecordingInfo.FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(RecordingInfo.FramesPerSecond(), frameDetector->FramesPerSecond())) { RecordingInfo.SetFramesPerSecond(frameDetector->FramesPerSecond()); RecordingInfo.Write(); Recordings.UpdateByName(recordingName); } } InfoWritten = true; } /* if (frameDetector->NewPayload()) { // We're at the first TS packet of a new payload... if (Buffering) esyslog("ERROR: encountered new payload while buffering - dropping some data!"); if (!frameDetector->NewFrame()) { // ...but the frame type is yet unknown, so we need to buffer packets until we see the frame type if (!Buffer) { dsyslog("frame type not in first packet of payload - buffering"); if (!(Buffer = MALLOC(uchar, BUFFERSIZE))) { esyslog("ERROR: can't allocate frame type buffer"); break; } } BufferIndex = 0; Buffering = true; } } else if (frameDetector->NewFrame()) // now we know the frame type, so stop buffering Buffering = false; if (Buffering) { if (BufferIndex + Count <= BUFFERSIZE) { memcpy(Buffer + BufferIndex, b, Count); BufferIndex += Count; } else esyslog("ERROR: too many bytes for frame type buffer (%d > %d) - dropped %d bytes", BufferIndex + Count, int(BUFFERSIZE), Count); } else if (FirstIframeSeen || frameDetector->IndependentFrame()) { */ #ifdef USE_LIVEBUFFER if(!FirstIframeSeen) FillInitialData(b, r); #endif /*USE_LIVEBUFFER*/ if (FirstIframeSeen || frameDetector->IndependentFrame()) { FirstIframeSeen = true; // start recording with the first I-frame if (!NextFile()) break; if (index && frameDetector->NewFrame()) index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize); if (frameDetector->IndependentFrame()) { recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE); fileSize += TS_SIZE; int Index = 0; while (uchar *pmt = patPmtGenerator.GetPmt(Index)) { recordFile->Write(pmt, TS_SIZE); fileSize += TS_SIZE; } } if (recordFile->Write(b, Count) < 0) { LOG_ERROR_STR(fileName->Name()); break; } fileSize += Count; t = time(NULL); } } ringBuffer->Del(Count); } } #ifdef USE_LIVEBUFFER if (handleError && (time(NULL) - t > MAXBROKENTIMEOUT)) { #else if (time(NULL) - t > MAXBROKENTIMEOUT) { #endif #if REELVDR Skins.QueueMessage(mtError, tr("can't record - check your configuration")); #else esyslog("ERROR: video data stream broken. Requesting Emergency Exit."); ShutdownHandler.RequestEmergencyExit(); #endif /*REELVDR*/ t = time(NULL); } } }