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#include <QtGlobal>
22#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
23#include <QtSystemDetection>
24#endif
25
26// POSIX
27#ifndef Q_OS_WINDOWS
28#include <sys/select.h>
29#endif
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33
34#include <chrono> // for milliseconds
35#include <thread> // for sleep_for
36
37// Qt
38#include <QDir>
39
40// MythTV
45
46#include "importrecorder.h"
47#include "mythcommflagplayer.h"
48#include "tv_rec.h"
49
50#define TVREC_CARDNUM \
51 ((m_tvrec != nullptr) ? QString::number(m_tvrec->GetInputId()) : "NULL")
52
53#define LOC QString("ImportRec[%1](%2): ") \
54 .arg(TVREC_CARDNUM, m_videodevice)
55
57 const QString &videodev,
58 [[maybe_unused]] const QString &audiodev,
59 [[maybe_unused]] const QString &vbidev)
60{
61 QString testVideoDev = videodev;
62
63 if (videodev.startsWith("file:", Qt::CaseInsensitive))
64 testVideoDev = videodev.mid(5);
65
66 QFileInfo fi(testVideoDev);
67 if (fi.exists() && fi.isReadable() && fi.isFile() && fi.size() > 1560)
68 SetOption("videodevice", testVideoDev);
69 else
70 SetOption("videodevice", "unknown file");
71
72 SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
73 SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
74}
75
76void UpdateFS(int pc, void* ir);
77void UpdateFS(int /*pc*/, void* ir)
78{
79 if(ir)
80 static_cast<ImportRecorder*>(ir)->UpdateRecSize();
81}
82
84{
86
87 if(m_cfp)
89}
90
92{
93 return m_nfc;
94}
95
97{
98 LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
99
100 {
101 QMutexLocker locker(&m_pauseLock);
102 m_requestRecording = true;
103 m_recording = true;
104 m_recordingWait.wakeAll();
105 }
106
107 LOG(VB_RECORD, LOG_INFO, LOC + "run -- " +
108 QString("attempting to open '%1'")
109 .arg(m_curRecording->GetPathname()));
110
111 // retry opening the file until StopRecording() is called.
112 while (!Open() && IsRecordingRequested() && !IsErrored())
113 { // sleep 250 milliseconds unless StopRecording() or Unpause()
114 // is called, just to avoid running this loop too often.
115 QMutexLocker locker(&m_pauseLock);
117 m_unpauseWait.wait(&m_pauseLock, 15000);
118 }
119
121
122 // build seek table
124 {
125 auto *ctx = new PlayerContext(kImportRecorderInUseID);
126 auto *cfp = new MythCommFlagPlayer(ctx, (PlayerFlags)(kAudioMuted | kVideoIsNull | kNoITV));
128 //This does update the status but does not set the ultimate
129 //recorded / failure status for the relevant recording
130 SetRecordingStatus(RecStatus::Recording, __FILE__, __LINE__);
131 ctx->SetPlayingInfo(m_curRecording);
132 ctx->SetRingBuffer(buffer);
133 ctx->SetPlayer(cfp);
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
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_importFd >= 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
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, 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.startsWith("myth://", Qt::CaseInsensitive))
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(250ms);
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_importFd = open(fn.toLocal8Bit().constData(), O_RDONLY);
228 if (m_importFd < 0)
229 {
230 LOG(VB_GENERAL, LOG_ERR, LOC +
231 QString("Couldn't open '%1'").arg(fn) + ENO);
232 }
233
234 return m_importFd >= 0;
235}
236
238{
239 if (m_importFd >= 0)
240 {
242 m_importFd = -1;
243 }
244}
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:98
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:183
QString GetPathname(void) const
Definition: programinfo.h:350
QMutex m_pauseLock
Definition: recorderbase.h:314
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:322
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
MythMediaBuffer * m_ringBuffer
Definition: recorderbase.h:292
QString m_videodevice
Definition: recorderbase.h:299
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:320
RecordingInfo * m_curRecording
Definition: recorderbase.h:311
QWaitCondition m_unpauseWait
Definition: recorderbase.h:318
QWaitCondition m_recordingWait
Definition: recorderbase.h:323
void SaveFilesize(uint64_t fsize) override
Sets recording file size in database, and sets "filesize" field.
#define close
Definition: compat.h:28
#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:64
@ kAudioMuted
Definition: mythplayer.h:73
@ kVideoIsNull
Definition: mythplayer.h:72
@ kNoITV
Definition: mythplayer.h:74
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
const QString kImportRecorderInUseID