MythTV  master
mhi.cpp
Go to the documentation of this file.
1 #include "mhi.h"
2 
3 #include <QRegion>
4 #include <QVector>
5 #include <QUrl>
6 #include <QPoint> // for QPoint
7 #include <QRgb> // for QRgb
8 #include <QVariant> // for QVariant
9 #include <QByteArray> // for QByteArray
10 #include <QStringList> // for QStringList
11 #include <QTime> // for QTime
12 #include <QHash> // for QHash
13 
14 #include <algorithm> // for min
15 #include <cmath> // for round, sqrt
16 #include <cstdint> // for uint8_t
17 #include <cstring> // for memcpy, memset
18 #include <deque> // for _Deque_iterator, operator!=
19 
20 #include "libmythbase/mthread.h" // for MThread
21 #include "libmythbase/mythcorecontext.h"// for MythCoreContext, etc
22 #include "libmythbase/mythdb.h" // for MythDB
23 #include "libmythbase/mythdbcon.h" // for MSqlQuery
24 #include "libmythbase/mythdirs.h"
25 #include "libmythbase/mythevent.h" // for MythEvent
27 #include "libmythui/mythimage.h"
29 #include "libmythui/mythpainter.h"
30 #include "libmythui/mythrect.h" // for MythRect
31 #include "libmythui/mythuiactions.h" // for ACTION_0, ACTION_1, etc
32 #include "libmythui/mythuiimage.h"
33 
34 #include "dsmcc.h" // for Dsmcc
35 #include "interactivescreen.h"
36 #include "interactivetv.h" // for InteractiveTV
37 #include "mythavutil.h"
38 #include "mythplayerui.h"
39 #include "tv_actions.h" // for ACTION_MENUTEXT, etc
40 
41 extern "C" {
42 #include "libavutil/imgutils.h"
43 }
44 
45 static bool ft_loaded = false;
46 static FT_Library ft_library;
47 
48 static constexpr uint8_t FONT_WIDTHRES { 54 };
49 static constexpr uint8_t FONT_HEIGHTRES { 72 }; // 1 pixel per point
50 static constexpr const char * FONT_TO_USE { "FreeSans.ttf" }; // Tiresias Screenfont.ttf is mandated
51 
52 
53 // LifecycleExtension tuneinfo:
54 const unsigned kTuneQuietly = 1U<<0; // b0 tune quietly
55 const unsigned kTuneKeepApp = 1U<<1; // b1 keep app running
56 const unsigned kTuneCarId = 1U<<2; // b2 carousel id in bits 8..16
57 const unsigned kTuneCarReset = 1U<<3; // b3 get carousel id from gateway info
58 //const unsigned kTuneBcastDisa = 1U<<4; // b4 broadcaster_interrupt disable
59 // b5..7 reserverd
60 // b8..15 carousel id
61 const unsigned kTuneKeepChnl = 1U<<16; // Keep current channel
62 // b17..31 reserved
63 
68 {
69  public:
70  QImage m_image;
71  int m_x {0};
72  int m_y {0};
73  bool m_bUnder {false};
74 };
75 
77  : m_parent(parent), m_dsmcc(new Dsmcc()),
78  m_engine(MHCreateEngine(this))
79 {
80  if (!ft_loaded)
81  {
82  FT_Error error = FT_Init_FreeType(&ft_library);
83  if (!error)
84  ft_loaded = true;
85  }
86 
87  if (ft_loaded)
88  {
89  // TODO: We need bold and italic versions.
90  if (LoadFont(FONT_TO_USE))
91  m_faceLoaded = true;
92  }
93 }
94 
95 // Load the font. Copied, generally, from OSD::LoadFont.
96 bool MHIContext::LoadFont(const QString& name)
97 {
98  QString fullnameA = GetConfDir() + "/" + name;
99  QByteArray fnameA = fullnameA.toLatin1();
100  FT_Error errorA = FT_New_Face(ft_library, fnameA.constData(), 0, &m_face);
101  if (!errorA)
102  return true;
103 
104  QString fullnameB = GetFontsDir() + name;
105  QByteArray fnameB = fullnameB.toLatin1();
106  FT_Error errorB = FT_New_Face(ft_library, fnameB.constData(), 0, &m_face);
107  if (!errorB)
108  return true;
109 
110  QString fullnameC = GetShareDir() + "themes/" + name;
111  QByteArray fnameC = fullnameC.toLatin1();
112  FT_Error errorC = FT_New_Face(ft_library, fnameC.constData(), 0, &m_face);
113  if (!errorC)
114  return true;
115 
116  const QString& fullnameD = name;
117  QByteArray fnameD = fullnameD.toLatin1();
118  FT_Error errorD = FT_New_Face(ft_library, fnameD.constData(), 0, &m_face);
119  if (!errorD)
120  return true;
121 
122  LOG(VB_GENERAL, LOG_ERR, QString("[mhi] Unable to find font: %1").arg(name));
123  return false;
124 }
125 
127 {
128  StopEngine();
129  delete(m_engine);
130  delete(m_dsmcc);
131  if (m_faceLoaded) FT_Done_Face(m_face);
132 
133  ClearDisplay();
134  ClearQueue();
135 }
136 
137 // NB caller must hold m_display_lock
139 {
140  for (auto & it : m_display)
141  delete it;
142  m_display.clear();
143  m_videoDisplayRect = QRect();
144 }
145 
146 // NB caller must hold m_dsmccLock
148 {
149  for (auto & it : m_dsmccQueue)
150  delete it;
151  m_dsmccQueue.clear();
152 }
153 
154 // Ask the engine to stop and block until it has.
156 {
157  if (nullptr == m_engineThread)
158  return;
159 
160  m_stop = true;
161  m_runLock.lock();
162  m_engineWait.wakeAll();
163  m_runLock.unlock();
164 
165  m_engineThread->wait();
166  delete m_engineThread;
167  m_engineThread = nullptr;
168 }
169 
170 
171 // Start or restart the MHEG engine.
172 void MHIContext::Restart(int chanid, int sourceid, bool isLive)
173 {
174  int tuneinfo = m_tuneInfo.isEmpty() ? 0 : m_tuneInfo.takeFirst();
175 
176  LOG(VB_MHEG, LOG_INFO,
177  QString("[mhi] Restart ch=%1 source=%2 live=%3 tuneinfo=0x%4")
178  .arg(chanid).arg(sourceid).arg(isLive).arg(tuneinfo,0,16));
179 
180  if (m_currentSource != sourceid)
181  {
182  m_currentSource = sourceid;
183  QMutexLocker locker(&m_channelMutex);
184  m_channelCache.clear();
185  }
186  m_currentStream = (chanid) ? chanid : -1;
187  if (!(tuneinfo & kTuneKeepChnl))
189 
190  if (tuneinfo & kTuneKeepApp)
191  {
192  // We have tuned to the channel in order to find the streams.
193  // Leave the MHEG engine running but restart the DSMCC carousel.
194  // This is a bit of a mess but it's the only way to be able to
195  // select streams from a different channel.
196  {
197  QMutexLocker locker(&m_dsmccLock);
198  if (tuneinfo & kTuneCarReset)
199  m_dsmcc->Reset();
200  ClearQueue();
201  }
202 
203  if (tuneinfo & (kTuneCarReset|kTuneCarId))
204  {
205  QMutexLocker locker(&m_runLock);
206  m_engine->EngineEvent(10); // NonDestructiveTuneOK
207  }
208  }
209  else
210  {
211  StopEngine();
212 
213  m_audioTag = -1;
214  m_videoTag = -1;
215 
216  {
217  QMutexLocker locker(&m_dsmccLock);
218  m_dsmcc->Reset();
219  ClearQueue();
220  }
221 
222  {
223  QMutexLocker locker(&m_keyLock);
224  m_keyQueue.clear();
225  }
226 
227  m_engine->SetBooting();
228  ClearDisplay();
229  m_updated = true;
230  m_stop = false;
231  m_isLive = isLive;
232  // Don't set the NBI version here. Restart is called
233  // after the PMT is processed.
234  m_engineThread = new MThread("MHEG", this);
236  }
237 }
238 
239 void MHIContext::run(void)
240 {
241  QMutexLocker locker(&m_runLock);
242 
243  while (!m_stop)
244  {
245  std::chrono::milliseconds toWait = 0ms;
246  // Dequeue and process any key presses.
247  int key = 0;
248  do
249  {
252  {
253  QMutexLocker locker2(&m_keyLock);
254  key = m_keyQueue.dequeue();
255  }
256 
257  if (key != 0)
259 
260  // Run the engine and find out how long to pause.
261  toWait = m_engine->RunAll();
262  if (toWait < 0ms)
263  return;
264  } while (key != 0);
265 
266  toWait = (toWait > 1s || toWait <= 0ms) ? 1s : toWait;
267 
268  if (!m_stop && (toWait > 0ms))
269  m_engineWait.wait(locker.mutex(), toWait.count());
270  }
271 }
272 
273 // Dequeue and process any DSMCC packets.
275 {
276  DSMCCPacket *packet = nullptr;
277  do
278  {
279  QMutexLocker locker(&m_dsmccLock);
280  packet = m_dsmccQueue.dequeue();
281  if (packet)
282  {
284  packet->m_data, packet->m_length,
285  packet->m_componentTag, packet->m_carouselId,
286  packet->m_dataBroadcastId);
287 
288  delete packet;
289  }
290  } while (packet);
291 }
292 
294  unsigned char *data, int length, int componentTag,
295  unsigned carouselId, int dataBroadcastId)
296 {
297  auto *dataCopy = (unsigned char*) malloc(length * sizeof(unsigned char));
298 
299  if (dataCopy == nullptr)
300  return;
301 
302  memcpy(dataCopy, data, length*sizeof(unsigned char));
303  {
304  QMutexLocker locker(&m_dsmccLock);
305  m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy, length,
306  componentTag, carouselId,
307  dataBroadcastId));
308  }
309  m_engineWait.wakeAll();
310 }
311 
312 // A NetworkBootInfo sub-descriptor is present in the PMT.
313 void MHIContext::SetNetBootInfo(const unsigned char *data, uint length)
314 {
315  if (length < 2) // A valid descriptor should always have at least 2 bytes.
316  return;
317 
318  LOG(VB_MHEG, LOG_INFO, QString("[mhi] SetNetBootInfo version %1 mode %2 len %3")
319  .arg(data[0]).arg(data[1]).arg(length));
320 
321  QMutexLocker locker(&m_dsmccLock);
322  // The carousel should be reset now as the stream has changed
323  m_dsmcc->Reset();
324  ClearQueue();
325  // Save the data from the descriptor.
326  m_nbiData.resize(0);
327  m_nbiData.reserve(length);
328  m_nbiData.insert(m_nbiData.begin(), data, data+length);
329  // If there is no Network Boot Info or we're setting it
330  // for the first time just update the "last version".
332  m_lastNbiVersion = data[0];
333  else
334  m_engineWait.wakeAll();
335 }
336 
337 // Called only by m_engineThread
339 {
340  QMutexLocker locker(&m_dsmccLock);
341  if (m_nbiData.size() >= 2 && m_nbiData[0] != m_lastNbiVersion)
342  {
343  m_lastNbiVersion = m_nbiData[0]; // Update the saved version
344  switch (m_nbiData[1])
345  {
346  case 1:
347  m_dsmcc->Reset();
348  m_engine->SetBooting();
349  locker.unlock();
350  {QMutexLocker locker2(&m_displayLock);
351  ClearDisplay();
352  m_updated = true;}
353  break;
354  case 2:
355  m_engine->EngineEvent(9); // NetworkBootInfo EngineEvent
356  break;
357  default:
358  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Unknown NetworkBoot type %1")
359  .arg(m_nbiData[1]));
360  break;
361  }
362  }
363 }
364 
365 // Called by the engine to check for the presence of an object in the carousel.
366 bool MHIContext::CheckCarouselObject(const QString& objectPath)
367 {
368  if (objectPath.startsWith("http:") || objectPath.startsWith("https:"))
369  {
370  QByteArray cert;
371 
372  // Verify access to server
373  if (!CheckAccess(objectPath, cert))
374  return false;
375 
376  return m_ic.CheckFile(objectPath, cert);
377  }
378 
379 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
380  QStringList path = objectPath.split(QChar('/'), QString::SkipEmptyParts);
381 #else
382  QStringList path = objectPath.split(QChar('/'), Qt::SkipEmptyParts);
383 #endif
384  QByteArray result; // Unused
385  QMutexLocker locker(&m_dsmccLock);
386  int res = m_dsmcc->GetDSMCCObject(path, result);
387  return res == 0; // It's available now.
388 }
389 
390 bool MHIContext::GetDSMCCObject(const QString &objectPath, QByteArray &result)
391 {
392 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
393  QStringList path = objectPath.split(QChar('/'), QString::SkipEmptyParts);
394 #else
395  QStringList path = objectPath.split(QChar('/'), Qt::SkipEmptyParts);
396 #endif
397  QMutexLocker locker(&m_dsmccLock);
398  int res = m_dsmcc->GetDSMCCObject(path, result);
399  return (res == 0);
400 }
401 
402 bool MHIContext::CheckAccess(const QString &objectPath, QByteArray &cert)
403 {
404  cert.clear();
405 
406  // Verify access to server
407  QByteArray servers;
408  if (!GetDSMCCObject("/auth.servers", servers))
409  {
410  LOG(VB_MHEG, LOG_INFO, QString(
411  "[mhi] CheckAccess(%1) No auth.servers").arg(objectPath) );
412  return false;
413  }
414 
415  QByteArray host = QUrl(objectPath).host().toLocal8Bit();
416  if (!servers.contains(host))
417  {
418  LOG(VB_MHEG, LOG_INFO, QString("[mhi] CheckAccess(%1) Host not known")
419  .arg(objectPath) );
420  LOG(VB_MHEG, LOG_DEBUG, QString("[mhi] Permitted servers: %1")
421  .arg(servers.constData()) );
422 
423  // BUG: https://securegate.iplayer.bbc.co.uk is not listed
424  if (!objectPath.startsWith("https:"))
425  return false;
426  }
427 
428  if (!objectPath.startsWith("https:"))
429  return true;
430 
431  // Use TLS cert from carousel file auth.tls.<x>
432  if (!GetDSMCCObject("/auth.tls.1", cert))
433  return false;
434 
435  // The cert has a 5 byte header: 16b cert_count + 24b cert_len
436  cert = cert.mid(5);
437  return true;
438 }
439 
440 // Called by the engine to request data from the carousel.
441 // Caller must hold m_runLock
442 bool MHIContext::GetCarouselData(const QString& objectPath, QByteArray &result)
443 {
444  QByteArray cert;
445  bool const isIC = objectPath.startsWith("http:") || objectPath.startsWith("https:");
446  if (isIC)
447  {
448  // Verify access to server
449  if (!CheckAccess(objectPath, cert))
450  return false;
451  }
452 
453  // Get the path components. The string will normally begin with "//"
454  // since this is an absolute path but that will be removed by split.
455 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
456  QStringList path = objectPath.split(QChar('/'), QString::SkipEmptyParts);
457 #else
458  QStringList path = objectPath.split(QChar('/'), Qt::SkipEmptyParts);
459 #endif
460  // Since the DSMCC carousel and the MHEG engine are currently on the
461  // same thread this is safe. Otherwise we need to make a deep copy of
462  // the result.
463 
464  bool bReported = false;
465  QElapsedTimer t; t.start();
466  while (!m_stop)
467  {
468  if (isIC)
469  {
470  switch (m_ic.GetFile(objectPath, result, cert))
471  {
473  if (bReported)
474  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Received %1").arg(objectPath));
475  return true;
477  if (bReported)
478  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Not found %1").arg(objectPath));
479  return false;
481  break;
482  }
483  }
484  else
485  {
486  QMutexLocker locker(&m_dsmccLock);
487  int res = m_dsmcc->GetDSMCCObject(path, result);
488  if (res == 0)
489  {
490  if (bReported)
491  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Received %1").arg(objectPath));
492  return true; // Found it
493  }
494  // NB don't exit if -1 (not present) is returned as the object may
495  // arrive later. Exiting can cause the inital app to not be found
496  }
497 
498  if (t.hasExpired(60000)) // TODO get this from carousel info
499  {
500  if (bReported)
501  LOG(VB_MHEG, LOG_INFO, QString("[mhi] timed out %1").arg(objectPath));
502  return false; // Not there.
503  }
504  // Otherwise we block.
505  if (!bReported)
506  {
507  bReported = true;
508  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Waiting for %1").arg(objectPath));
509  }
510  // Process DSMCC packets then block for a while or until we receive
511  // some more packets. We should eventually find out if this item is
512  // present.
514  m_engineWait.wait(&m_runLock, 300);
515  }
516  return false; // Stop has been set. Say the object isn't present.
517 }
518 
519 // Mapping from key name & UserInput register to UserInput EventData
521 {
522  using key_t = QPair< QString, int /*UserInput register*/ >;
523 
524 public:
525  MHKeyLookup();
526 
527  int Find(const QString &name, int reg) const
528  { return m_map.value(key_t(name,reg), 0); }
529 
530 private:
531  void key(const QString &name, int code, int r1,
532  int r2=0, int r3=0, int r4=0, int r5=0, int r6=0, int r7=0, int r8=0, int r9=0);
533 
534  QHash<key_t,int /*EventData*/ > m_map;
535 };
536 
537 void MHKeyLookup::key(const QString &name, int code, int r1,
538  int r2, int r3, int r4, int r5, int r6, int r7, int r8, int r9)
539 {
540  if (r1 > 0)
541  m_map.insert(key_t(name,r1), code);
542  if (r2 > 0)
543  m_map.insert(key_t(name,r2), code);
544  if (r3 > 0)
545  m_map.insert(key_t(name,r3), code);
546  if (r4 > 0)
547  m_map.insert(key_t(name,r4), code);
548  if (r5 > 0)
549  m_map.insert(key_t(name,r5), code);
550  if (r6 > 0)
551  m_map.insert(key_t(name,r6), code);
552  if (r7 > 0)
553  m_map.insert(key_t(name,r7), code);
554  if (r8 > 0)
555  m_map.insert(key_t(name,r8), code);
556  if (r9 > 0)
557  m_map.insert(key_t(name,r9), code);
558 }
559 
561 {
562  // Use a modification of the standard key mapping for RC's with a single
563  // stop button which is used for both Esc and TEXTEXIT (Back).
564  // This mapping doesn't pass Esc to the MHEG app in registers 3 or 5 and
565  // hence allows the user to exit playback when the red button icon is shown
566  QStringList keylist = GET_KEY("TV Playback", "TEXTEXIT").split(QChar(','));
567  bool strict = !keylist.contains("Esc", Qt::CaseInsensitive);
568 
569  // This supports the UK and NZ key profile registers.
570  // The UK uses 3, 4 and 5 and NZ 13, 14 and 15. These are
571  // similar but the NZ profile also provides an EPG key.
572  // ETSI ES 202 184 V2.2.1 (2011-03) adds group 6 for ICE.
573  // The BBC use group 7 for ICE
574  key(ACTION_UP, 1, 4,5,6,7,14,15);
575  key(ACTION_DOWN, 2, 4,5,6,7,14,15);
576  key(ACTION_LEFT, 3, 4,5,6,7,14,15);
577  key(ACTION_RIGHT, 4, 4,5,6,7,14,15);
578  key(ACTION_0, 5, 4,6,7,14);
579  key(ACTION_1, 6, 4,6,7,14);
580  key(ACTION_2, 7, 4,6,7,14);
581  key(ACTION_3, 8, 4,6,7,14);
582  key(ACTION_4, 9, 4,6,7,14);
583  key(ACTION_5, 10, 4,6,7,14);
584  key(ACTION_6, 11, 4,6,7,14);
585  key(ACTION_7, 12, 4,6,7,14);
586  key(ACTION_8, 13, 4,6,7,14);
587  key(ACTION_9, 14, 4,6,7,14);
588  key(ACTION_SELECT, 15, 4,5,6,7,14,15);
589  key(ACTION_TEXTEXIT, 16, strict ? 3 : 0,4,strict ? 5 : 0,6,7,13,14,15); // 16= Cancel
590  // 17= help
591  // 18..99 reserved by DAVIC
592  key(ACTION_MENURED, 100, 3,4,5,6,7,13,14,15);
593  key(ACTION_MENUGREEN, 101, 3,4,5,6,7,13,14,15);
594  key(ACTION_MENUYELLOW, 102, 3,4,5,6,7,13,14,15);
595  key(ACTION_MENUBLUE, 103, 3,4,5,6,7,13,14,15);
596  key(ACTION_MENUTEXT, 104, 3,4,5,6,7);
597  key(ACTION_MENUTEXT, 105, 13,14,15); // NB from original Myth code
598  // 105..119 reserved for future spec
599  key(ACTION_STOP, 120, 6,7);
600  key(ACTION_PLAY, 121, 6,7);
601  key(ACTION_PAUSE, 122, 6,7);
602  key(ACTION_JUMPFFWD, 123, 6,7); // 123= Skip Forward
603  key(ACTION_JUMPRWND, 124, 6,7); // 124= Skip Back
604 #if 0 // These conflict with left & right
605  key(ACTION_SEEKFFWD, 125, 6,7); // 125= Fast Forward
606  key(ACTION_SEEKRWND, 126, 6,7); // 126= Rewind
607 #endif
608  key(ACTION_PLAYBACK, 127, 6,7);
609  // 128..256 reserved for future spec
610  // 257..299 vendor specific
611  key(ACTION_MENUEPG, 300, 13,14,15);
612  // 301.. Vendor specific
613 }
614 
615 // Called from tv_play when a key is pressed.
616 // If it is one in the current profile we queue it for the engine
617 // and return true otherwise we return false.
618 bool MHIContext::OfferKey(const QString& key)
619 {
620  static const MHKeyLookup kKeymap;
621  int action = kKeymap.Find(key, m_keyProfile);
622  if (action == 0)
623  return false;
624 
625  LOG(VB_GENERAL, LOG_INFO, QString("[mhi] Adding MHEG key %1:%2:%3")
626  .arg(key).arg(action).arg(m_keyQueue.size()) );
627  { QMutexLocker locker(&m_keyLock);
629  m_engineWait.wakeAll();
630  return true;
631 }
632 
633 // Called from MythPlayer::VideoStart and MythPlayer::ReinitOSD
634 void MHIContext::Reinit(const QRect videoRect, const QRect dispRect, float aspect)
635 {
636  LOG(VB_MHEG, LOG_INFO,
637  QString("[mhi] Reinit video(y:%1 x:%2 w:%3 h:%4) "
638  "vis(y:%5 x:%6 w:%7 h:%8) aspect=%9")
639  .arg(videoRect.y()).arg(videoRect.x())
640  .arg(videoRect.width()).arg(videoRect.height())
641  .arg(dispRect.y()).arg(dispRect.x())
642  .arg(dispRect.width()).arg(dispRect.height()).arg(aspect));
643  m_videoDisplayRect = QRect();
644 
645  // MHEG presumes square pixels
646  enum { kNone, kHoriz, kBoth };
647  int mode = gCoreContext->GetNumSetting("MhegAspectCorrection", kNone);
648  auto const aspectd = static_cast<double>(aspect);
649  double const vz = (mode == kBoth) ? std::min(1.15, 1. / sqrt(aspectd)) : 1.;
650  double const hz = (mode > kNone) ? vz * aspectd : 1.;
651 
652  m_displayRect = QRect( int(dispRect.width() * (1 - hz) / 2),
653  int(dispRect.height() * (1 - vz) / 2),
654  int(dispRect.width() * hz), int(dispRect.height() * vz) );
655  m_videoRect = QRect( dispRect.x() + m_displayRect.x(),
656  dispRect.y() + int(dispRect.height() * (1 - hz) / 2),
657  int(dispRect.width() * hz), int(dispRect.height() * hz) );
658 }
659 
661 {
662  LOG(VB_MHEG, LOG_INFO, QString("[mhi] SetInputRegister %1").arg(num));
663  QMutexLocker locker(&m_keyLock);
664  m_keyQueue.clear();
665  m_keyProfile = num;
666 }
667 
669 {
670  // 0= Active, 1= Inactive, 2= Disabled
672 }
673 
674 // Called by the video player to redraw the image.
676  MythPainter *osdPainter)
677 {
678  if (!osdWindow || !osdPainter)
679  return;
680 
681  QMutexLocker locker(&m_displayLock);
682 
683  // In MHEG the video is just another item in the display stack
684  // but when we create the OSD we overlay everything over the video.
685  // We need to cut out anything belowthe video on the display stack
686  // to leave the video area clear.
687  auto it = m_display.begin();
688  for (; it != m_display.end(); ++it)
689  {
690  MHIImageData *data = *it;
691  if (!data->m_bUnder)
692  continue;
693 
694  QRect imageRect(data->m_x, data->m_y,
695  data->m_image.width(), data->m_image.height());
696  if (!m_videoDisplayRect.intersects(imageRect))
697  continue;
698 
699  // Replace this item with a set of cut-outs.
700  it = m_display.erase(it);
701 
702  for (const QRect& rect : QRegion(imageRect)-QRegion(m_videoDisplayRect))
703  {
704  QImage image =
705  data->m_image.copy(rect.x()-data->m_x, rect.y()-data->m_y,
706  rect.width(), rect.height());
707  auto *newData = new MHIImageData;
708  newData->m_image = image;
709  newData->m_x = rect.x();
710  newData->m_y = rect.y();
711  newData->m_bUnder = true;
712  it = m_display.insert(it, newData);
713  ++it;
714  }
715  --it;
716  delete data;
717  }
718 
719  m_updated = false;
720  osdWindow->DeleteAllChildren();
721  // Copy all the display items into the display.
722  it = m_display.begin();
723  for (int count = 0; it != m_display.end(); ++it, count++)
724  {
725  MHIImageData *data = *it;
726  MythImage* image = osdPainter->GetFormatImage();
727  if (!image)
728  continue;
729 
730  image->Assign(data->m_image);
731  auto *uiimage = new MythUIImage(osdWindow, QString("itv%1").arg(count));
732  if (uiimage)
733  {
734  uiimage->SetImage(image);
735  uiimage->SetArea(MythRect(data->m_x, data->m_y,
736  data->m_image.width(), data->m_image.height()));
737  }
738  image->DecrRef();
739  }
740  osdWindow->OptimiseDisplayedArea();
741  // N.B. bypasses OSD class hence no expiry set
742  osdWindow->SetVisible(true);
743 }
744 
745 void MHIContext::GetInitialStreams(int &audioTag, int &videoTag) const
746 {
747  audioTag = m_audioTag;
748  videoTag = m_videoTag;
749 }
750 
751 
752 // An area of the screen/image needs to be redrawn.
753 // Called from the MHEG engine.
754 // We always redraw the whole scene.
755 void MHIContext::RequireRedraw(const QRegion & /*region*/)
756 {
757  m_updated = false;
758  m_displayLock.lock();
759  ClearDisplay();
760  m_displayLock.unlock();
761  // Always redraw the whole screen
763  m_updated = true;
764 }
765 
766 inline int MHIContext::ScaleX(int n, bool roundup) const
767 {
768  return (n * m_displayRect.width() + (roundup ? kStdDisplayWidth - 1 : 0)) / kStdDisplayWidth;
769 }
770 
771 inline int MHIContext::ScaleY(int n, bool roundup) const
772 {
773  return (n * m_displayRect.height() + (roundup ? kStdDisplayHeight - 1 : 0)) / kStdDisplayHeight;
774 }
775 
776 inline QRect MHIContext::Scale(const QRect r) const
777 {
778  return { m_displayRect.topLeft() + QPoint(ScaleX(r.x()), ScaleY(r.y())),
779  QSize(ScaleX(r.width(), true), ScaleY(r.height(), true)) };
780 }
781 
782 inline int MHIContext::ScaleVideoX(int n, bool roundup) const
783 {
784  return (n * m_videoRect.width() + (roundup ? kStdDisplayWidth - 1 : 0)) / kStdDisplayWidth;
785 }
786 
787 inline int MHIContext::ScaleVideoY(int n, bool roundup) const
788 {
789  return (n * m_videoRect.height() + (roundup ? kStdDisplayHeight - 1 : 0)) / kStdDisplayHeight;
790 }
791 
792 inline QRect MHIContext::ScaleVideo(const QRect r) const
793 {
794  return { m_videoRect.topLeft() + QPoint(ScaleVideoX(r.x()), ScaleVideoY(r.y())),
795  QSize(ScaleVideoX(r.width(), true), ScaleVideoY(r.height(), true)) };
796 }
797 
798 void MHIContext::AddToDisplay(const QImage &image, const QRect displayRect, bool bUnder /*=false*/)
799 {
800  const QRect scaledRect = Scale(displayRect);
801 
802  auto *data = new MHIImageData;
803 
804  data->m_image = image.convertToFormat(QImage::Format_ARGB32).scaled(
805  scaledRect.width(), scaledRect.height(),
806  Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
807  data->m_x = scaledRect.x();
808  data->m_y = scaledRect.y();
809  data->m_bUnder = bUnder;
810 
811  QMutexLocker locker(&m_displayLock);
812  if (!bUnder)
813  m_display.push_back(data);
814  else
815  {
816  // Replace any existing items under the video with this
817  auto it = m_display.begin();
818  while (it != m_display.end())
819  {
820  MHIImageData *old = *it;
821  if (!old->m_bUnder)
822  ++it;
823  else
824  {
825  it = m_display.erase(it);
826  delete old;
827  }
828  }
829  m_display.push_front(data);
830  }
831 }
832 
833 inline int Roundup(int n, int r)
834 {
835  // NB assumes 2's complement arithmetic
836  return n + (-n & (r - 1));
837 }
838 
839 // The videoRect gives the size and position to which the video must be scaled.
840 // The displayRect gives the rectangle reserved for the video.
841 // e.g. part of the video may be clipped within the displayRect.
842 void MHIContext::DrawVideo(const QRect &videoRect, const QRect &dispRect)
843 {
844  // tell the video player to resize the video stream
845  if (m_parent->GetPlayer())
846  {
847  QRect vidRect;
848  if (videoRect != QRect(QPoint(0,0),QSize(kStdDisplayWidth,kStdDisplayHeight)))
849  {
850  vidRect = ScaleVideo(videoRect);
851  vidRect.setWidth(Roundup(vidRect.width(), 2));
852  vidRect.setHeight(Roundup(vidRect.height(), 2));
853  }
854  emit m_parent->GetPlayer()->ResizeForInteractiveTV(vidRect);
855  }
856 
857  m_videoDisplayRect = Scale(dispRect);
858 
859  // Mark all existing items in the display stack as under the video
860  QMutexLocker locker(&m_displayLock);
861  for (auto & it : m_display)
862  it->m_bUnder = true;
863 }
864 
865 // Caller must hold m_channelMutex
867 {
868  MSqlQuery query(MSqlQuery::InitCon());
869  query.prepare(
870  "SELECT networkid, serviceid, transportid, chanid "
871  "FROM channel, dtv_multiplex "
872  "WHERE channel.deleted IS NULL "
873  " AND channel.mplexid = dtv_multiplex.mplexid "
874  " AND channel.sourceid = dtv_multiplex.sourceid "
875  " AND channel.sourceid = :SOURCEID ;" );
876  query.bindValue(":SOURCEID", m_currentSource);
877  if (!query.exec())
878  {
879  MythDB::DBError("MHIContext::LoadChannelCache", query);
880  return false;
881  }
882  if (!query.isActive())
883  return false;
884  while (query.next())
885  {
886  int nid = query.value(0).toInt();
887  int sid = query.value(1).toInt();
888  int tid = query.value(2).toInt();
889  int cid = query.value(3).toInt();
890  m_channelCache.insert( Key_t(nid, sid), Val_t(tid, cid) );
891  }
892  return true;
893 }
894 
895 // Tuning. Get the index corresponding to a given channel.
896 // The format of the service is dvb://netID.[transPortID].serviceID
897 // where the IDs are in hex.
898 // or rec://svc/lcn/N where N is the "logical channel number"
899 // i.e. the Freeview channel.
900 // Returns -1 if it cannot find it.
901 int MHIContext::GetChannelIndex(const QString &str)
902 {
903  int nResult = -1;
904 
905  do
906  {
907  if (str.startsWith("dvb://"))
908  {
909  QStringList list = str.mid(6).split('.');
910  if (list.size() != 3)
911  break; // Malformed.
912  // The various fields are expressed in hexadecimal.
913  // Convert them to decimal for the DB.
914  bool ok = false;
915  int netID = list[0].toInt(&ok, 16);
916  if (!ok)
917  break;
918  int transportID = !list[1].isEmpty() ? list[1].toInt(&ok, 16) : -1;
919  if (!ok)
920  break;
921  int serviceID = list[2].toInt(&ok, 16);
922  if (!ok)
923  break;
924 
925  QMutexLocker locker(&m_channelMutex);
926  if (m_channelCache.isEmpty())
928 
929  ChannelCache_t::const_iterator it = m_channelCache.constFind(
930  Key_t(netID,serviceID) );
931  if (it == m_channelCache.constEnd())
932  break;
933  if (transportID < 0)
934  nResult = Cid(it);
935  else
936  {
937  do
938  {
939  if (Tid(it) == transportID)
940  {
941  nResult = Cid(it);
942  break;
943  }
944  }
945  while (++it != m_channelCache.constEnd());
946  }
947  }
948  else if (str.startsWith("rec://svc/lcn/"))
949  {
950  // I haven't seen this yet so this is untested.
951  bool ok = false;
952  int channelNo = str.mid(14).toInt(&ok); // Decimal integer
953  if (!ok)
954  break;
955  MSqlQuery query(MSqlQuery::InitCon());
956  query.prepare("SELECT chanid "
957  "FROM channel "
958  "WHERE deleted IS NULL AND "
959  " channum = :CHAN AND "
960  " channel.sourceid = :SOURCEID");
961  query.bindValue(":CHAN", channelNo);
962  query.bindValue(":SOURCEID", m_currentSource);
963  if (query.exec() && query.isActive() && query.next())
964  nResult = query.value(0).toInt();
965  }
966  else if (str == "rec://svc/cur")
968  else if (str == "rec://svc/def")
969  nResult = m_currentChannel;
970  else
971  {
972  LOG(VB_GENERAL, LOG_WARNING,
973  QString("[mhi] GetChannelIndex -- Unrecognized URL %1")
974  .arg(str));
975  }
976  }
977  while (false);
978 
979  LOG(VB_MHEG, LOG_INFO, QString("[mhi] GetChannelIndex %1 => %2")
980  .arg(str).arg(nResult));
981  return nResult;
982 
983 }
984 
985 // Get netId etc from the channel index. This is the inverse of GetChannelIndex.
986 bool MHIContext::GetServiceInfo(int channelId, int &netId, int &origNetId,
987  int &transportId, int &serviceId)
988 {
989  QMutexLocker locker(&m_channelMutex);
990  if (m_channelCache.isEmpty())
992 
993  for (auto it = m_channelCache.cbegin(); it != m_channelCache.cend(); ++it)
994  {
995  if (Cid(it) == channelId)
996  {
997  transportId = Tid(it);
998  netId = Nid(it);
999  origNetId = netId; // We don't have this in the database.
1000  serviceId = Sid(it);
1001  LOG(VB_MHEG, LOG_INFO, QString("[mhi] GetServiceInfo %1 => NID=%2 TID=%3 SID=%4")
1002  .arg(channelId).arg(netId).arg(transportId).arg(serviceId));
1003  return true;
1004  }
1005  }
1006 
1007  LOG(VB_MHEG, LOG_WARNING, QString("[mhi] GetServiceInfo %1 failed").arg(channelId));
1008  return false;
1009 }
1010 
1011 bool MHIContext::TuneTo(int channel, int tuneinfo)
1012 {
1013  if (!m_isLive)
1014  {
1015  LOG(VB_MHEG, LOG_WARNING, QString("[mhi] Can't TuneTo %1 0x%2 while not live")
1016  .arg(channel).arg(tuneinfo,0,16));
1017  return false; // Can't tune if this is a recording.
1018  }
1019 
1020  LOG(VB_GENERAL, LOG_INFO, QString("[mhi] TuneTo %1 0x%2")
1021  .arg(channel).arg(tuneinfo,0,16));
1022  m_tuneInfo.append(tuneinfo);
1023 
1024  // Post an event requesting a channel change.
1025  MythEvent me(QString("NETWORK_CONTROL CHANID %1").arg(channel));
1026  gCoreContext->dispatch(me);
1027  // Reset the NBI version here to prevent a reboot.
1028  QMutexLocker locker(&m_dsmccLock);
1030  m_nbiData.resize(0);
1031  return true;
1032 }
1033 
1034 
1035 // Begin playing the specified stream
1036 bool MHIContext::BeginStream(const QString &stream, MHStream *notify)
1037 {
1038  LOG(VB_MHEG, LOG_INFO, QString("[mhi] BeginStream %1 0x%2")
1039  .arg(stream).arg((quintptr)notify,0,16));
1040 
1041  m_audioTag = -1;
1042  m_videoTag = -1;
1043  m_notify = notify;
1044 
1045  if (stream.startsWith("http://") || stream.startsWith("https://"))
1046  {
1047  m_currentStream = -1;
1048 
1049  // The url is sometimes only http:// during stream startup
1050  if (QUrl(stream).authority().isEmpty())
1051  return false;
1052 
1053  emit m_parent->GetPlayer()->SetInteractiveStream(stream);
1054  return !stream.isEmpty();
1055  }
1056 
1057  int chan = GetChannelIndex(stream);
1058  if (chan < 0)
1059  return false;
1060  if (VERBOSE_LEVEL_CHECK(VB_MHEG, LOG_ANY))
1061  {
1062  int netId = 0;
1063  int origNetId = 0;
1064  int transportId = 0;
1065  int serviceId = 0;
1066  GetServiceInfo(chan, netId, origNetId, transportId, serviceId);
1067  }
1068 
1069  if (chan != m_currentStream)
1070  {
1071  // We have to tune to the channel where the stream is to be found.
1072  // Because the audio and video are both components of an MHEG stream
1073  // they will both be on the same channel.
1074  m_currentStream = chan;
1076  }
1077 
1078  return true;
1079 }
1080 
1082 {
1083  LOG(VB_MHEG, LOG_INFO, QString("[mhi] EndStream 0x%1")
1084  .arg((quintptr)m_notify,0,16) );
1085 
1086  m_notify = nullptr;
1087  emit m_parent->GetPlayer()->SetInteractiveStream(QString());
1088 }
1089 
1090 // Callback from MythPlayer when a stream starts or stops
1091 bool MHIContext::StreamStarted(bool bStarted)
1092 {
1093  if (!m_engine || !m_notify)
1094  return false;
1095 
1096  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Stream 0x%1 %2")
1097  .arg((quintptr)m_notify,0,16).arg(bStarted ? "started" : "stopped"));
1098 
1099  QMutexLocker locker(&m_runLock);
1100  m_engine->StreamStarted(m_notify, bStarted);
1101  if (!bStarted)
1102  m_notify = nullptr;
1103  return m_currentStream == -1; // Return true if it's an http stream
1104 }
1105 
1106 // Begin playing audio
1108 {
1109  LOG(VB_MHEG, LOG_INFO, QString("[mhi] BeginAudio %1").arg(tag));
1110 
1111  if (tag < 0)
1112  return true; // Leave it at the default.
1113 
1114  m_audioTag = tag;
1115  if (m_parent->GetPlayer())
1116  return m_parent->GetPlayer()->SetAudioByComponentTag(tag);
1117  return false;
1118  }
1119 
1120 // Stop playing audio
1122 {
1123  // Do nothing at the moment.
1124 }
1125 
1126 // Begin displaying video from the specified stream
1128 {
1129  LOG(VB_MHEG, LOG_INFO, QString("[mhi] BeginVideo %1").arg(tag));
1130 
1131  if (tag < 0)
1132  return true; // Leave it at the default.
1133 
1134  m_videoTag = tag;
1135  if (m_parent->GetPlayer())
1136  return m_parent->GetPlayer()->SetVideoByComponentTag(tag);
1137  return false;
1138 }
1139 
1140  // Stop displaying video
1142 {
1143  // Do nothing at the moment.
1144 }
1145 
1146 // Get current stream position, -1 if unknown
1147 std::chrono::milliseconds MHIContext::GetStreamPos()
1148 {
1149  return m_parent->GetPlayer() ? m_parent->GetPlayer()->GetStreamPos() : -1ms;
1150 }
1151 
1152 // Get current stream size, -1 if unknown
1153 std::chrono::milliseconds MHIContext::GetStreamMaxPos()
1154 {
1155  return m_parent->GetPlayer() ? m_parent->GetPlayer()->GetStreamMaxPos() : -1ms;
1156 }
1157 
1158 // Set current stream position
1159 std::chrono::milliseconds MHIContext::SetStreamPos(std::chrono::milliseconds pos)
1160 {
1161  if (m_parent->GetPlayer())
1163  // Note: return value is never used
1164  return 0ms;
1165 }
1166 
1167 // Play or pause a stream
1168 void MHIContext::StreamPlay(bool play)
1169 {
1170  if (m_parent->GetPlayer())
1171  emit m_parent->GetPlayer()->PlayInteractiveStream(play);
1172 }
1173 
1174 // Create a new object to draw dynamic line art.
1176  bool isBoxed, MHRgba lineColour, MHRgba fillColour)
1177 {
1178  return new MHIDLA(this, isBoxed, lineColour, fillColour);
1179 }
1180 
1181 // Create a new object to draw text.
1183 {
1184  return new MHIText(this);
1185 }
1186 
1187 // Create a new object to draw bitmaps.
1189 {
1190  return new MHIBitmap(this, tiled);
1191 }
1192 
1193 // Draw a rectangle. This is complicated if we want to get transparency right.
1194 void MHIContext::DrawRect(int xPos, int yPos, int width, int height,
1195  MHRgba colour)
1196 {
1197  if (colour.alpha() == 0 || height == 0 || width == 0)
1198  return; // Fully transparent
1199 
1200  QImage qImage(width, height, QImage::Format_ARGB32);
1201  qImage.fill(qRgba(colour.red(), colour.green(), colour.blue(), colour.alpha()));
1202 
1203  AddToDisplay(qImage, QRect(xPos, yPos, width, height));
1204 }
1205 
1206 // Draw an image at the specified position.
1207 // Generally the whole of the image is drawn but sometimes the
1208 // image may be clipped. x and y define the origin of the bitmap
1209 // and usually that will be the same as the origin of the bounding
1210 // box (clipRect).
1211 void MHIContext::DrawImage(int x, int y, const QRect clipRect,
1212  const QImage &qImage, bool bScaled, bool bUnder)
1213 {
1214  if (qImage.isNull())
1215  return;
1216 
1217  QRect imageRect(x, y, qImage.width(), qImage.height());
1218  QRect displayRect = clipRect & imageRect;
1219 
1220  if (bScaled || displayRect == imageRect) // No clipping required
1221  {
1222  AddToDisplay(qImage, displayRect, bUnder);
1223  }
1224  else if (!displayRect.isEmpty())
1225  { // We must clip the image.
1226  QImage clipped = qImage.copy(displayRect.translated(-x, -y));
1227  AddToDisplay(clipped, displayRect, bUnder);
1228  }
1229  // Otherwise draw nothing.
1230 }
1231 
1232 // Fill in the background. This is only called if there is some area of
1233 // the screen that is not covered with other visibles.
1234 void MHIContext::DrawBackground(const QRegion &reg)
1235 {
1236  if (reg.isEmpty())
1237  return;
1238 
1239  QRect bounds = reg.boundingRect();
1240  DrawRect(bounds.x(), bounds.y(), bounds.width(), bounds.height(),
1241  MHRgba(0, 0, 0, 255)/* black. */);
1242 }
1243 
1244 void MHIText::Draw(int x, int y)
1245 {
1246  m_parent->DrawImage(x, y, QRect(x, y, m_width, m_height), m_image);
1247 }
1248 
1249 void MHIText::SetSize(int width, int height)
1250 {
1251  m_width = width;
1252  m_height = height;
1253 }
1254 
1255 void MHIText::SetFont(int size, bool isBold, bool isItalic)
1256 {
1257  m_fontSize = size;
1258  m_fontItalic = isItalic;
1259  m_fontBold = isBold;
1260  // TODO: Only the size is currently used.
1261  // Bold and Italic are currently ignored.
1262 }
1263 
1264 // FT sizes are in 26.6 fixed point form
1265 const int kShift = 6;
1266 static inline FT_F26Dot6 Point2FT(int pt)
1267 {
1268  return pt << kShift;
1269 }
1270 
1271 static inline int FT2Point(FT_F26Dot6 fp)
1272 {
1273  return (fp + (1<<(kShift-1))) >> kShift;
1274 }
1275 
1276 // Return the bounding rectangle for a piece of text drawn in the
1277 // current font. If maxSize is non-negative it sets strLen to the
1278 // number of characters that will fit in the space and returns the
1279 // bounds for those characters.
1280 // N.B. The box is relative to the origin so the y co-ordinate will
1281 // be negative. It's also possible that the x co-ordinate could be
1282 // negative for slanted fonts but that doesn't currently happen.
1283 QRect MHIText::GetBounds(const QString &str, int &strLen, int maxSize)
1284 {
1285  if (!m_parent->IsFaceLoaded())
1286  return {0,0,0,0};
1287 
1288  FT_Face face = m_parent->GetFontFace();
1289  FT_Error error = FT_Set_Char_Size(face, 0, Point2FT(m_fontSize),
1291  if (error)
1292  return {0,0,0,0};
1293 
1294  int maxAscent = face->size->metrics.ascender;
1295  int maxDescent = -face->size->metrics.descender;
1296  int width = 0;
1297  FT_Bool useKerning = FT_HAS_KERNING(face);
1298  FT_UInt previous = 0;
1299 
1300  for (int n = 0; n < strLen; n++)
1301  {
1302  QChar ch = str.at(n);
1303  FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
1304 
1305  if (glyphIndex == 0)
1306  {
1307  LOG(VB_MHEG, LOG_INFO, QString("[mhi] Unknown glyph 0x%1")
1308  .arg(ch.unicode(),0,16));
1309  previous = 0;
1310  continue;
1311  }
1312 
1313  int kerning = 0;
1314 
1315  if (useKerning && previous != 0)
1316  {
1317  FT_Vector delta;
1318  FT_Get_Kerning(face, previous, glyphIndex,
1319  FT_KERNING_DEFAULT, &delta);
1320  kerning = delta.x;
1321  }
1322 
1323  error = FT_Load_Glyph(face, glyphIndex, 0); // Don't need to render.
1324 
1325  if (error)
1326  continue; // ignore errors.
1327 
1328  FT_GlyphSlot slot = face->glyph; /* a small shortcut */
1329  FT_Pos advance = slot->metrics.horiAdvance + kerning;
1330 
1331  if (maxSize >= 0)
1332  {
1333  if (FT2Point(width + advance) > maxSize)
1334  {
1335  // There isn't enough space for this character.
1336  strLen = n;
1337  break;
1338  }
1339  }
1340  // Calculate the ascent and descent of this glyph.
1341  int descent = slot->metrics.height - slot->metrics.horiBearingY;
1342 
1343  if (slot->metrics.horiBearingY > maxAscent)
1344  maxAscent = slot->metrics.horiBearingY;
1345 
1346  if (descent > maxDescent)
1347  maxDescent = descent;
1348 
1349  width += advance;
1350  previous = glyphIndex;
1351  }
1352 
1353  return {0, -FT2Point(maxAscent), FT2Point(width), FT2Point(maxAscent + maxDescent)};
1354 }
1355 
1356 // Reset the image and fill it with transparent ink.
1357 // The UK MHEG profile says that we should consider the background
1358 // as paper and the text as ink. We have to consider these as two
1359 // different layers. The background is drawn separately as a rectangle.
1360 void MHIText::Clear(void)
1361 {
1362  m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
1363  // QImage::fill doesn't set the alpha buffer.
1364  for (int i = 0; i < m_height; i++)
1365  {
1366  for (int j = 0; j < m_width; j++)
1367  {
1368  m_image.setPixel(j, i, qRgba(0, 0, 0, 0));
1369  }
1370  }
1371 }
1372 
1373 // Draw a line of text in the given position within the image.
1374 // It would be nice to be able to use TTFFont for this but it doesn't provide
1375 // what we want.
1376 void MHIText::AddText(int x, int y, const QString &str, MHRgba colour)
1377 {
1378  if (!m_parent->IsFaceLoaded()) return;
1379  FT_Face face = m_parent->GetFontFace();
1380 
1381  FT_Set_Char_Size(face, 0, Point2FT(m_fontSize),
1383 
1384  // X positions are computed to 64ths and rounded.
1385  // Y positions are in pixels
1386  int posX = Point2FT(x);
1387  int pixelY = y;
1388  FT_Bool useKerning = FT_HAS_KERNING(face);
1389  FT_UInt previous = 0;
1390 
1391  int len = str.length();
1392  for (int n = 0; n < len; n++)
1393  {
1394  // Load the glyph.
1395  QChar ch = str[n];
1396  FT_UInt glyphIndex = FT_Get_Char_Index(face, ch.unicode());
1397  if (glyphIndex == 0)
1398  {
1399  previous = 0;
1400  continue;
1401  }
1402 
1403  if (useKerning && previous != 0)
1404  {
1405  FT_Vector delta;
1406  FT_Get_Kerning(face, previous, glyphIndex,
1407  FT_KERNING_DEFAULT, &delta);
1408  posX += delta.x;
1409  }
1410  FT_Error error = FT_Load_Glyph(face, glyphIndex, FT_LOAD_RENDER);
1411 
1412  if (error)
1413  continue; // ignore errors
1414 
1415  FT_GlyphSlot slot = face->glyph;
1416  if (slot->format != FT_GLYPH_FORMAT_BITMAP)
1417  continue; // Problem
1418 
1419  if ((enum FT_Pixel_Mode_)slot->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY)
1420  continue;
1421 
1422  unsigned char *source = slot->bitmap.buffer;
1423  // Get the origin for the bitmap
1424  int baseX = FT2Point(posX) + slot->bitmap_left;
1425  int baseY = pixelY - slot->bitmap_top;
1426  // Copy the bitmap into the image.
1427  for (unsigned int i = 0; i < slot->bitmap.rows; i++)
1428  {
1429  for (unsigned int j = 0; j < slot->bitmap.width; j++)
1430  {
1431  int greyLevel = source[j];
1432  // Set the pixel to the specified colour but scale its
1433  // brightness according to the grey scale of the pixel.
1434  int red = colour.red();
1435  int green = colour.green();
1436  int blue = colour.blue();
1437  int alpha = colour.alpha() *
1438  greyLevel / slot->bitmap.num_grays;
1439  int xBit = j + baseX;
1440  int yBit = i + baseY;
1441 
1442  // The bits ought to be inside the bitmap but
1443  // I guess there's the possibility
1444  // that rounding might put it outside.
1445  if (xBit >= 0 && xBit < m_width &&
1446  yBit >= 0 && yBit < m_height)
1447  {
1448  m_image.setPixel(xBit, yBit,
1449  qRgba(red, green, blue, alpha));
1450  }
1451  }
1452  source += slot->bitmap.pitch;
1453  }
1454  posX += slot->advance.x;
1455  previous = glyphIndex;
1456  }
1457 }
1458 
1459 // Internal function to fill a rectangle with a colour
1460 void MHIDLA::DrawRect(int x, int y, int width, int height, MHRgba colour)
1461 {
1462  QRgb qColour = qRgba(colour.red(), colour.green(),
1463  colour.blue(), colour.alpha());
1464 
1465  // Constrain the drawing within the image.
1466  if (x < 0)
1467  {
1468  width += x;
1469  x = 0;
1470  }
1471 
1472  if (y < 0)
1473  {
1474  height += y;
1475  y = 0;
1476  }
1477 
1478  if (width <= 0 || height <= 0)
1479  return;
1480 
1481  int imageWidth = m_image.width();
1482  int imageHeight = m_image.height();
1483  if (x+width > imageWidth)
1484  width = imageWidth - x;
1485 
1486  if (y+height > imageHeight)
1487  height = imageHeight - y;
1488 
1489  if (width <= 0 || height <= 0)
1490  return;
1491 
1492  for (int i = 0; i < height; i++)
1493  {
1494  for (int j = 0; j < width; j++)
1495  {
1496  m_image.setPixel(x+j, y+i, qColour);
1497  }
1498  }
1499 }
1500 
1501 // Reset the drawing.
1503 {
1504  if (m_width == 0 || m_height == 0)
1505  {
1506  m_image = QImage();
1507  return;
1508  }
1509  m_image = QImage(m_width, m_height, QImage::Format_ARGB32);
1510  // Fill the image with "transparent colour".
1511  DrawRect(0, 0, m_width, m_height, MHRgba(0, 0, 0, 0));
1512 }
1513 
1514 void MHIDLA::Draw(int x, int y)
1515 {
1516  QRect bounds(x, y, m_width, m_height);
1517  if (m_boxed && m_lineWidth != 0)
1518  {
1519  // Draw the lines round the outside.
1520  // These don't form part of the drawing.
1521  m_parent->DrawRect(x, y, m_width,
1523 
1526 
1527  m_parent->DrawRect(x, y + m_lineWidth,
1529  m_boxLineColour);
1530 
1533  m_boxLineColour);
1534 
1535  // Deflate the box to within the border.
1536  bounds = QRect(bounds.x() + m_lineWidth,
1537  bounds.y() + m_lineWidth,
1538  bounds.width() - 2*m_lineWidth,
1539  bounds.height() - 2*m_lineWidth);
1540  }
1541 
1542  // Draw the background.
1544  y + m_lineWidth,
1545  m_width - m_lineWidth * 2,
1546  m_height - m_lineWidth * 2,
1547  m_boxFillColour);
1548 
1549  // Now the drawing.
1550  m_parent->DrawImage(x, y, bounds, m_image);
1551 }
1552 
1553 // The UK MHEG profile defines exactly how transparency is supposed to work.
1554 // The drawings are made using possibly transparent ink with any crossings
1555 // just set to that ink and then the whole drawing is alpha-merged with the
1556 // underlying graphics.
1557 // DynamicLineArt no longer seems to be used in transmissions in the UK
1558 // although it appears that DrawPoly is used in New Zealand. These are
1559 // very basic implementations of the functions.
1560 
1561 // Lines
1562 void MHIDLA::DrawLine(int x1, int y1, int x2, int y2)
1563 {
1564  // Get the arguments so that the lower x comes first and the
1565  // absolute gradient is less than one.
1566  if (abs(y2-y1) > abs(x2-x1))
1567  {
1568  if (y2 > y1)
1569  DrawLineSub(y1, x1, y2, x2, true);
1570  else
1571  DrawLineSub(y2, x2, y1, x1, true);
1572  }
1573  else
1574  {
1575  if (x2 > x1)
1576  DrawLineSub(x1, y1, x2, y2, false);
1577  else
1578  DrawLineSub(x2, y2, x1, y1, false);
1579  }
1580 }
1581 
1582 // Based on the Bresenham line drawing algorithm but extended to draw
1583 // thick lines.
1584 void MHIDLA::DrawLineSub(int x1, int y1, int x2, int y2, bool swapped)
1585 {
1586  QRgb colour = qRgba(m_lineColour.red(), m_lineColour.green(),
1588  int dx = x2-x1;
1589  int dy = abs(y2-y1);
1590  int yStep = y2 >= y1 ? 1 : -1;
1591  // Adjust the starting positions to take account of the
1592  // line width.
1593  int error2 = dx/2;
1594  for (int k = 0; k < m_lineWidth/2; k++)
1595  {
1596  y1--;
1597  y2--;
1598  error2 += dy;
1599  if (error2*2 > dx)
1600  {
1601  error2 -= dx;
1602  x1 += yStep;
1603  x2 += yStep;
1604  }
1605  }
1606  // Main loop
1607  int y = y1;
1608  int error = dx/2;
1609  for (int x = x1; x <= x2; x++) // Include both endpoints
1610  {
1611  error2 = dx/2;
1612  int j = 0;
1613  // Inner loop also uses the Bresenham algorithm to draw lines
1614  // perpendicular to the principal direction.
1615  for (int i = 0; i < m_lineWidth; i++)
1616  {
1617  if (swapped)
1618  {
1619  if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
1620  m_image.setPixel(y+i, x+j, colour);
1621  }
1622  else
1623  {
1624  if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
1625  m_image.setPixel(x+j, y+i, colour);
1626  }
1627  error2 += dy;
1628  if (error2*2 > dx)
1629  {
1630  error2 -= dx;
1631  j -= yStep;
1632  if (i < m_lineWidth-1)
1633  {
1634  // Add another pixel in this case.
1635  if (swapped)
1636  {
1637  if (x+j >= 0 && y+i >= 0 && y+i < m_width && x+j < m_height)
1638  m_image.setPixel(y+i, x+j, colour);
1639  }
1640  else
1641  {
1642  if (x+j >= 0 && y+i >= 0 && x+j < m_width && y+i < m_height)
1643  m_image.setPixel(x+j, y+i, colour);
1644  }
1645  }
1646  }
1647  }
1648  error += dy;
1649  if (error*2 > dx)
1650  {
1651  error -= dx;
1652  y += yStep;
1653  }
1654  }
1655 }
1656 
1657 // Rectangles
1658 void MHIDLA::DrawBorderedRectangle(int x, int y, int width, int height)
1659 {
1660  if (m_lineWidth != 0)
1661  {
1662  // Draw the lines round the rectangle.
1663  DrawRect(x, y, width, m_lineWidth,
1664  m_lineColour);
1665 
1666  DrawRect(x, y + height - m_lineWidth,
1667  width, m_lineWidth,
1668  m_lineColour);
1669 
1670  DrawRect(x, y + m_lineWidth,
1671  m_lineWidth, height - m_lineWidth * 2,
1672  m_lineColour);
1673 
1674  DrawRect(x + width - m_lineWidth, y + m_lineWidth,
1675  m_lineWidth, height - m_lineWidth * 2,
1676  m_lineColour);
1677 
1678  // Fill the rectangle.
1679  DrawRect(x + m_lineWidth, y + m_lineWidth,
1680  width - m_lineWidth * 2, height - m_lineWidth * 2,
1681  m_fillColour);
1682  }
1683  else
1684  {
1685  DrawRect(x, y, width, height, m_fillColour);
1686  }
1687 }
1688 
1689 // Ovals (ellipses)
1690 void MHIDLA::DrawOval(int /*x*/, int /*y*/, int /*width*/, int /*height*/)
1691 {
1692  // Not implemented. Not actually used in practice.
1693 }
1694 
1695 // Arcs and sectors
1696 void MHIDLA::DrawArcSector(int /*x*/, int /*y*/, int /*width*/, int /*height*/,
1697  int /*start*/, int /*arc*/, bool /*isSector*/)
1698 {
1699  // Not implemented. Not actually used in practice.
1700 }
1701 
1702 // Polygons. This is used directly and also to draw other figures.
1703 // The UK profile says that MHEG should not contain concave or
1704 // self-crossing polygons but we can get the former at least as
1705 // a result of rounding when drawing ellipses.
1706 struct lineSeg { int m_yBottom, m_yTop, m_xBottom; float m_slope; };
1707 
1708 void MHIDLA::DrawPoly(bool isFilled, const MHPointVec& xArray, const MHPointVec& yArray)
1709 {
1710  int nPoints = xArray.size();
1711  if (nPoints < 2)
1712  return;
1713 
1714  if (isFilled)
1715  {
1716  QVector <lineSeg> lineArray(nPoints);
1717  int nLines = 0;
1718  // Initialise the line segment array. Include all lines
1719  // apart from horizontal. Close the polygon by starting
1720  // with the last point in the array.
1721  int lastX = xArray[nPoints-1]; // Last point
1722  int lastY = yArray[nPoints-1];
1723  int yMin = lastY;
1724  int yMax = lastY;
1725  for (int k = 0; k < nPoints; k++)
1726  {
1727  int thisX = xArray[k];
1728  int thisY = yArray[k];
1729  if (lastY != thisY)
1730  {
1731  if (lastY > thisY)
1732  {
1733  lineArray[nLines].m_yBottom = thisY;
1734  lineArray[nLines].m_yTop = lastY;
1735  lineArray[nLines].m_xBottom = thisX;
1736  }
1737  else
1738  {
1739  lineArray[nLines].m_yBottom = lastY;
1740  lineArray[nLines].m_yTop = thisY;
1741  lineArray[nLines].m_xBottom = lastX;
1742  }
1743  lineArray[nLines++].m_slope =
1744  (float)(thisX-lastX) / (float)(thisY-lastY);
1745  }
1746  if (thisY < yMin)
1747  yMin = thisY;
1748  if (thisY > yMax)
1749  yMax = thisY;
1750  lastX = thisX;
1751  lastY = thisY;
1752  }
1753 
1754  // Find the intersections of each line in the line segment array
1755  // with the scan line. Because UK MHEG says that figures should be
1756  // convex we only need to consider two intersections.
1757  QRgb fillColour = qRgba(m_fillColour.red(), m_fillColour.green(),
1759  for (int y = yMin; y < yMax; y++)
1760  {
1761  int crossings = 0;
1762  int xMin = 0;
1763  int xMax = 0;
1764  for (int l = 0; l < nLines; l++)
1765  {
1766  if (y >= lineArray[l].m_yBottom && y < lineArray[l].m_yTop)
1767  {
1768  int x = (int)round((float)(y - lineArray[l].m_yBottom) *
1769  lineArray[l].m_slope) + lineArray[l].m_xBottom;
1770  if (crossings == 0 || x < xMin)
1771  xMin = x;
1772  if (crossings == 0 || x > xMax)
1773  xMax = x;
1774  crossings++;
1775  }
1776  }
1777  if (crossings == 2)
1778  {
1779  for (int x = xMin; x <= xMax; x++)
1780  m_image.setPixel(x, y, fillColour);
1781  }
1782  }
1783 
1784  // Draw the boundary
1785  int lastXpoint = xArray[nPoints-1]; // Last point
1786  int lastYpoint = yArray[nPoints-1];
1787  for (int i = 0; i < nPoints; i++)
1788  {
1789  DrawLine(xArray[i], yArray[i], lastXpoint, lastYpoint);
1790  lastXpoint = xArray[i];
1791  lastYpoint = yArray[i];
1792  }
1793  }
1794  else // PolyLine - draw lines between the points but don't close it.
1795  {
1796  for (int i = 1; i < nPoints; i++)
1797  {
1798  DrawLine(xArray[i], yArray[i], xArray[i-1], yArray[i-1]);
1799  }
1800  }
1801 }
1802 
1803 MHIBitmap::MHIBitmap(MHIContext *parent, bool tiled)
1804  : m_parent(parent), m_tiled(tiled),
1805  m_copyCtx(new MythAVCopy())
1806 {
1807 }
1808 
1810 {
1811  delete m_copyCtx;
1812 }
1813 
1814 void MHIBitmap::Draw(int x, int y, QRect rect, bool tiled, bool bUnder)
1815 {
1816  if (tiled)
1817  {
1818  if (m_image.width() == 0 || m_image.height() == 0)
1819  return;
1820  // Construct an image the size of the bounding box and tile the
1821  // bitmap over this.
1822  QImage tiledImage = QImage(rect.width(), rect.height(),
1823  QImage::Format_ARGB32);
1824 
1825  for (int i = 0; i < rect.width(); i++)
1826  {
1827  for (int j = 0; j < rect.height(); j++)
1828  {
1829  tiledImage.setPixel(i, j, m_image.pixel(i % m_image.width(), j % m_image.height()));
1830  }
1831  }
1832  m_parent->DrawImage(rect.x(), rect.y(), rect, tiledImage, true, bUnder);
1833  }
1834  else
1835  {
1836  // NB THe BBC expects bitmaps to be scaled, not clipped
1837  m_parent->DrawImage(x, y, rect, m_image, true, bUnder);
1838  }
1839 }
1840 
1841 // Create a bitmap from PNG.
1842 void MHIBitmap::CreateFromPNG(const unsigned char *data, int length)
1843 {
1844  m_image = QImage();
1845 
1846  if (!m_image.loadFromData(data, length, "PNG"))
1847  {
1848  m_image = QImage();
1849  return;
1850  }
1851 
1852  // Assume that if it has an alpha buffer then it's partly transparent.
1853  m_opaque = ! m_image.hasAlphaChannel();
1854 }
1855 
1856 // Create a bitmap from JPEG.
1857 //virtual
1858 void MHIBitmap::CreateFromJPEG(const unsigned char *data, int length)
1859 {
1860  m_image = QImage();
1861 
1862  if (!m_image.loadFromData(data, length, "JPG"))
1863  {
1864  m_image = QImage();
1865  return;
1866  }
1867 
1868  // Assume that if it has an alpha buffer then it's partly transparent.
1869  m_opaque = ! m_image.hasAlphaChannel();
1870 }
1871 
1872 // Convert an MPEG I-frame into a bitmap. This is used as the way of
1873 // sending still pictures. We convert the image to a QImage even
1874 // though that actually means converting it from YUV and eventually
1875 // converting it back again but we do this very infrequently so the
1876 // cost is outweighed by the simplification.
1877 void MHIBitmap::CreateFromMPEG(const unsigned char *data, int length)
1878 {
1879  AVCodecContext *c = nullptr;
1880  MythAVFrame picture;
1881  AVPacket pkt;
1882  uint8_t *buff = nullptr;
1883  bool gotPicture = false;
1884  m_image = QImage();
1885 
1886  // Find the mpeg2 video decoder.
1887  const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
1888  if (!codec)
1889  return;
1890  if (!picture)
1891  return;
1892 
1893  c = avcodec_alloc_context3(nullptr);
1894 
1895  if (avcodec_open2(c, codec, nullptr) < 0)
1896  goto Close;
1897 
1898  // Copy the data into AVPacket
1899  if (av_new_packet(&pkt, length) < 0)
1900  goto Close;
1901 
1902  memcpy(pkt.data, data, length);
1903  buff = pkt.data;
1904 
1905  // Get a picture from the packet. Allow 9 loops for
1906  // packet to be decoded. It should take only 2-3 loops
1907  for (int limit=0; limit<10 && !gotPicture; limit++)
1908  {
1909  int len = avcodec_receive_frame(c, picture);
1910  if (len == 0)
1911  gotPicture = true;
1912  if (len == AVERROR(EAGAIN))
1913  len = 0;
1914  if (len == 0)
1915  len = avcodec_send_packet(c, &pkt);
1916  if (len == AVERROR(EAGAIN) || len == AVERROR_EOF)
1917  len = 0;
1918  if (len < 0) // Error
1919  {
1920  std::string error;
1921  LOG(VB_GENERAL, LOG_ERR,
1922  QString("[mhi] video decode error: %1 (%2)")
1923  .arg(av_make_error_stdstring(error, len))
1924  .arg(gotPicture));
1925  goto Close;
1926  }
1927  else
1928  {
1929  pkt.data = nullptr;
1930  pkt.size = 0;
1931  }
1932  }
1933 
1934  if (gotPicture)
1935  {
1936  int nContentWidth = c->width;
1937  int nContentHeight = c->height;
1938  m_image = QImage(nContentWidth, nContentHeight, QImage::Format_ARGB32);
1939  m_opaque = true; // MPEG images are always opaque.
1940 
1941  AVFrame retbuf;
1942  memset(&retbuf, 0, sizeof(AVFrame));
1943 
1944  int bufflen = nContentWidth * nContentHeight * 3;
1945  auto *outputbuf = (unsigned char*)av_malloc(bufflen);
1946 
1947  av_image_fill_arrays(retbuf.data, retbuf.linesize,
1948  outputbuf, AV_PIX_FMT_RGB24,
1949  nContentWidth, nContentHeight,IMAGE_ALIGN);
1950 
1951  AVFrame *tmp = picture;
1952  m_copyCtx->Copy(&retbuf, AV_PIX_FMT_RGB24, tmp, c->pix_fmt,
1953  nContentWidth, nContentHeight);
1954 
1955  uint8_t * buf = outputbuf;
1956 
1957  // Copy the data a pixel at a time.
1958  // This should handle endianness correctly.
1959  for (int i = 0; i < nContentHeight; i++)
1960  {
1961  for (int j = 0; j < nContentWidth; j++)
1962  {
1963  int red = *buf++;
1964  int green = *buf++;
1965  int blue = *buf++;
1966  m_image.setPixel(j, i, qRgb(red, green, blue));
1967  }
1968  }
1969  av_freep(&outputbuf);
1970  }
1971 
1972 Close:
1973  pkt.data = buff;
1974  av_packet_unref(&pkt);
1975  avcodec_free_context(&c);
1976 }
1977 
1978 // Scale the bitmap. Only used for image derived from MPEG I-frames.
1979 void MHIBitmap::ScaleImage(int newWidth, int newHeight)
1980 {
1981  if (m_image.isNull())
1982  return;
1983 
1984  if (newWidth == m_image.width() && newHeight == m_image.height())
1985  return;
1986 
1987  if (newWidth <= 0 || newHeight <= 0)
1988  { // This would be a bit silly but handle it anyway.
1989  m_image = QImage();
1990  return;
1991  }
1992 
1993  m_image = m_image.scaled(newWidth, newHeight,
1994  Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1995 }
ACTION_PLAY
#define ACTION_PLAY
Definition: tv_actions.h:30
MHIContext::m_isLive
bool m_isLive
Definition: mhi.h:212
MHIContext::CreateText
MHTextDisplay * CreateText(void) override
Definition: mhi.cpp:1182
MythPlayerCaptionsUI::PlayInteractiveStream
void PlayInteractiveStream(bool Play)
MHIText::Draw
void Draw(int x, int y) override
Definition: mhi.cpp:1244
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:216
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
mythrect.h
mythevent.h
MHIContext::m_updated
bool m_updated
Definition: mhi.h:201
MHIContext::GetFontFace
FT_Face GetFontFace(void)
Definition: mhi.h:163
MHIImageData::m_y
int m_y
Definition: mhi.cpp:72
MHIContext::IsFaceLoaded
bool IsFaceLoaded(void) const
Definition: mhi.h:164
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
MHIContext::m_notify
MHStream * m_notify
Definition: mhi.h:189
ACTION_DOWN
static constexpr const char * ACTION_DOWN
Definition: mythuiactions.h:17
MHIContext::m_ic
MHInteractionChannel m_ic
Definition: mhi.h:188
MHIContext::Restart
void Restart(int chanid, int sourceid, bool isLive)
Restart the MHEG engine.
Definition: mhi.cpp:172
MHIDLA::DrawOval
void DrawOval(int x, int y, int width, int height) override
Definition: mhi.cpp:1690
NBI_VERSION_UNSET
static constexpr uint16_t NBI_VERSION_UNSET
Definition: mhi.h:45
MHIContext::m_currentStream
int m_currentStream
Definition: mhi.h:211
MHIContext::m_lastNbiVersion
uint m_lastNbiVersion
Definition: mhi.h:219
ACTION_JUMPRWND
#define ACTION_JUMPRWND
Definition: tv_actions.h:45
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
MHIContext::EndStream
void EndStream() override
Definition: mhi.cpp:1081
MHIContext::BeginVideo
bool BeginVideo(int tag) override
Begin displaying video.
Definition: mhi.cpp:1127
MHIContext::m_display
std::list< MHIImageData * > m_display
Definition: mhi.h:203
MHIContext::GetStreamMaxPos
std::chrono::milliseconds GetStreamMaxPos() override
Definition: mhi.cpp:1153
MHIText::AddText
void AddText(int x, int y, const QString &str, MHRgba colour) override
Definition: mhi.cpp:1376
error
static void error(const char *str,...)
Definition: vbi.cpp:36
MHIContext::run
void run(void) override
Definition: mhi.cpp:239
MHInteractionChannel::status
static EStatus status()
Definition: mhegic.cpp:40
ACTION_PLAYBACK
#define ACTION_PLAYBACK
Definition: tv_actions.h:7
mythplayerui.h
MHIContext::ScaleVideoY
int ScaleVideoY(int n, bool roundup=false) const
Definition: mhi.cpp:787
MHIContext::MHIContext
MHIContext(InteractiveTV *parent)
Definition: mhi.cpp:76
MHIContext::m_dsmcc
Dsmcc * m_dsmcc
Definition: mhi.h:184
mythdb.h
MHKeyLookup::MHKeyLookup
MHKeyLookup()
Definition: mhi.cpp:560
MHIContext::m_displayRect
QRect m_displayRect
Definition: mhi.h:223
MHStream
Definition: Stream.h:32
MHIDLA::m_height
int m_height
Height of the drawing.
Definition: mhi.h:357
ACTION_TEXTEXIT
#define ACTION_TEXTEXIT
Definition: tv_actions.h:81
MHIContext::m_tuneInfo
QList< int > m_tuneInfo
Definition: mhi.h:217
MHInteractionChannel::kError
@ kError
Definition: mhegic.h:35
MHIText::Clear
void Clear(void) override
Definition: mhi.cpp:1360
MHIDLA::DrawRect
void DrawRect(int x, int y, int width, int height, MHRgba colour)
Definition: mhi.cpp:1460
MHIDLA::m_lineWidth
int m_lineWidth
Current line width.
Definition: mhi.h:363
MHDLADisplay
Definition: freemheg.h:174
MHIContext::GetChannelIndex
int GetChannelIndex(const QString &str) override
Definition: mhi.cpp:901
MHIContext::CheckCarouselObject
bool CheckCarouselObject(const QString &objectPath) override
Definition: mhi.cpp:366
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
MHIContext::Nid
static int Nid(ChannelCache_t::const_iterator it)
Definition: mhi.h:233
x2
static int x2
Definition: mythsocket.cpp:51
MHIDLA::DrawArcSector
void DrawArcSector(int x, int y, int width, int height, int start, int arc, bool isSector) override
Definition: mhi.cpp:1696
MHInteractionChannel::GetFile
EResult GetFile(const QString &csPath, QByteArray &data, const QByteArray &cert=QByteArray())
Definition: mhegic.cpp:99
MHIContext::m_keyProfile
int m_keyProfile
Definition: mhi.h:193
MHIContext::SetStreamPos
std::chrono::milliseconds SetStreamPos(std::chrono::milliseconds pos) override
Definition: mhi.cpp:1159
kTuneKeepChnl
const unsigned kTuneKeepChnl
Definition: mhi.cpp:61
kTuneQuietly
const unsigned kTuneQuietly
Definition: mhi.cpp:54
MHIBitmap::CreateFromPNG
void CreateFromPNG(const unsigned char *data, int length) override
Create bitmap from PNG.
Definition: mhi.cpp:1842
MHIContext::Reinit
void Reinit(QRect videoRect, QRect dispRect, float aspect)
The display area has changed.
Definition: mhi.cpp:634
MythPlayerCaptionsUI::SetInteractiveStream
void SetInteractiveStream(const QString &Stream)
MHRgba::alpha
int alpha() const
Definition: freemheg.h:91
MythPainter::GetFormatImage
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
Definition: mythpainter.cpp:540
MHIContext::m_engineThread
MThread * m_engineThread
Definition: mhi.h:208
MHIContext::kStdDisplayHeight
static const int kStdDisplayHeight
Definition: mhi.h:169
MythAVFrame
MythAVFrame little utility class that act as a safe way to allocate an AVFrame which can then be allo...
Definition: mythaverror.h:52
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
MHIContext::Scale
QRect Scale(QRect r) const
Definition: mhi.cpp:776
MHIBitmap::m_image
QImage m_image
Definition: mhi.h:307
ACTION_0
static constexpr const char * ACTION_0
Definition: mythuiactions.h:4
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
MHKeyLookup::key
void key(const QString &name, int code, int r1, int r2=0, int r3=0, int r4=0, int r5=0, int r6=0, int r7=0, int r8=0, int r9=0)
Definition: mhi.cpp:537
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
DSMCCPacket::m_length
int m_length
Definition: mhi.h:387
mythdbcon.h
ACTION_LEFT
static constexpr const char * ACTION_LEFT
Definition: mythuiactions.h:18
GetFontsDir
QString GetFontsDir(void)
Definition: mythdirs.cpp:309
MHEG::StreamStarted
virtual void StreamStarted(MHStream *, bool bStarted=true)=0
ft_loaded
static bool ft_loaded
Definition: mhi.cpp:45
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
MHIContext::GetDSMCCObject
bool GetDSMCCObject(const QString &objectPath, QByteArray &result)
Definition: mhi.cpp:390
MythPlayerCaptionsUI::GetStreamPos
std::chrono::milliseconds GetStreamPos()
Definition: mythplayercaptionsui.cpp:636
MHIContext::NetworkBootRequested
void NetworkBootRequested(void)
Definition: mhi.cpp:338
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
ACTION_MENUYELLOW
#define ACTION_MENUYELLOW
Definition: tv_actions.h:79
MHIContext::m_face
FT_Face m_face
Definition: mhi.h:205
MHIContext::QueueDSMCCPacket
void QueueDSMCCPacket(unsigned char *data, int length, int componentTag, unsigned carouselId, int dataBroadcastId)
Definition: mhi.cpp:293
MHIContext::CheckAccess
bool CheckAccess(const QString &objectPath, QByteArray &cert)
Definition: mhi.cpp:402
MHIText::m_image
QImage m_image
Definition: mhi.h:256
mythdirs.h
MHIBitmap::m_parent
MHIContext * m_parent
Definition: mhi.h:305
MHIContext::m_engineWait
QWaitCondition m_engineWait
Definition: mhi.h:198
MHIContext::m_engine
MHEG * m_engine
Definition: mhi.h:195
ACTION_SELECT
static constexpr const char * ACTION_SELECT
Definition: mythuiactions.h:15
MythRect
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
ACTION_9
static constexpr const char * ACTION_9
Definition: mythuiactions.h:13
MHIContext::BeginStream
bool BeginStream(const QString &str, MHStream *notify) override
Begin playing the specified stream.
Definition: mhi.cpp:1036
MHIContext::m_currentChannel
int m_currentChannel
Definition: mhi.h:210
MHIContext::GetServiceInfo
bool GetServiceInfo(int channelId, int &netId, int &origNetId, int &transportId, int &serviceId) override
Get netId etc from the channel index.
Definition: mhi.cpp:986
MHIContext::ClearQueue
void ClearQueue(void)
Definition: mhi.cpp:147
mythuiimage.h
MythUIType::DeleteAllChildren
void DeleteAllChildren(void)
Delete all child widgets.
Definition: mythuitype.cpp:217
MHIDLA::m_boxFillColour
MHRgba m_boxFillColour
Fill colour for the background.
Definition: mhi.h:360
MHIContext::OfferKey
bool OfferKey(const QString &key)
Definition: mhi.cpp:618
MHIContext::BeginAudio
bool BeginAudio(int tag) override
Begin playing audio.
Definition: mhi.cpp:1107
MHKeyLookup
Definition: mhi.cpp:520
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
MHEG::SetBooting
virtual void SetBooting()=0
lineSeg::m_xBottom
int m_xBottom
Definition: mhi.cpp:1706
MHIImageData::m_image
QImage m_image
Definition: mhi.cpp:70
MHInteractionChannel::kSuccess
@ kSuccess
Definition: mhegic.h:35
MHIContext::m_videoTag
int m_videoTag
Definition: mhi.h:216
MHIContext::DrawRect
void DrawRect(int xPos, int yPos, int width, int height, MHRgba colour) override
Additional drawing functions.
Definition: mhi.cpp:1194
MHIContext::Sid
static int Sid(ChannelCache_t::const_iterator it)
Definition: mhi.h:234
MHIContext::ScaleY
int ScaleY(int n, bool roundup=false) const
Definition: mhi.cpp:771
AVFrame
struct AVFrame AVFrame
Definition: BorderDetector.h:15
MHIBitmap::m_opaque
bool m_opaque
Definition: mhi.h:308
MHInteractionChannel::kPending
@ kPending
Definition: mhegic.h:35
MHIImageData::m_x
int m_x
Definition: mhi.cpp:71
MHIImageData::m_bUnder
bool m_bUnder
Definition: mhi.cpp:73
MHIText::m_fontSize
int m_fontSize
Definition: mhi.h:257
MHIContext::CreateBitmap
MHBitmapDisplay * CreateBitmap(bool tiled) override
Definition: mhi.cpp:1188
MHIContext::m_keyQueue
MythDeque< int > m_keyQueue
Definition: mhi.h:192
mythlogging.h
ACTION_MENURED
#define ACTION_MENURED
Definition: tv_actions.h:77
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
MHIContext::SetInputRegister
void SetInputRegister(int num) override
Definition: mhi.cpp:660
ACTION_1
static constexpr const char * ACTION_1
Definition: mythuiactions.h:5
MHIText::m_width
int m_width
Definition: mhi.h:260
tv_actions.h
MHIContext::Key_t
QPair< int, int > Key_t
Definition: mhi.h:227
MHIDLA::m_lineColour
MHRgba m_lineColour
Current line colour.
Definition: mhi.h:361
MHIContext::m_faceLoaded
bool m_faceLoaded
Definition: mhi.h:206
MHIContext::m_videoRect
QRect m_videoRect
Definition: mhi.h:222
MHIContext::StopVideo
void StopVideo() override
Stop displaying video.
Definition: mhi.cpp:1141
lineSeg
Definition: mhi.cpp:1706
hardwareprofile.i18n.t
t
Definition: i18n.py:36
kTuneCarReset
const unsigned kTuneCarReset
Definition: mhi.cpp:57
MHIContext::m_stop
bool m_stop
Definition: mhi.h:199
MHIBitmap::Draw
void Draw(int x, int y, QRect rect, bool tiled, bool bUnder) override
Draw the completed drawing onto the display.
Definition: mhi.cpp:1814
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
MHEG::EngineEvent
virtual void EngineEvent(int)=0
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:227
MHIBitmap::m_copyCtx
MythAVCopy * m_copyCtx
Definition: mhi.h:309
MythPlayerCaptionsUI::SetAudioByComponentTag
bool SetAudioByComponentTag(int Tag)
Selects the audio stream using the DVB component tag.
Definition: mythplayercaptionsui.cpp:588
MHIDLA::DrawLineSub
void DrawLineSub(int x1, int y1, int x2, int y2, bool swapped)
Definition: mhi.cpp:1584
GetShareDir
QString GetShareDir(void)
Definition: mythdirs.cpp:222
x1
static int x1
Definition: mythsocket.cpp:50
MHIContext::GetInitialStreams
void GetInitialStreams(int &audioTag, int &videoTag) const
Get the initial component tags.
Definition: mhi.cpp:745
DSMCCPacket::m_dataBroadcastId
int m_dataBroadcastId
Definition: mhi.h:390
ACTION_PAUSE
#define ACTION_PAUSE
Definition: tv_actions.h:15
MHEG::RunAll
virtual std::chrono::milliseconds RunAll(void)=0
MHIContext::m_keyLock
QMutex m_keyLock
Definition: mhi.h:191
MythPlayerCaptionsUI::ResizeForInteractiveTV
void ResizeForInteractiveTV(const QRect &Rect)
MHIContext::m_audioTag
int m_audioTag
Definition: mhi.h:215
MythImage::DecrRef
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:52
MHIBitmap::~MHIBitmap
~MHIBitmap() override
Definition: mhi.cpp:1809
MHIDLA::m_image
QImage m_image
Definition: mhi.h:355
MHIContext::ScaleVideoX
int ScaleVideoX(int n, bool roundup=false) const
Definition: mhi.cpp:782
MHIContext::m_parent
InteractiveTV * m_parent
Definition: mhi.h:181
Dsmcc::GetDSMCCObject
int GetDSMCCObject(QStringList &objectPath, QByteArray &result)
Definition: dsmcc.cpp:549
MHIContext::m_currentSource
int m_currentSource
Definition: mhi.h:213
MHIText::GetBounds
QRect GetBounds(const QString &str, int &strLen, int maxSize=-1) override
Definition: mhi.cpp:1283
Dsmcc::ProcessSection
void ProcessSection(const unsigned char *data, int length, int componentTag, unsigned carouselId, int dataBroadcastId)
Definition: dsmcc.cpp:450
MHEG::DrawDisplay
virtual void DrawDisplay(const QRegion &toDraw)=0
ACTION_SEEKRWND
#define ACTION_SEEKRWND
Definition: tv_actions.h:42
DSMCCPacket::m_data
unsigned char * m_data
Definition: mhi.h:386
MHIDLA::m_fillColour
MHRgba m_fillColour
Current fill colour.
Definition: mhi.h:362
ACTION_8
static constexpr const char * ACTION_8
Definition: mythuiactions.h:12
MHBitmapDisplay
Definition: freemheg.h:211
MHIBitmap::ScaleImage
void ScaleImage(int newWidth, int newHeight) override
Scale the bitmap. Only used for image derived from MPEG I-frames.
Definition: mhi.cpp:1979
MHIDLA::DrawLine
void DrawLine(int x1, int y1, int x2, int y2) override
Definition: mhi.cpp:1562
ACTION_STOP
#define ACTION_STOP
Definition: tv_actions.h:8
mythpainter.h
MHRgba::blue
int blue() const
Definition: freemheg.h:90
MHCreateEngine
MHEG * MHCreateEngine(MHContext *context)
Definition: Engine.cpp:43
MHInteractionChannel::CheckFile
bool CheckFile(const QString &csPath, const QByteArray &cert=QByteArray())
Definition: mhegic.cpp:65
ACTION_7
static constexpr const char * ACTION_7
Definition: mythuiactions.h:11
uint
unsigned int uint
Definition: compat.h:79
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MHIText::m_fontItalic
bool m_fontItalic
Definition: mhi.h:258
interactivescreen.h
MHIText::m_fontBold
bool m_fontBold
Definition: mhi.h:259
MHIContext::m_dsmccQueue
MythDeque< DSMCCPacket * > m_dsmccQueue
Definition: mhi.h:186
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
MHIBitmap::MHIBitmap
MHIBitmap(MHIContext *parent, bool tiled)
Definition: mhi.cpp:1803
MHIContext::StreamPlay
void StreamPlay(bool play) override
Definition: mhi.cpp:1168
MHIContext::ScaleVideo
QRect ScaleVideo(QRect r) const
Definition: mhi.cpp:792
MHIDLA::m_boxed
bool m_boxed
Does it have a border?
Definition: mhi.h:358
ACTION_MENUTEXT
#define ACTION_MENUTEXT
Definition: tv_actions.h:82
Dsmcc::Reset
void Reset()
Definition: dsmcc.cpp:540
MHIText::SetSize
void SetSize(int width, int height) override
Definition: mhi.cpp:1249
FONT_TO_USE
static constexpr const char * FONT_TO_USE
Definition: mhi.cpp:50
MHIContext::StopEngine
void StopEngine(void)
Stop the MHEG engine if it's running and waits until it has.
Definition: mhi.cpp:155
ACTION_UP
static constexpr const char * ACTION_UP
Definition: mythuiactions.h:16
kShift
const int kShift
Definition: mhi.cpp:1265
MHIContext::m_videoDisplayRect
QRect m_videoDisplayRect
Definition: mhi.h:222
MHKeyLookup::key_t
QPair< QString, int > key_t
Definition: mhi.cpp:522
MHIBitmap::CreateFromMPEG
void CreateFromMPEG(const unsigned char *data, int length) override
Create bitmap from single I frame MPEG.
Definition: mhi.cpp:1877
MHIBitmap
Object for drawing bitmaps.
Definition: mhi.h:267
MythPlayerCaptionsUI::GetStreamMaxPos
std::chrono::milliseconds GetStreamMaxPos()
Definition: mythplayercaptionsui.cpp:642
MHIText::SetFont
void SetFont(int size, bool isBold, bool isItalic) override
Definition: mhi.cpp:1255
ACTION_MENUBLUE
#define ACTION_MENUBLUE
Definition: tv_actions.h:80
MHIContext::m_dsmccLock
QMutex m_dsmccLock
Definition: mhi.h:185
MHIText::m_parent
MHIContext * m_parent
Definition: mhi.h:255
GET_KEY
static QString GET_KEY(const QString &Context, const QString &Action)
Definition: mythmainwindow.h:182
mythimage.h
InteractiveScreen
Definition: interactivescreen.h:9
MHIContext::Cid
static int Cid(ChannelCache_t::const_iterator it)
Definition: mhi.h:232
MHIDLA::m_parent
MHIContext * m_parent
Definition: mhi.h:354
MHIContext::StreamStarted
bool StreamStarted(bool bStarted=true)
Definition: mhi.cpp:1091
ACTION_4
static constexpr const char * ACTION_4
Definition: mythuiactions.h:8
DSMCCPacket
Data for the queued DSMCC tables.
Definition: mhi.h:369
MHIContext::UpdateOSD
void UpdateOSD(InteractiveScreen *osdWindow, MythPainter *osdPainter)
Update the display.
Definition: mhi.cpp:675
MHIContext::CreateDynamicLineArt
MHDLADisplay * CreateDynamicLineArt(bool isBoxed, MHRgba lineColour, MHRgba fillColour) override
Creation functions for various visibles.
Definition: mhi.cpp:1175
InteractiveScreen::OptimiseDisplayedArea
void OptimiseDisplayedArea()
Definition: interactivescreen.cpp:34
MHIContext::m_channelMutex
QMutex m_channelMutex
Definition: mhi.h:230
ACTION_3
static constexpr const char * ACTION_3
Definition: mythuiactions.h:7
ACTION_RIGHT
static constexpr const char * ACTION_RIGHT
Definition: mythuiactions.h:19
Dsmcc
Definition: dsmcc.h:77
MHIContext::LoadFont
bool LoadFont(const QString &name)
Definition: mhi.cpp:96
mythcorecontext.h
MHIBitmap::CreateFromJPEG
void CreateFromJPEG(const unsigned char *data, int length) override
Create bitmap from JPEG.
Definition: mhi.cpp:1858
ACTION_MENUGREEN
#define ACTION_MENUGREEN
Definition: tv_actions.h:78
MythPlayerCaptionsUI::SetInteractiveStreamPos
void SetInteractiveStreamPos(std::chrono::milliseconds Position)
MythPainter
Definition: mythpainter.h:34
MythImage
Definition: mythimage.h:36
MHKeyLookup::m_map
QHash< key_t, int > m_map
Definition: mhi.cpp:534
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
MHIDLA::Clear
void Clear(void) override
Clear the drawing.
Definition: mhi.cpp:1502
MHEG::GenerateUserAction
virtual void GenerateUserAction(int nCode)=0
ACTION_5
static constexpr const char * ACTION_5
Definition: mythuiactions.h:9
MHIContext::Val_t
QPair< int, int > Val_t
Definition: mhi.h:226
mythavutil.h
MHRgba::red
int red() const
Definition: freemheg.h:88
MHIContext::ClearDisplay
void ClearDisplay(void)
Definition: mhi.cpp:138
MythUIType::SetVisible
virtual void SetVisible(bool visible)
Definition: mythuitype.cpp:1108
MHIContext::m_channelCache
ChannelCache_t m_channelCache
Definition: mhi.h:229
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
MHIContext::LoadChannelCache
bool LoadChannelCache()
Definition: mhi.cpp:866
MHIText
Definition: mhi.h:238
Roundup
int Roundup(int n, int r)
Definition: mhi.cpp:833
MHRgba::green
int green() const
Definition: freemheg.h:89
MHIDLA::Draw
void Draw(int x, int y) override
Draw the completed drawing onto the display.
Definition: mhi.cpp:1514
ACTION_JUMPFFWD
#define ACTION_JUMPFFWD
Definition: tv_actions.h:44
mthread.h
FONT_WIDTHRES
static constexpr uint8_t FONT_WIDTHRES
Definition: mhi.cpp:48
Point2FT
static FT_F26Dot6 Point2FT(int pt)
Definition: mhi.cpp:1266
MHIContext::ScaleX
int ScaleX(int n, bool roundup=false) const
Definition: mhi.cpp:766
MHIContext::m_nbiData
std::vector< unsigned char > m_nbiData
Definition: mhi.h:220
build_compdb.action
action
Definition: build_compdb.py:9
MythAVCopy
Definition: mythavutil.h:50
MHIContext::RequireRedraw
void RequireRedraw(const QRegion &region) override
An area of the screen/image needs to be redrawn.
Definition: mhi.cpp:755
MHIContext::GetCarouselData
bool GetCarouselData(const QString &objectPath, QByteArray &result) override
Definition: mhi.cpp:442
interactivetv.h
MythPlayerCaptionsUI::SetVideoByComponentTag
bool SetVideoByComponentTag(int Tag)
Selects the video stream using the DVB component tag.
Definition: mythplayercaptionsui.cpp:600
MHIContext
Contains various utility functions for interactive television.
Definition: mhi.h:50
kTuneKeepApp
const unsigned kTuneKeepApp
Definition: mhi.cpp:55
mythuiactions.h
MHIDLA::DrawPoly
void DrawPoly(bool isFilled, const MHPointVec &xArray, const MHPointVec &yArray) override
Definition: mhi.cpp:1708
MHIText::m_height
int m_height
Definition: mhi.h:261
FONT_HEIGHTRES
static constexpr uint8_t FONT_HEIGHTRES
Definition: mhi.cpp:49
MHIContext::ProcessDSMCCQueue
void ProcessDSMCCQueue(void)
Definition: mhi.cpp:274
lineSeg::m_yTop
int m_yTop
Definition: mhi.cpp:1706
lineSeg::m_yBottom
int m_yBottom
Definition: mhi.cpp:1706
MHIContext::DrawImage
void DrawImage(int x, int y, QRect rect, const QImage &image, bool bScaled=false, bool bUnder=false)
Definition: mhi.cpp:1211
MHIContext::StopAudio
void StopAudio() override
Stop playing audio.
Definition: mhi.cpp:1121
MHIContext::AddToDisplay
void AddToDisplay(const QImage &image, QRect rect, bool bUnder=false)
Definition: mhi.cpp:798
ACTION_2
static constexpr const char * ACTION_2
Definition: mythuiactions.h:6
lineSeg::m_slope
float m_slope
Definition: mhi.cpp:1706
kTuneCarId
const unsigned kTuneCarId
Definition: mhi.cpp:56
MHIImageData
Data for items in the interactive television display stack.
Definition: mhi.cpp:67
MythImage::Assign
void Assign(const QImage &img)
Definition: mythimage.cpp:77
InteractiveTV
This is the interface between an MHEG engine and a MythTV TV object.
Definition: interactivetv.h:15
MythDeque::enqueue
void enqueue(T d)
Adds item to the back of the list. O(1).
Definition: mythdeque.h:41
MythAVCopy::Copy
int Copy(AVFrame *To, const MythVideoFrame *From, unsigned char *Buffer, AVPixelFormat Fmt=AV_PIX_FMT_YUV420P)
Initialise AVFrame and copy contents of VideoFrame frame into it, performing any required conversion.
Definition: mythavutil.cpp:234
MHIContext::SetNetBootInfo
void SetNetBootInfo(const unsigned char *data, uint length)
Definition: mhi.cpp:313
MHIDLA
Object for displaying Dynamic Line Art.
Definition: mhi.h:315
MHIContext::~MHIContext
~MHIContext() override
Definition: mhi.cpp:126
MHIContext::GetStreamPos
std::chrono::milliseconds GetStreamPos() override
Definition: mhi.cpp:1147
FT2Point
static int FT2Point(FT_F26Dot6 fp)
Definition: mhi.cpp:1271
ACTION_MENUEPG
#define ACTION_MENUEPG
Definition: tv_actions.h:83
ft_library
static FT_Library ft_library
Definition: mhi.cpp:46
MHIContext::Tid
static int Tid(ChannelCache_t::const_iterator it)
Definition: mhi.h:231
MHIContext::DrawVideo
void DrawVideo(const QRect &videoRect, const QRect &dispRect) override
Definition: mhi.cpp:842
MythDeque::dequeue
T dequeue()
Removes item from front of list and returns a copy. O(1).
Definition: mythdeque.h:31
MHIDLA::m_width
int m_width
Width of the drawing.
Definition: mhi.h:356
MHTextDisplay
Definition: freemheg.h:195
mythmainwindow.h
MHIDLA::m_boxLineColour
MHRgba m_boxLineColour
Line colour for the background.
Definition: mhi.h:359
MHIContext::TuneTo
bool TuneTo(int channel, int tuneinfo) override
Definition: mhi.cpp:1011
dsmcc.h
MythCoreContext::dispatch
void dispatch(const MythEvent &event)
Definition: mythcorecontext.cpp:1725
MHKeyLookup::Find
int Find(const QString &name, int reg) const
Definition: mhi.cpp:527
ACTION_6
static constexpr const char * ACTION_6
Definition: mythuiactions.h:10
mhi.h
ACTION_SEEKFFWD
#define ACTION_SEEKFFWD
Definition: tv_actions.h:43
DSMCCPacket::m_componentTag
int m_componentTag
Definition: mhi.h:388
MHIDLA::DrawBorderedRectangle
void DrawBorderedRectangle(int x, int y, int width, int height) override
Definition: mhi.cpp:1658
av_make_error_stdstring
char * av_make_error_stdstring(std::string &errbuf, int errnum)
Definition: mythaverror.cpp:41
InteractiveTV::GetPlayer
MythPlayerCaptionsUI * GetPlayer(void)
Definition: interactivetv.h:51
MHIContext::DrawBackground
void DrawBackground(const QRegion &reg) override
Definition: mhi.cpp:1234
DSMCCPacket::m_carouselId
unsigned m_carouselId
Definition: mhi.h:389
MHRgba
Definition: freemheg.h:82
MHIContext::m_runLock
QMutex m_runLock
Definition: mhi.h:197
MHPointVec
std::vector< int > MHPointVec
Definition: BaseClasses.h:31
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:832
MHIContext::m_displayLock
QMutex m_displayLock
Definition: mhi.h:200
MHIContext::GetICStatus
int GetICStatus() override
Definition: mhi.cpp:668
MHIContext::kStdDisplayWidth
static const int kStdDisplayWidth
Definition: mhi.h:168