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