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