MythTV  master
importrecorder.cpp
Go to the documentation of this file.
1 /* -*- Mode: c++ -*-
2  * Class ImportRecorder
3  *
4  * Copyright (C) Daniel Kristjansson 2009
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 // POSIX
22 #ifndef _WIN32
23 #include <sys/select.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 
29 #include <chrono> // for milliseconds
30 #include <thread> // for sleep_for
31 
32 // Qt
33 #include <QDir>
34 
35 // MythTV
36 #include "libmythbase/mythdate.h"
37 #include "libmythbase/mythdirs.h"
38 
39 #include "importrecorder.h"
40 #include "mythcommflagplayer.h"
41 #include "tv_rec.h"
42 
43 #define TVREC_CARDNUM \
44  ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
45 
46 #define LOC QString("ImportRec[%1](%2): ") \
47  .arg(TVREC_CARDNUM, m_videodevice)
48 
50  const QString &videodev,
51  const QString &audiodev,
52  const QString &vbidev)
53 {
54  (void)audiodev;
55  (void)vbidev;
56  (void)profile;
57 
58  QString testVideoDev = videodev;
59 
60  if (videodev.startsWith("file:", Qt::CaseInsensitive))
61  testVideoDev = videodev.mid(5);
62 
63  QFileInfo fi(testVideoDev);
64  if (fi.exists() && fi.isReadable() && fi.isFile() && fi.size() > 1560)
65  SetOption("videodevice", testVideoDev);
66  else
67  SetOption("videodevice", "unknown file");
68 
69  SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
70  SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
71 }
72 
73 void UpdateFS(int pc, void* ir);
74 void UpdateFS(int /*pc*/, void* ir)
75 {
76  if(ir)
77  static_cast<ImportRecorder*>(ir)->UpdateRecSize();
78 }
79 
81 {
83 
84  if(m_cfp)
86 }
87 
89 {
90  return m_nfc;
91 }
92 
94 {
95  LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
96 
97  {
98  QMutexLocker locker(&m_pauseLock);
99  m_requestRecording = true;
100  m_recording = true;
101  m_recordingWait.wakeAll();
102  }
103 
104  LOG(VB_RECORD, LOG_INFO, LOC + "run -- " +
105  QString("attempting to open '%1'")
106  .arg(m_curRecording->GetPathname()));
107 
108  // retry opening the file until StopRecording() is called.
109  while (!Open() && IsRecordingRequested() && !IsErrored())
110  { // sleep 250 milliseconds unless StopRecording() or Unpause()
111  // is called, just to avoid running this loop too often.
112  QMutexLocker locker(&m_pauseLock);
113  if (m_requestRecording)
114  m_unpauseWait.wait(&m_pauseLock, 15000);
115  }
116 
118 
119  // build seek table
121  {
122  auto *ctx = new PlayerContext(kImportRecorderInUseID);
123  auto *cfp = new MythCommFlagPlayer(ctx, (PlayerFlags)(kAudioMuted | kVideoIsNull | kNoITV));
124  MythMediaBuffer *buffer = MythMediaBuffer::Create(m_ringBuffer->GetFilename(), false, true, 6s);
125  //This does update the status but does not set the ultimate
126  //recorded / failure status for the relevant recording
127  SetRecordingStatus(RecStatus::Recording, __FILE__, __LINE__);
128  ctx->SetPlayingInfo(m_curRecording);
129  ctx->SetRingBuffer(buffer);
130  ctx->SetPlayer(cfp);
131 
132  m_cfp=cfp;
134  cfp->RebuildSeekTable(false,UpdateFS,this);
136  m_cfp=nullptr;
137 
138  delete ctx;
139  }
140 
142 
143  // cleanup...
144  Close();
145 
146  FinishRecording();
147 
148  QMutexLocker locker(&m_pauseLock);
149  m_recording = false;
150  m_recordingWait.wakeAll();
151 
152  LOG(VB_RECORD, LOG_INFO, LOC + "run -- end");
153 }
154 
156 {
157  if (m_importFd >= 0) // already open
158  return true;
159 
160  if (!m_curRecording)
161  {
162  LOG(VB_RECORD, LOG_ERR, LOC + "no current recording!");
163  return false;
164  }
165 
166  ResetForNewFile();
167 
168  QString fn = m_curRecording->GetPathname();
169 
170  // Quick-and-dirty "copy" of sample prerecorded file.
171  // Sadly, won't work on Windows.
172  //
173  QFile preRecorded(m_videodevice);
174  QFile copy(fn);
175  if (preRecorded.exists() && (!copy.exists() || copy.size() == 0))
176  {
177  if (copy.exists()) // always created by RecorderBase?
178  {
179  QDir targetDir("."); // QDir::remove() needs an object
180  targetDir.remove(fn);
181  }
182 
183  LOG(VB_RECORD, LOG_INFO, LOC + QString("Trying to link %1 to %2")
184  .arg(m_videodevice, fn));
185 
186  if (preRecorded.link(fn))
187  LOG(VB_RECORD, LOG_DEBUG, LOC + "success!");
188  else
189  LOG(VB_RECORD, LOG_ERR, LOC + preRecorded.errorString());
190  }
191 
192  if (fn.startsWith("myth://", Qt::CaseInsensitive))
193  {
194  LOG(VB_RECORD, LOG_ERR, LOC + "Malformed recording ProgramInfo.");
195  return false;
196  }
197 
198  QFileInfo f(fn);
199  if (!f.exists())
200  {
201  LOG(VB_RECORD, LOG_INFO, LOC +
202  QString("'%1' does not exist yet").arg(fn));
203 
204  // Slow down run open loop when debugging -v record.
205  // This is just to make the debugging output less spammy.
206  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_ANY))
207  std::this_thread::sleep_for(250ms);
208 
209  return false;
210  }
211  if (!f.isReadable())
212  {
213  LOG(VB_GENERAL, LOG_ERR, LOC +
214  QString("'%1' is not readable").arg(fn));
215  return false;
216  }
217  if (!f.size())
218  {
219  LOG(VB_GENERAL, LOG_ERR, LOC +
220  QString("'%1' is empty").arg(fn));
221  return false;
222  }
223 
224  m_importFd = open(fn.toLocal8Bit().constData(), O_RDONLY);
225  if (m_importFd < 0)
226  {
227  LOG(VB_GENERAL, LOG_ERR, LOC +
228  QString("Couldn't open '%1'").arg(fn) + ENO);
229  }
230 
231  return m_importFd >= 0;
232 }
233 
235 {
236  if (m_importFd >= 0)
237  {
238  close(m_importFd);
239  m_importFd = -1;
240  }
241 }
ImportRecorder::m_cfp
MythCommFlagPlayer * m_cfp
Definition: importrecorder.h:49
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
ImportRecorder::Open
bool Open(void)
Definition: importrecorder.cpp:155
MythCoreContext::UnregisterFileForWrite
void UnregisterFileForWrite(const QString &file)
Definition: mythcorecontext.cpp:2110
DTVRecorder::ResetForNewFile
void ResetForNewFile(void) override
Definition: dtvrecorder.cpp:140
mythcommflagplayer.h
MythCoreContext::RegisterFileForWrite
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
Definition: mythcorecontext.cpp:2092
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
DTVRecorder::IsErrored
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:45
MythMediaBuffer
Definition: mythmediabuffer.h:50
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
PlayerFlags
PlayerFlags
Definition: mythplayer.h:65
mythdirs.h
DTVRecorder::FinishRecording
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
Definition: dtvrecorder.cpp:126
ImportRecorder::run
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
Definition: importrecorder.cpp:93
ProgramInfo::GetPathname
QString GetPathname(void) const
Definition: programinfo.h:343
RecorderBase::m_pauseLock
QMutex m_pauseLock
Definition: recorderbase.h:338
close
#define close
Definition: compat.h:43
RecorderBase::m_recordingWait
QWaitCondition m_recordingWait
Definition: recorderbase.h:347
RecorderBase::m_curRecording
RecordingInfo * m_curRecording
Definition: recorderbase.h:335
MythMediaBuffer::GetFilename
QString GetFilename(void) const
Definition: mythmediabuffer.cpp:1740
MythMediaBuffer::GetRealFileSize
long long GetRealFileSize(void) const
Definition: mythmediabuffer.cpp:462
mythdate.h
hardwareprofile.scan.profile
profile
Definition: scan.py:99
ImportRecorder::GetFramesWritten
long long GetFramesWritten(void) override
Returns number of frames written to disk.
Definition: importrecorder.cpp:88
MythFile::copy
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
Definition: mythmiscutil.cpp:264
LOC
#define LOC
Definition: importrecorder.cpp:46
RecordingInfo::SaveFilesize
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
Definition: recordinginfo.cpp:1734
ImportRecorder::SetOptionsFromProfile
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
Definition: importrecorder.cpp:49
importrecorder.h
RecorderBase::IsRecordingRequested
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.
Definition: recorderbase.cpp:250
MythCommFlagPlayer
Definition: mythcommflagplayer.h:25
ImportRecorder::m_importFd
int m_importFd
Definition: importrecorder.h:48
DTVRecorder::SetOption
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:91
kNoITV
@ kNoITV
Definition: mythplayer.h:76
ImportRecorder::Close
void Close(void)
Definition: importrecorder.cpp:234
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
ImportRecorder::UpdateRecSize
void UpdateRecSize()
Definition: importrecorder.cpp:80
MythPlayer::GetDecoder
DecoderBase * GetDecoder(void)
Returns the stream decoder currently in use.
Definition: mythplayer.h:186
RecorderBase::m_videodevice
QString m_videodevice
Definition: recorderbase.h:323
RecorderBase::m_ringBuffer
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:316
kVideoIsNull
@ kVideoIsNull
Definition: mythplayer.h:74
RecStatus::Recording
@ Recording
Definition: recordingstatus.h:30
RecorderBase::SetRecordingStatus
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
Definition: recorderbase.cpp:396
ImportRecorder::m_nfc
long long m_nfc
Definition: importrecorder.h:50
RecorderBase::m_recording
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:346
ImportRecorder
ImportRecorder imports files, creating a seek map and other stuff that MythTV likes to have for recor...
Definition: importrecorder.h:23
tv_rec.h
kAudioMuted
@ kAudioMuted
Definition: mythplayer.h:75
PlayerContext
Definition: playercontext.h:53
MythMediaBuffer::Create
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
Definition: mythmediabuffer.cpp:98
RecordingProfile
Definition: recordingprofile.h:41
UpdateFS
void UpdateFS(int pc, void *ir)
Definition: importrecorder.cpp:74
RecorderBase::m_unpauseWait
QWaitCondition m_unpauseWait
Definition: recorderbase.h:342
DecoderBase::GetFramesRead
long long GetFramesRead(void) const
Definition: decoderbase.h:203
kImportRecorderInUseID
const QString kImportRecorderInUseID
Definition: programtypes.cpp:19
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:896
RecorderBase::m_requestRecording
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:344