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 // add the start
400 chapters.append(0s);
401 // don't add the last 'chapter' - which is the title end
402 if (num > 1)
403 for (uint i = 0; i < num - 1; i++)
404 chapters.append(duration_cast<std::chrono::seconds>(mpeg::chrono::pts(times[i]) + HALFSECOND));
405
406 // Assigned via calloc, must be free'd not deleted
407 if (times)
408 free(times); // NOLINT(cppcoreguidelines-no-malloc)
409 m_chapterMap.insert(Title, chapters);
410 return duration_cast<std::chrono::seconds>(mpeg::chrono::pts(duration) + HALFSECOND);
411}
412
416{
417 uint32_t pos = 0;
418 uint32_t length = 1;
419 if (m_dvdnav)
420 {
421 if (dvdnav_get_position(m_dvdnav, &pos, &length) == DVDNAV_STATUS_ERR)
422 {
423 // try one more time
424 dvdnav_get_position(m_dvdnav, &pos, &length);
425 }
426 }
427 return pos * DVD_BLOCK_SIZE;
428}
429
431{
432 return m_title;
433}
434
436{
437 return m_playerWait;
438}
439
441{
442 return m_part;
443}
444
446{
447 return m_currentAngle;
448}
449
451{
453}
454
456{
457 return m_titleLength;
458}
459
460std::chrono::seconds MythDVDBuffer::GetChapterLength(void) const
461{
462 return duration_cast<std::chrono::seconds>(m_pgLength);
463}
464
465void MythDVDBuffer::GetPartAndTitle(int &Part, int &Title) const
466{
467 Part = m_part;
468 Title = m_title;
469}
470
471uint32_t MythDVDBuffer::AdjustTimestamp(uint32_t Timestamp) const
472{
473 uint32_t newTimestamp = Timestamp;
474 if (newTimestamp >= m_timeDiff)
475 newTimestamp -= m_timeDiff;
476 return newTimestamp;
477}
478
479int64_t MythDVDBuffer::AdjustTimestamp(int64_t Timestamp) const
480{
481 int64_t newTimestamp = Timestamp;
482 if ((newTimestamp != AV_NOPTS_VALUE) && (newTimestamp >= m_timeDiff))
483 newTimestamp -= m_timeDiff;
484 return newTimestamp;
485}
486
488{
489 QMutexLocker contextLocker(&m_contextLock);
490 if (m_context)
492 return m_context;
493}
494
496{
497 return m_dvdEvent;
498}
499
501{
503 {
504 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
505 m_playerWait = true;
506 int count = 0;
507 // cppcheck-suppress knownConditionTrueFalse
508 while (m_playerWait && count++ < 200)
509 {
510 m_rwLock.unlock();
511 std::this_thread::sleep_for(10ms);
512 m_rwLock.lockForWrite();
513 }
514
515 // cppcheck-suppress knownConditionTrueFalse
516 if (m_playerWait)
517 {
518 LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
519 m_playerWait = false;
520 }
521 }
522}
523
525{
526 uint8_t* blockBuf = nullptr;
527 uint tot = 0;
528 int needed = static_cast<int>(Size);
529 char* dest = static_cast<char*>(Buffer);
530 int offset = 0;
531 bool waiting = false;
532
533 if (m_gotStop)
534 {
535 LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read: called after DVDNAV_STOP");
536 errno = EBADF;
537 return -1;
538 }
539
541 LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running.");
542
543 while ((m_processState != PROCESS_WAIT) && needed)
544 {
545 bool reprocessing { false };
546 blockBuf = m_dvdBlockWriteBuf.data();
547
549 {
551 reprocessing = true;
552 }
553 else
554 {
555 m_dvdStat = dvdnav_get_next_cache_block(m_dvdnav, &blockBuf, &m_dvdEvent, &m_dvdEventSize);
556 reprocessing = false;
557 }
558
559 if (m_dvdStat == DVDNAV_STATUS_ERR)
560 {
561 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1")
562 .arg(dvdnav_err_to_string(m_dvdnav)));
563 errno = EIO;
564 return -1;
565 }
566
567 switch (m_dvdEvent)
568 {
569 // Standard packet for decoding
570 case DVDNAV_BLOCK_OK:
571 {
572 // copy block
573 if (!m_seeking)
574 {
575 memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
576 tot += DVD_BLOCK_SIZE;
577 }
578
579 // release buffer
580 if (blockBuf != m_dvdBlockWriteBuf.data())
581 dvdnav_free_cache_block(m_dvdnav, blockBuf);
582
583 // debug
584 LOG(VB_PLAYBACK|VB_FILE, LOG_DEBUG, LOC + "DVDNAV_BLOCK_OK");
585 }
586 break;
587
588 // cell change
589 case DVDNAV_CELL_CHANGE:
590 {
591 // get event details
592 auto *cell_event = reinterpret_cast<dvdnav_cell_change_event_t*>(blockBuf);
593
594 // update information for the current cell
595 m_cellChanged = true;
596 if (m_pgcLength != mpeg::chrono::pts(cell_event->pgc_length))
597 m_pgcLengthChanged = true;
598 m_pgLength = mpeg::chrono::pts(cell_event->pg_length);
599 m_pgcLength = mpeg::chrono::pts(cell_event->pgc_length);
600 m_cellStart = mpeg::chrono::pts(cell_event->cell_start);
601 m_pgStart = cell_event->pg_start;
602
603 // update title/part/still/menu information
607 uint32_t pos = 0;
608 uint32_t length = 0;
609 uint32_t stillTimer = dvdnav_get_next_still_flag(m_dvdnav);
610 m_still = 0s;
611 m_titleParts = 0;
612 dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
613 dvdnav_get_number_of_parts(m_dvdnav, m_title, &m_titleParts);
614 dvdnav_get_position(m_dvdnav, &pos, &length);
615 dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
616
617 if (m_title != m_lastTitle)
618 {
619 // Populate the chapter list for this title, used in the OSD menu
621 }
622
623 m_titleLength = length * DVD_BLOCK_SIZE;
624 if (!m_seeking)
626
627 // debug
628 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
629 QString("---- DVDNAV_CELL_CHANGE - Cell #%1 Menu %2 Length %3")
630 .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
631 .arg(static_cast<double>(cell_event->cell_length) / 90000.0, 0, 'f', 1));
632 QString still;
633 if (stillTimer == 0)
634 {
635 still = QString("Length: %1 seconds")
636 .arg(duration_cast<std::chrono::seconds>(m_pgcLength).count());
637 }
638 else if (stillTimer < 0xff)
639 {
640 still = QString("Stillframe: %1 seconds").arg(stillTimer);
641 }
642 else
643 {
644 still = QString("Infinite stillframe");
645 }
646
647 if (m_title == 0)
648 {
649 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Menu #%1 %2")
650 .arg(m_part).arg(still));
651 }
652 else
653 {
654 LOG(VB_PLAYBACK, LOG_INFO,
655 LOC + QString("Title #%1: %2 Part %3 of %4")
656 .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
657 }
658
659 // wait unless it is a transition from one normal video cell to
660 // another or the same menu id
661 if ((m_title != m_lastTitle) &&
662 // cppcheck-suppress knownConditionTrueFalse
663 (m_title != 0 || m_lastTitle != 0 || (m_part != m_lastPart)))
664 {
666 }
667
668 // Make sure the still frame timer is reset.
669 if (m_parent)
671
672 // clear menus/still frame selections
676 m_buttonSelected = false;
677 m_vobid = m_cellid = 0;
678 m_cellRepeated = false;
679 m_buttonSeenInCell = false;
680
682
683 // release buffer
684 if (blockBuf != m_dvdBlockWriteBuf.data())
685 dvdnav_free_cache_block(m_dvdnav, blockBuf);
686 }
687 break;
688
689 // new colour lookup table for subtitles/menu buttons
690 case DVDNAV_SPU_CLUT_CHANGE:
691 {
692 // debug
693 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_SPU_CLUT_CHANGE");
694
695 // store the new clut
696 // m_clut = std::to_array(blockBuf); // C++20
697 std::copy(blockBuf, blockBuf + (16 * sizeof(uint32_t)),
698 reinterpret_cast<uint8_t*>(m_clut.data()));
699 // release buffer
700 if (blockBuf != m_dvdBlockWriteBuf.data())
701 dvdnav_free_cache_block(m_dvdnav, blockBuf);
702 }
703 break;
704
705 // new Sub-picture Unit stream (subtitles/menu buttons)
706 case DVDNAV_SPU_STREAM_CHANGE:
707 {
708 // get event details
709 auto* spu = reinterpret_cast<dvdnav_spu_stream_change_event_t*>(blockBuf);
710
711 // clear any existing subs/buttons
713
714 // not sure
716 m_curSubtitleTrack = dvdnav_get_active_spu_stream(m_dvdnav);
717
718 // debug
719 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
720 QString("DVDNAV_SPU_STREAM_CHANGE: "
721 "physicalwide %1, physicalletterbox %2, "
722 "physicalpanscan %3, currenttrack %4")
723 .arg(spu->physical_wide).arg(spu->physical_letterbox)
724 .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
725
726 // release buffer
727 if (blockBuf != m_dvdBlockWriteBuf.data())
728 dvdnav_free_cache_block(m_dvdnav, blockBuf);
729 }
730 break;
731
732 // the audio stream changed
733 case DVDNAV_AUDIO_STREAM_CHANGE:
734 {
735 // get event details
736 auto* audio = reinterpret_cast<dvdnav_audio_stream_change_event_t*>(blockBuf);
737
738 // retrieve the new track
739 int new_track = GetAudioTrackNum(static_cast<uint>(audio->physical));
740
741 // debug
742 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
743 QString("DVDNAV_AUDIO_STREAM_CHANGE: old %1 new %2, physical %3, logical %4")
744 .arg(m_curAudioTrack).arg(new_track)
745 .arg(audio->physical).arg(audio->logical));
746
747 // tell the decoder to reset the audio streams if necessary
748 if (new_track != m_curAudioTrack)
749 {
750 m_curAudioTrack = new_track;
752 }
753
754 // release buffer
755 if (blockBuf != m_dvdBlockWriteBuf.data())
756 dvdnav_free_cache_block(m_dvdnav, blockBuf);
757 }
758 break;
759
760 // navigation packet
761 case DVDNAV_NAV_PACKET:
762 {
763 QMutexLocker lock(&m_seekLock);
764 bool lastInMenu = m_inMenu;
765
766 // retrieve the latest Presentation Control and
767 // Data Search Information structures
768 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
769 dsi_t *dsi = dvdnav_get_current_nav_dsi(m_dvdnav);
770
771 if (pci == nullptr || dsi == nullptr)
772 {
773 // Something has gone horribly wrong if this happens
774 LOG(VB_GENERAL, LOG_ERR, LOC + QString("DVDNAV_NAV_PACKET - Error retrieving DVD data structures - dsi 0x%1, pci 0x%2")
775 .arg(reinterpret_cast<uint64_t>(dsi), 0, 16)
776 .arg(reinterpret_cast<uint64_t>(pci), 0, 16));
777 }
778 else
779 {
780 // If the start PTS of this block is not the
781 // same as the end PTS of the last block,
782 // we've got a timestamp discontinuity
783 int64_t diff = static_cast<int64_t>(pci->pci_gi.vobu_s_ptm) - m_endPts;
784 if (diff != 0)
785 {
786 if (!reprocessing && !m_skipstillorwait)
787 {
788 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
789 .arg(pci->pci_gi.vobu_s_ptm).arg(m_endPts).arg(diff));
791 break;
792 }
793
794 m_timeDiff += diff;
795 }
796
797 m_endPts = pci->pci_gi.vobu_e_ptm;
798 m_inMenu = (pci->hli.hl_gi.btn_ns > 0);
799
800 if (m_inMenu && m_seeking && (dsi->synci.sp_synca[0] & 0x80000000) &&
802 {
803 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Jumped into middle of menu: lba %1, dest %2")
804 .arg(pci->pci_gi.nv_pck_lbn)
805 .arg(pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff)));
806
807 // We're in a menu, the subpicture packets are somewhere behind us
808 // and we've not decoded any subpicture.
809 // That probably means we've jumped into the middle of a menu.
810 // We'd better jump back to get the subpicture packet(s) otherwise
811 // there's no menu highlight to show.
812 m_seeking = false;
813 dvdnav_sector_search(m_dvdnav, pci->pci_gi.nv_pck_lbn - (dsi->synci.sp_synca[0] & 0x7fffffff), SEEK_SET);
814 }
815 else
816 {
817 pci_t pci_copy = *pci;
818
819 pci_copy.pci_gi.vobu_s_ptm = AdjustTimestamp(pci->pci_gi.vobu_s_ptm);
820 pci_copy.pci_gi.vobu_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_e_ptm);
821
822 if (pci->pci_gi.vobu_se_e_ptm != 0)
823 pci_copy.pci_gi.vobu_se_e_ptm = AdjustTimestamp(pci->pci_gi.vobu_se_e_ptm);
824
825 QMutexLocker contextLocker(&m_contextLock);
826 if (m_context)
828
829 m_context = new MythDVDContext(*dsi, pci_copy);
830
831 if (m_inMenu != lastInMenu)
832 {
833 if (m_inMenu)
834 {
837 }
838 else
839 {
841 }
842 }
843
844 // if we are in a looping menu, we don't want to reset the
845 // selected button when we restart
846 m_vobid = dsi->dsi_gi.vobu_vob_idn;
847 m_cellid = dsi->dsi_gi.vobu_c_idn;
850 {
851 m_cellRepeated = true;
852 }
853
854 // update our status
855 m_currentTime = mpeg::chrono::pts(dvdnav_get_current_time(m_dvdnav));
857
858 if (m_seeking)
859 {
860 auto relativetime = duration_cast<std::chrono::seconds>(m_seektime - m_currentTime);
861 if (abs(relativetime) <= 1s)
862 {
863 m_seeking = false;
864 m_seektime = 0_pts;
865 }
866 else
867 {
868 dvdnav_relative_time_search(m_dvdnav, relativetime.count() * 2);
869 }
870 }
871
872 // update the button stream number if this is the
873 // first NAV pack containing button information
874 if ( (pci->hli.hl_gi.hli_ss & 0x03) == 0x01 )
875 {
876 m_buttonStreamID = 32;
877 int aspect = dvdnav_get_video_aspect(m_dvdnav);
878
879 // workaround where dvd menu is
880 // present in VTS_DOMAIN. dvdnav adds 0x80 to stream id
881 // proper fix should be put in dvdnav sometime
882 int8_t spustream = dvdnav_get_active_spu_stream(m_dvdnav) & 0x7f;
883
884 if (aspect != 0 && spustream > 0)
885 m_buttonStreamID += spustream;
886
887 m_buttonSeenInCell = true;
888 }
889
890 // debug
891 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_NAV_PACKET - time:%1, lba:%2, vob:%3, cell:%4, seeking:%5, seektime:%6")
892 .arg(m_context->GetStartPTS())
893 .arg(m_context->GetLBA())
894 .arg(m_vobid)
895 .arg(m_cellid)
896 .arg(m_seeking)
897 .arg(m_seektime.count()));
898
899 if (!m_seeking)
900 {
901 memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
902 tot += DVD_BLOCK_SIZE;
903 }
904 }
905 }
906 // release buffer
907 if (blockBuf != m_dvdBlockWriteBuf.data())
908 dvdnav_free_cache_block(m_dvdnav, blockBuf);
909 }
910 break;
911
912 case DVDNAV_HOP_CHANNEL:
913 {
914 if (!reprocessing && !m_skipstillorwait)
915 {
916 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL - waiting");
918 break;
919 }
920
921 // debug
922 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL");
924 }
925 break;
926
927 // no op
928 case DVDNAV_NOP:
929 break;
930
931 // new Video Title Set - aspect ratio/letterboxing
932 case DVDNAV_VTS_CHANGE:
933 {
934 // retrieve event details
935 auto* vts = reinterpret_cast<dvdnav_vts_change_event_t*>(blockBuf);
936
937 // update player
938 int aspect = dvdnav_get_video_aspect(m_dvdnav);
939 if (aspect == 2) // 4:3
940 m_forcedAspect = 4.0F / 3.0F;
941 else if (aspect == 3) // 16:9
942 m_forcedAspect = 16.0F / 9.0F;
943 else
944 m_forcedAspect = -1;
945 int permission = dvdnav_get_video_scale_permission(m_dvdnav);
946
947 // debug
948 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
949 QString("DVDNAV_VTS_CHANGE: old_vtsN %1, new_vtsN %2, "
950 "aspect %3, perm %4")
951 .arg(vts->old_vtsN).arg(vts->new_vtsN)
952 .arg(aspect).arg(permission));
953
954 // trigger a rescan of the audio streams
955 if ((vts->old_vtsN != vts->new_vtsN) ||(vts->old_domain != vts->new_domain))
957
958 // Make sure we know we're not staying in the
959 // same cell (same vobid/cellid values can
960 // occur in every VTS)
961 m_lastvobid = m_vobid = 0;
963
964 // release buffer
965 if (blockBuf != m_dvdBlockWriteBuf.data())
966 dvdnav_free_cache_block(m_dvdnav, blockBuf);
967 }
968 break;
969
970 // menu button
971 case DVDNAV_HIGHLIGHT:
972 {
973 // retrieve details
974 auto* highlight = reinterpret_cast<dvdnav_highlight_event_t*>(blockBuf);
975
976 // update the current button
977 m_menuBtnLock.lock();
978 DVDButtonUpdate(false);
980 m_menuBtnLock.unlock();
981
982 // debug
983 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
984 QString("DVDNAV_HIGHLIGHT: display %1, palette %2, "
985 "sx %3, sy %4, ex %5, ey %6, pts %7, buttonN %8")
986 .arg(highlight->display).arg(highlight->palette)
987 .arg(highlight->sx).arg(highlight->sy)
988 .arg(highlight->ex).arg(highlight->ey)
989 .arg(highlight->pts).arg(highlight->buttonN));
990
991 // release buffer
992 if (blockBuf != m_dvdBlockWriteBuf.data())
993 dvdnav_free_cache_block(m_dvdnav, blockBuf);
994 }
995 break;
996
997 // dvd still frame
998 case DVDNAV_STILL_FRAME:
999 {
1000 // retrieve still frame details (length)
1001 auto* still = reinterpret_cast<dvdnav_still_event_t*>(blockBuf);
1002
1003 if (!reprocessing && !m_skipstillorwait)
1004 {
1005 if (m_still != std::chrono::seconds(still->length))
1006 {
1007 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1) - waiting")
1008 .arg(still->length));
1009 }
1010
1012 }
1013 else
1014 {
1015 // pause a little as the dvdnav VM will continue to return
1016 // this event until it has been skipped
1017 m_rwLock.unlock();
1018 std::this_thread::sleep_for(10ms);
1019 m_rwLock.lockForWrite();
1020
1021 // when scanning the file or exiting playback, skip immediately
1022 // otherwise update the timeout in the player
1024 {
1025 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Skipping DVDNAV_STILL_FRAME (%1)")
1026 .arg(still->length));
1028 }
1029 else if (m_parent)
1030 {
1031 // debug
1032 if (m_still != std::chrono::seconds(still->length))
1033 {
1034 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("DVDNAV_STILL_FRAME (%1)")
1035 .arg(still->length));
1036 }
1037
1038 m_still = std::chrono::seconds(still->length);
1039 Size = tot;
1041 }
1042
1043 // release buffer
1044 if (blockBuf != m_dvdBlockWriteBuf.data())
1045 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1046 }
1047 }
1048 break;
1049
1050 // wait for the player
1051 case DVDNAV_WAIT:
1052 {
1053 if (!reprocessing && !m_skipstillorwait && !waiting)
1054 {
1055 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT - waiting");
1057 }
1058 else
1059 {
1060 waiting = true;
1061
1062 //debug
1063 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");
1064
1065 // skip if required, otherwise wait (and loop)
1067 {
1068 WaitSkip();
1069 }
1070 else
1071 {
1072 m_dvdWaiting = true;
1073 m_rwLock.unlock();
1074 std::this_thread::sleep_for(10ms);
1075 m_rwLock.lockForWrite();
1076 }
1077
1078 // release buffer
1079 if (blockBuf != m_dvdBlockWriteBuf.data())
1080 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1081 }
1082 }
1083 break;
1084
1085 // exit playback
1086 case DVDNAV_STOP:
1087 {
1088 LOG(VB_GENERAL, LOG_INFO, LOC + "DVDNAV_STOP");
1089 Size = tot;
1090 m_gotStop = true;
1091
1092 // release buffer
1093 if (blockBuf != m_dvdBlockWriteBuf.data())
1094 dvdnav_free_cache_block(m_dvdnav, blockBuf);
1095 }
1096 break;
1097
1098 // this shouldn't happen
1099 default:
1100 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unknown DVD event: %1").arg(m_dvdEvent));
1101 break;
1102 }
1103
1104 needed = static_cast<int>(Size - tot);
1105 offset = static_cast<int>(tot);
1106 }
1107
1109 {
1110 errno = EAGAIN;
1111 return 0;
1112 }
1113 return static_cast<int>(tot);
1114}
1115
1117{
1118 QMutexLocker lock(&m_seekLock);
1119 if (Track < 1)
1120 Seek(0);
1121 else if (Track < m_titleParts)
1122 dvdnav_part_play(m_dvdnav, m_title, Track);
1123 else
1124 return false;
1125 m_gotStop = false;
1126 return true;
1127}
1128
1130{
1131 int newPart = m_part + 1;
1132
1133 QMutexLocker lock(&m_seekLock);
1134 if (newPart < m_titleParts)
1135 {
1136 dvdnav_part_play(m_dvdnav, m_title, newPart);
1137 m_gotStop = false;
1138 return true;
1139 }
1140 return false;
1141}
1142
1144{
1145 int newPart = m_part - 1;
1146
1147 QMutexLocker lock(&m_seekLock);
1148 if (newPart > 0)
1149 dvdnav_part_play(m_dvdnav, m_title, newPart);
1150 else
1151 Seek(0);
1152 m_gotStop = false;
1153}
1154
1158std::chrono::seconds MythDVDBuffer::GetTotalTimeOfTitle(void) const
1159{
1160 return duration_cast<std::chrono::seconds>(m_pgcLength);
1161}
1162
1164{
1165 return m_forcedAspect;
1166}
1167
1170std::chrono::seconds MythDVDBuffer::GetCellStart(void) const
1171{
1172 return duration_cast<std::chrono::seconds>(m_cellStart);
1173}
1174
1178{
1179 bool ret = m_cellChanged;
1180 m_cellChanged = false;
1181 return ret;
1182}
1183
1185{
1186 return dvdnav_get_next_still_flag(m_dvdnav) > 0;
1187}
1188
1190{
1191 return m_audioStreamsChanged;
1192}
1193
1195{
1196 return m_dvdWaiting;
1197}
1198
1200{
1201 return m_titleParts;
1202}
1203
1207{
1208 bool ret = m_pgcLengthChanged;
1209 m_pgcLengthChanged = false;
1210 return ret;
1211}
1212
1214{
1215 QMutexLocker locker(&m_seekLock);
1216 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Skipping still frame.");
1217
1218 m_still = 0s;
1219 dvdnav_still_skip(m_dvdnav);
1220
1221 // Make sure the still frame timer is disabled.
1222 if (m_parent)
1224}
1225
1227{
1228 QMutexLocker locker(&m_seekLock);
1229 dvdnav_wait_skip(m_dvdnav);
1230 m_dvdWaiting = false;
1231 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exiting DVDNAV_WAIT status");
1232}
1233
1235{
1236 m_playerWait = false;
1237}
1238
1240{
1242}
1243
1245{
1246 return m_processState == PROCESS_WAIT;
1247}
1248
1251bool MythDVDBuffer::GoToMenu(const QString &str)
1252{
1253 DVDMenuID_t menuid = DVD_MENU_Escape;
1254 QMutexLocker locker(&m_seekLock);
1255
1256 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVDRingBuf: GoToMenu %1").arg(str));
1257
1258 if (str.compare("chapter") == 0)
1259 menuid = DVD_MENU_Part;
1260 else if (str.compare("root") == 0)
1261 menuid = DVD_MENU_Root;
1262 else if (str.compare("title") == 0)
1263 menuid = DVD_MENU_Title;
1264 else
1265 return false;
1266
1267 dvdnav_status_t ret = dvdnav_menu_call(m_dvdnav, menuid);
1268 return ret == DVDNAV_STATUS_OK;
1269}
1270
1276{
1277 bool success = false;
1278 QString target;
1279
1280 QMutexLocker locker(&m_seekLock);
1281
1282 if (dvdnav_is_domain_vts(m_dvdnav) && !m_inMenu)
1283 {
1284 if (dvdnav_go_up(m_dvdnav) == DVDNAV_STATUS_OK)
1285 {
1286 target = "GoUp";
1287 success = true;
1288 }
1289 else if (dvdnav_menu_call(m_dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
1290 {
1291 target = "Root";
1292 success = true;
1293 }
1294 else if (dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) == DVDNAV_STATUS_OK)
1295 {
1296 target = "Title";
1297 success = true;
1298 }
1299 else
1300 {
1301 target = "Nothing available";
1302 }
1303 }
1304 else
1305 {
1306 target = QString("No jump, %1 menu").arg(m_inMenu ? "in" : "not in");
1307 }
1308
1309 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVDRingBuf: GoBack - %1").arg(target));
1310 return success;
1311}
1312
1314{
1315 QMutexLocker locker(&m_seekLock);
1316 // This conditional appears to be unnecessary, and might have come
1317 // from a mistake in a libdvdnav resync.
1318 //if (!dvdnav_is_domain_vts(m_dvdnav))
1319 dvdnav_next_pg_search(m_dvdnav);
1320}
1321
1323{
1324 QMutexLocker locker(&m_seekLock);
1325 // This conditional appears to be unnecessary, and might have come
1326 // from a mistake in a libdvdnav resync.
1327 //if (!dvdnav_is_domain_vts(m_dvdnav))
1328 dvdnav_prev_pg_search(m_dvdnav);
1329}
1330
1331bool MythDVDBuffer::HandleAction(const QStringList &Actions, mpeg::chrono::pts /*Pts*/)
1332{
1333 if (!NumMenuButtons())
1334 return false;
1335
1336 if (Actions.contains(ACTION_UP) || Actions.contains(ACTION_CHANNELUP))
1337 MoveButtonUp();
1338 else if (Actions.contains(ACTION_DOWN) || Actions.contains(ACTION_CHANNELDOWN))
1340 else if (Actions.contains(ACTION_LEFT) || Actions.contains(ACTION_SEEKRWND))
1342 else if (Actions.contains(ACTION_RIGHT) || Actions.contains(ACTION_SEEKFFWD))
1344 else if (Actions.contains(ACTION_SELECT))
1346 else
1347 return false;
1348
1349 return true;
1350}
1351
1353{
1354 m_skipstillorwait = Ignore;
1355}
1356
1358{
1359 if (NumMenuButtons() > 1)
1360 {
1361 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1362 dvdnav_left_button_select(m_dvdnav, pci);
1363 }
1364}
1365
1367{
1368 if (NumMenuButtons() > 1)
1369 {
1370 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1371 dvdnav_right_button_select(m_dvdnav, pci);
1372 }
1373}
1374
1376{
1377 if (NumMenuButtons() > 1)
1378 {
1379 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1380 dvdnav_upper_button_select(m_dvdnav, pci);
1381 }
1382}
1383
1385{
1386 if (NumMenuButtons() > 1)
1387 {
1388 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1389 dvdnav_lower_button_select(m_dvdnav, pci);
1390 }
1391}
1392
1395{
1396 if (NumMenuButtons() > 0)
1397 {
1399 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1400 dvdnav_button_activate(m_dvdnav, pci);
1401 }
1402}
1403
1405void MythDVDBuffer::GetMenuSPUPkt(uint8_t *Buffer, int Size, int StreamID, uint32_t StartTime)
1406{
1407 if (Size < 4)
1408 return;
1409
1411 return;
1412
1413 QMutexLocker lock(&m_menuBtnLock);
1414
1416 auto *spu_pkt = reinterpret_cast<uint8_t*>(av_malloc(static_cast<size_t>(Size)));
1417 memcpy(spu_pkt, Buffer, static_cast<size_t>(Size));
1418 m_menuSpuPkt = spu_pkt;
1419 m_menuBuflength = Size;
1420 if (!m_buttonSelected)
1421 {
1423 m_buttonSelected = true;
1424 }
1425
1426 if (DVDButtonUpdate(false))
1427 {
1428 int32_t gotbutton = 0;
1430 m_menuSpuPkt, m_menuBuflength, StartTime);
1431 }
1432}
1433
1436{
1437 // this is unlocked by ReleaseMenuButton
1438 m_menuBtnLock.lock();
1439
1440 if ((m_menuBuflength > 4) && m_buttonExists && (NumMenuButtons() > 0))
1441 {
1442 Version = m_buttonVersion;
1443 return &(m_dvdMenuButton);
1444 }
1445
1446 return nullptr;
1447}
1448
1449
1451{
1452 m_menuBtnLock.unlock();
1453}
1454
1458{
1459 QRect rect(0,0,0,0);
1460 if (!m_buttonExists)
1461 return rect;
1462 rect.setRect(m_hlButton.x(), m_hlButton.y(), m_hlButton.width(), m_hlButton.height());
1463 return rect;
1464}
1465
1469bool MythDVDBuffer::DecodeSubtitles(AVSubtitle *Subtitle, int *GotSubtitles,
1470 const uint8_t *SpuPkt, int BufSize, uint32_t StartTime)
1471{
1472 AlphaArray alpha {0, 0, 0, 0};
1473 PaletteArray palette {0, 0, 0, 0};
1474
1475 if (!SpuPkt)
1476 return false;
1477
1478 if (BufSize < 4)
1479 return false;
1480
1481 bool force_subtitle_display = false;
1482 Subtitle->rects = nullptr;
1483 Subtitle->num_rects = 0;
1484 Subtitle->start_display_time = StartTime;
1485 Subtitle->end_display_time = StartTime;
1486
1487 int cmd_pos = qFromBigEndian<qint16>(SpuPkt + 2);
1488 while ((cmd_pos + 4) < BufSize)
1489 {
1490 int offset1 = -1;
1491 int offset2 = -1;
1492 int date = qFromBigEndian<qint16>(SpuPkt + cmd_pos);
1493 int next_cmd_pos = qFromBigEndian<qint16>(SpuPkt + cmd_pos + 2);
1494 int pos = cmd_pos + 4;
1495 int x1 = 0;
1496 int x2 = 0;
1497 int y1 = 0;
1498 int y2 = 0;
1499 while (pos < BufSize)
1500 {
1501 int cmd = SpuPkt[pos++];
1502 switch(cmd)
1503 {
1504 case 0x00:
1505 force_subtitle_display = true;
1506 break;
1507 case 0x01:
1508 Subtitle->start_display_time = ((static_cast<uint>(date) << 10) / 90) + StartTime;
1509 break;
1510 case 0x02:
1511 Subtitle->end_display_time = ((static_cast<uint>(date) << 10) / 90) + StartTime;
1512 break;
1513 case 0x03:
1514 {
1515 if ((BufSize - pos) < 2)
1516 goto fail;
1517
1518 palette[3] = SpuPkt[pos] >> 4;
1519 palette[2] = SpuPkt[pos] & 0x0f;
1520 palette[1] = SpuPkt[pos + 1] >> 4;
1521 palette[0] = SpuPkt[pos + 1] & 0x0f;
1522 pos +=2;
1523 }
1524 break;
1525 case 0x04:
1526 {
1527 if ((BufSize - pos) < 2)
1528 goto fail;
1529 alpha[3] = SpuPkt[pos] >> 4;
1530 alpha[2] = SpuPkt[pos] & 0x0f;
1531 alpha[1] = SpuPkt[pos + 1] >> 4;
1532 alpha[0] = SpuPkt[pos + 1] & 0x0f;
1533 pos +=2;
1534 }
1535 break;
1536 case 0x05:
1537 {
1538 if ((BufSize - pos) < 6)
1539 goto fail;
1540 x1 = (SpuPkt[pos] << 4) | (SpuPkt[pos + 1] >> 4);
1541 x2 = ((SpuPkt[pos + 1] & 0x0f) << 8) | SpuPkt[pos + 2];
1542 y1 = (SpuPkt[pos + 3] << 4) | (SpuPkt[pos + 4] >> 4);
1543 y2 = ((SpuPkt[pos + 4] & 0x0f) << 8) | SpuPkt[pos + 5];
1544 pos +=6;
1545 }
1546 break;
1547 case 0x06:
1548 {
1549 if ((BufSize - pos) < 4)
1550 goto fail;
1551 offset1 = qFromBigEndian<qint16>(SpuPkt + pos);
1552 offset2 = qFromBigEndian<qint16>(SpuPkt + pos + 2);
1553 pos +=4;
1554 }
1555 break;
1556 case 0x07:
1557 {
1558 if ((BufSize - pos) < 2)
1559 goto fail;
1560
1561 pos += qFromBigEndian<qint16>(SpuPkt + pos);
1562 }
1563 break;
1564 case 0xff:
1565 default:
1566 goto the_end;
1567 }
1568 }
1569 the_end:
1570 if (offset1 >= 0)
1571 {
1572 int width = x2 - x1 + 1;
1573 width = std::max(width, 0);
1574 int height = y2 - y1 + 1;
1575 height = std::max(height, 0);
1576 if (width > 0 && height > 0)
1577 {
1578 if (Subtitle->rects != nullptr)
1579 {
1580 for (uint i = 0; i < Subtitle->num_rects; i++)
1581 {
1582 av_free(Subtitle->rects[i]->data[0]);
1583 av_free(Subtitle->rects[i]->data[1]);
1584 av_freep(reinterpret_cast<void*>(&Subtitle->rects[i]));
1585 }
1586 av_freep(reinterpret_cast<void*>(&Subtitle->rects));
1587 Subtitle->num_rects = 0;
1588 }
1589
1590 auto *bitmap = static_cast<uint8_t*>(av_malloc(static_cast<size_t>(width) * height));
1591 Subtitle->num_rects = (NumMenuButtons() > 0) ? 2 : 1;
1592 Subtitle->rects = static_cast<AVSubtitleRect**>(av_mallocz(sizeof(AVSubtitleRect*) * Subtitle->num_rects));
1593 for (uint i = 0; i < Subtitle->num_rects; i++)
1594 Subtitle->rects[i] = static_cast<AVSubtitleRect*>(av_mallocz(sizeof(AVSubtitleRect)));
1595#ifdef __cpp_size_t_suffix
1596 Subtitle->rects[0]->data[1] = static_cast<uint8_t*>(av_mallocz(4UZ * 4UZ));
1597#else
1598 Subtitle->rects[0]->data[1] = static_cast<uint8_t*>(av_mallocz(4_UZ * 4_UZ));
1599#endif
1600 DecodeRLE(bitmap, width * 2, width, (height + 1) / 2,
1601 SpuPkt, offset1 * 2, BufSize);
1602 DecodeRLE(bitmap + width, width * 2, width, height / 2,
1603 SpuPkt, offset2 * 2, BufSize);
1604 GuessPalette(reinterpret_cast<uint32_t*>(Subtitle->rects[0]->data[1]), palette, alpha);
1605 Subtitle->rects[0]->data[0] = bitmap;
1606 Subtitle->rects[0]->x = x1;
1607 Subtitle->rects[0]->y = y1;
1608 Subtitle->rects[0]->w = width;
1609 Subtitle->rects[0]->h = height;
1610 Subtitle->rects[0]->type = SUBTITLE_BITMAP;
1611 Subtitle->rects[0]->nb_colors = 4;
1612 Subtitle->rects[0]->linesize[0] = width;
1613 if (NumMenuButtons() > 0)
1614 {
1615 Subtitle->rects[1]->type = SUBTITLE_BITMAP;
1616#ifdef __cpp_size_t_suffix
1617 Subtitle->rects[1]->data[1] = static_cast<uint8_t*>(av_malloc(4UZ * 4UZ));
1618#else
1619 Subtitle->rects[1]->data[1] = static_cast<uint8_t*>(av_malloc(4_UZ * 4_UZ));
1620#endif
1621 GuessPalette(reinterpret_cast<uint32_t*>(Subtitle->rects[1]->data[1]),
1623 }
1624 else
1625 {
1627 }
1628 *GotSubtitles = 1;
1629 }
1630 }
1631 if (next_cmd_pos == cmd_pos)
1632 break;
1633 cmd_pos = next_cmd_pos;
1634 }
1635 if (Subtitle->num_rects > 0)
1636 {
1637 if (force_subtitle_display)
1638 {
1639 for (unsigned i = 0; i < Subtitle->num_rects; i++)
1640 {
1641 Subtitle->rects[i]->flags |= AV_SUBTITLE_FLAG_FORCED;
1642 }
1643 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Decoded forced subtitle");
1644 }
1645 return true;
1646 }
1647fail:
1648 return false;
1649}
1650
1655{
1656 if (!m_parent)
1657 return false;
1658
1659 QSize videodispdim = m_parent->GetVideoSize();
1660 int videoheight = videodispdim.height();
1661 int videowidth = videodispdim.width();
1662
1663 int32_t button = 0;
1664 dvdnav_highlight_area_t highlight;
1665 dvdnav_get_current_highlight(m_dvdnav, &button);
1666 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1667 dvdnav_status_t dvdRet =
1668 dvdnav_get_highlight_area_from_group(pci, DVD_BTN_GRP_Wide, button,
1669 static_cast<int32_t>(ButtonMode), &highlight);
1670
1671 if (dvdRet == DVDNAV_STATUS_ERR)
1672 return false;
1673
1674 for (uint i = 0 ; i < 4 ; i++)
1675 {
1676 m_buttonAlpha[i] = 0xf & (highlight.palette >> (4 * i));
1677 m_buttonColor[i] = 0xf & (highlight.palette >> (16 + 4 * i));
1678 }
1679
1680 // If the button overlay has already been decoded, make sure
1681 // the correct palette for the current highlight is set
1682 if (m_dvdMenuButton.rects && (m_dvdMenuButton.num_rects > 1))
1683 {
1684 GuessPalette(reinterpret_cast<uint32_t*>(m_dvdMenuButton.rects[1]->data[1]),
1686 }
1687
1688 m_hlButton.setCoords(highlight.sx, highlight.sy, highlight.ex, highlight.ey);
1689 return ((highlight.sx + highlight.sy) > 0) &&
1690 (highlight.sx < videowidth && highlight.sy < videoheight);
1691}
1692
1696{
1697 if (m_buttonExists || m_dvdMenuButton.rects)
1698 {
1699 for (uint i = 0; i < m_dvdMenuButton.num_rects; i++)
1700 {
1701 AVSubtitleRect* rect = m_dvdMenuButton.rects[i];
1702 av_free(rect->data[0]);
1703 av_free(rect->data[1]);
1704 av_free(rect);
1705 }
1706 av_free(reinterpret_cast<void*>(m_dvdMenuButton.rects));
1707 m_dvdMenuButton.rects = nullptr;
1708 m_dvdMenuButton.num_rects = 0;
1709 m_buttonExists = false;
1710 }
1711}
1712
1717{
1718 if (m_menuBuflength == 0)
1719 return;
1720
1721 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clearing Menu SPU Packet" );
1722
1724
1725 av_free(m_menuSpuPkt);
1726 m_menuBuflength = 0;
1727 m_hlButton.setRect(0, 0, 0, 0);
1728}
1729
1731{
1732 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1733 int numButtons = pci->hli.hl_gi.btn_ns;
1734 if (numButtons > 0 && numButtons < 36)
1735 return numButtons;
1736 return 0;
1737}
1738
1742{
1743 uint audioLang = 0;
1744 int8_t physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1745
1746 if (physicalStreamId >= 0)
1747 {
1748 uint16_t lang = dvdnav_audio_stream_to_lang(m_dvdnav, static_cast<uint8_t>(physicalStreamId));
1749 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Audio StreamID: %1; lang: %2").arg(Index).arg(lang));
1750 audioLang = ConvertLangCode(lang);
1751 }
1752 else
1753 {
1754 LOG(VB_PLAYBACK, LOG_WARNING, LOC + QString("Audio StreamID: %1 - not found!").arg(Index));
1755 }
1756
1757 return audioLang;
1758}
1759
1766{
1767 const uint AC3_OFFSET = 0x0080;
1768 const uint DTS_OFFSET = 0x0088;
1769 const uint LPCM_OFFSET = 0x00A0;
1770 const uint MP2_OFFSET = 0x01C0;
1771
1772 if (StreamId >= MP2_OFFSET)
1773 StreamId -= MP2_OFFSET;
1774 else if (StreamId >= LPCM_OFFSET)
1775 StreamId -= LPCM_OFFSET;
1776 else if (StreamId >= DTS_OFFSET)
1777 StreamId -= DTS_OFFSET;
1778 else if (StreamId >= AC3_OFFSET)
1779 StreamId -= AC3_OFFSET;
1780
1781 int logical = -1;
1782 for (uint8_t i = 0; i < 8; i++)
1783 {
1784 // Get the physical stream number at the given index
1785 // of the logical mapping table (function name is wrong!)
1786 int8_t phys = dvdnav_get_audio_logical_stream(m_dvdnav, i);
1787 if (static_cast<uint>(phys) == StreamId)
1788 {
1789 logical = i;
1790 break;
1791 }
1792 }
1793
1794 return logical;
1795}
1796
1798{
1799 int ret = -1;
1800 int8_t physicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1801 if (physicalStreamId < 0)
1802 return ret;
1803
1804 audio_attr_t attributes;
1805 if (dvdnav_get_audio_attr(m_dvdnav, static_cast<uint8_t>(physicalStreamId), &attributes) == DVDNAV_STATUS_OK)
1806 {
1807 LOG(VB_AUDIO, LOG_INFO, QString("DVD Audio Track #%1 Language Extension Code - %2")
1808 .arg(Index).arg(attributes.code_extension));
1809 return attributes.code_extension;
1810 }
1811
1812 return ret;
1813}
1814
1817{
1818 uint16_t lang = dvdnav_spu_stream_to_lang(m_dvdnav, static_cast<uint8_t>(Id));
1819 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("StreamID: %1; lang: %2").arg(Id).arg(lang));
1820 return ConvertLangCode(lang);
1821}
1822
1827{
1828 int8_t logstream = -1;
1829
1830 // VM always sets stream_id to zero if we're not in the VTS
1831 // domain and always returns 0 (instead of -1) if nothing has
1832 // been found, so only try to retrieve the logical stream if
1833 // we *are* in the VTS domain or we *are* trying to map stream 0.
1834 if (dvdnav_is_domain_vts(m_dvdnav) || (StreamId == 0))
1835 logstream = dvdnav_get_spu_logical_stream(m_dvdnav, static_cast<uint8_t>(StreamId));
1836
1837 return logstream;
1838}
1839
1842{
1843 if (Code == 0)
1844 return 0;
1845
1846 std::array<QChar,2> str2 { QChar(Code >> 8), QChar(Code & 0xff) };
1847 QString str3 = iso639_str2_to_str3(QString(str2.data(), str2.size()));
1848
1849 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("code: %1; iso639: %2").arg(Code).arg(str3));
1850
1851 if (!str3.isEmpty())
1852 return static_cast<uint>(iso639_str3_to_key(str3));
1853 return 0;
1854}
1855
1860{
1861 pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
1862 int32_t button = pci->hli.hl_gi.fosl_btnn;
1863 if (button > 0 && !m_cellRepeated)
1864 {
1865 dvdnav_button_select(m_dvdnav,pci,button);
1866 return;
1867 }
1868 dvdnav_get_current_highlight(m_dvdnav,&button);
1869 if (button > 0 && button <= NumMenuButtons())
1870 dvdnav_button_select(m_dvdnav,pci,button);
1871 else
1872 dvdnav_button_select(m_dvdnav,pci,1);
1873}
1874
1879void MythDVDBuffer::SetTrack(uint Type, int TrackNo)
1880{
1881 if (Type == kTrackTypeSubtitle)
1882 {
1883 m_curSubtitleTrack = static_cast<int8_t>(TrackNo);
1884 m_autoselectsubtitle = TrackNo < 0;
1885 }
1886 else if (Type == kTrackTypeAudio)
1887 {
1888 m_curAudioTrack = TrackNo;
1889 dvdnav_set_active_audio_stream(m_dvdnav, static_cast<int8_t>(TrackNo));
1890 }
1891}
1892
1899{
1900 if (Type == kTrackTypeSubtitle)
1901 return m_curSubtitleTrack;
1902 if (Type == kTrackTypeAudio)
1903 return m_curAudioTrack;
1904 return 0;
1905}
1906
1908{
1909 int8_t physical = dvdnav_get_audio_logical_stream(m_dvdnav, static_cast<uint8_t>(Index));
1910 if (physical >= 0)
1911 {
1912 uint16_t channels = dvdnav_audio_stream_channels(m_dvdnav, static_cast<uint8_t>(physical));
1913 if (channels != 0xFFFf)
1914 return channels;
1915 }
1916 return 0;
1917}
1918
1920{
1921 m_audioStreamsChanged = Change;
1922}
1923
1926bool MythDVDBuffer::GetNameAndSerialNum(QString& Name, QString& SerialNumber)
1927{
1928 Name = m_discName;
1929 SerialNumber = m_discSerialNumber;
1930 return !(Name.isEmpty() && SerialNumber.isEmpty());
1931}
1932
1936{
1937 State.clear();
1938 char* dvdstate = dvdnav_get_state(m_dvdnav);
1939
1940 if (dvdstate)
1941 {
1942 State = dvdstate;
1943 free(dvdstate); // From C library. NOLINT(cppcoreguidelines-no-malloc)
1944 }
1945
1946 return (!State.isEmpty());
1947}
1948
1952{
1953 QByteArray state = State.toUtf8();
1954 return (dvdnav_set_state(m_dvdnav, state.constData()) == DVDNAV_STATUS_OK);
1955}
1956
1963{
1964 int format = dvdnav_get_video_format(m_dvdnav);
1965 double dvdfps = (format == 1) ? 25.00 : 29.97;
1966 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Frame Rate %1").arg(dvdfps));
1967 return dvdfps;
1968}
1969
1971{
1972 return m_part == 0;
1973}
1974
1976{
1977 return ((m_titleParts == 0) || (m_part == (m_titleParts - 1)) || (m_titleParts == 1));
1978}
1979
1980void MythDVDBuffer::PlayTitleAndPart(int Title, int Part)
1981{
1982 dvdnav_part_play(m_dvdnav, Title, Part);
1983}
1984
1987{
1988 QMutexLocker lock(&m_seekLock);
1990}
1991
1994{
1995 if (m_filename.startsWith("/"))
1996 MediaMonitor::SetCDSpeed(m_filename.toLocal8Bit().constData(), Speed);
1997}
1998
2000std::chrono::seconds MythDVDBuffer::TitleTimeLeft(void) const
2001{
2003}
2004
2005std::chrono::seconds MythDVDBuffer::GetCurrentTime(void) const
2006{
2007 return duration_cast<std::chrono::seconds>(m_currentTime);
2008}
2009
2011void MythDVDBuffer::GuessPalette(uint32_t *RGBAPalette, const PaletteArray Palette, const AlphaArray Alpha)
2012{
2013 memset(RGBAPalette, 0, 16);
2014 for (int i = 0 ; i < 4 ; i++)
2015 {
2016 uint32_t yuv = m_clut[Palette[i]];
2017 uint y = (yuv >> 16) & 0xff;
2018 uint cr = (yuv >> 8) & 0xff;
2019 uint cb = (yuv >> 0) & 0xff;
2020 uint r = std::clamp(uint(y + (1.4022 * (cr - 128))), 0U, 0xFFU);
2021 uint b = std::clamp(uint(y + (1.7710 * (cb - 128))), 0U, 0xFFU);
2022 // NOLINTNEXTLINE(modernize-use-std-numbers)
2023 uint g = std::clamp(uint((1.7047 * y) - (0.1952 * b) - (0.5647 * r)), 0U, 0xFFU);
2024 RGBAPalette[i] = ((Alpha[i] * 17U) << 24) | (r << 16 )| (g << 8) | b;
2025 }
2026}
2027
2031int MythDVDBuffer::DecodeRLE(uint8_t *Bitmap, int Linesize, int Width, int Height,
2032 const uint8_t *Buffer, int NibbleOffset, int BufferSize)
2033{
2034 int nibbleEnd = BufferSize * 2;
2035 int x = 0;
2036 int y = 0;
2037 uint8_t *data = Bitmap;
2038 for(;;)
2039 {
2040 if (NibbleOffset >= nibbleEnd)
2041 return -1;
2042 uint v = GetNibble(Buffer, NibbleOffset++);
2043 if (v < 0x4)
2044 {
2045 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2046 if (v < 0x10)
2047 {
2048 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2049 if (v < 0x040)
2050 {
2051 v = (v << 4) | GetNibble(Buffer, NibbleOffset++);
2052 if (v < 4)
2053 v |= static_cast<uint>(Width - x) << 2;
2054 }
2055 }
2056 }
2057 int len = v >> 2;
2058 len = std::min(len, Width - x);
2059 int color = v & 0x03;
2060 memset(data + x, color, static_cast<size_t>(len));
2061 x += len;
2062 if (x >= Width)
2063 {
2064 y++;
2065 if (y >= Height)
2066 break;
2067 data += Linesize;
2068 x = 0;
2069 NibbleOffset += (NibbleOffset & 1);
2070 }
2071 }
2072 return 0;
2073}
2074
2077uint MythDVDBuffer::GetNibble(const uint8_t *Buffer, int NibbleOffset)
2078{
2079 return (Buffer[NibbleOffset >> 1] >> ((1 - (NibbleOffset & 1)) << 2)) & 0xf;
2080}
2081
2086int MythDVDBuffer::IsTransparent(const uint8_t *Buffer, int Pitch, int Num, const ColorArray& Colors)
2087{
2088 for (int i = 0; i < Num; i++)
2089 {
2090 if (!Colors[*Buffer])
2091 return 0;
2092 Buffer += Pitch;
2093 }
2094 return 1;
2095}
2096
2102{
2103 ColorArray colors {};
2104
2105 if (Subtitle->num_rects == 0 || Subtitle->rects == nullptr ||
2106 Subtitle->rects[0]->w <= 0 || Subtitle->rects[0]->h <= 0)
2107 {
2108 return 0;
2109 }
2110
2111 for (int i = 0; i < Subtitle->rects[0]->nb_colors; i++)
2112 if (((reinterpret_cast<uint32_t*>(Subtitle->rects[0]->data[1])[i] >> 24)) == 0)
2113 colors[i] = 1;
2114
2115 ptrdiff_t bottom = 0;
2116 while (bottom < Subtitle->rects[0]->h &&
2117 IsTransparent(Subtitle->rects[0]->data[0] + (bottom * Subtitle->rects[0]->linesize[0]),
2118 1, Subtitle->rects[0]->w, colors))
2119 {
2120 bottom++;
2121 }
2122
2123 if (bottom == Subtitle->rects[0]->h)
2124 {
2125 av_freep(reinterpret_cast<void*>(&Subtitle->rects[0]->data[0]));
2126 Subtitle->rects[0]->w = Subtitle->rects[0]->h = 0;
2127 return 0;
2128 }
2129
2130 ptrdiff_t top = Subtitle->rects[0]->h - 1;
2131 while (top > 0 &&
2132 IsTransparent(Subtitle->rects[0]->data[0] + (top * Subtitle->rects[0]->linesize[0]), 1,
2133 Subtitle->rects[0]->w, colors))
2134 {
2135 top--;
2136 }
2137
2138 int left = 0;
2139 while (left < (Subtitle->rects[0]->w - 1) &&
2140 IsTransparent(Subtitle->rects[0]->data[0] + left, Subtitle->rects[0]->linesize[0],
2141 Subtitle->rects[0]->h, colors))
2142 {
2143 left++;
2144 }
2145
2146 int right = Subtitle->rects[0]->w - 1;
2147 while (right > 0 &&
2148 IsTransparent(Subtitle->rects[0]->data[0] + right, Subtitle->rects[0]->linesize[0],
2149 Subtitle->rects[0]->h, colors))
2150 {
2151 right--;
2152 }
2153
2154 int width = right - left + 1;
2155 int height = top - bottom + 1;
2156 auto *bitmap = static_cast<uint8_t*>(av_malloc(static_cast<size_t>(width) * height));
2157 if (!bitmap)
2158 return 1;
2159
2160 for (int y = 0; y < height; y++)
2161 {
2162 memcpy(bitmap + (static_cast<ptrdiff_t>(width) * y), Subtitle->rects[0]->data[0] + left +
2163 ((bottom + y) * Subtitle->rects[0]->linesize[0]), static_cast<size_t>(width));
2164 }
2165
2166 av_freep(reinterpret_cast<void*>(&Subtitle->rects[0]->data[0]));
2167 Subtitle->rects[0]->data[0] = bitmap;
2168 Subtitle->rects[0]->linesize[0] = width;
2169 Subtitle->rects[0]->w = width;
2170 Subtitle->rects[0]->h = height;
2171 Subtitle->rects[0]->x += left;
2172 Subtitle->rects[0]->y += bottom;
2173 return 1;
2174}
2175
2177{
2178 if (!m_dvdnav)
2179 return false;
2180
2181 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switching to Angle %1...").arg(Angle));
2182 dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, static_cast<int32_t>(Angle));
2183 if (status == DVDNAV_STATUS_OK)
2184 {
2185 m_currentAngle = Angle;
2186 return true;
2187 }
2188 return false;
2189}
2190
2192{
2193 m_parent = Parent;
2194}
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:54
static int x2
Definition: mythsocket.cpp:55
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:44
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:201
#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