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_stream_handler->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_stream_handler->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 failing, failed;
137  bool is_TS = false;
138 
139  if (!m_stream_data)
140  {
141  m_error = "MPEGStreamData pointer has not been set";
142  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- " + m_error);
143  Close();
144  return;
145  }
146 
148 
149  {
150  QMutexLocker locker(&m_pauseLock);
151  m_request_recording = true;
152  m_recording = true;
153  m_recordingWait.wakeAll();
154  }
155 
156  if (m_channel->HasGeneratedPAT())
157  {
159  const ProgramMapTable *pmt = m_channel->GetGeneratedPMT();
162  m_stream_data->HandleTables(pat->ProgramPID(0), *pmt);
163  LOG(VB_GENERAL, LOG_INFO, LOC + "PMT set"); // debugging
164  }
165 
166  StartNewFile();
167  is_TS = (m_stream_handler->GetStreamType() == V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
168 
169  if (is_TS)
170  {
171  LOG(VB_RECORD, LOG_INFO, LOC + "mpeg2ts");
174  }
175  else
176  {
177  LOG(VB_RECORD, LOG_INFO, LOC + "program stream (non mpeg2ts)");
179  }
180 
182 
183  StartEncoding();
184 
185  while (IsRecordingRequested() && !IsErrored())
186  {
187  if (PauseAndWait())
188  continue;
189 
190  if (is_TS && !m_input_pmt)
191  {
192  LOG(VB_GENERAL, LOG_WARNING, LOC +
193  "Recording will not commence until a PMT is set.");
194  std::this_thread::sleep_for(std::chrono::milliseconds(5));
195  continue;
196  }
197 
198  if (!m_stream_handler->Status(failed, failing))
199  {
200  if (failed)
201  {
202  m_error = "Stream handler died unexpectedly.";
203  LOG(VB_GENERAL, LOG_ERR, LOC + "run() -- " + m_error);
204  }
205  else if (failing)
206  {
207  SetRecordingStatus(RecStatus::Failing, __FILE__, __LINE__);
208  }
209  }
210  }
211  LOG(VB_RECORD, LOG_INFO, LOC + "Shutting down");
212 
213  StopEncoding();
214 
216  if (m_stream_handler->GetStreamType() == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
217  {
220  }
221  else
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_stream_handler)
251  {
252  LOG(VB_GENERAL, LOG_ERR, LOC +
253  "Open() -- Failed to get a stream handler.");
254  return false;
255  }
256 
257  if (!m_stream_handler->IsOpen())
258  {
259  LOG(VB_GENERAL, LOG_ERR, LOC +
260  QString("Open() -- Failed to open recorder: %1")
261  .arg(m_stream_handler->ErrorString()));
263  m_tvrec ? m_tvrec->GetInputId() : -1);
264  return false;
265  }
266 
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 
286 {
287  QMutexLocker locker(&m_pauseLock);
288  if (m_request_pause)
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_stream_data)
310 
311  m_paused = false;
312  }
313 
314  // Always wait a little bit, unless woken up
316 
317  return IsPaused(true);
318 }
319 
321 {
322  LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StartEncoding() -- begin");
325  m_seen_sps = false;
326 
327  LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StartEncoding() -- end");
329 }
330 
332 {
333  LOG(VB_RECORD, LOG_DEBUG, LOC + "V4L2encRecorder::StopEncoding()");
335 }
H264Parser m_h264_parser
Definition: dtvrecorder.h:153
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:232
bool m_wait_for_keyframe_option
Wait for the a GOP/SEQ-start before sending data.
Definition: dtvrecorder.h:156
void use_I_forKeyframes(bool val)
Definition: H264Parser.h:189
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:607
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:48
int DesiredProgram(void) const
#define LOC
QString GetDevice(void) const override
Returns String representing device, useful for debugging.
Definition: v4lchannel.h:61
void SetIntOption(RecordingProfile *profile, const QString &name)
bool m_request_recording
True if API call has requested a recording be [re]started.
Definition: recorderbase.h:332
QString GetAudioDevice(void) const
Definition: v4lchannel.h:63
virtual QString getValue(void) const
virtual bool IsPaused(bool holding_lock=false) const
Returns true iff recorder is paused.
void Reset(void)
Definition: H264Parser.cpp:102
QWaitCondition m_unpauseWait
Definition: recorderbase.h:330
ProgramMapTable * m_input_pmt
PMT on input side.
Definition: dtvrecorder.h:175
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:141
virtual void AddListener(MPEGStreamData *data, bool allow_section_reader=false, bool needs_buffering=false, QString output_file=QString())
QString GetSetting(const QString &key, const QString &defaultval="")
MPEGStreamData * m_stream_data
Definition: dtvrecorder.h:164
void RemoveWritingListener(TSPacketListener *)
void RecorderPaused(void)
This is a callback, called by the "recorder" instance when it has actually paused.
Definition: tv_rec.cpp:2935
QString ErrorString(void) const
bool m_seen_sps
Definition: dtvrecorder.h:152
void AddAVListener(TSPacketListenerAV *)
virtual void SetRecordingStatus(RecStatus::Type status, const QString &file, int line)
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:32
The Program Association Table lists all the programs in a stream, and is always found on PID 0.
Definition: mpegtables.h:579
V4L2encStreamHandler * m_stream_handler
bool SetOption(const QString &opt, int value)
bool m_request_pause
Definition: recorderbase.h:327
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
TVRec * m_tvrec
Definition: recorderbase.h:303
const ProgramMapTable * PMTSingleProgram(void) const
V4LChannel * m_channel
void AddWritingListener(TSPacketListener *)
bool IsOpen(void) const
bool PauseAndWait(int timeout=500) override
If m_request_pause is true, sets pause and blocks up to timeout milliseconds or until unpaused,...
QString m_error
non-empty iff irrecoverable recording error detected
Definition: dtvrecorder.h:162
static void Return(V4L2encStreamHandler *&ref, int inputid)
void RemoveAVListener(TSPacketListenerAV *)
A PMT table maps a program described in the ProgramAssociationTable to various PID's which describe t...
Definition: mpegtables.h:656
const ProgramMapTable * GetGeneratedPMT(void) const
Definition: dtvchannel.h:140
void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert) override
void SetStrOption(RecordingProfile *profile, const QString &name)
uint ProgramPID(uint i) const
Definition: mpegtables.h:610
virtual bool IsRecordingRequested(void)
Tells us if StopRecording() has been called.