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  ((tvrec != nullptr) ? QString::number(tvrec->GetInputId()) : "NULL")
44 
45 #define LOC QString("ImportRec[%1](%2): ") \
46  .arg(TVREC_CARDNUM).arg(videodevice)
47 
49  DTVRecorder(rec), _import_fd(-1), m_cfp(nullptr), m_nfc(0)
50 {
51 }
52 
54 {
55 }
56 
58  const QString &videodev,
59  const QString &audiodev,
60  const QString &vbidev)
61 {
62  (void)audiodev;
63  (void)vbidev;
64  (void)profile;
65 
66  QString testVideoDev = videodev;
67 
68  if (videodev.toLower().startsWith("file:"))
69  testVideoDev = videodev.mid(5);
70 
71  QFileInfo fi(testVideoDev);
72  if (fi.exists() && fi.isReadable() && fi.isFile() && fi.size() > 1560)
73  SetOption("videodevice", testVideoDev);
74  else
75  SetOption("videodevice", "unknown file");
76 
77  SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
78  SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
79 }
80 
81 void UpdateFS(int pc, void* ir);
82 void UpdateFS(int /*pc*/, void* ir)
83 {
84  if(ir)
85  static_cast<ImportRecorder*>(ir)->UpdateRecSize();
86 }
87 
89 {
91 
92  if(m_cfp)
94 }
95 
97 {
98  return m_nfc;
99 }
100 
102 {
103  LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
104 
105  {
106  QMutexLocker locker(&pauseLock);
107  request_recording = true;
108  recording = true;
109  recordingWait.wakeAll();
110  }
111 
112  LOG(VB_RECORD, LOG_INFO, LOC + "run -- " +
113  QString("attempting to open '%1'")
114  .arg(curRecording->GetPathname()));
115 
116  // retry opening the file until StopRecording() is called.
117  while (!Open() && IsRecordingRequested() && !IsErrored())
118  { // sleep 250 milliseconds unless StopRecording() or Unpause()
119  // is called, just to avoid running this loop too often.
120  QMutexLocker locker(&pauseLock);
121  if (request_recording)
122  unpauseWait.wait(&pauseLock, 15000);
123  }
124 
126 
127  // build seek table
129  {
130  MythCommFlagPlayer *cfp =
133  ringBuffer->GetFilename(), false, true, 6000);
134  //This does update the status but does not set the ultimate
135  //recorded / failure status for the relevant recording
136  SetRecordingStatus(RecStatus::Recording, __FILE__, __LINE__);
137 
140  ctx->SetRingBuffer(rb);
141  ctx->SetPlayer(cfp);
142  cfp->SetPlayerInfo(nullptr, nullptr, ctx);
143 
144  m_cfp=cfp;
146  cfp->RebuildSeekTable(false,UpdateFS,this);
148  m_cfp=nullptr;
149 
150  delete ctx;
151  }
152 
154 
155  // cleanup...
156  Close();
157 
158  FinishRecording();
159 
160  QMutexLocker locker(&pauseLock);
161  recording = false;
162  recordingWait.wakeAll();
163 
164  LOG(VB_RECORD, LOG_INFO, LOC + "run -- end");
165 }
166 
168 {
169  if (_import_fd >= 0) // already open
170  return true;
171 
172  if (!curRecording)
173  {
174  LOG(VB_RECORD, LOG_ERR, LOC + "no current recording!");
175  return false;
176  }
177 
178  ResetForNewFile();
179 
180  QString fn = curRecording->GetPathname();
181 
182  // Quick-and-dirty "copy" of sample prerecorded file.
183  // Sadly, won't work on Windows.
184  //
185  QFile preRecorded(videodevice);
186  QFile copy(fn);
187  if (preRecorded.exists() && (!copy.exists() || copy.size() == 0))
188  {
189  if (copy.exists()) // always created by RecorderBase?
190  {
191  QDir targetDir("."); // QDir::remove() needs an object
192  targetDir.remove(fn);
193  }
194 
195  LOG(VB_RECORD, LOG_INFO, LOC + QString("Trying to link %1 to %2")
196  .arg(videodevice).arg(fn));
197 
198  if (preRecorded.link(fn))
199  LOG(VB_RECORD, LOG_DEBUG, LOC + "success!");
200  else
201  LOG(VB_RECORD, LOG_ERR, LOC + preRecorded.errorString());
202  }
203 
204  if (fn.toLower().startsWith("myth://"))
205  {
206  LOG(VB_RECORD, LOG_ERR, LOC + "Malformed recording ProgramInfo.");
207  return false;
208  }
209 
210  QFileInfo f(fn);
211  if (!f.exists())
212  {
213  LOG(VB_RECORD, LOG_INFO, LOC +
214  QString("'%1' does not exist yet").arg(fn));
215 
216  // Slow down run open loop when debugging -v record.
217  // This is just to make the debugging output less spammy.
218  if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_ANY))
219  std::this_thread::sleep_for(std::chrono::milliseconds(250));
220 
221  return false;
222  }
223  else if (!f.isReadable())
224  {
225  LOG(VB_GENERAL, LOG_ERR, LOC +
226  QString("'%1' is not readable").arg(fn));
227  return false;
228  }
229  else if (!f.size())
230  {
231  LOG(VB_GENERAL, LOG_ERR, LOC +
232  QString("'%1' is empty").arg(fn));
233  return false;
234  }
235 
236  _import_fd = open(fn.toLocal8Bit().constData(), O_RDONLY);
237  if (_import_fd < 0)
238  {
239  LOG(VB_GENERAL, LOG_ERR, LOC +
240  QString("Couldn't open '%1'").arg(fn) + ENO);
241  }
242 
243  return _import_fd >= 0;
244 }
245 
247 {
248  if (_import_fd >= 0)
249  {
250  close(_import_fd);
251  _import_fd = -1;
252  }
253 }
const char * kImportRecorderInUseID
RingBuffer * ringBuffer
Definition: recorderbase.h:297
This is a specialization of RecorderBase used to handle MPEG-2, MPEG-4, MPEG-4 AVC,...
Definition: dtvrecorder.h:28
QWaitCondition unpauseWait
Definition: recorderbase.h:323
RecordingInfo * curRecording
Definition: recorderbase.h:316
PlayerFlags
Definition: mythplayer.h:88
QMutex pauseLock
Definition: recorderbase.h:319
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
void ResetForNewFile(void) override
QString GetFilename(void) const
Returns name of file used by this RingBuffer.
void SetPlayer(MythPlayer *new_player)
void SetRingBuffer(RingBuffer *buf)
QWaitCondition recordingWait
Definition: recorderbase.h:328
ImportRecorder(TVRec *)
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:112
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.
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
bool RebuildSeekTable(bool showPercentage=true, StatusCallback cb=nullptr, void *cbData=nullptr)
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
bool recording
True while recording is actually being performed.
Definition: recorderbase.h:327
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:150
bool request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:325
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
QString GetSetting(const QString &key, const QString &defaultval="")
void SetOption(const QString &opt, const QString &value) override
Set an specific option.
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.
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
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:532
Implements a file/stream reader/writer.
long long GetFramesRead(void) const
Definition: decoderbase.h:209
QString GetPathname(void) const
Definition: programinfo.h:337
QString videodevice
Definition: recorderbase.h:304
#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.