MythTV  0.26-pre
bdringbuffer.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends