MythTV master
mythbdbuffer.cpp
Go to the documentation of this file.
1// Std
2#include <thread>
3#include <fcntl.h>
4
5// Qt
6#include <QDir>
7#include <QCoreApplication>
8
9// MythTV
10#include "libmythbase/iso639.h"
22
23#include "io/mythiowrapper.h"
24#include "libbluray/bluray.h"
25#include "tv_actions.h"
27#include "Bluray/mythbdinfo.h"
28#include "Bluray/mythbdbuffer.h"
29
30// BluRay
31#ifdef HAVE_LIBBLURAY
32#include <libbluray/log_control.h>
33#include <libbluray/meta_data.h>
34#include <libbluray/overlay.h>
35#include <libbluray/keys.h>
36#else
37#include "util/log_control.h"
38#include "libbluray/bdnav/meta_data.h"
39#include "libbluray/decoders/overlay.h"
40#include "libbluray/keys.h"
41#endif
42
43#define LOC QString("BDBuffer: ")
44
45static void HandleOverlayCallback(void *Data, const bd_overlay_s *const Overlay)
46{
47 auto *bdrb = static_cast<MythBDBuffer*>(Data);
48 if (bdrb)
49 bdrb->SubmitOverlay(Overlay);
50}
51
52static void HandleARGBOverlayCallback(void *Data, const bd_argb_overlay_s *const Overlay)
53{
54 auto *bdrb = static_cast<MythBDBuffer*>(Data);
55 if (bdrb)
56 bdrb->SubmitARGBOverlay(Overlay);
57}
58
59static void FileOpenedCallback(void* Data)
60{
61 auto *obj = static_cast<MythBDBuffer*>(Data);
62 if (obj)
63 obj->ProgressUpdate();
64}
65
66static void BDLogger(const char* Message)
67{
68 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString(Message).trimmed());
69}
70
71static int BDRead(void *Handle, void *Buf, int LBA, int NumBlocks)
72{
73 if (MythFileSeek(*(static_cast<int*>(Handle)), LBA * 2048LL, SEEK_SET) != -1)
74 return static_cast<int>(MythFileRead(*(static_cast<int*>(Handle)), Buf,
75 static_cast<size_t>(NumBlocks) * 2048) / 2048);
76 return -1;
77}
78
79MythBDBuffer::MythBDBuffer(const QString &Filename)
81 m_tryHDMVNavigation(qEnvironmentVariableIsSet("MYTHTV_HDMV")),
82 m_overlayPlanes(2, nullptr),
83 m_mainThread(QThread::currentThread())
84{
85 MythBDBuffer::OpenFile(Filename);
86}
87
89{
91 Close();
92}
93
95{
96 if (m_bdnav)
97 {
98 m_infoLock.lock();
99 for (auto it = m_cachedTitleInfo.begin(); it !=m_cachedTitleInfo.end(); ++it)
100 bd_free_title_info(it.value());
101 m_cachedTitleInfo.clear();
102 for (auto it = m_cachedPlaylistInfo.begin(); it !=m_cachedPlaylistInfo.end(); ++it)
103 bd_free_title_info(it.value());
104 m_cachedPlaylistInfo.clear();
105 m_infoLock.unlock();
106 bd_close(m_bdnav);
107 m_bdnav = nullptr;
108 }
109
110 if (m_imgHandle > 0)
111 {
113 m_imgHandle = -1;
114 }
115
117}
118
119long long MythBDBuffer::SeekInternal(long long Position, int Whence)
120{
121 long long ret = -1;
122
123 m_posLock.lockForWrite();
124
125 // Optimize no-op seeks
126 if (m_readAheadRunning &&
127 ((Whence == SEEK_SET && Position == m_readPos) || (Whence == SEEK_CUR && Position == 0)))
128 {
129 ret = m_readPos;
130 m_posLock.unlock();
131 return ret;
132 }
133
134 // only valid for SEEK_SET & SEEK_CUR
135 long long newposition = (SEEK_SET == Whence) ? Position : m_readPos + Position;
136
137 // Here we perform a normal seek. When successful we
138 // need to call ResetReadAhead(). A reset means we will
139 // need to refill the buffer, which takes some time.
140 if ((SEEK_END == Whence) || ((SEEK_CUR == Whence) && newposition != 0))
141 {
142 errno = EINVAL;
143 ret = -1;
144 }
145 else
146 {
147 SeekInternal(static_cast<uint64_t>(newposition));
148 m_currentTime = mpeg::chrono::pts(bd_tell_time(m_bdnav));
149 ret = newposition;
150 }
151
152 if (ret >= 0)
153 {
154 m_readPos = ret;
155 m_ignoreReadPos = -1;
158 m_readAdjust = 0;
159 }
160 else
161 {
162 QString cmd = QString("Seek(%1, %2)").arg(Position)
163 .arg(seek2string(Whence));
164 LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
165 }
166
167 m_posLock.unlock();
168 m_generalWait.wakeAll();
169 return ret;
170}
171
172uint64_t MythBDBuffer::SeekInternal(uint64_t Position)
173{
174 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Seeking to '%1'").arg(Position));
176 if (m_bdnav)
177 return static_cast<uint64_t>(bd_seek_time(m_bdnav, Position));
178 return 0;
179}
180
182{
183 if (!m_infoLock.tryLock())
184 return;
185 Desc = tr("Title %1 chapter %2").arg(m_currentTitle).arg(m_currentTitleInfo->chapters->idx);
186 m_infoLock.unlock();
187}
188
189bool MythBDBuffer::HandleAction(const QStringList &Actions, mpeg::chrono::pts Pts)
190{
192 return false;
193
194 if (Actions.contains(ACTION_MENUTEXT))
195 {
196 PressButton(BD_VK_POPUP, Pts);
197 return true;
198 }
199
200 if (!IsInMenu())
201 return false;
202
203 bool handled = true;
204 if (Actions.contains(ACTION_UP) || Actions.contains(ACTION_CHANNELUP))
205 PressButton(BD_VK_UP, Pts);
206 else if (Actions.contains(ACTION_DOWN) || Actions.contains(ACTION_CHANNELDOWN))
207 PressButton(BD_VK_DOWN, Pts);
208 else if (Actions.contains(ACTION_LEFT) || Actions.contains(ACTION_SEEKRWND))
209 PressButton(BD_VK_LEFT, Pts);
210 else if (Actions.contains(ACTION_RIGHT) || Actions.contains(ACTION_SEEKFFWD))
211 PressButton(BD_VK_RIGHT, Pts);
212 else if (Actions.contains(ACTION_0))
213 PressButton(BD_VK_0, Pts);
214 else if (Actions.contains(ACTION_1))
215 PressButton(BD_VK_1, Pts);
216 else if (Actions.contains(ACTION_2))
217 PressButton(BD_VK_2, Pts);
218 else if (Actions.contains(ACTION_3))
219 PressButton(BD_VK_3, Pts);
220 else if (Actions.contains(ACTION_4))
221 PressButton(BD_VK_4, Pts);
222 else if (Actions.contains(ACTION_5))
223 PressButton(BD_VK_5, Pts);
224 else if (Actions.contains(ACTION_6))
225 PressButton(BD_VK_6, Pts);
226 else if (Actions.contains(ACTION_7))
227 PressButton(BD_VK_7, Pts);
228 else if (Actions.contains(ACTION_8))
229 PressButton(BD_VK_8, Pts);
230 else if (Actions.contains(ACTION_9))
231 PressButton(BD_VK_9, Pts);
232 else if (Actions.contains(ACTION_SELECT))
233 PressButton(BD_VK_ENTER, Pts);
234 else
235 handled = false;
236
237 return handled;
238}
239
241{
242 // This thread check is probably unnecessary as processEvents should
243 // only handle events in the calling thread - and not all threads
245 return;
246
247 qApp->postEvent(GetMythMainWindow(),
249 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
250}
251
253{
254 return m_playerWait;
255}
256
258{
259 m_playerWait = false;
260}
261
270bool MythBDBuffer::OpenFile(const QString &Filename, std::chrono::milliseconds /*Retry*/)
271{
272 m_safeFilename = Filename;
273 m_filename = Filename;
274
275 // clean path filename
276 QString filename = QDir(QDir::cleanPath(Filename)).canonicalPath();
277 if (filename.isEmpty())
278 {
279 LOG(VB_GENERAL, LOG_ERR, LOC + QString("%1 nonexistent").arg(Filename));
280 filename = Filename;
281 }
283
284 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened MythBDBuffer device at %1")
285 .arg(filename));
286
287 // Make sure log messages from the Bluray library appear in our logs
288 bd_set_debug_handler(BDLogger);
289 bd_set_debug_mask(DBG_CRIT | DBG_NAV | DBG_BLURAY);
290
291 // Use our own wrappers for file and directory access
293
294 // Ask mythiowrapper to update this object on file open progress. Opening
295 // a bluray disc can involve opening several hundred files which can take
296 // several minutes when the disc structure is remote. The callback allows
297 // us to 'kick' the main UI - as the 'please wait' widget is still visible
298 // at this stage
299 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, FileOpenedCallback);
300
301 QMutexLocker locker(&m_infoLock);
302 m_rwLock.lockForWrite();
303
304 if (m_bdnav)
305 Close();
306
307 QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir());
308
309 if (filename.startsWith("myth:") && MythCDROM::inspectImage(filename) != MythCDROM::kUnknown)
310 {
311 // Use streaming for remote images.
312 // Streaming encrypted images causes a SIGSEGV in aacs code when
313 // using the makemkv libraries due to the missing "device" name.
314 // Since a local device (which is likely to be encrypted) can be
315 // opened directly, only use streaming for remote images, which
316 // presumably won't be encrypted.
317 m_imgHandle = MythFileOpen(filename.toLocal8Bit().data(), O_RDONLY);
318 if (m_imgHandle >= 0)
319 {
320 m_bdnav = bd_init();
321 if (m_bdnav)
322 bd_open_stream(m_bdnav, &m_imgHandle, BDRead);
323 }
324 }
325 else
326 {
327 m_bdnav = bd_open(filename.toLocal8Bit().data(), qPrintable(keyfile));
328 }
329
330 if (!m_bdnav)
331 {
332 m_lastError = tr("Could not open Blu-ray device: %1").arg(filename);
333 m_rwLock.unlock();
334 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
335 return false;
336 }
337
338 const meta_dl *metaDiscLibrary = bd_get_meta(m_bdnav);
339
340 if (metaDiscLibrary)
341 {
342 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Title: %1 (%2)")
343 .arg(metaDiscLibrary->di_name, metaDiscLibrary->language_code));
344 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Alternative Title: %1")
345 .arg(metaDiscLibrary->di_alternative));
346 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Number: %1 of %2")
347 .arg(metaDiscLibrary->di_set_number).arg(metaDiscLibrary->di_num_sets));
348 }
349
351
352 // Check disc to see encryption status, menu and navigation types.
353 m_topMenuSupported = false;
354 m_firstPlaySupported = false;
355 const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(m_bdnav);
356 if (!discinfo)
357 {
358 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve disc info");
359 bd_close(m_bdnav);
360 m_bdnav = nullptr;
361 m_lastError = tr("Could not open Blu-ray device: %1").arg(filename);
362 m_rwLock.unlock();
363 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
364 return false;
365 }
366
367 if ((discinfo->aacs_detected && !discinfo->aacs_handled) ||
368 (discinfo->bdplus_detected && !discinfo->bdplus_handled))
369 {
370 // couldn't decrypt bluray
371 bd_close(m_bdnav);
372 m_bdnav = nullptr;
373 m_lastError = tr("Could not open Blu-ray device %1, failed to decrypt").arg(filename);
374 m_rwLock.unlock();
375 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
376 return false;
377 }
378
379 // The following settings affect HDMV navigation
380 // (default audio track selection, parental controls, menu language, etc.)
381 // They are not yet used.
382
383 // Set parental level "age" to 99 for now. TODO: Add support for FE level
384 bd_set_player_setting(m_bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99);
385
386 // Set preferred language to FE guide language
387 std::string langpref = gCoreContext->GetSetting("ISO639Language0", "eng").toLatin1().data();
388 QString QScountry = gCoreContext->GetLocale()->GetCountryCode().toLower();
389 std::string country = QScountry.toLatin1().data();
390 bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref.c_str());
391 // Set preferred presentation graphics language to the FE guide language
392 bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref.c_str());
393 // Set preferred menu language to the FE guide language
394 bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref.c_str());
395 // Set player country code via MythLocale. (not a region setting)
396 bd_set_player_setting_str(m_bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country.c_str());
397
398 uint32_t regioncode = static_cast<uint32_t>(gCoreContext->GetNumSetting("BlurayRegionCode"));
399 if (regioncode > 0)
400 bd_set_player_setting(m_bdnav, BLURAY_PLAYER_SETTING_REGION_CODE, regioncode);
401
402 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1 as keyfile...").arg(keyfile));
403
404 // Return an index of relevant titles (excludes dupe clips + titles)
405 LOG(VB_GENERAL, LOG_INFO, LOC + "Retrieving title list (please wait).");
406 m_numTitles = bd_get_titles(m_bdnav, TITLES_RELEVANT, 30);
407 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found %1 titles.").arg(m_numTitles));
408 if (!m_numTitles)
409 {
410 // no title, no point trying any longer
411 bd_close(m_bdnav);
412 m_bdnav = nullptr;
413 m_lastError = tr("Unable to find any Blu-ray compatible titles");
414 m_rwLock.unlock();
415 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
416 return false;
417 }
418
419 m_topMenuSupported = (discinfo->top_menu_supported != 0U);
420 m_firstPlaySupported = (discinfo->first_play_supported != 0U);
421
422 LOG(VB_PLAYBACK, LOG_INFO, LOC + "*** Blu-ray Disc Information ***");
423 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("First Play Supported: %1")
424 .arg(discinfo->first_play_supported ? "yes" : "no"));
425 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Top Menu Supported: %1")
426 .arg(discinfo->top_menu_supported ? "yes" : "no"));
427 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of HDMV Titles: %1")
428 .arg(discinfo->num_hdmv_titles));
429 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of BD-J Titles: %1")
430 .arg(discinfo->num_bdj_titles));
431 if (discinfo->num_bdj_titles && discinfo->bdj_detected)
432 {
433 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD-J Supported: %1")
434 .arg(discinfo->bdj_handled ? "yes" : "no"));
435 }
436 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of Unsupported Titles: %1")
437 .arg(discinfo->num_unsupported_titles));
438 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS present on disc: %1")
439 .arg(discinfo->aacs_detected ? "yes" : "no"));
440 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libaacs used: %1")
441 .arg(discinfo->libaacs_detected ? "yes" : "no"));
442 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS handled: %1")
443 .arg(discinfo->aacs_handled ? "yes" : "no"));
444 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ present on disc: %1")
445 .arg(discinfo->bdplus_detected ? "yes" : "no"));
446 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libbdplus used: %1")
447 .arg(discinfo->libbdplus_detected ? "yes" : "no"));
448 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ handled: %1")
449 .arg(discinfo->bdplus_handled ? "yes" : "no"));
450
451 m_mainTitle = 0;
452 m_currentTitleLength = 0_pts;
453 m_titlesize = 0;
454 m_currentTime = 0_pts;
455 m_currentTitleInfo = nullptr;
458 m_lastEvent.event = BD_EVENT_NONE;
459 m_lastEvent.param = 0;
460
461 // Mostly event-driven values below
462 m_currentAngle = 0;
463 m_currentTitle = -1;
472 m_pgTextSTEnabled = false;
476 m_stillMode = BLURAY_STILL_NONE;
477 m_stillTime = 0s;
478 m_timeDiff = 0;
479 m_inMenu = false;
480
481 // First, attempt to initialize the disc in HDMV navigation mode.
482 // If this fails, fall back to the traditional built-in title switching
483 // mode.
485 {
486 LOG(VB_GENERAL, LOG_INFO, LOC + "Using HDMV navigation mode.");
487 m_isHDMVNavigation = true;
488
489 // Register the Menu Overlay Callback
490 bd_register_overlay_proc(m_bdnav, this, HandleOverlayCallback);
491 bd_register_argb_overlay_proc(m_bdnav, this, HandleARGBOverlayCallback, nullptr);
492 }
493 else
494 {
495 LOG(VB_GENERAL, LOG_INFO, LOC + "Using title navigation mode.");
496
497 // Loop through the relevant titles and find the longest
498 uint64_t titleLength = 0;
499 BLURAY_TITLE_INFO *titleInfo = nullptr;
500 bool found = false;
501 for(uint32_t i = 0; i < m_numTitles; ++i)
502 {
503 titleInfo = GetTitleInfo(i);
504 if (!titleInfo)
505 continue;
506 if (titleLength == 0 || (titleInfo->duration > titleLength))
507 {
508 m_mainTitle = titleInfo->idx;
509 titleLength = titleInfo->duration;
510 found = true;
511 }
512 }
513
514 if (!found)
515 {
516 // no title, no point trying any longer
517 bd_close(m_bdnav);
518 m_bdnav = nullptr;
519 m_lastError = tr("Unable to find any usable Blu-ray titles");
520 m_rwLock.unlock();
521 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
522 return false;
523 }
525 }
526
528 m_setSwitchToNext = false;
529 m_ateof = false;
530 m_commsError = false;
531 m_numFailures = 0;
532 m_rawBitrate = 8000;
534 m_rwLock.unlock();
535 MythFileOpenRegisterCallback(filename.toLocal8Bit().data(), this, nullptr);
536 return true;
537}
538
539long long MythBDBuffer::GetReadPosition(void) const
540{
541 if (m_bdnav)
542 return static_cast<long long>(bd_tell(m_bdnav));
543 return 0;
544}
545
546bool MythBDBuffer::IsOpen(void) const
547{
548 return m_bdnav;
549}
550
552{
553 QMutexLocker locker(&m_infoLock);
555 return m_currentTitleInfo->chapter_count - 1;
556 return 0;
557}
558
560{
561 if (m_bdnav)
562 return bd_get_current_chapter(m_bdnav);
563 return 0;
564}
565
566std::chrono::milliseconds MythBDBuffer::GetChapterStartTimeMs(uint32_t Chapter)
567{
568 if (Chapter >= GetNumChapters())
569 return 0ms;
570 QMutexLocker locker(&m_infoLock);
571 auto start = mpeg::chrono::pts(m_currentTitleInfo->chapters[Chapter].start);
572 return duration_cast<std::chrono::milliseconds>(start);
573}
574
575std::chrono::seconds MythBDBuffer::GetChapterStartTime(uint32_t Chapter)
576{
577 if (Chapter >= GetNumChapters())
578 return 0s;
579 QMutexLocker locker(&m_infoLock);
580 auto start = mpeg::chrono::pts(m_currentTitleInfo->chapters[Chapter].start);
581 return duration_cast<std::chrono::seconds>(start);
582}
583
584uint64_t MythBDBuffer::GetChapterStartFrame(uint32_t Chapter)
585{
586 if (Chapter >= GetNumChapters())
587 return 0;
588 QMutexLocker locker(&m_infoLock);
589 return static_cast<uint64_t>((m_currentTitleInfo->chapters[Chapter].start * GetFrameRate()) / 90000.0);
590}
591
592uint32_t MythBDBuffer::GetNumTitles(void) const
593{
594 return m_numTitles;
595}
596
598{
599 QMutexLocker locker(&m_infoLock);
600 return m_currentTitle;
601}
602
604{
605 return static_cast<uint64_t>(m_currentAngle);
606}
607
608std::chrono::seconds MythBDBuffer::GetTitleDuration(int Title)
609{
610 QMutexLocker locker(&m_infoLock);
611 auto numTitles = GetNumTitles();
612 if (numTitles <= 0 || Title < 0 || Title >= static_cast<int>(numTitles))
613 return 0s;
614
615 BLURAY_TITLE_INFO *info = GetTitleInfo(static_cast<uint32_t>(Title));
616 if (!info)
617 return 0s;
618
619 return duration_cast<std::chrono::seconds>(mpeg::chrono::pts(info->duration));
620}
621
622uint64_t MythBDBuffer::GetTitleSize(void) const
623{
624 return m_titlesize;
625}
626
627std::chrono::seconds MythBDBuffer::GetTotalTimeOfTitle(void) const
628{
629 return duration_cast<std::chrono::seconds>(m_currentTitleLength);
630}
631
632std::chrono::seconds MythBDBuffer::GetCurrentTime(void) const
633{
634 return duration_cast<std::chrono::seconds>(m_currentTime);
635}
636
637bool MythBDBuffer::SwitchTitle(uint32_t Index)
638{
639 if (!m_bdnav)
640 return false;
641
642 m_infoLock.lock();
644 m_infoLock.unlock();
645 bd_select_title(m_bdnav, Index);
646
647 return UpdateTitleInfo();
648}
649
651{
652 if (!m_bdnav)
653 return false;
654
655 LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - start");
656
657 m_infoLock.lock();
659 m_currentTitle = static_cast<int>(bd_get_current_title(m_bdnav));
660 m_infoLock.unlock();
661 bool result = UpdateTitleInfo();
662
663 LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - end");
664 return result;
665}
666
667BLURAY_TITLE_INFO* MythBDBuffer::GetTitleInfo(uint32_t Index)
668{
669 if (!m_bdnav)
670 return nullptr;
671
672 QMutexLocker locker(&m_infoLock);
673 if (m_cachedTitleInfo.contains(Index))
674 return m_cachedTitleInfo.value(Index);
675
676 if (Index > m_numTitles)
677 return nullptr;
678
679 BLURAY_TITLE_INFO* result = bd_get_title_info(m_bdnav, Index, 0);
680 if (result)
681 {
682 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found title %1 info").arg(Index));
683 m_cachedTitleInfo.insert(Index,result);
684 return result;
685 }
686 return nullptr;
687}
688
689BLURAY_TITLE_INFO* MythBDBuffer::GetPlaylistInfo(uint32_t Index)
690{
691 if (!m_bdnav)
692 return nullptr;
693
694 QMutexLocker locker(&m_infoLock);
695 if (m_cachedPlaylistInfo.contains(Index))
696 return m_cachedPlaylistInfo.value(Index);
697
698 BLURAY_TITLE_INFO* result = bd_get_playlist_info(m_bdnav, Index, 0);
699 if (result)
700 {
701 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found playlist %1 info").arg(Index));
702 m_cachedPlaylistInfo.insert(Index,result);
703 return result;
704 }
705 return nullptr;
706}
707
709{
710 QMutexLocker locker(&m_infoLock);
712 return false;
713
714 m_titleChanged = true;
717 m_currentAngle = 0;
719 m_timeDiff = 0;
720 m_titlesize = bd_get_title_size(m_bdnav);
721 uint32_t chapter_count = GetNumChapters();
722 auto total_msecs = duration_cast<std::chrono::milliseconds>(m_currentTitleLength);
723 auto duration = MythDate::formatTime(total_msecs, "HH:mm:ss.z");
724 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New title info: Index %1 Playlist: %2 Duration: %3 ""Chapters: %5")
725 .arg(m_currentTitle).arg(m_currentTitleInfo->playlist).arg(duration).arg(chapter_count));
726 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New title info: Clips: %1 Angles: %2 Title Size: %3 Frame Rate %4")
728 .arg(GetFrameRate()));
729
730 for (uint i = 0; i < chapter_count; i++)
731 {
732 uint64_t framenum = GetChapterStartFrame(i);
733 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Chapter %1 found @ [%2]->%3")
736 QString::number(framenum)));
737 }
738
739 int still = BLURAY_STILL_NONE;
740 std::chrono::seconds time = 0s;
741 if (m_currentTitleInfo->clip_count)
742 {
743 for (uint i = 0; i < m_currentTitleInfo->clip_count; i++)
744 {
745 LOG(VB_PLAYBACK, LOG_INFO, LOC +
746 QString("Clip %1 stillmode %2 stilltime %3 videostreams %4 audiostreams %5 igstreams %6")
747 .arg(i).arg(m_currentTitleInfo->clips[i].still_mode)
748 .arg(m_currentTitleInfo->clips[i].still_time)
749 .arg(m_currentTitleInfo->clips[i].video_stream_count)
750 .arg(m_currentTitleInfo->clips[i].audio_stream_count)
751 .arg(m_currentTitleInfo->clips[i].ig_stream_count));
752 still |= m_currentTitleInfo->clips[i].still_mode;
753 time = std::chrono::seconds(m_currentTitleInfo->clips[i].still_time);
754 }
755 }
756
757 if (m_currentTitleInfo->clip_count > 1 && still != BLURAY_STILL_NONE)
758 {
759 LOG(VB_GENERAL, LOG_WARNING, LOC + "Warning: more than 1 clip, following still "
760 "frame analysis may be wrong");
761 }
762
763 if (still == BLURAY_STILL_TIME)
764 {
765 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Entering still frame (%1 seconds) UNSUPPORTED").arg(time.count()));
766 bd_read_skip_still(m_bdnav);
767 }
768 else if (still == BLURAY_STILL_INFINITE)
769 {
770 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Entering infinite still frame.");
771 }
772
773 m_stillMode = still;
774 m_stillTime = time;
775 return true;
776}
777
779{
780 bool ret = m_titleChanged;
781 m_titleChanged = false;
782 return ret;
783}
784
786{
787 if (!m_bdnav)
788 return false;
789
790 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switching to Angle '%1'").arg(Angle));
791 bd_seamless_angle_change(m_bdnav, Angle);
792 m_currentAngle = static_cast<int>(Angle);
793 return true;
794}
795
796uint64_t MythBDBuffer::GetNumAngles(void) const
797{
799}
800
802{
803 if (m_bdnav)
804 return bd_get_title_size(m_bdnav);
805 return 0;
806}
807
808int64_t MythBDBuffer::AdjustTimestamp(int64_t Timestamp) const
809{
810 int64_t newTimestamp = Timestamp;
811 if ((newTimestamp != AV_NOPTS_VALUE) && (newTimestamp >= m_timeDiff))
812 newTimestamp -= m_timeDiff;
813 return newTimestamp;
814}
815
817{
818 int result = 0;
820 {
821 result = HandleBDEvents() ? 0 : -1;
822 while (result == 0)
823 {
824 BD_EVENT event;
825 result = bd_read_ext(m_bdnav, static_cast<unsigned char*>(Buffer),
826 static_cast<int>(Size), &event);
827 if (result == 0)
828 {
829 HandleBDEvent(event);
830 result = HandleBDEvents() ? 0 : -1;
831 }
832 }
833 }
834 else
835 {
837 {
839
841 result = bd_read(m_bdnav, static_cast<unsigned char*>(Buffer), static_cast<int>(Size));
842
844
845 if (m_processState == PROCESS_WAIT && lastState == PROCESS_NORMAL)
846 {
847 // We're waiting for the decoder to drain its buffers
848 // so don't give it any more data just yet.
849 m_pendingData = QByteArray(static_cast<const char*>(Buffer), result);
850 result = 0;
851 }
852 else
853 if (m_processState == PROCESS_NORMAL && lastState == PROCESS_REPROCESS)
854 {
855 // The decoder has finished draining its buffers so give
856 // it that last block of data we read
857 result = m_pendingData.size();
858 memcpy(Buffer, m_pendingData.constData(), static_cast<size_t>(result));
859 m_pendingData.clear();
860 }
861 }
862 }
863
864 if (result < 0)
865 StopReads();
866
867 m_currentTime = mpeg::chrono::pts(bd_tell_time(m_bdnav));
868 return result;
869}
870
872{
873 QMutexLocker locker(&m_infoLock);
875 {
876 switch (m_currentTitleInfo->clips->video_streams->rate)
877 {
878 case BLURAY_VIDEO_RATE_24000_1001: return 23.97;
879 case BLURAY_VIDEO_RATE_24: return 24.00;
880 case BLURAY_VIDEO_RATE_25: return 25.00;
881 case BLURAY_VIDEO_RATE_30000_1001: return 29.97;
882 case BLURAY_VIDEO_RATE_50: return 50.00;
883 case BLURAY_VIDEO_RATE_60000_1001: return 59.94;
884 default: break;
885 }
886 }
887 return 0;
888}
889
891{
892 QMutexLocker locker(&m_infoLock);
893
894 int code = iso639_str3_to_key("und");
895
896 if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
897 {
898 bd_clip& clip = m_currentTitleInfo->clips[0];
899 const BLURAY_STREAM_INFO* stream = FindStream(StreamID, clip.audio_streams, clip.audio_stream_count);
900 if (stream)
901 {
902 const uint8_t* lang = stream->lang;
903 code = iso639_key_to_canonical_key((lang[0] << 16) | (lang[1] << 8) | lang[2]);
904 }
905 }
906
907 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Audio Lang: 0x%1 Code: %2")
908 .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
909 return code;
910}
911
913{
914 QMutexLocker locker(&m_infoLock);
915 int code = iso639_str3_to_key("und");
916 if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
917 {
918 bd_clip& clip = m_currentTitleInfo->clips[0];
919 const BLURAY_STREAM_INFO* stream = FindStream(StreamID, clip.pg_streams, clip.pg_stream_count);
920 if (stream)
921 {
922 const uint8_t* lang = stream->lang;
923 code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
924 }
925 }
926
927 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Subtitle Lang: 0x%1 Code: %2")
928 .arg(code, 3, 16).arg(iso639_key_to_str3(code)));
929 return code;
930}
931
933{
934 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Key %1 (pts %2)").arg(Key).arg(Pts.count()));
935 // HACK for still frame menu navigation
936 Pts = 1_pts;
937 if (!m_bdnav || /*Pts <= 0 ||*/ Key < 0)
938 return;
939 bd_user_input(m_bdnav, Pts.count(), static_cast<uint32_t>(Key));
940}
941
943{
944 if (!m_bdnav)
945 return;
946 if (Pts <= 0 || X == 0 || Y == 0)
947 return;
948 bd_mouse_select(m_bdnav, Pts, X, Y);
949}
950
953bool MythBDBuffer::GoToMenu(const QString &Menu, mpeg::chrono::pts Pts)
954{
955 if (!m_isHDMVNavigation || Pts < 0_pts)
956 return false;
957
959 {
960 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Top Menu not supported");
961 return false;
962 }
963
964 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GoToMenu %1").arg(Menu));
965
966 if (Menu.compare("root") == 0)
967 {
968 if (bd_menu_call(m_bdnav, Pts.count()))
969 {
970 LOG(VB_PLAYBACK, LOG_INFO, LOC +QString("Invoked Top Menu (pts %1)").arg(Pts.count()));
971 return true;
972 }
973 }
974 else if (Menu.compare("popup") == 0)
975 {
976 PressButton(BD_VK_POPUP, Pts);
977 return true;
978 }
979
980 return false;
981}
982
984{
986 {
988 {
990 // HandleBDEvent will change the process state
991 // if it needs to so don't do it here.
992 }
993
994 while (m_processState == PROCESS_NORMAL && bd_get_event(m_bdnav, &m_lastEvent))
995 {
997 if (m_lastEvent.event == BD_EVENT_NONE || m_lastEvent.event == BD_EVENT_ERROR)
998 return false;
999 }
1000 }
1001 return true;
1002}
1003
1005{
1006 switch (Event.event) {
1007 case BD_EVENT_NONE:
1008 break;
1009 case BD_EVENT_ERROR:
1010 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_ERROR %1").arg(Event.param));
1011 break;
1012 case BD_EVENT_ENCRYPTED:
1013 LOG(VB_GENERAL, LOG_ERR, LOC + "EVENT_ENCRYPTED, playback will fail.");
1014 break;
1015
1016 /* current playback position */
1017
1018 case BD_EVENT_ANGLE:
1019 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_ANGLE %1").arg(Event.param));
1020 m_currentAngle = static_cast<int>(Event.param);
1021 break;
1022 case BD_EVENT_TITLE:
1023 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_TITLE %1 (old %2)")
1024 .arg(Event.param).arg(m_currentTitle));
1025 m_currentTitle = static_cast<int>(Event.param);
1026 break;
1027 case BD_EVENT_END_OF_TITLE:
1028 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_END_OF_TITLE %1").arg(m_currentTitle));
1029 WaitForPlayer();
1030 break;
1031 case BD_EVENT_PLAYLIST:
1032 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PLAYLIST %1 (old %2)")
1033 .arg(Event.param).arg(m_currentPlaylist));
1034 m_currentPlaylist = static_cast<int>(Event.param);
1035 m_timeDiff = 0;
1037 SwitchPlaylist(static_cast<uint32_t>(m_currentPlaylist));
1038 break;
1039 case BD_EVENT_PLAYITEM:
1040 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PLAYITEM %1").arg(Event.param));
1041 {
1042 if (m_currentPlayitem != static_cast<int>(Event.param))
1043 {
1044 auto out = static_cast<int64_t>(m_currentTitleInfo->clips[m_currentPlayitem].out_time);
1045 auto in = static_cast<int64_t>(m_currentTitleInfo->clips[Event.param].in_time);
1046 int64_t diff = in - out;
1047 if (diff != 0 && m_processState == PROCESS_NORMAL)
1048 {
1049 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("PTS discontinuity - waiting for decoder: this %1, last %2, diff %3")
1050 .arg(in).arg(out).arg(diff));
1052 break;
1053 }
1054
1055 m_timeDiff += diff;
1057 m_currentPlayitem = static_cast<int>(Event.param);
1058 }
1059 }
1060 break;
1061 case BD_EVENT_CHAPTER:
1062 // N.B. event chapter numbering 1...N, chapter seeks etc 0...
1063 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_CHAPTER %1").arg(Event.param));
1064 m_currentChapter = static_cast<int>(Event.param);
1065 break;
1066 case BD_EVENT_PLAYMARK:
1067 /* playmark reached */
1068 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PLAYMARK"));
1069 break;
1070
1071 /* playback control */
1072 case BD_EVENT_PLAYLIST_STOP:
1073 /* HDMV VM or JVM stopped playlist playback. Flush all buffers. */
1074 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ToDo EVENT_PLAYLIST_STOP %1")
1075 .arg(Event.param));
1076 break;
1077
1078 case BD_EVENT_STILL:
1079 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_STILL %1").arg(Event.param));
1080 break;
1081 case BD_EVENT_STILL_TIME:
1082 // we use the clip information to determine the still frame status
1083 // sleep a little
1084 std::this_thread::sleep_for(10ms);
1085 break;
1086 case BD_EVENT_SEEK:
1087 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SEEK"));
1088 break;
1089
1090 /* stream selection */
1091
1092 case BD_EVENT_AUDIO_STREAM:
1093 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_AUDIO_STREAM %1").arg(Event.param));
1094 m_currentAudioStream = static_cast<int>(Event.param);
1095 break;
1096 case BD_EVENT_IG_STREAM:
1097 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_IG_STREAM %1").arg(Event.param));
1098 m_currentIGStream = static_cast<int>(Event.param);
1099 break;
1100 case BD_EVENT_PG_TEXTST_STREAM:
1101 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PG_TEXTST_STREAM %1").arg(Event.param));
1102 m_currentPGTextSTStream = static_cast<int>(Event.param);
1103 break;
1104 case BD_EVENT_SECONDARY_AUDIO_STREAM:
1105 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_AUDIO_STREAM %1").arg(Event.param));
1106 m_currentSecondaryAudioStream = static_cast<int>(Event.param);
1107 break;
1108 case BD_EVENT_SECONDARY_VIDEO_STREAM:
1109 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO_STREAM %1").arg(Event.param));
1110 m_currentSecondaryVideoStream = static_cast<int>(Event.param);
1111 break;
1112
1113 case BD_EVENT_PG_TEXTST:
1114 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PG_TEXTST %1")
1115 .arg(Event.param ? "enable" : "disable"));
1116 m_pgTextSTEnabled = (Event.param != 0U);
1117 break;
1118 case BD_EVENT_SECONDARY_AUDIO:
1119 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_AUDIO %1")
1120 .arg(Event.param ? "enable" : "disable"));
1121 m_secondaryAudioEnabled = (Event.param != 0U);
1122 break;
1123 case BD_EVENT_SECONDARY_VIDEO:
1124 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO %1")
1125 .arg(Event.param ? "enable" : "disable"));
1126 m_secondaryVideoEnabled = (Event.param != 0U);
1127 break;
1128 case BD_EVENT_SECONDARY_VIDEO_SIZE:
1129 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO_SIZE %1")
1130 .arg(Event.param==0 ? "PIP" : "fullscreen"));
1131 m_secondaryVideoIsFullscreen = (Event.param != 0U);
1132 break;
1133
1134 /* status */
1135 case BD_EVENT_IDLE:
1136 /* Nothing to do. Playlist is not playing, but title applet is running.
1137 * Application should not call bd_read*() immediately again to avoid busy loop. */
1138 std::this_thread::sleep_for(40ms);
1139 break;
1140
1141 case BD_EVENT_MENU:
1142 /* Interactive menu visible */
1143 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_MENU %1")
1144 .arg(Event.param==0 ? "no" : "yes"));
1145 m_inMenu = (Event.param == 1);
1146 break;
1147
1148 case BD_EVENT_KEY_INTEREST_TABLE:
1149 /* BD-J key interest table changed */
1150 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ToDo EVENT_KEY_INTEREST_TABLE %1")
1151 .arg(Event.param));
1152 break;
1153
1154 case BD_EVENT_UO_MASK_CHANGED:
1155 /* User operations mask was changed */
1156 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("ToDo EVENT_UO_MASK_CHANGED %1")
1157 .arg(Event.param));
1158 break;
1159
1160 default:
1161 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Unknown Event! %1 %2")
1162 .arg(Event.event).arg(Event.param));
1163 break;
1164 }
1165}
1166
1168{
1169 return m_stillTime > 0s && m_stillMode != BLURAY_STILL_NONE;
1170}
1171
1179const BLURAY_STREAM_INFO* MythBDBuffer::FindStream(uint StreamID,
1180 BLURAY_STREAM_INFO* Streams,
1181 int StreamCount)
1182{
1183 const BLURAY_STREAM_INFO* stream = nullptr;
1184 for (int i = 0; i < StreamCount && !stream; i++)
1185 if (Streams[i].pid == StreamID)
1186 stream = &Streams[i];
1187 return stream;
1188}
1189
1191{
1192 if (m_currentTitleInfo && m_currentTitleInfo->clip_count > 0)
1193 {
1194 bd_clip& clip = m_currentTitleInfo->clips[0];
1195 if (FindStream(StreamId, clip.audio_streams, clip.audio_stream_count) ||
1196 FindStream(StreamId, clip.video_streams, clip.video_stream_count) ||
1197 FindStream(StreamId, clip.ig_streams, clip.ig_stream_count) ||
1198 FindStream(StreamId, clip.pg_streams, clip.pg_stream_count) ||
1199 FindStream(StreamId, clip.sec_audio_streams, clip.sec_audio_stream_count) ||
1200 FindStream(StreamId, clip.sec_video_streams, clip.sec_video_stream_count))
1201 {
1202 return true;
1203 }
1204 }
1205
1206 return false;
1207}
1208
1210{
1212}
1213
1215{
1216 return m_processState == PROCESS_WAIT;
1217}
1218
1220{
1221 return m_isHDMVNavigation;
1222}
1223
1225{
1227 return;
1228
1229 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
1230 m_playerWait = true;
1231 int count = 0;
1232 while (m_playerWait && count++ < 200)
1233 std::this_thread::sleep_for(10ms);
1234 if (m_playerWait)
1235 {
1236 LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
1237 m_playerWait = false;
1238 }
1239}
1240
1242{
1243 m_ignorePlayerWait = Ignore;
1244}
1245
1247{
1249 {
1250 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Starting from beginning...");
1251 return true; //bd_play(m_bdnav);
1252 }
1253 return true;
1254}
1255
1256bool MythBDBuffer::GetNameAndSerialNum(QString &Name, QString &SerialNum)
1257{
1258 if (!m_bdnav)
1259 return false;
1260 Name = m_discName;
1261 SerialNum = m_discSerialNumber;
1262 return !SerialNum.isEmpty();
1263}
1264
1268{
1269 int title = GetCurrentTitle();
1271 uint64_t angle = GetCurrentAngle();
1272 if (title >= 0)
1273 State = QString("title:%1,time:%2,angle:%3").arg(title).arg(time.count()).arg(angle);
1274 else
1275 State.clear();
1276 return !State.isEmpty();
1277}
1278
1282{
1283 QStringList states = State.split(",", Qt::SkipEmptyParts);
1284 QHash<QString, uint64_t> settings;
1285
1286 for (const QString& state : std::as_const(states))
1287 {
1288 QStringList keyvalue = state.split(":", Qt::SkipEmptyParts);
1289 if (keyvalue.length() != 2)
1290 {
1291 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Invalid BD state: %1 (%2)")
1292 .arg(state, State));
1293 }
1294 else
1295 {
1296 settings[keyvalue[0]] = keyvalue[1].toULongLong();
1297 //LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString( "%1 = %2" ).arg(keyvalue[0]).arg(keyvalue[1]));
1298 }
1299 }
1300
1301 if (settings.contains("title") && settings.contains("time"))
1302 {
1303 uint32_t title = static_cast<uint32_t>(settings["title"]);
1304 uint64_t time = settings["time"];
1305 uint64_t angle = 0;
1306
1307 if (settings.contains("angle"))
1308 angle = settings["angle"];
1309
1310 if (title != static_cast<uint32_t>(m_currentTitle))
1311 SwitchTitle(title);
1312
1313 SeekInternal(static_cast<long long>(time), SEEK_SET);
1314 SwitchAngle(static_cast<uint>(angle));
1315 return true;
1316 }
1317
1318 return false;
1319}
1320
1321
1323{
1324 QMutexLocker lock(&m_overlayLock);
1325
1326 while (!m_overlayImages.isEmpty())
1327 {
1328 MythBDOverlay *overlay = m_overlayImages.takeFirst();
1329 delete overlay;
1330 overlay = nullptr;
1331 }
1332
1333 // NOLINTNEXTLINE(modernize-loop-convert)
1334 for (int i = 0; i < m_overlayPlanes.size(); i++)
1335 {
1336 MythBDOverlay*& osd = m_overlayPlanes[i];
1337
1338 if (osd)
1339 {
1340 delete osd;
1341 osd = nullptr;
1342 }
1343 }
1344}
1345
1347{
1348 QMutexLocker lock(&m_overlayLock);
1349 if (!m_overlayImages.isEmpty())
1350 return m_overlayImages.takeFirst();
1351 return nullptr;
1352}
1353
1354void MythBDBuffer::SubmitOverlay(const bd_overlay_s* const Overlay)
1355{
1356 if (!Overlay || (Overlay->plane > m_overlayPlanes.size()))
1357 return;
1358
1359 LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1360 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd = %1, %2")
1361 .arg(Overlay->cmd).arg(Overlay->plane));
1362 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay rect = (%1,%2,%3,%4)")
1363 .arg(Overlay->x).arg(Overlay->y).arg(Overlay->w).arg(Overlay->h));
1364 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1")
1365 .arg(Overlay->pts));
1366 LOG(VB_PLAYBACK, LOG_DEBUG, QString("update palette = %1")
1367 .arg(Overlay->palette_update_flag ? "yes":"no"));
1368
1369 MythBDOverlay*& osd = m_overlayPlanes[Overlay->plane];
1370
1371 switch(Overlay->cmd)
1372 {
1373 case BD_OVERLAY_INIT:
1374 // init overlay plane. Size and position of plane in x,y,w,h
1375 // init overlay plane. Size of plane in w,h
1376 delete osd;
1377 osd = new MythBDOverlay(Overlay);
1378 break;
1379 case BD_OVERLAY_CLOSE:
1380 // close overlay
1381 {
1382 if (osd)
1383 {
1384 delete osd;
1385 osd = nullptr;
1386 }
1387 QMutexLocker lock(&m_overlayLock);
1388 m_overlayImages.append(new MythBDOverlay());
1389 }
1390 break;
1391 /* following events can be processed immediately, but changes
1392 * should not be flushed to display before next FLUSH event
1393 */
1394 case BD_OVERLAY_HIDE: /* overlay is empty and can be hidden */
1395 case BD_OVERLAY_CLEAR: /* clear plane */
1396 if (osd)
1397 osd->Wipe();
1398 break;
1399 case BD_OVERLAY_WIPE: /* clear area (x,y,w,h) */
1400 if (osd)
1401 osd->Wipe(Overlay->x, Overlay->y, Overlay->w, Overlay->h);
1402 break;
1403 case BD_OVERLAY_DRAW: /* draw bitmap (x,y,w,h,img,palette,crop) */
1404 if (osd)
1405 {
1406 const BD_PG_RLE_ELEM *rlep = Overlay->img;
1407 int actual = Overlay->w * Overlay->h;
1408 uint8_t *data = osd->m_image.bits();
1409 data = &data[(Overlay->y * osd->m_image.bytesPerLine()) + Overlay->x];
1410 for (int i = 0; i < actual; i += rlep->len, rlep++)
1411 {
1412 int dst_y = (i / Overlay->w) * osd->m_image.bytesPerLine();
1413 int dst_x = (i % Overlay->w);
1414 memset(data + dst_y + dst_x, rlep->color, rlep->len);
1415 }
1416 osd->SetPalette(Overlay->palette);
1417 }
1418 break;
1419
1420 case BD_OVERLAY_FLUSH: /* all changes have been done, flush overlay to display at given pts */
1421 if (osd)
1422 {
1423 auto* newOverlay = new MythBDOverlay(*osd);
1424 newOverlay->m_image = osd->m_image.convertToFormat(QImage::Format_ARGB32);
1425 newOverlay->m_pts = Overlay->pts;
1426 QMutexLocker lock(&m_overlayLock);
1427 m_overlayImages.append(newOverlay);
1428 }
1429 break;
1430 default: break;
1431 }
1432}
1433
1434void MythBDBuffer::SubmitARGBOverlay(const bd_argb_overlay_s * const Overlay)
1435{
1436 if (!Overlay || (Overlay->plane > m_overlayPlanes.size()))
1437 return;
1438
1439 LOG(VB_PLAYBACK, LOG_DEBUG, QString("--------------------"));
1440 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->cmd,plane = %1, %2")
1441 .arg(Overlay->cmd).arg(Overlay->plane));
1442 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->(x,y,w,h) = %1,%2,%3x%4 - %5")
1443 .arg(Overlay->x).arg(Overlay->y).arg(Overlay->w).arg(Overlay->h).arg(Overlay->stride));
1444 LOG(VB_PLAYBACK, LOG_DEBUG, QString("overlay->pts = %1").arg(Overlay->pts));
1445
1446 MythBDOverlay*& osd = m_overlayPlanes[Overlay->plane];
1447 switch(Overlay->cmd)
1448 {
1449 case BD_ARGB_OVERLAY_INIT:
1450 /* init overlay plane. Size of plane in w,h */
1451 delete osd;
1452 osd = new MythBDOverlay(Overlay);
1453 break;
1454 case BD_ARGB_OVERLAY_CLOSE:
1455 /* close overlay */
1456 {
1457 if (osd)
1458 {
1459 delete osd;
1460 osd = nullptr;
1461 }
1462 QMutexLocker lock(&m_overlayLock);
1463 m_overlayImages.append(new MythBDOverlay());
1464 }
1465 break;
1466 /* following events can be processed immediately, but changes
1467 * should not be flushed to display before next FLUSH event
1468 */
1469 case BD_ARGB_OVERLAY_DRAW:
1470 if (osd)
1471 {
1472 /* draw image */
1473 uint8_t* data = osd->m_image.bits();
1474 int32_t srcOffset = 0;
1475 int32_t dstOffset = (Overlay->y * osd->m_image.bytesPerLine()) + (Overlay->x * 4);
1476 for (uint16_t y = 0; y < Overlay->h; y++)
1477 {
1478 memcpy(&data[dstOffset], &Overlay->argb[srcOffset], Overlay->w * 4_UZ);
1479 dstOffset += osd->m_image.bytesPerLine();
1480 srcOffset += Overlay->stride;
1481 }
1482 }
1483 break;
1484 case BD_ARGB_OVERLAY_FLUSH:
1485 /* all changes have been done, flush overlay to display at given pts */
1486 if (osd)
1487 {
1488 QMutexLocker lock(&m_overlayLock);
1489 auto* newOverlay = new MythBDOverlay(*osd);
1490 newOverlay->m_pts = Overlay->pts;
1491 m_overlayImages.append(newOverlay);
1492 }
1493 break;
1494 default:
1495 LOG(VB_PLAYBACK, LOG_ERR, QString("Unknown ARGB overlay - %1").arg(Overlay->cmd));
1496 break;
1497 }
1498}
Event details.
Definition: zmdefines.h:28
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
A class to allow a MythMediaBuffer to read from BDs.
Definition: mythbdbuffer.h:20
uint64_t GetTotalReadPosition(void)
QRecursiveMutex m_infoLock
Definition: mythbdbuffer.h:135
void ClickButton(int64_t Pts, uint16_t X, uint16_t Y)
QHash< uint32_t, BLURAY_TITLE_INFO * > m_cachedTitleInfo
Definition: mythbdbuffer.h:133
std::chrono::seconds GetTotalTimeOfTitle(void) const
std::chrono::seconds GetCurrentTime(void) const
int m_currentPGTextSTStream
Definition: mythbdbuffer.h:115
void IgnoreWaitStates(bool Ignore) override
bool HandleAction(const QStringList &Actions, mpeg::chrono::pts Pts) override
bool GetNameAndSerialNum(QString &Name, QString &SerialNum) override
bool HandleBDEvents(void)
bool m_ignorePlayerWait
Definition: mythbdbuffer.h:124
bool TitleChanged(void)
bool m_secondaryVideoIsFullscreen
Definition: mythbdbuffer.h:121
uint64_t m_titlesize
Definition: mythbdbuffer.h:105
std::chrono::milliseconds GetChapterStartTimeMs(uint32_t Chapter)
void HandleBDEvent(BD_EVENT &Event)
bool RestoreBDStateSnapshot(const QString &State)
Restore a BD snapshot.
void GetDescForPos(QString &Desc)
int m_currentPlaylist
Definition: mythbdbuffer.h:110
BLURAY_TITLE_INFO * GetPlaylistInfo(uint32_t Index)
void SkipBDWaitingForPlayer(void)
int64_t m_timeDiff
Definition: mythbdbuffer.h:132
int SafeRead(void *Buffer, uint Size) override
void ProgressUpdate(void)
bool m_pgTextSTEnabled
Definition: mythbdbuffer.h:118
bool IsOpen(void) const override
BLURAY * m_bdnav
Definition: mythbdbuffer.h:96
QHash< uint32_t, BLURAY_TITLE_INFO * > m_cachedPlaylistInfo
Definition: mythbdbuffer.h:134
QMutex m_overlayLock
Definition: mythbdbuffer.h:125
long long GetReadPosition(void) const override
int GetCurrentTitle(void)
MythBDOverlay * GetOverlay(void)
int m_currentChapter
Definition: mythbdbuffer.h:112
uint32_t m_numTitles
Definition: mythbdbuffer.h:101
bool IsReadingBlocked(void)
bool m_secondaryAudioEnabled
Definition: mythbdbuffer.h:119
int64_t AdjustTimestamp(int64_t Timestamp) const
void UnblockReading(void)
uint32_t GetNumTitles(void) const
bool StartFromBeginning(void) override
int m_currentSecondaryVideoStream
Definition: mythbdbuffer.h:117
uint32_t GetCurrentChapter(void)
bool IsValidStream(uint StreamId)
~MythBDBuffer() override
uint64_t GetCurrentAngle(void) const
QList< MythBDOverlay * > m_overlayImages
Definition: mythbdbuffer.h:126
QByteArray m_pendingData
Definition: mythbdbuffer.h:131
double GetFrameRate(void)
std::chrono::seconds GetTitleDuration(int Title)
BLURAY_TITLE_INFO * m_currentTitleInfo
Definition: mythbdbuffer.h:104
BD_EVENT m_lastEvent
Definition: mythbdbuffer.h:130
bool SwitchAngle(uint Angle)
int m_currentPlayitem
Definition: mythbdbuffer.h:111
MythBDBuffer(const QString &Filename)
void WaitForPlayer(void)
uint64_t GetNumAngles(void) const
bool GetBDStateSnapshot(QString &State)
Get a snapshot of the current BD state.
void ClearOverlays(void)
QThread * m_mainThread
Definition: mythbdbuffer.h:136
bool OpenFile(const QString &Filename, std::chrono::milliseconds Retry=kDefaultOpenTimeout) override
Opens a bluray device for reading.
QVector< MythBDOverlay * > m_overlayPlanes
Definition: mythbdbuffer.h:127
mpeg::chrono::pts m_currentTitleLength
Definition: mythbdbuffer.h:103
bool m_topMenuSupported
Definition: mythbdbuffer.h:99
void SubmitOverlay(const bd_overlay_s *Overlay)
int GetAudioLanguage(uint StreamID)
std::chrono::seconds m_stillTime
Definition: mythbdbuffer.h:128
mpeg::chrono::pts m_currentTime
Definition: mythbdbuffer.h:107
bool SwitchTitle(uint32_t Index)
bool m_titleChanged
Definition: mythbdbuffer.h:122
static const BLURAY_STREAM_INFO * FindStream(uint StreamID, BLURAY_STREAM_INFO *Streams, int StreamCount)
Find the stream with the given ID from an array of streams.
bool m_isHDMVNavigation
Definition: mythbdbuffer.h:97
long long SeekInternal(long long Position, int Whence) override
uint64_t m_currentTitleAngleCount
Definition: mythbdbuffer.h:106
int m_currentAudioStream
Definition: mythbdbuffer.h:113
int GetSubtitleLanguage(uint StreamID)
bool IsHDMVNavigation(void) const
void SubmitARGBOverlay(const bd_argb_overlay_s *Overlay)
int m_currentIGStream
Definition: mythbdbuffer.h:114
int m_currentSecondaryAudioStream
Definition: mythbdbuffer.h:116
uint32_t GetNumChapters(void)
bool UpdateTitleInfo(void)
BLURAY_TITLE_INFO * GetTitleInfo(uint32_t Index)
bool m_secondaryVideoEnabled
Definition: mythbdbuffer.h:120
bool m_tryHDMVNavigation
Definition: mythbdbuffer.h:98
void PressButton(int32_t Key, mpeg::chrono::pts Pts)
void Close(void)
bool m_firstPlaySupported
Definition: mythbdbuffer.h:100
uint32_t m_mainTitle
Definition: mythbdbuffer.h:102
bool BDWaitingForPlayer(void) const
bool GoToMenu(const QString &Menu, mpeg::chrono::pts Pts)
jump to a Blu-ray root or popup menu
uint64_t GetChapterStartFrame(uint32_t Chapter)
uint64_t GetTitleSize(void) const
std::chrono::seconds GetChapterStartTime(uint32_t Chapter)
bool SwitchPlaylist(uint32_t Index)
bool IsInStillFrame(void) const override
bool GetNameAndSerialNum(QString &Name, QString &SerialNum)
Definition: mythbdinfo.cpp:166
void SetPalette(const BD_PG_PALETTE_ENTRY *Palette)
void Wipe(void)
@ kUnknown
Definition: mythcdrom.h:28
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:188
QString GetSetting(const QString &key, const QString &defaultval="")
int GetNumSetting(const QString &key, int defaultval=0)
MythLocale * GetLocale(void) const
This class is used as a container for messages.
Definition: mythevent.h:17
static const Type kUpdateTvProgressEventType
Definition: mythevent.h:81
QString GetCountryCode() const
Definition: mythlocale.cpp:59
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.
bool IsInMenu(void) const override
MythOpticalState m_processState
Contains listing of PMT Stream ID's for various A/V Stream types.
Definition: mpegtables.h:110
unsigned int uint
Definition: freesurround.h:24
IFSPoint * Buf
Definition: ifs.cpp:105
int iso639_key_to_canonical_key(int iso639_2)
Definition: iso639.cpp:118
ISO 639-1 and ISO 639-2 support functions.
static QString iso639_key_to_str3(int code)
Definition: iso639.h:45
static int iso639_str3_to_key(const unsigned char *iso639_2)
Definition: iso639.h:60
unsigned short uint16_t
Definition: iso6937tables.h:3
bool is_current_thread(MThread *thread)
Use this to determine if you are in the named thread.
Definition: mthread.cpp:40
#define LOC
static void BDLogger(const char *Message)
static void FileOpenedCallback(void *Data)
static int BDRead(void *Handle, void *Buf, int LBA, int NumBlocks)
static void HandleOverlayCallback(void *Data, const bd_overlay_s *const Overlay)
static void HandleARGBOverlayCallback(void *Data, const bd_argb_overlay_s *const Overlay)
static constexpr int32_t BD_BLOCK_SIZE
Definition: mythbdbuffer.h:14
void MythBDIORedirect(void)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetConfDir(void)
Definition: mythdirs.cpp:263
ssize_t MythFileRead(int FileID, void *Buffer, size_t Count)
off_t MythFileSeek(int FileID, off_t Offset, int Whence)
int MythfileClose(int FileID)
void MythFileOpenRegisterCallback(const char *Pathname, void *Object, callback_t Func)
int MythFileOpen(const char *Pathname, int Flags)
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:74
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
static QString seek2string(int Whence)
@ kMythBufferBD
static constexpr const char * ACTION_7
Definition: mythuiactions.h:11
static constexpr const char * ACTION_5
Definition: mythuiactions.h:9
static constexpr const char * ACTION_0
Definition: mythuiactions.h:4
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_3
Definition: mythuiactions.h:7
static constexpr const char * ACTION_1
Definition: mythuiactions.h:5
static constexpr const char * ACTION_4
Definition: mythuiactions.h:8
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
static constexpr const char * ACTION_6
Definition: mythuiactions.h:10
static constexpr const char * ACTION_8
Definition: mythuiactions.h:12
static constexpr const char * ACTION_2
Definition: mythuiactions.h:6
static constexpr const char * ACTION_9
Definition: mythuiactions.h:13
QString formatTime(std::chrono::milliseconds msecs, QString fmt)
Format a milliseconds time value.
Definition: mythdate.cpp:242
QString intToPaddedString(int n, int width=2)
Creates a zero padded string representation of an integer.
Definition: stringutil.h:27
dictionary info
Definition: azlyrics.py:7
std::chrono::duration< CHRONO_TYPE, std::ratio< 1, 90000 > > pts
Definition: mythchrono.h:55
#define ACTION_MENUTEXT
Definition: tv_actions.h:82
#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