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(QString().sprintf("%02d", hours))
950  .arg(QString().sprintf("%02d", minutes))
951  .arg(QString().sprintf("%02.1f", secs));
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(QString().sprintf("%02d", i + 1))
977  .arg(QString().sprintf("%02d", hours))
978  .arg(QString().sprintf("%02d", minutes))
979  .arg(QString().sprintf("%06.3f", secs))
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  LOG(VB_GENERAL, LOG_WARNING, LOC +
1005  "Warning: more than 1 clip, following still "
1006  "frame analysis may be wrong");
1007 
1008  if (still == BLURAY_STILL_TIME)
1009  {
1010  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1011  QString("Entering still frame (%1 seconds) UNSUPPORTED").arg(time));
1012  bd_read_skip_still(m_bdnav);
1013  }
1014  else if (still == BLURAY_STILL_INFINITE)
1015  {
1016  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Entering infinite still frame.");
1017  }
1018 
1019  m_stillMode = still;
1020  m_stillTime = time;
1021 
1022  return true;
1023 }
1024 
1026 {
1027  bool ret = m_titleChanged;
1028  m_titleChanged = false;
1029  return ret;
1030 }
1031 
1033 {
1034  if (!m_bdnav)
1035  return false;
1036 
1037  LOG(VB_GENERAL, LOG_INFO, LOC +
1038  QString("Switching to Angle %1...").arg(angle));
1039  bd_seamless_angle_change(m_bdnav, angle);
1040  m_currentAngle = angle;
1041  return true;
1042 }
1043 
1045 {
1046  if (m_bdnav)
1047  return bd_get_title_size(m_bdnav);
1048  return 0;
1049 }
1050 
1051 int64_t BDRingBuffer::AdjustTimestamp(int64_t timestamp)
1052 {
1053  int64_t newTimestamp = timestamp;
1054 
1055  if (newTimestamp != AV_NOPTS_VALUE && newTimestamp >= m_timeDiff)
1056  {
1057  newTimestamp -= m_timeDiff;
1058  }
1059 
1060  return newTimestamp;
1061 }
1062 
1063 int BDRingBuffer::safe_read(void *data, uint sz)
1064 {
1065  int result = 0;
1066  if (m_isHDMVNavigation)
1067  {
1068  result = HandleBDEvents() ? 0 : -1;
1069  while (result == 0)
1070  {
1071  BD_EVENT event;
1072  result = bd_read_ext(m_bdnav,
1073  (unsigned char *)data,
1074  sz, &event);
1075  if (result == 0)
1076  {
1077  HandleBDEvent(event);
1078  result = HandleBDEvents() ? 0 : -1;
1079  }
1080  }
1081  }
1082  else
1083  {
1085  {
1086  processState_t lastState = m_processState;
1087 
1089  result = bd_read(m_bdnav, (unsigned char *)data, sz);
1090 
1091  HandleBDEvents();
1092 
1093  if (m_processState == PROCESS_WAIT && lastState == PROCESS_NORMAL)
1094  {
1095  // We're waiting for the decoder to drain its buffers
1096  // so don't give it any more data just yet.
1097  m_pendingData = QByteArray((const char*)data, result);
1098  result = 0;
1099  }
1100  else
1101  if (m_processState == PROCESS_NORMAL && lastState == PROCESS_REPROCESS)
1102  {
1103  // The decoder has finished draining its buffers so give
1104  // it that last block of data we read
1105  result = m_pendingData.size();
1106  memcpy(data, m_pendingData.constData(), result);
1107  m_pendingData.clear();
1108  }
1109  }
1110  }
1111 
1112  if (result < 0)
1113  StopReads();
1114 
1115  m_currentTime = bd_tell_time(m_bdnav);
1116  return result;
1117 }
1118 
1120 {
1121  QMutexLocker locker(&m_infoLock);
1122  if (m_bdnav && m_currentTitleInfo)
1123  {
1124  uint8_t rate = m_currentTitleInfo->clips->video_streams->rate;
1125  switch (rate)
1126  {
1127  case BLURAY_VIDEO_RATE_24000_1001:
1128  return 23.97;
1129  break;
1130  case BLURAY_VIDEO_RATE_24:
1131  return 24;
1132  break;
1133  case BLURAY_VIDEO_RATE_25:
1134  return 25;
1135  break;
1136  case BLURAY_VIDEO_RATE_30000_1001:
1137  return 29.97;
1138  break;
1139  case BLURAY_VIDEO_RATE_50:
1140  return 50;
1141  break;
1142  case BLURAY_VIDEO_RATE_60000_1001:
1143  return 59.94;
1144  break;
1145  default:
1146  return 0;
1147  break;
1148  }
1149  }
1150  return 0;
1151 }
1152 
1154 {
1155  QMutexLocker locker(&m_infoLock);
1156 
1157  int code = iso639_str3_to_key("und");
1158 
1159  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1160  {
1161  bd_clip& clip = m_currentTitleInfo->clips[0];
1162 
1163  const BLURAY_STREAM_INFO* stream = FindStream(streamID, clip.audio_streams, clip.audio_stream_count);
1164 
1165  if (stream)
1166  {
1167  const uint8_t* lang = stream->lang;
1168  code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
1169  }
1170  }
1171 
1172  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Audio Lang: 0x%1 Code: %2")
1173  .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
1174 
1175  return code;
1176 }
1177 
1179 {
1180  QMutexLocker locker(&m_infoLock);
1181 
1182  int code = iso639_str3_to_key("und");
1183 
1184  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1185  {
1186  bd_clip& clip = m_currentTitleInfo->clips[0];
1187 
1188  const BLURAY_STREAM_INFO* stream = FindStream(streamID, clip.pg_streams, clip.pg_stream_count);
1189 
1190  if (stream)
1191  {
1192  const uint8_t* lang = stream->lang;
1193  code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
1194  }
1195  }
1196 
1197  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Subtitle Lang: 0x%1 Code: %2")
1198  .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
1199 
1200  return code;
1201 }
1202 
1203 void BDRingBuffer::PressButton(int32_t key, int64_t pts)
1204 {
1205  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1206  QString("Key %1 (pts %2)").arg(key).arg(pts));
1207  // HACK for still frame menu navigation
1208  pts = 1;
1209 
1210  if (!m_bdnav || pts <= 0 || key < 0)
1211  return;
1212 
1213  bd_user_input(m_bdnav, pts, key);
1214 }
1215 
1217 {
1218  if (!m_bdnav)
1219  return;
1220 
1221  if (pts <= 0 || x == 0 || y == 0)
1222  return;
1223 
1224  bd_mouse_select(m_bdnav, pts, x, y);
1225 }
1226 
1229 bool BDRingBuffer::GoToMenu(const QString &str, int64_t pts)
1230 {
1231  if (!m_isHDMVNavigation || pts < 0)
1232  return false;
1233 
1234  if (!m_topMenuSupported)
1235  {
1236  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Top Menu not supported");
1237  return false;
1238  }
1239 
1240  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GoToMenu %1").arg(str));
1241 
1242  if (str.compare("root") == 0)
1243  {
1244  if (bd_menu_call(m_bdnav, pts))
1245  {
1246  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1247  QString("Invoked Top Menu (pts %1)").arg(pts));
1248  return true;
1249  }
1250  }
1251  else if (str.compare("popup") == 0)
1252  {
1253  PressButton(BD_VK_POPUP, pts);
1254  return true;
1255  }
1256  else
1257  return false;
1258 
1259  return false;
1260 }
1261 
1263 {
1265  {
1267  {
1269  // HandleBDEvent will change the process state
1270  // if it needs to so don't do it here.
1271  }
1272 
1273  while (m_processState == PROCESS_NORMAL && bd_get_event(m_bdnav, &m_lastEvent))
1274  {
1276  if (m_lastEvent.event == BD_EVENT_NONE ||
1277  m_lastEvent.event == BD_EVENT_ERROR)
1278  {
1279  return false;
1280  }
1281  }
1282  }
1283  return true;
1284 }
1285 
1287 {
1288  switch (ev.event) {
1289  case BD_EVENT_NONE:
1290  break;
1291  case BD_EVENT_ERROR:
1292  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1293  QString("EVENT_ERROR %1").arg(ev.param));
1294  break;
1295  case BD_EVENT_ENCRYPTED:
1296  LOG(VB_GENERAL, LOG_ERR, LOC +
1297  "EVENT_ENCRYPTED, playback will fail.");
1298  break;
1299 
1300  /* current playback position */
1301 
1302  case BD_EVENT_ANGLE:
1303  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1304  QString("EVENT_ANGLE %1").arg(ev.param));
1305  m_currentAngle = ev.param;
1306  break;
1307  case BD_EVENT_TITLE:
1308  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1309  QString("EVENT_TITLE %1 (old %2)")
1310  .arg(ev.param).arg(m_currentTitle));
1311  m_currentTitle = ev.param;
1312  break;
1313  case BD_EVENT_END_OF_TITLE:
1314  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1315  QString("EVENT_END_OF_TITLE %1").arg(m_currentTitle));
1316  WaitForPlayer();
1317  break;
1318  case BD_EVENT_PLAYLIST:
1319  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1320  QString("EVENT_PLAYLIST %1 (old %2)")
1321  .arg(ev.param).arg(m_currentPlaylist));
1322  m_currentPlaylist = ev.param;
1323  m_timeDiff = 0;
1324  m_currentPlayitem = 0;
1326  break;
1327  case BD_EVENT_PLAYITEM:
1328  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1329  QString("EVENT_PLAYITEM %1").arg(ev.param));
1330  {
1331  if (m_currentPlayitem != (int)ev.param)
1332  {
1333  int64_t out = m_currentTitleInfo->clips[m_currentPlayitem].out_time;
1334  int64_t in = m_currentTitleInfo->clips[ev.param].in_time;
1335  int64_t diff = in - out;
1336 
1337  if (diff != 0 && m_processState == PROCESS_NORMAL)
1338  {
1339  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
1340  .arg(in)
1341  .arg(out)
1342  .arg(diff));
1343 
1345  break;
1346  }
1347 
1348  m_timeDiff += diff;
1350  m_currentPlayitem = (int)ev.param;
1351  }
1352  }
1353  break;
1354  case BD_EVENT_CHAPTER:
1355  // N.B. event chapter numbering 1...N, chapter seeks etc 0...
1356  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_CHAPTER %1")
1357  .arg(ev.param));
1358  m_currentChapter = ev.param;
1359  break;
1360  case BD_EVENT_PLAYMARK:
1361  /* playmark reached */
1362  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PLAYMARK"));
1363  break;
1364 
1365  /* playback control */
1366  case BD_EVENT_PLAYLIST_STOP:
1367  /* HDMV VM or JVM stopped playlist playback. Flush all buffers. */
1368  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1369  QString("ToDo EVENT_PLAYLIST_STOP %1")
1370  .arg(ev.param));
1371  break;
1372 
1373  case BD_EVENT_STILL:
1374  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1375  QString("EVENT_STILL %1").arg(ev.param));
1376  break;
1377  case BD_EVENT_STILL_TIME:
1378  // we use the clip information to determine the still frame status
1379  // sleep a little
1380  usleep(10000);
1381  break;
1382  case BD_EVENT_SEEK:
1383  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SEEK"));
1384  break;
1385 
1386  /* stream selection */
1387 
1388  case BD_EVENT_AUDIO_STREAM:
1389  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_AUDIO_STREAM %1")
1390  .arg(ev.param));
1391  m_currentAudioStream = ev.param;
1392  break;
1393  case BD_EVENT_IG_STREAM:
1394  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_IG_STREAM %1")
1395  .arg(ev.param));
1396  m_currentIGStream = ev.param;
1397  break;
1398  case BD_EVENT_PG_TEXTST_STREAM:
1399  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1400  QString("EVENT_PG_TEXTST_STREAM %1").arg(ev.param));
1401  m_currentPGTextSTStream = ev.param;
1402  break;
1403  case BD_EVENT_SECONDARY_AUDIO_STREAM:
1404  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1405  QString("EVENT_SECONDARY_AUDIO_STREAM %1").arg(ev.param));
1406  m_currentSecondaryAudioStream = ev.param;
1407  break;
1408  case BD_EVENT_SECONDARY_VIDEO_STREAM:
1409  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1410  QString("EVENT_SECONDARY_VIDEO_STREAM %1").arg(ev.param));
1411  m_currentSecondaryVideoStream = ev.param;
1412  break;
1413 
1414  case BD_EVENT_PG_TEXTST:
1415  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PG_TEXTST %1")
1416  .arg(ev.param ? "enable" : "disable"));
1417  m_PGTextSTEnabled = (ev.param != 0U);
1418  break;
1419  case BD_EVENT_SECONDARY_AUDIO:
1420  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_AUDIO %1")
1421  .arg(ev.param ? "enable" : "disable"));
1422  m_secondaryAudioEnabled = (ev.param != 0U);
1423  break;
1424  case BD_EVENT_SECONDARY_VIDEO:
1425  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO %1")
1426  .arg(ev.param ? "enable" : "disable"));
1427  m_secondaryVideoEnabled = (ev.param != 0U);
1428  break;
1429  case BD_EVENT_SECONDARY_VIDEO_SIZE:
1430  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1431  QString("EVENT_SECONDARY_VIDEO_SIZE %1")
1432  .arg(ev.param==0 ? "PIP" : "fullscreen"));
1433  m_secondaryVideoIsFullscreen = (ev.param != 0U);
1434  break;
1435 
1436  /* status */
1437  case BD_EVENT_IDLE:
1438  /* Nothing to do. Playlist is not playing, but title applet is running.
1439  * Application should not call bd_read*() immediately again to avoid busy loop. */
1440  usleep(40000);
1441  break;
1442 
1443  case BD_EVENT_MENU:
1444  /* Interactive menu visible */
1445  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1446  QString("EVENT_MENU %1")
1447  .arg(ev.param==0 ? "no" : "yes"));
1448  m_inMenu = (ev.param == 1);
1449  break;
1450 
1451  case BD_EVENT_KEY_INTEREST_TABLE:
1452  /* BD-J key interest table changed */
1453  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1454  QString("ToDo EVENT_KEY_INTEREST_TABLE %1")
1455  .arg(ev.param));
1456  break;
1457 
1458  case BD_EVENT_UO_MASK_CHANGED:
1459  /* User operations mask was changed */
1460  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1461  QString("ToDo EVENT_UO_MASK_CHANGED %1")
1462  .arg(ev.param));
1463  break;
1464 
1465  default:
1466  LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Unknown Event! %1 %2")
1467  .arg(ev.event).arg(ev.param));
1468  break;
1469  }
1470 }
1471 
1473 {
1474  return m_stillTime > 0 && m_stillMode != BLURAY_STILL_NONE;
1475 }
1476 
1484 const BLURAY_STREAM_INFO* BDRingBuffer::FindStream(int streamid, BLURAY_STREAM_INFO* streams, int streamCount)
1485 {
1486  const BLURAY_STREAM_INFO* stream = nullptr;
1487 
1488  for(int i = 0; i < streamCount && !stream; i++)
1489  {
1490  if (streams[i].pid == streamid)
1491  stream = &streams[i];
1492  }
1493 
1494  return stream;
1495 }
1496 
1498 {
1499  bool valid = false;
1500 
1501  if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1502  {
1503  bd_clip& clip = m_currentTitleInfo->clips[0];
1504  if( FindStream(streamid,clip.audio_streams, clip.audio_stream_count) ||
1505  FindStream(streamid,clip.video_streams, clip.video_stream_count) ||
1506  FindStream(streamid,clip.ig_streams, clip.ig_stream_count) ||
1507  FindStream(streamid,clip.pg_streams, clip.pg_stream_count) ||
1508  FindStream(streamid,clip.sec_audio_streams, clip.sec_audio_stream_count) ||
1509  FindStream(streamid,clip.sec_video_streams, clip.sec_video_stream_count)
1510  )
1511  {
1512  valid = true;
1513  }
1514  }
1515 
1516  return valid;
1517 }
1518 
1520 {
1521  if (m_ignorePlayerWait)
1522  return;
1523 
1524  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
1525  m_playerWait = true;
1526  int count = 0;
1527  while (m_playerWait && count++ < 200)
1528  usleep(10000);
1529  if (m_playerWait)
1530  {
1531  LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
1532  m_playerWait = false;
1533  }
1534 }
1535 
1537 {
1538  if (m_bdnav && m_isHDMVNavigation)
1539  {
1540  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Starting from beginning...");
1541  return true; //bd_play(m_bdnav);
1542  }
1543  return true;
1544 }
1545 
1546 bool BDRingBuffer::GetNameAndSerialNum(QString &name, QString &serialnum)
1547 {
1548  if (!m_bdnav)
1549  return false;
1550 
1551  name = m_name;
1552  serialnum = m_serialNumber;
1553 
1554  return !serialnum.isEmpty();
1555 }
1556 
1560 {
1561  int title = GetCurrentTitle();
1562  uint64_t time = m_currentTime;
1563  uint64_t angle = GetCurrentAngle();
1564 
1565  if (title >= 0)
1566  {
1567  state = QString("title:%1,time:%2,angle:%3").arg(title)
1568  .arg(time)
1569  .arg(angle);
1570  }
1571  else
1572  {
1573  state.clear();
1574  }
1575 
1576  return(!state.isEmpty());
1577 }
1578 
1581 bool BDRingBuffer::RestoreBDStateSnapshot(const QString& state)
1582 {
1583  bool rc = false;
1584  QStringList states = state.split(",", QString::SkipEmptyParts);
1585  QHash<QString, uint64_t> settings;
1586 
1587  foreach (const QString& entry, states)
1588  {
1589  QStringList keyvalue = entry.split(":", QString::SkipEmptyParts);
1590 
1591  if (keyvalue.length() != 2)
1592  {
1593  LOG(VB_PLAYBACK, LOG_ERR, LOC +
1594  QString("Invalid BD state: %1 (%2)")
1595  .arg(entry).arg(state));
1596  }
1597  else
1598  {
1599  settings[keyvalue[0]] = keyvalue[1].toULongLong();
1600  //LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString( "%1 = %2" ).arg(keyvalue[0]).arg(keyvalue[1]));
1601  }
1602 
1603  }
1604 
1605  if (settings.contains("title") &&
1606  settings.contains("time") )
1607  {
1608  uint32_t title = (uint32_t)settings["title"];
1609  uint64_t time = settings["time"];
1610  uint64_t angle = 0;
1611 
1612  if (settings.contains("angle"))
1613  angle = settings["angle"];
1614 
1615  if(title != (uint32_t)m_currentTitle)
1616  SwitchTitle(title);
1617 
1618  SeekInternal(time, SEEK_SET);
1619 
1620  SwitchAngle(angle);
1621  rc = true;
1622  }
1623 
1624  return rc;
1625 }
1626 
1627 
1629 {
1630  QMutexLocker lock(&m_overlayLock);
1631 
1632  while (!m_overlayImages.isEmpty())
1633  {
1634  BDOverlay *overlay = m_overlayImages.takeFirst();
1635  delete overlay;
1636  overlay = nullptr;
1637  }
1638 
1639  for (int i = 0; i < m_overlayPlanes.size(); i++)
1640  {
1641  BDOverlay*& osd = m_overlayPlanes[i];
1642 
1643  if (osd)
1644  {
1645  delete osd;
1646  osd = nullptr;
1647  }
1648  }
1649 }
1650 
1652 {
1653  QMutexLocker lock(&m_overlayLock);
1654  if (!m_overlayImages.isEmpty())
1655  return m_overlayImages.takeFirst();
1656  return nullptr;
1657 }
1658 
1659 void BDRingBuffer::SubmitOverlay(const bd_overlay_s * const overlay)
1660 {
1661  if (!overlay || overlay->plane > m_overlayPlanes.size())
1662  return;
1663 
1664  LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1665  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd = %1, %2").arg(overlay->cmd).arg(overlay->plane));
1666  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay rect = (%1,%2,%3,%4)").arg(overlay->x).arg(overlay->y)
1667  .arg(overlay->w).arg(overlay->h));
1668  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1").arg(overlay->pts));
1669  LOG(VB_PLAYBACK, LOG_DEBUG, QString("update palette = %1").arg(overlay->palette_update_flag ? "yes":"no"));
1670 
1671  BDOverlay*& osd = m_overlayPlanes[overlay->plane];
1672 
1673  switch(overlay->cmd)
1674  {
1675  case BD_OVERLAY_INIT: /* init overlay plane. Size and position of plane in x,y,w,h */
1676  /* init overlay plane. Size of plane in w,h */
1677  delete osd;
1678  osd = new BDOverlay(overlay);
1679  break;
1680 
1681  case BD_OVERLAY_CLOSE: /* close overlay plane */
1682  /* close overlay */
1683  {
1684  if (osd)
1685  {
1686  delete osd;
1687  osd = nullptr;
1688  }
1689 
1690  QMutexLocker lock(&m_overlayLock);
1691  m_overlayImages.append(new BDOverlay());
1692  }
1693  break;
1694 
1695  /* following events can be processed immediately, but changes
1696  * should not be flushed to display before next FLUSH event
1697  */
1698  case BD_OVERLAY_HIDE: /* overlay is empty and can be hidden */
1699  case BD_OVERLAY_CLEAR: /* clear plane */
1700  if (osd)
1701  osd->wipe();
1702  break;
1703 
1704  case BD_OVERLAY_WIPE: /* clear area (x,y,w,h) */
1705  if (osd)
1706  osd->wipe(overlay->x, overlay->y, overlay->w, overlay->h);
1707  break;
1708 
1709  case BD_OVERLAY_DRAW: /* draw bitmap (x,y,w,h,img,palette,crop) */
1710  if (osd)
1711  {
1712  const BD_PG_RLE_ELEM *rlep = overlay->img;
1713  unsigned actual = overlay->w * overlay->h;
1714  uint8_t *data = osd->m_image.bits();
1715  data = &data[(overlay->y * osd->m_image.bytesPerLine()) + overlay->x];
1716 
1717  for (unsigned i = 0; i < actual; i += rlep->len, rlep++)
1718  {
1719  int dst_y = (i / overlay->w) * osd->m_image.bytesPerLine();
1720  int dst_x = (i % overlay->w);
1721  memset(data + dst_y + dst_x, rlep->color, rlep->len);
1722  }
1723 
1724  osd->setPalette(overlay->palette);
1725  }
1726  break;
1727 
1728  case BD_OVERLAY_FLUSH: /* all changes have been done, flush overlay to display at given pts */
1729  if (osd)
1730  {
1731  auto* newOverlay = new BDOverlay(*osd);
1732  newOverlay->m_image =
1733  osd->m_image.convertToFormat(QImage::Format_ARGB32);
1734  newOverlay->m_pts = overlay->pts;
1735 
1736  QMutexLocker lock(&m_overlayLock);
1737  m_overlayImages.append(newOverlay);
1738  }
1739  break;
1740 
1741  default:
1742  break;
1743  }
1744 }
1745 
1746 void BDRingBuffer::SubmitARGBOverlay(const bd_argb_overlay_s * const overlay)
1747 {
1748  if (!overlay || overlay->plane > m_overlayPlanes.size())
1749  return;
1750 
1751  LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1752  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd,plane = %1, %2").arg(overlay->cmd)
1753  .arg(overlay->plane));
1754  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->(x,y,w,h) = %1,%2,%3x%4 - %5").arg(overlay->x)
1755  .arg(overlay->y)
1756  .arg(overlay->w)
1757  .arg(overlay->h)
1758  .arg(overlay->stride));
1759  LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1").arg(overlay->pts));
1760 
1761  BDOverlay*& osd = m_overlayPlanes[overlay->plane];
1762 
1763  switch(overlay->cmd)
1764  {
1765  case BD_ARGB_OVERLAY_INIT:
1766  /* init overlay plane. Size of plane in w,h */
1767  delete osd;
1768  osd = new BDOverlay(overlay);
1769  break;
1770 
1771  case BD_ARGB_OVERLAY_CLOSE:
1772  /* close overlay */
1773  {
1774  if (osd)
1775  {
1776  delete osd;
1777  osd = nullptr;
1778  }
1779 
1780  QMutexLocker lock(&m_overlayLock);
1781  m_overlayImages.append(new BDOverlay());
1782  }
1783  break;
1784 
1785  /* following events can be processed immediately, but changes
1786  * should not be flushed to display before next FLUSH event
1787  */
1788  case BD_ARGB_OVERLAY_DRAW:
1789  if (osd)
1790  {
1791  /* draw image */
1792  uint8_t* data = osd->m_image.bits();
1793 
1794  uint32_t srcOffset = 0;
1795  uint32_t dstOffset = (overlay->y * osd->m_image.bytesPerLine()) + (overlay->x * 4);
1796 
1797  for (uint16_t y = 0; y < overlay->h; y++)
1798  {
1799  memcpy(&data[dstOffset],
1800  &overlay->argb[srcOffset],
1801  overlay->w * 4);
1802 
1803  dstOffset += osd->m_image.bytesPerLine();
1804  srcOffset += overlay->stride;
1805  }
1806  }
1807  break;
1808 
1809  case BD_ARGB_OVERLAY_FLUSH:
1810  /* all changes have been done, flush overlay to display at given pts */
1811  if (osd)
1812  {
1813  QMutexLocker lock(&m_overlayLock);
1814  auto* newOverlay = new BDOverlay(*osd);
1815  newOverlay->m_pts = overlay->pts;
1816  m_overlayImages.append(newOverlay);
1817  }
1818  break;
1819 
1820  default:
1821  LOG(VB_PLAYBACK, LOG_ERR, QString("Unknown ARGB overlay - %1").arg(overlay->cmd));
1822  break;
1823  }
1824 }
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
bool m_PGTextSTEnabled
Definition: bdringbuffer.h:205
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:121
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()
unsigned short uint16_t
Definition: iso6937tables.h:1
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:68
uint8_t m_stillTime
Definition: bdringbuffer.h:219
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)
uint32_t GetCurrentChapter(void)