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