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