MythTV master
mythdvdbuffer.cpp
Go to the documentation of this file.
1// Std
2#include <algorithm>
3#include <limits> // workaround QTBUG-90395
4#include <thread>
5
6// Qt
7#include <QCoreApplication>
8#include <QtEndian>
9
10// MythTV
11#include "libmythbase/compat.h"
12#include "libmythbase/iso639.h"
13#include "libmythbase/mythconfig.h"
20
21#include "mythdvdbuffer.h"
22#include "mythdvdplayer.h"
23#include "tv_actions.h"
24
25#define LOC QString("DVDRB: ")
26
27#define IncrementButtonVersion if (++m_buttonVersion > 1024) m_buttonVersion = 1;
28static constexpr int8_t DVD_DRIVE_SPEED { 1 };
29
30static const std::array<const std::string,8> DVDMenuTable
31{
32 "",
33 "",
34 QT_TRANSLATE_NOOP("(DVD menu)", "Title Menu"),
35 QT_TRANSLATE_NOOP("(DVD menu)", "Root Menu"),
36 QT_TRANSLATE_NOOP("(DVD menu)", "Subpicture Menu"),
37 QT_TRANSLATE_NOOP("(DVD menu)", "Audio Menu"),
38 QT_TRANSLATE_NOOP("(DVD menu)", "Angle Menu"),
39 //: DVD part/chapter menu
40 QT_TRANSLATE_NOOP("(DVD menu)", "Part Menu")
41};
42
43const QMap<int, int> MythDVDBuffer::kSeekSpeedMap =
44{ { 3, 1 }, { 5, 2 }, { 10, 4 }, { 20, 8 },
45 { 30, 10 }, { 60, 15 }, { 120, 20 }, { 180, 60 } };
46
47MythDVDBuffer::MythDVDBuffer(const QString &Filename)
49{
51}
52
54{
56
57 CloseDVD();
58 m_menuBtnLock.lock();
60 m_menuBtnLock.unlock();
62}
63
65{
66 QMutexLocker contextLocker(&m_contextLock);
67 m_rwLock.lockForWrite();
68 if (m_dvdnav)
69 {
70 SetDVDSpeed(-1);
71 dvdnav_close(m_dvdnav);
72 m_dvdnav = nullptr;
73 }
74
75 if (m_context)
76 {
78 m_context = nullptr;
79 }
80
81 m_gotStop = false;
83 m_rwLock.unlock();
84}
85
87{
88 m_rwLock.lockForWrite();
89 for (QList<std::chrono::seconds> chapters : std::as_const(m_chapterMap))
90 chapters.clear();
91 m_chapterMap.clear();
92 m_rwLock.unlock();
93}
94
95long long MythDVDBuffer::SeekInternal(long long Position, int Whence)
96{
97 long long ret = -1;
98
99 m_posLock.lockForWrite();
100
101 // Optimize no-op seeks
102 if (m_readAheadRunning &&
103 ((Whence == SEEK_SET && Position == m_readPos) || (Whence == SEEK_CUR && Position == 0)))
104 {
105 ret = m_readPos;
106 m_posLock.unlock();
107 return ret;
108 }
109
110 // only valid for SEEK_SET & SEEK_CUR
111 long long new_pos = (SEEK_SET==Whence) ? Position : m_readPos + Position;
112
113 // Here we perform a normal seek. When successful we
114 // need to call ResetReadAhead(). A reset means we will
115 // need to refill the buffer, which takes some time.
116 if ((SEEK_END == Whence) || ((SEEK_CUR == Whence) && new_pos != 0))
117 {
118 errno = EINVAL;
119 ret = -1;
120 }
121 else
122 {
123 NormalSeek(new_pos);
124 ret = new_pos;
125 }
126
127 if (ret >= 0)
128 {
129 m_readPos = ret;
130 m_ignoreReadPos = -1;
133 m_readAdjust = 0;
134 }
135 else
136 {
137 QString cmd = QString("Seek(%1, %2)").arg(Position)
138 .arg(seek2string(Whence));
139 LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
140 }
141
142 m_posLock.unlock();
143 m_generalWait.wakeAll();
144 return ret;
145}
146
147long long MythDVDBuffer::NormalSeek(long long Time)
148{
149 QMutexLocker locker(&m_seekLock);
150 return Seek(Time);
151}
152
153bool MythDVDBuffer::SectorSeek(uint64_t Sector)
154{
155 dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
156
157 QMutexLocker lock(&m_seekLock);
158
159 dvdRet = dvdnav_sector_search(m_dvdnav, static_cast<int64_t>(Sector), SEEK_SET);
160
161 if (dvdRet == DVDNAV_STATUS_ERR)
162 {
163 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("SectorSeek() to sector %1 failed").arg(Sector));
164 return false;
165 }
166 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVD Playback SectorSeek() sector: %1").arg(Sector));
167 return true;
168}
169
170long long MythDVDBuffer::Seek(long long Time)
171{
172 dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
173
174 int seekSpeed = 0;
175 int ffrewSkip = 1;
176 if (m_parent)
177 ffrewSkip = m_parent->GetFFRewSkip();
178
179 if (ffrewSkip != 1 && ffrewSkip != 0 && Time != 0)
180 {
181 auto it = kSeekSpeedMap.lowerBound(static_cast<int>(std::abs(Time)));
182 if (it == kSeekSpeedMap.end())
183 seekSpeed = kSeekSpeedMap.last();
184 else
185 seekSpeed = *it;
186 if (Time < 0)
187 seekSpeed = -seekSpeed;
188 dvdRet = dvdnav_relative_time_search(m_dvdnav, seekSpeed);
189 }
190 else
191 {
193 dvdRet = dvdnav_absolute_time_search(m_dvdnav, m_seektime.count(), 0);
194 }
195
196 LOG(VB_PLAYBACK, LOG_DEBUG, QString("DVD Playback Seek() time: %1; seekSpeed: %2")
197 .arg(Time).arg(seekSpeed));
198
199 if (dvdRet == DVDNAV_STATUS_ERR)
200 {
201 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Seek() to time %1 failed").arg(Time));
202 return -1;
203 }
204
205 if (!m_inMenu)
206 {
207 m_gotStop = false;
208 if (Time > 0 && ffrewSkip == 1)
209 m_seeking = true;
210 }
211
212 return m_currentpos;
213}
214
215bool MythDVDBuffer::IsOpen(void) const
216{
217 return m_dvdnav;
218}
219
221{
222 return GetTotalTimeOfTitle() >= 2min;
223}
224
226{
227 return m_still > 0s;
228}
229
231{
232 // Don't allow seeking when the ringbuffer is
233 // waiting for the player to flush its buffers
234 // or waiting for the decoder.
235 return ((m_dvdEvent != DVDNAV_WAIT) &&
236 (m_dvdEvent != DVDNAV_HOP_CHANNEL) &&
238}
239
240void MythDVDBuffer::GetDescForPos(QString &Description) const
241{
242 if (m_inMenu)
243 {
244 if ((m_part <= DVD_MENU_MAX) && !DVDMenuTable[m_part].empty())
245 Description = QCoreApplication::translate("(DVD menu)", DVDMenuTable[m_part].c_str());
246 }
247 else
248 {
249 Description = tr("Title %1 chapter %2").arg(m_title).arg(m_part);
250 }
251}
252
259bool MythDVDBuffer::OpenFile(const QString &Filename, std::chrono::milliseconds /*Retry*/)
260{
261 QMutexLocker contextLocker(&m_contextLock);
262 m_rwLock.lockForWrite();
263
264 if (m_dvdnav)
265 {
266 m_rwLock.unlock();
267 CloseDVD();
268 m_rwLock.lockForWrite();
269 }
270
271 m_safeFilename = Filename;
272 m_filename = Filename;
273 dvdnav_status_t res = dvdnav_open(&m_dvdnav, m_filename.toLocal8Bit().constData());
274 if (res == DVDNAV_STATUS_ERR)
275 {
276 m_lastError = tr("Failed to open DVD device at %1").arg(m_filename);
277 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open DVD device at '%1'").arg(m_filename));
278 m_rwLock.unlock();
279 return false;
280 }
281
282 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened DVD device at '%1'").arg(m_filename));
283
284 if (m_context)
285 {
287 m_context = nullptr;
288 }
289
290 // Set preferred languages
291 QString lang = gCoreContext->GetSetting("Language").section('_', 0, 0);
292
293 dvdnav_menu_language_select(m_dvdnav, lang.toLatin1().data());
294 dvdnav_audio_language_select(m_dvdnav, lang.toLatin1().data());
295 dvdnav_spu_language_select(m_dvdnav, lang.toLatin1().data());
296
297 dvdnav_set_readahead_flag(m_dvdnav, 0);
298 dvdnav_set_PGC_positioning_flag(m_dvdnav, 1);
299
300 // Check we aren't starting in a still frame (which will probably fail as
301 // ffmpeg will be unable to create a decoder)
302 if (dvdnav_get_next_still_flag(m_dvdnav))
303 {
304 LOG(VB_GENERAL, LOG_NOTICE,
305 LOC + "The selected title is a still frame. "
306 "Playback is likely to fail - please raise a bug report at "
307 "https://github.com/MythTV/mythtv/issues");
308 }
309
311
312 SetDVDSpeed();
313 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Serial Number %1").arg(m_discSerialNumber));
315 m_setSwitchToNext = false;
316 m_ateof = false;
317 m_commsError = false;
318 m_numFailures = 0;
319 m_rawBitrate = 8000;
321
322 m_rwLock.unlock();
323
324 return true;
325}
326
328{
329 LOG(VB_GENERAL, LOG_INFO, LOC + "Resetting DVD device.");
330
331 // if a DVDNAV_STOP event has been emitted, dvdnav_reset does not
332 // seem to restore the state, hence we need to re-create
333 if (m_gotStop)
334 {
335 LOG(VB_GENERAL, LOG_ERR, LOC +
336 "DVD errored after initial scan - trying again");
337 CloseDVD();
339 if (!m_dvdnav)
340 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-open DVD.");
341 }
342
343 if (m_dvdnav)
344 {
345 // Set preferred languages
346 QString lang = gCoreContext->GetSetting("Language").section('_', 0, 0);
347 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Setting DVD languages to %1")
348 .arg(lang));
349
350 QMutexLocker lock(&m_seekLock);
351 dvdnav_reset(m_dvdnav);
352 dvdnav_menu_language_select(m_dvdnav, lang.toLatin1().data());
353 dvdnav_audio_language_select(m_dvdnav, lang.toLatin1().data());
354 dvdnav_spu_language_select(m_dvdnav, lang.toLatin1().data());
356 }
357
358 m_endPts = 0;
359 m_timeDiff = 0;
360
361 QMutexLocker contextLocker(&m_contextLock);
362 if (m_context)
363 {
365 m_context = nullptr;
366 }
367
368 return m_dvdnav;
369}
370
371void MythDVDBuffer::GetChapterTimes(QList<std::chrono::seconds> &Times)
372{
373 if (!m_chapterMap.contains(m_title))
375 if (!m_chapterMap.contains(m_title))
376 return;
377 const QList<std::chrono::seconds>& chapters = m_chapterMap.value(m_title);
378 std::copy(chapters.cbegin(), chapters.cend(), std::back_inserter(Times));
379}
380
381static constexpr mpeg::chrono::pts HALFSECOND { 45000_pts };
382std::chrono::seconds MythDVDBuffer::GetChapterTimes(int Title)
383{
384 if (!m_dvdnav)
385 return 0s;
386
387 uint64_t duration = 0;
388 uint64_t *times = nullptr;
389 uint32_t num = dvdnav_describe_title_chapters(m_dvdnav, Title, &times, &duration);
390
391 if (num < 1)
392 {
393 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve chapter data");
394 return 0s;
395 }
396
397 QList<std::chrono::seconds> chapters;
398 // add the start
399 chapters.append(0s);
400 // don't add the last 'chapter' - which is the title end
401 if (num > 1)
402 for (uint i = 0; i < num - 1; i++)
403 chapters.append(duration_cast<std::chrono::seconds>(mpeg::chrono::pts(times[i]) + HALFSECOND));
404
405 // Assigned via calloc, must be free'd not deleted
406 if (times)
407 free(times);
408 m_chapterMap.insert(Title, chapters);
409 return duration_cast<std::chrono::seconds>(mpeg::chrono::pts(duration) + HALFSECOND);
410}
411
415{
416 uint32_t pos = 0;
417 uint32_t length = 1;
418 if (m_dvdnav)
419 {
420 if (dvdnav_get_position(m_dvdnav, &pos, &length) == DVDNAV_STATUS_ERR)
421 {
422 // try one more time
423 dvdnav_get_position(m_dvdnav, &pos, &length);
424 }
425 }
426 return pos * DVD_BLOCK_SIZE;
427}
428
430{
431 return m_title;
432}
433
435{
436 return m_playerWait;
437}
438
440{
441 return m_part;
442}
443
445{
446 return m_currentAngle;
447}
448
450{
452}
453
455{
456 return m_titleLength;
457}
458
459std::chrono::seconds MythDVDBuffer::GetChapterLength(void) const
460{
461 return duration_cast<std::chrono::seconds>(m_pgLength);
462}
463
464void MythDVDBuffer::GetPartAndTitle(int &Part, int &Title) const
465{
466 Part = m_part;
467 Title = m_title;
468}
469
470uint32_t MythDVDBuffer::AdjustTimestamp(uint32_t Timestamp) const
471{
472 uint32_t newTimestamp = Timestamp;
473 if (newTimestamp >= m_timeDiff)
474 newTimestamp -= m_timeDiff;
475 return newTimestamp;
476}
477
478int64_t MythDVDBuffer::AdjustTimestamp(int64_t Timestamp) const
479{
480 int64_t newTimestamp = Timestamp;
481 if ((newTimestamp != AV_NOPTS_VALUE) && (newTimestamp >= m_timeDiff))
482 newTimestamp -= m_timeDiff;
483 return newTimestamp;
484}
485
487{
488 QMutexLocker contextLocker(&m_contextLock);
489 if (m_context)
491 return m_context;
492}
493
495{
496 return m_dvdEvent;
497}
498
500{
502 {
503 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
504 m_playerWait = true;
505 int count = 0;
506 // cppcheck-suppress knownConditionTrueFalse
507 while (m_playerWait && count++ < 200)
508 {
509 m_rwLock.unlock();
510 std::this_thread::sleep_for(10ms);
511 m_rwLock.lockForWrite();
512 }
513
514 // cppcheck-suppress knownConditionTrueFalse
515 if (m_playerWait)
516 {
517 LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
518 m_playerWait = false;
519 }
520 }
521}
522
524{
525 uint8_t* blockBuf = nullptr;
526 uint tot = 0;
527 int needed = static_cast<int>(Size);
528 char* dest = static_cast<char*>(Buffer);
529 int offset = 0;
530 bool waiting = false;
531
532 if (m_gotStop)
533 {
534 LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read: called after DVDNAV_STOP");
535 errno = EBADF;
536 return -1;
537 }
538
540 LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running.");
541
542 while ((m_processState != PROCESS_WAIT) && needed)
543 {
544 bool reprocessing { false };
545 blockBuf = m_dvdBlockWriteBuf.data();
546
548 {
550 reprocessing = true;
551 }
552 else
553 {
554 m_dvdStat = dvdnav_get_next_cache_block(m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize);
555 reprocessing = false;
556 }
557
558 if (m_dvdStat == DVDNAV_STATUS_ERR)
559 {
560 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1")
561 .arg(dvdnav_err_to_string(m_dvdnav)));
562 errno = EIO;
563 return -1;
564 }
565
566 switch (m_dvdEvent)
567 {
568 // Standard packet for decoding
569 case DVDNAV_BLOCK_OK:
570 {
571 // copy block
572 if (!m_seeking)
573 {
574 memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
575 tot += DVD_BLOCK_SIZE;
576 }
577
578 // release buffer
579 if (blockBuf != m_dvdBlockWriteBuf.data())
580 dvdnav_free_cache_block(m_dvdnav, blockBuf);
581
582 // debug
583 LOG(VB_PLAYBACK|VB_FILE, LOG_DEBUG, LOC + "DVDNAV_BLOCK_OK");
584 }
585 break;
586
587 // cell change
588 case DVDNAV_CELL_CHANGE:
589 {
590 // get event details
591 auto *cell_event = reinterpret_cast<dvdnav_cell_change_event_t*>(blockBuf);
592
593 // update information for the current cell
594 m_cellChanged = true;
595 if (m_pgcLength != mpeg::chrono::pts(cell_event->pgc_length))
596 m_pgcLengthChanged = true;
597 m_pgLength = mpeg::chrono::pts(cell_event->pg_length);
598 m_pgcLength = mpeg::chrono::pts(cell_event->pgc_length);
599 m_cellStart = mpeg::chrono::pts(cell_event->cell_start);
600 m_pgStart = cell_event->pg_start;
601
602 // update title/part/still/menu information
606 uint32_t pos = 0;
607 uint32_t length = 0;
608 uint32_t stillTimer = dvdnav_get_next_still_flag(m_dvdnav);
609 m_still = 0s;
610 m_titleParts = 0;
611 dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
612 dvdnav_get_number_of_parts(m_dvdnav, m_title, &m_titleParts);
613 dvdnav_get_position(m_dvdnav, &pos, &length);
614 dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
615
616 if (m_title != m_lastTitle)
617 {
618 // Populate the chapter list for this title, used in the OSD menu
620 }
621
622 m_titleLength = length * DVD_BLOCK_SIZE;
623 if (!m_seeking)
625
626 // debug
627 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
628 QString("---- DVDNAV_CELL_CHANGE - Cell #%1 Menu %2 Length %3")
629 .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
630 .arg(static_cast<double>(cell_event->cell_length) / 90000.0, 0, 'f', 1));
631 QString still;
632 if (stillTimer == 0)
633 {
634 still = QString("Length: %1 seconds")
635 .arg(duration_cast<std::chrono::seconds>(m_pgcLength).count());
636 }
637 else if (stillTimer < 0xff)
638 {
639 still = QString("Stillframe: %1 seconds").arg(stillTimer);
640 }
641 else
642 {
643 still = QString("Infinite stillframe");
644 }
645
646 if (m_title == 0)
647 {
648 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Menu #%1 %2")
649 .arg(m_part).arg(still));
650 }
651 else
652 {
653 LOG(VB_PLAYBACK, LOG_INFO,
654 LOC + QString("Title #%1: %2 Part %3 of %4")
655 .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
656 }
657
658 // wait unless it is a transition from one normal video cell to
659 // another or the same menu id
660 if ((m_title != m_lastTitle) &&
661 // cppcheck-suppress knownConditionTrueFalse
662 (m_title != 0 || m_lastTitle != 0 || (m_part != m_lastPart)))
663 {
665 }
666
667 // Make sure the still frame timer is reset.
668 if (m_parent)
670
671 // clear menus/still frame selections
675 m_buttonSelected = false;
676 m_vobid = m_cellid = 0;
677 m_cellRepeated = false;
678 m_buttonSeenInCell = false;
679
681
682 // release buffer
683 if (blockBuf != m_dvdBlockWriteBuf.data())
684 dvdnav_free_cache_block(m_dvdnav, blockBuf);
685 }
686 break;
687
688 // new colour lookup table for subtitles/menu buttons
689 case DVDNAV_SPU_CLUT_CHANGE:
690 {
691 // debug
692 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_SPU_CLUT_CHANGE");
693
694 // store the new clut
695 // m_clut = std::to_array(blockBuf); // C++20
696 std::copy(blockBuf, blockBuf + (16 * sizeof(uint32_t)),
697 reinterpret_cast<uint8_t*>(m_clut.data()));
698 // release buffer
699 if (blockBuf != m_dvdBlockWriteBuf.data())
700 dvdnav_free_cache_block(m_dvdnav, blockBuf);
701 }
702 break;
703
704 // new Sub-picture Unit stream (subtitles/menu buttons)
705 case DVDNAV_SPU_STREAM_CHANGE:
706 {
707 // get event details
708 auto* spu = reinterpret_cast<dvdnav_spu_stream_change_event_t*>(blockBuf);
709
710 // clear any existing subs/buttons
712
713 // not sure
715 m_curSubtitleTrack = dvdnav_get_active_spu_stream(m_dvdnav);
716
717 // debug
718 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
719 QString("DVDNAV_SPU_STREAM_CHANGE: "
720 "physicalwide %1, physicalletterbox %2, "
721 "physicalpanscan %3, currenttrack %4")
722 .arg(spu->physical_wide).arg(spu->physical_letterbox)
723 .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
724
725 // release buffer
726 if (blockBuf != m_dvdBlockWriteBuf.data())
727 dvdnav_free_cache_block(m_dvdnav, blockBuf);
728 }
729 break;
730
731 // the audio stream changed
732 case DVDNAV_AUDIO_STREAM_CHANGE:
733 {
734 // get event details
735 auto* audio = reinterpret_cast<dvdnav_audio_stream_change_event_t*>(blockBuf);
736
737 // retrieve the new track
738 int new_track = GetAudioTrackNum(static_cast<uint>(audio->physical));
739
740 // debug
741 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
742 QString("DVDNAV_AUDIO_STREAM_CHANGE: old %1 new %2, physical %3, logical %4")
743 .arg(m_curAudioTrack).arg(new_track)
744 .arg(audio->physical).arg(audio->logical));
745
746 // tell the decoder to reset the audio streams if necessary
747 if (new_track != m_curAudioTrack)
748 {
749 m_curAudioTrack = new_track;
751 }
752
753 // release buffer
754 if (blockBuf != m_dvdBlockWriteBuf.data())
755 dvdnav_free_cache_block(m_dvdnav, blockBuf);
756 }
757 break;
758
759 // navigation packet
760 case DVDNAV_NAV_PACKET:
761 {
762 QMutexLocker lock(&m_seekLock);
763 bool lastInMenu = m_inMenu;
764
765 // retrieve the latest Presentation Control and
766 // Data Search Information structures
767 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
768 dsi_t *dsi = dvdnav_get_current_nav_dsi(m_dvdnav);
769
770 if (pci == nullptr || dsi == nullptr)
771 {
772 // Something has gone horribly wrong if this happens
773 LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDNAV_NAV_PACKET - Error retrieving DVD data structures - dsi 0x%1, pci 0x%2")
774 .arg(reinterpret_cast<uint64_t>(dsi), 0, 16)
775 .arg(reinterpret_cast<uint64_t>(pci), 0, 16));
776 }
777 else
778 {
779 // If the start PTS of this block is not the
780 // same as the end PTS of the last block,
781 // we've got a timestamp discontinuity
782 int64_t diff = static_cast<int64_t>(pci->pci_gi.vobu_s_ptm) - m_endPts;
783 if (diff != 0)
784 {
785 if (!reprocessing && !m_skipstillorwait)
786 {
787 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
788 .arg(pci->pci_gi.vobu_s_ptm).arg(m_endPts).arg(diff));
790 break;
791 }
792
793 m_timeDiff += diff;
794 }
795
796 m_endPts = pci->pci_gi.vobu_e_ptm;
797 m_inMenu = (pci->hli.hl_gi.btn_ns > 0);
798
799 if (m_inMenu && m_seeking && (dsi->synci.sp_synca[0] & 0x80000000) &&
801 {
802 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Jumped into middle of menu: lba %1, dest %2")
803 .arg(pci->pci_gi.nv_pck_lbn)
804 .arg(pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff)));
805
806 // We're in a menu, the subpicture packets are somewhere behind us
807 // and we've not decoded any subpicture.
808 // That probably means we've jumped into the middle of a menu.
809 // We'd better jump back to get the subpicture packet(s) otherwise
810 // there's no menu highlight to show.
811 m_seeking = false;
812 dvdnav_sector_search(m_dvdnav, pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff), SEEK_SET);
813 }
814 else
815 {
816 pci_t pci_copy = *pci;
817
818 pci_copy.pci_gi.vobu_s_ptm = AdjustTimestamp(pci->pci_gi.vobu_s_ptm);
819 pci_copy.pci_gi.vobu_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_e_ptm);
820
821 if (pci->pci_gi.vobu_se_e_ptm != 0)
822 pci_copy.pci_gi.vobu_se_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_se_e_ptm);
823
824 QMutexLocker contextLocker(&m_contextLock);
825 if (m_context)
827
828 m_context = new MythDVDContext(*dsi, pci_copy);
829
830 if (m_inMenu != lastInMenu)
831 {
832 if (m_inMenu)
833 {
836 }
837 else
838 {
840 }
841 }
842
843 // if we are in a looping menu, we don't want to reset the
844 // selected button when we restart
845 m_vobid = dsi->dsi_gi.vobu_vob_idn;
846 m_cellid = dsi->dsi_gi.vobu_c_idn;
849 {
850 m_cellRepeated = true;
851 }
852
853 // update our status
854 m_currentTime = mpeg::chrono::pts(dvdnav_get_current_time(m_dvdnav));
856
857 if (m_seeking)
858 {
859 auto relativetime = duration_cast<std::chrono::seconds>(m_seektime - m_currentTime);
860 if (abs(relativetime) <= 1s)
861 {
862 m_seeking = false;
863 m_seektime = 0_pts;
864 }
865 else
866 {
867 dvdnav_relative_time_search(m_dvdnav, relativetime.count() * 2);
868 }
869 }
870
871 // update the button stream number if this is the
872 // first NAV pack containing button information
873 if ( (pci->hli.hl_gi.hli_ss & 0x03) == 0x01 )
874 {
875 m_buttonStreamID = 32;
876 int aspect = dvdnav_get_video_aspect(m_dvdnav);
877
878 // workaround where dvd menu is
879 // present in VTS_DOMAIN. dvdnav adds 0x80 to stream id
880 // proper fix should be put in dvdnav sometime
881 int8_t spustream = dvdnav_get_active_spu_stream(m_dvdnav) & 0x7f;
882
883 if (aspect != 0 && spustream > 0)
884 m_buttonStreamID += spustream;
885
886 m_buttonSeenInCell = true;
887 }
888
889 // debug
890 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_NAV_PACKET - time:%1, lba:%2, vob:%3, cell:%4, seeking:%5, seektime:%6")
891 .arg(m_context->GetStartPTS())
892 .arg(m_context->GetLBA())
893 .arg(m_vobid)
894 .arg(m_cellid)
895 .arg(m_seeking)
896 .arg(m_seektime.count()));
897
898 if (!m_seeking)
899 {
900 memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
901 tot += DVD_BLOCK_SIZE;
902 }
903 }
904 }
905 // release buffer
906 if (blockBuf != m_dvdBlockWriteBuf.data())
907 dvdnav_free_cache_block(m_dvdnav, blockBuf);
908 }
909 break;
910
911 case DVDNAV_HOP_CHANNEL:
912 {
913 if (!reprocessing && !m_skipstillorwait)
914 {
915 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL - waiting");
917 break;
918 }
919
920 // debug
921 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL");
923 }
924 break;
925
926 // no op
927 case DVDNAV_NOP:
928 break;
929
930 // new Video Title Set - aspect ratio/letterboxing
931 case DVDNAV_VTS_CHANGE:
932 {
933 // retrieve event details
934 auto* vts = reinterpret_cast<dvdnav_vts_change_event_t*>(blockBuf);
935
936 // update player
937 int aspect = dvdnav_get_video_aspect(m_dvdnav);
938 if (aspect == 2) // 4:3
939 m_forcedAspect = 4.0F / 3.0F;
940 else if (aspect == 3) // 16:9
941 m_forcedAspect = 16.0F / 9.0F;
942 else
943 m_forcedAspect = -1;
944 int permission = dvdnav_get_video_scale_permission(m_dvdnav);
945
946 // debug
947 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
948 QString("DVDNAV_VTS_CHANGE: old_vtsN %1, new_vtsN %2, "
949 "aspect %3, perm %4")
950 .arg(vts->old_vtsN).arg(vts->new_vtsN)
951 .arg(aspect).arg(permission));
952
953 // trigger a rescan of the audio streams
954 if ((vts->old_vtsN != vts->new_vtsN) ||(vts->old_domain != vts->new_domain))
956
957 // Make sure we know we're not staying in the
958 // same cell (same vobid/cellid values can
959 // occur in every VTS)
960 m_lastvobid = m_vobid = 0;
962
963 // release buffer
964 if (blockBuf != m_dvdBlockWriteBuf.data())
965 dvdnav_free_cache_block(m_dvdnav, blockBuf);
966 }
967 break;
968
969 // menu button
970 case DVDNAV_HIGHLIGHT:
971 {
972 // retrieve details
973 auto* highlight = reinterpret_cast<dvdnav_highlight_event_t*>(blockBuf);
974
975 // update the current button
976 m_menuBtnLock.lock();
977 DVDButtonUpdate(false);
979 m_menuBtnLock.unlock();
980
981 // debug
982 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
983 QString("DVDNAV_HIGHLIGHT: display %1, palette %2, "
984 "sx %3, sy %4, ex %5, ey %6, pts %7, buttonN %8")
985 .arg(highlight->display).arg(highlight->palette)
986 .arg(highlight->sx).arg(highlight->sy)
987 .arg(highlight->ex).arg(highlight->ey)
988 .arg(highlight->pts).arg(highlight->buttonN));
989
990 // release buffer
991 if (blockBuf != m_dvdBlockWriteBuf.data())
992 dvdnav_free_cache_block(m_dvdnav, blockBuf);
993 }
994 break;
995
996 // dvd still frame
997 case DVDNAV_STILL_FRAME:
998 {
999 // retrieve still frame details (length)
1000 auto* still = reinterpret_cast<dvdnav_still_event_t*>(blockBuf);
1001
1002 if (!reprocessing && !m_skipstillorwait)
1003 {
1004 if (m_still != std::chrono::seconds(still->length))
1005 {
1006 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1) - waiting")
1007 .arg(still->length));
1008 }
1009
1011 }
1012 else
1013 {
1014 // pause a little as the dvdnav VM will continue to return
1015 // this event until it has been skipped
1016 m_rwLock.unlock();
1017 std::this_thread::sleep_for(10ms);
1018 m_rwLock.lockForWrite();
1019
1020 // when scanning the file or exiting playback, skip immediately
1021 // otherwise update the timeout in the player
1023 {
1024 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Skipping DVDNAV_STILL_FRAME (%1)")
1025 .arg(still->length));
1027 }
1028 else if (m_parent)
1029 {
1030 // debug
1031 if (m_still != std::chrono::seconds(still->length))
1032 {
1033 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1)")
1034 .arg(still->length));
1035 }
1036
1037 m_still = std::chrono::seconds(still->length);
1038 Size = tot;
1040 }
1041
1042 // release buffer
1043 if (blockBuf != m_dvdBlockWriteBuf.data())
1044 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1045 }
1046 }
1047 break;
1048
1049 // wait for the player
1050 case DVDNAV_WAIT:
1051 {
1052 if (!reprocessing && !m_skipstillorwait && !waiting)
1053 {
1054 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT - waiting");
1056 }
1057 else
1058 {
1059 waiting = true;
1060
1061 //debug
1062 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");
1063
1064 // skip if required, otherwise wait (and loop)
1066 {
1067 WaitSkip();
1068 }
1069 else
1070 {
1071 m_dvdWaiting = true;
1072 m_rwLock.unlock();
1073 std::this_thread::sleep_for(10ms);
1074 m_rwLock.lockForWrite();
1075 }
1076
1077 // release buffer
1078 if (blockBuf != m_dvdBlockWriteBuf.data())
1079 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1080 }
1081 }
1082 break;
1083
1084 // exit playback
1085 case DVDNAV_STOP:
1086 {
1087 LOG(VB_GENERAL, LOG_INFO, LOC + "DVDNAV_STOP");
1088 Size = tot;
1089 m_gotStop = true;
1090
1091 // release buffer
1092 if (blockBuf != m_dvdBlockWriteBuf.data())
1093 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1094 }
1095 break;
1096
1097 // this shouldn't happen
1098 default:
1099 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown DVD event: %1").arg(m_dvdEvent));
1100 break;
1101 }
1102
1103 needed = static_cast<int>(Size - tot);
1104 offset = static_cast<int>(tot);
1105 }
1106
1108 {
1109 errno = EAGAIN;
1110 return 0;
1111 }
1112 return static_cast<int>(tot);
1113}
1114
1116{
1117 QMutexLocker lock(&m_seekLock);
1118 if (Track < 1)
1119 Seek(0);
1120 else if (Track < m_titleParts)
1121 dvdnav_part_play(m_dvdnav, m_title, Track);
1122 else
1123 return false;
1124 m_gotStop = false;
1125 return true;
1126}
1127
1129{
1130 int newPart = m_part + 1;
1131
1132 QMutexLocker lock(&m_seekLock);
1133 if (newPart < m_titleParts)
1134 {
1135 dvdnav_part_play(m_dvdnav, m_title, newPart);
1136 m_gotStop = false;
1137 return true;
1138 }
1139 return false;
1140}
1141
1143{
1144 int newPart = m_part - 1;
1145
1146 QMutexLocker lock(&m_seekLock);
1147 if (newPart > 0)
1148 dvdnav_part_play(m_dvdnav, m_title, newPart);
1149 else
1150 Seek(0);
1151 m_gotStop = false;
1152}
1153
1157std::chrono::seconds MythDVDBuffer::GetTotalTimeOfTitle(void) const
1158{
1159 return duration_cast<std::chrono::seconds>(m_pgcLength);
1160}
1161
1163{
1164 return m_forcedAspect;
1165}
1166
1169std::chrono::seconds MythDVDBuffer::GetCellStart(void) const
1170{
1171 return duration_cast<std::chrono::seconds>(m_cellStart);
1172}
1173
1177{
1178 bool ret = m_cellChanged;
1179 m_cellChanged = false;
1180 return ret;
1181}
1182
1184{
1185 return dvdnav_get_next_still_flag(m_dvdnav) > 0;
1186}
1187
1189{
1190 return m_audioStreamsChanged;
1191}
1192
1194{
1195 return m_dvdWaiting;
1196}
1197
1199{
1200 return m_titleParts;
1201}
1202
1206{
1207 bool ret = m_pgcLengthChanged;
1208 m_pgcLengthChanged = false;
1209 return ret;
1210}
1211
1213{
1214 QMutexLocker locker(&m_seekLock);
1215 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Skipping still frame.");
1216
1217 m_still = 0s;
1218 dvdnav_still_skip(m_dvdnav);
1219
1220 // Make sure the still frame timer is disabled.
1221 if (m_parent)
1223}
1224
1226{
1227 QMutexLocker locker(&m_seekLock);
1228 dvdnav_wait_skip(m_dvdnav);
1229 m_dvdWaiting = false;
1230 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exiting DVDNAV_WAIT status");
1231}
1232
1234{
1235 m_playerWait = false;
1236}
1237
1239{
1241}
1242
1244{
1245 return m_processState == PROCESS_WAIT;
1246}
1247
1250bool MythDVDBuffer::GoToMenu(const QString &str)
1251{
1252 DVDMenuID_t menuid = DVD_MENU_Escape;
1253 QMutexLocker locker(&m_seekLock);
1254
1255 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVDRingBuf: GoToMenu %1").arg(str));
1256
1257 if (str.compare("chapter") == 0)
1258 menuid = DVD_MENU_Part;
1259 else if (str.compare("root") == 0)
1260 menuid = DVD_MENU_Root;
1261 else if (str.compare("title") == 0)
1262 menuid = DVD_MENU_Title;
1263 else
1264 return false;
1265
1266 dvdnav_status_t ret = dvdnav_menu_call(m_dvdnav, menuid);
1267 return ret == DVDNAV_STATUS_OK;
1268}
1269
1275{
1276 bool success = false;
1277 QString target;
1278
1279 QMutexLocker locker(&m_seekLock);
1280
1281 if (dvdnav_is_domain_vts(m_dvdnav) && !m_inMenu)
1282 {
1283 if (dvdnav_go_up(m_dvdnav) == DVDNAV_STATUS_OK)
1284 {
1285 target = "GoUp";
1286 success = true;
1287 }
1288 else if (dvdnav_menu_call(m_dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
1289 {
1290 target = "Root";
1291 success = true;
1292 }
1293 else if (dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) == DVDNAV_STATUS_OK)
1294 {
1295 target = "Title";
1296 success = true;
1297 }
1298 else
1299 {
1300 target = "Nothing available";
1301 }
1302 }
1303 else
1304 {
1305 target = QString("No jump, %1 menu").arg(m_inMenu ? "in" : "not in");
1306 }
1307
1308 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVDRingBuf: GoBack - %1").arg(target));
1309 return success;
1310}
1311
1313{
1314 QMutexLocker locker(&m_seekLock);
1315 // This conditional appears to be unnecessary, and might have come
1316 // from a mistake in a libdvdnav resync.
1317 //if (!dvdnav_is_domain_vts(m_dvdnav))
1318 dvdnav_next_pg_search(m_dvdnav);
1319}
1320
1322{
1323 QMutexLocker locker(&m_seekLock);
1324 // This conditional appears to be unnecessary, and might have come
1325 // from a mistake in a libdvdnav resync.
1326 //if (!dvdnav_is_domain_vts(m_dvdnav))
1327 dvdnav_prev_pg_search(m_dvdnav);
1328}
1329
1330bool MythDVDBuffer::HandleAction(const QStringList &Actions, mpeg::chrono::pts /*Pts*/)
1331{
1332 if (!NumMenuButtons())
1333 return false;
1334
1335 if (Actions.contains(ACTION_UP) || Actions.contains(ACTION_CHANNELUP))
1336 MoveButtonUp();
1337 else if (Actions.contains(ACTION_DOWN) || Actions.contains(ACTION_CHANNELDOWN))
1339 else if (Actions.contains(ACTION_LEFT) || Actions.contains(ACTION_SEEKRWND))
1341 else if (Actions.contains(ACTION_RIGHT) || Actions.contains(ACTION_SEEKFFWD))
1343 else if (Actions.contains(ACTION_SELECT))
1345 else
1346 return false;
1347
1348 return true;
1349}
1350
1352{
1353 m_skipstillorwait = Ignore;
1354}
1355
1357{
1358 if (NumMenuButtons() > 1)
1359 {
1360 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1361 dvdnav_left_button_select(m_dvdnav, pci);
1362 }
1363}
1364
1366{
1367 if (NumMenuButtons() > 1)
1368 {
1369 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1370 dvdnav_right_button_select(m_dvdnav, pci);
1371 }
1372}
1373
1375{
1376 if (NumMenuButtons() > 1)
1377 {
1378 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1379 dvdnav_upper_button_select(m_dvdnav, pci);
1380 }
1381}
1382
1384{
1385 if (NumMenuButtons() > 1)
1386 {
1387 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1388 dvdnav_lower_button_select(m_dvdnav, pci);
1389 }
1390}
1391
1394{
1395 if (NumMenuButtons() > 0)
1396 {
1398 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1399 dvdnav_button_activate(m_dvdnav, pci);
1400 }
1401}
1402
1404void MythDVDBuffer::GetMenuSPUPkt(uint8_t *Buffer, int Size, int StreamID, uint32_t StartTime)
1405{
1406 if (Size < 4)
1407 return;
1408
1410 return;
1411
1412 QMutexLocker lock(&m_menuBtnLock);
1413
1415 auto *spu_pkt = reinterpret_cast<uint8_t*>(av_malloc(static_cast<size_t>(Size)));
1416 memcpy(spu_pkt, Buffer, static_cast<size_t>(Size));
1417 m_menuSpuPkt = spu_pkt;
1418 m_menuBuflength = Size;
1419 if (!m_buttonSelected)
1420 {
1422 m_buttonSelected = true;
1423 }
1424
1425 if (DVDButtonUpdate(false))
1426 {
1427 int32_t gotbutton = 0;
1429 m_menuSpuPkt, m_menuBuflength, StartTime);
1430 }
1431}
1432
1435{
1436 // this is unlocked by ReleaseMenuButton
1437 m_menuBtnLock.lock();
1438
1439 if ((m_menuBuflength > 4) && m_buttonExists && (NumMenuButtons() > 0))
1440 {
1441 Version = m_buttonVersion;
1442 return &(m_dvdMenuButton);
1443 }
1444
1445 return nullptr;
1446}
1447
1448
1450{
1451 m_menuBtnLock.unlock();
1452}
1453
1457{
1458 QRect rect(0,0,0,0);
1459 if (!m_buttonExists)
1460 return rect;
1461 rect.setRect(m_hlButton.x(), m_hlButton.y(), m_hlButton.width(), m_hlButton.height());
1462 return rect;
1463}
1464
1468bool MythDVDBuffer::DecodeSubtitles(AVSubtitle *Subtitle, int *GotSubtitles,
1469 const uint8_t *SpuPkt, int BufSize, uint32_t StartTime)
1470{
1471 AlphaArray alpha {0, 0, 0, 0};
1472 PaletteArray palette {0, 0, 0, 0};
1473
1474 if (!SpuPkt)
1475 return false;
1476
1477 if (BufSize < 4)
1478 return false;
1479
1480 bool force_subtitle_display = false;
1481 Subtitle->rects = nullptr;
1482 Subtitle->num_rects = 0;
1483 Subtitle->start_display_time = StartTime;
1484 Subtitle->end_display_time = StartTime;
1485
1486 int cmd_pos = qFromBigEndian<qint16>(SpuPkt + 2);
1487 while ((cmd_pos + 4) < BufSize)
1488 {
1489 int offset1 = -1;
1490 int offset2 = -1;
1491 int date = qFromBigEndian<qint16>(SpuPkt + cmd_pos);
1492 int next_cmd_pos = qFromBigEndian<qint16>(SpuPkt + cmd_pos + 2);
1493 int pos = cmd_pos + 4;
1494 int x1 = 0;
1495 int x2 = 0;
1496 int y1 = 0;
1497 int y2 = 0;
1498 while (pos < BufSize)
1499 {
1500 int cmd = SpuPkt[pos++];
1501 switch(cmd)
1502 {
1503 case 0x00:
1504 force_subtitle_display = true;
1505 break;
1506 case 0x01:
1507 Subtitle->start_display_time = ((static_cast<uint>(date) << 10) / 90) + StartTime;
1508 break;
1509 case 0x02:
1510 Subtitle->end_display_time = ((static_cast<uint>(date) << 10) / 90) + StartTime;
1511 break;
1512 case 0x03:
1513 {
1514 if ((BufSize - pos) < 2)
1515 goto fail;
1516
1517 palette[3] = SpuPkt[pos] >> 4;
1518 palette[2] = SpuPkt[pos] & 0x0f;
1519 palette[1] = SpuPkt[pos + 1] >> 4;
1520 palette[0] = SpuPkt[pos + 1] & 0x0f;
1521 pos +=2;
1522 }
1523 break;
1524 case 0x04:
1525 {
1526 if ((BufSize - pos) < 2)
1527 goto fail;
1528 alpha[3] = SpuPkt[pos] >> 4;
1529 alpha[2] = SpuPkt[pos] & 0x0f;
1530 alpha[1] = SpuPkt[pos + 1] >> 4;
1531 alpha[0] = SpuPkt[pos + 1] & 0x0f;
1532 pos +=2;
1533 }
1534 break;
1535 case 0x05:
1536 {
1537 if ((BufSize - pos) < 6)
1538 goto fail;
1539 x1 = (SpuPkt[pos] << 4) | (SpuPkt[pos + 1] >> 4);
1540 x2 = ((SpuPkt[pos + 1] & 0x0f) << 8) | SpuPkt[pos + 2];
1541 y1 = (SpuPkt[pos + 3] << 4) | (SpuPkt[pos + 4] >> 4);
1542 y2 = ((SpuPkt[pos + 4] & 0x0f) << 8) | SpuPkt[pos + 5];
1543 pos +=6;
1544 }
1545 break;
1546 case 0x06:
1547 {
1548 if ((BufSize - pos) < 4)
1549 goto fail;
1550 offset1 = qFromBigEndian<qint16>(SpuPkt + pos);
1551 offset2 = qFromBigEndian<qint16>(SpuPkt + pos + 2);
1552 pos +=4;
1553 }
1554 break;
1555 case 0x07:
1556 {
1557 if ((BufSize - pos) < 2)
1558 goto fail;
1559
1560 pos += qFromBigEndian<qint16>(SpuPkt + pos);
1561 }
1562 break;
1563 case 0xff:
1564 default:
1565 goto the_end;
1566 }
1567 }
1568 the_end:
1569 if (offset1 >= 0)
1570 {
1571 int width = x2 - x1 + 1;
1572 width = std::max(width, 0);
1573 int height = y2 - y1 + 1;
1574 height = std::max(height, 0);
1575 if (width > 0 && height > 0)
1576 {
1577 if (Subtitle->rects != nullptr)
1578 {
1579 for (uint i = 0; i < Subtitle->num_rects; i++)
1580 {
1581 av_free(Subtitle->rects[i]->data[0]);
1582 av_free(Subtitle->rects[i]->data[1]);
1583 av_freep(reinterpret_cast<void*>(&Subtitle->rects[i]));
1584 }
1585 av_freep(reinterpret_cast<void*>(&Subtitle->rects));
1586 Subtitle->num_rects = 0;
1587 }
1588
1589 auto *bitmap = static_cast<uint8_t*>(av_malloc(static_cast<size_t>(width) * height));
1590 Subtitle->num_rects = (NumMenuButtons() > 0) ? 2 : 1;
1591 Subtitle->rects = static_cast<AVSubtitleRect**>(av_mallocz(sizeof(AVSubtitleRect*) * Subtitle->num_rects));
1592 for (uint i = 0; i < Subtitle->num_rects; i++)
1593 Subtitle->rects[i] = static_cast<AVSubtitleRect*>(av_mallocz(sizeof(AVSubtitleRect)));
1594 Subtitle->rects[0]->data[1] = static_cast<uint8_t*>(av_mallocz(4_UZ * 4_UZ));
1595 DecodeRLE(bitmap, width * 2, width, (height + 1) / 2,
1596 SpuPkt, offset1 * 2, BufSize);
1597 DecodeRLE(bitmap + width, width * 2, width, height / 2,
1598 SpuPkt, offset2 * 2, BufSize);
1599 GuessPalette(reinterpret_cast<uint32_t*>(Subtitle->rects[0]->data[1]), palette, alpha);
1600 Subtitle->rects[0]->data[0] = bitmap;
1601 Subtitle->rects[0]->x = x1;
1602 Subtitle->rects[0]->y = y1;
1603 Subtitle->rects[0]->w = width;
1604 Subtitle->rects[0]->h = height;
1605 Subtitle->rects[0]->type = SUBTITLE_BITMAP;
1606 Subtitle->rects[0]->nb_colors = 4;
1607 Subtitle->rects[0]->linesize[0] = width;
1608 if (NumMenuButtons() > 0)
1609 {
1610 Subtitle->rects[1]->type = SUBTITLE_BITMAP;
1611 Subtitle->rects[1]->data[1] = static_cast<uint8_t*>(av_malloc(4_UZ * 4_UZ));
1612 GuessPalette(reinterpret_cast<uint32_t*>(Subtitle->rects[1]->data[1]),
1614 }
1615 else
1616 {
1618 }
1619 *GotSubtitles = 1;
1620 }
1621 }
1622 if (next_cmd_pos == cmd_pos)
1623 break;
1624 cmd_pos = next_cmd_pos;
1625 }
1626 if (Subtitle->num_rects > 0)
1627 {
1628 if (force_subtitle_display)
1629 {
1630 for (unsigned i = 0; i < Subtitle->num_rects; i++)
1631 {
1632 Subtitle->rects[i]->flags |= AV_SUBTITLE_FLAG_FORCED;
1633 }
1634 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Decoded forced subtitle");
1635 }
1636 return true;
1637 }
1638fail:
1639 return false;
1640}
1641
1646{
1647 if (!m_parent)
1648 return false;
1649
1650 QSize videodispdim = m_parent->GetVideoSize();
1651 int videoheight = videodispdim.height();
1652 int videowidth = videodispdim.width();
1653
1654 int32_t button = 0;
1655 dvdnav_highlight_area_t highlight;
1656 dvdnav_get_current_highlight(m_dvdnav, &button);
1657 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1658 dvdnav_status_t dvdRet =
1659 dvdnav_get_highlight_area_from_group(pci, DVD_BTN_GRP_Wide, button,
1660 static_cast<int32_t>(ButtonMode), &highlight);
1661
1662 if (dvdRet == DVDNAV_STATUS_ERR)
1663 return false;
1664
1665 for (uint i = 0 ; i < 4 ; i++)
1666 {
1667 m_buttonAlpha[i] = 0xf & (highlight.palette >> (4 * i));
1668 m_buttonColor[i] = 0xf & (highlight.palette >> (16 + 4 * i));
1669 }
1670
1671 // If the button overlay has already been decoded, make sure
1672 // the correct palette for the current highlight is set
1673 if (m_dvdMenuButton.rects && (m_dvdMenuButton.num_rects > 1))
1674 {
1675 GuessPalette(reinterpret_cast<uint32_t*>(m_dvdMenuButton.rects[1]->data[1]),
1677 }
1678
1679 m_hlButton.setCoords(highlight.sx, highlight.sy, highlight.ex, highlight.ey);
1680 return ((highlight.sx + highlight.sy) > 0) &&
1681 (highlight.sx < videowidth && highlight.sy < videoheight);
1682}
1683
1687{
1688 if (m_buttonExists || m_dvdMenuButton.rects)
1689 {
1690 for (uint i = 0; i < m_dvdMenuButton.num_rects; i++)
1691 {
1692 AVSubtitleRect* rect = m_dvdMenuButton.rects[i];
1693 av_free(rect->data[0]);
1694 av_free(rect->data[1]);
1695 av_free(rect);
1696 }
1697 av_free(reinterpret_cast<void*>(m_dvdMenuButton.rects));
1698 m_dvdMenuButton.rects = nullptr;
1699 m_dvdMenuButton.num_rects = 0;
1700 m_buttonExists = false;
1701 }
1702}
1703
1708{
1709 if (m_menuBuflength == 0)
1710 return;
1711
1712 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clearing Menu SPU Packet" );
1713
1715
1716 av_free(m_menuSpuPkt);
1717 m_menuBuflength = 0;
1718 m_hlButton.setRect(0, 0, 0, 0);
1719}
1720
1722{
1723 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1724 int numButtons = pci->hli.hl_gi.btn_ns;
1725 if (numButtons > 0 && numButtons < 36)
1726 return numButtons;
1727 return 0;
1728}
1729
1733{
1734 uint audioLang = 0;
1735 int8_t physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1736
1737 if (physicalStreamId >= 0)
1738 {
1739 uint16_t lang = dvdnav_audio_stream_to_lang(m_dvdnav, static_cast<uint8_t>(physicalStreamId));
1740 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Audio StreamID: %1; lang: %2").arg(Index).arg(lang));
1741 audioLang = ConvertLangCode(lang);
1742 }
1743 else
1744 {
1745 LOG(VB_PLAYBACK, LOG_WARNING, LOC + QString("Audio StreamID: %1 - not found!").arg(Index));
1746 }
1747
1748 return audioLang;
1749}
1750
1757{
1758 const uint AC3_OFFSET = 0x0080;
1759 const uint DTS_OFFSET = 0x0088;
1760 const uint LPCM_OFFSET = 0x00A0;
1761 const uint MP2_OFFSET = 0x01C0;
1762
1763 if (StreamId >= MP2_OFFSET)
1764 StreamId -= MP2_OFFSET;
1765 else if (StreamId >= LPCM_OFFSET)
1766 StreamId -= LPCM_OFFSET;
1767 else if (StreamId >= DTS_OFFSET)
1768 StreamId -= DTS_OFFSET;
1769 else if (StreamId >= AC3_OFFSET)
1770 StreamId -= AC3_OFFSET;
1771
1772 int logical = -1;
1773 for (uint8_t i = 0; i < 8; i++)
1774 {
1775 // Get the physical stream number at the given index
1776 // of the logical mapping table (function name is wrong!)
1777 int8_t phys = dvdnav_get_audio_logical_stream(m_dvdnav, i);
1778 if (static_cast<uint>(phys) == StreamId)
1779 {
1780 logical = i;
1781 break;
1782 }
1783 }
1784
1785 return logical;
1786}
1787
1789{
1790 int ret = -1;
1791 int8_t physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1792 if (physicalStreamId < 0)
1793 return ret;
1794
1795 audio_attr_t attributes;
1796 if (dvdnav_get_audio_attr(m_dvdnav, static_cast<uint8_t>(physicalStreamId), &attributes) == DVDNAV_STATUS_OK)
1797 {
1798 LOG(VB_AUDIO, LOG_INFO, QString("DVD Audio Track #%1 Language Extension Code - %2")
1799 .arg(Index).arg(attributes.code_extension));
1800 return attributes.code_extension;
1801 }
1802
1803 return ret;
1804}
1805
1808{
1809 uint16_t lang = dvdnav_spu_stream_to_lang(m_dvdnav, static_cast<uint8_t>(Id));
1810 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StreamID: %1; lang: %2").arg(Id).arg(lang));
1811 return ConvertLangCode(lang);
1812}
1813
1818{
1819 int8_t logstream = -1;
1820
1821 // VM always sets stream_id to zero if we're not in the VTS
1822 // domain and always returns 0 (instead of -1) if nothing has
1823 // been found, so only try to retrieve the logical stream if
1824 // we *are* in the VTS domain or we *are* trying to map stream 0.
1825 if (dvdnav_is_domain_vts(m_dvdnav) || (StreamId == 0))
1826 logstream = dvdnav_get_spu_logical_stream(m_dvdnav, static_cast<uint8_t>(StreamId));
1827
1828 return logstream;
1829}
1830
1833{
1834 if (Code == 0)
1835 return 0;
1836
1837 std::array<QChar,2> str2 { QChar(Code >> 8), QChar(Code & 0xff) };
1838 QString str3 = iso639_str2_to_str3(QString(str2.data(), str2.size()));
1839
1840 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("code: %1; iso639: %2").arg(Code).arg(str3));
1841
1842 if (!str3.isEmpty())
1843 return static_cast<uint>(iso639_str3_to_key(str3));
1844 return 0;
1845}
1846
1851{
1852 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1853 int32_t button = pci->hli.hl_gi.fosl_btnn;
1854 if (button > 0 && !m_cellRepeated)
1855 {
1856 dvdnav_button_select(m_dvdnav,pci,button);
1857 return;
1858 }
1859 dvdnav_get_current_highlight(m_dvdnav,&button);
1860 if (button > 0 && button <= NumMenuButtons())
1861 dvdnav_button_select(m_dvdnav,pci,button);
1862 else
1863 dvdnav_button_select(m_dvdnav,pci,1);
1864}
1865
1870void MythDVDBuffer::SetTrack(uint Type, int TrackNo)
1871{
1872 if (Type == kTrackTypeSubtitle)
1873 {
1874 m_curSubtitleTrack = static_cast<int8_t>(TrackNo);
1875 m_autoselectsubtitle = TrackNo < 0;
1876 }
1877 else if (Type == kTrackTypeAudio)
1878 {
1879 m_curAudioTrack = TrackNo;
1880 dvdnav_set_active_audio_stream(m_dvdnav, static_cast<int8_t>(TrackNo));
1881 }
1882}
1883
1890{
1891 if (Type == kTrackTypeSubtitle)
1892 return m_curSubtitleTrack;
1893 if (Type == kTrackTypeAudio)
1894 return m_curAudioTrack;
1895 return 0;
1896}
1897
1899{
1900 int8_t physical = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1901 if (physical >= 0)
1902 {
1903 uint16_t channels = dvdnav_audio_stream_channels(m_dvdnav, static_cast<uint8_t>(physical));
1904 if (channels != 0xFFFf)
1905 return channels;
1906 }
1907 return 0;
1908}
1909
1911{
1912 m_audioStreamsChanged = Change;
1913}
1914
1917bool MythDVDBuffer::GetNameAndSerialNum(QString& Name, QString& SerialNumber)
1918{
1919 Name = m_discName;
1920 SerialNumber = m_discSerialNumber;
1921 return !(Name.isEmpty() && SerialNumber.isEmpty());
1922}
1923
1927{
1928 State.clear();
1929 char* dvdstate = dvdnav_get_state(m_dvdnav);
1930
1931 if (dvdstate)
1932 {
1933 State = dvdstate;
1934 free(dvdstate);
1935 }
1936
1937 return (!State.isEmpty());
1938}
1939
1943{
1944 QByteArray state = State.toUtf8();
1945 return (dvdnav_set_state(m_dvdnav, state.constData()) == DVDNAV_STATUS_OK);
1946}
1947
1954{
1955 int format = dvdnav_get_video_format(m_dvdnav);
1956 double dvdfps = (format == 1) ? 25.00 : 29.97;
1957 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Frame Rate %1").arg(dvdfps));
1958 return dvdfps;
1959}
1960
1962{
1963 return m_part == 0;
1964}
1965
1967{
1968 return ((m_titleParts == 0) || (m_part == (m_titleParts - 1)) || (m_titleParts == 1));
1969}
1970
1971void MythDVDBuffer::PlayTitleAndPart(int Title, int Part)
1972{
1973 dvdnav_part_play(m_dvdnav, Title, Part);
1974}
1975
1978{
1979 QMutexLocker lock(&m_seekLock);
1981}
1982
1985{
1986 if (m_filename.startsWith("/"))
1987 MediaMonitor::SetCDSpeed(m_filename.toLocal8Bit().constData(), Speed);
1988}
1989
1991std::chrono::seconds MythDVDBuffer::TitleTimeLeft(void) const
1992{
1994}
1995
1996std::chrono::seconds MythDVDBuffer::GetCurrentTime(void) const
1997{
1998 return duration_cast<std::chrono::seconds>(m_currentTime);
1999}
2000
2002void MythDVDBuffer::GuessPalette(uint32_t *RGBAPalette, const PaletteArray Palette, const AlphaArray Alpha)
2003{
2004 memset(RGBAPalette, 0, 16);
2005 for (int i = 0 ; i < 4 ; i++)
2006 {
2007 uint32_t yuv = m_clut[Palette[i]];
2008 uint y = (yuv >> 16) & 0xff;
2009 uint cr = (yuv >> 8) & 0xff;
2010 uint cb = (yuv >> 0) & 0xff;
2011 uint r = std::clamp(uint(y + (1.4022 * (cr - 128))), 0U, 0xFFU);
2012 uint b = std::clamp(uint(y + (1.7710 * (cb - 128))), 0U, 0xFFU);
2013 uint g = std::clamp(uint((1.7047 * y) - (0.1952 * b) - (0.5647 * r)), 0U, 0xFFU);
2014 RGBAPalette[i] = ((Alpha[i] * 17U) << 24) | (r << 16 )| (g << 8) | b;
2015 }
2016}
2017
2021int MythDVDBuffer::DecodeRLE(uint8_t *Bitmap, int Linesize, int Width, int Height,
2022 const uint8_t *Buffer, int NibbleOffset, int BufferSize)
2023{
2024 int nibbleEnd = BufferSize * 2;
2025 int x = 0;
2026 int y = 0;
2027 uint8_t *data = Bitmap;
2028 for(;;)
2029 {
2030 if (NibbleOffset >= nibbleEnd)
2031 return -1;
2032 uint v = GetNibble(Buffer, NibbleOffset++);
2033 if (v < 0x4)
2034 {
2035 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2036 if (v < 0x10)
2037 {
2038 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2039 if (v < 0x040)
2040 {
2041 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2042 if (v < 4)
2043 v |= static_cast<uint>(Width - x) << 2;
2044 }
2045 }
2046 }
2047 int len = v >> 2;
2048 len = std::min(len, Width - x);
2049 int color = v & 0x03;
2050 memset(data + x, color, static_cast<size_t>(len));
2051 x += len;
2052 if (x >= Width)
2053 {
2054 y++;
2055 if (y >= Height)
2056 break;
2057 data += Linesize;
2058 x = 0;
2059 NibbleOffset += (NibbleOffset & 1);
2060 }
2061 }
2062 return 0;
2063}
2064
2067uint MythDVDBuffer::GetNibble(const uint8_t *Buffer, int NibbleOffset)
2068{
2069 return (Buffer[NibbleOffset >> 1] >> ((1 - (NibbleOffset & 1)) << 2)) & 0xf;
2070}
2071
2076int MythDVDBuffer::IsTransparent(const uint8_t *Buffer, int Pitch, int Num, const ColorArray& Colors)
2077{
2078 for (int i = 0; i < Num; i++)
2079 {
2080 if (!Colors[*Buffer])
2081 return 0;
2082 Buffer += Pitch;
2083 }
2084 return 1;
2085}
2086
2092{
2093 ColorArray colors {};
2094
2095 if (Subtitle->num_rects == 0 || Subtitle->rects == nullptr ||
2096 Subtitle->rects[0]->w <= 0 || Subtitle->rects[0]->h <= 0)
2097 {
2098 return 0;
2099 }
2100
2101 for (int i = 0; i < Subtitle->rects[0]->nb_colors; i++)
2102 if (((reinterpret_cast<uint32_t*>(Subtitle->rects[0]->data[1])[i] >> 24)) == 0)
2103 colors[i] = 1;
2104
2105 ptrdiff_t bottom = 0;
2106 while (bottom < Subtitle->rects[0]->h &&
2107 IsTransparent(Subtitle->rects[0]->data[0] + (bottom * Subtitle->rects[0]->linesize[0]),
2108 1, Subtitle->rects[0]->w, colors))
2109 {
2110 bottom++;
2111 }
2112
2113 if (bottom == Subtitle->rects[0]->h)
2114 {
2115 av_freep(reinterpret_cast<void*>(&Subtitle->rects[0]->data[0]));
2116 Subtitle->rects[0]->w = Subtitle->rects[0]->h = 0;
2117 return 0;
2118 }
2119
2120 ptrdiff_t top = Subtitle->rects[0]->h - 1;
2121 while (top > 0 &&
2122 IsTransparent(Subtitle->rects[0]->data[0] + (top * Subtitle->rects[0]->linesize[0]), 1,
2123 Subtitle->rects[0]->w, colors))
2124 {
2125 top--;
2126 }
2127
2128 int left = 0;
2129 while (left < (Subtitle->rects[0]->w - 1) &&
2130 IsTransparent(Subtitle->rects[0]->data[0] + left, Subtitle->rects[0]->linesize[0],
2131 Subtitle->rects[0]->h, colors))
2132 {
2133 left++;
2134 }
2135
2136 int right = Subtitle->rects[0]->w - 1;
2137 while (right > 0 &&
2138 IsTransparent(Subtitle->rects[0]->data[0] + right, Subtitle->rects[0]->linesize[0],
2139 Subtitle->rects[0]->h, colors))
2140 {
2141 right--;
2142 }
2143
2144 int width = right - left + 1;
2145 int height = top - bottom + 1;
2146 auto *bitmap = static_cast<uint8_t*>(av_malloc(static_cast<size_t>(width) * height));
2147 if (!bitmap)
2148 return 1;
2149
2150 for (int y = 0; y < height; y++)
2151 {
2152 memcpy(bitmap + (static_cast<ptrdiff_t>(width) * y), Subtitle->rects[0]->data[0] + left +
2153 ((bottom + y) * Subtitle->rects[0]->linesize[0]), static_cast<size_t>(width));
2154 }
2155
2156 av_freep(reinterpret_cast<void*>(&Subtitle->rects[0]->data[0]));
2157 Subtitle->rects[0]->data[0] = bitmap;
2158 Subtitle->rects[0]->linesize[0] = width;
2159 Subtitle->rects[0]->w = width;
2160 Subtitle->rects[0]->h = height;
2161 Subtitle->rects[0]->x += left;
2162 Subtitle->rects[0]->y += bottom;
2163 return 1;
2164}
2165
2167{
2168 if (!m_dvdnav)
2169 return false;
2170
2171 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switching to Angle %1...").arg(Angle));
2172 dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, static_cast<int32_t>(Angle));
2173 if (status == DVDNAV_STATUS_OK)
2174 {
2175 m_currentAngle = Angle;
2176 return true;
2177 }
2178 return false;
2179}
2180
2182{
2183 m_parent = Parent;
2184}
Definition: polygon.h:8
static void SetCDSpeed(const char *device, int speed)
QString GetSetting(const QString &key, const QString &defaultval="")
long long SeekInternal(long long Position, int Whence) override
bool HandleAction(const QStringList &Actions, mpeg::chrono::pts Pts) override
void GetDescForPos(QString &Description) const
bool m_skipstillorwait
mpeg::chrono::pts m_pgLength
MythDVDContext * m_context
long long GetTotalReadPosition(void) const
static int DecodeRLE(uint8_t *Bitmap, int Linesize, int Width, int Height, const uint8_t *Buffer, int NibbleOffset, int BufferSize)
decodes the bitmap from the subtitle packet.
void WaitSkip(void)
void IgnoreWaitStates(bool Ignore) override
int NumMenuButtons(void) const
bool PlayTrack(int Track)
dvdnav_status_t m_dvdStat
void ClearChapterCache(void)
AVSubtitle m_dvdMenuButton
void GetMenuSPUPkt(uint8_t *Buffer, int Size, int StreamID, uint32_t StartTime)
Get SPU pkt from dvd menu subtitle stream.
void SkipDVDWaitingForPlayer(void)
mpeg::chrono::pts m_currentTime
uint16_t GetNumAudioChannels(int Index)
void GoToNextProgram(void)
int32_t m_lastPart
int NumPartsInTitle(void) const
long long NormalSeek(long long Time)
dvdnav_t * m_dvdnav
void MoveButtonRight(void)
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.
bool EndOfTitle(void) const
std::chrono::seconds GetTotalTimeOfTitle(void) const
get the total time of the title in seconds 90000 ticks = 1 sec
QMutex m_menuBtnLock
void SetDVDSpeed(void)
set dvd speed. uses the constant DVD_DRIVE_SPEED table
bool GetDVDStateSnapshot(QString &State)
Get a snapshot of the current DVD VM state.
void PlayTitleAndPart(int Title, int Part)
void CloseDVD(void)
MythDVDPlayer * m_parent
int GetAudioTrackNum(uint StreamId)
get the logical track index (into PGC_AST_CTL) of the element that maps the given physical stream id.
bool SwitchAngle(int Angle)
QRecursiveMutex m_contextLock
int32_t m_lastTitle
DvdBuffer m_dvdBlockWriteBuf
uint GetSubtitleLanguage(int Id)
Get the subtitle language from the dvd.
MythDVDBuffer(const QString &Filename)
bool RestoreDVDStateSnapshot(const QString &State)
Restore a DVD VM from a snapshot.
bool DVDButtonUpdate(bool ButtonMode)
update the dvd menu button parameters when a user changes the dvd menu button position
int GetTrack(uint Type) const
get the track the dvd should be playing.
long long m_titleLength
bool m_autoselectsubtitle
int64_t m_timeDiff
int32_t GetLastEvent(void) const
bool SectorSeek(uint64_t Sector)
void MoveButtonLeft(void)
bool DVDWaitingForPlayer(void) const
void GetChapterTimes(QList< std::chrono::seconds > &Times)
int GetNumAngles(void) const
bool IsStillFramePending(void) const
uint32_t AdjustTimestamp(uint32_t Timestamp) const
static const QMap< int, int > kSeekSpeedMap
QRect GetButtonCoords(void)
get coordinates of highlighted button
uint GetAudioLanguage(int Index)
get the audio language from the dvd
void PrevTrack(void)
int32_t m_dvdEvent
int8_t GetSubtitleTrackNum(uint StreamId)
get the logical subtitle track/stream number from the dvd
void ClearMenuSPUParameters(void)
clears the menu SPU pkt and parameters.
std::chrono::seconds TitleTimeLeft(void) const
returns seconds left in the title
std::chrono::seconds GetCellStart(void) const
get the start of the cell in seconds
mpeg::chrono::pts m_pgcLength
static int FindSmallestBoundingRectangle(AVSubtitle *Subtitle)
Obtained from ffmpeg dvdsubdec.c Used to find smallest bounded rect and helps prevent jerky picture d...
void MoveButtonUp(void)
bool OpenFile(const QString &Filename, std::chrono::milliseconds Retry=kDefaultOpenTimeout) override
Opens a dvd device for reading.
bool m_pgcLengthChanged
bool m_audioStreamsChanged
double GetFrameRate(void)
used by DecoderBase for the total frame number calculation for position map support and ffw/rew.
bool NextTrack(void)
void GuessPalette(uint32_t *RGBAPalette, PaletteArray Palette, AlphaArray Alpha)
converts palette values from YUV to RGB
std::chrono::seconds GetChapterLength(void) const
std::chrono::seconds GetCurrentTime(void) const
long long m_cellstartPos
static uint ConvertLangCode(uint16_t Code)
converts the subtitle/audio lang code to iso639.
bool PGCLengthChanged(void)
check if pgc length has changed
bool AudioStreamsChanged(void) const
void SkipStillFrame(void)
~MythDVDBuffer() override
long long m_pgStart
long long m_currentpos
int GetAudioTrackType(uint Index)
int64_t m_endPts
int GetTitle(void) const
void ActivateButton(void)
Action taken when a dvd menu button is selected.
bool IsSeekingAllowed(void) override
MythDVDContext * GetDVDContext(void)
void MoveButtonDown(void)
bool m_lastButtonSeenInCell
CLUTArray m_clut
void UnblockReading(void)
long long Seek(long long Time)
void SelectDefaultButton(void)
determines the default dvd menu button to show when you initially access the dvd menu.
bool GoToMenu(const QString &str)
jump to a dvd root or chapter menu
int GetCurrentAngle(void) const
void ReleaseMenuButton(void)
QMap< int, QList< std::chrono::seconds > > m_chapterMap
int GetPart(void) const
uint8_t * m_menuSpuPkt
int m_currentTitleAngleCount
bool DecodeSubtitles(AVSubtitle *Subtitle, int *GotSubtitles, const uint8_t *SpuPkt, int BufSize, uint32_t StartTime)
generate dvd subtitle bitmap or dvd menu bitmap.
mpeg::chrono::pts m_seektime
void SetTrack(uint Type, int TrackNo)
set the dvd subtitle/audio track used
bool m_buttonSeenInCell
void WaitForPlayer(void)
float m_forcedAspect
std::chrono::seconds m_lastStill
AVSubtitle * GetMenuSubtitle(uint &Version)
returns dvd menu button information if available.
int32_t m_titleParts
void GoToPreviousProgram(void)
bool IsOpen(void) const override
PaletteArray m_buttonAlpha
void SetParent(MythDVDPlayer *Parent)
bool IsInStillFrame(void) const override
AlphaArray m_buttonColor
bool GetNameAndSerialNum(QString &Name, QString &SerialNumber) override
Get the dvd title and serial num.
bool CellChanged(void)
check if dvd cell has changed
long long GetReadPosition(void) const override
returns current position in the PGC.
void ClearMenuButton(void)
clears the dvd menu button structures
int32_t m_dvdEventSize
bool IsWaiting(void) const
static uint GetNibble(const uint8_t *Buffer, int NibbleOffset)
copied from ffmpeg's dvdsubdec.c
void GetPartAndTitle(int &Part, int &Title) const
bool StartFromBeginning(void) override
std::chrono::seconds m_still
mpeg::chrono::pts m_cellStart
bool IsBookmarkAllowed(void) override
int8_t m_curSubtitleTrack
static int IsTransparent(const uint8_t *Buffer, int Pitch, int Num, const ColorArray &Colors)
Obtained from ffmpeg dvdsubdec.c Used to find smallest bounded rectangle.
int SafeRead(void *Buffer, uint Size) override
float GetAspectOverride(void) const
bool StartOfTitle(void) const
bool IsReadingBlocked(void)
Encapsulates playback context at any given moment.
uint32_t GetLBA(void) const
int64_t GetStartPTS(void) const
bool GetNameAndSerialNum(QString &Name, QString &SerialNumber)
void SetStillFrameTimeout(std::chrono::seconds Length)
static void DisableScreensaver()
static void RestoreScreensaver()
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
long long m_ignoreReadPos
long long m_readAdjust
QReadWriteLock m_posLock
QReadWriteLock m_rwLock
void ResetReadAhead(long long NewInternal)
Restart the read-ahead thread at the 'newinternal' position.
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
MythOpticalState m_processState
QSize GetVideoSize(void) const
Definition: mythplayer.h:131
int GetFFRewSkip(void) const
Definition: mythplayer.h:137
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
Contains listing of PMT Stream ID's for various A/V Stream types.
Definition: mpegtables.h:110
@ kTrackTypeSubtitle
Definition: decoderbase.h:31
@ kTrackTypeAudio
Definition: decoderbase.h:29
unsigned int uint
Definition: freesurround.h:24
QString iso639_str2_to_str3(const QString &str2)
Definition: iso639.cpp:68
ISO 639-1 and ISO 639-2 support functions.
static int iso639_str3_to_key(const unsigned char *iso639_2)
Definition: iso639.h:60
unsigned short uint16_t
Definition: iso6937tables.h:3
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOC
#define IncrementButtonVersion
static const std::array< const std::string, 8 > DVDMenuTable
static constexpr mpeg::chrono::pts HALFSECOND
static constexpr int8_t DVD_DRIVE_SPEED
std::array< uint8_t, 256 > ColorArray
Definition: mythdvdbuffer.h:35
std::array< uint8_t, 4 > AlphaArray
Definition: mythdvdbuffer.h:33
static constexpr int32_t DVD_MENU_MAX
Definition: mythdvdbuffer.h:28
std::array< uint8_t, 4 > PaletteArray
Definition: mythdvdbuffer.h:34
static constexpr size_t DVD_BLOCK_SIZE
Definition: mythdvdinfo.h:16
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static QString seek2string(int Whence)
@ kMythBufferDVD
static int x1
Definition: mythsocket.cpp:50
static int x2
Definition: mythsocket.cpp:51
static constexpr const char * ACTION_LEFT
Definition: mythuiactions.h:18
static constexpr const char * ACTION_DOWN
Definition: mythuiactions.h:17
static constexpr const char * ACTION_RIGHT
Definition: mythuiactions.h:19
static constexpr const char * ACTION_SELECT
Definition: mythuiactions.h:15
static constexpr const char * ACTION_UP
Definition: mythuiactions.h:16
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
#define ACTION_CHANNELUP
Definition: tv_actions.h:16
#define ACTION_SEEKFFWD
Definition: tv_actions.h:43
#define ACTION_SEEKRWND
Definition: tv_actions.h:42
#define ACTION_CHANNELDOWN
Definition: tv_actions.h:17
State
Definition: zmserver.h:69