MythTV  master
bdringbuffer.cpp
Go to the documentation of this file.
1 #include "config.h"
2 
3 #include <fcntl.h>
4 
5 #include <QDir>
6 #include <QCoreApplication>
7 #include <QCryptographicHash>
8 #include <QPainter>
9 
10 #if CONFIG_LIBBLURAY_EXTERNAL
11 #include "libbluray/log_control.h"
12 #include "libbluray/meta_data.h"
13 #include "libbluray/overlay.h"
14 #else
15 #include "util/log_control.h"
16 #include "libbluray/bdnav/meta_data.h"
17 #include "libbluray/decoders/overlay.h"
18 #endif
19 #include "libbluray/keys.h" // for ::BD_VK_POPUP, ::BD_VK_0, etc
20 
21 #include "mythcdrom.h"
22 #include "mythmainwindow.h"
23 #include "mythevent.h"
24 #include "iso639.h"
25 #include "bdiowrapper.h"
26 #include "bdringbuffer.h"
27 #include "mythlogging.h"
28 #include "mythcorecontext.h"
29 #include "mythlocale.h"
30 #include "mythdirs.h"
31 #include "libbluray/bluray.h"
32 #include "mythiowrapper.h"
33 #include "mythuiactions.h" // for ACTION_0, ACTION_1, etc
34 #include "tv_actions.h" // for ACTION_CHANNELDOWN, etc
35 
36 #define LOC QString("BDRingBuf: ")
37 
38 BDOverlay::BDOverlay(const bd_overlay_s * const overlay)
39  : m_image(overlay->w, overlay->h, QImage::Format_Indexed8),
40  m_x(overlay->x),
41  m_y(overlay->y)
42 {
43  wipe();
44 }
45 
46 BDOverlay::BDOverlay(const bd_argb_overlay_s * const overlay)
47  : m_image(overlay->w, overlay->h, QImage::Format_ARGB32),
48  m_x(overlay->x),
49  m_y(overlay->y)
50 {
51 }
52 
53 void BDOverlay::setPalette(const BD_PG_PALETTE_ENTRY *palette)
54 {
55  if( palette )
56  {
57  QVector<QRgb> rgbpalette;
58  for (int i = 0; i < 256; i++)
59  {
60  int y = palette[i].Y;
61  int cr = palette[i].Cr;
62  int cb = palette[i].Cb;
63  int a = palette[i].T;
64  int r = int(y + 1.4022 * (cr - 128));
65  int b = int(y + 1.7710 * (cb - 128));
66  int g = int(1.7047 * y - (0.1952 * b) - (0.5647 * r));
67  if (r < 0) r = 0;
68  if (g < 0) g = 0;
69  if (b < 0) b = 0;
70  if (r > 0xff) r = 0xff;
71  if (g > 0xff) g = 0xff;
72  if (b > 0xff) b = 0xff;
73  rgbpalette.push_back((a << 24) | (r << 16) | (g << 8) | b);
74  }
75 
76  m_image.setColorTable(rgbpalette);
77  }
78 }
79 
81 {
82  wipe(0, 0, m_image.width(), m_image.height());
83 }
84 
85 void BDOverlay::wipe(int x, int y, int width, int height)
86 {
87  if (m_image.format() == QImage::Format_Indexed8)
88  {
89  uint8_t *data = m_image.bits();
90  uint32_t offset = (y * m_image.bytesPerLine()) + x;
91  for (int i = 0; i < height; i++ )
92  {
93  memset( &data[offset], 0xff, width );
94  offset += m_image.bytesPerLine();
95  }
96  }
97  else
98  {
99  QColor transparent(0, 0, 0, 255);
100  QPainter painter(&m_image);
101  painter.setCompositionMode(QPainter::CompositionMode_Source);
102  painter.fillRect(x, y, width, height, transparent);
103  }
104 }
105 
106 static void HandleOverlayCallback(void *data, const bd_overlay_s *const overlay)
107 {
108  auto *bdrb = (BDRingBuffer*) data;
109  if (bdrb)
110  bdrb->SubmitOverlay(overlay);
111 }
112 
113 static void HandleARGBOverlayCallback(void *data, const bd_argb_overlay_s *const overlay)
114 {
115  auto *bdrb = (BDRingBuffer*) data;
116  if (bdrb)
117  bdrb->SubmitARGBOverlay(overlay);
118 }
119 
120 static void file_opened_callback(void* bdr)
121 {
122  auto *obj = (BDRingBuffer*)bdr;
123  if (obj)
124  obj->ProgressUpdate();
125 }
126 
127 static void bd_logger(const char* msg)
128 {
129  LOG(VB_PLAYBACK, LOG_DEBUG, QString("libbluray: %1").arg(QString(msg).trimmed()));
130 }
131 
132 static int _img_read(void *handle, void *buf, int lba, int num_blocks)
133 {
134  int result = -1;
135 
136  if (mythfile_seek(*((int*)handle), lba * 2048LL, SEEK_SET) != -1)
137  result = mythfile_read(*((int*)handle), buf, num_blocks * 2048) / 2048;
138 
139  return result;
140 }
141 
142 BDInfo::BDInfo(const QString &filename)
143 {
144  BLURAY* m_bdnav = nullptr;
145 
146  LOG(VB_PLAYBACK, LOG_INFO, QString("BDInfo: Trying %1").arg(filename));
147  QString name = filename;
148 
149  if (name.startsWith("bd:"))
150  {
151  name.remove(0,3);
152  while (name.startsWith("//"))
153  name.remove(0,1);
154  }
155 
156  // clean path filename
157  name = QDir(QDir::cleanPath(name)).canonicalPath();
158  if (name.isEmpty())
159  {
160  LOG(VB_GENERAL, LOG_ERR, QString("BDInfo:%1 nonexistent").arg(name));
161  name = filename;
162  }
163 
164  LOG(VB_GENERAL, LOG_INFO, QString("BDInfo: Opened BDRingBuffer device at %1")
165  .arg(name));
166 
167  // Make sure log messages from the Bluray library appear in our logs
168  bd_set_debug_handler(bd_logger);
169  bd_set_debug_mask(DBG_CRIT | DBG_NAV | DBG_BLURAY);
170 
171  // Use our own wrappers for file and directory access
172  redirectBDIO();
173 
174  QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir());
175  QByteArray keyarray = keyfile.toLatin1();
176  const char *keyfilepath = keyarray.data();
177  int imgHandle = -1;
178 
179  if (filename.startsWith("myth:") && MythCDROM::inspectImage(filename) != MythCDROM::kUnknown)
180  {
181  // Use streaming for remote images.
182  // Streaming encrypted images causes a SIGSEGV in aacs code when
183  // using the makemkv libraries due to the missing "device" name.
184  // Since a local device (which is likely to be encrypted) can be
185  // opened directly, only use streaming for remote images, which
186  // presumably won't be encrypted.
187  imgHandle = mythfile_open(filename.toLocal8Bit().data(), O_RDONLY);
188 
189  if (imgHandle >= 0)
190  {
191  m_bdnav = bd_init();
192 
193  if (m_bdnav)
194  bd_open_stream(m_bdnav, &imgHandle, _img_read);
195  }
196  }
197  else
198  m_bdnav = bd_open(name.toLocal8Bit().data(), keyfilepath);
199 
200  if (!m_bdnav)
201  {
202  m_lastError = tr("Could not open Blu-ray device: %1").arg(name);
203  LOG(VB_GENERAL, LOG_ERR, QString("BDInfo: ") + m_lastError);
204  m_isValid = false;
205  }
206  else
207  {
208  GetNameAndSerialNum(m_bdnav, m_name, m_serialnumber, name, QString("BDInfo: "));
209  bd_close(m_bdnav);
210  }
211 
212  if (imgHandle >= 0)
213  mythfile_close(imgHandle);
214 
215  LOG(VB_PLAYBACK, LOG_INFO, QString("BDInfo: Done"));
216 }
217 
218 void BDInfo::GetNameAndSerialNum(BLURAY* bdnav,
219  QString &name,
220  QString &serialnum,
221  const QString &filename,
222  const QString &logPrefix)
223 {
224  const meta_dl *metaDiscLibrary = bd_get_meta(bdnav);
225 
226  if (metaDiscLibrary)
227  name = QString(metaDiscLibrary->di_name);
228  else
229  {
230  // Use the directory name for the Bluray name
231  QDir dir(filename);
232  name = dir.dirName();
233  LOG(VB_PLAYBACK, LOG_DEBUG, QString("%1Generated bd name - %2")
234  .arg(logPrefix)
235  .arg(name));
236  }
237 
238  void* pBuf = nullptr;
239  int64_t bufsize = 0;
240 
241  serialnum.clear();
242 
243  // Try to find the first clip info file and
244  // use its SHA1 hash as a serial number.
245  for (uint32_t idx = 0; idx < 200; idx++)
246  {
247  QString clip = QString("BDMV/CLIPINF/%1.clpi").arg(idx, 5, 10, QChar('0'));
248 
249  if (bd_read_file(bdnav, clip.toLocal8Bit().data(), &pBuf, &bufsize) != 0)
250  {
251  QCryptographicHash crypto(QCryptographicHash::Sha1);
252 
253  // Add the clip number to the hash
254  crypto.addData((const char*)&idx, sizeof(idx));
255  // then the length of the file
256  crypto.addData((const char*)&bufsize, sizeof(bufsize));
257  // and then the contents
258  crypto.addData((const char*)pBuf, bufsize);
259 
260  serialnum = QString("%1__gen").arg(QString(crypto.result().toBase64()));
261  free(pBuf);
262 
263  LOG(VB_PLAYBACK, LOG_DEBUG,
264  QString("%1Generated serial number - %2")
265  .arg(logPrefix)
266  .arg(serialnum));
267 
268  break;
269  }
270  }
271 
272  if (serialnum.isEmpty())
273  {
274  LOG(VB_GENERAL, LOG_ERR,
275  QString("%1Unable to generate serial number").arg(logPrefix));
276  }
277 }
278 
279 bool BDInfo::GetNameAndSerialNum(QString &name, QString &serial)
280 {
281  name = m_name;
282  serial = m_serialnumber;
283  return !(name.isEmpty() && serial.isEmpty());
284 }
285 
286 BDRingBuffer::BDRingBuffer(const QString &lfilename)
288  m_overlayPlanes(2, nullptr)
289 {
290  m_tryHDMVNavigation = nullptr != getenv("MYTHTV_HDMV");
291  m_mainThread = QThread::currentThread();
292  BDRingBuffer::OpenFile(lfilename);
293 }
294 
296 {
298 
299  close();
300 }
301 
303 {
304  if (m_bdnav)
305  {
306  m_infoLock.lock();
307  QHash<uint32_t, BLURAY_TITLE_INFO*>::iterator it;
308 
309  for (it = m_cachedTitleInfo.begin(); it !=m_cachedTitleInfo.end(); ++it)
310  bd_free_title_info(it.value());
311  m_cachedTitleInfo.clear();
312 
313  for (it = m_cachedPlaylistInfo.begin(); it !=m_cachedPlaylistInfo.end(); ++it)
314  bd_free_title_info(it.value());
315  m_cachedPlaylistInfo.clear();
316  m_infoLock.unlock();
317 
318  bd_close(m_bdnav);
319  m_bdnav = nullptr;
320  }
321 
322  if (m_imgHandle > 0)
323  {
325  m_imgHandle = -1;
326  }
327 
328  ClearOverlays();
329 }
330 
331 long long BDRingBuffer::SeekInternal(long long pos, int whence)
332 {
333  long long ret = -1;
334 
335  m_posLock.lockForWrite();
336 
337  // Optimize no-op seeks
338  if (m_readAheadRunning &&
339  ((whence == SEEK_SET && pos == m_readPos) ||
340  (whence == SEEK_CUR && pos == 0)))
341  {
342  ret = m_readPos;
343 
344  m_posLock.unlock();
345 
346  return ret;
347  }
348 
349  // only valid for SEEK_SET & SEEK_CUR
350  long long new_pos = (SEEK_SET==whence) ? pos : m_readPos + pos;
351 
352  // Here we perform a normal seek. When successful we
353  // need to call ResetReadAhead(). A reset means we will
354  // need to refill the buffer, which takes some time.
355  if ((SEEK_END == whence) ||
356  ((SEEK_CUR == whence) && new_pos != 0))
357  {
358  errno = EINVAL;
359  ret = -1;
360  }
361  else
362  {
363  SeekInternal(new_pos);
364  m_currentTime = bd_tell_time(m_bdnav);
365  ret = new_pos;
366  }
367 
368  if (ret >= 0)
369  {
370  m_readPos = ret;
371 
372  m_ignoreReadPos = -1;
373 
374  if (m_readAheadRunning)
376 
377  m_readAdjust = 0;
378  }
379  else
380  {
381  QString cmd = QString("Seek(%1, %2)").arg(pos)
382  .arg((whence == SEEK_SET) ? "SEEK_SET" :
383  ((whence == SEEK_CUR) ?"SEEK_CUR" : "SEEK_END"));
384  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
385  }
386 
387  m_posLock.unlock();
388 
389  m_generalWait.wakeAll();
390 
391  return ret;
392 }
393 
394 uint64_t BDRingBuffer::SeekInternal(uint64_t pos)
395 {
396  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Seeking to %1.").arg(pos));
398  if (m_bdnav)
399  return bd_seek_time(m_bdnav, pos);
400  return 0;
401 }
402 
403 void BDRingBuffer::GetDescForPos(QString &desc)
404 {
405  if (!m_infoLock.tryLock())
406  return;
407  desc = tr("Title %1 chapter %2")
408  .arg(m_currentTitle)
409  .arg(m_currentTitleInfo->chapters->idx);
410  m_infoLock.unlock();
411 }
412 
413 bool BDRingBuffer::HandleAction(const QStringList &actions, int64_t pts)
414 {
415  if (!m_isHDMVNavigation)
416  return false;
417 
418  if (actions.contains(ACTION_MENUTEXT))
419  {
420  PressButton(BD_VK_POPUP, pts);
421  return true;
422  }
423 
424  if (!IsInMenu())
425  return false;
426 
427  bool handled = true;
428  if (actions.contains(ACTION_UP) ||
429  actions.contains(ACTION_CHANNELUP))
430  {
431  PressButton(BD_VK_UP, pts);
432  }
433  else if (actions.contains(ACTION_DOWN) ||
434  actions.contains(ACTION_CHANNELDOWN))
435  {
436  PressButton(BD_VK_DOWN, pts);
437  }
438  else if (actions.contains(ACTION_LEFT) ||
439  actions.contains(ACTION_SEEKRWND))
440  {
441  PressButton(BD_VK_LEFT, pts);
442  }
443  else if (actions.contains(ACTION_RIGHT) ||
444  actions.contains(ACTION_SEEKFFWD))
445  {
446  PressButton(BD_VK_RIGHT, pts);
447  }
448  else if (actions.contains(ACTION_0))
449  {
450  PressButton(BD_VK_0, pts);
451  }
452  else if (actions.contains(ACTION_1))
453  {
454  PressButton(BD_VK_1, pts);
455  }
456  else if (actions.contains(ACTION_2))
457  {
458  PressButton(BD_VK_2, pts);
459  }
460  else if (actions.contains(ACTION_3))
461  {
462  PressButton(BD_VK_3, pts);
463  }
464  else if (actions.contains(ACTION_4))
465  {
466  PressButton(BD_VK_4, pts);
467  }
468  else if (actions.contains(ACTION_5))
469  {
470  PressButton(BD_VK_5, pts);
471  }
472  else if (actions.contains(ACTION_6))
473  {
474  PressButton(BD_VK_6, pts);
475  }
476  else if (actions.contains(ACTION_7))
477  {
478  PressButton(BD_VK_7, pts);
479  }
480  else if (actions.contains(ACTION_8))
481  {
482  PressButton(BD_VK_8, pts);
483  }
484  else if (actions.contains(ACTION_9))
485  {
486  PressButton(BD_VK_9, pts);
487  }
488  else if (actions.contains(ACTION_SELECT))
489  {
490  PressButton(BD_VK_ENTER, pts);
491  }
492  else
493  handled = false;
494 
495  return handled;
496 }
497 
499 {
500  // This thread check is probably unnecessary as processEvents should
501  // only handle events in the calling thread - and not all threads
503  return;
504 
505  qApp->postEvent(GetMythMainWindow(),
507  qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
508 }
509 
518 bool BDRingBuffer::OpenFile(const QString &lfilename, uint /*retry_ms*/)
519 {
520  m_safeFilename = lfilename;
521  m_filename = lfilename;
522 
523  // clean path filename
524  QString filename = QDir(QDir::cleanPath(lfilename)).canonicalPath();
525  if (filename.isEmpty())
526  {
527  LOG(VB_GENERAL, LOG_ERR, LOC +
528  QString("%1 nonexistent").arg(lfilename));
529  filename = lfilename;
530  }
532 
533  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened BDRingBuffer device at %1")
534  .arg(filename));
535 
536  // Make sure log messages from the Bluray library appear in our logs
537  bd_set_debug_handler(bd_logger);
538  bd_set_debug_mask(DBG_CRIT | DBG_NAV | DBG_BLURAY);
539 
540  // Use our own wrappers for file and directory access
541  redirectBDIO();
542 
543  // Ask mythiowrapper to update this object on file open progress. Opening
544  // a bluray disc can involve opening several hundred files which can take
545  // several minutes when the disc structure is remote. The callback allows
546  // us to 'kick' the main UI - as the 'please wait' widget is still visible
547  // at this stage
548  mythfile_open_register_callback(filename.toLocal8Bit().data(), this,
550 
551  QMutexLocker locker(&m_infoLock);
552  m_rwLock.lockForWrite();
553 
554  if (m_bdnav)
555  close();
556 
557  QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir());
558  QByteArray keyarray = keyfile.toLatin1();
559  const char *keyfilepath = keyarray.data();
560 
561  if (filename.startsWith("myth:") && MythCDROM::inspectImage(filename) != MythCDROM::kUnknown)
562  {
563  // Use streaming for remote images.
564  // Streaming encrypted images causes a SIGSEGV in aacs code when
565  // using the makemkv libraries due to the missing "device" name.
566  // Since a local device (which is likely to be encrypted) can be
567  // opened directly, only use streaming for remote images, which
568  // presumably won't be encrypted.
569  m_imgHandle = mythfile_open(filename.toLocal8Bit().data(), O_RDONLY);
570 
571  if (m_imgHandle >= 0)
572  {
573  m_bdnav = bd_init();
574 
575  if (m_bdnav)
576  bd_open_stream(m_bdnav, &m_imgHandle, _img_read);
577  }
578  }
579  else
580  m_bdnav = bd_open(filename.toLocal8Bit().data(), keyfilepath);
581 
582  if (!m_bdnav)
583  {
584  m_lastError = tr("Could not open Blu-ray device: %1").arg(filename);
585  m_rwLock.unlock();
586  mythfile_open_register_callback(filename.toLocal8Bit().data(), this, nullptr);
587  return false;
588  }
589 
590  const meta_dl *metaDiscLibrary = bd_get_meta(m_bdnav);
591 
592  if (metaDiscLibrary)
593  {
594  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Title: %1 (%2)")
595  .arg(metaDiscLibrary->di_name)
596  .arg(metaDiscLibrary->language_code));
597  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Alternative Title: %1")
598  .arg(metaDiscLibrary->di_alternative));
599  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Number: %1 of %2")
600  .arg(metaDiscLibrary->di_set_number)
601  .arg(metaDiscLibrary->di_num_sets));
602  }
603 
605 
606  // Check disc to see encryption status, menu and navigation types.
607  m_topMenuSupported = false;
608  m_firstPlaySupported = false;
609  const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(m_bdnav);
610  if (!discinfo || (discinfo->aacs_detected && !discinfo->aacs_handled) ||
611  (discinfo->bdplus_detected && !discinfo->bdplus_handled))
612  {
613  // couldn't decrypt bluray
614  bd_close(m_bdnav);
615  m_bdnav = nullptr;
616  m_lastError = tr("Could not open Blu-ray device %1, failed to decrypt")
617  .arg(filename);
618  m_rwLock.unlock();
619  mythfile_open_register_callback(filename.toLocal8Bit().data(), this, nullptr);
620  return false;
621  }
622 
623  // The following settings affect HDMV navigation
624  // (default audio track selection,
625  // parental controls, menu language, etc. They are not yet used.
626 
627  // Set parental level "age" to 99 for now. TODO: Add support for FE level
628  bd_set_player_setting(m_bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99);
629 
630  // Set preferred language to FE guide language
631  const char *langpref = gCoreContext->GetSetting(
632  "ISO639Language0", "eng").toLatin1().data();
633  QString QScountry = gCoreContext->GetLocale()->GetCountryCode().toLower();
634  const char *country = QScountry.toLatin1().data();
635  bd_set_player_setting_str(
636  m_bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref);
637 
638  // Set preferred presentation graphics language to the FE guide language
639  bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref);
640 
641  // Set preferred menu language to the FE guide language
642  bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref);
643 
644  // Set player country code via MythLocale. (not a region setting)
645  bd_set_player_setting_str(
646  m_bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country);
647 
648  int regioncode = 0;
649  regioncode = gCoreContext->GetNumSetting("BlurayRegionCode");
650  if (regioncode > 0)
651  bd_set_player_setting(m_bdnav, BLURAY_PLAYER_SETTING_REGION_CODE,
652  regioncode);
653 
654  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1 as keyfile...")
655  .arg(QString(keyfilepath)));
656 
657  // Return an index of relevant titles (excludes dupe clips + titles)
658  LOG(VB_GENERAL, LOG_INFO, LOC + "Retrieving title list (please wait).");
659  m_numTitles = bd_get_titles(m_bdnav, TITLES_RELEVANT, 30);
660  LOG(VB_GENERAL, LOG_INFO, LOC +
661  QString("Found %1 titles.").arg(m_numTitles));
662  if (!m_numTitles)
663  {
664  // no title, no point trying any longer
665  bd_close(m_bdnav);
666  m_bdnav = nullptr;
667  m_lastError = tr("Unable to find any Blu-ray compatible titles");
668  m_rwLock.unlock();
669  mythfile_open_register_callback(filename.toLocal8Bit().data(), this, nullptr);
670  return false;
671  }
672 
673  if (discinfo)
674  {
675  m_topMenuSupported = (discinfo->top_menu_supported != 0U);
676  m_firstPlaySupported = (discinfo->first_play_supported != 0U);
677 
678  LOG(VB_PLAYBACK, LOG_INFO, LOC + "*** Blu-ray Disc Information ***");
679  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("First Play Supported: %1")
680  .arg(discinfo->first_play_supported ? "yes" : "no"));
681  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Top Menu Supported: %1")
682  .arg(discinfo->top_menu_supported ? "yes" : "no"));
683  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of HDMV Titles: %1")
684  .arg(discinfo->num_hdmv_titles));
685  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of BD-J Titles: %1")
686  .arg(discinfo->num_bdj_titles));
687  LOG(VB_PLAYBACK, LOG_INFO, LOC +
688  QString("Number of Unsupported Titles: %1")
689  .arg(discinfo->num_unsupported_titles));
690  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS present on disc: %1")
691  .arg(discinfo->aacs_detected ? "yes" : "no"));
692  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libaacs used: %1")
693  .arg(discinfo->libaacs_detected ? "yes" : "no"));
694  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS handled: %1")
695  .arg(discinfo->aacs_handled ? "yes" : "no"));
696  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ present on disc: %1")
697  .arg(discinfo->bdplus_detected ? "yes" : "no"));
698  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libbdplus used: %1")
699  .arg(discinfo->libbdplus_detected ? "yes" : "no"));
700  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ handled: %1")
701  .arg(discinfo->bdplus_handled ? "yes" : "no"));
702  }
703  m_mainTitle = 0;
705  m_titlesize = 0;
706  m_currentTime = 0;
707  m_currentTitleInfo = nullptr;
710  m_lastEvent.event = BD_EVENT_NONE;
711  m_lastEvent.param = 0;
712 
713 
714  // Mostly event-driven values below
715  m_currentAngle = 0;
716  m_currentTitle = -1;
717  m_currentPlaylist = 0;
718  m_currentPlayitem = 0;
719  m_currentChapter = 0;
721  m_currentIGStream = 0;
725  m_pgTextSTEnabled = false;
726  m_secondaryAudioEnabled = false;
727  m_secondaryVideoEnabled = false;
729  m_stillMode = BLURAY_STILL_NONE;
730  m_stillTime = 0;
731  m_timeDiff = 0;
732  m_inMenu = false;
733 
734  // First, attempt to initialize the disc in HDMV navigation mode.
735  // If this fails, fall back to the traditional built-in title switching
736  // mode.
738  {
739  LOG(VB_GENERAL, LOG_INFO, LOC + "Using HDMV navigation mode.");
740  m_isHDMVNavigation = true;
741 
742  // Register the Menu Overlay Callback
743  bd_register_overlay_proc(m_bdnav, this, HandleOverlayCallback);
744  bd_register_argb_overlay_proc(m_bdnav, this, HandleARGBOverlayCallback, nullptr);
745  }
746  else
747  {
748  LOG(VB_GENERAL, LOG_INFO, LOC + "Using title navigation mode.");
749 
750  // Loop through the relevant titles and find the longest
751  uint64_t titleLength = 0;
752  BLURAY_TITLE_INFO *titleInfo = nullptr;
753  bool found = false;
754  for( unsigned i = 0; i < m_numTitles; ++i)
755  {
756  titleInfo = GetTitleInfo(i);
757  if (!titleInfo)
758  continue;
759  if (titleLength == 0 ||
760  (titleInfo->duration > titleLength))
761  {
762  m_mainTitle = titleInfo->idx;
763  titleLength = titleInfo->duration;
764  found = true;
765  }
766  }
767 
768  if (!found)
769  {
770  // no title, no point trying any longer
771  bd_close(m_bdnav);
772  m_bdnav = nullptr;
773  m_lastError = tr("Unable to find any usable Blu-ray titles");
774  m_rwLock.unlock();
775  mythfile_open_register_callback(filename.toLocal8Bit().data(), this, nullptr);
776  return false;
777  }
779  }
780 
782  m_setSwitchToNext = false;
783  m_ateof = false;
784  m_commsError = false;
785  m_numFailures = 0;
786  m_rawBitrate = 8000;
788 
789  m_rwLock.unlock();
790 
791  mythfile_open_register_callback(filename.toLocal8Bit().data(), this, nullptr);
792  return true;
793 }
794 
795 long long BDRingBuffer::GetReadPosition(void) const
796 {
797  if (m_bdnav)
798  return bd_tell(m_bdnav);
799  return 0;
800 }
801 
803 {
804  QMutexLocker locker(&m_infoLock);
805  if (m_currentTitleInfo)
806  return m_currentTitleInfo->chapter_count - 1;
807  return 0;
808 }
809 
811 {
812  if (m_bdnav)
813  return bd_get_current_chapter(m_bdnav);
814  return 0;
815 }
816 
817 uint64_t BDRingBuffer::GetChapterStartTime(uint32_t chapter)
818 {
819  if (chapter >= GetNumChapters())
820  return 0;
821  QMutexLocker locker(&m_infoLock);
822  return (uint64_t)((long double)m_currentTitleInfo->chapters[chapter].start /
823  90000.0F);
824 }
825 
826 uint64_t BDRingBuffer::GetChapterStartFrame(uint32_t chapter)
827 {
828  if (chapter >= GetNumChapters())
829  return 0;
830  QMutexLocker locker(&m_infoLock);
831  return (uint64_t)((long double)(m_currentTitleInfo->chapters[chapter].start *
832  GetFrameRate()) / 90000.0F);
833 }
834 
836 {
837  QMutexLocker locker(&m_infoLock);
838  return m_currentTitle;
839 }
840 
842 {
843  QMutexLocker locker(&m_infoLock);
844  int numTitles = GetNumTitles();
845 
846  if (!(numTitles > 0 && title >= 0 && title < numTitles))
847  return 0;
848 
849  BLURAY_TITLE_INFO *info = GetTitleInfo(title);
850  if (!info)
851  return 0;
852 
853  int duration = ((info->duration) / 90000.0F);
854  return duration;
855 }
856 
857 bool BDRingBuffer::SwitchTitle(uint32_t index)
858 {
859  if (!m_bdnav)
860  return false;
861 
862  m_infoLock.lock();
864  m_infoLock.unlock();
865  bd_select_title(m_bdnav, index);
866 
867  return UpdateTitleInfo();
868 }
869 
870 bool BDRingBuffer::SwitchPlaylist(uint32_t index)
871 {
872  if (!m_bdnav)
873  return false;
874 
875  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - start");
876 
877  m_infoLock.lock();
879  m_currentTitle = bd_get_current_title(m_bdnav);
880  m_infoLock.unlock();
881  bool result = UpdateTitleInfo();
882 
883  LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - end");
884  return result;
885 }
886 
887 BLURAY_TITLE_INFO* BDRingBuffer::GetTitleInfo(uint32_t index)
888 {
889  if (!m_bdnav)
890  return nullptr;
891 
892  QMutexLocker locker(&m_infoLock);
893  if (m_cachedTitleInfo.contains(index))
894  return m_cachedTitleInfo.value(index);
895 
896  if (index > m_numTitles)
897  return nullptr;
898 
899  BLURAY_TITLE_INFO* result = bd_get_title_info(m_bdnav, index, 0);
900  if (result)
901  {
902  LOG(VB_PLAYBACK, LOG_INFO, LOC +
903  QString("Found title %1 info").arg(index));
904  m_cachedTitleInfo.insert(index,result);
905  return result;
906  }
907  return nullptr;
908 }
909 
910 BLURAY_TITLE_INFO* BDRingBuffer::GetPlaylistInfo(uint32_t index)
911 {
912  if (!m_bdnav)
913  return nullptr;
914 
915  QMutexLocker locker(&m_infoLock);
916  if (m_cachedPlaylistInfo.contains(index))
917  return m_cachedPlaylistInfo.value(index);
918 
919  BLURAY_TITLE_INFO* result = bd_get_playlist_info(m_bdnav, index, 0);
920  if (result)
921  {
922  LOG(VB_PLAYBACK, LOG_INFO, LOC +
923  QString("Found playlist %1 info").arg(index));
924  m_cachedPlaylistInfo.insert(index,result);
925  return result;
926  }
927  return nullptr;
928 }
929 
931 {
932  QMutexLocker locker(&m_infoLock);
933  if (!m_currentTitleInfo)
934  return false;
935 
936  m_titleChanged = true;
939  m_currentAngle = 0;
940  m_currentPlayitem = 0;
941  m_timeDiff = 0;
942  m_titlesize = bd_get_title_size(m_bdnav);
943  uint32_t chapter_count = GetNumChapters();
944  uint64_t total_secs = m_currentTitleLength / 90000;
945  int hours = (int)total_secs / 60 / 60;
946  int minutes = ((int)total_secs / 60) - (hours * 60);
947  double secs = (double)total_secs - (double)(hours * 60 * 60 + minutes * 60);
948  QString duration = QString("%1:%2:%3")
949  .arg(hours, 2,10,QChar('0'))
950  .arg(minutes, 2,10,QChar('0'))
951  .arg(secs, 2,'f',1,QChar('0'));
952  LOG(VB_GENERAL, LOG_INFO, LOC +
953  QString("New title info: Index %1 Playlist: %2 Duration: %3 "
954  "Chapters: %5")
955  .arg(m_currentTitle).arg(m_currentTitleInfo->playlist)
956  .arg(duration).arg(chapter_count));
957  LOG(VB_GENERAL, LOG_INFO, LOC +
958  QString("New title info: Clips: %1 Angles: %2 Title Size: %3 "
959  "Frame Rate %4")
960  .arg(m_currentTitleInfo->clip_count)
962  .arg(GetFrameRate()));
963 
964  if (chapter_count)
965  {
966  for (uint i = 0; i < chapter_count; i++)
967  {
968  uint64_t framenum = GetChapterStartFrame(i);
969  total_secs = GetChapterStartTime(i);
970  hours = (int)total_secs / 60 / 60;
971  minutes = ((int)total_secs / 60) - (hours * 60);
972  secs = (double)total_secs -
973  (double)(hours * 60 * 60 + minutes * 60);
974  LOG(VB_PLAYBACK, LOG_INFO, LOC +
975  QString("Chapter %1 found @ [%2:%3:%4]->%5")
976  .arg(i + 1, 2,10,QChar('0'))
977  .arg(hours, 2,10,QChar('0'))
978  .arg(minutes, 2,10,QChar('0'))
979  .arg(secs, 6,'f',3,QChar('0'))
980  .arg(framenum));
981  }
982  }
983 
984  int still = BLURAY_STILL_NONE;
985  int time = 0;
986  if (m_currentTitleInfo->clip_count)
987  {
988  for (uint i = 0; i < m_currentTitleInfo->clip_count; i++)
989  {
990  LOG(VB_PLAYBACK, LOG_INFO, LOC +
991  QString("Clip %1 stillmode %2 stilltime %3 videostreams %4 "
992  "audiostreams %5 igstreams %6")
993  .arg(i).arg(m_currentTitleInfo->clips[i].still_mode)
994  .arg(m_currentTitleInfo->clips[i].still_time)
995  .arg(m_currentTitleInfo->clips[i].video_stream_count)
996  .arg(m_currentTitleInfo->clips[i].audio_stream_count)
997  .arg(m_currentTitleInfo->clips[i].ig_stream_count));
998  still |= m_currentTitleInfo->clips[i].still_mode;
999  time = m_currentTitleInfo->clips[i].still_time;
1000  }
1001  }
1002 
1003  if (m_currentTitleInfo->clip_count > 1 && still != BLURAY_STILL_NONE)
1004  {
1005  LOG(VB_GENERAL, LOG_WARNING, LOC +
1006  "Warning: more than 1 clip, following still "
1007  "frame analysis may be wrong");
1008  }
1009 
1010  if (still == BLURAY_STILL_TIME)
1011  {
1012  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1013  QString("Entering still frame (%1 seconds) UNSUPPORTED").arg(time));
1014  bd_read_skip_still(m_bdnav);
1015  }
1016  else if (still == BLURAY_STILL_INFINITE)
1017  {
1018  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Entering infinite still frame.");
1019  }
1020 
1021  m_stillMode = still;
1022  m_stillTime = time;
1023 
1024  return true;
1025 }
1026 
1028 {
1029  bool ret = m_titleChanged;
1030  m_titleChanged = false;
1031  return ret;
1032 }
1033 
1035 {
1036  if (!m_bdnav)
1037  return false;
1038 
1039  LOG(VB_GENERAL, LOG_INFO, LOC +
1040  QString("Switching to Angle %1...").arg(angle));
1041  bd_seamless_angle_change(m_bdnav, angle);
1042  m_currentAngle = angle;
1043  return true;
1044 }
1045 
1047 {
1048  if (m_bdnav)
1049  return bd_get_title_size(m_bdnav);
1050  return 0;
1051 }
1052 
1053 int64_t BDRingBuffer::AdjustTimestamp(int64_t timestamp)
1054 {
1055  int64_t newTimestamp = timestamp;
1056 
1057  if (newTimestamp != AV_NOPTS_VALUE && newTimestamp >= m_timeDiff)
1058  {
1059  newTimestamp -= m_timeDiff;
1060  }
1061 
1062  return newTimestamp;
1063 }
1064 
1065 int BDRingBuffer::safe_read(void *data, uint sz)
1066 {
1067  int result = 0;
1068  if (m_isHDMVNavigation)
1069  {
1070  result = HandleBDEvents() ? 0 : -1;
1071  while (result == 0)
1072  {
1073  BD_EVENT event;
1074  result = bd_read_ext(m_bdnav,
1075  (unsigned char *)data,
1076  sz, &event);
1077  if (result == 0)
1078  {
1079  HandleBDEvent(event);
1080  result = HandleBDEvents() ? 0 : -1;
1081  }
1082  }
1083  }
1084  else
1085  {
1087  {
1088  processState_t lastState = m_processState;
1089 
1091  result = bd_read(m_bdnav, (unsigned char *)data, sz);
1092 
1093  HandleBDEvents();
1094 
1095  if (m_processState == PROCESS_WAIT && lastState == PROCESS_NORMAL)
1096  {
1097  // We're waiting for the decoder to drain its buffers
1098  // so don't give it any more data just yet.
1099  m_pendingData = QByteArray((const char*)data, result);
1100  result = 0;
1101  }
1102  else
1103  if (m_processState == PROCESS_NORMAL && lastState == PROCESS_REPROCESS)
1104  {
1105  // The decoder has finished draining its buffers so give
1106  // it that last block of data we read
1107  result = m_pendingData.size();
1108  memcpy(data, m_pendingData.constData(), result);
1109  m_pendingData.clear();
1110  }
1111  }
1112  }
1113 
1114  if (result < 0)
1115  StopReads();
1116 
1117  m_currentTime = bd_tell_time(m_bdnav);
1118  return result;
1119 }
1120 
1122 {
1123  QMutexLocker locker(&m_infoLock);
1124  if (m_bdnav && m_currentTitleInfo)
1125  {
1126  uint8_t rate = m_currentTitleInfo->clips->video_streams->rate;
1127  switch (rate)
1128  {
1129  case BLURAY_VIDEO_RATE_24000_1001:
1130  return 23.97;
1131  break;
1132  case BLURAY_VIDEO_RATE_24:
1133  return 24;
1134  break;
1135  case BLURAY_VIDEO_RATE_25:
1136  return 25;
1137  break;
1138  case BLURAY_VIDEO_RATE_30000_1001:
1139  return 29.97;
1140  break;
1141  case BLURAY_VIDEO_RATE_50:
1142  return 50;
1143  break;
1144  case BLURAY_VIDEO_RATE_60000_1001:
1145  return 59.94;
1146  break;
1147  default:
1148  return 0;
1149  break;
1150  }
1151  }
1152  return 0;
1153 }
1154 
1156 {
1157  QMutexLocker locker(&m_infoLock);
1158 
1159  int code = iso639_str3_to_key("und");
1160 
1161  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1162  {
1163  bd_clip& clip = m_currentTitleInfo->clips[0];
1164 
1165  const BLURAY_STREAM_INFO* stream = FindStream(streamID, clip.audio_streams, clip.audio_stream_count);
1166 
1167  if (stream)
1168  {
1169  const uint8_t* lang = stream->lang;
1170  code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
1171  }
1172  }
1173 
1174  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Audio Lang: 0x%1 Code: %2")
1175  .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
1176 
1177  return code;
1178 }
1179 
1181 {
1182  QMutexLocker locker(&m_infoLock);
1183 
1184  int code = iso639_str3_to_key("und");
1185 
1186  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1187  {
1188  bd_clip& clip = m_currentTitleInfo->clips[0];
1189 
1190  const BLURAY_STREAM_INFO* stream = FindStream(streamID, clip.pg_streams, clip.pg_stream_count);
1191 
1192  if (stream)
1193  {
1194  const uint8_t* lang = stream->lang;
1195  code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
1196  }
1197  }
1198 
1199  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Subtitle Lang: 0x%1 Code: %2")
1200  .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
1201 
1202  return code;
1203 }
1204 
1205 void BDRingBuffer::PressButton(int32_t key, int64_t pts)
1206 {
1207  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1208  QString("Key %1 (pts %2)").arg(key).arg(pts));
1209  // HACK for still frame menu navigation
1210  pts = 1;
1211 
1212  if (!m_bdnav || pts <= 0 || key < 0)
1213  return;
1214 
1215  bd_user_input(m_bdnav, pts, key);
1216 }
1217 
1219 {
1220  if (!m_bdnav)
1221  return;
1222 
1223  if (pts <= 0 || x == 0 || y == 0)
1224  return;
1225 
1226  bd_mouse_select(m_bdnav, pts, x, y);
1227 }
1228 
1231 bool BDRingBuffer::GoToMenu(const QString &str, int64_t pts)
1232 {
1233  if (!m_isHDMVNavigation || pts < 0)
1234  return false;
1235 
1236  if (!m_topMenuSupported)
1237  {
1238  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Top Menu not supported");
1239  return false;
1240  }
1241 
1242  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GoToMenu %1").arg(str));
1243 
1244  if (str.compare("root") == 0)
1245  {
1246  if (bd_menu_call(m_bdnav, pts))
1247  {
1248  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1249  QString("Invoked Top Menu (pts %1)").arg(pts));
1250  return true;
1251  }
1252  }
1253  else if (str.compare("popup") == 0)
1254  {
1255  PressButton(BD_VK_POPUP, pts);
1256  return true;
1257  }
1258  else
1259  return false;
1260 
1261  return false;
1262 }
1263 
1265 {
1267  {
1269  {
1271  // HandleBDEvent will change the process state
1272  // if it needs to so don't do it here.
1273  }
1274 
1275  while (m_processState == PROCESS_NORMAL && bd_get_event(m_bdnav, &m_lastEvent))
1276  {
1278  if (m_lastEvent.event == BD_EVENT_NONE ||
1279  m_lastEvent.event == BD_EVENT_ERROR)
1280  {
1281  return false;
1282  }
1283  }
1284  }
1285  return true;
1286 }
1287 
1289 {
1290  switch (ev.event) {
1291  case BD_EVENT_NONE:
1292  break;
1293  case BD_EVENT_ERROR:
1294  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1295  QString("EVENT_ERROR %1").arg(ev.param));
1296  break;
1297  case BD_EVENT_ENCRYPTED:
1298  LOG(VB_GENERAL, LOG_ERR, LOC +
1299  "EVENT_ENCRYPTED, playback will fail.");
1300  break;
1301 
1302  /* current playback position */
1303 
1304  case BD_EVENT_ANGLE:
1305  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1306  QString("EVENT_ANGLE %1").arg(ev.param));
1307  m_currentAngle = ev.param;
1308  break;
1309  case BD_EVENT_TITLE:
1310  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1311  QString("EVENT_TITLE %1 (old %2)")
1312  .arg(ev.param).arg(m_currentTitle));
1313  m_currentTitle = ev.param;
1314  break;
1315  case BD_EVENT_END_OF_TITLE:
1316  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1317  QString("EVENT_END_OF_TITLE %1").arg(m_currentTitle));
1318  WaitForPlayer();
1319  break;
1320  case BD_EVENT_PLAYLIST:
1321  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1322  QString("EVENT_PLAYLIST %1 (old %2)")
1323  .arg(ev.param).arg(m_currentPlaylist));
1324  m_currentPlaylist = ev.param;
1325  m_timeDiff = 0;
1326  m_currentPlayitem = 0;
1328  break;
1329  case BD_EVENT_PLAYITEM:
1330  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1331  QString("EVENT_PLAYITEM %1").arg(ev.param));
1332  {
1333  if (m_currentPlayitem != (int)ev.param)
1334  {
1335  int64_t out = m_currentTitleInfo->clips[m_currentPlayitem].out_time;
1336  int64_t in = m_currentTitleInfo->clips[ev.param].in_time;
1337  int64_t diff = in - out;
1338 
1339  if (diff != 0 && m_processState == PROCESS_NORMAL)
1340  {
1341  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
1342  .arg(in)
1343  .arg(out)
1344  .arg(diff));
1345 
1347  break;
1348  }
1349 
1350  m_timeDiff += diff;
1352  m_currentPlayitem = (int)ev.param;
1353  }
1354  }
1355  break;
1356  case BD_EVENT_CHAPTER:
1357  // N.B. event chapter numbering 1...N, chapter seeks etc 0...
1358  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_CHAPTER %1")
1359  .arg(ev.param));
1360  m_currentChapter = ev.param;
1361  break;
1362  case BD_EVENT_PLAYMARK:
1363  /* playmark reached */
1364  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PLAYMARK"));
1365  break;
1366 
1367  /* playback control */
1368  case BD_EVENT_PLAYLIST_STOP:
1369  /* HDMV VM or JVM stopped playlist playback. Flush all buffers. */
1370  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1371  QString("ToDo EVENT_PLAYLIST_STOP %1")
1372  .arg(ev.param));
1373  break;
1374 
1375  case BD_EVENT_STILL:
1376  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1377  QString("EVENT_STILL %1").arg(ev.param));
1378  break;
1379  case BD_EVENT_STILL_TIME:
1380  // we use the clip information to determine the still frame status
1381  // sleep a little
1382  usleep(10000);
1383  break;
1384  case BD_EVENT_SEEK:
1385  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SEEK"));
1386  break;
1387 
1388  /* stream selection */
1389 
1390  case BD_EVENT_AUDIO_STREAM:
1391  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_AUDIO_STREAM %1")
1392  .arg(ev.param));
1393  m_currentAudioStream = ev.param;
1394  break;
1395  case BD_EVENT_IG_STREAM:
1396  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_IG_STREAM %1")
1397  .arg(ev.param));
1398  m_currentIGStream = ev.param;
1399  break;
1400  case BD_EVENT_PG_TEXTST_STREAM:
1401  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1402  QString("EVENT_PG_TEXTST_STREAM %1").arg(ev.param));
1403  m_currentPGTextSTStream = ev.param;
1404  break;
1405  case BD_EVENT_SECONDARY_AUDIO_STREAM:
1406  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1407  QString("EVENT_SECONDARY_AUDIO_STREAM %1").arg(ev.param));
1408  m_currentSecondaryAudioStream = ev.param;
1409  break;
1410  case BD_EVENT_SECONDARY_VIDEO_STREAM:
1411  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1412  QString("EVENT_SECONDARY_VIDEO_STREAM %1").arg(ev.param));
1413  m_currentSecondaryVideoStream = ev.param;
1414  break;
1415 
1416  case BD_EVENT_PG_TEXTST:
1417  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PG_TEXTST %1")
1418  .arg(ev.param ? "enable" : "disable"));
1419  m_pgTextSTEnabled = (ev.param != 0U);
1420  break;
1421  case BD_EVENT_SECONDARY_AUDIO:
1422  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_AUDIO %1")
1423  .arg(ev.param ? "enable" : "disable"));
1424  m_secondaryAudioEnabled = (ev.param != 0U);
1425  break;
1426  case BD_EVENT_SECONDARY_VIDEO:
1427  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO %1")
1428  .arg(ev.param ? "enable" : "disable"));
1429  m_secondaryVideoEnabled = (ev.param != 0U);
1430  break;
1431  case BD_EVENT_SECONDARY_VIDEO_SIZE:
1432  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1433  QString("EVENT_SECONDARY_VIDEO_SIZE %1")
1434  .arg(ev.param==0 ? "PIP" : "fullscreen"));
1435  m_secondaryVideoIsFullscreen = (ev.param != 0U);
1436  break;
1437 
1438  /* status */
1439  case BD_EVENT_IDLE:
1440  /* Nothing to do. Playlist is not playing, but title applet is running.
1441  * Application should not call bd_read*() immediately again to avoid busy loop. */
1442  usleep(40000);
1443  break;
1444 
1445  case BD_EVENT_MENU:
1446  /* Interactive menu visible */
1447  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1448  QString("EVENT_MENU %1")
1449  .arg(ev.param==0 ? "no" : "yes"));
1450  m_inMenu = (ev.param == 1);
1451  break;
1452 
1453  case BD_EVENT_KEY_INTEREST_TABLE:
1454  /* BD-J key interest table changed */
1455  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1456  QString("ToDo EVENT_KEY_INTEREST_TABLE %1")
1457  .arg(ev.param));
1458  break;
1459 
1460  case BD_EVENT_UO_MASK_CHANGED:
1461  /* User operations mask was changed */
1462  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1463  QString("ToDo EVENT_UO_MASK_CHANGED %1")
1464  .arg(ev.param));
1465  break;
1466 
1467  default:
1468  LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Unknown Event! %1 %2")
1469  .arg(ev.event).arg(ev.param));
1470  break;
1471  }
1472 }
1473 
1475 {
1476  return m_stillTime > 0 && m_stillMode != BLURAY_STILL_NONE;
1477 }
1478 
1486 const BLURAY_STREAM_INFO* BDRingBuffer::FindStream(int streamid, BLURAY_STREAM_INFO* streams, int streamCount)
1487 {
1488  const BLURAY_STREAM_INFO* stream = nullptr;
1489 
1490  for(int i = 0; i < streamCount && !stream; i++)
1491  {
1492  if (streams[i].pid == streamid)
1493  stream = &streams[i];
1494  }
1495 
1496  return stream;
1497 }
1498 
1500 {
1501  bool valid = false;
1502 
1503  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1504  {
1505  bd_clip& clip = m_currentTitleInfo->clips[0];
1506  if( FindStream(streamid,clip.audio_streams, clip.audio_stream_count) ||
1507  FindStream(streamid,clip.video_streams, clip.video_stream_count) ||
1508  FindStream(streamid,clip.ig_streams, clip.ig_stream_count) ||
1509  FindStream(streamid,clip.pg_streams, clip.pg_stream_count) ||
1510  FindStream(streamid,clip.sec_audio_streams, clip.sec_audio_stream_count) ||
1511  FindStream(streamid,clip.sec_video_streams, clip.sec_video_stream_count)
1512  )
1513  {
1514  valid = true;
1515  }
1516  }
1517 
1518  return valid;
1519 }
1520 
1522 {
1523  if (m_ignorePlayerWait)
1524  return;
1525 
1526  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
1527  m_playerWait = true;
1528  int count = 0;
1529  while (m_playerWait && count++ < 200)
1530  usleep(10000);
1531  if (m_playerWait)
1532  {
1533  LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
1534  m_playerWait = false;
1535  }
1536 }
1537 
1539 {
1540  if (m_bdnav && m_isHDMVNavigation)
1541  {
1542  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Starting from beginning...");
1543  return true; //bd_play(m_bdnav);
1544  }
1545  return true;
1546 }
1547 
1548 bool BDRingBuffer::GetNameAndSerialNum(QString &name, QString &serialnum)
1549 {
1550  if (!m_bdnav)
1551  return false;
1552 
1553  name = m_name;
1554  serialnum = m_serialNumber;
1555 
1556  return !serialnum.isEmpty();
1557 }
1558 
1562 {
1563  int title = GetCurrentTitle();
1564  uint64_t time = m_currentTime;
1565  uint64_t angle = GetCurrentAngle();
1566 
1567  if (title >= 0)
1568  {
1569  state = QString("title:%1,time:%2,angle:%3").arg(title)
1570  .arg(time)
1571  .arg(angle);
1572  }
1573  else
1574  {
1575  state.clear();
1576  }
1577 
1578  return(!state.isEmpty());
1579 }
1580 
1583 bool BDRingBuffer::RestoreBDStateSnapshot(const QString& state)
1584 {
1585  bool rc = false;
1586  QStringList states = state.split(",", QString::SkipEmptyParts);
1587  QHash<QString, uint64_t> settings;
1588 
1589  foreach (const QString& entry, states)
1590  {
1591  QStringList keyvalue = entry.split(":", QString::SkipEmptyParts);
1592 
1593  if (keyvalue.length() != 2)
1594  {
1595  LOG(VB_PLAYBACK, LOG_ERR, LOC +
1596  QString("Invalid BD state: %1 (%2)")
1597  .arg(entry).arg(state));
1598  }
1599  else
1600  {
1601  settings[keyvalue[0]] = keyvalue[1].toULongLong();
1602  //LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString( "%1 = %2" ).arg(keyvalue[0]).arg(keyvalue[1]));
1603  }
1604 
1605  }
1606 
1607  if (settings.contains("title") &&
1608  settings.contains("time") )
1609  {
1610  uint32_t title = (uint32_t)settings["title"];
1611  uint64_t time = settings["time"];
1612  uint64_t angle = 0;
1613 
1614  if (settings.contains("angle"))
1615  angle = settings["angle"];
1616 
1617  if(title != (uint32_t)m_currentTitle)
1618  SwitchTitle(title);
1619 
1620  SeekInternal(time, SEEK_SET);
1621 
1622  SwitchAngle(angle);
1623  rc = true;
1624  }
1625 
1626  return rc;
1627 }
1628 
1629 
1631 {
1632  QMutexLocker lock(&m_overlayLock);
1633 
1634  while (!m_overlayImages.isEmpty())
1635  {
1636  BDOverlay *overlay = m_overlayImages.takeFirst();
1637  delete overlay;
1638  overlay = nullptr;
1639  }
1640 
1641  // NOLINTNEXTLINE(modernize-loop-convert)
1642  for (int i = 0; i < m_overlayPlanes.size(); i++)
1643  {
1644  BDOverlay*& osd = m_overlayPlanes[i];
1645 
1646  if (osd)
1647  {
1648  delete osd;
1649  osd = nullptr;
1650  }
1651  }
1652 }
1653 
1655 {
1656  QMutexLocker lock(&m_overlayLock);
1657  if (!m_overlayImages.isEmpty())
1658  return m_overlayImages.takeFirst();
1659  return nullptr;
1660 }
1661 
1662 void BDRingBuffer::SubmitOverlay(const bd_overlay_s * const overlay)
1663 {
1664  if (!overlay || overlay->plane > m_overlayPlanes.size())
1665  return;
1666 
1667  LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1668  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd = %1, %2").arg(overlay->cmd).arg(overlay->plane));
1669  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay rect = (%1,%2,%3,%4)").arg(overlay->x).arg(overlay->y)
1670  .arg(overlay->w).arg(overlay->h));
1671  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1").arg(overlay->pts));
1672  LOG(VB_PLAYBACK, LOG_DEBUG, QString("update palette = %1").arg(overlay->palette_update_flag ? "yes":"no"));
1673 
1674  BDOverlay*& osd = m_overlayPlanes[overlay->plane];
1675 
1676  switch(overlay->cmd)
1677  {
1678  case BD_OVERLAY_INIT: /* init overlay plane. Size and position of plane in x,y,w,h */
1679  /* init overlay plane. Size of plane in w,h */
1680  delete osd;
1681  osd = new BDOverlay(overlay);
1682  break;
1683 
1684  case BD_OVERLAY_CLOSE: /* close overlay plane */
1685  /* close overlay */
1686  {
1687  if (osd)
1688  {
1689  delete osd;
1690  osd = nullptr;
1691  }
1692 
1693  QMutexLocker lock(&m_overlayLock);
1694  m_overlayImages.append(new BDOverlay());
1695  }
1696  break;
1697 
1698  /* following events can be processed immediately, but changes
1699  * should not be flushed to display before next FLUSH event
1700  */
1701  case BD_OVERLAY_HIDE: /* overlay is empty and can be hidden */
1702  case BD_OVERLAY_CLEAR: /* clear plane */
1703  if (osd)
1704  osd->wipe();
1705  break;
1706 
1707  case BD_OVERLAY_WIPE: /* clear area (x,y,w,h) */
1708  if (osd)
1709  osd->wipe(overlay->x, overlay->y, overlay->w, overlay->h);
1710  break;
1711 
1712  case BD_OVERLAY_DRAW: /* draw bitmap (x,y,w,h,img,palette,crop) */
1713  if (osd)
1714  {
1715  const BD_PG_RLE_ELEM *rlep = overlay->img;
1716  unsigned actual = overlay->w * overlay->h;
1717  uint8_t *data = osd->m_image.bits();
1718  data = &data[(overlay->y * osd->m_image.bytesPerLine()) + overlay->x];
1719 
1720  for (unsigned i = 0; i < actual; i += rlep->len, rlep++)
1721  {
1722  int dst_y = (i / overlay->w) * osd->m_image.bytesPerLine();
1723  int dst_x = (i % overlay->w);
1724  memset(data + dst_y + dst_x, rlep->color, rlep->len);
1725  }
1726 
1727  osd->setPalette(overlay->palette);
1728  }
1729  break;
1730 
1731  case BD_OVERLAY_FLUSH: /* all changes have been done, flush overlay to display at given pts */
1732  if (osd)
1733  {
1734  auto* newOverlay = new BDOverlay(*osd);
1735  newOverlay->m_image =
1736  osd->m_image.convertToFormat(QImage::Format_ARGB32);
1737  newOverlay->m_pts = overlay->pts;
1738 
1739  QMutexLocker lock(&m_overlayLock);
1740  m_overlayImages.append(newOverlay);
1741  }
1742  break;
1743 
1744  default:
1745  break;
1746  }
1747 }
1748 
1749 void BDRingBuffer::SubmitARGBOverlay(const bd_argb_overlay_s * const overlay)
1750 {
1751  if (!overlay || overlay->plane > m_overlayPlanes.size())
1752  return;
1753 
1754  LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1755  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd,plane = %1, %2").arg(overlay->cmd)
1756  .arg(overlay->plane));
1757  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->(x,y,w,h) = %1,%2,%3x%4 - %5").arg(overlay->x)
1758  .arg(overlay->y)
1759  .arg(overlay->w)
1760  .arg(overlay->h)
1761  .arg(overlay->stride));
1762  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1").arg(overlay->pts));
1763 
1764  BDOverlay*& osd = m_overlayPlanes[overlay->plane];
1765 
1766  switch(overlay->cmd)
1767  {
1768  case BD_ARGB_OVERLAY_INIT:
1769  /* init overlay plane. Size of plane in w,h */
1770  delete osd;
1771  osd = new BDOverlay(overlay);
1772  break;
1773 
1774  case BD_ARGB_OVERLAY_CLOSE:
1775  /* close overlay */
1776  {
1777  if (osd)
1778  {
1779  delete osd;
1780  osd = nullptr;
1781  }
1782 
1783  QMutexLocker lock(&m_overlayLock);
1784  m_overlayImages.append(new BDOverlay());
1785  }
1786  break;
1787 
1788  /* following events can be processed immediately, but changes
1789  * should not be flushed to display before next FLUSH event
1790  */
1791  case BD_ARGB_OVERLAY_DRAW:
1792  if (osd)
1793  {
1794  /* draw image */
1795  uint8_t* data = osd->m_image.bits();
1796 
1797  uint32_t srcOffset = 0;
1798  uint32_t dstOffset = (overlay->y * osd->m_image.bytesPerLine()) + (overlay->x * 4);
1799 
1800  for (uint16_t y = 0; y < overlay->h; y++)
1801  {
1802  memcpy(&data[dstOffset],
1803  &overlay->argb[srcOffset],
1804  overlay->w * 4);
1805 
1806  dstOffset += osd->m_image.bytesPerLine();
1807  srcOffset += overlay->stride;
1808  }
1809  }
1810  break;
1811 
1812  case BD_ARGB_OVERLAY_FLUSH:
1813  /* all changes have been done, flush overlay to display at given pts */
1814  if (osd)
1815  {
1816  QMutexLocker lock(&m_overlayLock);
1817  auto* newOverlay = new BDOverlay(*osd);
1818  newOverlay->m_pts = overlay->pts;
1819  m_overlayImages.append(newOverlay);
1820  }
1821  break;
1822 
1823  default:
1824  LOG(VB_PLAYBACK, LOG_ERR, QString("Unknown ARGB overlay - %1").arg(overlay->cmd));
1825  break;
1826  }
1827 }
int mythfile_open(const char *pathname, int flags)
void close(void)
#define ACTION_6
Definition: mythuiactions.h:10
void WaitForPlayer(void)
ISO 639-1 and ISO 639-2 support functions.
bool m_secondaryAudioEnabled
Definition: bdringbuffer.h:206
processState_t m_processState
Definition: bdringbuffer.h:223
MythLocale * GetLocale(void) const
BD_EVENT m_lastEvent
Definition: bdringbuffer.h:222
#define ACTION_CHANNELUP
Definition: tv_actions.h:16
long long SeekInternal(long long pos, int whence) override
static void bd_logger(const char *msg)
#define ACTION_0
Definition: mythuiactions.h:4
#define ACTION_2
Definition: mythuiactions.h:6
bool GetNameAndSerialNum(QString &_name, QString &_serialnum)
void PressButton(int32_t key, int64_t pts)
bool SwitchTitle(uint32_t index)
#define ACTION_SEEKFFWD
Definition: tv_actions.h:43
uint64_t m_currentTitleAngleCount
Definition: bdringbuffer.h:188
int m_currentPlayitem
Definition: bdringbuffer.h:196
BDOverlay * GetOverlay(void)
bool HandleAction(const QStringList &actions, int64_t pts) override
QReadWriteLock m_rwLock
#define ACTION_UP
Definition: mythuiactions.h:16
int safe_read(void *data, uint sz) override
uint64_t GetTotalReadPosition(void)
bool GetNameAndSerialNum(QString &name, QString &serialnum)
QList< BDOverlay * > m_overlayImages
Definition: bdringbuffer.h:216
uint32_t GetNumChapters(void)
#define ACTION_CHANNELDOWN
Definition: tv_actions.h:17
uint64_t GetChapterStartFrame(uint32_t chapter)
void ClickButton(int64_t pts, uint16_t x, uint16_t y)
QImage m_image
Definition: bdringbuffer.h:62
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool GetBDStateSnapshot(QString &state)
Get a snapshot of the current BD state.
static int iso639_str3_to_key(const unsigned char *iso639_2)
Definition: iso639.h:63
bool HandleBDEvents(void)
#define ACTION_RIGHT
Definition: mythuiactions.h:19
uint64_t GetCurrentAngle(void) const
Definition: bdringbuffer.h:101
int m_currentPlaylist
Definition: bdringbuffer.h:195
int iso639_key_to_canonical_key(int iso639_2)
Definition: iso639.cpp:120
void mythfile_open_register_callback(const char *pathname, void *object, callback_t func)
bool m_secondaryVideoIsFullscreen
Definition: bdringbuffer.h:208
uint64_t GetChapterStartTime(uint32_t chapter)
QVector< BDOverlay * > m_overlayPlanes
Definition: bdringbuffer.h:217
QThread * m_mainThread
Definition: bdringbuffer.h:233
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
Definition: ringbuffer.cpp:367
QString GetCountryCode() const
Definition: mythlocale.cpp:58
int GetCurrentTitle(void)
int m_currentSecondaryAudioStream
Definition: bdringbuffer.h:202
bool GoToMenu(const QString &str, int64_t pts)
jump to a Blu-ray root or popup menu
QString GetConfDir(void)
Definition: mythdirs.cpp:224
#define ACTION_SELECT
Definition: mythuiactions.h:15
uint64_t m_currentTime
Definition: bdringbuffer.h:189
BDRingBuffer(const QString &lfilename)
int64_t AdjustTimestamp(int64_t timestamp)
void HandleBDEvent(BD_EVENT &event)
#define ACTION_8
Definition: mythuiactions.h:12
This class is used as a container for messages.
Definition: mythevent.h:16
bool IsValidStream(int streamid)
~BDRingBuffer() override
int m_currentChapter
Definition: bdringbuffer.h:197
bool StartFromBeginning(void) override
off_t mythfile_seek(int fileID, off_t offset, int whence)
int m_currentPGTextSTStream
Definition: bdringbuffer.h:201
#define LOC
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: ringbuffer.cpp:694
#define ACTION_1
Definition: mythuiactions.h:5
bool SwitchPlaylist(uint32_t index)
QString GetSetting(const QString &key, const QString &defaultval="")
QReadWriteLock m_posLock
uint32_t GetNumTitles(void) const
Definition: bdringbuffer.h:99
int GetAudioLanguage(uint streamID)
void SubmitARGBOverlay(const bd_argb_overlay_s *overlay)
double GetFrameRate(void)
BLURAY * m_bdnav
Definition: bdringbuffer.h:174
void SubmitOverlay(const bd_overlay_s *overlay)
#define ACTION_7
Definition: mythuiactions.h:11
QHash< uint32_t, BLURAY_TITLE_INFO * > m_cachedPlaylistInfo
Definition: bdringbuffer.h:228
uint32_t m_mainTitle
Definition: bdringbuffer.h:182
bool SwitchAngle(uint angle)
void wipe()
void redirectBDIO()
bool m_isHDMVNavigation
Definition: bdringbuffer.h:175
volatile bool m_inMenu
Definition: bdringbuffer.h:221
unsigned int uint
Definition: compat.h:140
BDOverlay()=default
void ProgressUpdate(void)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
BLURAY_TITLE_INFO * m_currentTitleInfo
Definition: bdringbuffer.h:186
ssize_t mythfile_read(int fileID, void *buf, size_t count)
QString m_serialNumber
Definition: bdringbuffer.h:231
QMutex m_overlayLock
Definition: bdringbuffer.h:215
QHash< uint32_t, BLURAY_TITLE_INFO * > m_cachedTitleInfo
Definition: bdringbuffer.h:227
BLURAY_TITLE_INFO * GetPlaylistInfo(uint32_t index)
bool OpenFile(const QString &lfilename, uint retry_ms=kDefaultOpenTimeout) override
Opens a bluray device for reading.
MythMainWindow * GetMythMainWindow(void)
#define ACTION_MENUTEXT
Definition: tv_actions.h:82
void StopReads(void)
????
Definition: ringbuffer.cpp:711
bool IsInMenu(void) const override
Definition: bdringbuffer.h:117
int GetSubtitleLanguage(uint streamID)
int m_currentSecondaryVideoStream
Definition: bdringbuffer.h:203
static int _img_read(void *handle, void *buf, int lba, int num_blocks)
int GetNumSetting(const QString &key, int defaultval=0)
bool RestoreBDStateSnapshot(const QString &state)
Restore a BD snapshot.
uint32_t m_numTitles
Definition: bdringbuffer.h:180
bool TitleChanged(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static Type kUpdateTvProgressEventType
Definition: mythevent.h:75
uint8_t m_stillTime
Definition: bdringbuffer.h:219
unsigned short uint16_t
Definition: iso6937tables.h:1
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
bool m_topMenuSupported
Definition: bdringbuffer.h:177
bool m_isValid
Definition: bdringbuffer.h:48
static void file_opened_callback(void *bdr)
#define ACTION_4
Definition: mythuiactions.h:8
#define ACTION_9
Definition: mythuiactions.h:13
QString m_name
Definition: bdringbuffer.h:230
bool m_titleChanged
Definition: bdringbuffer.h:210
QString m_name
Definition: bdringbuffer.h:45
#define ACTION_SEEKRWND
Definition: tv_actions.h:42
void ClearOverlays(void)
uint64_t m_titlesize
Definition: bdringbuffer.h:187
QMutex m_infoLock
Definition: bdringbuffer.h:229
GoomState states[STATES_NB]
Definition: goom_core.c:52
bool m_firstPlaySupported
Definition: bdringbuffer.h:178
bool m_secondaryVideoEnabled
Definition: bdringbuffer.h:207
long long GetReadPosition(void) const override
Returns how far into the file we have read.
static void HandleOverlayCallback(void *data, const bd_overlay_s *const overlay)
static QString iso639_key_to_str3(int code)
Definition: iso639.h:46
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:179
QByteArray m_pendingData
Definition: bdringbuffer.h:224
uint64_t m_currentTitleLength
Definition: bdringbuffer.h:184
int GetTitleDuration(int title)
int m_currentIGStream
Definition: bdringbuffer.h:200
void ResetReadAhead(long long newinternal)
Restart the read-ahead thread at the 'newinternal' position.
Definition: ringbuffer.cpp:610
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:41
Implements a file/stream reader/writer.
static void usleep(unsigned long time)
Definition: mthread.cpp:348
#define ACTION_LEFT
Definition: mythuiactions.h:18
void setPalette(const BD_PG_PALETTE_ENTRY *palette)
int64_t m_timeDiff
Definition: bdringbuffer.h:225
#define BD_BLOCK_SIZE
Definition: bdringbuffer.h:6
#define ACTION_5
Definition: mythuiactions.h:9
int mythfile_close(int fileID)
#define ACTION_3
Definition: mythuiactions.h:7
BDInfo(const QString &filename)
QString m_serialnumber
Definition: bdringbuffer.h:46
uint8_t m_stillMode
Definition: bdringbuffer.h:220
bool m_tryHDMVNavigation
Definition: bdringbuffer.h:176
void GetDescForPos(QString &desc)
#define ACTION_DOWN
Definition: mythuiactions.h:17
static void HandleARGBOverlayCallback(void *data, const bd_argb_overlay_s *const overlay)
int m_currentAudioStream
Definition: bdringbuffer.h:199
bool m_ignorePlayerWait
Definition: bdringbuffer.h:213
static const BLURAY_STREAM_INFO * FindStream(int streamid, BLURAY_STREAM_INFO *streams, int streamCount)
Find the stream with the given ID from an array of streams.
QString m_lastError
Definition: bdringbuffer.h:47
bool IsInStillFrame(void) const override
BLURAY_TITLE_INFO * GetTitleInfo(uint32_t index)
bool UpdateTitleInfo(void)
bool m_pgTextSTEnabled
Definition: bdringbuffer.h:205
uint32_t GetCurrentChapter(void)