MythTV master
v4l2encrecorder.cpp
Go to the documentation of this file.
1/* -*- Mode: c++ -*-
2 * Class V4L2encRecorder
3 *
4 * Copyright (C) John Poet 2014
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 <chrono> // for milliseconds
22#include <sys/ioctl.h>
23#include <thread> // for sleep_for
24
25// Qt includes
26#include <QString>
27
28// MythTV includes
31
32#include "io/mythmediabuffer.h"
36#include "recordingprofile.h"
37#include "tv_rec.h"
38
39#define LOC QString("V4L2Rec[%1](%2): ") \
40 .arg(m_tvrec ? m_tvrec->GetInputId() : -1) \
41 .arg(m_channel->GetDevice())
42
44 V4LRecorder(rec), m_channel(channel)
45{
46 if (!Open())
47 {
48 m_error = "Failed to open device";
49 LOG(VB_GENERAL, LOG_ERR, LOC + "Open() -- " + m_error);
50 return;
51 }
52}
53
54
56{
57 const StandardSetting *setting = profile->byName(name);
58 if (setting)
59 {
60 if (!m_streamHandler->SetOption(name, setting->getValue().toInt()))
61 V4LRecorder::SetOption(name, setting->getValue().toInt());
62 }
63}
64
66{
67 const StandardSetting *setting = profile->byName(name);
68 if (setting)
69 {
70 if (!m_streamHandler->SetOption(name, setting->getValue()))
71 V4LRecorder::SetOption(name, setting->getValue());
72 }
73}
74
76 const QString &videodev,
77 [[maybe_unused]] const QString &audiodev,
78 [[maybe_unused]] const QString &vbidev)
79{
80 LOG(VB_GENERAL, LOG_INFO, LOC + "SetOptionsFromProfile() -- begin"); //debugging
81
82 SetOption("videodevice", videodev);
83 SetOption("vbidevice", vbidev);
84 SetOption("audiodevice", audiodev);
85
86 SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
87 SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
88
89 SetIntOption(profile, "mpeg2bitratemode");
90 SetIntOption(profile, "mpeg2bitrate");
91 SetIntOption(profile, "mpeg2maxbitrate");
92 SetStrOption(profile, "mpeg2streamtype");
93 SetStrOption(profile, "mpeg2aspectratio");
94 SetStrOption(profile, "mpeg2language");
95
96 SetIntOption(profile, "samplerate");
97 SetStrOption(profile, "mpeg2audtype");
98 SetIntOption(profile, "audbitratemode");
99 SetIntOption(profile, "mpeg2audbitratel1");
100 SetIntOption(profile, "mpeg2audbitratel2");
101 SetIntOption(profile, "mpeg2audbitratel3");
102 SetIntOption(profile, "mpeg2audvolume");
103
104 SetIntOption(profile, "width");
105 SetIntOption(profile, "height");
106
107 SetIntOption(profile, "low_mpegbitratemode");
108 SetIntOption(profile, "low_mpegavgbitrate");
109 SetIntOption(profile, "low_mpegpeakbitrate");
110 SetIntOption(profile, "medium_mpegbitratemode");
111 SetIntOption(profile, "medium_mpegavgbitrate");
112 SetIntOption(profile, "medium_mpegpeakbitrate");
113 SetIntOption(profile, "high_mpegbitratemode");
114 SetIntOption(profile, "high_mpegavgbitrate");
115 SetIntOption(profile, "high_mpegpeakbitrate");
116
117 SetStrOption(profile, "audiocodec");
118
119 LOG(VB_GENERAL, LOG_INFO, LOC + "SetOptionsFromProfile -- end"); // debugging
120}
121
123{
124 LOG(VB_RECORD, LOG_INFO, LOC + "StartNewFile -- begin"); // debugging
125 // Make sure the first things in the file are a PAT & PMT
128 LOG(VB_RECORD, LOG_INFO, LOC + "StartNewFile -- end"); // debugging
129}
130
131
133{
134 LOG(VB_RECORD, LOG_INFO, LOC + "run() -- begin");
135
136 bool is_TS = false;
137
138 if (!m_streamData)
139 {
140 m_error = "MPEGStreamData pointer has not been set";
141 LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- " + m_error);
142 Close();
143 return;
144 }
145
147
148 {
149 QMutexLocker locker(&m_pauseLock);
150 m_requestRecording = true;
151 m_recording = true;
152 m_recordingWait.wakeAll();
153 }
154
156 {
161 m_streamData->HandleTables(pat->ProgramPID(0), *pmt);
162 LOG(VB_GENERAL, LOG_INFO, LOC + "PMT set"); // debugging
163 }
164
165 StartNewFile();
166 is_TS = (m_streamHandler->GetStreamType() == V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
167
168 if (is_TS)
169 {
170 LOG(VB_RECORD, LOG_INFO, LOC + "mpeg2ts");
173 }
174 else
175 {
176 LOG(VB_RECORD, LOG_INFO, LOC + "program stream (non mpeg2ts)");
178 }
179
181
183
184 while (IsRecordingRequested() && !IsErrored())
185 {
186 if (PauseAndWait())
187 continue;
188
189 if (is_TS && !m_inputPmt)
190 {
191 LOG(VB_GENERAL, LOG_WARNING, LOC +
192 "Recording will not commence until a PMT is set.");
193 std::this_thread::sleep_for(5ms);
194 continue;
195 }
196
197 bool failing = false;
198 bool failed = false;
199 if (!m_streamHandler->Status(failed, failing))
200 {
201 if (failed)
202 {
203 m_error = "Stream handler died unexpectedly.";
204 LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- " + m_error);
205 }
206 else if (failing)
207 {
208 SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
209 }
210 }
211 }
212 LOG(VB_RECORD, LOG_INFO, LOC + "Shutting down");
213
214 StopEncoding();
215
217 if (m_streamHandler->GetStreamType() == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
218 {
221 }
222 else
223 {
225 }
226
227 Close();
228
230
231 QMutexLocker locker(&m_pauseLock);
232 m_recording = false;
233 m_recordingWait.wakeAll();
234
235 LOG(VB_RECORD, LOG_INFO, LOC + "run() -- end");
236}
237
239{
240 LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- begin");
241
242 if (IsOpen())
243 {
244 LOG(VB_RECORD, LOG_WARNING, LOC + "Open() -- Card already open");
245 return true;
246 }
247
248// ?? ResetForNewFile();
249
251 m_channel->GetAudioDevice().toInt(),
252 m_tvrec ? m_tvrec->GetInputId() : -1);
253 if (!m_streamHandler)
254 {
255 LOG(VB_GENERAL, LOG_ERR, LOC +
256 "Open() -- Failed to get a stream handler.");
257 return false;
258 }
259
260 if (!m_streamHandler->IsOpen())
261 {
262 LOG(VB_GENERAL, LOG_ERR, LOC +
263 QString("Open() -- Failed to open recorder: %1")
266 m_tvrec ? m_tvrec->GetInputId() : -1);
267 return false;
268 }
269
270 m_useIForKeyframe = false;
271
272 LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- Success.");
273 return true;
274}
275
277{
278 LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- begin");
279
280 if (IsOpen())
282 m_tvrec ? m_tvrec->GetInputId() : -1);
283
284
285 LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
286}
287
288bool V4L2encRecorder::PauseAndWait(std::chrono::milliseconds timeout)
289{
290 QMutexLocker locker(&m_pauseLock);
291 if (m_requestPause)
292 {
293 if (!IsPaused(true))
294 {
295 LOG(VB_RECORD, LOG_INFO, LOC + "PauseAndWait() -- pause");
296
297 StopEncoding();
298
299 m_paused = true;
300 m_pauseWait.wakeAll();
301
302 if (m_tvrec)
304 }
305 }
306 else if (IsPaused(true))
307 {
308 LOG(VB_RECORD, LOG_INFO, LOC + "PauseAndWait() -- unpause");
310
311 if (m_streamData)
313
314 m_paused = false;
315 }
316
317 // Always wait a little bit, unless woken up
318 m_unpauseWait.wait(&m_pauseLock, timeout.count());
319
320 return IsPaused(true);
321}
322
324{
325 LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StartEncoding() -- begin");
326 if (m_h2645Parser != nullptr)
329 m_seenSps = false;
330
331 LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StartEncoding() -- end");
333}
334
336{
337 LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StopEncoding()");
339}
bool HasGeneratedPAT(void) const
Definition: dtvchannel.h:135
const ProgramMapTable * GetGeneratedPMT(void) const
Definition: dtvchannel.h:138
const ProgramAssociationTable * GetGeneratedPAT(void) const
Definition: dtvchannel.h:137
bool m_seenSps
Definition: dtvrecorder.h:151
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:161
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
ProgramMapTable * m_inputPmt
PMT on input side.
Definition: dtvrecorder.h:174
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:46
H2645Parser * m_h2645Parser
Definition: dtvrecorder.h:152
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:155
MPEGStreamData * m_streamData
Definition: dtvrecorder.h:163
bool m_useIForKeyframe
Definition: dtvrecorder.h:215
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
virtual void Reset(void)
Definition: H2645Parser.cpp:92
void AddPSStreamListener(PSStreamListener *val)
const ProgramMapTable * PMTSingleProgram(void) const
void AddWritingListener(TSPacketListener *val)
int DesiredProgram(void) const
void RemoveWritingListener(TSPacketListener *val)
void RemovePSStreamListener(PSStreamListener *val)
const ProgramAssociationTable * PATSingleProgram(void) const
virtual void Reset(void)
void RemoveAVListener(TSPacketListenerAV *val)
virtual bool HandleTables(uint pid, const PSIPTable &psip)
Process PSIP packets.
void AddAVListener(TSPacketListenerAV *val)
QString GetSetting(const QString &key, const QString &defaultval="")
@ MPEG_PAT_PID
Definition: mpegtables.h:211
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:599
uint ProgramNumber(uint i) const
Definition: mpegtables.h:626
uint ProgramPID(uint i) const
Definition: mpegtables.h:629
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:676
QMutex m_pauseLock
Definition: recorderbase.h:313
bool m_requestPause
Definition: recorderbase.h:314
TVRec * m_tvrec
Definition: recorderbase.h:290
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)
QWaitCondition m_pauseWait
Definition: recorderbase.h:316
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:319
QWaitCondition m_unpauseWait
Definition: recorderbase.h:317
QWaitCondition m_recordingWait
Definition: recorderbase.h:322
virtual QString getValue(void) const
virtual void RemoveListener(MPEGStreamData *data)
virtual void AddListener(MPEGStreamData *data, bool allow_section_reader=false, bool needs_buffering=false, const QString &output_file=QString())
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:143
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2996
uint GetInputId(void) const
Returns the inputid.
Definition: tv_rec.h:234
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
V4L2encRecorder(TVRec *rec, V4LChannel *channel)
V4LChannel * m_channel
void SetStrOption(RecordingProfile *profile, const QString &name)
void SetIntOption(RecordingProfile *profile, const QString &name)
V4L2encStreamHandler * m_streamHandler
bool StartEncoding(void)
bool PauseAndWait(std::chrono::milliseconds timeout=500ms) override
If m_requestPause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
bool StopEncoding(void)
void StartNewFile(void) override
bool IsOpen(void) const
bool SetOption(const QString &opt, int value)
bool Status(bool &failed, bool &failing)
static void Return(V4L2encStreamHandler *&ref, int inputid)
QString ErrorString(void) const
static V4L2encStreamHandler * Get(const QString &devname, int audioinput, int inputid)
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:32
QString GetAudioDevice(void) const
Definition: v4lchannel.h:63
QString GetDevice(void) const override
Returns String representing device, useful for debugging.
Definition: v4lchannel.h:61
Abstract base class for Video4Linux based recorders.
Definition: v4lrecorder.h:17
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:56
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC