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
40
41#include "importrecorder.h"
42#include "mythcommflagplayer.h"
43#include "tv_rec.h"
44
45#define TVREC_CARDNUM \
46 ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
47
48#define LOC QString("ImportRec[%1](%2): ") \
49 .arg(TVREC_CARDNUM, m_videodevice)
50
52 const QString &videodev,
53 [[maybe_unused]] const QString &audiodev,
54 [[maybe_unused]] const QString &vbidev)
55{
56 QString testVideoDev = videodev;
57
58 if (videodev.startsWith("file:", Qt::CaseInsensitive))
59 testVideoDev = videodev.mid(5);
60
61 QFileInfo fi(testVideoDev);
62 if (fi.exists() && fi.isReadable() && fi.isFile() && fi.size() > 1560)
63 SetOption("videodevice", testVideoDev);
64 else
65 SetOption("videodevice", "unknown file");
66
67 SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
68 SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
69}
70
71void UpdateFS(int pc, void* ir);
72void UpdateFS(int /*pc*/, void* ir)
73{
74 if(ir)
75 static_cast<ImportRecorder*>(ir)->UpdateRecSize();
76}
77
79{
81
82 if(m_cfp)
84}
85
87{
88 return m_nfc;
89}
90
92{
93 LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
94
95 {
96 QMutexLocker locker(&m_pauseLock);
97 m_requestRecording = true;
98 m_recording = true;
99 m_recordingWait.wakeAll();
100 }
101
102 LOG(VB_RECORD, LOG_INFO, LOC + "run -- " +
103 QString("attempting to open '%1'")
104 .arg(m_curRecording->GetPathname()));
105
106 // retry opening the file until StopRecording() is called.
107 while (!Open() && IsRecordingRequested() && !IsErrored())
108 { // sleep 250 milliseconds unless StopRecording() or Unpause()
109 // is called, just to avoid running this loop too often.
110 QMutexLocker locker(&m_pauseLock);
112 m_unpauseWait.wait(&m_pauseLock, 15000);
113 }
114
116
117 // build seek table
119 {
120 auto *ctx = new PlayerContext(kImportRecorderInUseID);
121 auto *cfp = new MythCommFlagPlayer(ctx, (PlayerFlags)(kAudioMuted | kVideoIsNull | kNoITV));
123 //This does update the status but does not set the ultimate
124 //recorded / failure status for the relevant recording
125 SetRecordingStatus(RecStatus::Recording, __FILE__, __LINE__);
126 ctx->SetPlayingInfo(m_curRecording);
127 ctx->SetRingBuffer(buffer);
128 ctx->SetPlayer(cfp);
129
130 m_cfp=cfp;
132 cfp->RebuildSeekTable(false,UpdateFS,this);
134 m_cfp=nullptr;
135
136 delete ctx;
137 }
138
140
141 // cleanup...
142 Close();
143
145
146 QMutexLocker locker(&m_pauseLock);
147 m_recording = false;
148 m_recordingWait.wakeAll();
149
150 LOG(VB_RECORD, LOG_INFO, LOC + "run -- end");
151}
152
154{
155 if (m_importFd >= 0) // already open
156 return true;
157
158 if (!m_curRecording)
159 {
160 LOG(VB_RECORD, LOG_ERR, LOC + "no current recording!");
161 return false;
162 }
163
165
166 QString fn = m_curRecording->GetPathname();
167
168 // Quick-and-dirty "copy" of sample prerecorded file.
169 // Sadly, won't work on Windows.
170 //
171 QFile preRecorded(m_videodevice);
172 QFile copy(fn);
173 if (preRecorded.exists() && (!copy.exists() || copy.size() == 0))
174 {
175 if (copy.exists()) // always created by RecorderBase?
176 {
177 QDir targetDir("."); // QDir::remove() needs an object
178 targetDir.remove(fn);
179 }
180
181 LOG(VB_RECORD, LOG_INFO, LOC + QString("Trying to link %1 to %2")
182 .arg(m_videodevice, fn));
183
184 if (preRecorded.link(fn))
185 LOG(VB_RECORD, LOG_DEBUG, LOC + "success!");
186 else
187 LOG(VB_RECORD, LOG_ERR, LOC + preRecorded.errorString());
188 }
189
190 if (fn.startsWith("myth://", Qt::CaseInsensitive))
191 {
192 LOG(VB_RECORD, LOG_ERR, LOC + "Malformed recording ProgramInfo.");
193 return false;
194 }
195
196 QFileInfo f(fn);
197 if (!f.exists())
198 {
199 LOG(VB_RECORD, LOG_INFO, LOC +
200 QString("'%1' does not exist yet").arg(fn));
201
202 // Slow down run open loop when debugging -v record.
203 // This is just to make the debugging output less spammy.
204 if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_ANY))
205 std::this_thread::sleep_for(250ms);
206
207 return false;
208 }
209 if (!f.isReadable())
210 {
211 LOG(VB_GENERAL, LOG_ERR, LOC +
212 QString("'%1' is not readable").arg(fn));
213 return false;
214 }
215 if (!f.size())
216 {
217 LOG(VB_GENERAL, LOG_ERR, LOC +
218 QString("'%1' is empty").arg(fn));
219 return false;
220 }
221
222 m_importFd = open(fn.toLocal8Bit().constData(), O_RDONLY);
223 if (m_importFd < 0)
224 {
225 LOG(VB_GENERAL, LOG_ERR, LOC +
226 QString("Couldn't open '%1'").arg(fn) + ENO);
227 }
228
229 return m_importFd >= 0;
230}
231
233{
234 if (m_importFd >= 0)
235 {
237 m_importFd = -1;
238 }
239}
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:46
void ResetForNewFile(void) override
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: dtvrecorder.cpp:92
long long GetFramesRead(void) const
Definition: decoderbase.h:194
ImportRecorder imports files, creating a seek map and other stuff that MythTV likes to have for recor...
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
long long GetFramesWritten(void) override
Returns number of frames written to disk.
long long m_nfc
MythCommFlagPlayer * m_cfp
QString GetSetting(const QString &key, const QString &defaultval="")
void RegisterFileForWrite(const QString &file, uint64_t size=0LL)
void UnregisterFileForWrite(const QString &file)
long long GetRealFileSize(void) const
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
QString GetFilename(void) const
DecoderBase * GetDecoder(void)
Returns the stream decoder currently in use.
Definition: mythplayer.h:184
QString GetPathname(void) const
Definition: programinfo.h:344
QMutex m_pauseLock
Definition: recorderbase.h:313
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:321
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:291
QString m_videodevice
Definition: recorderbase.h:298
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:319
RecordingInfo * m_curRecording
Definition: recorderbase.h:310
QWaitCondition m_unpauseWait
Definition: recorderbase.h:317
QWaitCondition m_recordingWait
Definition: recorderbase.h:322
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
#define close
Definition: compat.h:39
#define LOC
void UpdateFS(int pc, void *ir)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
PlayerFlags
Definition: mythplayer.h:65
@ kAudioMuted
Definition: mythplayer.h:74
@ kVideoIsNull
Definition: mythplayer.h:73
@ kNoITV
Definition: mythplayer.h:75
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
const QString kImportRecorderInUseID