00001 #include <QImage>
00002 #include <QDir>
00003 #include <QCoreApplication>
00004
00005 #include "bdnav/mpls_parse.h"
00006 #include "bdnav/meta_parse.h"
00007 #include "bdnav/navigation.h"
00008 #include "bdnav/bdparse.h"
00009 #include "decoders/overlay.h"
00010
00011 #include "mythmainwindow.h"
00012 #include "mythevent.h"
00013 #include "iso639.h"
00014 #include "bdringbuffer.h"
00015 #include "mythlogging.h"
00016 #include "mythcorecontext.h"
00017 #include "mythlocale.h"
00018 #include "mythdirs.h"
00019 #include "bluray.h"
00020 #include "tv.h"
00021 #include "mythiowrapper.h"
00022
00023 #define LOC QString("BDRingBuf: ")
00024
00025 static void HandleOverlayCallback(void *data, const bd_overlay_s *const overlay)
00026 {
00027 BDRingBuffer *bdrb = (BDRingBuffer*) data;
00028 if (bdrb)
00029 bdrb->SubmitOverlay(overlay);
00030 }
00031
00032 static void file_opened_callback(void* bdr)
00033 {
00034 BDRingBuffer *obj = (BDRingBuffer*)bdr;
00035 if (obj)
00036 obj->ProgressUpdate();
00037 }
00038
00039 BDRingBuffer::BDRingBuffer(const QString &lfilename)
00040 : RingBuffer(kRingBuffer_BD),
00041 bdnav(NULL), m_isHDMVNavigation(false), m_tryHDMVNavigation(false),
00042 m_topMenuSupported(false), m_firstPlaySupported(false),
00043 m_numTitles(0), m_titleChanged(false), m_playerWait(false),
00044 m_ignorePlayerWait(true),
00045 m_stillTime(0), m_stillMode(BLURAY_STILL_NONE),
00046 m_infoLock(QMutex::Recursive), m_mainThread(NULL)
00047 {
00048 m_tryHDMVNavigation = NULL != getenv("MYTHTV_HDMV");
00049 m_mainThread = QThread::currentThread();
00050 OpenFile(lfilename);
00051 }
00052
00053 BDRingBuffer::~BDRingBuffer()
00054 {
00055 close();
00056 }
00057
00058 void BDRingBuffer::close(void)
00059 {
00060 if (bdnav)
00061 {
00062 m_infoLock.lock();
00063 QHash<uint32_t, BLURAY_TITLE_INFO*>::iterator it;
00064
00065 for (it = m_cachedTitleInfo.begin(); it !=m_cachedTitleInfo.end(); ++it)
00066 bd_free_title_info(it.value());
00067 m_cachedTitleInfo.clear();
00068
00069 for (it = m_cachedPlaylistInfo.begin(); it !=m_cachedPlaylistInfo.end(); ++it)
00070 bd_free_title_info(it.value());
00071 m_cachedPlaylistInfo.clear();
00072 m_infoLock.unlock();
00073
00074 bd_close(bdnav);
00075 bdnav = NULL;
00076 }
00077
00078 ClearOverlays();
00079 }
00080
00081 long long BDRingBuffer::Seek(long long pos, int whence, bool has_lock)
00082 {
00083 LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(%1,%2,%3)")
00084 .arg(pos).arg((whence == SEEK_SET) ? "SEEK_SET" :
00085 ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"))
00086 .arg(has_lock ? "locked" : "unlocked"));
00087
00088 long long ret = -1;
00089
00090
00091
00092 if (!has_lock)
00093 rwlock.lockForWrite();
00094
00095 poslock.lockForWrite();
00096
00097
00098 if (readaheadrunning &&
00099 ((whence == SEEK_SET && pos == readpos) ||
00100 (whence == SEEK_CUR && pos == 0)))
00101 {
00102 ret = readpos;
00103
00104 poslock.unlock();
00105 if (!has_lock)
00106 rwlock.unlock();
00107
00108 return ret;
00109 }
00110
00111
00112 long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
00113
00114
00115
00116
00117 if ((SEEK_END == whence) ||
00118 ((SEEK_CUR == whence) && new_pos != 0))
00119 {
00120 errno = EINVAL;
00121 ret = -1;
00122 }
00123 else
00124 {
00125 Seek(new_pos);
00126 ret = new_pos;
00127 }
00128
00129 if (ret >= 0)
00130 {
00131 readpos = ret;
00132
00133 ignorereadpos = -1;
00134
00135 if (readaheadrunning)
00136 ResetReadAhead(readpos);
00137
00138 readAdjust = 0;
00139 }
00140 else
00141 {
00142 QString cmd = QString("Seek(%1, %2)").arg(pos)
00143 .arg((whence == SEEK_SET) ? "SEEK_SET" :
00144 ((whence == SEEK_CUR) ?"SEEK_CUR" : "SEEK_END"));
00145 LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
00146 }
00147
00148 poslock.unlock();
00149
00150 generalWait.wakeAll();
00151
00152 if (!has_lock)
00153 rwlock.unlock();
00154
00155 return ret;
00156 }
00157
00158 uint64_t BDRingBuffer::Seek(uint64_t pos)
00159 {
00160 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Seeking to %1.").arg(pos));
00161 if (bdnav)
00162 return bd_seek_time(bdnav, pos);
00163 return 0;
00164 }
00165
00166 void BDRingBuffer::GetDescForPos(QString &desc)
00167 {
00168 if (!m_infoLock.tryLock())
00169 return;
00170 desc = QObject::tr("Title %1 chapter %2")
00171 .arg(m_currentTitleInfo->idx)
00172 .arg(m_currentTitleInfo->chapters->idx);
00173 m_infoLock.unlock();
00174 }
00175
00176 bool BDRingBuffer::HandleAction(const QStringList &actions, int64_t pts)
00177 {
00178 if (!m_isHDMVNavigation)
00179 return false;
00180
00181 if (actions.contains(ACTION_MENUTEXT))
00182 {
00183 PressButton(BD_VK_POPUP, pts);
00184 return true;
00185 }
00186
00187 if (!IsInMenu())
00188 return false;
00189
00190 bool handled = true;
00191 if (actions.contains(ACTION_UP) ||
00192 actions.contains(ACTION_CHANNELUP))
00193 {
00194 PressButton(BD_VK_UP, pts);
00195 }
00196 else if (actions.contains(ACTION_DOWN) ||
00197 actions.contains(ACTION_CHANNELDOWN))
00198 {
00199 PressButton(BD_VK_DOWN, pts);
00200 }
00201 else if (actions.contains(ACTION_LEFT) ||
00202 actions.contains(ACTION_SEEKRWND))
00203 {
00204 PressButton(BD_VK_LEFT, pts);
00205 }
00206 else if (actions.contains(ACTION_RIGHT) ||
00207 actions.contains(ACTION_SEEKFFWD))
00208 {
00209 PressButton(BD_VK_RIGHT, pts);
00210 }
00211 else if (actions.contains(ACTION_0))
00212 {
00213 PressButton(BD_VK_0, pts);
00214 }
00215 else if (actions.contains(ACTION_1))
00216 {
00217 PressButton(BD_VK_1, pts);
00218 }
00219 else if (actions.contains(ACTION_2))
00220 {
00221 PressButton(BD_VK_2, pts);
00222 }
00223 else if (actions.contains(ACTION_3))
00224 {
00225 PressButton(BD_VK_3, pts);
00226 }
00227 else if (actions.contains(ACTION_4))
00228 {
00229 PressButton(BD_VK_4, pts);
00230 }
00231 else if (actions.contains(ACTION_5))
00232 {
00233 PressButton(BD_VK_5, pts);
00234 }
00235 else if (actions.contains(ACTION_6))
00236 {
00237 PressButton(BD_VK_6, pts);
00238 }
00239 else if (actions.contains(ACTION_7))
00240 {
00241 PressButton(BD_VK_7, pts);
00242 }
00243 else if (actions.contains(ACTION_8))
00244 {
00245 PressButton(BD_VK_8, pts);
00246 }
00247 else if (actions.contains(ACTION_9))
00248 {
00249 PressButton(BD_VK_9, pts);
00250 }
00251 else if (actions.contains(ACTION_SELECT))
00252 {
00253 PressButton(BD_VK_ENTER, pts);
00254 }
00255 else
00256 handled = false;
00257
00258 return handled;
00259 }
00260
00261 void BDRingBuffer::ProgressUpdate(void)
00262 {
00263
00264
00265 if (!is_current_thread(m_mainThread))
00266 return;
00267
00268 qApp->postEvent(GetMythMainWindow(),
00269 new MythEvent(MythEvent::kUpdateTvProgressEventType));
00270 qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
00271 }
00272
00273 bool BDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
00274 {
00275 safefilename = lfilename;
00276 filename = lfilename;
00277
00278
00279 QString filename = QDir(QDir::cleanPath(lfilename)).canonicalPath();
00280 if (filename.isEmpty())
00281 {
00282 LOG(VB_GENERAL, LOG_ERR, LOC +
00283 QString("%1 nonexistent").arg(lfilename));
00284 filename = lfilename;
00285 }
00286 safefilename = filename;
00287
00288 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened BDRingBuffer device at %1")
00289 .arg(filename));
00290
00291
00292
00293
00294
00295
00296 mythfile_open_register_callback(filename.toLocal8Bit().data(), this,
00297 file_opened_callback);
00298
00299 QMutexLocker locker(&m_infoLock);
00300 rwlock.lockForWrite();
00301
00302 if (bdnav)
00303 close();
00304
00305 QString keyfile = QString("%1/KEYDB.cfg").arg(GetConfDir());
00306 QByteArray keyarray = keyfile.toAscii();
00307 const char *keyfilepath = keyarray.data();
00308
00309 bdnav = bd_open(filename.toLocal8Bit().data(), keyfilepath);
00310
00311 if (!bdnav)
00312 {
00313 rwlock.unlock();
00314 mythfile_open_register_callback(filename.toLocal8Bit().data(), this, NULL);
00315 return false;
00316 }
00317
00318 m_metaDiscLibrary = bd_get_meta(bdnav);
00319
00320 if (m_metaDiscLibrary)
00321 {
00322 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Title: %1 (%2)")
00323 .arg(m_metaDiscLibrary->di_name)
00324 .arg(m_metaDiscLibrary->language_code));
00325 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Alternative Title: %1")
00326 .arg(m_metaDiscLibrary->di_alternative));
00327 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Disc Number: %1 of %2")
00328 .arg(m_metaDiscLibrary->di_set_number)
00329 .arg(m_metaDiscLibrary->di_num_sets));
00330 }
00331
00332
00333 m_topMenuSupported = false;
00334 m_firstPlaySupported = false;
00335 const BLURAY_DISC_INFO *discinfo = bd_get_disc_info(bdnav);
00336 if (discinfo)
00337 {
00338 m_topMenuSupported = discinfo->top_menu_supported;
00339 m_firstPlaySupported = discinfo->first_play_supported;
00340
00341 LOG(VB_PLAYBACK, LOG_INFO, LOC + "*** Blu-ray Disc Information ***");
00342 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("First Play Supported: %1")
00343 .arg(discinfo->first_play_supported ? "yes" : "no"));
00344 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Top Menu Supported: %1")
00345 .arg(discinfo->top_menu_supported ? "yes" : "no"));
00346 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of HDMV Titles: %1")
00347 .arg(discinfo->num_hdmv_titles));
00348 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Number of BD-J Titles: %1")
00349 .arg(discinfo->num_bdj_titles));
00350 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00351 QString("Number of Unsupported Titles: %1")
00352 .arg(discinfo->num_unsupported_titles));
00353 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS present on disc: %1")
00354 .arg(discinfo->aacs_detected ? "yes" : "no"));
00355 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libaacs used: %1")
00356 .arg(discinfo->libaacs_detected ? "yes" : "no"));
00357 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("AACS handled: %1")
00358 .arg(discinfo->aacs_handled ? "yes" : "no"));
00359 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ present on disc: %1")
00360 .arg(discinfo->bdplus_detected ? "yes" : "no"));
00361 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("libbdplus used: %1")
00362 .arg(discinfo->libbdplus_detected ? "yes" : "no"));
00363 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("BD+ handled: %1")
00364 .arg(discinfo->bdplus_handled ? "yes" : "no"));
00365 }
00366
00367
00368
00369
00370
00371
00372 bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99);
00373
00374
00375 const char *langpref = gCoreContext->GetSetting(
00376 "ISO639Language0", "eng").toLatin1().data();
00377 QString QScountry = gCoreContext->GetLocale()->GetCountryCode().toLower();
00378 const char *country = QScountry.toLatin1().data();
00379 bd_set_player_setting_str(
00380 bdnav, BLURAY_PLAYER_SETTING_AUDIO_LANG, langpref);
00381
00382
00383 bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref);
00384
00385
00386 bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref);
00387
00388
00389 bd_set_player_setting_str(
00390 bdnav, BLURAY_PLAYER_SETTING_COUNTRY_CODE, country);
00391
00392 int regioncode = 0;
00393 regioncode = gCoreContext->GetNumSetting("BlurayRegionCode");
00394 if (regioncode > 0)
00395 bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_REGION_CODE,
00396 regioncode);
00397
00398 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1 as keyfile...")
00399 .arg(QString(keyfilepath)));
00400
00401
00402 LOG(VB_GENERAL, LOG_INFO, LOC + "Retrieving title list (please wait).");
00403 m_numTitles = bd_get_titles(bdnav, TITLES_RELEVANT, 30);
00404 LOG(VB_GENERAL, LOG_INFO, LOC +
00405 QString("Found %1 titles.").arg(m_numTitles));
00406 m_mainTitle = 0;
00407 m_currentTitleLength = 0;
00408 m_titlesize = 0;
00409 m_currentTime = 0;
00410 m_currentTitleInfo = NULL;
00411 m_currentTitleAngleCount = 0;
00412
00413
00414 m_currentAngle = 0;
00415 m_currentTitle = 0;
00416 m_currentPlaylist = 0;
00417 m_currentPlayitem = 0;
00418 m_currentChapter = 0;
00419 m_currentAudioStream = 0;
00420 m_currentIGStream = 0;
00421 m_currentPGTextSTStream = 0;
00422 m_currentSecondaryAudioStream = 0;
00423 m_currentSecondaryVideoStream = 0;
00424 m_PGTextSTEnabled = false;
00425 m_secondaryAudioEnabled = false;
00426 m_secondaryVideoEnabled = false;
00427 m_secondaryVideoIsFullscreen = false;
00428 m_stillMode = BLURAY_STILL_NONE;
00429 m_stillTime = 0;
00430 m_inMenu = false;
00431
00432
00433
00434
00435 if (m_tryHDMVNavigation && m_firstPlaySupported && bd_play(bdnav))
00436 {
00437 LOG(VB_GENERAL, LOG_INFO, LOC + "Using HDMV navigation mode.");
00438 m_isHDMVNavigation = true;
00439
00440
00441 bd_register_overlay_proc(bdnav, this, HandleOverlayCallback);
00442 }
00443 else
00444 {
00445 LOG(VB_GENERAL, LOG_INFO, LOC + "Using title navigation mode.");
00446
00447
00448 uint64_t titleLength = 0;
00449 BLURAY_TITLE_INFO *titleInfo = NULL;
00450 for( unsigned i = 0; i < m_numTitles; ++i)
00451 {
00452 titleInfo = GetTitleInfo(i);
00453 if (titleLength == 0 ||
00454 (titleInfo->duration > titleLength))
00455 {
00456 m_mainTitle = titleInfo->idx;
00457 titleLength = titleInfo->duration;
00458 }
00459 }
00460
00461 SwitchTitle(m_mainTitle);
00462 }
00463
00464 readblocksize = BD_BLOCK_SIZE * 62;
00465 setswitchtonext = false;
00466 ateof = false;
00467 commserror = false;
00468 numfailures = 0;
00469 rawbitrate = 8000;
00470 CalcReadAheadThresh();
00471
00472 rwlock.unlock();
00473
00474 mythfile_open_register_callback(filename.toLocal8Bit().data(), this, NULL);
00475 return true;
00476 }
00477
00478 long long BDRingBuffer::GetReadPosition(void) const
00479 {
00480 if (bdnav)
00481 return bd_tell(bdnav);
00482 return 0;
00483 }
00484
00485 uint32_t BDRingBuffer::GetNumChapters(void)
00486 {
00487 QMutexLocker locker(&m_infoLock);
00488 if (m_currentTitleInfo)
00489 return m_currentTitleInfo->chapter_count - 1;
00490 return 0;
00491 }
00492
00493 uint32_t BDRingBuffer::GetCurrentChapter(void)
00494 {
00495 if (bdnav)
00496 return bd_get_current_chapter(bdnav);
00497 return 0;
00498 }
00499
00500 uint64_t BDRingBuffer::GetChapterStartTime(uint32_t chapter)
00501 {
00502 if (chapter < 0 || chapter >= GetNumChapters())
00503 return 0;
00504 QMutexLocker locker(&m_infoLock);
00505 return (uint64_t)((long double)m_currentTitleInfo->chapters[chapter].start /
00506 90000.0f);
00507 }
00508
00509 uint64_t BDRingBuffer::GetChapterStartFrame(uint32_t chapter)
00510 {
00511 if (chapter < 0 || chapter >= GetNumChapters())
00512 return 0;
00513 QMutexLocker locker(&m_infoLock);
00514 return (uint64_t)((long double)(m_currentTitleInfo->chapters[chapter].start *
00515 GetFrameRate()) / 90000.0f);
00516 }
00517
00518 int BDRingBuffer::GetCurrentTitle(void)
00519 {
00520 QMutexLocker locker(&m_infoLock);
00521 if (m_currentTitleInfo)
00522 return m_currentTitleInfo->idx;
00523 return -1;
00524 }
00525
00526 int BDRingBuffer::GetTitleDuration(int title)
00527 {
00528 QMutexLocker locker(&m_infoLock);
00529 int numTitles = GetNumTitles();
00530
00531 if (!(numTitles > 0 && title >= 0 && title < numTitles))
00532 return 0;
00533
00534 BLURAY_TITLE_INFO *info = GetTitleInfo(title);
00535 if (!info)
00536 return 0;
00537
00538 int duration = ((info->duration) / 90000.0f);
00539 return duration;
00540 }
00541
00542 bool BDRingBuffer::SwitchTitle(uint32_t index)
00543 {
00544 if (!bdnav)
00545 return false;
00546
00547 m_infoLock.lock();
00548 m_currentTitleInfo = GetTitleInfo(index);
00549 m_infoLock.unlock();
00550 bd_select_title(bdnav, index);
00551
00552 return UpdateTitleInfo();
00553 }
00554
00555 bool BDRingBuffer::SwitchPlaylist(uint32_t index)
00556 {
00557 if (!bdnav)
00558 return false;
00559
00560 LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - start");
00561
00562 m_infoLock.lock();
00563 m_currentTitleInfo = GetPlaylistInfo(index);
00564 m_infoLock.unlock();
00565 bool result = UpdateTitleInfo();
00566
00567 LOG(VB_PLAYBACK, LOG_INFO, LOC + "SwitchPlaylist - end");
00568 return result;
00569 }
00570
00571 BLURAY_TITLE_INFO* BDRingBuffer::GetTitleInfo(uint32_t index)
00572 {
00573 if (!bdnav)
00574 return NULL;
00575
00576 QMutexLocker locker(&m_infoLock);
00577 if (m_cachedTitleInfo.contains(index))
00578 return m_cachedTitleInfo.value(index);
00579
00580 if (index > m_numTitles)
00581 return NULL;
00582
00583 BLURAY_TITLE_INFO* result = bd_get_title_info(bdnav, index, 0);
00584 if (result)
00585 {
00586 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00587 QString("Found title %1 info").arg(index));
00588 m_cachedTitleInfo.insert(index,result);
00589 return result;
00590 }
00591 return NULL;
00592 }
00593
00594 BLURAY_TITLE_INFO* BDRingBuffer::GetPlaylistInfo(uint32_t index)
00595 {
00596 if (!bdnav)
00597 return NULL;
00598
00599 QMutexLocker locker(&m_infoLock);
00600 if (m_cachedPlaylistInfo.contains(index))
00601 return m_cachedPlaylistInfo.value(index);
00602
00603 BLURAY_TITLE_INFO* result = bd_get_playlist_info(bdnav, index, 0);
00604 if (result)
00605 {
00606 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00607 QString("Found playlist %1 info").arg(index));
00608 m_cachedPlaylistInfo.insert(index,result);
00609 return result;
00610 }
00611 return NULL;
00612 }
00613
00614 bool BDRingBuffer::UpdateTitleInfo(void)
00615 {
00616 QMutexLocker locker(&m_infoLock);
00617 if (!m_currentTitleInfo)
00618 return false;
00619
00620 m_titleChanged = true;
00621 m_currentTitleLength = m_currentTitleInfo->duration;
00622 m_currentTitleAngleCount = m_currentTitleInfo->angle_count;
00623 m_currentAngle = 0;
00624 m_titlesize = bd_get_title_size(bdnav);
00625 uint32_t chapter_count = GetNumChapters();
00626 uint64_t total_secs = m_currentTitleLength / 90000;
00627 int hours = (int)total_secs / 60 / 60;
00628 int minutes = ((int)total_secs / 60) - (hours * 60);
00629 double secs = (double)total_secs - (double)(hours * 60 * 60 + minutes * 60);
00630 QString duration = QString("%1:%2:%3")
00631 .arg(QString().sprintf("%02d", hours))
00632 .arg(QString().sprintf("%02d", minutes))
00633 .arg(QString().sprintf("%02.1f", secs));
00634 LOG(VB_GENERAL, LOG_INFO, LOC +
00635 QString("New title info: Index %1 Playlist: %2 Duration: %3 "
00636 "Chapters: %5")
00637 .arg(m_currentTitleInfo->idx).arg(m_currentTitleInfo->playlist)
00638 .arg(duration).arg(chapter_count));
00639 LOG(VB_GENERAL, LOG_INFO, LOC +
00640 QString("New title info: Clips: %1 Angles: %2 Title Size: %3 "
00641 "Frame Rate %4")
00642 .arg(m_currentTitleInfo->clip_count)
00643 .arg(m_currentTitleAngleCount).arg(m_titlesize)
00644 .arg(GetFrameRate()));
00645
00646 if (chapter_count)
00647 {
00648 for (uint i = 0; i < chapter_count; i++)
00649 {
00650 uint64_t total_secs = GetChapterStartTime(i);
00651 uint64_t framenum = GetChapterStartFrame(i);
00652 int hours = (int)total_secs / 60 / 60;
00653 int minutes = ((int)total_secs / 60) - (hours * 60);
00654 double secs = (double)total_secs -
00655 (double)(hours * 60 * 60 + minutes * 60);
00656 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00657 QString("Chapter %1 found @ [%2:%3:%4]->%5")
00658 .arg(QString().sprintf("%02d", i + 1))
00659 .arg(QString().sprintf("%02d", hours))
00660 .arg(QString().sprintf("%02d", minutes))
00661 .arg(QString().sprintf("%06.3f", secs))
00662 .arg(framenum));
00663 }
00664 }
00665
00666 int still = BLURAY_STILL_NONE;
00667 int time = 0;
00668 if (m_currentTitleInfo->clip_count)
00669 {
00670 for (uint i = 0; i < m_currentTitleInfo->clip_count; i++)
00671 {
00672 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00673 QString("Clip %1 stillmode %2 stilltime %3 videostreams %4 "
00674 "audiostreams %5 igstreams %6")
00675 .arg(i).arg(m_currentTitleInfo->clips[i].still_mode)
00676 .arg(m_currentTitleInfo->clips[i].still_time)
00677 .arg(m_currentTitleInfo->clips[i].video_stream_count)
00678 .arg(m_currentTitleInfo->clips[i].audio_stream_count)
00679 .arg(m_currentTitleInfo->clips[i].ig_stream_count));
00680 still |= m_currentTitleInfo->clips[i].still_mode;
00681 time = m_currentTitleInfo->clips[i].still_time;
00682 }
00683 }
00684
00685 if (m_currentTitleInfo->clip_count > 1 && still != BLURAY_STILL_NONE)
00686 LOG(VB_GENERAL, LOG_WARNING, LOC +
00687 "Warning: more than 1 clip, following still "
00688 "frame analysis may be wrong");
00689
00690 if (still == BLURAY_STILL_TIME)
00691 {
00692 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00693 QString("Entering still frame (%1 seconds) UNSUPPORTED").arg(time));
00694 bd_read_skip_still(bdnav);
00695 }
00696 else if (still == BLURAY_STILL_INFINITE)
00697 {
00698 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Entering infinite still frame.");
00699 }
00700
00701 m_stillMode = still;
00702 m_stillTime = time;
00703
00704 return true;
00705 }
00706
00707 bool BDRingBuffer::TitleChanged(void)
00708 {
00709 bool ret = m_titleChanged;
00710 m_titleChanged = false;
00711 return ret;
00712 }
00713
00714 bool BDRingBuffer::SwitchAngle(uint angle)
00715 {
00716 if (!bdnav)
00717 return false;
00718
00719 LOG(VB_GENERAL, LOG_INFO, LOC +
00720 QString("Switching to Angle %1...").arg(angle));
00721 bd_seamless_angle_change(bdnav, angle);
00722 m_currentAngle = angle;
00723 return true;
00724 }
00725
00726 uint64_t BDRingBuffer::GetTotalReadPosition(void)
00727 {
00728 if (bdnav)
00729 return bd_get_title_size(bdnav);
00730 return 0;
00731 }
00732
00733 int BDRingBuffer::safe_read(void *data, uint sz)
00734 {
00735 int result = 0;
00736 if (m_isHDMVNavigation)
00737 {
00738 HandleBDEvents();
00739 while (result == 0)
00740 {
00741 BD_EVENT event;
00742 result = bd_read_ext(bdnav,
00743 (unsigned char *)data,
00744 sz, &event);
00745 HandleBDEvent(event);
00746 if (result == 0)
00747 HandleBDEvents();
00748 }
00749 }
00750 else
00751 {
00752 result = bd_read(bdnav, (unsigned char *)data, sz);
00753 }
00754
00755 m_currentTime = bd_tell(bdnav);
00756 return result;
00757 }
00758
00759 double BDRingBuffer::GetFrameRate(void)
00760 {
00761 QMutexLocker locker(&m_infoLock);
00762 if (bdnav && m_currentTitleInfo)
00763 {
00764 uint8_t rate = m_currentTitleInfo->clips->video_streams->rate;
00765 switch (rate)
00766 {
00767 case BD_VIDEO_RATE_24000_1001:
00768 return 23.97;
00769 break;
00770 case BD_VIDEO_RATE_24:
00771 return 24;
00772 break;
00773 case BD_VIDEO_RATE_25:
00774 return 25;
00775 break;
00776 case BD_VIDEO_RATE_30000_1001:
00777 return 29.97;
00778 break;
00779 case BD_VIDEO_RATE_50:
00780 return 50;
00781 break;
00782 case BD_VIDEO_RATE_60000_1001:
00783 return 59.94;
00784 break;
00785 default:
00786 return 0;
00787 break;
00788 }
00789 }
00790 return 0;
00791 }
00792
00793 int BDRingBuffer::GetAudioLanguage(uint streamID)
00794 {
00795 QMutexLocker locker(&m_infoLock);
00796 if (!m_currentTitleInfo ||
00797 streamID >= m_currentTitleInfo->clips->audio_stream_count)
00798 return iso639_str3_to_key("und");
00799
00800 uint8_t lang[4] = { 0, 0, 0, 0 };
00801 memcpy(lang, m_currentTitleInfo->clips->audio_streams[streamID].lang, 4);
00802 int code = iso639_key_to_canonical_key((lang[0]<<16)|(lang[1]<<8)|lang[2]);
00803
00804 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Audio Lang: %1 Code: %2")
00805 .arg(code).arg(iso639_key_to_str3(code)));
00806
00807 return code;
00808 }
00809
00810 int BDRingBuffer::GetSubtitleLanguage(uint streamID)
00811 {
00812 QMutexLocker locker(&m_infoLock);
00813 if (!m_currentTitleInfo)
00814 return iso639_str3_to_key("und");
00815
00816 int pgCount = m_currentTitleInfo->clips->pg_stream_count;
00817 uint subCount = 0;
00818 for (int i = 0; i < pgCount; ++i)
00819 {
00820 if (m_currentTitleInfo->clips->pg_streams[i].coding_type >= 0x90 &&
00821 m_currentTitleInfo->clips->pg_streams[i].coding_type <= 0x92)
00822 {
00823 if (streamID == subCount)
00824 {
00825 uint8_t lang[4] = { 0, 0, 0, 0 };
00826 memcpy(lang,
00827 m_currentTitleInfo->clips->pg_streams[streamID].lang, 4);
00828 int key = (lang[0]<<16)|(lang[1]<<8)|lang[2];
00829 int code = iso639_key_to_canonical_key(key);
00830 LOG(VB_GENERAL, LOG_INFO, LOC +
00831 QString("Subtitle Lang: %1 Code: %2")
00832 .arg(code).arg(iso639_key_to_str3(code)));
00833 return code;
00834 }
00835 subCount++;
00836 }
00837 }
00838 return iso639_str3_to_key("und");
00839 }
00840
00841 void BDRingBuffer::PressButton(int32_t key, int64_t pts)
00842 {
00843 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00844 QString("Key %1 (pts %2)").arg(key).arg(pts));
00845
00846 pts = 1;
00847
00848 if (!bdnav || pts <= 0 || key < 0)
00849 return;
00850
00851 bd_user_input(bdnav, pts, key);
00852 }
00853
00854 void BDRingBuffer::ClickButton(int64_t pts, uint16_t x, uint16_t y)
00855 {
00856 if (!bdnav)
00857 return;
00858
00859 if (pts <= 0 || x <= 0 || y <= 0)
00860 return;
00861
00862 bd_mouse_select(bdnav, pts, x, y);
00863 }
00864
00867 bool BDRingBuffer::GoToMenu(const QString str, int64_t pts)
00868 {
00869 if (!m_isHDMVNavigation || pts < 0)
00870 return false;
00871
00872 if (!m_topMenuSupported)
00873 {
00874 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Top Menu not supported");
00875 return false;
00876 }
00877
00878 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("GoToMenu %1").arg(str));
00879
00880 if (str.compare("root") == 0)
00881 {
00882 if (bd_menu_call(bdnav, pts))
00883 {
00884 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00885 QString("Invoked Top Menu (pts %1)").arg(pts));
00886 return true;
00887 }
00888 }
00889 else if (str.compare("popup") == 0)
00890 {
00891 PressButton(BD_VK_POPUP, pts);
00892 return true;
00893 }
00894 else
00895 return false;
00896
00897 return false;
00898 }
00899
00900 bool BDRingBuffer::HandleBDEvents(void)
00901 {
00902 BD_EVENT ev;
00903 while (bd_get_event(bdnav, &ev))
00904 {
00905 HandleBDEvent(ev);
00906 if (ev.event == BD_EVENT_NONE ||
00907 ev.event == BD_EVENT_ERROR)
00908 {
00909 return false;
00910 }
00911 }
00912 return true;
00913 }
00914
00915 void BDRingBuffer::HandleBDEvent(BD_EVENT &ev)
00916 {
00917 switch (ev.event) {
00918 case BD_EVENT_NONE:
00919 break;
00920 case BD_EVENT_ERROR:
00921 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00922 QString("EVENT_ERROR %1").arg(ev.param));
00923 break;
00924 case BD_EVENT_ENCRYPTED:
00925 LOG(VB_GENERAL, LOG_ERR, LOC +
00926 "EVENT_ENCRYPTED, playback will fail.");
00927 break;
00928
00929
00930
00931 case BD_EVENT_ANGLE:
00932 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00933 QString("EVENT_ANGLE %1").arg(ev.param));
00934 m_currentAngle = ev.param;
00935 break;
00936 case BD_EVENT_TITLE:
00937 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00938 QString("EVENT_TITLE %1 (old %2)")
00939 .arg(ev.param).arg(m_currentTitle));
00940 m_currentTitle = ev.param;
00941 break;
00942 case BD_EVENT_END_OF_TITLE:
00943 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00944 QString("EVENT_END_OF_TITLE %1").arg(m_currentTitle));
00945 WaitForPlayer();
00946 break;
00947 case BD_EVENT_PLAYLIST:
00948 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00949 QString("EVENT_PLAYLIST %1 (old %2)")
00950 .arg(ev.param).arg(m_currentPlaylist));
00951 m_currentPlaylist = ev.param;
00952 m_currentTitle = bd_get_current_title(bdnav);
00953 SwitchPlaylist(m_currentPlaylist);
00954 break;
00955 case BD_EVENT_PLAYITEM:
00956 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00957 QString("EVENT_PLAYITEM %1").arg(ev.param));
00958 m_currentPlayitem = ev.param;
00959 break;
00960 case BD_EVENT_CHAPTER:
00961
00962 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_CHAPTER %1")
00963 .arg(ev.param));
00964 m_currentChapter = ev.param;
00965 break;
00966 case BD_EVENT_STILL:
00967 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00968 QString("EVENT_STILL %1").arg(ev.param));
00969 break;
00970 case BD_EVENT_STILL_TIME:
00971
00972
00973 usleep(10000);
00974 break;
00975 case BD_EVENT_SEEK:
00976 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SEEK"));
00977 break;
00978
00979
00980
00981 case BD_EVENT_AUDIO_STREAM:
00982 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_AUDIO_STREAM %1")
00983 .arg(ev.param));
00984 m_currentAudioStream = ev.param;
00985 break;
00986 case BD_EVENT_IG_STREAM:
00987 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_IG_STREAM %1")
00988 .arg(ev.param));
00989 m_currentIGStream = ev.param;
00990 break;
00991 case BD_EVENT_PG_TEXTST_STREAM:
00992 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00993 QString("EVENT_PG_TEXTST_STREAM %1").arg(ev.param));
00994 m_currentPGTextSTStream = ev.param;
00995 break;
00996 case BD_EVENT_SECONDARY_AUDIO_STREAM:
00997 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00998 QString("EVENT_SECONDARY_AUDIO_STREAM %1").arg(ev.param));
00999 m_currentSecondaryAudioStream = ev.param;
01000 break;
01001 case BD_EVENT_SECONDARY_VIDEO_STREAM:
01002 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01003 QString("EVENT_SECONDARY_VIDEO_STREAM %1").arg(ev.param));
01004 m_currentSecondaryVideoStream = ev.param;
01005 break;
01006
01007 case BD_EVENT_PG_TEXTST:
01008 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_PG_TEXTST %1")
01009 .arg(ev.param ? "enable" : "disable"));
01010 m_PGTextSTEnabled = ev.param;
01011 break;
01012 case BD_EVENT_SECONDARY_AUDIO:
01013 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_AUDIO %1")
01014 .arg(ev.param ? "enable" : "disable"));
01015 m_secondaryAudioEnabled = ev.param;
01016 break;
01017 case BD_EVENT_SECONDARY_VIDEO:
01018 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SECONDARY_VIDEO %1")
01019 .arg(ev.param ? "enable" : "disable"));
01020 m_secondaryVideoEnabled = ev.param;
01021 break;
01022 case BD_EVENT_SECONDARY_VIDEO_SIZE:
01023 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01024 QString("EVENT_SECONDARY_VIDEO_SIZE %1")
01025 .arg(ev.param==0 ? "PIP" : "fullscreen"));
01026 m_secondaryVideoIsFullscreen = ev.param;
01027 break;
01028
01029 default:
01030 LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Unknown Event! %1 %2")
01031 .arg(ev.event).arg(ev.param));
01032 break;
01033 }
01034 }
01035
01036 bool BDRingBuffer::IsInStillFrame(void) const
01037 {
01038 return m_stillTime > 0 && m_stillMode != BLURAY_STILL_NONE;
01039 }
01040
01041 void BDRingBuffer::WaitForPlayer(void)
01042 {
01043 if (m_ignorePlayerWait)
01044 return;
01045
01046 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Waiting for player's buffers to drain");
01047 m_playerWait = true;
01048 int count = 0;
01049 while (m_playerWait && count++ < 200)
01050 usleep(10000);
01051 if (m_playerWait)
01052 {
01053 LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
01054 m_playerWait = false;
01055 }
01056 }
01057
01058 bool BDRingBuffer::StartFromBeginning(void)
01059 {
01060 if (bdnav && m_isHDMVNavigation)
01061 {
01062 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Starting from beginning...");
01063 return true;
01064 }
01065 return true;
01066 }
01067
01068 bool BDRingBuffer::GetNameAndSerialNum(QString &name, QString &serial)
01069 {
01070 if (!m_metaDiscLibrary)
01071 return false;
01072
01073 name = QString(m_metaDiscLibrary->di_name);
01074 serial = QString::number(m_metaDiscLibrary->di_set_number);
01075
01076 if (name.isEmpty() && serial.isEmpty())
01077 return false;
01078 return true;
01079 }
01080
01081 void BDRingBuffer::ClearOverlays(void)
01082 {
01083 QMutexLocker lock(&m_overlayLock);
01084
01085 while (!m_overlayImages.isEmpty())
01086 BDOverlay::DeleteOverlay(m_overlayImages.takeFirst());
01087 }
01088
01089 BDOverlay* BDRingBuffer::GetOverlay(void)
01090 {
01091 QMutexLocker lock(&m_overlayLock);
01092 if (!m_overlayImages.isEmpty())
01093 return m_overlayImages.takeFirst();
01094 return NULL;
01095 }
01096
01097 void BDRingBuffer::SubmitOverlay(const bd_overlay_s * const overlay)
01098 {
01099 QMutexLocker lock(&m_overlayLock);
01100
01101 if (!overlay)
01102 return;
01103
01104 if ((overlay->w < 1) || (overlay->w > 1920) || (overlay->x > 1920) ||
01105 (overlay->h < 1) || (overlay->h > 1080) || (overlay->y > 1080))
01106 {
01107 LOG(VB_GENERAL, LOG_ERR, LOC +
01108 QString("Invalid overlay size: %1x%2+%3+%4")
01109 .arg(overlay->w).arg(overlay->h)
01110 .arg(overlay->x).arg(overlay->y));
01111 return;
01112 }
01113
01114 if (!overlay->img)
01115 {
01116 m_inMenu = false;
01117 QRect pos(overlay->x, overlay->y, overlay->w, overlay->h);
01118 m_overlayImages.append(new BDOverlay(NULL, NULL, pos,
01119 overlay->plane, overlay->pts));
01120 return;
01121 }
01122
01123 const BD_PG_RLE_ELEM *rlep = overlay->img;
01124 static const unsigned palettesize = 256 * 4;
01125 unsigned width = (overlay->w + 0x3) & (~0x3);
01126 unsigned pixels = ((overlay->w + 0xf) & (~0xf)) *
01127 ((overlay->h + 0xf) & (~0xf));
01128 unsigned actual = overlay->w * overlay->h;
01129 uint8_t *data = (uint8_t*)av_mallocz(pixels);
01130 uint8_t *palette = (uint8_t*)av_mallocz(palettesize);
01131
01132 int line = 0;
01133 int this_line = 0;
01134 for (unsigned i = 0; i < actual; i += rlep->len, rlep++)
01135 {
01136 if ((rlep->color == 0 && rlep->len == 0) || this_line >= overlay->w)
01137 {
01138 this_line = 0;
01139 line++;
01140 i = (line * width) + 1;
01141 }
01142 else
01143 {
01144 this_line += rlep->len;
01145 memset(data + i, rlep->color, rlep->len);
01146 }
01147 }
01148
01149 memcpy(palette, overlay->palette, palettesize);
01150
01151 QRect pos(overlay->x, overlay->y, width, overlay->h);
01152 m_overlayImages.append(new BDOverlay(data, palette, pos,
01153 overlay->plane, overlay->pts));
01154
01155 if (overlay->plane == 1)
01156 m_inMenu = true;
01157 }