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 <sys/ioctl.h>
22 #include <chrono> // for milliseconds
23 #include <thread> // for sleep_for
24 
25 // Qt includes
26 #include <QString>
27 
28 // MythTV includes
29 #include "recordingprofile.h"
30 #include "v4l2encstreamhandler.h"
31 #include "v4l2encrecorder.h"
32 #include "v4lchannel.h"
33 #include "ringbuffer.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  const QString &audiodev,
75  const QString &vbidev)
76 {
77  LOG(VB_GENERAL, LOG_INFO, LOC + "SetOptionsFromProfile() -- begin"); //debugging
78 
79  (void)audiodev;
80  (void)vbidev;
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 
155  if (m_channel->HasGeneratedPAT())
156  {
158  const ProgramMapTable *pmt = m_channel->GetGeneratedPMT();
159  m_streamData->Reset(pat->ProgramNumber(0));
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 
182  StartEncoding();
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(std::chrono::milliseconds(5));
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
224 
225  Close();
226 
227  FinishRecording();
228 
229  QMutexLocker locker(&m_pauseLock);
230  m_recording = false;
231  m_recordingWait.wakeAll();
232 
233  LOG(VB_RECORD, LOG_INFO, LOC + "run() -- end");
234 }
235 
237 {
238  LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- begin");
239 
240  if (IsOpen())
241  {
242  LOG(VB_RECORD, LOG_WARNING, LOC + "Open() -- Card already open");
243  return true;
244  }
245 
246 // ?? ResetForNewFile();
247 
249  m_channel->GetAudioDevice().toInt(),
250  m_tvrec ? m_tvrec->GetInputId() : -1);
251  if (!m_streamHandler)
252  {
253  LOG(VB_GENERAL, LOG_ERR, LOC +
254  "Open() -- Failed to get a stream handler.");
255  return false;
256  }
257 
258  if (!m_streamHandler->IsOpen())
259  {
260  LOG(VB_GENERAL, LOG_ERR, LOC +
261  QString("Open() -- Failed to open recorder: %1")
262  .arg(m_streamHandler->ErrorString()));
264  m_tvrec ? m_tvrec->GetInputId() : -1);
265  return false;
266  }
267 
269 
270  LOG(VB_RECORD, LOG_INFO, LOC + "Open() -- Success.");
271  return true;
272 }
273 
275 {
276  LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- begin");
277 
278  if (IsOpen())
280  m_tvrec ? m_tvrec->GetInputId() : -1);
281 
282 
283  LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
284 }
285 
287 {
288  QMutexLocker locker(&m_pauseLock);
289  if (m_requestPause)
290  {
291  if (!IsPaused(true))
292  {
293  LOG(VB_RECORD, LOG_INFO, LOC + "PauseAndWait() -- pause");
294 
295  StopEncoding();
296 
297  m_paused = true;
298  m_pauseWait.wakeAll();
299 
300  if (m_tvrec)
302  }
303  }
304  else if (IsPaused(true))
305  {
306  LOG(VB_RECORD, LOG_INFO, LOC + "PauseAndWait() -- unpause");
307  StartEncoding();
308 
309  if (m_streamData)
311 
312  m_paused = false;
313  }
314 
315  // Always wait a little bit, unless woken up
317 
318  return IsPaused(true);
319 }
320 
322 {
323  LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StartEncoding() -- begin");
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 }
void AddWritingListener(TSPacketListener *val)
const ProgramAssociationTable * PATSingleProgram(void) const
virtual void Reset(void)
V4L2encRecorder(TVRec *rec, V4LChannel *channel)
void SetOption(const QString &name, const QString &value) override
Set an specific option.
Definition: v4lrecorder.cpp:59
static V4L2encStreamHandler * Get(const QString &devname, int audioinput, int inputid)
uint GetInputId(void)
Returns the inputid.
Definition: tv_rec.h:234
void use_I_forKeyframes(bool val)
Definition: H264Parser.h:188
bool HasGeneratedPAT(void) const
Definition: dtvchannel.h:137
void SetOptionsFromProfile(RecordingProfile *profile, const QString &videodev, const QString &audiodev, const QString &vbidev) override
Sets basic recorder options.
void AddPSStreamListener(PSStreamListener *val)
void StartNewFile(void) override
QMutex m_pauseLock
Definition: recorderbase.h:326
uint ProgramNumber(uint i) const
Definition: mpegtables.h:617
virtual void RemoveListener(MPEGStreamData *data)
void RemovePSStreamListener(PSStreamListener *val)
const ProgramAssociationTable * GetGeneratedPAT(void) const
Definition: dtvchannel.h:139
bool StartEncoding(void)
void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert) override
bool Status(bool &failed, bool &failing)
virtual bool HandleTables(uint pid, const PSIPTable &psip)
Process PSIP packets.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void run(void) override
run() starts the recording process, and does not exit until the recording is complete.
bool IsErrored(void) override
Tells us whether an unrecoverable error has been encountered.
Definition: dtvrecorder.h:47
int DesiredProgram(void) const
#define LOC
QString GetDevice(void) const override
Returns String representing device, useful for debugging.
Definition: v4lchannel.h:65
void SetIntOption(RecordingProfile *profile, const QString &name)
QString GetAudioDevice(void) const
Definition: v4lchannel.h:67
V4L2encStreamHandler * m_streamHandler
virtual QString getValue(void) const
bool m_seenSps
Definition: dtvrecorder.h:151
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
void Reset(void)
Definition: H264Parser.cpp:103
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
bool StopEncoding(void)
Abstract base class for Video4Linux based recorders.
Definition: v4lrecorder.h:24
void FinishRecording(void) override
Flushes the ringbuffer, and if this is not a live LiveTV recording saves the position map and filesiz...
QWaitCondition m_pauseWait
Definition: recorderbase.h:329
This is the coordinating class of the Recorder Subsystem.
Definition: tv_rec.h:142
virtual void AddListener(MPEGStreamData *data, bool allow_section_reader=false, bool needs_buffering=false, QString output_file=QString())
void RemoveAVListener(TSPacketListenerAV *val)
QString GetSetting(const QString &key, const QString &defaultval="")
bool m_requestRecording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2942
QString ErrorString(void) const
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
bool m_requestPause
Definition: recorderbase.h:327
QWaitCondition m_recordingWait
Definition: recorderbase.h:335
bool m_recording
True while recording is actually being performed.
Definition: recorderbase.h:334
Implements tuning for TV cards using the V4L driver API, both versions 1 and 2.
Definition: v4lchannel.h:35
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:589
bool SetOption(const QString &opt, int value)
void RemoveWritingListener(TSPacketListener *val)
void AddAVListener(TSPacketListenerAV *val)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
TVRec * m_tvrec
Definition: recorderbase.h:303
const ProgramMapTable * PMTSingleProgram(void) const
V4LChannel * m_channel
bool IsOpen(void) const
bool PauseAndWait(int timeout=500) override
If m_requestPause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
bool m_waitForKeyframeOption
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:155
MPEGStreamData * m_streamData
Definition: dtvrecorder.h:163
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:161
static void Return(V4L2encStreamHandler *&ref, int inputid)
ProgramMapTable * m_inputPmt
PMT on input side.
Definition: dtvrecorder.h:174
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:666
const ProgramMapTable * GetGeneratedPMT(void) const
Definition: dtvchannel.h:140
H264Parser m_h264Parser
Definition: dtvrecorder.h:152
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
void SetStrOption(RecordingProfile *profile, const QString &name)
uint ProgramPID(uint i) const
Definition: mpegtables.h:620
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.