MythTV  master
mythtv/programs/mythavtest/main.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 #include <iostream>
3 #include <utility>
4 
5 using namespace std;
6 
7 #include <QApplication>
8 #include <QDir>
9 #include <QRegExp>
10 #include <QString>
11 #include <QSurfaceFormat>
12 #include <QTime>
13 
14 #include "tv_play.h"
15 #include "programinfo.h"
16 #include "commandlineparser.h"
17 #include "mythplayer.h"
18 #include "jitterometer.h"
19 
20 #include "exitcodes.h"
21 #include "mythcontext.h"
22 #include "mythversion.h"
23 #include "mythdbcon.h"
24 #include "compat.h"
25 #include "dbcheck.h"
26 #include "mythlogging.h"
27 #include "signalhandling.h"
28 #include "mythmiscutil.h"
29 #include "mythvideoout.h"
30 
31 // libmythui
32 #include "mythuihelper.h"
33 #include "mythmainwindow.h"
34 
36 {
37  public:
38  VideoPerformanceTest(QString filename, bool decodeno, bool onlydecode,
39  int runfor, bool deint, bool gpu)
40  : m_file(std::move(filename)),
41  m_noDecode(decodeno),
42  m_decodeOnly(onlydecode),
43  m_secondsToRun(runfor),
44  m_deinterlace(deint),
45  m_allowGpu(gpu),
46  m_ctx(nullptr)
47  {
48  if (m_secondsToRun < 1)
49  m_secondsToRun = 1;
50  if (m_secondsToRun > 3600)
51  m_secondsToRun = 3600;
52  }
53 
55  {
56  delete m_ctx;
57  }
58 
59  void Test(void)
60  {
61  PIPMap dummy;
62  RingBuffer *rb = RingBuffer::Create(m_file, false, true, 2000);
63  auto *mp = new MythPlayer(
65  mp->GetAudio()->SetAudioInfo("NULL", "NULL", 0, 0);
66  mp->GetAudio()->SetNoAudio();
67  m_ctx = new PlayerContext("VideoPerformanceTest");
68  m_ctx->SetRingBuffer(rb);
69  m_ctx->SetPlayer(mp);
70  auto *pinfo = new ProgramInfo(m_file);
71  m_ctx->SetPlayingInfo(pinfo); // makes a copy
72  delete pinfo;
73  mp->SetPlayerInfo(nullptr, GetMythMainWindow(), m_ctx);
74 
76  if (!mp->StartPlaying())
77  {
78  LOG(VB_GENERAL, LOG_ERR, "Failed to start playback.");
79  return;
80  }
81 
82  MythVideoOutput *vo = mp->GetVideoOutput();
83  if (!vo)
84  {
85  LOG(VB_GENERAL, LOG_ERR, "No video output.");
86  return;
87  }
88 
89  LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
90  LOG(VB_GENERAL, LOG_INFO, "Ensure Sync to VBlank is disabled.");
91  LOG(VB_GENERAL, LOG_INFO, "Otherwise rate will be limited to that of the display.");
92  LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
93  LOG(VB_GENERAL, LOG_INFO, QString("Starting video performance test for '%1'.")
94  .arg(m_file));
95  LOG(VB_GENERAL, LOG_INFO, QString("Test will run for %1 seconds.")
96  .arg(m_secondsToRun));
97 
98  if (m_noDecode)
99  LOG(VB_GENERAL, LOG_INFO, "No decode after startup - checking display performance");
100  else if (m_decodeOnly)
101  LOG(VB_GENERAL, LOG_INFO, "Decoding frames only - skipping display.");
102  DecoderBase* dec = mp->GetDecoder();
103  if (dec)
104  LOG(VB_GENERAL, LOG_INFO, QString("Using decoder: %1").arg(dec->GetCodecDecoderName()));
105 
106  auto *jitter = new Jitterometer("Performance: ", static_cast<int>(mp->GetFrameRate()));
107 
108  int ms = m_secondsToRun * 1000;
109  QTime start = QTime::currentTime();
110  VideoFrame *frame = nullptr;
111  while (true)
112  {
113  QCoreApplication::processEvents();
114  int duration = start.msecsTo(QTime::currentTime());
115  if (duration < 0 || duration > ms)
116  {
117  LOG(VB_GENERAL, LOG_INFO, "Complete.");
118  break;
119  }
120 
121  if (mp->IsErrored())
122  {
123  LOG(VB_GENERAL, LOG_ERR, "Playback error.");
124  break;
125  }
126 
127  if (mp->GetEof() != kEofStateNone)
128  {
129  LOG(VB_GENERAL, LOG_INFO, "End of file.");
130  break;
131  }
132 
133  if (!mp->PrebufferEnoughFrames())
134  continue;
135 
136  mp->SetBuffering(false);
137  vo->StartDisplayingFrame();
138  if ((m_noDecode && !frame) || !m_noDecode)
139  frame = vo->GetLastShownFrame();
140  mp->CheckAspectRatio(frame);
141 
142  if (!m_decodeOnly)
143  {
145  vo->ProcessFrame(frame, nullptr, dummy, scan);
146  vo->PrepareFrame(frame, scan, nullptr);
147  vo->Show(scan);
148 
149  if (doubledeint && m_deinterlace)
150  {
151  doubledeint = GetDoubleRateOption(frame, DEINT_CPU);
153  if (doubledeint && !other)
154  vo->ProcessFrame(frame, nullptr, dummy, kScan_Intr2ndField);
155  vo->PrepareFrame(frame, kScan_Intr2ndField, nullptr);
156  vo->Show(scan);
157  }
158  }
159  if (!m_noDecode)
160  vo->DoneDisplayingFrame(frame);
161  jitter->RecordCycleTime();
162  }
163  LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
164  delete jitter;
165  }
166 
167  private:
168  QString m_file;
175 };
176 
177 int main(int argc, char *argv[])
178 {
179 
180 #if defined (Q_OS_LINUX)
181 #if defined (USING_VAAPI) || defined (USING_MMAL)
182  // When using VAAPI (linux/desktop only) we want to use EGL to ensure we
183  // can use zero copy video buffers for the best performance (N.B. not tested
184  // on AMD desktops). For non-VAAPI users this should make no difference - on NVidia
185  // installations it has no effect.
186  // Likewise for MMAL (Raspberry Pi), we want EGL for zero copy direct rendering.
187  // This is the only way to force Qt to use EGL and must be done before any
188  // GUI is created.
189  // If problems are encountered, set the environment variable NO_EGL
190 
191  // Disabled this for now as it does actually break NVidia desktops
192  //if (qgetenv("NO_EGL").isEmpty())
193  // setenv("QT_XCB_GL_INTEGRATION", "xcb_egl", 0);
194 
195 
196  // This makes Xlib calls thread-safe which seems to be required for hardware
197  // accelerated Flash playback to work without causing mythfrontend to abort.
198  QApplication::setAttribute(Qt::AA_X11InitThreads);
199 #endif
200 #endif
201 
203  if (!cmdline.Parse(argc, argv))
204  {
205  cmdline.PrintHelp();
207  }
208 
209  if (cmdline.toBool("showhelp"))
210  {
211  cmdline.PrintHelp();
212  return GENERIC_EXIT_OK;
213  }
214 
215  if (cmdline.toBool("showversion"))
216  {
218  return GENERIC_EXIT_OK;
219  }
220 
221  QSurfaceFormat format;
222  format.setDepthBufferSize(0);
223  format.setStencilBufferSize(0);
224  format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
225  format.setProfile(QSurfaceFormat::CoreProfile);
226  format.setSwapInterval(1);
227 
228  // try and disable vsync if running test
229  if (cmdline.toBool("test"))
230  {
231  // try and disable sync to vblank on linux x11
232  qputenv("vblank_mode", "0"); // Intel and AMD
233  qputenv("__GL_SYNC_TO_VBLANK", "0"); // NVidia
234 
235  // the default surface format has a swap interval of 1. This is used by
236  // the MythMainwindow widget that then drives vsync for all widgets/children
237  // (i.e. MythPainterWindow) and we cannot override it on some drivers. So
238  // force the default here.
239  format.setSwapInterval(0);
240  }
241 
242  QSurfaceFormat::setDefaultFormat(format);
243 
244  QApplication a(argc, argv);
245  QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHAVTEST);
246 
247  int retval = cmdline.ConfigureLogging();
248  if (retval != GENERIC_EXIT_OK)
249  return retval;
250 
251  if (!cmdline.toString("display").isEmpty())
252  {
254  }
255 
256  if (!cmdline.toString("geometry").isEmpty())
257  {
259  }
260 
261  QString filename = "";
262  if (!cmdline.toString("infile").isEmpty())
263  filename = cmdline.toString("infile");
264  else if (!cmdline.GetArgs().empty())
265  filename = cmdline.GetArgs()[0];
266 
268  if (!gContext->Init())
269  {
270  LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting.");
272  }
273 
275 
276  QString themename = gCoreContext->GetSetting("Theme");
277  QString themedir = GetMythUI()->FindThemeDir(themename);
278  if (themedir.isEmpty())
279  {
280  QString msg = QString("Fatal Error: Couldn't find theme '%1'.")
281  .arg(themename);
282  LOG(VB_GENERAL, LOG_ERR, msg);
283  return GENERIC_EXIT_NO_THEME;
284  }
285 
286  GetMythUI()->LoadQtConfig();
287 
288 #if defined(Q_OS_MACX)
289  // Mac OS X doesn't define the AudioOutputDevice setting
290 #else
291  QString auddevice = gCoreContext->GetSetting("AudioOutputDevice");
292  if (auddevice.isEmpty())
293  {
294  LOG(VB_GENERAL, LOG_ERR, "Fatal Error: Audio not configured, you need "
295  "to run 'mythfrontend', not 'mythtv'.");
297  }
298 #endif
299 
300  MythMainWindow *mainWindow = GetMythMainWindow();
301 #if CONFIG_DARWIN
302  mainWindow->Init(OPENGL2_PAINTER);
303 #else
304  mainWindow->Init();
305 #endif
306 
307 #ifndef _WIN32
308  QList<int> signallist;
309  signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT << SIGBUS << SIGFPE
310  << SIGILL;
311 #if ! CONFIG_DARWIN
312  signallist << SIGRTMIN;
313 #endif
314  SignalHandler::Init(signallist);
315  signal(SIGHUP, SIG_IGN);
316 #endif
317 
318  if (cmdline.toBool("test"))
319  {
320  int seconds = 5;
321  if (!cmdline.toString("seconds").isEmpty())
322  seconds = cmdline.toInt("seconds");
323  auto *test = new VideoPerformanceTest(filename,
324  cmdline.toBool("nodecode"),
325  cmdline.toBool("decodeonly"), seconds,
326  cmdline.toBool("deinterlace"),
327  cmdline.toBool("gpu"));
328  test->Test();
329  delete test;
330  }
331  else
332  {
333  TV::InitKeys();
334  setHttpProxy();
335 
336  if (!UpgradeTVDatabaseSchema(false))
337  {
338  LOG(VB_GENERAL, LOG_ERR, "Fatal Error: Incorrect database schema.");
339  delete gContext;
341  }
342 
343  if (filename.isEmpty())
344  {
345  TV::StartTV(nullptr, kStartTVNoFlags);
346  }
347  else
348  {
349  ProgramInfo pginfo(filename);
350  TV::StartTV(&pginfo, kStartTVNoFlags);
351  }
352  }
354 
355  delete gContext;
356 
358 
359  return GENERIC_EXIT_OK;
360 }
361 
362 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Startup context for MythTV.
Definition: mythcontext.h:42
QMap< MythPlayer *, PIPLocation > PIPMap
Definition: mythvideoout.h:32
QString FindThemeDir(const QString &themename, bool doFallback=true)
Returns the full path to the theme denoted by themename.
bool UpgradeTVDatabaseSchema(const bool upgradeAllowed, const bool upgradeIfNoUI, const bool informSystemd)
Called from outside dbcheck.cpp to update the schema.
def scan(profile, smoonURL, gate)
Definition: scan.py:57
MythDeintType GetDoubleRateOption(const VideoFrame *Frame, MythDeintType Type, MythDeintType Override)
Definition: mythframe.cpp:853
static QString themedir
Definition: mythdirs.cpp:21
PlayerFlags
Definition: mythplayer.h:86
void LoadQtConfig(void)
virtual void Show(FrameScanType)=0
#define GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:10
void setHttpProxy(void)
Get network proxy settings from OS, and use for [Q]Http[Comms].
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
static void PrintVersion(void)
Print application version information.
virtual void StartDisplayingFrame(void)
Tell GetLastShownFrame() to return the next frame from the head of the queue of frames to display.
void PrintHelp(void) const
Print command line option help.
static void Init(QList< int > &signallist, QObject *parent=nullptr)
void DestroyMythMainWindow(void)
FrameScanType
Definition: videoouttypes.h:80
virtual void PrepareFrame(VideoFrame *Frame, FrameScanType, OSD *Osd)=0
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static RingBuffer * Create(const QString &xfilename, bool write, bool usereadahead=true, int timeout_ms=kDefaultOpenTimeout, bool stream_only=false)
Creates a RingBuffer instance.
Definition: ringbuffer.cpp:104
MythContext * gContext
This global variable contains the MythContext instance for the application.
Definition: mythcontext.cpp:62
virtual void ProcessFrame(VideoFrame *Frame, OSD *Osd, const PIPMap &PipPlayers, FrameScanType Scan=kScan_Ignore)=0
void Init(const QString &forcedpainter=QString(), bool mayReInit=true)
int main(int argc, char *argv[])
MythDeintType
Definition: mythframe.h:120
static void SetX11Display(const QString &display)
This needs to be set before MythUIHelper is initialized so that the MythUIHelper::Init() can detect X...
#define OPENGL2_PAINTER
Definition: mythuidefines.h:4
void ApplySettingsOverride(void)
Apply all overrides to the global context.
Holds information on recordings and videos.
Definition: programinfo.h:66
QStringList GetArgs(void) const
Return list of additional values provided on the command line independent of any keyword.
QString GetSetting(const QString &key, const QString &defaultval="")
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
static void ParseGeometryOverride(const QString &geometry)
Parse an X11 style command line geometry string.
virtual QString GetCodecDecoderName(void) const =0
static void Done(void)
MythCommFlagCommandLineParser cmdline
MythUIHelper * GetMythUI()
MythMainWindow * GetMythMainWindow(void)
static bool StartTV(ProgramInfo *tvrec, uint flags, const ChannelInfoList &selection=ChannelInfoList())
returns true if the recording completed when exiting.
Definition: tv_play.cpp:289
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
#define MYTH_APPNAME_MYTHAVTEST
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
#define SIGHUP
Definition: compat.h:213
#define GENERIC_EXIT_SETUP_ERROR
Incorrectly setup system.
Definition: exitcodes.h:21
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:16
bool Init(const bool gui=true, const bool promptForBackend=false, const bool disableAutoDiscovery=false, const bool ignoreDB=false)
virtual VideoFrame * GetLastShownFrame(void)
Returns frame from the head of the ready to be displayed queue, if StartDisplayingFrame has been call...
Implements a file/stream reader/writer.
#define GENERIC_EXIT_NO_THEME
No Theme available.
Definition: exitcodes.h:14
#define GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:13
#define GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:15
VideoPerformanceTest(QString filename, bool decodeno, bool onlydecode, int runfor, bool deint, bool gpu)
static void InitKeys(void)
Definition: tv_play.cpp:503
virtual void DoneDisplayingFrame(VideoFrame *Frame)
Releases frame returned from GetLastShownFrame() onto the queue of frames ready for decoding onto.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
int ConfigureLogging(const QString &mask="general", unsigned int progress=0)
Read in logging options and initialize the logging interface.
#define GENERIC_EXIT_DB_OUTOFDATE
Database needs upgrade.
Definition: exitcodes.h:16