7 #include <QCoreApplication>
24 #include "libbluray/bluray.h"
32 #include <libbluray/log_control.h>
33 #include <libbluray/meta_data.h>
34 #include <libbluray/overlay.h>
35 #include <libbluray/keys.h>
37 #include "util/log_control.h"
38 #include "libbluray/bdnav/meta_data.h"
39 #include "libbluray/decoders/overlay.h"
40 #include "libbluray/keys.h"
43 #define LOC QString("BDBuffer: ")
68 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(Message).trimmed());
71 static int BDRead(
void *Handle,
void *
Buf,
int LBA,
int NumBlocks)
73 if (
MythFileSeek(*(
static_cast<int*
>(Handle)), LBA * 2048LL, SEEK_SET) != -1)
75 static_cast<size_t>(NumBlocks) * 2048) / 2048);
81 m_tryHDMVNavigation(qEnvironmentVariableIsSet(
"MYTHTV_HDMV")),
82 m_overlayPlanes(2, nullptr),
83 m_mainThread(QThread::currentThread())
100 bd_free_title_info(it.value());
103 bd_free_title_info(it.value());
127 ((Whence == SEEK_SET && Position ==
m_readPos) || (Whence == SEEK_CUR && Position == 0)))
135 long long newposition = (SEEK_SET == Whence) ? Position :
m_readPos + Position;
140 if ((SEEK_END == Whence) || ((SEEK_CUR == Whence) && newposition != 0))
162 QString cmd = QString(
"Seek(%1, %2)").arg(Position)
164 LOG(VB_GENERAL, LOG_ERR,
LOC + cmd +
" Failed" +
ENO);
174 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Seeking to '%1'").arg(Position));
177 return static_cast<uint64_t
>(bd_seek_time(
m_bdnav, Position));
212 else if (Actions.contains(
ACTION_0))
214 else if (Actions.contains(
ACTION_1))
216 else if (Actions.contains(
ACTION_2))
218 else if (Actions.contains(
ACTION_3))
220 else if (Actions.contains(
ACTION_4))
222 else if (Actions.contains(
ACTION_5))
224 else if (Actions.contains(
ACTION_6))
226 else if (Actions.contains(
ACTION_7))
228 else if (Actions.contains(
ACTION_8))
230 else if (Actions.contains(
ACTION_9))
249 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
276 QString
filename = QDir(QDir::cleanPath(Filename)).canonicalPath();
279 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"%1 nonexistent").arg(Filename));
284 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Opened MythBDBuffer device at %1")
289 bd_set_debug_mask(DBG_CRIT | DBG_NAV | DBG_BLURAY);
307 QString keyfile = QString(
"%1/KEYDB.cfg").arg(
GetConfDir());
338 const meta_dl *metaDiscLibrary = bd_get_meta(
m_bdnav);
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));
355 const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(
m_bdnav);
358 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Failed to retrieve disc info");
367 if ((discinfo->aacs_detected && !discinfo->aacs_handled) ||
368 (discinfo->bdplus_detected && !discinfo->bdplus_handled))
384 bd_set_player_setting(
m_bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99);
389 std::string country = QScountry.toLatin1().data();
390 bd_set_player_setting_str(
m_bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref.c_str());
392 bd_set_player_setting_str(
m_bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref.c_str());
394 bd_set_player_setting_str(
m_bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref.c_str());
396 bd_set_player_setting_str(
m_bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country.c_str());
400 bd_set_player_setting(
m_bdnav, BLURAY_PLAYER_SETTING_REGION_CODE, regioncode);
402 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1 as keyfile...").arg(keyfile));
405 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Retrieving title list (please wait).");
413 m_lastError = tr(
"Unable to find any Blu-ray compatible titles");
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)
433 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"BD-J Supported: %1")
434 .arg(discinfo->bdj_handled ?
"yes" :
"no"));
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"));
486 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using HDMV navigation mode.");
495 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using title navigation mode.");
498 uint64_t titleLength = 0;
499 BLURAY_TITLE_INFO *titleInfo =
nullptr;
506 if (titleLength == 0 || (titleInfo->duration > titleLength))
509 titleLength = titleInfo->duration;
519 m_lastError = tr(
"Unable to find any usable Blu-ray titles");
542 return static_cast<long long>(bd_tell(
m_bdnav));
562 return bd_get_current_chapter(
m_bdnav);
572 return duration_cast<std::chrono::milliseconds>(
start);
581 return duration_cast<std::chrono::seconds>(
start);
612 if (numTitles <= 0 || Title < 0 || Title >=
static_cast<int>(numTitles))
645 bd_select_title(
m_bdnav, Index);
655 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"SwitchPlaylist - start");
663 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"SwitchPlaylist - end");
679 BLURAY_TITLE_INFO* result = bd_get_title_info(
m_bdnav, Index, 0);
682 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found title %1 info").arg(Index));
698 BLURAY_TITLE_INFO* result = bd_get_playlist_info(
m_bdnav, Index, 0);
701 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found playlist %1 info").arg(Index));
724 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New title info: Index %1 Playlist: %2 Duration: %3 ""Chapters: %5")
726 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New title info: Clips: %1 Angles: %2 Title Size: %3 Frame Rate %4")
730 for (
uint i = 0; i < chapter_count; i++)
733 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Chapter %1 found @ [%2]->%3")
736 QString::number(framenum)));
739 int still = BLURAY_STILL_NONE;
740 std::chrono::seconds time = 0s;
745 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
746 QString(
"Clip %1 stillmode %2 stilltime %3 videostreams %4 audiostreams %5 igstreams %6")
759 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Warning: more than 1 clip, following still "
760 "frame analysis may be wrong");
763 if (still == BLURAY_STILL_TIME)
765 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Entering still frame (%1 seconds) UNSUPPORTED").arg(time.count()));
768 else if (still == BLURAY_STILL_INFINITE)
770 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Entering infinite still frame.");
790 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switching to Angle '%1'").arg(Angle));
791 bd_seamless_angle_change(
m_bdnav, Angle);
804 return bd_get_title_size(
m_bdnav);
810 int64_t newTimestamp = Timestamp;
811 if ((newTimestamp != AV_NOPTS_VALUE) && (newTimestamp >=
m_timeDiff))
825 result = bd_read_ext(
m_bdnav,
static_cast<unsigned char*
>(
Buffer),
826 static_cast<int>(Size), &event);
841 result = bd_read(
m_bdnav,
static_cast<unsigned char*
>(
Buffer),
static_cast<int>(Size));
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;
899 const BLURAY_STREAM_INFO* stream =
FindStream(
StreamID, clip.audio_streams, clip.audio_stream_count);
902 const uint8_t* lang = stream->lang;
907 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Audio Lang: 0x%1 Code: %2")
919 const BLURAY_STREAM_INFO* stream =
FindStream(
StreamID, clip.pg_streams, clip.pg_stream_count);
922 const uint8_t* lang = stream->lang;
927 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Subtitle Lang: 0x%1 Code: %2")
934 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Key %1 (pts %2)").arg(Key).arg(Pts.count()));
939 bd_user_input(
m_bdnav, Pts.count(),
static_cast<uint32_t
>(Key));
946 if (Pts <= 0 || X == 0 || Y == 0)
948 bd_mouse_select(
m_bdnav, Pts, X, Y);
960 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Top Menu not supported");
964 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"GoToMenu %1").arg(Menu));
966 if (Menu.compare(
"root") == 0)
968 if (bd_menu_call(
m_bdnav, Pts.count()))
970 LOG(VB_PLAYBACK, LOG_INFO,
LOC +QString(
"Invoked Top Menu (pts %1)").arg(Pts.count()));
974 else if (Menu.compare(
"popup") == 0)
1006 switch (
Event.event) {
1009 case BD_EVENT_ERROR:
1010 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_ERROR %1").arg(
Event.param));
1012 case BD_EVENT_ENCRYPTED:
1013 LOG(VB_GENERAL, LOG_ERR,
LOC +
"EVENT_ENCRYPTED, playback will fail.");
1018 case BD_EVENT_ANGLE:
1019 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_ANGLE %1").arg(
Event.param));
1022 case BD_EVENT_TITLE:
1023 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_TITLE %1 (old %2)")
1027 case BD_EVENT_END_OF_TITLE:
1031 case BD_EVENT_PLAYLIST:
1032 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_PLAYLIST %1 (old %2)")
1039 case BD_EVENT_PLAYITEM:
1040 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_PLAYITEM %1").arg(
Event.param));
1046 int64_t diff = in - out;
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));
1061 case BD_EVENT_CHAPTER:
1063 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_CHAPTER %1").arg(
Event.param));
1066 case BD_EVENT_PLAYMARK:
1068 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_PLAYMARK"));
1072 case BD_EVENT_PLAYLIST_STOP:
1074 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"ToDo EVENT_PLAYLIST_STOP %1")
1078 case BD_EVENT_STILL:
1079 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_STILL %1").arg(
Event.param));
1081 case BD_EVENT_STILL_TIME:
1084 std::this_thread::sleep_for(10ms);
1087 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_SEEK"));
1092 case BD_EVENT_AUDIO_STREAM:
1093 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_AUDIO_STREAM %1").arg(
Event.param));
1096 case BD_EVENT_IG_STREAM:
1097 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_IG_STREAM %1").arg(
Event.param));
1100 case BD_EVENT_PG_TEXTST_STREAM:
1101 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_PG_TEXTST_STREAM %1").arg(
Event.param));
1104 case BD_EVENT_SECONDARY_AUDIO_STREAM:
1105 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_SECONDARY_AUDIO_STREAM %1").arg(
Event.param));
1108 case BD_EVENT_SECONDARY_VIDEO_STREAM:
1109 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_SECONDARY_VIDEO_STREAM %1").arg(
Event.param));
1113 case BD_EVENT_PG_TEXTST:
1114 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_PG_TEXTST %1")
1115 .arg(
Event.param ?
"enable" :
"disable"));
1118 case BD_EVENT_SECONDARY_AUDIO:
1119 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_SECONDARY_AUDIO %1")
1120 .arg(
Event.param ?
"enable" :
"disable"));
1123 case BD_EVENT_SECONDARY_VIDEO:
1124 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_SECONDARY_VIDEO %1")
1125 .arg(
Event.param ?
"enable" :
"disable"));
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"));
1138 std::this_thread::sleep_for(40ms);
1143 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"EVENT_MENU %1")
1144 .arg(
Event.param==0 ?
"no" :
"yes"));
1148 case BD_EVENT_KEY_INTEREST_TABLE:
1150 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"ToDo EVENT_KEY_INTEREST_TABLE %1")
1154 case BD_EVENT_UO_MASK_CHANGED:
1156 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"ToDo EVENT_UO_MASK_CHANGED %1")
1161 LOG(VB_PLAYBACK, LOG_ERR,
LOC + QString(
"Unknown Event! %1 %2")
1180 BLURAY_STREAM_INFO* Streams,
1183 const BLURAY_STREAM_INFO* stream =
nullptr;
1184 for (
int i = 0; i < StreamCount && !stream; i++)
1186 stream = &Streams[i];
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))
1229 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Waiting for player's buffers to drain");
1233 std::this_thread::sleep_for(10ms);
1236 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Player wait state was not cleared");
1250 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Starting from beginning...");
1262 return !SerialNum.isEmpty();
1273 State = QString(
"title:%1,time:%2,angle:%3").arg(title).arg(time.count()).arg(angle);
1276 return !
State.isEmpty();
1283 QStringList states =
State.split(
",", Qt::SkipEmptyParts);
1284 QHash<QString, uint64_t> settings;
1286 for (
const QString& state : std::as_const(states))
1288 QStringList keyvalue = state.split(
":", Qt::SkipEmptyParts);
1289 if (keyvalue.length() != 2)
1291 LOG(VB_PLAYBACK, LOG_ERR,
LOC + QString(
"Invalid BD state: %1 (%2)")
1292 .arg(state,
State));
1296 settings[keyvalue[0]] = keyvalue[1].toULongLong();
1301 if (settings.contains(
"title") && settings.contains(
"time"))
1303 uint32_t title =
static_cast<uint32_t
>(settings[
"title"]);
1304 uint64_t time = settings[
"time"];
1307 if (settings.contains(
"angle"))
1308 angle = settings[
"angle"];
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"));
1371 switch(Overlay->cmd)
1373 case BD_OVERLAY_INIT:
1379 case BD_OVERLAY_CLOSE:
1394 case BD_OVERLAY_HIDE:
1395 case BD_OVERLAY_CLEAR:
1399 case BD_OVERLAY_WIPE:
1401 osd->
Wipe(Overlay->x, Overlay->y, Overlay->w, Overlay->h);
1403 case BD_OVERLAY_DRAW:
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++)
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);
1420 case BD_OVERLAY_FLUSH:
1424 newOverlay->m_image = osd->
m_image.convertToFormat(QImage::Format_ARGB32);
1425 newOverlay->m_pts = Overlay->pts;
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));
1447 switch(Overlay->cmd)
1449 case BD_ARGB_OVERLAY_INIT:
1454 case BD_ARGB_OVERLAY_CLOSE:
1469 case BD_ARGB_OVERLAY_DRAW:
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++)
1478 memcpy(&data[dstOffset], &Overlay->argb[srcOffset], Overlay->w * 4_UZ);
1479 dstOffset += osd->
m_image.bytesPerLine();
1480 srcOffset += Overlay->stride;
1484 case BD_ARGB_OVERLAY_FLUSH:
1490 newOverlay->m_pts = Overlay->pts;
1495 LOG(VB_PLAYBACK, LOG_ERR, QString(
"Unknown ARGB overlay - %1").arg(Overlay->cmd));