|
MythTV
0.26-pre
|
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" // for actions 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 // lockForWrite takes priority over lockForRead, so this will 00091 // take priority over the lockForRead in the read ahead thread. 00092 if (!has_lock) 00093 rwlock.lockForWrite(); 00094 00095 poslock.lockForWrite(); 00096 00097 // Optimize no-op seeks 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 // only valid for SEEK_SET & SEEK_CUR 00112 long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos; 00113 00114 // Here we perform a normal seek. When successful we 00115 // need to call ResetReadAhead(). A reset means we will 00116 // need to refill the buffer, which takes some time. 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 // This thread check is probably unnecessary as processEvents should 00264 // only handle events in the calling thread - and not all threads 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 // clean path filename 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 // Ask mythiowrapper to update this object on file open progress. Opening 00292 // a bluray disc can involve opening several hundred files which can take 00293 // several minutes when the disc structure is remote. The callback allows 00294 // us to 'kick' the main UI - as the 'please wait' widget is still visible 00295 // at this stage 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 // Check disc to see encryption status, menu and navigation types. 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 // The following settings affect HDMV navigation 00368 // (default audio track selection, 00369 // parental controls, menu language, etc. They are not yet used. 00370 00371 // Set parental level "age" to 99 for now. TODO: Add support for FE level 00372 bd_set_player_setting(bdnav, BLURAY_PLAYER_SETTING_PARENTAL, 99); 00373 00374 // Set preferred language to FE guide language 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 // Set preferred presentation graphics language to the FE guide language 00383 bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_PG_LANG, langpref); 00384 00385 // Set preferred menu language to the FE guide language 00386 bd_set_player_setting_str(bdnav, BLURAY_PLAYER_SETTING_MENU_LANG, langpref); 00387 00388 // Set player country code via MythLocale. (not a region setting) 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 // Return an index of relevant titles (excludes dupe clips + titles) 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 // Mostly event-driven values below 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 // First, attempt to initialize the disc in HDMV navigation mode. 00433 // If this fails, fall back to the traditional built-in title switching 00434 // mode. 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 // Register the Menu Overlay Callback 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 // Loop through the relevant titles and find the longest 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 // HACK for still frame menu navigation 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 /* current playback position */ 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 // N.B. event chapter numbering 1...N, chapter seeks etc 0... 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 // we use the clip information to determine the still frame status 00972 // sleep a little 00973 usleep(10000); 00974 break; 00975 case BD_EVENT_SEEK: 00976 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("EVENT_SEEK")); 00977 break; 00978 00979 /* stream selection */ 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; //bd_play(bdnav); 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 }
1.7.6.1