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