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 "mythcommflagplayer.h"
37 #include "importrecorder.h"
38 #include "mythdirs.h"
39 #include "tv_rec.h"
40 #include "mythdate.h"
41 
42 #define TVREC_CARDNUM \
43  ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
44 
45 #define LOC QString("ImportRec[%1](%2): ") \
46  .arg(TVREC_CARDNUM).arg(m_videodevice)
47 
49  const QString &videodev,
50  const QString &audiodev,
51  const QString &vbidev)
52 {
53  (void)audiodev;
54  (void)vbidev;
55  (void)profile;
56 
57  QString testVideoDev = videodev;
58 
59  if (videodev.toLower().startsWith("file:"))
60  testVideoDev = videodev.mid(5);
61 
62  QFileInfo fi(testVideoDev);
63  if (fi.exists() && fi.isReadable() && fi.isFile() && fi.size() > 1560)
64  SetOption("videodevice", testVideoDev);
65  else
66  SetOption("videodevice", "unknown file");
67 
68  SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
69  SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
70 }
71 
72 void UpdateFS(int pc, void* ir);
73 void UpdateFS(int /*pc*/, void* ir)
74 {
75  if(ir)
76  static_cast<ImportRecorder*>(ir)->UpdateRecSize();
77 }
78 
80 {
82 
83  if(m_cfp)
85 }
86 
88 {
89  return m_nfc;
90 }
91 
93 {
94  LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
95 
96  {
97  QMutexLocker locker(&m_pauseLock);
98  m_request_recording = true;
99  m_recording = true;
100  m_recordingWait.wakeAll();
101  }
102 
103  LOG(VB_RECORD, LOG_INFO, LOC + "run -- " +
104  QString("attempting to open '%1'")
105  .arg(m_curRecording->GetPathname()));
106 
107  // retry opening the file until StopRecording() is called.
108  while (!Open() && IsRecordingRequested() && !IsErrored())
109  { // sleep 250 milliseconds unless StopRecording() or Unpause()
110  // is called, just to avoid running this loop too often.
111  QMutexLocker locker(&m_pauseLock);
113  m_unpauseWait.wait(&m_pauseLock, 15000);
114  }
115 
117 
118  // build seek table
120  {
121  MythCommFlagPlayer *cfp =
124  m_ringBuffer->GetFilename(), false, true, 6000);
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 
131  ctx->SetRingBuffer(rb);
132  ctx->SetPlayer(cfp);
133  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
134 
135  m_cfp=cfp;
137  cfp->RebuildSeekTable(false,UpdateFS,this);
139  m_cfp=nullptr;
140 
141  delete ctx;
142  }
143 
145 
146  // cleanup...
147  Close();
148 
149  FinishRecording();
150 
151  QMutexLocker locker(&m_pauseLock);
152  m_recording = false;
153  m_recordingWait.wakeAll();
154 
155  LOG(VB_RECORD, LOG_INFO, LOC + "run -- end");
156 }
157 
159 {
160  if (m_import_fd >= 0) // already open
161  return true;
162 
163  if (!m_curRecording)
164  {
165  LOG(VB_RECORD, LOG_ERR, LOC + "no current recording!");
166  return false;
167  }
168 
169  ResetForNewFile();
170 
171  QString fn = m_curRecording->GetPathname();
172 
173  // Quick-and-dirty "copy" of sample prerecorded file.
174  // Sadly, won't work on Windows.
175  //
176  QFile preRecorded(m_videodevice);
177  QFile copy(fn);
178  if (preRecorded.exists() && (!copy.exists() || copy.size() == 0))
179  {
180  if (copy.exists()) // always created by RecorderBase?
181  {
182  QDir targetDir("."); // QDir::remove() needs an object
183  targetDir.remove(fn);
184  }
185 
186  LOG(VB_RECORD, LOG_INFO, LOC + QString("Trying to link %1 to %2")
187  .arg(m_videodevice).arg(fn));
188 
189  if (preRecorded.link(fn))
190  LOG(VB_RECORD, LOG_DEBUG, LOC + "success!");
191  else
192  LOG(VB_RECORD, LOG_ERR, LOC + preRecorded.errorString());
193  }
194 
195  if (fn.toLower().startsWith("myth://"))
196  {
197  LOG(VB_RECORD, LOG_ERR, LOC + "Malformed recording ProgramInfo.");
198  return false;
199  }
200 
201  QFileInfo f(fn);
202  if (!f.exists())
203  {
204  LOG(VB_RECORD, LOG_INFO, LOC +
205  QString("'%1' does not exist yet").arg(fn));
206 
207  // Slow down run open loop when debugging -v record.
208  // This is just to make the debugging output less spammy.
209  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_ANY))
210  std::this_thread::sleep_for(std::chrono::milliseconds(250));
211 
212  return false;
213  }
214  if (!f.isReadable())
215  {
216  LOG(VB_GENERAL, LOG_ERR, LOC +
217  QString("'%1' is not readable").arg(fn));
218  return false;
219  }
220  if (!f.size())
221  {
222  LOG(VB_GENERAL, LOG_ERR, LOC +
223  QString("'%1' is empty").arg(fn));
224  return false;
225  }
226 
227  m_import_fd = open(fn.toLocal8Bit().constData(), O_RDONLY);
228  if (m_import_fd < 0)
229  {
230  LOG(VB_GENERAL, LOG_ERR, LOC +
231  QString("Couldn't open '%1'").arg(fn) + ENO);
232  }
233 
234  return m_import_fd >= 0;
235 }
236 
238 {
239  if (m_import_fd >= 0)
240  {
242  m_import_fd = -1;
243  }
244 }
const char * kImportRecorderInUseID
PlayerFlags
Definition: mythplayer.h:88
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
QMutex m_pauseLock
Definition: recorderbase.h:326
void ResetForNewFile(void) override
QString GetFilename(void) const
Returns name of file used by this RingBuffer.
void SetRingBuffer(RingBuffer *buf)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:104
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:48
void UpdateFS(int pc, void *ir)
long long copy(QFile &dst, QFile &src, uint block_size)
Copies src file to dst file.
bool m_request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
RingBuffer * m_ringBuffer
Definition: recorderbase.h:304
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
bool RebuildSeekTable(bool showPercentage=true, StatusCallback cb=nullptr, void *cbData=nullptr)
void SetPlayer(MythPlayer *newplayer)
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
#define close
Definition: compat.h:16
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
QString GetSetting(const QString &key, const QString &defaultval="")
long long GetFramesWritten(void) override
Returns number of frames written to disk.
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
QWaitCondition m_recordingWait
Definition: recorderbase.h:335
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:334
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:83
void UnregisterFileForWrite(const QString &file)
void SetPlayerInfo(TV *tv, QWidget *widget, PlayerContext *ctx)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
MythCommFlagPlayer * m_cfp
long long GetRealFileSize(void) const
Returns the size of the file we are reading/writing, or -1 if the query fails.
Definition: ringbuffer.cpp:497
QString m_videodevice
Definition: recorderbase.h:311
RecordingInfo * m_curRecording
Definition: recorderbase.h:323
Implements a file/stream reader/writer.
long long GetFramesRead(void) const
Definition: decoderbase.h:205
QString GetPathname(void) const
Definition: programinfo.h:335
#define LOC
DecoderBase * GetDecoder(void)
Returns the stream decoder currently in use.
Definition: mythplayer.h:278
long long m_nfc
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.