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