MythTV master
mythavtest.cpp
Go to the documentation of this file.
1#include <iostream>
2#include <unistd.h>
3#include <utility>
4
5#include <QtGlobal>
6#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
7#include <QtEnvironmentVariables>
8#endif
9#include <QApplication>
10#include <QDir>
11#include <QString>
12#include <QSurfaceFormat>
13#include <QTime>
14
15// libmyth*
16#include "libmyth/mythcontext.h"
17#include "libmythbase/compat.h"
24#include "libmythbase/mythversion.h"
25#include "libmythtv/dbcheck.h"
30#include "libmythtv/tv_play.h"
33
35
37{
38 public:
39 VideoPerformanceTest(QString filename, bool decodeno, bool onlydecode,
40 std::chrono::seconds runfor, bool deint, bool gpu)
41 : m_file(std::move(filename)),
42 m_noDecode(decodeno),
43 m_decodeOnly(onlydecode),
44 m_secondsToRun(std::clamp(runfor, 1s, 3600s)),
45 m_deinterlace(deint),
46 m_allowGpu(gpu)
47 {
48 }
49
51 {
52 delete m_ctx;
53 }
54
55 void Test(void)
56 {
57 MythMediaBuffer *rb = MythMediaBuffer::Create(m_file, false, true, 2s);
58 m_ctx = new PlayerContext("VideoPerformanceTest");
59 auto *mp = new MythPlayerUI(GetMythMainWindow(), nullptr, m_ctx, static_cast<PlayerFlags>(kAudioMuted | (m_allowGpu ? kDecodeAllowGPU: kNoFlags)));
60 mp->GetAudio()->SetAudioInfo("NULL", "NULL", 0, 0);
61 mp->GetAudio()->SetNoAudio();
63 m_ctx->SetPlayer(mp);
64 auto *pinfo = new ProgramInfo(m_file);
65 m_ctx->SetPlayingInfo(pinfo); // makes a copy
66 delete pinfo;
67
69 if (!mp->StartPlaying())
70 {
71 LOG(VB_GENERAL, LOG_ERR, "Failed to start playback.");
72 return;
73 }
74
75 MythVideoOutput *vo = mp->GetVideoOutput();
76 if (!vo)
77 {
78 LOG(VB_GENERAL, LOG_ERR, "No video output.");
79 return;
80 }
81
82 LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
83 LOG(VB_GENERAL, LOG_INFO, "Ensure Sync to VBlank is disabled.");
84 LOG(VB_GENERAL, LOG_INFO, "Otherwise rate will be limited to that of the display.");
85 LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
86 LOG(VB_GENERAL, LOG_INFO, QString("Starting video performance test for '%1'.")
87 .arg(m_file));
88 LOG(VB_GENERAL, LOG_INFO, QString("Test will run for %1 seconds.")
89 .arg(m_secondsToRun.count()));
90
91 if (m_noDecode)
92 LOG(VB_GENERAL, LOG_INFO, "No decode after startup - checking display performance");
93 else if (m_decodeOnly)
94 LOG(VB_GENERAL, LOG_INFO, "Decoding frames only - skipping display.");
95 DecoderBase* dec = mp->GetDecoder();
96 if (dec)
97 LOG(VB_GENERAL, LOG_INFO, QString("Using decoder: %1").arg(dec->GetCodecDecoderName()));
98
99 auto *jitter = new Jitterometer("Performance: ", static_cast<int>(mp->GetFrameRate()));
100
101 QTime start = QTime::currentTime();
102 MythVideoFrame *frame = nullptr;
103 while (true)
104 {
105 mp->ProcessCallbacks();
106 auto duration = std::chrono::milliseconds(start.msecsTo(QTime::currentTime()));
107 if (duration < 0ms || duration > m_secondsToRun)
108 {
109 LOG(VB_GENERAL, LOG_INFO, "Complete.");
110 break;
111 }
112
113 if (mp->IsErrored())
114 {
115 LOG(VB_GENERAL, LOG_ERR, "Playback error.");
116 break;
117 }
118
119 if (mp->GetEof() != kEofStateNone)
120 {
121 LOG(VB_GENERAL, LOG_INFO, "End of file.");
122 break;
123 }
124
125 if (!mp->PrebufferEnoughFrames())
126 continue;
127
128 mp->SetBuffering(false);
130 if ((m_noDecode && !frame) || !m_noDecode)
131 frame = vo->GetLastShownFrame();
132 mp->CheckAspectRatio(frame);
133
134 if (!m_decodeOnly)
135 {
137 vo->PrepareFrame(frame, scan);
138 vo->RenderFrame(frame, scan);
139 vo->EndFrame();
140
141 if (doubledeint && m_deinterlace)
142 {
143 doubledeint = frame->GetDoubleRateOption(DEINT_CPU);
145 if (doubledeint && !other)
148 vo->EndFrame();
149 }
150 }
151 if (!m_noDecode)
152 vo->DoneDisplayingFrame(frame);
153 jitter->RecordCycleTime();
154 }
155 LOG(VB_GENERAL, LOG_INFO, "-----------------------------------");
156 delete jitter;
157 }
158
159 private:
160 QString m_file;
161 bool m_noDecode {false};
162 bool m_decodeOnly {false};
163 std::chrono::seconds m_secondsToRun {1s};
164 bool m_deinterlace {false};
165 bool m_allowGpu {false};
167};
168
169int main(int argc, char *argv[])
170{
172 if (!cmdline.Parse(argc, argv))
173 {
176 }
177
178 if (cmdline.toBool("showhelp"))
179 {
181 return GENERIC_EXIT_OK;
182 }
183
184 if (cmdline.toBool("showversion"))
185 {
187 return GENERIC_EXIT_OK;
188 }
189
190 int swapinterval = 1;
191 if (cmdline.toBool("test"))
192 {
193 // try and disable sync to vblank on linux x11
194 qputenv("vblank_mode", "0"); // Intel and AMD
195 qputenv("__GL_SYNC_TO_VBLANK", "0"); // NVidia
196 // the default surface format has a swap interval of 1. This is used by
197 // the MythMainwindow widget that then drives vsync for all widgets/children
198 // (i.e. MythPainterWindow) and we cannot override it on some drivers. So
199 // force the default here.
200 swapinterval = 0;
201 }
202
204
205 QApplication a(argc, argv);
206 QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHAVTEST);
207
208 int retval = cmdline.ConfigureLogging();
209 if (retval != GENERIC_EXIT_OK)
210 return retval;
211
212 if (!cmdline.toString("geometry").isEmpty())
214
215 QString filename = "";
216 if (!cmdline.toString("infile").isEmpty())
217 filename = cmdline.toString("infile");
218 else if (!cmdline.GetArgs().empty())
219 filename = cmdline.GetArgs().at(0);
220
221 MythContext context {MYTH_BINARY_VERSION, true};
222 if (!context.Init())
223 {
224 LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting.");
226 }
227
229
230 QString themename = gCoreContext->GetSetting("Theme");
231 QString themedir = GetMythUI()->FindThemeDir(themename);
232 if (themedir.isEmpty())
233 {
234 QString msg = QString("Fatal Error: Couldn't find theme '%1'.")
235 .arg(themename);
236 LOG(VB_GENERAL, LOG_ERR, msg);
238 }
239
240#ifdef Q_OS_MACOS
241 // Mac OS X doesn't define the AudioOutputDevice setting
242#else
243 QString auddevice = gCoreContext->GetSetting("AudioOutputDevice");
244 if (auddevice.isEmpty())
245 {
246 LOG(VB_GENERAL, LOG_ERR, "Fatal Error: Audio not configured, you need "
247 "to run 'mythfrontend', not 'mythtv'.");
249 }
250#endif
251
252 MythMainWindow *mainWindow = GetMythMainWindow();
253 mainWindow->Init();
254
255 if (cmdline.toBool("test"))
256 {
257 std::chrono::seconds seconds = 5s;
258 if (!cmdline.toString("seconds").isEmpty())
259 seconds = std::chrono::seconds(cmdline.toInt("seconds"));
260 auto *test = new VideoPerformanceTest(filename,
261 cmdline.toBool("nodecode"),
262 cmdline.toBool("decodeonly"), seconds,
263 cmdline.toBool("deinterlace"),
264 cmdline.toBool("gpu"));
265 test->Test();
266 delete test;
267 }
268 else
269 {
270 TV::InitKeys();
271 setHttpProxy();
272
273 if (!UpgradeTVDatabaseSchema(false))
274 {
275 LOG(VB_GENERAL, LOG_ERR, "Fatal Error: Incorrect database schema.");
277 }
278
279 if (filename.isEmpty())
280 {
282 }
283 else
284 {
285 ProgramInfo pginfo(filename);
287 }
288 }
289
290 return GENERIC_EXIT_OK;
291}
292
293/* vim: set expandtab tabstop=4 shiftwidth=4: */
virtual QString GetCodecDecoderName(void) const =0
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
int toInt(const QString &key) const
Returns stored QVariant as an integer, falling to default if not provided.
virtual bool Parse(int argc, const char *const *argv)
Loop through argv and populate arguments with values.
void ApplySettingsOverride(void)
Apply all overrides to the global context.
int ConfigureLogging(const QString &mask="general", bool progress=false)
Read in logging options and initialize the logging interface.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
static void PrintVersion(void)
Print application version information.
QStringList GetArgs(void) const
Return list of additional values provided on the command line independent of any keyword.
void PrintHelp(void) const
Print command line option help.
Startup context for MythTV.
Definition: mythcontext.h:20
QString GetSetting(const QString &key, const QString &defaultval="")
static void ConfigureQtGUI(int SwapInterval, const MythCommandLineParser &CmdLine)
Shared static initialisation code for all MythTV GUI applications.
void Init(bool MayReInit=true)
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
static void ParseGeometryOverride(const QString &Geometry)
Parse an X11 style command line geometry string.
QString FindThemeDir(const QString &ThemeName, bool Fallback=true)
Returns the full path to the theme denoted by themename.
MythDeintType GetDoubleRateOption(MythDeintType Type, MythDeintType Override=DEINT_NONE) const
Definition: mythframe.cpp:453
virtual void StartDisplayingFrame()
Tell GetLastShownFrame() to return the next frame from the head of the queue of frames to display.
virtual void EndFrame()=0
virtual void PrepareFrame(MythVideoFrame *Frame, FrameScanType Scan=kScan_Ignore)=0
virtual MythVideoFrame * GetLastShownFrame()
Returns frame from the head of the ready to be displayed queue, if StartDisplayingFrame has been call...
virtual void DoneDisplayingFrame(MythVideoFrame *Frame)
Releases frame returned from GetLastShownFrame() onto the queue of frames ready for decoding onto.
virtual void RenderFrame(MythVideoFrame *Frame, FrameScanType)=0
void SetRingBuffer(MythMediaBuffer *Buffer)
void SetPlayingInfo(const ProgramInfo *info)
assign programinfo to the context
void SetPlayer(MythPlayer *newplayer)
Holds information on recordings and videos.
Definition: programinfo.h:74
static bool StartTV(ProgramInfo *TVRec, uint Flags, const ChannelInfoList &Selection=ChannelInfoList())
Start playback of media.
Definition: tv_play.cpp:290
static void InitKeys()
Definition: tv_play.cpp:497
std::chrono::seconds m_secondsToRun
Definition: mythavtest.cpp:163
PlayerContext * m_ctx
Definition: mythavtest.cpp:166
VideoPerformanceTest(QString filename, bool decodeno, bool onlydecode, std::chrono::seconds runfor, bool deint, bool gpu)
Definition: mythavtest.cpp:39
bool UpgradeTVDatabaseSchema(const bool upgradeAllowed, const bool upgradeIfNoUI, const bool informSystemd)
Called from outside dbcheck.cpp to update the schema.
Definition: dbcheck.cpp:362
@ kEofStateNone
Definition: decoderbase.h:69
@ GENERIC_EXIT_NO_MYTHCONTEXT
No MythContext available.
Definition: exitcodes.h:16
@ GENERIC_EXIT_DB_OUTOFDATE
Database needs upgrade.
Definition: exitcodes.h:19
@ GENERIC_EXIT_OK
Exited with no error.
Definition: exitcodes.h:13
@ GENERIC_EXIT_SETUP_ERROR
Incorrectly setup system.
Definition: exitcodes.h:24
@ GENERIC_EXIT_NO_THEME
No Theme available.
Definition: exitcodes.h:17
@ GENERIC_EXIT_INVALID_CMDLINE
Command line parse error.
Definition: exitcodes.h:18
static constexpr const char * MYTH_APPNAME_MYTHAVTEST
Definition: mythappname.h:16
int main(int argc, char *argv[])
Definition: mythavtest.cpp:169
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static QString themedir
Definition: mythdirs.cpp:27
MythDeintType
Definition: mythframe.h:67
@ DEINT_DRIVER
Definition: mythframe.h:74
@ DEINT_SHADER
Definition: mythframe.h:73
@ DEINT_CPU
Definition: mythframe.h:72
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
void setHttpProxy(void)
Get network proxy settings from OS, and use for [Q]Http[Comms].
PlayerFlags
Definition: mythplayer.h:64
@ kAudioMuted
Definition: mythplayer.h:73
@ kNoFlags
Definition: mythplayer.h:65
@ kDecodeAllowGPU
Definition: mythplayer.h:71
MythUIHelper * GetMythUI()
MythCommFlagCommandLineParser cmdline
def scan(profile, smoonURL, gate)
Definition: scan.py:54
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
@ kStartTVNoFlags
Definition: tv_play.h:115
FrameScanType
Definition: videoouttypes.h:95
@ kScan_Intr2ndField
Definition: videoouttypes.h:99
@ kScan_Interlaced
Definition: videoouttypes.h:98
@ kScan_Progressive