MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
dvdringbuffer.cpp
Go to the documentation of this file.
1 #include <unistd.h>
2 #include <stdlib.h>
3 
4 #include "mythconfig.h"
5 
6 #include "dvdringbuffer.h"
7 #include "mythcontext.h"
8 #include "mythmediamonitor.h"
9 #include "iso639.h"
10 
11 #include "mythdvdplayer.h"
12 #include "compat.h"
13 #include "mythlogging.h"
14 #include "mythuihelper.h"
15 
16 #define LOC QString("DVDRB: ")
17 
18 #define IncrementButtonVersion \
19  if (++m_buttonVersion > 1024) \
20  m_buttonVersion = 1;
21 
22 #define DVD_DRIVE_SPEED 1
23 
24 static const char *dvdnav_menu_table[] =
25 {
26  NULL,
27  NULL,
28  "Title",
29  "Root",
30  "Subpicture",
31  "Audio",
32  "Angle",
33  "Part",
34 };
35 
36 DVDInfo::DVDInfo(const QString &filename)
37  : m_nav(NULL), m_name(NULL), m_serialnumber(NULL)
38 {
39  LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Trying %1").arg(filename));
40  QString name = filename;
41  if (name.startsWith("dvd://"))
42  name.remove(0,5);
43  else if (name.startsWith("dvd:/"))
44  name.remove(0,4);
45  else if (name.startsWith("dvd:"))
46  name.remove(0,4);
47 
48  QByteArray fname = name.toLocal8Bit();
49  dvdnav_status_t res = dvdnav_open(&m_nav, fname.constData());
50  if (res == DVDNAV_STATUS_ERR)
51  {
52  LOG(VB_GENERAL, LOG_ERR, QString("DVDInfo: Failed to open device at %1")
53  .arg(fname.constData()));
54  return;
55  }
56 
58  if (res == DVDNAV_STATUS_ERR)
59  LOG(VB_GENERAL, LOG_ERR, "DVDInfo: Failed to get name.");
61  if (res == DVDNAV_STATUS_ERR)
62  LOG(VB_GENERAL, LOG_ERR, "DVDInfo: Failed to get serial number.");
63 }
64 
66 {
67  if (m_nav)
69  LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Finishing."));
70 }
71 
72 bool DVDInfo::GetNameAndSerialNum(QString &name, QString &serial)
73 {
74  name = QString(m_name);
75  serial = QString(m_serialnumber);
76  if (name.isEmpty() && serial.isEmpty())
77  return false;
78  return true;
79 }
80 
81 DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
83  m_dvdnav(NULL), m_dvdBlockReadBuf(NULL),
84  m_dvdBlockRPos(0), m_dvdBlockWPos(0),
85  m_pgLength(0), m_pgcLength(0),
86  m_cellStart(0), m_cellChanged(false),
87  m_pgcLengthChanged(false), m_pgStart(0),
88  m_currentpos(0),
89  m_lastNav(NULL), m_part(0), m_lastPart(0),
90  m_title(0), m_lastTitle(0), m_playerWait(false),
91  m_titleParts(0), m_gotStop(false), m_currentAngle(0),
92  m_currentTitleAngleCount(0),
93  m_endPts(0), m_timeDiff(0),
94  m_newSequence(false),
95  m_still(0), m_lastStill(0),
96  m_audioStreamsChanged(false),
97  m_dvdWaiting(false),
98  m_titleLength(0),
99 
100  m_skipstillorwait(true),
101  m_cellstartPos(0), m_buttonSelected(false),
102  m_buttonExists(false),
103  m_buttonSeenInCell(false), m_lastButtonSeenInCell(false),
104  m_cellid(0), m_lastcellid(0),
105  m_vobid(0), m_lastvobid(0),
106  m_cellRepeated(false),
107 
108  m_curAudioTrack(0),
109  m_curSubtitleTrack(0),
110  m_autoselectsubtitle(true),
111  m_dvdname(NULL), m_serialnumber(NULL),
112  m_seeking(false), m_seektime(0),
113  m_currentTime(0),
114  m_parent(NULL),
115  m_forcedAspect(-1.0f),
116  m_processState(PROCESS_NORMAL),
117  m_dvdStat(DVDNAV_STATUS_OK),
118  m_dvdEvent(0),
119  m_dvdEventSize(0),
120 
121  // Menu/buttons
122  m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0),
123  m_hl_button(0, 0, 0, 0), m_menuSpuPkt(0), m_menuBuflength(0)
124 {
125  memset(&m_dvdMenuButton, 0, sizeof(AVSubtitle));
126  memset(m_dvdBlockWriteBuf, 0, sizeof(char) * DVD_BLOCK_SIZE);
127  memset(m_clut, 0, sizeof(uint32_t) * 16);
128  memset(m_button_color, 0, sizeof(uint8_t) * 4);
129  memset(m_button_alpha, 0, sizeof(uint8_t) * 4);
130  uint def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 };
131  uint seekValues[8] = { 1, 2, 4, 8, 10, 15, 20, 60 };
132 
133  for (uint i = 0; i < 8; i++)
134  m_seekSpeedMap.insert(def[i], seekValues[i]);
135 
136  OpenFile(lfilename);
137 }
138 
140 {
142 
143  CloseDVD();
144  m_menuBtnLock.lock();
146  m_menuBtnLock.unlock();
148 }
149 
151 {
152  rwlock.lockForWrite();
153  if (m_dvdnav)
154  {
155  SetDVDSpeed(-1);
157  m_dvdnav = NULL;
158  }
159  m_gotStop = false;
160  m_audioStreamsChanged = true;
161  rwlock.unlock();
162 }
163 
165 {
166  rwlock.lockForWrite();
167  foreach (QList<uint64_t> chapters, m_chapterMap)
168  chapters.clear();
169  m_chapterMap.clear();
170  rwlock.unlock();
171 }
172 
173 long long DVDRingBuffer::Seek(long long pos, int whence, bool has_lock)
174 {
175  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(%1,%2,%3)")
176  .arg(pos).arg((whence == SEEK_SET) ? "SEEK_SET":
177  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"))
178  .arg(has_lock ? "locked" : "unlocked"));
179 
180  long long ret = -1;
181 
182  // lockForWrite takes priority over lockForRead, so this will
183  // take priority over the lockForRead in the read ahead thread.
184  if (!has_lock)
185  rwlock.lockForWrite();
186 
187  poslock.lockForWrite();
188 
189  // Optimize no-op seeks
190  if (readaheadrunning &&
191  ((whence == SEEK_SET && pos == readpos) ||
192  (whence == SEEK_CUR && pos == 0)))
193  {
194  ret = readpos;
195 
196  poslock.unlock();
197  if (!has_lock)
198  rwlock.unlock();
199 
200  return ret;
201  }
202 
203  // only valid for SEEK_SET & SEEK_CUR
204  long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
205 
206  // Here we perform a normal seek. When successful we
207  // need to call ResetReadAhead(). A reset means we will
208  // need to refill the buffer, which takes some time.
209  if ((SEEK_END == whence) ||
210  ((SEEK_CUR == whence) && new_pos != 0))
211  {
212  errno = EINVAL;
213  ret = -1;
214  }
215  else
216  {
217  NormalSeek(new_pos);
218  ret = new_pos;
219  }
220 
221  if (ret >= 0)
222  {
223  readpos = ret;
224 
225  ignorereadpos = -1;
226 
227  if (readaheadrunning)
229 
230  readAdjust = 0;
231  }
232  else
233  {
234  QString cmd = QString("Seek(%1, %2)").arg(pos)
235  .arg((whence == SEEK_SET) ? "SEEK_SET" :
236  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
237  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
238  }
239 
240  poslock.unlock();
241 
242  generalWait.wakeAll();
243 
244  if (!has_lock)
245  rwlock.unlock();
246 
247  return ret;
248 }
249 
250 long long DVDRingBuffer::NormalSeek(long long time)
251 {
252  QMutexLocker lock(&m_seekLock);
253  return Seek(time);
254 }
255 
256 long long DVDRingBuffer::Seek(long long time)
257 {
258  dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
259 
260  int seekSpeed = 0;
261  int ffrewSkip = 1;
262  if (m_parent)
263  ffrewSkip = m_parent->GetFFRewSkip();
264 
265  if (ffrewSkip != 1 && ffrewSkip != 0 && time != 0)
266  {
267  QMap<uint, uint>::const_iterator it = m_seekSpeedMap.lowerBound(labs(time));
268  if (it == m_seekSpeedMap.end())
269  seekSpeed = *(it - 1);
270  else
271  seekSpeed = *it;
272  if (time < 0)
273  seekSpeed = -seekSpeed;
274  dvdRet = dvdnav_relative_time_search(m_dvdnav, seekSpeed);
275  }
276  else
277  {
278  m_seektime = (uint64_t)time;
280  }
281 
282  LOG(VB_PLAYBACK, LOG_DEBUG,
283  QString("DVD Playback Seek() time: %1; seekSpeed: %2")
284  .arg(time).arg(seekSpeed));
285 
286  if (dvdRet == DVDNAV_STATUS_ERR)
287  {
288  LOG(VB_PLAYBACK, LOG_ERR, LOC +
289  QString("Seek() to time %1 failed").arg(time));
290  return -1;
291  }
292  else if (!m_inMenu)
293  {
294  m_gotStop = false;
295  if (time > 0 && ffrewSkip == 1)
296  m_seeking = true;
297  }
298 
299  return m_currentpos;
300 }
301 
303 {
304  return GetTotalTimeOfTitle() >= 120;
305 }
306 
307 void DVDRingBuffer::GetDescForPos(QString &desc)
308 {
309  if (m_inMenu)
310  {
311  if ((m_part <= DVD_MENU_MAX) && dvdnav_menu_table[m_part] )
312  {
313  desc = QString("%1 Menu").arg(dvdnav_menu_table[m_part]);
314  }
315  }
316  else
317  {
318  desc = QObject::tr("Title %1 chapter %2").arg(m_title).arg(m_part);
319  }
320 }
321 
322 bool DVDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
323 {
324  rwlock.lockForWrite();
325 
326  if (m_dvdnav)
327  {
328  rwlock.unlock();
329  CloseDVD();
330  rwlock.lockForWrite();
331  }
332 
333  safefilename = lfilename;
334  filename = lfilename;
335  QByteArray fname = filename.toLocal8Bit();
336 
337  dvdnav_status_t res = dvdnav_open(&m_dvdnav, fname.constData());
338  if (res == DVDNAV_STATUS_ERR)
339  {
340  LOG(VB_GENERAL, LOG_ERR,
341  LOC + QString("Failed to open DVD device at %1")
342  .arg(fname.constData()));
343  rwlock.unlock();
344  return false;
345  }
346 
347  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened DVD device at %1")
348  .arg(fname.constData()));
349 
352 
353  // Check we aren't starting in a still frame (which will probably fail as
354  // ffmpeg will be unable to create a decoder)
356  {
357  LOG(VB_GENERAL, LOG_NOTICE,
358  LOC + "The selected title is a still frame. "
359  "Playback is likely to fail - please raise a bug report at "
360  "http://code.mythtv.org/trac");
361  }
362 
365  SetDVDSpeed();
366 
367  LOG(VB_PLAYBACK, LOG_INFO, LOC +
368  QString("DVD Serial Number %1").arg(m_serialnumber));
369 
370  readblocksize = DVD_BLOCK_SIZE * 62;
371  setswitchtonext = false;
372  ateof = false;
373  commserror = false;
374  numfailures = 0;
375  rawbitrate = 8000;
376 
378 
379  rwlock.unlock();
380 
381  return true;
382 }
383 
385 {
386  LOG(VB_GENERAL, LOG_INFO, LOC + "Resetting DVD device.");
387 
388  // if a DVDNAV_STOP event has been emitted, dvdnav_reset does not
389  // seem to restore the state, hence we need to re-create
390  if (m_gotStop)
391  {
392  LOG(VB_GENERAL, LOG_ERR, LOC +
393  "DVD errored after initial scan - trying again");
394  CloseDVD();
396  if (!m_dvdnav)
397  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-open DVD.");
398  }
399 
400  if (m_dvdnav)
401  {
402  QMutexLocker lock(&m_seekLock);
405  m_audioStreamsChanged = true;
406  }
407 
408  m_endPts = 0;
409  m_timeDiff = 0;
410 
411  return m_dvdnav;
412 }
413 
414 void DVDRingBuffer::GetChapterTimes(QList<long long> &times)
415 {
416  if (!m_chapterMap.contains(m_title))
418 
419  if (!m_chapterMap.contains(m_title))
420  return;
421 
422  foreach (uint64_t chapter, m_chapterMap.value(m_title))
423  times.push_back(chapter);
424 }
425 
427 {
428  if (!m_dvdnav)
429  return 0;
430 
431  uint64_t duration;
432  uint64_t *chaps;
433  uint32_t num = dvdnav_describe_title_chapters(m_dvdnav, title,
434  &chaps, &duration);
435 
436  if (num < 1)
437  {
438  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve chapter data");
439  return 0;
440  }
441 
442  QList<uint64_t> chapters;
443  // add the start
444  chapters.append(0);
445  // don't add the last 'chapter' - which is the title end
446  if (num > 1)
447  {
448  for (uint i = 0; i < num - 1; i++)
449  chapters.append((chaps[i] + 45000) / 90000);
450  }
451  // Assigned via calloc, must be free'd not deleted
452  if (chaps)
453  free(chaps);
454  m_chapterMap.insert(title, chapters);
455  return (duration + 45000) / 90000;
456 }
457 
460 long long DVDRingBuffer::GetReadPosition(void) const
461 {
462  uint32_t pos = 0;
463  uint32_t length = 1;
464  if (m_dvdnav)
465  {
466  if (dvdnav_get_position(m_dvdnav, &pos, &length) == DVDNAV_STATUS_ERR)
467  {
468  // try one more time
469  dvdnav_get_position(m_dvdnav, &pos, &length);
470  }
471  }
472  return pos * DVD_BLOCK_SIZE;
473 }
474 
476 {
477  if (!m_skipstillorwait)
478  {
479  LOG(VB_PLAYBACK, LOG_INFO,
480  LOC + "Waiting for player's buffers to drain");
481  m_playerWait = true;
482  int count = 0;
483  while (m_playerWait && count++ < 200)
484  {
485  rwlock.unlock();
486  usleep(10000);
487  rwlock.lockForWrite();
488  }
489 
490  if (m_playerWait)
491  {
492  LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
493  m_playerWait = false;
494  }
495  }
496 }
497 
499 {
500  unsigned char *blockBuf = NULL;
501  uint tot = 0;
502  int needed = sz;
503  char *dest = (char*) data;
504  int offset = 0;
505  bool bReprocessing = false;
506 
507  if (m_gotStop)
508  {
509  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read: called after DVDNAV_STOP");
510  errno = EBADF;
511  return -1;
512  }
513 
514  if (readaheadrunning)
515  LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running.");
516 
517  while ((m_processState != PROCESS_WAIT) && needed)
518  {
519  blockBuf = m_dvdBlockWriteBuf;
520 
522  {
524  bReprocessing = true;
525  }
526  else
527  {
529  m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize);
530 
531  bReprocessing = false;
532  }
533 
534  if (m_dvdStat == DVDNAV_STATUS_ERR)
535  {
536  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1")
538  errno = EIO;
539  return -1;
540  }
541 
542  switch (m_dvdEvent)
543  {
544  // Standard packet for decoding
545  case DVDNAV_BLOCK_OK:
546  {
547  // copy block
548  if (!m_seeking)
549  {
550  memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
551  tot += DVD_BLOCK_SIZE;
552  }
553 
554  // release buffer
555  if (blockBuf != m_dvdBlockWriteBuf)
557 
558  // debug
559  LOG(VB_PLAYBACK|VB_FILE, LOG_DEBUG, LOC + "DVDNAV_BLOCK_OK");
560  }
561  break;
562 
563  // cell change
564  case DVDNAV_CELL_CHANGE:
565  {
566  // get event details
567  dvdnav_cell_change_event_t *cell_event =
568  (dvdnav_cell_change_event_t*) (blockBuf);
569 
570  // a menu is anything that isn't in the VTS domain
572 
573  // update information for the current cell
574  m_cellChanged = true;
575  if (m_pgcLength != cell_event->pgc_length)
576  m_pgcLengthChanged = true;
577  m_pgLength = cell_event->pg_length;
578  m_pgcLength = cell_event->pgc_length;
579  m_cellStart = cell_event->cell_start;
580  m_pgStart = cell_event->pg_start;
581 
582  // update title/part/still/menu information
584  m_lastPart = m_part;
586  uint32_t pos;
587  uint32_t length;
591  dvdnav_get_position(m_dvdnav, &pos, &length);
593 
594  if (m_title != m_lastTitle)
595  {
596  // Populate the chapter list for this title, used in the OSD menu
598  }
599 
600  m_titleLength = length * DVD_BLOCK_SIZE;
601  if (!m_seeking)
603 
604  // debug
605  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
606  QString("---- DVDNAV_CELL_CHANGE - Cell "
607  "#%1 Menu %2 Length %3")
608  .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
609  .arg((float)cell_event->cell_length / 90000.0f,0,'f',1));
610  QString still = m_still ? ((m_still < 0xff) ?
611  QString("Stillframe: %1 seconds").arg(m_still) :
612  QString("Infinite stillframe")) :
613  QString("Length: %1 seconds")
614  .arg((float)m_pgcLength / 90000.0f, 0, 'f', 1);
615  if (m_title == 0)
616  {
617  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Menu #%1 %2")
618  .arg(m_part).arg(still));
619  }
620  else
621  {
622  LOG(VB_PLAYBACK, LOG_INFO,
623  LOC + QString("Title #%1: %2 Part %3 of %4")
624  .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
625  }
626 
627  // wait unless it is a transition from one normal video cell to
628  // another or the same menu id
629  if (((m_still != m_lastStill) || (m_title != m_lastTitle)) &&
630  !((m_title == 0 && m_lastTitle == 0) &&
631  (m_part == m_lastPart)))
632  {
633  WaitForPlayer();
634  }
635 
636  // Make sure the still frame timer is updated (if this isn't
637  // a still frame, this will ensure the timer knows about it).
638  if (m_parent)
639  {
641  }
642 
643  // clear menus/still frame selections
647  m_buttonSelected = false;
648  m_vobid = m_cellid = 0;
649  m_cellRepeated = false;
650  m_buttonSeenInCell = false;
651 
652  IncrementButtonVersion;
653  if (m_inMenu)
654  {
655  m_autoselectsubtitle = true;
657  }
658  else
660 
661  // release buffer
662  if (blockBuf != m_dvdBlockWriteBuf)
664  }
665  break;
666 
667  // new colour lookup table for subtitles/menu buttons
668  case DVDNAV_SPU_CLUT_CHANGE:
669  {
670  // debug
671  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_SPU_CLUT_CHANGE");
672 
673  // store the new clut
674  memcpy(m_clut, blockBuf, 16 * sizeof(uint32_t));
675  // release buffer
676  if (blockBuf != m_dvdBlockWriteBuf)
678  }
679  break;
680 
681  // new Sub-picture Unit stream (subtitles/menu buttons)
682  case DVDNAV_SPU_STREAM_CHANGE:
683  {
684  // get event details
687 
688  // clear any existing subs/buttons
689  IncrementButtonVersion;
690 
691  // not sure
694 
695  // debug
696  LOG(VB_PLAYBACK, LOG_DEBUG,
697  QString(LOC + "DVDNAV_SPU_STREAM_CHANGE: "
698  "physicalwide %1, physicalletterbox %2, "
699  "physicalpanscan %3, currenttrack %4")
700  .arg(spu->physical_wide).arg(spu->physical_letterbox)
701  .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
702 
703  // release buffer
704  if (blockBuf != m_dvdBlockWriteBuf)
706  }
707  break;
708 
709  // the audio stream changed
710  case DVDNAV_AUDIO_STREAM_CHANGE:
711  {
712  // retrieve the new track
713  int new_track = dvdnav_get_active_audio_stream(m_dvdnav);
714 
715  // debug
716  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
717  QString("DVDNAV_AUDIO_STREAM_CHANGE: old %1 new %2")
718  .arg(new_track).arg(m_curAudioTrack));
719 
720  // tell the decoder to reset the audio streams if necessary
721  if (new_track != m_curAudioTrack)
722  {
723  m_curAudioTrack = new_track;
724  m_audioStreamsChanged = true;
725  }
726 
727  // release buffer
728  if (blockBuf != m_dvdBlockWriteBuf)
730  }
731  break;
732 
733  // navigation packet
734  case DVDNAV_NAV_PACKET:
735  {
736  QMutexLocker lock(&m_seekLock);
737 
739 
740  // If the start PTS of this block is not the
741  // same as the end PTS of the last block,
742  // we've got a timestamp discontinuity
743  int64_t diff = (int64_t)pci->pci_gi.vobu_s_ptm - m_endPts;
744  if (diff != 0)
745  {
746  if (!bReprocessing && !m_skipstillorwait)
747  {
748  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
749  .arg(pci->pci_gi.vobu_s_ptm)
750  .arg(m_endPts)
751  .arg(diff));
752 
754  break;
755  }
756 
757  m_timeDiff += diff;
758  }
759 
760  m_endPts = pci->pci_gi.vobu_e_ptm;
761 
762  // get the latest nav
763  m_lastNav = (dvdnav_t *)blockBuf;
764 
765  // retrive the latest Data Search Information
766  dsi_t *dsi = dvdnav_get_current_nav_dsi(m_dvdnav);
767 
768  // if we are in a looping menu, we don't want to reset the
769  // selected button when we restart
770  m_vobid = dsi->dsi_gi.vobu_vob_idn;
771  m_cellid = dsi->dsi_gi.vobu_c_idn;
772  if ((m_lastvobid == m_vobid) && (m_lastcellid == m_cellid)
774  {
775  m_cellRepeated = true;
776  }
777 
778  // update our status
781 
782  if (m_seeking)
783  {
784 
785  int relativetime =
786  (int)((m_seektime - m_currentTime)/ 90000);
787  if (relativetime <= 1)
788  {
789  m_seeking = false;
790  m_seektime = 0;
791  }
792  else
793  {
794  dvdnav_relative_time_search(m_dvdnav, relativetime * 2);
795  }
796  }
797 
798  // update the button stream number if this is the
799  // first NAV pack containing button information
800  if ( (pci->hli.hl_gi.hli_ss & 0x03) == 0x01 )
801  {
802  m_buttonStreamID = 32;
803  int aspect = dvdnav_get_video_aspect(m_dvdnav);
804 
805  // workaround where dvd menu is
806  // present in VTS_DOMAIN. dvdnav adds 0x80 to stream id
807  // proper fix should be put in dvdnav sometime
808  int8_t spustream = dvdnav_get_active_spu_stream(m_dvdnav) & 0x7f;
809 
810  if (aspect != 0 && spustream > 0)
811  m_buttonStreamID += spustream;
812 
813  m_buttonSeenInCell = true;
814  }
815 
816  // debug
817  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_NAV_PACKET - time:%1, pos:%2, vob:%3, cell:%4, seeking:%5, seektime:%6")
818  .arg(m_currentTime)
819  .arg(m_currentpos)
820  .arg(m_vobid)
821  .arg(m_cellid)
822  .arg(m_seeking)
823  .arg(m_seektime));
824 
825  // release buffer
826  if (blockBuf != m_dvdBlockWriteBuf)
828  }
829  break;
830 
831  case DVDNAV_HOP_CHANNEL:
832  {
833  // debug
834  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL");
835  WaitForPlayer();
836  }
837  break;
838 
839  // no op
840  case DVDNAV_NOP:
841  break;
842 
843  // new Video Title Set - aspect ratio/letterboxing
844  case DVDNAV_VTS_CHANGE:
845  {
846  // retrieve event details
848  (dvdnav_vts_change_event_t*)(blockBuf);
849 
850  // update player
851  int aspect = dvdnav_get_video_aspect(m_dvdnav);
852  if (aspect == 2) // 4:3
853  m_forcedAspect = 4.0f / 3.0f;
854  else if (aspect == 3) // 16:9
855  m_forcedAspect = 16.0f / 9.0f;
856  else
857  m_forcedAspect = -1;
859 
860  // debug
861  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
862  QString("DVDNAV_VTS_CHANGE: old_vtsN %1, new_vtsN %2, "
863  "aspect %3, perm %4")
864  .arg(vts->old_vtsN).arg(vts->new_vtsN)
865  .arg(aspect).arg(permission));
866 
867  // trigger a rescan of the audio streams
868  if ((vts->old_vtsN != vts->new_vtsN) ||
869  (vts->old_domain != vts->new_domain))
870  {
871  m_audioStreamsChanged = true;
872  }
873 
874  // Make sure we know we're not staying in the
875  // same cell (same vobid/cellid values can
876  // occur in every VTS)
877  m_lastvobid = m_vobid = 0;
878  m_lastcellid = m_cellid = 0;
879 
880  // release buffer
881  if (blockBuf != m_dvdBlockWriteBuf)
883  }
884  break;
885 
886  // menu button
887  case DVDNAV_HIGHLIGHT:
888  {
889  // retrieve details
891  (dvdnav_highlight_event_t*)(blockBuf);
892 
893  // update the current button
894  m_menuBtnLock.lock();
895  DVDButtonUpdate(false);
896  IncrementButtonVersion;
897  m_menuBtnLock.unlock();
898 
899  // debug
900  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
901  QString("DVDNAV_HIGHLIGHT: display %1, palette %2, "
902  "sx %3, sy %4, ex %5, ey %6, pts %7, buttonN %8")
903  .arg(hl->display).arg(hl->palette)
904  .arg(hl->sx).arg(hl->sy)
905  .arg(hl->ex).arg(hl->ey)
906  .arg(hl->pts).arg(hl->buttonN));
907 
908  // release buffer
909  if (blockBuf != m_dvdBlockWriteBuf)
911  }
912  break;
913 
914  // dvd still frame
915  case DVDNAV_STILL_FRAME:
916  {
917  // retrieve still frame details (length)
918  dvdnav_still_event_t* still =
919  (dvdnav_still_event_t*)(blockBuf);
920 
921  // sense check
922  if (!m_still)
923  LOG(VB_GENERAL, LOG_WARNING, LOC + "DVDNAV_STILL_FRAME in "
924  "cell that is not marked as a still frame");
925 
926  if (still->length != m_still)
927  LOG(VB_GENERAL, LOG_WARNING, LOC + "DVDNAV_STILL_FRAME "
928  "length does not match cell still length");
929 
930  // pause a little as the dvdnav VM will continue to return
931  // this event until it has been skipped
932  rwlock.unlock();
933  usleep(10000);
934  rwlock.lockForWrite();
935 
936  // when scanning the file or exiting playback, skip immediately
937  // otherwise update the timeout in the player
938  if (m_skipstillorwait)
939  SkipStillFrame();
940  else if (m_parent)
941  {
942  if ((still->length > 0) && (still->length < 0xff))
944  }
945 
946  // debug
947  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_STILL_FRAME");
948 
949  // release buffer
950  if (blockBuf != m_dvdBlockWriteBuf)
952  }
953  break;
954 
955  // wait for the player
956  case DVDNAV_WAIT:
957  {
958  //debug
959  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");
960 
961  // skip if required, otherwise wait (and loop)
962  if (m_skipstillorwait)
963  WaitSkip();
964  else
965  {
966  m_dvdWaiting = true;
967  rwlock.unlock();
968  usleep(10000);
969  rwlock.lockForWrite();
970  }
971 
972  // release buffer
973  if (blockBuf != m_dvdBlockWriteBuf)
975  }
976  break;
977 
978  // exit playback
979  case DVDNAV_STOP:
980  {
981  LOG(VB_GENERAL, LOG_INFO, LOC + "DVDNAV_STOP");
982  sz = tot;
983  m_gotStop = true;
984 
985  // release buffer
986  if (blockBuf != m_dvdBlockWriteBuf)
988  }
989  break;
990 
991  // this shouldn't happen
992  default:
993  {
994  LOG(VB_GENERAL, LOG_ERR, LOC +
995  QString("Unknown DVD event: %1").arg(m_dvdEvent));
996  }
997  break;
998  }
999 
1000  needed = sz - tot;
1001  offset = tot;
1002  }
1003 
1005  {
1006  errno = EAGAIN;
1007  return 0;
1008  }
1009  else
1010  {
1011  return tot;
1012  }
1013 }
1014 
1016 {
1017  QMutexLocker lock(&m_seekLock);
1018  if (track < 1)
1019  Seek(0);
1020  else if (track < m_titleParts)
1022  else
1023  return false;
1024  m_gotStop = false;
1025  return true;
1026 }
1027 
1029 {
1030  int newPart = m_part + 1;
1031 
1032  QMutexLocker lock(&m_seekLock);
1033  if (newPart < m_titleParts)
1034  {
1035  dvdnav_part_play(m_dvdnav, m_title, newPart);
1036  m_gotStop = false;
1037  return true;
1038  }
1039  return false;
1040 }
1041 
1043 {
1044  int newPart = m_part - 1;
1045 
1046  QMutexLocker lock(&m_seekLock);
1047  if (newPart > 0)
1048  dvdnav_part_play(m_dvdnav, m_title, newPart);
1049  else
1050  Seek(0);
1051  m_gotStop = false;
1052 }
1053 
1058 {
1059  return m_pgcLength / 90000.0 + 0.5;
1060 }
1061 
1065 {
1066  return m_cellStart / 90000;
1067 }
1068 
1072 {
1073  bool ret = m_cellChanged;
1074  m_cellChanged = false;
1075  return ret;
1076 }
1077 
1081 {
1082  bool ret = m_pgcLengthChanged;
1083  m_pgcLengthChanged = false;
1084  return ret;
1085 }
1086 
1088 {
1089  QMutexLocker locker(&m_seekLock);
1090  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Skipping still frame.");
1092 
1093  // Make sure the still frame timer is disabled.
1094  if (m_parent)
1095  {
1097  }
1098 }
1099 
1101 {
1102  QMutexLocker locker(&m_seekLock);
1104  m_dvdWaiting = false;
1105  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exiting DVDNAV_WAIT status");
1106 }
1107 
1110 bool DVDRingBuffer::GoToMenu(const QString str)
1111 {
1112  DVDMenuID_t menuid;
1113  QMutexLocker locker(&m_seekLock);
1114 
1115  LOG(VB_PLAYBACK, LOG_INFO,
1116  LOC + QString("DVDRingBuf: GoToMenu %1").arg(str));
1117 
1118  if (str.compare("chapter") == 0)
1119  {
1120  menuid = DVD_MENU_Part;
1121  }
1122  else if (str.compare("root") == 0)
1123  {
1124  menuid = DVD_MENU_Root;
1125  }
1126  else if (str.compare("title") == 0)
1127  menuid = DVD_MENU_Title;
1128  else
1129  return false;
1130 
1131  dvdnav_status_t ret = dvdnav_menu_call(m_dvdnav, menuid);
1132  if (ret == DVDNAV_STATUS_OK)
1133  return true;
1134  return false;
1135 }
1136 
1138 {
1139  QMutexLocker locker(&m_seekLock);
1140  // This conditional appears to be unnecessary, and might have come
1141  // from a mistake in a libdvdnav resync.
1142  //if (!dvdnav_is_domain_vts(m_dvdnav))
1144 }
1145 
1147 {
1148  QMutexLocker locker(&m_seekLock);
1149  // This conditional appears to be unnecessary, and might have come
1150  // from a mistake in a libdvdnav resync.
1151  //if (!dvdnav_is_domain_vts(m_dvdnav))
1153 }
1154 
1155 bool DVDRingBuffer::HandleAction(const QStringList &actions, int64_t pts)
1156 {
1157  (void)pts;
1158 
1159  if (!NumMenuButtons())
1160  return false;
1161 
1162  bool handled = true;
1163  if (actions.contains(ACTION_UP) ||
1164  actions.contains(ACTION_CHANNELUP))
1165  {
1166  MoveButtonUp();
1167  }
1168  else if (actions.contains(ACTION_DOWN) ||
1169  actions.contains(ACTION_CHANNELDOWN))
1170  {
1171  MoveButtonDown();
1172  }
1173  else if (actions.contains(ACTION_LEFT) ||
1174  actions.contains(ACTION_SEEKRWND))
1175  {
1176  MoveButtonLeft();
1177  }
1178  else if (actions.contains(ACTION_RIGHT) ||
1179  actions.contains(ACTION_SEEKFFWD))
1180  {
1181  MoveButtonRight();
1182  }
1183  else if (actions.contains(ACTION_SELECT))
1184  {
1185  ActivateButton();
1186  }
1187  else
1188  handled = false;
1189 
1190  return handled;
1191 }
1192 
1194 {
1195  if (NumMenuButtons() > 1)
1196  {
1199  }
1200 }
1201 
1203 {
1204  if (NumMenuButtons() > 1)
1205  {
1208  }
1209 }
1210 
1212 {
1213  if (NumMenuButtons() > 1)
1214  {
1217  }
1218 }
1219 
1221 {
1222  if (NumMenuButtons() > 1)
1223  {
1226  }
1227 }
1228 
1232 {
1233  if (NumMenuButtons() > 0)
1234  {
1235  IncrementButtonVersion;
1238  }
1239 }
1240 
1243 void DVDRingBuffer::GetMenuSPUPkt(uint8_t *buf, int buf_size, int stream_id)
1244 {
1245  if (buf_size < 4)
1246  return;
1247 
1248  if (m_buttonStreamID != stream_id)
1249  return;
1250 
1251  QMutexLocker lock(&m_menuBtnLock);
1252 
1254  uint8_t *spu_pkt;
1255  spu_pkt = (uint8_t*)av_malloc(buf_size);
1256  memcpy(spu_pkt, buf, buf_size);
1257  m_menuSpuPkt = spu_pkt;
1258  m_menuBuflength = buf_size;
1259  if (!m_buttonSelected)
1260  {
1262  m_buttonSelected = true;
1263  }
1264 
1265  if (DVDButtonUpdate(false))
1266  {
1267  int32_t gotbutton;
1270  }
1271 }
1272 
1277 {
1278  // this is unlocked by ReleaseMenuButton
1279  m_menuBtnLock.lock();
1280 
1281  if ((m_menuBuflength > 4) && m_buttonExists && (NumMenuButtons() > 0))
1282  {
1283  version = m_buttonVersion;
1284  return &(m_dvdMenuButton);
1285  }
1286 
1287  return NULL;
1288 }
1289 
1290 
1292 {
1293  m_menuBtnLock.unlock();
1294 }
1295 
1299 {
1300  QRect rect(0,0,0,0);
1301  if (!m_buttonExists)
1302  return rect;
1303 
1304  rect.setRect(m_hl_button.x(), m_hl_button.y(), m_hl_button.width(),
1305  m_hl_button.height());
1306 
1307  return rect;
1308 }
1309 
1313 bool DVDRingBuffer::DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles,
1314  const uint8_t *spu_pkt, int buf_size)
1315 {
1316  #define GETBE16(p) (((p)[0] << 8) | (p)[1])
1317 
1318  int cmd_pos, pos, cmd, next_cmd_pos, offset1, offset2;
1319  int x1, x2, y1, y2;
1320  uint8_t alpha[4], palette[4];
1321  uint i;
1322  int date;
1323 
1324  if (!spu_pkt)
1325  return false;
1326 
1327  if (buf_size < 4)
1328  return false;
1329 
1330  bool force_subtitle_display = false;
1331  sub->rects = NULL;
1332  sub->num_rects = 0;
1333  sub->start_display_time = 0;
1334  sub->end_display_time = 0;
1335 
1336  cmd_pos = GETBE16(spu_pkt + 2);
1337  while ((cmd_pos + 4) < buf_size)
1338  {
1339  offset1 = -1;
1340  offset2 = -1;
1341  date = GETBE16(spu_pkt + cmd_pos);
1342  next_cmd_pos = GETBE16(spu_pkt + cmd_pos + 2);
1343  pos = cmd_pos + 4;
1344  x1 = x2 = y1 = y2 = 0;
1345  while (pos < buf_size)
1346  {
1347  cmd = spu_pkt[pos++];
1348  switch(cmd)
1349  {
1350  case 0x00:
1351  force_subtitle_display = true;
1352  break;
1353  case 0x01:
1354  sub->start_display_time = (date << 10) / 90;
1355  break;
1356  case 0x02:
1357  sub->end_display_time = (date << 10) / 90;
1358  break;
1359  case 0x03:
1360  {
1361  if ((buf_size - pos) < 2)
1362  goto fail;
1363 
1364  palette[3] = spu_pkt[pos] >> 4;
1365  palette[2] = spu_pkt[pos] & 0x0f;
1366  palette[1] = spu_pkt[pos + 1] >> 4;
1367  palette[0] = spu_pkt[pos + 1] & 0x0f;
1368  pos +=2;
1369  }
1370  break;
1371  case 0x04:
1372  {
1373  if ((buf_size - pos) < 2)
1374  goto fail;
1375  alpha[3] = spu_pkt[pos] >> 4;
1376  alpha[2] = spu_pkt[pos] & 0x0f;
1377  alpha[1] = spu_pkt[pos + 1] >> 4;
1378  alpha[0] = spu_pkt[pos + 1] & 0x0f;
1379  pos +=2;
1380  }
1381  break;
1382  case 0x05:
1383  {
1384  if ((buf_size - pos) < 6)
1385  goto fail;
1386  x1 = (spu_pkt[pos] << 4) | (spu_pkt[pos + 1] >> 4);
1387  x2 = ((spu_pkt[pos + 1] & 0x0f) << 8) | spu_pkt[pos + 2];
1388  y1 = (spu_pkt[pos + 3] << 4) | (spu_pkt[pos + 4] >> 4);
1389  y2 = ((spu_pkt[pos + 4] & 0x0f) << 8) | spu_pkt[pos + 5];
1390  pos +=6;
1391  }
1392  break;
1393  case 0x06:
1394  {
1395  if ((buf_size - pos) < 4)
1396  goto fail;
1397  offset1 = GETBE16(spu_pkt + pos);
1398  offset2 = GETBE16(spu_pkt + pos + 2);
1399  pos +=4;
1400  }
1401  break;
1402  case 0xff:
1403  default:
1404  goto the_end;
1405  }
1406  }
1407  the_end:
1408  if (offset1 >= 0)
1409  {
1410  int w, h;
1411  uint8_t *bitmap;
1412  w = x2 - x1 + 1;
1413  if (w < 0)
1414  w = 0;
1415  h = y2 - y1 + 1;
1416  if (h < 0)
1417  h = 0;
1418  if (w > 0 && h > 0)
1419  {
1420  if (sub->rects != NULL)
1421  {
1422  for (i = 0; i < sub->num_rects; i++)
1423  {
1424  av_free(sub->rects[i]->pict.data[0]);
1425  av_free(sub->rects[i]->pict.data[1]);
1426  av_freep(&sub->rects[i]);
1427  }
1428  av_freep(&sub->rects);
1429  sub->num_rects = 0;
1430  }
1431 
1432  bitmap = (uint8_t*) av_malloc(w * h);
1433  sub->num_rects = (NumMenuButtons() > 0) ? 2 : 1;
1434  sub->rects = (AVSubtitleRect **)
1435  av_mallocz(sizeof(AVSubtitleRect*) * sub->num_rects);
1436  for (i = 0; i < sub->num_rects; i++)
1437  {
1438  sub->rects[i] = (AVSubtitleRect *) av_mallocz(sizeof(AVSubtitleRect));
1439  }
1440  sub->rects[0]->pict.data[1] = (uint8_t*)av_mallocz(4 * 4);
1441  decode_rle(bitmap, w * 2, w, (h + 1) / 2,
1442  spu_pkt, offset1 * 2, buf_size);
1443  decode_rle(bitmap + w, w * 2, w, h / 2,
1444  spu_pkt, offset2 * 2, buf_size);
1445  guess_palette((uint32_t*)sub->rects[0]->pict.data[1], palette, alpha);
1446  sub->rects[0]->pict.data[0] = bitmap;
1447  sub->rects[0]->x = x1;
1448  sub->rects[0]->y = y1;
1449  sub->rects[0]->w = w;
1450  sub->rects[0]->h = h;
1451  sub->rects[0]->type = SUBTITLE_BITMAP;
1452  sub->rects[0]->nb_colors = 4;
1453  sub->rects[0]->pict.linesize[0] = w;
1454  if (NumMenuButtons() > 0)
1455  {
1456  sub->rects[1]->type = SUBTITLE_BITMAP;
1457  sub->rects[1]->pict.data[1] = (uint8_t*)av_malloc(4 *4);
1458  guess_palette((uint32_t*)sub->rects[1]->pict.data[1],
1460  }
1461  else
1463  *gotSubtitles = 1;
1464  }
1465  }
1466  if (next_cmd_pos == cmd_pos)
1467  break;
1468  cmd_pos = next_cmd_pos;
1469  }
1470  if (sub->num_rects > 0)
1471  {
1472  if (force_subtitle_display)
1473  {
1474  sub->forced = 1;
1475  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Decoded forced subtitle");
1476  }
1477  return true;
1478  }
1479 fail:
1480  return false;
1481 }
1482 
1487 {
1488  if (!m_parent)
1489  return false;
1490 
1491  QSize video_disp_dim = m_parent->GetVideoSize();
1492  int videoheight = video_disp_dim.height();
1493  int videowidth = video_disp_dim.width();
1494 
1495  int32_t button;
1496  pci_t *pci;
1497  dvdnav_status_t dvdRet;
1501  dvdRet = dvdnav_get_highlight_area_from_group(pci, DVD_BTN_GRP_Wide, button, b_mode, &hl);
1502 
1503  if (dvdRet == DVDNAV_STATUS_ERR)
1504  return false;
1505 
1506  for (uint i = 0 ; i < 4 ; i++)
1507  {
1508  m_button_alpha[i] = 0xf & (hl.palette >> (4 * i ));
1509  m_button_color[i] = 0xf & (hl.palette >> (16+4 *i ));
1510  }
1511 
1512  // If the button overlay has already been decoded, make sure
1513  // the correct palette for the current highlight is set
1514  if (m_dvdMenuButton.rects && (m_dvdMenuButton.num_rects > 1))
1515  {
1516  guess_palette((uint32_t*)m_dvdMenuButton.rects[1]->pict.data[1],
1518  }
1519 
1520  m_hl_button.setCoords(hl.sx, hl.sy, hl.ex, hl.ey);
1521 
1522  if (((hl.sx + hl.sy) > 0) &&
1523  (hl.sx < videowidth && hl.sy < videoheight))
1524  return true;
1525 
1526  return false;
1527 }
1528 
1532 {
1533  if (m_buttonExists || m_dvdMenuButton.rects)
1534  {
1535  for (uint i = 0; i < m_dvdMenuButton.num_rects; i++)
1536  {
1537  AVSubtitleRect* rect = m_dvdMenuButton.rects[i];
1538  av_free(rect->pict.data[0]);
1539  av_free(rect->pict.data[1]);
1540  av_free(rect);
1541  }
1542  av_free(m_dvdMenuButton.rects);
1543  m_dvdMenuButton.rects = NULL;
1544  m_dvdMenuButton.num_rects = 0;
1545  m_buttonExists = false;
1546  }
1547 }
1548 
1553 {
1554  if (m_menuBuflength == 0)
1555  return;
1556 
1557  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clearing Menu SPU Packet" );
1558 
1559  ClearMenuButton();
1560 
1562  m_menuBuflength = 0;
1563  m_hl_button.setRect(0, 0, 0, 0);
1564 }
1565 
1567 {
1569  int numButtons = pci->hli.hl_gi.btn_ns;
1570  if (numButtons > 0 && numButtons < 36)
1571  return numButtons;
1572  else
1573  return 0;
1574 }
1575 
1579 {
1581  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1582  QString("StreamID: %1; lang: %2").arg(id).arg(lang));
1583  return ConvertLangCode(lang);
1584 }
1585 
1590 {
1591  return dvdnav_get_audio_logical_stream(m_dvdnav, stream_id);
1592 }
1593 
1595 {
1596  int ret = -1;
1597  audio_attr_t attributes;
1598  int logicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, stream_id);
1599  if (dvdnav_get_audio_attr(m_dvdnav, logicalStreamId, &attributes) >= 1)
1600  {
1601  LOG(VB_AUDIO, LOG_INFO, QString("DVD Audio Track #%1 Language "
1602  "Extension Code - %2")
1603  .arg(stream_id)
1604  .arg(attributes.code_extension));
1605  ret = attributes.code_extension;
1606  }
1607 
1608  return ret;
1609 }
1610 
1614 {
1616  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1617  QString("StreamID: %1; lang: %2").arg(id).arg(lang));
1618  return ConvertLangCode(lang);
1619 }
1620 
1624 {
1625  if (code == 0)
1626  return 0;
1627 
1628  QChar str2[2];
1629  str2[0] = QChar(code >> 8);
1630  str2[1] = QChar(code & 0xff);
1631  QString str3 = iso639_str2_to_str3(QString(str2, 2));
1632 
1633  LOG(VB_PLAYBACK, LOG_INFO, LOC +
1634  QString("code: %1; iso639: %2").arg(code).arg(str3));
1635 
1636  if (!str3.isEmpty())
1637  return iso639_str3_to_key(str3);
1638  return 0;
1639 }
1640 
1645 {
1647  int32_t button = pci->hli.hl_gi.fosl_btnn;
1648  if (button > 0 && !m_cellRepeated)
1649  {
1650  dvdnav_button_select(m_dvdnav,pci,button);
1651  return;
1652  }
1654  if (button > 0 && button <= NumMenuButtons())
1655  dvdnav_button_select(m_dvdnav,pci,button);
1656  else
1658 }
1659 
1665 {
1666  if (type == kTrackTypeSubtitle)
1667  {
1668  m_curSubtitleTrack = trackNo;
1669  if (trackNo < 0)
1670  m_autoselectsubtitle = true;
1671  else
1672  m_autoselectsubtitle = false;
1673  }
1674  else if (type == kTrackTypeAudio)
1675  {
1676  m_curAudioTrack = trackNo;
1678  }
1679 }
1680 
1687 {
1688  if (type == kTrackTypeSubtitle)
1689  return m_curSubtitleTrack;
1690  else if (type == kTrackTypeAudio)
1691  return m_curAudioTrack;
1692 
1693  return 0;
1694 }
1695 
1697 {
1698  unsigned char channels = dvdnav_audio_stream_channels(m_dvdnav, id);
1699  if (channels == 0xff)
1700  return 0;
1701  return (uint8_t)channels;
1702 }
1703 
1706 bool DVDRingBuffer::GetNameAndSerialNum(QString& _name, QString& _serial)
1707 {
1708  _name = QString(m_dvdname);
1709  _serial = QString(m_serialnumber);
1710  if (_name.isEmpty() && _serial.isEmpty())
1711  return false;
1712  return true;
1713 }
1714 
1721 {
1722  double dvdfps = 0;
1724 
1725  dvdfps = (format == 1)? 25.00 : 29.97;
1726  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Frame Rate %1").arg(dvdfps));
1727  return dvdfps;
1728 }
1729 
1734 {
1735  QMutexLocker lock(&m_seekLock);
1736  SetDVDSpeed(DVD_DRIVE_SPEED);
1737 }
1738 
1742 {
1743  if (filename.startsWith("/"))
1744  MediaMonitor::SetCDSpeed(filename.toLocal8Bit().constData(), speed);
1745 }
1746 
1750 {
1751  return (GetTotalTimeOfTitle() - GetCurrentTime());
1752 }
1753 
1756 void DVDRingBuffer::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
1757  uint8_t *alpha)
1758 {
1759  int i,r,g,b,y,cr,cb;
1760  uint32_t yuv;
1761 
1762  memset(rgba_palette, 0, 16);
1763 
1764  for (i=0 ; i < 4 ; i++)
1765  {
1766  yuv = m_clut[palette[i]];
1767  y = ((yuv >> 16) & 0xff);
1768  cr = ((yuv >> 8) & 0xff);
1769  cb = ((yuv >> 0) & 0xff);
1770  r = int(y + 1.4022 * (cr - 128));
1771  b = int(y + 1.7710 * (cb - 128));
1772  g = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)) ;
1773  if (r < 0) r = 0;
1774  if (g < 0) g = 0;
1775  if (b < 0) b = 0;
1776  if (r > 0xff) r = 0xff;
1777  if (g > 0xff) g = 0xff;
1778  if (b > 0xff) b = 0xff;
1779  rgba_palette[i] = ((alpha[i] * 17) << 24) | (r << 16 )| (g << 8) | b;
1780  }
1781 }
1782 
1786 int DVDRingBuffer::decode_rle(uint8_t *bitmap, int linesize, int w, int h,
1787  const uint8_t *buf, int nibble_offset, int buf_size)
1788 {
1789  unsigned int v;
1790  int x, y, len, color, nibble_end;
1791  uint8_t *d;
1792 
1793  nibble_end = buf_size * 2;
1794  x = 0;
1795  y = 0;
1796  d = bitmap;
1797  for(;;) {
1798  if (nibble_offset >= nibble_end)
1799  return -1;
1800  v = get_nibble(buf, nibble_offset++);
1801  if (v < 0x4) {
1802  v = (v << 4) | get_nibble(buf, nibble_offset++);
1803  if (v < 0x10) {
1804  v = (v << 4) | get_nibble(buf, nibble_offset++);
1805  if (v < 0x040) {
1806  v = (v << 4) | get_nibble(buf, nibble_offset++);
1807  if (v < 4) {
1808  v |= (w - x) << 2;
1809  }
1810  }
1811  }
1812  }
1813  len = v >> 2;
1814  if (len > (w - x))
1815  len = (w - x);
1816  color = v & 0x03;
1817  memset(d + x, color, len);
1818  x += len;
1819  if (x >= w) {
1820  y++;
1821  if (y >= h)
1822  break;
1823  d += linesize;
1824  x = 0;
1825  nibble_offset += (nibble_offset & 1);
1826  }
1827  }
1828  return 0;
1829 }
1830 
1833 int DVDRingBuffer::get_nibble(const uint8_t *buf, int nibble_offset)
1834 {
1835  return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
1836 }
1837 
1842 int DVDRingBuffer::is_transp(const uint8_t *buf, int pitch, int n,
1843  const uint8_t *transp_color)
1844 {
1845  int i;
1846  for (i = 0; i < n; i++)
1847  {
1848  if (!transp_color[*buf])
1849  return 0;
1850  buf += pitch;
1851  }
1852  return 1;
1853 }
1854 
1861 {
1862  uint8_t transp_color[256];
1863  int y1, y2, x1, x2, y, w, h, i;
1864  uint8_t *bitmap;
1865 
1866  if (s->num_rects == 0 || s->rects == NULL ||
1867  s->rects[0]->w <= 0 || s->rects[0]->h <= 0)
1868  {
1869  return 0;
1870  }
1871 
1872  memset(transp_color, 0, 256);
1873  for (i = 0; i < s->rects[0]->nb_colors * 4; i+=4)
1874  {
1875  if ((s->rects[0]->pict.data[1][i] >> 24) == 0)
1876  transp_color[i] = 1;
1877  }
1878 
1879  y1 = 0;
1880  while (y1 < s->rects[0]->h &&
1881  is_transp(s->rects[0]->pict.data[0] + y1 * s->rects[0]->pict.linesize[0],
1882  1, s->rects[0]->w, transp_color))
1883  {
1884  y1++;
1885  }
1886 
1887  if (y1 == s->rects[0]->h)
1888  {
1889  av_freep(&s->rects[0]->pict.data[0]);
1890  s->rects[0]->w = s->rects[0]->h = 0;
1891  return 0;
1892  }
1893 
1894  y2 = s->rects[0]->h - 1;
1895  while (y2 > 0 &&
1896  is_transp(s->rects[0]->pict.data[0] + y2 * s->rects[0]->pict.linesize[0], 1,
1897  s->rects[0]->w, transp_color))
1898  {
1899  y2--;
1900  }
1901 
1902  x1 = 0;
1903  while (x1 < (s->rects[0]->w - 1) &&
1904  is_transp(s->rects[0]->pict.data[0] + x1, s->rects[0]->pict.linesize[0],
1905  s->rects[0]->h, transp_color))
1906  {
1907  x1++;
1908  }
1909 
1910  x2 = s->rects[0]->w - 1;
1911  while (x2 > 0 &&
1912  is_transp(s->rects[0]->pict.data[0] + x2, s->rects[0]->pict.linesize[0],
1913  s->rects[0]->h, transp_color))
1914  {
1915  x2--;
1916  }
1917 
1918  w = x2 - x1 + 1;
1919  h = y2 - y1 + 1;
1920  bitmap = (uint8_t*) av_malloc(w * h);
1921  if (!bitmap)
1922  return 1;
1923 
1924  for(y = 0; y < h; y++)
1925  {
1926  memcpy(bitmap + w * y, s->rects[0]->pict.data[0] + x1 +
1927  (y1 + y) * s->rects[0]->pict.linesize[0], w);
1928  }
1929 
1930  av_freep(&s->rects[0]->pict.data[0]);
1931  s->rects[0]->pict.data[0] = bitmap;
1932  s->rects[0]->pict.linesize[0] = w;
1933  s->rects[0]->w = w;
1934  s->rects[0]->h = h;
1935  s->rects[0]->x += x1;
1936  s->rects[0]->y += y1;
1937  return 1;
1938 }
1939 
1941 {
1942  if (!m_dvdnav)
1943  return false;
1944 
1945  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switching to Angle %1...")
1946  .arg(angle));
1947  dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, (int32_t)angle);
1948  if (status == DVDNAV_STATUS_OK)
1949  {
1950  m_currentAngle = angle;
1951  return true;
1952  }
1953  return false;
1954 }
1955 
1956 bool DVDRingBuffer::NewSequence(bool new_sequence)
1957 {
1958  bool result = false;
1959  if (new_sequence)
1960  {
1961  LOG(VB_PLAYBACK, LOG_INFO, LOC + "New sequence");
1962  m_newSequence = true;
1963  return result;
1964  }
1965 
1966  result = m_newSequence && m_inMenu;
1967  m_newSequence = false;
1968  if (result)
1969  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Asking for still frame");
1970  return result;
1971 }