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