MythTV  master
dvdringbuffer.cpp
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <zlib.h>
5 #undef Z_NULL
6 #define Z_NULL nullptr
7 
8 // Qt headers
9 #include <QCoreApplication>
10 #include <QDir>
11 
12 // MythTV headers
13 #include "mythconfig.h"
14 
15 #include "dvdringbuffer.h"
16 #include "mythcontext.h"
17 #include "mythmediamonitor.h"
18 #include "mythiowrapper.h"
19 #include "iso639.h"
20 
21 #include "mythdvdplayer.h"
22 #include "compat.h"
23 #include "mythlogging.h"
24 #include "mythuihelper.h"
25 #include "mythuiactions.h" // for ACTION_DOWN, ACTION_LEFT, etc
26 #include "tv_actions.h" // for ACTION_CHANNELDOWN, etc
27 
28 #define LOC QString("DVDRB: ")
29 
30 #define IncrementButtonVersion \
31  if (++m_buttonVersion > 1024) \
32  m_buttonVersion = 1;
33 
34 #define DVD_DRIVE_SPEED 1
35 
36 static const char *dvdnav_menu_table[] =
37 {
38  nullptr,
39  nullptr,
40  QT_TRANSLATE_NOOP("(DVD menu)", "Title Menu"),
41  QT_TRANSLATE_NOOP("(DVD menu)", "Root Menu"),
42  QT_TRANSLATE_NOOP("(DVD menu)", "Subpicture Menu"),
43  QT_TRANSLATE_NOOP("(DVD menu)", "Audio Menu"),
44  QT_TRANSLATE_NOOP("(DVD menu)", "Angle Menu"),
45  //: DVD part/chapter menu
46  QT_TRANSLATE_NOOP("(DVD menu)", "Part Menu")
47 };
48 
49 DVDInfo::DVDInfo(const QString &filename)
50 {
51  LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Trying %1").arg(filename));
52  QString name = filename;
53  if (name.startsWith("dvd:"))
54  {
55  name.remove(0,4);
56  while (name.startsWith("//"))
57  name.remove(0,1);
58  }
59 
60  QByteArray fname = name.toLocal8Bit();
61  dvdnav_status_t res = dvdnav_open(&m_nav, fname.constData());
62  if (res == DVDNAV_STATUS_ERR)
63  {
64  m_lastError = tr("Failed to open device at %1")
65  .arg(fname.constData());
66  LOG(VB_GENERAL, LOG_ERR, QString("DVDInfo: ") + m_lastError);
67  return;
68  }
69 
70  GetNameAndSerialNum(m_nav, m_name, m_serialnumber, name, QString("DVDInfo: "));
71 }
72 
74 {
75  if (m_nav)
76  dvdnav_close(m_nav);
77  LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Finishing."));
78 }
79 
80 void DVDInfo::GetNameAndSerialNum(dvdnav_t* nav,
81  QString &name,
82  QString &serialnum,
83  const QString &filename,
84  const QString &logPrefix)
85 {
86  const char* dvdname = nullptr;
87  const char* dvdserial = nullptr;
88 
89  dvdnav_status_t res = dvdnav_get_title_string(nav, &dvdname);
90  if (res == DVDNAV_STATUS_ERR)
91  LOG(VB_GENERAL, LOG_ERR, QString("%1Failed to get name.").arg(logPrefix));
92  res = dvdnav_get_serial_string(nav, &dvdserial);
93  if (res == DVDNAV_STATUS_ERR)
94  LOG(VB_GENERAL, LOG_ERR, QString("%1Failed to get serial number.").arg(logPrefix));
95 
96  name = QString(dvdname);
97  serialnum = QString(dvdserial);
98 
99  if (name.isEmpty() && serialnum.isEmpty())
100  {
101  struct stat stat {};
102  if ((mythfile_stat(filename.toLocal8Bit(), &stat) == 0) && S_ISDIR(stat.st_mode))
103  {
104  // Name and serial number are empty because we're reading
105  // from a directory (and not a device or image).
106 
107  // Use the directory name for the DVD name
108  QDir dir(filename);
109  name = dir.dirName();
110  LOG(VB_PLAYBACK, LOG_DEBUG, QString("%1Generated dvd name - %2")
111  .arg(logPrefix)
112  .arg(name));
113 
114  // And use the CRC of VTS_01_0.IFO as a serial number
115  QString ifo = filename + QString("/VIDEO_TS/VTS_01_0.IFO");
116  int fd = mythfile_open(ifo.toLocal8Bit(), O_RDONLY);
117 
118  if (fd > 0)
119  {
120  uint8_t buf[2048];
121  ssize_t read = 0;
122  uint32_t crc = crc32(0L, Z_NULL, 0);
123 
124  while((read = mythfile_read(fd, buf, sizeof(buf))) > 0)
125  crc = crc32(crc, buf, read);
126 
127  mythfile_close(fd);
128  serialnum = QString("%1__gen").arg(crc, 0, 16, QChar('0'));
129  LOG(VB_PLAYBACK, LOG_DEBUG,
130  QString("%1Generated serial number - %2")
131  .arg(logPrefix)
132  .arg(serialnum));
133  }
134  else
135  {
136  LOG(VB_GENERAL, LOG_ERR,
137  QString("%1Unable to open %2 to generate serial number")
138  .arg(logPrefix)
139  .arg(ifo));
140  }
141  }
142  }
143 }
144 
145 bool DVDInfo::GetNameAndSerialNum(QString &name, QString &serial)
146 {
147  name = m_name;
148  serial = m_serialnumber;
149  return !(name.isEmpty() && serial.isEmpty());
150 }
151 
152 MythDVDContext::MythDVDContext(const dsi_t& dsi, const pci_t& pci) :
153  ReferenceCounter("MythDVDContext"),
154  m_dsi(dsi),
155  m_pci(pci)
156 {
157 }
158 
163 {
164  return ((GetEndPTS() - GetStartPTS()) * GetFPS()) / 90000;
165 }
166 
171 {
172  int frames = 0;
173 
174  if (GetSeqEndPTS())
175  {
176  // Sequence end PTS is set. This means that video frames
177  // are not present all the way to 'End PTS'
178  frames = ((GetSeqEndPTS() - GetStartPTS()) * GetFPS()) / 90000;
179  }
180  else if (m_dsi.dsi_gi.vobu_1stref_ea != 0)
181  {
182  // At least one video frame is present
183  frames = GetNumFrames();
184  }
185 
186  return frames;
187 }
188 
194 {
195  uint32_t lba = m_dsi.vobu_sri.prev_video;
196 
197  if (lba != 0xbfffffff)
198  {
199  // If there is a previous video frame in this
200  // cell, calculate the absolute LBA from the
201  // offset
202  lba = GetLBA() - (lba & 0x7ffffff);
203  }
204 
205  return lba;
206 }
207 
208 DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
210 {
211  uint def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 };
212  uint seekValues[8] = { 1, 2, 4, 8, 10, 15, 20, 60 };
213 
214  for (uint i = 0; i < 8; i++)
215  m_seekSpeedMap.insert(def[i], seekValues[i]);
216 
217  OpenFile(lfilename);
218 }
219 
221 {
223 
224  CloseDVD();
225  m_menuBtnLock.lock();
227  m_menuBtnLock.unlock();
229 }
230 
232 {
233  QMutexLocker contextLocker(&m_contextLock);
234  m_rwLock.lockForWrite();
235  if (m_dvdnav)
236  {
237  SetDVDSpeed(-1);
238  dvdnav_close(m_dvdnav);
239  m_dvdnav = nullptr;
240  }
241 
242  if (m_context)
243  {
244  m_context->DecrRef();
245  m_context = nullptr;
246  }
247 
248  m_gotStop = false;
249  m_audioStreamsChanged = true;
250  m_rwLock.unlock();
251 }
252 
254 {
255  m_rwLock.lockForWrite();
256  foreach (QList<uint64_t> chapters, m_chapterMap)
257  chapters.clear();
258  m_chapterMap.clear();
259  m_rwLock.unlock();
260 }
261 
262 long long DVDRingBuffer::SeekInternal(long long pos, int whence)
263 {
264  long long ret = -1;
265 
266  m_posLock.lockForWrite();
267 
268  // Optimize no-op seeks
269  if (m_readAheadRunning &&
270  ((whence == SEEK_SET && pos == m_readPos) ||
271  (whence == SEEK_CUR && pos == 0)))
272  {
273  ret = m_readPos;
274 
275  m_posLock.unlock();
276 
277  return ret;
278  }
279 
280  // only valid for SEEK_SET & SEEK_CUR
281  long long new_pos = (SEEK_SET==whence) ? pos : m_readPos + pos;
282 
283  // Here we perform a normal seek. When successful we
284  // need to call ResetReadAhead(). A reset means we will
285  // need to refill the buffer, which takes some time.
286  if ((SEEK_END == whence) ||
287  ((SEEK_CUR == whence) && new_pos != 0))
288  {
289  errno = EINVAL;
290  ret = -1;
291  }
292  else
293  {
294  NormalSeek(new_pos);
295  ret = new_pos;
296  }
297 
298  if (ret >= 0)
299  {
300  m_readPos = ret;
301 
302  m_ignoreReadPos = -1;
303 
304  if (m_readAheadRunning)
306 
307  m_readAdjust = 0;
308  }
309  else
310  {
311  QString cmd = QString("Seek(%1, %2)").arg(pos)
312  .arg((whence == SEEK_SET) ? "SEEK_SET" :
313  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
314  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
315  }
316 
317  m_posLock.unlock();
318 
319  m_generalWait.wakeAll();
320 
321  return ret;
322 }
323 
324 long long DVDRingBuffer::NormalSeek(long long time)
325 {
326  QMutexLocker lock(&m_seekLock);
327  return Seek(time);
328 }
329 
330 bool DVDRingBuffer::SectorSeek(uint64_t sector)
331 {
332  dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
333 
334  QMutexLocker lock(&m_seekLock);
335 
336  dvdRet = dvdnav_sector_search(m_dvdnav, sector, SEEK_SET);
337 
338  if (dvdRet == DVDNAV_STATUS_ERR)
339  {
340  LOG(VB_PLAYBACK, LOG_ERR, LOC +
341  QString("SectorSeek() to sector %1 failed").arg(sector));
342  return false;
343  }
344  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
345  QString("DVD Playback SectorSeek() sector: %1").arg(sector));
346  return true;
347 }
348 
349 long long DVDRingBuffer::Seek(long long time)
350 {
351  dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
352 
353  int seekSpeed = 0;
354  int ffrewSkip = 1;
355  if (m_parent)
356  ffrewSkip = m_parent->GetFFRewSkip();
357 
358  if (ffrewSkip != 1 && ffrewSkip != 0 && time != 0)
359  {
360  QMap<uint, uint>::const_iterator it = m_seekSpeedMap.lowerBound(labs(time));
361  if (it == m_seekSpeedMap.end())
362  seekSpeed = *(it - 1);
363  else
364  seekSpeed = *it;
365  if (time < 0)
366  seekSpeed = -seekSpeed;
367  dvdRet = dvdnav_relative_time_search(m_dvdnav, seekSpeed);
368  }
369  else
370  {
371  m_seektime = time;
372  dvdRet = dvdnav_absolute_time_search(m_dvdnav, (uint64_t)m_seektime, 0);
373  }
374 
375  LOG(VB_PLAYBACK, LOG_DEBUG,
376  QString("DVD Playback Seek() time: %1; seekSpeed: %2")
377  .arg(time).arg(seekSpeed));
378 
379  if (dvdRet == DVDNAV_STATUS_ERR)
380  {
381  LOG(VB_PLAYBACK, LOG_ERR, LOC +
382  QString("Seek() to time %1 failed").arg(time));
383  return -1;
384  }
385  if (!m_inMenu)
386  {
387  m_gotStop = false;
388  if (time > 0 && ffrewSkip == 1)
389  m_seeking = true;
390  }
391 
392  return m_currentpos;
393 }
394 
396 {
397  return GetTotalTimeOfTitle() >= 120;
398 }
399 
401 {
402  // Don't allow seeking when the ringbuffer is
403  // waiting for the player to flush its buffers
404  // or waiting for the decoder.
405  return ((m_dvdEvent != DVDNAV_WAIT) &&
406  (m_dvdEvent != DVDNAV_HOP_CHANNEL) &&
408 }
409 
410 void DVDRingBuffer::GetDescForPos(QString &desc)
411 {
412  if (m_inMenu)
413  {
415  {
416  desc = QCoreApplication::translate("(DVD menu)",
418  }
419  }
420  else
421  {
422  desc = tr("Title %1 chapter %2").arg(m_title).arg(m_part);
423  }
424 }
425 
434 bool DVDRingBuffer::OpenFile(const QString &lfilename, uint /*retry_ms*/)
435 {
436  QMutexLocker contextLocker(&m_contextLock);
437  m_rwLock.lockForWrite();
438 
439  if (m_dvdnav)
440  {
441  m_rwLock.unlock();
442  CloseDVD();
443  m_rwLock.lockForWrite();
444  }
445 
446  m_safeFilename = lfilename;
447  m_filename = lfilename;
448  QByteArray fname = m_filename.toLocal8Bit();
449 
450  dvdnav_status_t res = dvdnav_open(&m_dvdnav, fname.constData());
451  if (res == DVDNAV_STATUS_ERR)
452  {
453  m_lastError = tr("Failed to open DVD device at %1").arg(m_filename);
454  LOG(VB_GENERAL, LOG_ERR,
455  LOC + QString("Failed to open DVD device at %1")
456  .arg(fname.constData()));
457  m_rwLock.unlock();
458  return false;
459  }
460 
461  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened DVD device at %1")
462  .arg(fname.constData()));
463 
464  if (m_context)
465  {
466  m_context->DecrRef();
467  m_context = nullptr;
468  }
469 
470  // Set preferred languages
471  QString lang = gCoreContext->GetSetting("Language").section('_', 0, 0);
472 
473  dvdnav_menu_language_select(m_dvdnav, lang.toLatin1().data());
474  dvdnav_audio_language_select(m_dvdnav, lang.toLatin1().data());
475  dvdnav_spu_language_select(m_dvdnav, lang.toLatin1().data());
476 
477  dvdnav_set_readahead_flag(m_dvdnav, 0);
478  dvdnav_set_PGC_positioning_flag(m_dvdnav, 1);
479 
480  // Check we aren't starting in a still frame (which will probably fail as
481  // ffmpeg will be unable to create a decoder)
482  if (dvdnav_get_next_still_flag(m_dvdnav))
483  {
484  LOG(VB_GENERAL, LOG_NOTICE,
485  LOC + "The selected title is a still frame. "
486  "Playback is likely to fail - please raise a bug report at "
487  "http://code.mythtv.org/trac");
488  }
489 
491 
492  SetDVDSpeed();
493 
494  LOG(VB_PLAYBACK, LOG_INFO, LOC +
495  QString("DVD Serial Number %1").arg(m_serialnumber));
496 
498  m_setSwitchToNext = false;
499  m_ateof = false;
500  m_commsError = false;
501  m_numFailures = 0;
502  m_rawBitrate = 8000;
503 
505 
506  m_rwLock.unlock();
507 
508  return true;
509 }
510 
512 {
513  LOG(VB_GENERAL, LOG_INFO, LOC + "Resetting DVD device.");
514 
515  // if a DVDNAV_STOP event has been emitted, dvdnav_reset does not
516  // seem to restore the state, hence we need to re-create
517  if (m_gotStop)
518  {
519  LOG(VB_GENERAL, LOG_ERR, LOC +
520  "DVD errored after initial scan - trying again");
521  CloseDVD();
523  if (!m_dvdnav)
524  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-open DVD.");
525  }
526 
527  if (m_dvdnav)
528  {
529  // Set preferred languages
530  QString lang = gCoreContext->GetSetting("Language").section('_', 0, 0);
531  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Setting DVD languages to %1")
532  .arg(lang));
533 
534  QMutexLocker lock(&m_seekLock);
535  dvdnav_reset(m_dvdnav);
536  dvdnav_menu_language_select(m_dvdnav, lang.toLatin1().data());
537  dvdnav_audio_language_select(m_dvdnav, lang.toLatin1().data());
538  dvdnav_spu_language_select(m_dvdnav, lang.toLatin1().data());
539  m_audioStreamsChanged = true;
540  }
541 
542  m_endPts = 0;
543  m_timeDiff = 0;
544 
545  QMutexLocker contextLocker(&m_contextLock);
546  if (m_context)
547  {
548  m_context->DecrRef();
549  m_context = nullptr;
550  }
551 
552  return m_dvdnav;
553 }
554 
555 void DVDRingBuffer::GetChapterTimes(QList<long long> &times)
556 {
557  if (!m_chapterMap.contains(m_title))
559 
560  if (!m_chapterMap.contains(m_title))
561  return;
562 
563  foreach (uint64_t chapter, m_chapterMap.value(m_title))
564  times.push_back(chapter);
565 }
566 
568 {
569  if (!m_dvdnav)
570  return 0;
571 
572  uint64_t duration = 0;
573  uint64_t *chaps = nullptr;
574  uint32_t num = dvdnav_describe_title_chapters(m_dvdnav, title,
575  &chaps, &duration);
576 
577  if (num < 1)
578  {
579  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve chapter data");
580  return 0;
581  }
582 
583  QList<uint64_t> chapters;
584  // add the start
585  chapters.append(0);
586  // don't add the last 'chapter' - which is the title end
587  if (num > 1)
588  {
589  for (uint i = 0; i < num - 1; i++)
590  chapters.append((chaps[i] + 45000) / 90000);
591  }
592  // Assigned via calloc, must be free'd not deleted
593  if (chaps)
594  free(chaps);
595  m_chapterMap.insert(title, chapters);
596  return (duration + 45000) / 90000;
597 }
598 
601 long long DVDRingBuffer::GetReadPosition(void) const
602 {
603  uint32_t pos = 0;
604  uint32_t length = 1;
605  if (m_dvdnav)
606  {
607  if (dvdnav_get_position(m_dvdnav, &pos, &length) == DVDNAV_STATUS_ERR)
608  {
609  // try one more time
610  dvdnav_get_position(m_dvdnav, &pos, &length);
611  }
612  }
613  return pos * DVD_BLOCK_SIZE;
614 }
615 
616 uint32_t DVDRingBuffer::AdjustTimestamp(uint32_t timestamp)
617 {
618  uint32_t newTimestamp = timestamp;
619 
620  if (newTimestamp >= m_timeDiff)
621  {
622  newTimestamp -= m_timeDiff;
623  }
624 
625  return newTimestamp;
626 }
627 
628 int64_t DVDRingBuffer::AdjustTimestamp(int64_t timestamp)
629 {
630  int64_t newTimestamp = timestamp;
631 
632  if (newTimestamp != AV_NOPTS_VALUE && newTimestamp >= m_timeDiff)
633  {
634  newTimestamp -= m_timeDiff;
635  }
636 
637  return newTimestamp;
638 }
639 
641 {
642  QMutexLocker contextLocker(&m_contextLock);
643 
644  if (m_context)
645  m_context->IncrRef();
646 
647  return m_context;
648 }
649 
651 {
652  if (!m_skipstillorwait)
653  {
654  LOG(VB_PLAYBACK, LOG_INFO,
655  LOC + "Waiting for player's buffers to drain");
656  m_playerWait = true;
657  int count = 0;
658  while (m_playerWait && count++ < 200)
659  {
660  m_rwLock.unlock();
661  usleep(10000);
662  m_rwLock.lockForWrite();
663  }
664 
665  if (m_playerWait)
666  {
667  LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
668  m_playerWait = false;
669  }
670  }
671 }
672 
673 int DVDRingBuffer::safe_read(void *data, uint sz)
674 {
675  unsigned char *blockBuf = nullptr;
676  uint tot = 0;
677  int needed = sz;
678  char *dest = (char*) data;
679  int offset = 0;
680  bool bReprocessing = false;
681  bool waiting = false;
682 
683  if (m_gotStop)
684  {
685  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read: called after DVDNAV_STOP");
686  errno = EBADF;
687  return -1;
688  }
689 
690  if (m_readAheadRunning)
691  LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running.");
692 
693  while ((m_processState != PROCESS_WAIT) && needed)
694  {
695  blockBuf = m_dvdBlockWriteBuf;
696 
698  {
700  bReprocessing = true;
701  }
702  else
703  {
704  m_dvdStat = dvdnav_get_next_cache_block(
705  m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize);
706 
707  bReprocessing = false;
708  }
709 
710  if (m_dvdStat == DVDNAV_STATUS_ERR)
711  {
712  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1")
713  .arg(dvdnav_err_to_string(m_dvdnav)));
714  errno = EIO;
715  return -1;
716  }
717 
718  switch (m_dvdEvent)
719  {
720  // Standard packet for decoding
721  case DVDNAV_BLOCK_OK:
722  {
723  // copy block
724  if (!m_seeking)
725  {
726  memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
727  tot += DVD_BLOCK_SIZE;
728  }
729 
730  // release buffer
731  if (blockBuf != m_dvdBlockWriteBuf)
732  dvdnav_free_cache_block(m_dvdnav, blockBuf);
733 
734  // debug
735  LOG(VB_PLAYBACK|VB_FILE, LOG_DEBUG, LOC + "DVDNAV_BLOCK_OK");
736  }
737  break;
738 
739  // cell change
740  case DVDNAV_CELL_CHANGE:
741  {
742  // get event details
743  auto *cell_event = (dvdnav_cell_change_event_t*) (blockBuf);
744 
745  // update information for the current cell
746  m_cellChanged = true;
747  if (m_pgcLength != cell_event->pgc_length)
748  m_pgcLengthChanged = true;
749  m_pgLength = cell_event->pg_length;
750  m_pgcLength = cell_event->pgc_length;
751  m_cellStart = cell_event->cell_start;
752  m_pgStart = cell_event->pg_start;
753 
754  // update title/part/still/menu information
756  m_lastPart = m_part;
758  uint32_t pos = 0;
759  uint32_t length = 0;
760  uint32_t stillTimer = dvdnav_get_next_still_flag(m_dvdnav);
761  m_still = 0;
762  m_titleParts = 0;
763  dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
764  dvdnav_get_number_of_parts(m_dvdnav, m_title, &m_titleParts);
765  dvdnav_get_position(m_dvdnav, &pos, &length);
766  dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
767 
768  if (m_title != m_lastTitle)
769  {
770  // Populate the chapter list for this title, used in the OSD menu
772  }
773 
774  m_titleLength = length * DVD_BLOCK_SIZE;
775  if (!m_seeking)
777 
778  // debug
779  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
780  QString("---- DVDNAV_CELL_CHANGE - Cell "
781  "#%1 Menu %2 Length %3")
782  .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
783  .arg((float)cell_event->cell_length / 90000.0F,0,'f',1));
784  QString still = stillTimer ? ((stillTimer < 0xff) ?
785  QString("Stillframe: %1 seconds").arg(stillTimer) :
786  QString("Infinite stillframe")) :
787  QString("Length: %1 seconds")
788  .arg((float)m_pgcLength / 90000.0F, 0, 'f', 1);
789  if (m_title == 0)
790  {
791  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Menu #%1 %2")
792  .arg(m_part).arg(still));
793  }
794  else
795  {
796  LOG(VB_PLAYBACK, LOG_INFO,
797  LOC + QString("Title #%1: %2 Part %3 of %4")
798  .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
799  }
800 
801  // wait unless it is a transition from one normal video cell to
802  // another or the same menu id
803  if ((m_title != m_lastTitle) &&
804  !((m_title == 0 && m_lastTitle == 0) &&
805  (m_part == m_lastPart)))
806  {
807  WaitForPlayer();
808  }
809 
810  // Make sure the still frame timer is reset.
811  if (m_parent)
812  {
814  }
815 
816  // clear menus/still frame selections
820  m_buttonSelected = false;
821  m_vobid = m_cellid = 0;
822  m_cellRepeated = false;
823  m_buttonSeenInCell = false;
824 
826 
827  // release buffer
828  if (blockBuf != m_dvdBlockWriteBuf)
829  dvdnav_free_cache_block(m_dvdnav, blockBuf);
830  }
831  break;
832 
833  // new colour lookup table for subtitles/menu buttons
834  case DVDNAV_SPU_CLUT_CHANGE:
835  {
836  // debug
837  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_SPU_CLUT_CHANGE");
838 
839  // store the new clut
840  memcpy(m_clut, blockBuf, 16 * sizeof(uint32_t));
841  // release buffer
842  if (blockBuf != m_dvdBlockWriteBuf)
843  dvdnav_free_cache_block(m_dvdnav, blockBuf);
844  }
845  break;
846 
847  // new Sub-picture Unit stream (subtitles/menu buttons)
848  case DVDNAV_SPU_STREAM_CHANGE:
849  {
850  // get event details
851  auto* spu = (dvdnav_spu_stream_change_event_t*)(blockBuf);
852 
853  // clear any existing subs/buttons
855 
856  // not sure
858  m_curSubtitleTrack = dvdnav_get_active_spu_stream(m_dvdnav);
859 
860  // debug
861  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
862  QString("DVDNAV_SPU_STREAM_CHANGE: "
863  "physicalwide %1, physicalletterbox %2, "
864  "physicalpanscan %3, currenttrack %4")
865  .arg(spu->physical_wide).arg(spu->physical_letterbox)
866  .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
867 
868  // release buffer
869  if (blockBuf != m_dvdBlockWriteBuf)
870  dvdnav_free_cache_block(m_dvdnav, blockBuf);
871  }
872  break;
873 
874  // the audio stream changed
875  case DVDNAV_AUDIO_STREAM_CHANGE:
876  {
877  // get event details
878  auto* audio = (dvdnav_audio_stream_change_event_t*)(blockBuf);
879 
880  // retrieve the new track
881  int new_track = GetAudioTrackNum(audio->physical);
882 
883  // debug
884  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
885  QString("DVDNAV_AUDIO_STREAM_CHANGE: old %1 new %2, physical %3, logical %4")
886  .arg(m_curAudioTrack).arg(new_track)
887  .arg(audio->physical).arg(audio->logical));
888 
889  // tell the decoder to reset the audio streams if necessary
890  if (new_track != m_curAudioTrack)
891  {
892  m_curAudioTrack = new_track;
893  m_audioStreamsChanged = true;
894  }
895 
896  // release buffer
897  if (blockBuf != m_dvdBlockWriteBuf)
898  dvdnav_free_cache_block(m_dvdnav, blockBuf);
899  }
900  break;
901 
902  // navigation packet
903  case DVDNAV_NAV_PACKET:
904  {
905  QMutexLocker lock(&m_seekLock);
906  bool lastInMenu = m_inMenu;
907 
908  // retrieve the latest Presentation Control and
909  // Data Search Information structures
910  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
911  dsi_t *dsi = dvdnav_get_current_nav_dsi(m_dvdnav);
912 
913  if (pci == nullptr || dsi == nullptr)
914  {
915  // Something has gone horribly wrong if this happens
916  LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDNAV_NAV_PACKET - Error retrieving DVD data structures - dsi 0x%1, pci 0x%2")
917  .arg((uint64_t)dsi,0,16)
918  .arg((uint64_t)pci,0,16));
919  }
920  else
921  {
922  // If the start PTS of this block is not the
923  // same as the end PTS of the last block,
924  // we've got a timestamp discontinuity
925  int64_t diff = (int64_t)pci->pci_gi.vobu_s_ptm - m_endPts;
926  if (diff != 0)
927  {
928  if (!bReprocessing && !m_skipstillorwait)
929  {
930  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
931  .arg(pci->pci_gi.vobu_s_ptm)
932  .arg(m_endPts)
933  .arg(diff));
934 
936  break;
937  }
938 
939  m_timeDiff += diff;
940  }
941 
942  m_endPts = pci->pci_gi.vobu_e_ptm;
943  m_inMenu = (pci->hli.hl_gi.btn_ns > 0);
944 
945  if (m_inMenu &&
946  m_seeking &&
947  (dsi->synci.sp_synca[0] & 0x80000000) &&
949  {
950  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Jumped into middle of menu: lba %1, dest %2")
951  .arg(pci->pci_gi.nv_pck_lbn)
952  .arg(pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff)));
953 
954  // We're in a menu, the subpicture packets are somewhere behind us
955  // and we've not decoded any subpicture.
956  // That probably means we've jumped into the middle of a menu.
957  // We'd better jump back to get the subpicture packet(s) otherwise
958  // there's no menu highlight to show.
959  m_seeking = false;
960  dvdnav_sector_search(m_dvdnav, pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff), SEEK_SET);
961  }
962  else
963  {
964  pci_t pci_copy = *pci;
965 
966  pci_copy.pci_gi.vobu_s_ptm = AdjustTimestamp(pci->pci_gi.vobu_s_ptm);
967  pci_copy.pci_gi.vobu_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_e_ptm);
968 
969  if (pci->pci_gi.vobu_se_e_ptm != 0)
970  pci_copy.pci_gi.vobu_se_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_se_e_ptm);
971 
972  QMutexLocker contextLocker(&m_contextLock);
973  if (m_context)
974  m_context->DecrRef();
975 
976  m_context = new MythDVDContext(*dsi, pci_copy);
977 
978  // get the latest nav
979  m_lastNav = (dvdnav_t *)blockBuf;
980 
981  if (m_inMenu != lastInMenu)
982  {
983  if (m_inMenu)
984  {
985  m_autoselectsubtitle = true;
987  }
988  else
990  }
991 
992  // if we are in a looping menu, we don't want to reset the
993  // selected button when we restart
994  m_vobid = dsi->dsi_gi.vobu_vob_idn;
995  m_cellid = dsi->dsi_gi.vobu_c_idn;
996  if ((m_lastvobid == m_vobid) && (m_lastcellid == m_cellid)
998  {
999  m_cellRepeated = true;
1000  }
1001 
1002  // update our status
1003  m_currentTime = dvdnav_get_current_time(m_dvdnav);
1005 
1006  if (m_seeking)
1007  {
1008  int relativetime =
1009  (int)((m_seektime - m_currentTime)/ 90000);
1010  if (abs(relativetime) <= 1)
1011  {
1012  m_seeking = false;
1013  m_seektime = 0;
1014  }
1015  else
1016  {
1017  dvdnav_relative_time_search(m_dvdnav, relativetime * 2);
1018  }
1019  }
1020 
1021  // update the button stream number if this is the
1022  // first NAV pack containing button information
1023  if ( (pci->hli.hl_gi.hli_ss & 0x03) == 0x01 )
1024  {
1025  m_buttonStreamID = 32;
1026  int aspect = dvdnav_get_video_aspect(m_dvdnav);
1027 
1028  // workaround where dvd menu is
1029  // present in VTS_DOMAIN. dvdnav adds 0x80 to stream id
1030  // proper fix should be put in dvdnav sometime
1031  int8_t spustream = dvdnav_get_active_spu_stream(m_dvdnav) & 0x7f;
1032 
1033  if (aspect != 0 && spustream > 0)
1034  m_buttonStreamID += spustream;
1035 
1036  m_buttonSeenInCell = true;
1037  }
1038 
1039  // debug
1040  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_NAV_PACKET - time:%1, lba:%2, vob:%3, cell:%4, seeking:%5, seektime:%6")
1041  .arg(m_context->GetStartPTS())
1042  .arg(m_context->GetLBA())
1043  .arg(m_vobid)
1044  .arg(m_cellid)
1045  .arg(m_seeking)
1046  .arg(m_seektime));
1047 
1048  if (!m_seeking)
1049  {
1050  memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
1051  tot += DVD_BLOCK_SIZE;
1052  }
1053  }
1054  }
1055  // release buffer
1056  if (blockBuf != m_dvdBlockWriteBuf)
1057  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1058  }
1059  break;
1060 
1061  case DVDNAV_HOP_CHANNEL:
1062  {
1063  if (!bReprocessing && !m_skipstillorwait)
1064  {
1065  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL - waiting");
1067  break;
1068  }
1069 
1070  // debug
1071  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL");
1072  WaitForPlayer();
1073  }
1074  break;
1075 
1076  // no op
1077  case DVDNAV_NOP:
1078  break;
1079 
1080  // new Video Title Set - aspect ratio/letterboxing
1081  case DVDNAV_VTS_CHANGE:
1082  {
1083  // retrieve event details
1084  auto* vts = (dvdnav_vts_change_event_t*)(blockBuf);
1085 
1086  // update player
1087  int aspect = dvdnav_get_video_aspect(m_dvdnav);
1088  if (aspect == 2) // 4:3
1089  m_forcedAspect = 4.0F / 3.0F;
1090  else if (aspect == 3) // 16:9
1091  m_forcedAspect = 16.0F / 9.0F;
1092  else
1093  m_forcedAspect = -1;
1094  int permission = dvdnav_get_video_scale_permission(m_dvdnav);
1095 
1096  // debug
1097  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1098  QString("DVDNAV_VTS_CHANGE: old_vtsN %1, new_vtsN %2, "
1099  "aspect %3, perm %4")
1100  .arg(vts->old_vtsN).arg(vts->new_vtsN)
1101  .arg(aspect).arg(permission));
1102 
1103  // trigger a rescan of the audio streams
1104  if ((vts->old_vtsN != vts->new_vtsN) ||
1105  (vts->old_domain != vts->new_domain))
1106  {
1107  m_audioStreamsChanged = true;
1108  }
1109 
1110  // Make sure we know we're not staying in the
1111  // same cell (same vobid/cellid values can
1112  // occur in every VTS)
1113  m_lastvobid = m_vobid = 0;
1114  m_lastcellid = m_cellid = 0;
1115 
1116  // release buffer
1117  if (blockBuf != m_dvdBlockWriteBuf)
1118  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1119  }
1120  break;
1121 
1122  // menu button
1123  case DVDNAV_HIGHLIGHT:
1124  {
1125  // retrieve details
1126  auto* hl = (dvdnav_highlight_event_t*)(blockBuf);
1127 
1128  // update the current button
1129  m_menuBtnLock.lock();
1130  DVDButtonUpdate(false);
1132  m_menuBtnLock.unlock();
1133 
1134  // debug
1135  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
1136  QString("DVDNAV_HIGHLIGHT: display %1, palette %2, "
1137  "sx %3, sy %4, ex %5, ey %6, pts %7, buttonN %8")
1138  .arg(hl->display).arg(hl->palette)
1139  .arg(hl->sx).arg(hl->sy)
1140  .arg(hl->ex).arg(hl->ey)
1141  .arg(hl->pts).arg(hl->buttonN));
1142 
1143  // release buffer
1144  if (blockBuf != m_dvdBlockWriteBuf)
1145  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1146  }
1147  break;
1148 
1149  // dvd still frame
1150  case DVDNAV_STILL_FRAME:
1151  {
1152  // retrieve still frame details (length)
1153  auto* still = (dvdnav_still_event_t*)(blockBuf);
1154 
1155  if (!bReprocessing && !m_skipstillorwait)
1156  {
1157  if (m_still != still->length)
1158  {
1159  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1) - waiting")
1160  .arg(still->length));
1161  }
1162 
1164  }
1165  else
1166  {
1167  // pause a little as the dvdnav VM will continue to return
1168  // this event until it has been skipped
1169  m_rwLock.unlock();
1170  usleep(10000);
1171  m_rwLock.lockForWrite();
1172 
1173  // when scanning the file or exiting playback, skip immediately
1174  // otherwise update the timeout in the player
1175  if (m_skipstillorwait)
1176  {
1177  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Skipping DVDNAV_STILL_FRAME (%1)")
1178  .arg(still->length));
1179  SkipStillFrame();
1180  }
1181  else if (m_parent)
1182  {
1183  // debug
1184  if (m_still != still->length)
1185  {
1186  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1)")
1187  .arg(still->length));
1188  }
1189 
1190  m_still = still->length;
1191  sz = tot;
1193  }
1194 
1195  // release buffer
1196  if (blockBuf != m_dvdBlockWriteBuf)
1197  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1198  }
1199  }
1200  break;
1201 
1202  // wait for the player
1203  case DVDNAV_WAIT:
1204  {
1205  if (!bReprocessing && !m_skipstillorwait && !waiting)
1206  {
1207  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT - waiting");
1209  }
1210  else
1211  {
1212  waiting = true;
1213 
1214  //debug
1215  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");
1216 
1217  // skip if required, otherwise wait (and loop)
1218  if (m_skipstillorwait)
1219  WaitSkip();
1220  else
1221  {
1222  m_dvdWaiting = true;
1223  m_rwLock.unlock();
1224  usleep(10000);
1225  m_rwLock.lockForWrite();
1226  }
1227 
1228  // release buffer
1229  if (blockBuf != m_dvdBlockWriteBuf)
1230  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1231  }
1232  }
1233  break;
1234 
1235  // exit playback
1236  case DVDNAV_STOP:
1237  {
1238  LOG(VB_GENERAL, LOG_INFO, LOC + "DVDNAV_STOP");
1239  sz = tot;
1240  m_gotStop = true;
1241 
1242  // release buffer
1243  if (blockBuf != m_dvdBlockWriteBuf)
1244  dvdnav_free_cache_block(m_dvdnav, blockBuf);
1245  }
1246  break;
1247 
1248  // this shouldn't happen
1249  default:
1250  {
1251  LOG(VB_GENERAL, LOG_ERR, LOC +
1252  QString("Unknown DVD event: %1").arg(m_dvdEvent));
1253  }
1254  break;
1255  }
1256 
1257  needed = sz - tot;
1258  offset = tot;
1259  }
1260 
1262  {
1263  errno = EAGAIN;
1264  return 0;
1265  }
1266  return tot;
1267 }
1268 
1270 {
1271  QMutexLocker lock(&m_seekLock);
1272  if (track < 1)
1273  Seek(0);
1274  else if (track < m_titleParts)
1275  dvdnav_part_play(m_dvdnav, m_title, track);
1276  else
1277  return false;
1278  m_gotStop = false;
1279  return true;
1280 }
1281 
1283 {
1284  int newPart = m_part + 1;
1285 
1286  QMutexLocker lock(&m_seekLock);
1287  if (newPart < m_titleParts)
1288  {
1289  dvdnav_part_play(m_dvdnav, m_title, newPart);
1290  m_gotStop = false;
1291  return true;
1292  }
1293  return false;
1294 }
1295 
1297 {
1298  int newPart = m_part - 1;
1299 
1300  QMutexLocker lock(&m_seekLock);
1301  if (newPart > 0)
1302  dvdnav_part_play(m_dvdnav, m_title, newPart);
1303  else
1304  Seek(0);
1305  m_gotStop = false;
1306 }
1307 
1312 {
1313  return lround(m_pgcLength / 90000.0);
1314 }
1315 
1319 {
1320  return m_cellStart / 90000;
1321 }
1322 
1326 {
1327  bool ret = m_cellChanged;
1328  m_cellChanged = false;
1329  return ret;
1330 }
1331 
1335 {
1336  bool ret = m_pgcLengthChanged;
1337  m_pgcLengthChanged = false;
1338  return ret;
1339 }
1340 
1342 {
1343  QMutexLocker locker(&m_seekLock);
1344  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Skipping still frame.");
1345 
1346  m_still = 0;
1347  dvdnav_still_skip(m_dvdnav);
1348 
1349  // Make sure the still frame timer is disabled.
1350  if (m_parent)
1351  {
1353  }
1354 }
1355 
1357 {
1358  QMutexLocker locker(&m_seekLock);
1359  dvdnav_wait_skip(m_dvdnav);
1360  m_dvdWaiting = false;
1361  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exiting DVDNAV_WAIT status");
1362 }
1363 
1366 bool DVDRingBuffer::GoToMenu(const QString &str)
1367 {
1368  DVDMenuID_t menuid = DVD_MENU_Escape;
1369  QMutexLocker locker(&m_seekLock);
1370 
1371  LOG(VB_PLAYBACK, LOG_INFO,
1372  LOC + QString("DVDRingBuf: GoToMenu %1").arg(str));
1373 
1374  if (str.compare("chapter") == 0)
1375  {
1376  menuid = DVD_MENU_Part;
1377  }
1378  else if (str.compare("root") == 0)
1379  {
1380  menuid = DVD_MENU_Root;
1381  }
1382  else if (str.compare("title") == 0)
1383  menuid = DVD_MENU_Title;
1384  else
1385  return false;
1386 
1387  dvdnav_status_t ret = dvdnav_menu_call(m_dvdnav, menuid);
1388  return ret == DVDNAV_STATUS_OK;
1389 }
1390 
1396 {
1397  bool success = false;
1398  QString target;
1399 
1400  QMutexLocker locker(&m_seekLock);
1401 
1402  if (dvdnav_is_domain_vts(m_dvdnav) && !m_inMenu)
1403  {
1404  if(dvdnav_go_up(m_dvdnav) == DVDNAV_STATUS_OK)
1405  {
1406  target = "GoUp";
1407  success = true;
1408  }
1409  else
1410  if(dvdnav_menu_call(m_dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
1411  {
1412  target = "Root";
1413  success = true;
1414  }
1415  else
1416  if(dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) == DVDNAV_STATUS_OK)
1417  {
1418  target = "Title";
1419  success = true;
1420  }
1421  else
1422  {
1423  target = "Nothing available";
1424  }
1425  }
1426  else
1427  {
1428  target = QString("No jump, %1 menu").arg(m_inMenu ? "in" : "not in");
1429  }
1430 
1431  LOG(VB_PLAYBACK, LOG_INFO,
1432  LOC + QString("DVDRingBuf: GoBack - %1").arg(target));
1433 
1434  return success;
1435 }
1436 
1438 {
1439  QMutexLocker locker(&m_seekLock);
1440  // This conditional appears to be unnecessary, and might have come
1441  // from a mistake in a libdvdnav resync.
1442  //if (!dvdnav_is_domain_vts(m_dvdnav))
1443  dvdnav_next_pg_search(m_dvdnav);
1444 }
1445 
1447 {
1448  QMutexLocker locker(&m_seekLock);
1449  // This conditional appears to be unnecessary, and might have come
1450  // from a mistake in a libdvdnav resync.
1451  //if (!dvdnav_is_domain_vts(m_dvdnav))
1452  dvdnav_prev_pg_search(m_dvdnav);
1453 }
1454 
1455 bool DVDRingBuffer::HandleAction(const QStringList &actions, int64_t pts)
1456 {
1457  (void)pts;
1458 
1459  if (!NumMenuButtons())
1460  return false;
1461 
1462  bool handled = true;
1463  if (actions.contains(ACTION_UP) ||
1464  actions.contains(ACTION_CHANNELUP))
1465  {
1466  MoveButtonUp();
1467  }
1468  else if (actions.contains(ACTION_DOWN) ||
1469  actions.contains(ACTION_CHANNELDOWN))
1470  {
1471  MoveButtonDown();
1472  }
1473  else if (actions.contains(ACTION_LEFT) ||
1474  actions.contains(ACTION_SEEKRWND))
1475  {
1476  MoveButtonLeft();
1477  }
1478  else if (actions.contains(ACTION_RIGHT) ||
1479  actions.contains(ACTION_SEEKFFWD))
1480  {
1481  MoveButtonRight();
1482  }
1483  else if (actions.contains(ACTION_SELECT))
1484  {
1485  ActivateButton();
1486  }
1487  else
1488  handled = false;
1489 
1490  return handled;
1491 }
1492 
1494 {
1495  if (NumMenuButtons() > 1)
1496  {
1497  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1498  dvdnav_left_button_select(m_dvdnav, pci);
1499  }
1500 }
1501 
1503 {
1504  if (NumMenuButtons() > 1)
1505  {
1506  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1507  dvdnav_right_button_select(m_dvdnav, pci);
1508  }
1509 }
1510 
1512 {
1513  if (NumMenuButtons() > 1)
1514  {
1515  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1516  dvdnav_upper_button_select(m_dvdnav, pci);
1517  }
1518 }
1519 
1521 {
1522  if (NumMenuButtons() > 1)
1523  {
1524  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1525  dvdnav_lower_button_select(m_dvdnav, pci);
1526  }
1527 }
1528 
1532 {
1533  if (NumMenuButtons() > 0)
1534  {
1536  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1537  dvdnav_button_activate(m_dvdnav, pci);
1538  }
1539 }
1540 
1543 void DVDRingBuffer::GetMenuSPUPkt(uint8_t *buf, int buf_size,
1544  int stream_id, uint32_t startTime)
1545 {
1546  if (buf_size < 4)
1547  return;
1548 
1549  if (m_buttonStreamID != stream_id)
1550  return;
1551 
1552  QMutexLocker lock(&m_menuBtnLock);
1553 
1555  auto *spu_pkt = (uint8_t*)av_malloc(buf_size);
1556  memcpy(spu_pkt, buf, buf_size);
1557  m_menuSpuPkt = spu_pkt;
1558  m_menuBuflength = buf_size;
1559  if (!m_buttonSelected)
1560  {
1562  m_buttonSelected = true;
1563  }
1564 
1565  if (DVDButtonUpdate(false))
1566  {
1567  int32_t gotbutton = 0;
1569  m_menuSpuPkt, m_menuBuflength, startTime);
1570  }
1571 }
1572 
1577 {
1578  // this is unlocked by ReleaseMenuButton
1579  m_menuBtnLock.lock();
1580 
1581  if ((m_menuBuflength > 4) && m_buttonExists && (NumMenuButtons() > 0))
1582  {
1584  return &(m_dvdMenuButton);
1585  }
1586 
1587  return nullptr;
1588 }
1589 
1590 
1592 {
1593  m_menuBtnLock.unlock();
1594 }
1595 
1599 {
1600  QRect rect(0,0,0,0);
1601  if (!m_buttonExists)
1602  return rect;
1603 
1604  rect.setRect(m_hl_button.x(), m_hl_button.y(), m_hl_button.width(),
1605  m_hl_button.height());
1606 
1607  return rect;
1608 }
1609 
1613 bool DVDRingBuffer::DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles,
1614  const uint8_t *spu_pkt, int buf_size, uint32_t startTime)
1615 {
1616  #define GETBE16(p) (((p)[0] << 8) | (p)[1])
1617 
1618  uint8_t alpha[4] = {0, 0, 0, 0};
1619  uint8_t palette[4] = {0, 0, 0, 0};
1620 
1621  if (!spu_pkt)
1622  return false;
1623 
1624  if (buf_size < 4)
1625  return false;
1626 
1627  bool force_subtitle_display = false;
1628  sub->rects = nullptr;
1629  sub->num_rects = 0;
1630  sub->start_display_time = startTime;
1631  sub->end_display_time = startTime;
1632 
1633  int cmd_pos = GETBE16(spu_pkt + 2);
1634  while ((cmd_pos + 4) < buf_size)
1635  {
1636  int offset1 = -1;
1637  int offset2 = -1;
1638  int date = GETBE16(spu_pkt + cmd_pos);
1639  int next_cmd_pos = GETBE16(spu_pkt + cmd_pos + 2);
1640  int pos = cmd_pos + 4;
1641  int x1 = 0;
1642  int x2 = 0;
1643  int y1 = 0;
1644  int y2 = 0;
1645  while (pos < buf_size)
1646  {
1647  int cmd = spu_pkt[pos++];
1648  switch(cmd)
1649  {
1650  case 0x00:
1651  force_subtitle_display = true;
1652  break;
1653  case 0x01:
1654  sub->start_display_time = ((date << 10) / 90) + startTime;
1655  break;
1656  case 0x02:
1657  sub->end_display_time = ((date << 10) / 90) + startTime;
1658  break;
1659  case 0x03:
1660  {
1661  if ((buf_size - pos) < 2)
1662  goto fail;
1663 
1664  palette[3] = spu_pkt[pos] >> 4;
1665  palette[2] = spu_pkt[pos] & 0x0f;
1666  palette[1] = spu_pkt[pos + 1] >> 4;
1667  palette[0] = spu_pkt[pos + 1] & 0x0f;
1668  pos +=2;
1669  }
1670  break;
1671  case 0x04:
1672  {
1673  if ((buf_size - pos) < 2)
1674  goto fail;
1675  alpha[3] = spu_pkt[pos] >> 4;
1676  alpha[2] = spu_pkt[pos] & 0x0f;
1677  alpha[1] = spu_pkt[pos + 1] >> 4;
1678  alpha[0] = spu_pkt[pos + 1] & 0x0f;
1679  pos +=2;
1680  }
1681  break;
1682  case 0x05:
1683  {
1684  if ((buf_size - pos) < 6)
1685  goto fail;
1686  x1 = (spu_pkt[pos] << 4) | (spu_pkt[pos + 1] >> 4);
1687  x2 = ((spu_pkt[pos + 1] & 0x0f) << 8) | spu_pkt[pos + 2];
1688  y1 = (spu_pkt[pos + 3] << 4) | (spu_pkt[pos + 4] >> 4);
1689  y2 = ((spu_pkt[pos + 4] & 0x0f) << 8) | spu_pkt[pos + 5];
1690  pos +=6;
1691  }
1692  break;
1693  case 0x06:
1694  {
1695  if ((buf_size - pos) < 4)
1696  goto fail;
1697  offset1 = GETBE16(spu_pkt + pos);
1698  offset2 = GETBE16(spu_pkt + pos + 2);
1699  pos +=4;
1700  }
1701  break;
1702  case 0x07:
1703  {
1704  if ((buf_size - pos) < 2)
1705  goto fail;
1706 
1707  pos += GETBE16(spu_pkt + pos);
1708  }
1709  break;
1710  case 0xff:
1711  default:
1712  goto the_end;
1713  }
1714  }
1715  the_end:
1716  if (offset1 >= 0)
1717  {
1718  int w = x2 - x1 + 1;
1719  if (w < 0)
1720  w = 0;
1721  int h = y2 - y1 + 1;
1722  if (h < 0)
1723  h = 0;
1724  if (w > 0 && h > 0)
1725  {
1726  if (sub->rects != nullptr)
1727  {
1728  for (uint i = 0; i < sub->num_rects; i++)
1729  {
1730  av_free(sub->rects[i]->data[0]);
1731  av_free(sub->rects[i]->data[1]);
1732  av_freep(&sub->rects[i]);
1733  }
1734  av_freep(&sub->rects);
1735  sub->num_rects = 0;
1736  }
1737 
1738  auto *bitmap = (uint8_t*) av_malloc(w * h);
1739  sub->num_rects = (NumMenuButtons() > 0) ? 2 : 1;
1740  sub->rects = (AVSubtitleRect **)
1741  av_mallocz(sizeof(AVSubtitleRect*) * sub->num_rects);
1742  for (uint i = 0; i < sub->num_rects; i++)
1743  {
1744  sub->rects[i] = (AVSubtitleRect *) av_mallocz(sizeof(AVSubtitleRect));
1745  }
1746  sub->rects[0]->data[1] = (uint8_t*)av_mallocz(4 * 4);
1747  decode_rle(bitmap, w * 2, w, (h + 1) / 2,
1748  spu_pkt, offset1 * 2, buf_size);
1749  decode_rle(bitmap + w, w * 2, w, h / 2,
1750  spu_pkt, offset2 * 2, buf_size);
1751  guess_palette((uint32_t*)sub->rects[0]->data[1], palette, alpha);
1752  sub->rects[0]->data[0] = bitmap;
1753  sub->rects[0]->x = x1;
1754  sub->rects[0]->y = y1;
1755  sub->rects[0]->w = w;
1756  sub->rects[0]->h = h;
1757  sub->rects[0]->type = SUBTITLE_BITMAP;
1758  sub->rects[0]->nb_colors = 4;
1759  sub->rects[0]->linesize[0] = w;
1760  if (NumMenuButtons() > 0)
1761  {
1762  sub->rects[1]->type = SUBTITLE_BITMAP;
1763  sub->rects[1]->data[1] = (uint8_t*)av_malloc(4 *4);
1764  guess_palette((uint32_t*)sub->rects[1]->data[1],
1766  }
1767  else
1769  *gotSubtitles = 1;
1770  }
1771  }
1772  if (next_cmd_pos == cmd_pos)
1773  break;
1774  cmd_pos = next_cmd_pos;
1775  }
1776  if (sub->num_rects > 0)
1777  {
1778  if (force_subtitle_display)
1779  {
1780  sub->forced = 1;
1781  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Decoded forced subtitle");
1782  }
1783  return true;
1784  }
1785 fail:
1786  return false;
1787 }
1788 
1793 {
1794  if (!m_parent)
1795  return false;
1796 
1797  QSize video_disp_dim = m_parent->GetVideoSize();
1798  int videoheight = video_disp_dim.height();
1799  int videowidth = video_disp_dim.width();
1800 
1801  int32_t button = 0;
1802  dvdnav_highlight_area_t hl;
1803  dvdnav_get_current_highlight(m_dvdnav, &button);
1804  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1805  dvdnav_status_t dvdRet =
1806  dvdnav_get_highlight_area_from_group(pci, DVD_BTN_GRP_Wide, button,
1807  static_cast<int32_t>(b_mode), &hl);
1808 
1809  if (dvdRet == DVDNAV_STATUS_ERR)
1810  return false;
1811 
1812  for (uint i = 0 ; i < 4 ; i++)
1813  {
1814  m_button_alpha[i] = 0xf & (hl.palette >> (4 * i ));
1815  m_button_color[i] = 0xf & (hl.palette >> (16+4 *i ));
1816  }
1817 
1818  // If the button overlay has already been decoded, make sure
1819  // the correct palette for the current highlight is set
1820  if (m_dvdMenuButton.rects && (m_dvdMenuButton.num_rects > 1))
1821  {
1822  guess_palette((uint32_t*)m_dvdMenuButton.rects[1]->data[1],
1824  }
1825 
1826  m_hl_button.setCoords(hl.sx, hl.sy, hl.ex, hl.ey);
1827 
1828  return ((hl.sx + hl.sy) > 0) &&
1829  (hl.sx < videowidth && hl.sy < videoheight);
1830 }
1831 
1835 {
1836  if (m_buttonExists || m_dvdMenuButton.rects)
1837  {
1838  for (uint i = 0; i < m_dvdMenuButton.num_rects; i++)
1839  {
1840  AVSubtitleRect* rect = m_dvdMenuButton.rects[i];
1841  av_free(rect->data[0]);
1842  av_free(rect->data[1]);
1843  av_free(rect);
1844  }
1845  av_free(m_dvdMenuButton.rects);
1846  m_dvdMenuButton.rects = nullptr;
1847  m_dvdMenuButton.num_rects = 0;
1848  m_buttonExists = false;
1849  }
1850 }
1851 
1856 {
1857  if (m_menuBuflength == 0)
1858  return;
1859 
1860  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clearing Menu SPU Packet" );
1861 
1862  ClearMenuButton();
1863 
1865  m_menuBuflength = 0;
1866  m_hl_button.setRect(0, 0, 0, 0);
1867 }
1868 
1870 {
1871  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1872  int numButtons = pci->hli.hl_gi.btn_ns;
1873  if (numButtons > 0 && numButtons < 36)
1874  return numButtons;
1875  return 0;
1876 }
1877 
1881 {
1882  uint audioLang = 0;
1883  int physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, idx);
1884 
1885  if (physicalStreamId >= 0)
1886  {
1887  uint16_t lang = dvdnav_audio_stream_to_lang(m_dvdnav, physicalStreamId);
1888  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1889  QString("Audio StreamID: %1; lang: %2").arg(idx).arg(lang));
1890  audioLang = ConvertLangCode(lang);
1891  }
1892  else
1893  {
1894  LOG(VB_PLAYBACK, LOG_WARNING, LOC +
1895  QString("Audio StreamID: %1 - not found!").arg(idx));
1896  }
1897 
1898  return audioLang;
1899 }
1900 
1907 {
1908  const uint AC3_OFFSET = 0x0080;
1909  const uint DTS_OFFSET = 0x0088;
1910  const uint LPCM_OFFSET = 0x00A0;
1911  const uint MP2_OFFSET = 0x01C0;
1912 
1913  int logical = -1;
1914 
1915  if (stream_id >= MP2_OFFSET) {
1916  stream_id -= MP2_OFFSET;
1917  } else if (stream_id >= LPCM_OFFSET) {
1918  stream_id -= LPCM_OFFSET;
1919  } else if (stream_id >= DTS_OFFSET) {
1920  stream_id -= DTS_OFFSET;
1921  } else if (stream_id >= AC3_OFFSET) {
1922  stream_id -= AC3_OFFSET;
1923  }
1924 
1925  for (int i = 0; i < 8; i++)
1926  {
1927  // Get the physical stream number at the given index
1928  // of the logical mapping table (function name is wrong!)
1929  int phys = dvdnav_get_audio_logical_stream(m_dvdnav, i);
1930 
1931  if ((uint)phys == stream_id)
1932  {
1933  logical = i;
1934  break;
1935  }
1936  }
1937 
1938  return logical;
1939 }
1940 
1942 {
1943  int ret = -1;
1944  audio_attr_t attributes;
1945 
1946  int physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, idx);
1947 
1948  if (physicalStreamId < 0)
1949  return -1;
1950 
1951  if (dvdnav_get_audio_attr(m_dvdnav, physicalStreamId, &attributes) == DVDNAV_STATUS_OK)
1952  {
1953  LOG(VB_AUDIO, LOG_INFO, QString("DVD Audio Track #%1 Language "
1954  "Extension Code - %2")
1955  .arg(idx)
1956  .arg(attributes.code_extension));
1957  ret = attributes.code_extension;
1958  }
1959 
1960  return ret;
1961 }
1962 
1966 {
1967  uint16_t lang = dvdnav_spu_stream_to_lang(m_dvdnav, id);
1968  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1969  QString("StreamID: %1; lang: %2").arg(id).arg(lang));
1970  return ConvertLangCode(lang);
1971 }
1972 
1977 {
1978  int logstream = -1;
1979 
1980  // VM always sets stream_id to zero if we're not in the VTS
1981  // domain and always returns 0 (instead of -1) if nothing has
1982  // been found, so only try to retrieve the logical stream if
1983  // we *are* in the VTS domain or we *are* trying to map stream
1984  // 0.
1985  if (dvdnav_is_domain_vts(m_dvdnav) || (stream_id == 0))
1986  logstream = dvdnav_get_spu_logical_stream(m_dvdnav, stream_id);
1987 
1988  return logstream;
1989 }
1990 
1994 {
1995  if (code == 0)
1996  return 0;
1997 
1998  QChar str2[2];
1999  str2[0] = QChar(code >> 8);
2000  str2[1] = QChar(code & 0xff);
2001  QString str3 = iso639_str2_to_str3(QString(str2, 2));
2002 
2003  LOG(VB_PLAYBACK, LOG_INFO, LOC +
2004  QString("code: %1; iso639: %2").arg(code).arg(str3));
2005 
2006  if (!str3.isEmpty())
2007  return iso639_str3_to_key(str3);
2008  return 0;
2009 }
2010 
2015 {
2016  pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
2017  int32_t button = pci->hli.hl_gi.fosl_btnn;
2018  if (button > 0 && !m_cellRepeated)
2019  {
2020  dvdnav_button_select(m_dvdnav,pci,button);
2021  return;
2022  }
2023  dvdnav_get_current_highlight(m_dvdnav,&button);
2024  if (button > 0 && button <= NumMenuButtons())
2025  dvdnav_button_select(m_dvdnav,pci,button);
2026  else
2027  dvdnav_button_select(m_dvdnav,pci,1);
2028 }
2029 
2035 {
2036  if (type == kTrackTypeSubtitle)
2037  {
2038  m_curSubtitleTrack = trackNo;
2039  m_autoselectsubtitle = (trackNo < 0);
2040  }
2041  else if (type == kTrackTypeAudio)
2042  {
2043  m_curAudioTrack = trackNo;
2044  dvdnav_set_active_audio_stream(m_dvdnav, trackNo);
2045  }
2046 }
2047 
2054 {
2055  if (type == kTrackTypeSubtitle)
2056  return m_curSubtitleTrack;
2057  if (type == kTrackTypeAudio)
2058  return m_curAudioTrack;
2059 
2060  return 0;
2061 }
2062 
2064 {
2065  uint8_t numChannels = 0U;
2066 
2067  int physical = dvdnav_get_audio_logical_stream(m_dvdnav, idx);
2068 
2069  if (physical >= 0)
2070  {
2071  unsigned char channels = dvdnav_audio_stream_channels(m_dvdnav, physical);
2072  if (channels != 0xff)
2073  numChannels = channels;
2074  }
2075 
2076  return numChannels;
2077 }
2078 
2081 bool DVDRingBuffer::GetNameAndSerialNum(QString& _name, QString& _serial)
2082 {
2083  _name = m_dvdname;
2084  _serial = m_serialnumber;
2085  return !(_name.isEmpty() && _serial.isEmpty());
2086 }
2087 
2091 {
2092  state.clear();
2093  char* dvdstate = dvdnav_get_state(m_dvdnav);
2094 
2095  if (dvdstate)
2096  {
2097  state = dvdstate;
2098  free(dvdstate);
2099  }
2100 
2101  return (!state.isEmpty());
2102 }
2103 
2107 {
2108  QByteArray ba_state = state.toUtf8();
2109 
2110  return (dvdnav_set_state(m_dvdnav, ba_state.constData()) == DVDNAV_STATUS_OK);
2111 }
2112 
2119 {
2120  double dvdfps = 0;
2121  int format = dvdnav_get_video_format(m_dvdnav);
2122 
2123  dvdfps = (format == 1)? 25.00 : 29.97;
2124  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Frame Rate %1").arg(dvdfps));
2125  return dvdfps;
2126 }
2127 
2132 {
2133  QMutexLocker lock(&m_seekLock);
2135 }
2136 
2140 {
2141  if (m_filename.startsWith("/"))
2142  MediaMonitor::SetCDSpeed(m_filename.toLocal8Bit().constData(), speed);
2143 }
2144 
2148 {
2149  return (GetTotalTimeOfTitle() - GetCurrentTime());
2150 }
2151 
2154 void DVDRingBuffer::guess_palette(uint32_t *rgba_palette,
2155  const uint8_t *palette,
2156  const uint8_t *alpha)
2157 {
2158  memset(rgba_palette, 0, 16);
2159 
2160  for (int i=0 ; i < 4 ; i++)
2161  {
2162  uint32_t yuv = m_clut[palette[i]];
2163  int y = ((yuv >> 16) & 0xff);
2164  int cr = ((yuv >> 8) & 0xff);
2165  int cb = ((yuv >> 0) & 0xff);
2166  int r = int(y + 1.4022 * (cr - 128));
2167  int b = int(y + 1.7710 * (cb - 128));
2168  int g = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)) ;
2169  if (r < 0) r = 0;
2170  if (g < 0) g = 0;
2171  if (b < 0) b = 0;
2172  if (r > 0xff) r = 0xff;
2173  if (g > 0xff) g = 0xff;
2174  if (b > 0xff) b = 0xff;
2175  rgba_palette[i] = ((alpha[i] * 17) << 24) | (r << 16 )| (g << 8) | b;
2176  }
2177 }
2178 
2182 int DVDRingBuffer::decode_rle(uint8_t *bitmap, int linesize, int w, int h,
2183  const uint8_t *buf, int nibble_offset, int buf_size)
2184 {
2185  int nibble_end = buf_size * 2;
2186  int x = 0;
2187  int y = 0;
2188  uint8_t *d = bitmap;
2189  for(;;) {
2190  if (nibble_offset >= nibble_end)
2191  return -1;
2192  uint v = get_nibble(buf, nibble_offset++);
2193  if (v < 0x4) {
2194  v = (v << 4) | get_nibble(buf, nibble_offset++);
2195  if (v < 0x10) {
2196  v = (v << 4) | get_nibble(buf, nibble_offset++);
2197  if (v < 0x040) {
2198  v = (v << 4) | get_nibble(buf, nibble_offset++);
2199  if (v < 4) {
2200  v |= (w - x) << 2;
2201  }
2202  }
2203  }
2204  }
2205  int len = v >> 2;
2206  if (len > (w - x))
2207  len = (w - x);
2208  int color = v & 0x03;
2209  memset(d + x, color, len);
2210  x += len;
2211  if (x >= w) {
2212  y++;
2213  if (y >= h)
2214  break;
2215  d += linesize;
2216  x = 0;
2217  nibble_offset += (nibble_offset & 1);
2218  }
2219  }
2220  return 0;
2221 }
2222 
2225 int DVDRingBuffer::get_nibble(const uint8_t *buf, int nibble_offset)
2226 {
2227  return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
2228 }
2229 
2234 int DVDRingBuffer::is_transp(const uint8_t *buf, int pitch, int n,
2235  const uint8_t *transp_color)
2236 {
2237  for (int i = 0; i < n; i++)
2238  {
2239  if (!transp_color[*buf])
2240  return 0;
2241  buf += pitch;
2242  }
2243  return 1;
2244 }
2245 
2252 {
2253  uint8_t transp_color[256] = { 0 };
2254 
2255  if (s->num_rects == 0 || s->rects == nullptr ||
2256  s->rects[0]->w <= 0 || s->rects[0]->h <= 0)
2257  {
2258  return 0;
2259  }
2260 
2261  for (int i = 0; i < s->rects[0]->nb_colors; i++)
2262  {
2263  if ((((uint32_t*)s->rects[0]->data[1])[i] >> 24) == 0)
2264  transp_color[i] = 1;
2265  }
2266 
2267  int y1 = 0;
2268  while (y1 < s->rects[0]->h &&
2269  is_transp(s->rects[0]->data[0] + y1 * s->rects[0]->linesize[0],
2270  1, s->rects[0]->w, transp_color))
2271  {
2272  y1++;
2273  }
2274 
2275  if (y1 == s->rects[0]->h)
2276  {
2277  av_freep(&s->rects[0]->data[0]);
2278  s->rects[0]->w = s->rects[0]->h = 0;
2279  return 0;
2280  }
2281 
2282  int y2 = s->rects[0]->h - 1;
2283  while (y2 > 0 &&
2284  is_transp(s->rects[0]->data[0] + y2 * s->rects[0]->linesize[0], 1,
2285  s->rects[0]->w, transp_color))
2286  {
2287  y2--;
2288  }
2289 
2290  int x1 = 0;
2291  while (x1 < (s->rects[0]->w - 1) &&
2292  is_transp(s->rects[0]->data[0] + x1, s->rects[0]->linesize[0],
2293  s->rects[0]->h, transp_color))
2294  {
2295  x1++;
2296  }
2297 
2298  int x2 = s->rects[0]->w - 1;
2299  while (x2 > 0 &&
2300  is_transp(s->rects[0]->data[0] + x2, s->rects[0]->linesize[0],
2301  s->rects[0]->h, transp_color))
2302  {
2303  x2--;
2304  }
2305 
2306  int w = x2 - x1 + 1;
2307  int h = y2 - y1 + 1;
2308  auto *bitmap = (uint8_t*) av_malloc(w * h);
2309  if (!bitmap)
2310  return 1;
2311 
2312  for (int y = 0; y < h; y++)
2313  {
2314  memcpy(bitmap + w * y, s->rects[0]->data[0] + x1 +
2315  (y1 + y) * s->rects[0]->linesize[0], w);
2316  }
2317 
2318  av_freep(&s->rects[0]->data[0]);
2319  s->rects[0]->data[0] = bitmap;
2320  s->rects[0]->linesize[0] = w;
2321  s->rects[0]->w = w;
2322  s->rects[0]->h = h;
2323  s->rects[0]->x += x1;
2324  s->rects[0]->y += y1;
2325  return 1;
2326 }
2327 
2329 {
2330  if (!m_dvdnav)
2331  return false;
2332 
2333  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switching to Angle %1...")
2334  .arg(angle));
2335  dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, (int32_t)angle);
2336  if (status == DVDNAV_STATUS_OK)
2337  {
2338  m_currentAngle = angle;
2339  return true;
2340  }
2341  return false;
2342 }
MythDVDContext()=delete
bool CellChanged(void)
check if dvd cell has changed
bool SectorSeek(uint64_t sector)
int safe_read(void *data, uint sz) override
void SetTrack(uint type, int trackNo)
set the dvd subtitle/audio track used
uint8_t m_button_color[4]
int mythfile_open(const char *pathname, int flags)
long long m_currentpos
QMap< uint, uint > m_seekSpeedMap
ISO 639-1 and ISO 639-2 support functions.
QString m_dvdname
int32_t m_titleParts
Encapsulates playback context at any given moment.
Definition: dvdringbuffer.h:32
#define LOC
int64_t m_currentTime
uint GetAudioLanguage(int idx)
get the audio language from the dvd
#define ACTION_CHANNELUP
Definition: tv_actions.h:16
bool nextTrack(void)
static void DisableScreensaver(void)
void GoToNextProgram(void)
uint32_t m_clut[16]
DVDInfo(const QString &filename)
#define DVD_MENU_MAX
Definition: dvdringbuffer.h:6
QMutex m_menuBtnLock
int GetSubtitleTrackNum(uint stream_id)
get the logical subtitle track/stream number from the dvd
bool DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles, const uint8_t *spu_pkt, int buf_size, uint32_t startTime)
generate dvd subtitle bitmap or dvd menu bitmap.
General purpose reference counter.
uint GetTotalTimeOfTitle(void)
get the total time of the title in seconds 90000 ticks = 1 sec
long long m_pgLength
bool PGCLengthChanged(void)
check if pgc length has changed
void ActivateButton(void)
action taken when a dvd menu button is selected
bool SwitchAngle(uint angle)
int GetAudioTrackType(uint idx)
#define ACTION_SEEKFFWD
Definition: tv_actions.h:43
QReadWriteLock m_rwLock
#define ACTION_UP
Definition: mythuiactions.h:16
bool RestoreDVDStateSnapshot(QString &state)
Restore a DVD VM from a snapshot.
#define Z_NULL
uint GetSubtitleLanguage(int id)
get the subtitle language from the dvd
QString m_lastError
Definition: dvdringbuffer.h:88
void SetDVDSpeed(void)
set dvd speed.
bool DVDButtonUpdate(bool b_mode)
update the dvd menu button parameters when a user changes the dvd menu button position
int GetFPS() const
Definition: dvdringbuffer.h:47
bool GetNameAndSerialNum(QString &name, QString &serialnum)
uint8_t m_button_alpha[4]
QMutex m_contextLock
#define ACTION_CHANNELDOWN
Definition: tv_actions.h:17
#define IncrementButtonVersion
QString m_serialnumber
Definition: dvdringbuffer.h:87
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
long long m_pgcLength
void SkipStillFrame(void)
void ClearMenuButton(void)
clears the dvd menu button structures
int32_t m_dvdEventSize
static int iso639_str3_to_key(const unsigned char *iso639_2)
Definition: iso639.h:63
QString m_name
Definition: dvdringbuffer.h:86
static int is_transp(const uint8_t *buf, int pitch, int n, const uint8_t *transp_color)
obtained from ffmpeg dvdsubdec.c used to find smallest bounded rectangle
int64_t m_timeDiff
#define ACTION_RIGHT
Definition: mythuiactions.h:19
uint32_t GetLBAPrevVideoFrame() const
Returns the logical block address of the previous VOBU containing video.
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
Definition: ringbuffer.cpp:367
void GetChapterTimes(QList< long long > &times)
QRect GetButtonCoords(void)
get coordinates of highlighted button
def read(device=None, features=[])
Definition: disc.py:35
long long m_cellstartPos
static int x4
Definition: mythsocket.cpp:63
int8_t m_curSubtitleTrack
void CloseDVD(void)
#define ACTION_SELECT
Definition: mythuiactions.h:15
static void SetCDSpeed(const char *device, int speed)
int64_t m_endPts
bool StartFromBeginning(void) override
DVDRingBuffer(const QString &lfilename)
QSize GetVideoSize(void) const
Definition: mythplayer.h:209
bool m_autoselectsubtitle
dvdnav_t * m_nav
Definition: dvdringbuffer.h:85
bool IsSeekingAllowed(void) override
#define GETBE16(p)
int GetTrack(uint type)
get the track the dvd should be playing.
virtual int IncrRef(void)
Increments reference count.
static const char * dvdnav_menu_table[]
bool m_audioStreamsChanged
static uint ConvertLangCode(uint16_t code)
converts the subtitle/audio lang code to iso639.
void GetDescForPos(QString &desc)
static int x2
Definition: mythsocket.cpp:61
uint TitleTimeLeft(void)
returns seconds left in the title
static const uint16_t * d
long long m_cellStart
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: ringbuffer.cpp:694
QString GetSetting(const QString &key, const QString &defaultval="")
QReadWriteLock m_posLock
dvdnav_t * m_dvdnav
uint32_t GetLBA() const
Definition: dvdringbuffer.h:43
int64_t GetStartPTS() const
Definition: dvdringbuffer.h:40
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
long long NormalSeek(long long time)
AVSubtitle m_dvdMenuButton
bool playTrack(int track)
MythDVDPlayer * m_parent
~DVDInfo(void)
int NumMenuButtons(void) const
void SetStillFrameTimeout(int Length)
bool m_pgcLengthChanged
bool m_buttonSeenInCell
long long m_pgStart
unsigned int uint
Definition: compat.h:140
void WaitForPlayer(void)
double GetFrameRate(void)
used by DecoderBase for the total frame number calculation for position map support and ffw/rew.
int32_t m_lastPart
static int decode_rle(uint8_t *bitmap, int linesize, int w, int h, const uint8_t *buf, int nibble_offset, int buf_size)
decodes the bitmap from the subtitle packet.
#define DVD_BLOCK_SIZE
Definition: dvdringbuffer.h:5
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:99
bool GoBack(void)
Attempts to back-up by trying to jump to the 'Go up' PGC, the root menu or the title menu in turn.
static int get_nibble(const uint8_t *buf, int nibble_offset)
copied from ffmpeg's dvdsubdec.c
void * av_malloc(unsigned int size)
int GetFFRewSkip(void) const
Definition: mythplayer.h:221
AVSubtitle * GetMenuSubtitle(uint &version)
returns dvd menu button information if available.
ssize_t mythfile_read(int fileID, void *buf, size_t count)
static int find_smallest_bounding_rectangle(AVSubtitle *s)
obtained from ffmpeg dvdsubdec.c used to find smallest bounded rect.
unsigned char m_dvdBlockWriteBuf[DVD_BLOCK_SIZE]
#define DVD_DRIVE_SPEED
void GoToPreviousProgram(void)
int GetNumFrames() const
Returns the duration of this VOBU in frames.
static void RestoreScreensaver(void)
bool OpenFile(const QString &lfilename, uint retry_ms=kDefaultOpenTimeout) override
Opens a dvd device for reading.
int32_t m_lastTitle
MythDVDContext * GetDVDContext(void)
uint8_t * m_menuSpuPkt
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void WaitSkip(void)
uint8_t GetNumAudioChannels(int idx)
bool GetNameAndSerialNum(QString &_name, QString &_serialnum)
Get the dvd title and serial num.
QMap< uint, QList< uint64_t > > m_chapterMap
unsigned short uint16_t
Definition: iso6937tables.h:1
uint GetCellStart(void)
get the start of the cell in seconds
QString iso639_str2_to_str3(const QString &str2)
Definition: iso639.cpp:69
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
void guess_palette(uint32_t *rgba_palette, const uint8_t *palette, const uint8_t *alpha)
converts palette values from YUV to RGB
bool m_skipstillorwait
int64_t m_seektime
void prevTrack(void)
#define ACTION_SEEKRWND
Definition: tv_actions.h:42
void ClearMenuSPUParameters(void)
clears the menu SPU pkt and parameters.
void MoveButtonRight(void)
void MoveButtonUp(void)
void ClearChapterCache(void)
void MoveButtonLeft(void)
int64_t GetEndPTS() const
Definition: dvdringbuffer.h:41
QString m_serialnumber
int GetNumFramesPresent() const
Returns the number of video frames present in this VOBU.
const char * frames[3]
Definition: element.c:46
int64_t GetSeqEndPTS() const
Definition: dvdringbuffer.h:42
void av_free(void *ptr)
dvdnav_t * m_lastNav
void ResetReadAhead(long long newinternal)
Restart the read-ahead thread at the 'newinternal' position.
Definition: ringbuffer.cpp:610
bool m_lastButtonSeenInCell
Implements a file/stream reader/writer.
float m_forcedAspect
static void usleep(unsigned long time)
Definition: mthread.cpp:348
~DVDRingBuffer() override
#define ACTION_LEFT
Definition: mythuiactions.h:18
long long Seek(long long time)
void MoveButtonDown(void)
void SelectDefaultButton(void)
determines the default dvd menu button to show when you initially access the dvd menu.
int mythfile_close(int fileID)
long long m_titleLength
int GetAudioTrackNum(uint stream_id)
get the logical track index (into PGC_AST_CTL) of the element that maps the given physical stream id.
MythDVDContext * m_context
dvdnav_status_t m_dvdStat
int64_t GetCurrentTime(void)
long long GetReadPosition(void) const override
returns current position in the PGC.
int32_t m_dvdEvent
static uint32_t crc32(const unsigned char *data, int len)
Definition: dsmcc.cpp:613
#define ACTION_DOWN
Definition: mythuiactions.h:17
uint32_t AdjustTimestamp(uint32_t timestamp)
long long SeekInternal(long long pos, int whence) override
bool HandleAction(const QStringList &actions, int64_t pts) override
void GetMenuSPUPkt(uint8_t *buf, int buf_size, int stream_id, uint32_t startTime)
get SPU pkt from dvd menu subtitle stream
bool GoToMenu(const QString &str)
jump to a dvd root or chapter menu
bool IsBookmarkAllowed(void) override
bool GetDVDStateSnapshot(QString &state)
Get a snapshot of the current DVD VM state.
static int x1
Definition: mythsocket.cpp:60
int mythfile_stat(const char *path, struct stat *buf)
void ReleaseMenuButton(void)
processState_t m_processState
int m_currentTitleAngleCount