MythTV  0.26-pre
dvdringbuffer.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 #include <stdlib.h>
00003 
00004 #include "mythconfig.h"
00005 
00006 #include "dvdringbuffer.h"
00007 #include "mythcontext.h"
00008 #include "mythmediamonitor.h"
00009 #include "iso639.h"
00010 
00011 #include "mythdvdplayer.h"
00012 #include "compat.h"
00013 #include "mythlogging.h"
00014 #include "mythuihelper.h"
00015 
00016 #define LOC      QString("DVDRB: ")
00017 
00018 #define IncrementButtonVersion \
00019     if (++m_buttonVersion > 1024) \
00020         m_buttonVersion = 1;
00021 
00022 #define DVD_DRIVE_SPEED 1
00023 
00024 static const char *dvdnav_menu_table[] =
00025 {
00026     NULL,
00027     NULL,
00028     "Title",
00029     "Root",
00030     "Subpicture",
00031     "Audio",
00032     "Angle",
00033     "Part",
00034 };
00035 
00036 DVDInfo::DVDInfo(const QString &filename)
00037   : m_nav(NULL), m_name(NULL), m_serialnumber(NULL)
00038 {
00039     LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Trying %1").arg(filename));
00040     QString name = filename;
00041     if (name.left(6) == "dvd://")
00042         name.remove(0,5);
00043     else if (name.left(5) == "dvd:/")
00044         name.remove(0,4);
00045     else if (name.left(4) == "dvd:")
00046         name.remove(0,4);
00047 
00048     QByteArray fname = name.toLocal8Bit();
00049     dvdnav_status_t res = dvdnav_open(&m_nav, fname.constData());
00050     if (res == DVDNAV_STATUS_ERR)
00051     {
00052         LOG(VB_GENERAL, LOG_ERR, QString("DVDInfo: Failed to open device at %1")
00053                 .arg(fname.constData()));
00054         return;
00055     }
00056 
00057     res = dvdnav_get_title_string(m_nav, &m_name);
00058     if (res == DVDNAV_STATUS_ERR)
00059         LOG(VB_GENERAL, LOG_ERR, "DVDInfo: Failed to get name.");
00060     res = dvdnav_get_serial_string(m_nav, &m_serialnumber);
00061     if (res == DVDNAV_STATUS_ERR)
00062         LOG(VB_GENERAL, LOG_ERR, "DVDInfo: Failed to get serial number.");
00063 }
00064 
00065 DVDInfo::~DVDInfo(void)
00066 {
00067     if (m_nav)
00068         dvdnav_close(m_nav);
00069     LOG(VB_PLAYBACK, LOG_INFO, QString("DVDInfo: Finishing."));
00070 }
00071 
00072 bool DVDInfo::GetNameAndSerialNum(QString &name, QString &serial)
00073 {
00074     name   = QString(m_name);
00075     serial = QString(m_serialnumber);
00076     if (name.isEmpty() && serial.isEmpty())
00077         return false;
00078     return true;
00079 }
00080 
00081 DVDRingBuffer::DVDRingBuffer(const QString &lfilename) :
00082     RingBuffer(kRingBuffer_DVD),
00083     m_dvdnav(NULL),     m_dvdBlockReadBuf(NULL),
00084     m_dvdBlockRPos(0),  m_dvdBlockWPos(0),
00085     m_pgLength(0),      m_pgcLength(0),
00086     m_cellStart(0),     m_cellChanged(false),
00087     m_pgcLengthChanged(false), m_pgStart(0),
00088     m_currentpos(0),
00089     m_lastNav(NULL),    m_part(0), m_lastPart(0),
00090     m_title(0),         m_lastTitle(0),   m_playerWait(false),
00091     m_titleParts(0),    m_gotStop(false), m_currentAngle(0),
00092     m_currentTitleAngleCount(0), m_newSequence(false),
00093     m_still(0), m_lastStill(0),
00094     m_audioStreamsChanged(false),
00095     m_dvdWaiting(false),
00096     m_titleLength(0),
00097 
00098     m_skipstillorwait(true),
00099     m_cellstartPos(0), m_buttonSelected(false),
00100     m_buttonExists(false), m_cellid(0),
00101     m_lastcellid(0), m_vobid(0),
00102     m_lastvobid(0), m_cellRepeated(false),
00103 
00104     m_curAudioTrack(0),
00105     m_curSubtitleTrack(0),
00106     m_autoselectsubtitle(true),
00107     m_dvdname(NULL), m_serialnumber(NULL),
00108     m_seeking(false), m_seektime(0),
00109     m_currentTime(0),
00110     m_parent(NULL),
00111     m_forcedAspect(-1.0f),
00112 
00113     // Menu/buttons
00114     m_inMenu(false), m_buttonVersion(1), m_buttonStreamID(0),
00115     m_hl_button(0, 0, 0, 0), m_menuSpuPkt(0), m_menuBuflength(0)
00116 {
00117     memset(&m_dvdMenuButton, 0, sizeof(AVSubtitle));
00118     memset(m_dvdBlockWriteBuf, 0, sizeof(char) * DVD_BLOCK_SIZE);
00119     memset(m_clut, 0, sizeof(uint32_t) * 16);
00120     memset(m_button_color, 0, sizeof(uint8_t) * 4);
00121     memset(m_button_alpha, 0, sizeof(uint8_t) * 4);
00122     uint def[8] = { 3, 5, 10, 20, 30, 60, 120, 180 };
00123     uint seekValues[8] = { 1, 2, 4, 8, 10, 15, 20, 60 };
00124 
00125     for (uint i = 0; i < 8; i++)
00126         m_seekSpeedMap.insert(def[i], seekValues[i]);
00127 
00128     OpenFile(lfilename);
00129 }
00130 
00131 DVDRingBuffer::~DVDRingBuffer()
00132 {
00133     CloseDVD();
00134     m_menuBtnLock.lock();
00135     ClearMenuSPUParameters();
00136     m_menuBtnLock.unlock();
00137     ClearChapterCache();
00138 }
00139 
00140 void DVDRingBuffer::CloseDVD(void)
00141 {
00142     rwlock.lockForWrite();
00143     if (m_dvdnav)
00144     {
00145         SetDVDSpeed(-1);
00146         dvdnav_close(m_dvdnav);
00147         m_dvdnav = NULL;
00148     }
00149     m_gotStop = false;
00150     m_audioStreamsChanged = true;
00151     rwlock.unlock();
00152 }
00153 
00154 void DVDRingBuffer::ClearChapterCache(void)
00155 {
00156     rwlock.lockForWrite();
00157     foreach (QList<uint64_t> chapters, m_chapterMap)
00158         chapters.clear();
00159     m_chapterMap.clear();
00160     rwlock.unlock();
00161 }
00162 
00163 long long DVDRingBuffer::Seek(long long pos, int whence, bool has_lock)
00164 {
00165     LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(%1,%2,%3)")
00166             .arg(pos).arg((whence == SEEK_SET) ? "SEEK_SET":
00167                           ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"))
00168             .arg(has_lock ? "locked" : "unlocked"));
00169 
00170     long long ret = -1;
00171 
00172     // lockForWrite takes priority over lockForRead, so this will
00173     // take priority over the lockForRead in the read ahead thread.
00174     if (!has_lock)
00175         rwlock.lockForWrite();
00176 
00177     poslock.lockForWrite();
00178 
00179     // Optimize no-op seeks
00180     if (readaheadrunning &&
00181         ((whence == SEEK_SET && pos == readpos) ||
00182          (whence == SEEK_CUR && pos == 0)))
00183     {
00184         ret = readpos;
00185 
00186         poslock.unlock();
00187         if (!has_lock)
00188             rwlock.unlock();
00189 
00190         return ret;
00191     }
00192 
00193     // only valid for SEEK_SET & SEEK_CUR
00194     long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
00195 
00196     // Here we perform a normal seek. When successful we
00197     // need to call ResetReadAhead(). A reset means we will
00198     // need to refill the buffer, which takes some time.
00199     if ((SEEK_END == whence) ||
00200         ((SEEK_CUR == whence) && new_pos != 0))
00201     {
00202         errno = EINVAL;
00203         ret = -1;
00204     }
00205     else
00206     {
00207         NormalSeek(new_pos);
00208         ret = new_pos;
00209     }
00210 
00211     if (ret >= 0)
00212     {
00213         readpos = ret;
00214 
00215         ignorereadpos = -1;
00216 
00217         if (readaheadrunning)
00218             ResetReadAhead(readpos);
00219 
00220         readAdjust = 0;
00221     }
00222     else
00223     {
00224         QString cmd = QString("Seek(%1, %2)").arg(pos)
00225             .arg((whence == SEEK_SET) ? "SEEK_SET" :
00226                  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
00227         LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
00228     }
00229 
00230     poslock.unlock();
00231 
00232     generalWait.wakeAll();
00233 
00234     if (!has_lock)
00235         rwlock.unlock();
00236 
00237     return ret;
00238 }
00239 
00240 long long DVDRingBuffer::NormalSeek(long long time)
00241 {
00242     QMutexLocker lock(&m_seekLock);
00243     return Seek(time);
00244 }
00245 
00246 long long DVDRingBuffer::Seek(long long time)
00247 {
00248     dvdnav_status_t dvdRet = DVDNAV_STATUS_OK;
00249 
00250     int seekSpeed = 0;
00251     int ffrewSkip = 1;
00252     if (m_parent)
00253         ffrewSkip = m_parent->GetFFRewSkip();
00254 
00255     if (ffrewSkip != 1 && ffrewSkip != 0 && time != 0)
00256     {
00257         QMap<uint, uint>::const_iterator it = m_seekSpeedMap.lowerBound(labs(time));
00258         if (it == m_seekSpeedMap.end())
00259             seekSpeed = *(it - 1);
00260         else
00261             seekSpeed = *it;
00262         if (time < 0)
00263             seekSpeed = -seekSpeed;
00264         dvdRet = dvdnav_relative_time_search(m_dvdnav, seekSpeed);
00265     }
00266     else
00267     {
00268         m_seektime = (uint64_t)time;
00269         dvdRet = dvdnav_absolute_time_search(m_dvdnav, m_seektime, 0);
00270     }
00271 
00272     LOG(VB_PLAYBACK, LOG_DEBUG,
00273         QString("DVD Playback Seek() time: %1; seekSpeed: %2")
00274                         .arg(time).arg(seekSpeed));
00275 
00276     if (dvdRet == DVDNAV_STATUS_ERR)
00277     {
00278         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00279             QString("Seek() to time %1 failed").arg(time));
00280         return -1;
00281     }
00282     else if (!m_inMenu)
00283     {
00284         m_gotStop = false;
00285         if (time > 0 && ffrewSkip == 1)
00286             m_seeking = true;
00287     }
00288 
00289     return m_currentpos;
00290 }
00291 
00292 bool DVDRingBuffer::IsBookmarkAllowed(void)
00293 {
00294     return GetTotalTimeOfTitle() >= 120;
00295 }
00296 
00297 void DVDRingBuffer::GetDescForPos(QString &desc)
00298 {
00299     if (m_inMenu)
00300     {
00301         if ((m_part <= DVD_MENU_MAX) && dvdnav_menu_table[m_part] )
00302         {
00303             desc = QString("%1 Menu").arg(dvdnav_menu_table[m_part]);
00304         }
00305     }
00306     else
00307     {
00308         desc = QObject::tr("Title %1 chapter %2").arg(m_title).arg(m_part);
00309     }
00310 }
00311 
00312 bool DVDRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
00313 {
00314     rwlock.lockForWrite();
00315 
00316     if (m_dvdnav)
00317     {
00318         rwlock.unlock();
00319         CloseDVD();
00320         rwlock.lockForWrite();
00321     }
00322 
00323     safefilename = lfilename;
00324     filename = lfilename;
00325     QByteArray fname = filename.toLocal8Bit();
00326 
00327     dvdnav_status_t res = dvdnav_open(&m_dvdnav, fname.constData());
00328     if (res == DVDNAV_STATUS_ERR)
00329     {
00330         LOG(VB_GENERAL, LOG_ERR,
00331             LOC + QString("Failed to open DVD device at %1")
00332                 .arg(fname.constData()));
00333         rwlock.unlock();
00334         return false;
00335     }
00336 
00337     LOG(VB_GENERAL, LOG_INFO, LOC + QString("Opened DVD device at %1")
00338             .arg(fname.constData()));
00339 
00340     dvdnav_set_readahead_flag(m_dvdnav, 0);
00341     dvdnav_set_PGC_positioning_flag(m_dvdnav, 1);
00342 
00343     int32_t num_titles = 0;
00344     res = dvdnav_get_number_of_titles(m_dvdnav, &num_titles);
00345     if (num_titles == 0 || res == DVDNAV_STATUS_ERR)
00346     {
00347         char buf[DVD_BLOCK_SIZE * 5];
00348         LOG(VB_GENERAL, LOG_INFO,
00349             LOC + QString("Reading %1 bytes from the drive")
00350                 .arg(DVD_BLOCK_SIZE * 5));
00351         safe_read(buf, DVD_BLOCK_SIZE * 5);
00352         res = dvdnav_get_number_of_titles(m_dvdnav, &num_titles);
00353     }
00354 
00355     if (res == DVDNAV_STATUS_ERR)
00356     {
00357         LOG(VB_GENERAL, LOG_ERR,
00358             LOC + QString("Failed to get the number of titles on the DVD" ));
00359     }
00360     else
00361     {
00362         LOG(VB_GENERAL, LOG_INFO,
00363             LOC + QString("There are %1 titles on the disk")
00364                 .arg(num_titles));
00365      }
00366 
00367     dvdnav_title_play(m_dvdnav, 1);
00368 
00369     // Check we aren't starting in a still frame (which will probably fail as
00370     // ffmpeg will be unable to create a decoder)
00371     if (dvdnav_get_next_still_flag(m_dvdnav))
00372     {
00373         LOG(VB_GENERAL, LOG_NOTICE,
00374             LOC + "The selected title is a still frame. "
00375             "Playback is likely to fail - please raise a bug report at "
00376             "http://code.mythtv.org/trac");
00377     }
00378 
00379     dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
00380     dvdnav_get_title_string(m_dvdnav, &m_dvdname);
00381     dvdnav_get_serial_string(m_dvdnav, &m_serialnumber);
00382     dvdnav_get_angle_info(m_dvdnav, &m_currentAngle, &m_currentTitleAngleCount);
00383     SetDVDSpeed();
00384 
00385     // Populate the chapter list for this title, used in the OSD menu
00386     GetChapterTimes(m_title);
00387 
00388     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00389             QString("DVD Serial Number %1").arg(m_serialnumber));
00390 
00391     readblocksize   = DVD_BLOCK_SIZE * 62;
00392     setswitchtonext = false;
00393     ateof           = false;
00394     commserror      = false;
00395     numfailures     = 0;
00396     rawbitrate      = 8000;
00397 
00398     CalcReadAheadThresh();
00399 
00400     rwlock.unlock();
00401 
00402     return true;
00403 }
00404 
00405 bool DVDRingBuffer::StartFromBeginning(void)
00406 {
00407     LOG(VB_GENERAL, LOG_INFO, LOC + "Resetting DVD device.");
00408 
00409     // if a DVDNAV_STOP event has been emitted, dvdnav_reset does not
00410     // seem to restore the state, hence we need to re-create
00411     if (m_gotStop)
00412     {
00413         LOG(VB_GENERAL, LOG_ERR, LOC +
00414             "DVD errored after initial scan - trying again");
00415         CloseDVD();
00416         OpenFile(filename);
00417         if (!m_dvdnav)
00418             LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-open DVD.");
00419     }
00420 
00421     if (m_dvdnav)
00422     {
00423         QMutexLocker lock(&m_seekLock);
00424         dvdnav_first_play(m_dvdnav);
00425         m_audioStreamsChanged = true;
00426     }
00427 
00428     return m_dvdnav;
00429 }
00430 
00431 void DVDRingBuffer::GetChapterTimes(QList<long long> &times)
00432 {
00433     if (!m_chapterMap.contains(m_title))
00434         GetChapterTimes(m_title);
00435 
00436     if (!m_chapterMap.contains(m_title))
00437         return;
00438 
00439     foreach (uint64_t chapter, m_chapterMap.value(m_title))
00440         times.push_back(chapter);
00441 }
00442 
00443 uint64_t DVDRingBuffer::GetChapterTimes(uint title)
00444 {
00445     if (!m_dvdnav)
00446         return 0;
00447 
00448     uint64_t duration;
00449     uint64_t *chaps;
00450     uint32_t num = dvdnav_describe_title_chapters(m_dvdnav, title,
00451                                                   &chaps, &duration);
00452 
00453     if (num < 1)
00454     {
00455         LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve chapter data");
00456         return 0;
00457     }
00458 
00459     QList<uint64_t> chapters;
00460     // add the start
00461     chapters.append(0);
00462     // don't add the last 'chapter' - which is the title end
00463     if (num > 1)
00464     {
00465         for (uint i = 0; i < num - 1; i++)
00466             chapters.append((chaps[i] + 45000) / 90000);
00467     }
00468     // Assigned via calloc, must be free'd not deleted
00469     if (chaps)
00470         free(chaps);
00471     m_chapterMap.insert(title, chapters);
00472     return (duration + 45000) / 90000;
00473 }
00474 
00477 long long DVDRingBuffer::GetReadPosition(void) const
00478 {
00479     uint32_t pos = 0;
00480     uint32_t length = 1;
00481     if (m_dvdnav)
00482     {
00483         if (dvdnav_get_position(m_dvdnav, &pos, &length) == DVDNAV_STATUS_ERR)
00484         {
00485             // try one more time
00486             dvdnav_get_position(m_dvdnav, &pos, &length);
00487         }
00488     }
00489     return pos * DVD_BLOCK_SIZE;
00490 }
00491 
00492 void DVDRingBuffer::WaitForPlayer(void)
00493 {
00494     if (!m_skipstillorwait)
00495     {
00496         LOG(VB_PLAYBACK, LOG_INFO,
00497             LOC + "Waiting for player's buffers to drain");
00498         m_playerWait = true;
00499         int count = 0;
00500         while (m_playerWait && count++ < 200)
00501         {
00502             rwlock.unlock();
00503             usleep(10000);
00504             rwlock.lockForWrite();
00505         }
00506 
00507         if (m_playerWait)
00508         {
00509             LOG(VB_GENERAL, LOG_ERR, LOC + "Player wait state was not cleared");
00510             m_playerWait = false;
00511         }
00512     }
00513 }
00514 
00515 int DVDRingBuffer::safe_read(void *data, uint sz)
00516 {
00517     dvdnav_status_t dvdStat;
00518     unsigned char  *blockBuf     = NULL;
00519     uint            tot          = 0;
00520     int32_t         dvdEvent     = 0;
00521     int32_t         dvdEventSize = 0;
00522     int             needed       = sz;
00523     char           *dest         = (char*) data;
00524     int             offset       = 0;
00525 
00526     if (m_gotStop)
00527     {
00528         LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read: called after DVDNAV_STOP");
00529         errno = EBADF;
00530         return -1;
00531     }
00532 
00533     if (readaheadrunning)
00534         LOG(VB_GENERAL, LOG_ERR, LOC + "read ahead thread running.");
00535 
00536     while (needed)
00537     {
00538         blockBuf = m_dvdBlockWriteBuf;
00539 
00540         dvdStat = dvdnav_get_next_cache_block(
00541             m_dvdnav, &blockBuf, &dvdEvent, &dvdEventSize);
00542 
00543         if (dvdStat == DVDNAV_STATUS_ERR)
00544         {
00545             LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read block: %1")
00546                     .arg(dvdnav_err_to_string(m_dvdnav)));
00547             errno = EIO;
00548             return -1;
00549         }
00550 
00551         switch (dvdEvent)
00552         {
00553             // Standard packet for decoding
00554             case DVDNAV_BLOCK_OK:
00555             {
00556                 // copy block
00557                 if (!m_seeking)
00558                 {
00559                     memcpy(dest + offset, blockBuf, DVD_BLOCK_SIZE);
00560                     tot += DVD_BLOCK_SIZE;
00561                 }
00562 
00563                 // release buffer
00564                 if (blockBuf != m_dvdBlockWriteBuf)
00565                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00566 
00567                 // debug
00568                 LOG(VB_PLAYBACK|VB_FILE, LOG_DEBUG, LOC + "DVDNAV_BLOCK_OK");
00569             }
00570             break;
00571 
00572             // cell change
00573             case DVDNAV_CELL_CHANGE:
00574             {
00575                 // get event details
00576                 dvdnav_cell_change_event_t *cell_event =
00577                     (dvdnav_cell_change_event_t*) (blockBuf);
00578 
00579                 // a menu is anything that isn't in the VTS domain
00580                 m_inMenu = !dvdnav_is_domain_vts(m_dvdnav);
00581 
00582                 // update information for the current cell
00583                 m_cellChanged = true;
00584                 if (m_pgcLength != cell_event->pgc_length)
00585                     m_pgcLengthChanged = true;
00586                 m_pgLength  = cell_event->pg_length;
00587                 m_pgcLength = cell_event->pgc_length;
00588                 m_cellStart = cell_event->cell_start;
00589                 m_pgStart   = cell_event->pg_start;
00590 
00591                 // update title/part/still/menu information
00592                 m_lastTitle = m_title;
00593                 m_lastPart  = m_part;
00594                 m_lastStill = m_still;
00595                 uint32_t pos;
00596                 uint32_t length;
00597                 m_still = dvdnav_get_next_still_flag(m_dvdnav);
00598                 dvdnav_current_title_info(m_dvdnav, &m_title, &m_part);
00599                 dvdnav_get_number_of_parts(m_dvdnav, m_title, &m_titleParts);
00600                 dvdnav_get_position(m_dvdnav, &pos, &length);
00601                 m_titleLength = length *DVD_BLOCK_SIZE;
00602                 if (!m_seeking)
00603                     m_cellstartPos = GetReadPosition();
00604 
00605                 // debug
00606                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
00607                     QString("---- DVDNAV_CELL_CHANGE - Cell "
00608                             "#%1 Menu %2 Length %3")
00609                       .arg(cell_event->cellN).arg(m_inMenu ? "Yes" : "No")
00610                       .arg((float)cell_event->cell_length / 90000.0f,0,'f',1));
00611                 QString still = m_still ? ((m_still < 0xff) ?
00612                     QString("Stillframe: %1 seconds").arg(m_still) :
00613                     QString("Infinite stillframe")) :
00614                     QString("Length: %1 seconds")
00615                         .arg((float)m_pgcLength / 90000.0f, 0, 'f', 1);
00616                 if (m_title == 0)
00617                 {
00618                     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Menu #%1 %2")
00619                         .arg(m_part).arg(still));
00620                 }
00621                 else
00622                 {
00623                     LOG(VB_PLAYBACK, LOG_INFO,
00624                         LOC + QString("Title #%1: %2 Part %3 of %4")
00625                         .arg(m_title).arg(still).arg(m_part).arg(m_titleParts));
00626                 }
00627 
00628                 // wait unless it is a transition from one normal video cell to
00629                 // another or the same menu id
00630                 if (((m_still != m_lastStill) || (m_title != m_lastTitle)) &&
00631                     !((m_title == 0 && m_lastTitle == 0) &&
00632                       (m_part == m_lastPart)))
00633                 {
00634                     WaitForPlayer();
00635                 }
00636 
00637                 // if the new cell is a still frame, reset the timer
00638                 if (m_parent)
00639                 {
00640                     if (m_still && (m_still < 0xff))
00641                         m_parent->ResetStillFrameTimer();
00642                     else
00643                         m_parent->SetStillFrameTimeout(0);
00644                 }
00645 
00646                 // clear menus/still frame selections
00647                 m_lastvobid = m_vobid;
00648                 m_lastcellid = m_cellid;
00649                 m_buttonSelected = false;
00650                 m_vobid = m_cellid = 0;
00651                 m_cellRepeated = false;
00652 
00653                 IncrementButtonVersion;
00654                 if (m_inMenu)
00655                 {
00656                     m_autoselectsubtitle = true;
00657                     GetMythUI()->RestoreScreensaver();
00658                 }
00659                 else
00660                     GetMythUI()->DisableScreensaver();
00661 
00662                 // release buffer
00663                 if (blockBuf != m_dvdBlockWriteBuf)
00664                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00665             }
00666             break;
00667 
00668             // new colour lookup table for subtitles/menu buttons
00669             case DVDNAV_SPU_CLUT_CHANGE:
00670             {
00671                 // debug
00672                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_SPU_CLUT_CHANGE");
00673 
00674                 // store the new clut
00675                 memcpy(m_clut, blockBuf, 16 * sizeof(uint32_t));
00676                 // release buffer
00677                 if (blockBuf != m_dvdBlockWriteBuf)
00678                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00679             }
00680             break;
00681 
00682             // new Sub-picture Unit stream (subtitles/menu buttons)
00683             case DVDNAV_SPU_STREAM_CHANGE:
00684             {
00685                 // get event details
00686                 dvdnav_spu_stream_change_event_t* spu =
00687                     (dvdnav_spu_stream_change_event_t*)(blockBuf);
00688 
00689                 // clear any existing subs/buttons
00690                 IncrementButtonVersion;
00691 
00692                 // update the stream number
00693                 if (m_inMenu || NumMenuButtons() > 0)
00694                 {
00695                     m_buttonStreamID = 32;
00696                     int aspect = dvdnav_get_video_aspect(m_dvdnav);
00697 
00698                     // workaround where dvd menu is
00699                     // present in VTS_DOMAIN. dvdnav adds 0x80 to stream id
00700                     // proper fix should be put in dvdnav sometime
00701                     int physical_wide = (spu->physical_wide & 0xF);
00702 
00703                     if (aspect != 0 && physical_wide > 0)
00704                         m_buttonStreamID += physical_wide;
00705                 }
00706 
00707                 // not sure
00708                 if (m_autoselectsubtitle)
00709                     m_curSubtitleTrack = dvdnav_get_active_spu_stream(m_dvdnav);
00710 
00711                 // debug
00712                 LOG(VB_PLAYBACK, LOG_DEBUG,
00713                     QString(LOC + "DVDNAV_SPU_STREAM_CHANGE: "
00714                                   "physicalwide %1, physicalletterbox %2, "
00715                                   "physicalpanscan %3, currenttrack %4")
00716                         .arg(spu->physical_wide).arg(spu->physical_letterbox)
00717                         .arg(spu->physical_pan_scan).arg(m_curSubtitleTrack));
00718 
00719                 // release buffer
00720                 if (blockBuf != m_dvdBlockWriteBuf)
00721                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00722             }
00723             break;
00724 
00725             // the audio stream changed
00726             case DVDNAV_AUDIO_STREAM_CHANGE:
00727             {
00728                 // retrieve the new track
00729                 int new_track = dvdnav_get_active_audio_stream(m_dvdnav);
00730 
00731                 // debug
00732                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
00733                         QString("DVDNAV_AUDIO_STREAM_CHANGE: old %1 new %2")
00734                         .arg(new_track).arg(m_curAudioTrack));
00735 
00736                 // tell the decoder to reset the audio streams if necessary
00737                 if (new_track != m_curAudioTrack)
00738                 {
00739                     m_curAudioTrack = new_track;
00740                     m_audioStreamsChanged = true;
00741                 }
00742 
00743                 // release buffer
00744                 if (blockBuf != m_dvdBlockWriteBuf)
00745                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00746             }
00747             break;
00748 
00749             // navigation packet
00750             case DVDNAV_NAV_PACKET:
00751             {
00752                 QMutexLocker lock(&m_seekLock);
00753 
00754                 // get the latest nav
00755                 m_lastNav = (dvdnav_t *)blockBuf;
00756 
00757                 // retrive the latest Data Search Information
00758                 dsi_t *dsi = dvdnav_get_current_nav_dsi(m_dvdnav);
00759 
00760                 // if we are in a looping menu, we don't want to reset the
00761                 // selected button when we restart
00762                 if (m_vobid == 0 && m_cellid == 0)
00763                 {
00764                     m_vobid  = dsi->dsi_gi.vobu_vob_idn;
00765                     m_cellid = dsi->dsi_gi.vobu_c_idn;
00766                     if ((m_lastvobid == m_vobid) && (m_lastcellid == m_cellid)
00767                          && m_inMenu)
00768                     {
00769                         m_cellRepeated = true;
00770                     }
00771                 }
00772 
00773                 // update our status
00774                 m_currentTime = (uint)dvdnav_get_current_time(m_dvdnav);
00775                 m_currentpos = GetReadPosition();
00776 
00777                 if (m_seeking)
00778                 {
00779 
00780                     int relativetime =
00781                         (int)((m_seektime - m_currentTime)/ 90000);
00782                     if (relativetime <= 1)
00783                     {
00784                         m_seeking = false;
00785                         m_seektime = 0;
00786                     }
00787                     else
00788                     {
00789                         dvdnav_relative_time_search(m_dvdnav, relativetime * 2);
00790                     }
00791                 }
00792 
00793                 // debug
00794                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_NAV_PACKET");
00795 
00796                 // release buffer
00797                 if (blockBuf != m_dvdBlockWriteBuf)
00798                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00799             }
00800             break;
00801 
00802             case DVDNAV_HOP_CHANNEL:
00803             {
00804                 // debug
00805                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_HOP_CHANNEL");
00806                 WaitForPlayer();
00807             }
00808             break;
00809 
00810             // no op
00811             case DVDNAV_NOP:
00812                 break;
00813 
00814             // new Video Title Set - aspect ratio/letterboxing
00815             case DVDNAV_VTS_CHANGE:
00816             {
00817                 // retrieve event details
00818                 dvdnav_vts_change_event_t* vts =
00819                     (dvdnav_vts_change_event_t*)(blockBuf);
00820 
00821                 // update player
00822                 int aspect = dvdnav_get_video_aspect(m_dvdnav);
00823                 if (aspect == 2) // 4:3
00824                     m_forcedAspect = 4.0f / 3.0f;
00825                 else if (aspect == 3) // 16:9
00826                     m_forcedAspect = 16.0f / 9.0f;
00827                 else
00828                     m_forcedAspect = -1;
00829                 int permission = dvdnav_get_video_scale_permission(m_dvdnav);
00830 
00831                 // debug
00832                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
00833                     QString("DVDNAV_VTS_CHANGE: old_vtsN %1, new_vtsN %2, "
00834                             "aspect %3, perm %4")
00835                             .arg(vts->old_vtsN).arg(vts->new_vtsN)
00836                             .arg(aspect).arg(permission));
00837 
00838                 // trigger a rescan of the audio streams
00839                 if ((vts->old_vtsN != vts->new_vtsN) ||
00840                     (vts->old_domain != vts->new_domain))
00841                 {
00842                     m_audioStreamsChanged = true;
00843                 }
00844 
00845                 // release buffer
00846                 if (blockBuf != m_dvdBlockWriteBuf)
00847                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00848             }
00849             break;
00850 
00851             // menu button
00852             case DVDNAV_HIGHLIGHT:
00853             {
00854                 // retrieve details
00855                 dvdnav_highlight_event_t* hl =
00856                     (dvdnav_highlight_event_t*)(blockBuf);
00857 
00858                 // update the current button
00859                 m_menuBtnLock.lock();
00860                 DVDButtonUpdate(false);
00861                 IncrementButtonVersion;
00862                 m_menuBtnLock.unlock();
00863 
00864                 // debug
00865                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
00866                     QString("DVDNAV_HIGHLIGHT: display %1, palette %2, "
00867                             "sx %3, sy %4, ex %5, ey %6, pts %7, buttonN %8")
00868                         .arg(hl->display).arg(hl->palette)
00869                         .arg(hl->sx).arg(hl->sy)
00870                         .arg(hl->ex).arg(hl->ey)
00871                         .arg(hl->pts).arg(hl->buttonN));
00872 
00873                 // release buffer
00874                 if (blockBuf != m_dvdBlockWriteBuf)
00875                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00876             }
00877             break;
00878 
00879             // dvd still frame
00880             case DVDNAV_STILL_FRAME:
00881             {
00882                 // retrieve still frame details (length)
00883                 dvdnav_still_event_t* still =
00884                     (dvdnav_still_event_t*)(blockBuf);
00885 
00886                 // sense check
00887                 if (!m_still)
00888                     LOG(VB_GENERAL, LOG_WARNING, LOC + "DVDNAV_STILL_FRAME in "
00889                             "cell that is not marked as a still frame");
00890 
00891                 if (still->length != m_still)
00892                     LOG(VB_GENERAL, LOG_WARNING, LOC + "DVDNAV_STILL_FRAME "
00893                             "length does not match cell still length");
00894 
00895                 // pause a little as the dvdnav VM will continue to return
00896                 // this event until it has been skipped
00897                 rwlock.unlock();
00898                 usleep(10000);
00899                 rwlock.lockForWrite();
00900 
00901                 // when scanning the file or exiting playback, skip immediately
00902                 // otherwise update the timeout in the player
00903                 if (m_skipstillorwait)
00904                     SkipStillFrame();
00905                 else if (m_parent)
00906                 {
00907                     if ((still->length > 0) && (still->length < 0xff))
00908                         m_parent->SetStillFrameTimeout(still->length);
00909                 }
00910 
00911                 // debug
00912                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_STILL_FRAME");
00913 
00914                 // release buffer
00915                 if (blockBuf != m_dvdBlockWriteBuf)
00916                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00917             }
00918             break;
00919 
00920             // wait for the player
00921             case DVDNAV_WAIT:
00922             {
00923                 //debug
00924                 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "DVDNAV_WAIT");
00925 
00926                 // skip if required, otherwise wait (and loop)
00927                 if (m_skipstillorwait)
00928                     WaitSkip();
00929                 else
00930                 {
00931                     m_dvdWaiting = true;
00932                     rwlock.unlock();
00933                     usleep(10000);
00934                     rwlock.lockForWrite();
00935                 }
00936 
00937                 // release buffer
00938                 if (blockBuf != m_dvdBlockWriteBuf)
00939                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00940             }
00941             break;
00942 
00943             // exit playback
00944             case DVDNAV_STOP:
00945             {
00946                 LOG(VB_GENERAL, LOG_INFO, LOC + "DVDNAV_STOP");
00947                 sz = tot;
00948                 m_gotStop = true;
00949 
00950                 // release buffer
00951                 if (blockBuf != m_dvdBlockWriteBuf)
00952                     dvdnav_free_cache_block(m_dvdnav, blockBuf);
00953             }
00954             break;
00955 
00956             // this shouldn't happen
00957             default:
00958             {
00959                 LOG(VB_GENERAL, LOG_ERR, LOC +
00960                     QString("Unknown DVD event: %1").arg(dvdEvent));
00961             }
00962             break;
00963         }
00964 
00965         needed = sz - tot;
00966         offset = tot;
00967     }
00968 
00969     return tot;
00970 }
00971 
00972 bool DVDRingBuffer::playTrack(int track)
00973 {
00974     QMutexLocker lock(&m_seekLock);
00975     if (track < 1)
00976         Seek(0);
00977     else if (track < m_titleParts)
00978         dvdnav_part_play(m_dvdnav, m_title, track);
00979     else
00980         return false;
00981     m_gotStop = false;
00982     return true;
00983 }
00984 
00985 bool DVDRingBuffer::nextTrack(void)
00986 {
00987     int newPart = m_part + 1;
00988 
00989     QMutexLocker lock(&m_seekLock);
00990     if (newPart < m_titleParts)
00991     {
00992         dvdnav_part_play(m_dvdnav, m_title, newPart);
00993         m_gotStop = false;
00994         return true;
00995     }
00996     return false;
00997 }
00998 
00999 void DVDRingBuffer::prevTrack(void)
01000 {
01001     int newPart = m_part - 1;
01002 
01003     QMutexLocker lock(&m_seekLock);
01004     if (newPart > 0)
01005         dvdnav_part_play(m_dvdnav, m_title, newPart);
01006     else
01007         Seek(0);
01008     m_gotStop = false;
01009 }
01010 
01014 uint DVDRingBuffer::GetTotalTimeOfTitle(void)
01015 {
01016     return m_pgcLength / 90000;
01017 }
01018 
01021 uint DVDRingBuffer::GetCellStart(void)
01022 {
01023     return m_cellStart / 90000;
01024 }
01025 
01028 bool DVDRingBuffer::CellChanged(void)
01029 {
01030     bool ret = m_cellChanged;
01031     m_cellChanged = false;
01032     return ret;
01033 }
01034 
01037 bool DVDRingBuffer::PGCLengthChanged(void)
01038 {
01039     bool ret = m_pgcLengthChanged;
01040     m_pgcLengthChanged = false;
01041     return ret;
01042 }
01043 
01044 void DVDRingBuffer::SkipStillFrame(void)
01045 {
01046     QMutexLocker locker(&m_seekLock);
01047     LOG(VB_PLAYBACK, LOG_INFO, LOC + "Skipping still frame.");
01048     dvdnav_still_skip(m_dvdnav);
01049 }
01050 
01051 void DVDRingBuffer::WaitSkip(void)
01052 {
01053     QMutexLocker locker(&m_seekLock);
01054     dvdnav_wait_skip(m_dvdnav);
01055     m_dvdWaiting = false;
01056     LOG(VB_PLAYBACK, LOG_INFO, LOC + "Exiting DVDNAV_WAIT status");
01057 }
01058 
01061 bool DVDRingBuffer::GoToMenu(const QString str)
01062 {
01063     DVDMenuID_t menuid;
01064     QMutexLocker locker(&m_seekLock);
01065 
01066     LOG(VB_PLAYBACK, LOG_INFO,
01067         LOC + QString("DVDRingBuf: GoToMenu %1").arg(str));
01068 
01069     if (str.compare("chapter") == 0)
01070     {
01071         menuid = DVD_MENU_Part;
01072     }
01073     else if (str.compare("root") == 0)
01074     {
01075         menuid = DVD_MENU_Root;
01076     }
01077     else if (str.compare("title") == 0)
01078         menuid = DVD_MENU_Title;
01079     else
01080         return false;
01081 
01082     dvdnav_status_t ret = dvdnav_menu_call(m_dvdnav, menuid);
01083     if (ret == DVDNAV_STATUS_OK)
01084         return true;
01085     return false;
01086 }
01087 
01088 void DVDRingBuffer::GoToNextProgram(void)
01089 {
01090     QMutexLocker locker(&m_seekLock);
01091     // This conditional appears to be unnecessary, and might have come
01092     // from a mistake in a libdvdnav resync.
01093     //if (!dvdnav_is_domain_vts(m_dvdnav))
01094         dvdnav_next_pg_search(m_dvdnav);
01095 }
01096 
01097 void DVDRingBuffer::GoToPreviousProgram(void)
01098 {
01099     QMutexLocker locker(&m_seekLock);
01100     // This conditional appears to be unnecessary, and might have come
01101     // from a mistake in a libdvdnav resync.
01102     //if (!dvdnav_is_domain_vts(m_dvdnav))
01103         dvdnav_prev_pg_search(m_dvdnav);
01104 }
01105 
01106 bool DVDRingBuffer::HandleAction(const QStringList &actions, int64_t pts)
01107 {
01108     (void)pts;
01109 
01110     if (!NumMenuButtons())
01111         return false;
01112 
01113     bool handled = true;
01114     if (actions.contains(ACTION_UP) ||
01115         actions.contains(ACTION_CHANNELUP))
01116     {
01117         MoveButtonUp();
01118     }
01119     else if (actions.contains(ACTION_DOWN) ||
01120              actions.contains(ACTION_CHANNELDOWN))
01121     {
01122         MoveButtonDown();
01123     }
01124     else if (actions.contains(ACTION_LEFT) ||
01125              actions.contains(ACTION_SEEKRWND))
01126     {
01127         MoveButtonLeft();
01128     }
01129     else if (actions.contains(ACTION_RIGHT) ||
01130              actions.contains(ACTION_SEEKFFWD))
01131     {
01132         MoveButtonRight();
01133     }
01134     else if (actions.contains(ACTION_SELECT))
01135     {
01136         ActivateButton();
01137     }
01138     else
01139         handled = false;
01140 
01141     return handled;
01142 }
01143 
01144 void DVDRingBuffer::MoveButtonLeft(void)
01145 {
01146     if (NumMenuButtons() > 1)
01147     {
01148         pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01149         dvdnav_left_button_select(m_dvdnav, pci);
01150     }
01151 }
01152 
01153 void DVDRingBuffer::MoveButtonRight(void)
01154 {
01155     if (NumMenuButtons() > 1)
01156     {
01157         pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01158         dvdnav_right_button_select(m_dvdnav, pci);
01159     }
01160 }
01161 
01162 void DVDRingBuffer::MoveButtonUp(void)
01163 {
01164     if (NumMenuButtons() > 1)
01165     {
01166         pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01167         dvdnav_upper_button_select(m_dvdnav, pci);
01168     }
01169 }
01170 
01171 void DVDRingBuffer::MoveButtonDown(void)
01172 {
01173     if (NumMenuButtons() > 1)
01174     {
01175         pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01176         dvdnav_lower_button_select(m_dvdnav, pci);
01177     }
01178 }
01179 
01182 void DVDRingBuffer::ActivateButton(void)
01183 {
01184     if (NumMenuButtons() > 0)
01185     {
01186         IncrementButtonVersion;
01187         pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01188         dvdnav_button_activate(m_dvdnav, pci);
01189     }
01190 }
01191 
01194 void DVDRingBuffer::GetMenuSPUPkt(uint8_t *buf, int buf_size, int stream_id)
01195 {
01196     if (buf_size < 4)
01197         return;
01198 
01199     if (m_buttonStreamID != stream_id)
01200         return;
01201 
01202     QMutexLocker lock(&m_menuBtnLock);
01203 
01204     ClearMenuSPUParameters();
01205     uint8_t *spu_pkt;
01206     spu_pkt = (uint8_t*)av_malloc(buf_size);
01207     memcpy(spu_pkt, buf, buf_size);
01208     m_menuSpuPkt = spu_pkt;
01209     m_menuBuflength = buf_size;
01210     if (!m_buttonSelected)
01211     {
01212         SelectDefaultButton();
01213         m_buttonSelected = true;
01214     }
01215 
01216     if (DVDButtonUpdate(false))
01217     {
01218         int32_t gotbutton;
01219         m_buttonExists = DecodeSubtitles(&m_dvdMenuButton, &gotbutton,
01220                                         m_menuSpuPkt, m_menuBuflength);
01221     }
01222 }
01223 
01227 AVSubtitle *DVDRingBuffer::GetMenuSubtitle(uint &version)
01228 {
01229     // this is unlocked by ReleaseMenuButton
01230     m_menuBtnLock.lock();
01231 
01232     if ((m_menuBuflength > 4) && m_buttonExists && (NumMenuButtons() > 0))
01233     {
01234         version = m_buttonVersion;
01235         return &(m_dvdMenuButton);
01236     }
01237 
01238     return NULL;
01239 }
01240 
01241 
01242 void DVDRingBuffer::ReleaseMenuButton(void)
01243 {
01244     m_menuBtnLock.unlock();
01245 }
01246 
01249 QRect DVDRingBuffer::GetButtonCoords(void)
01250 {
01251     QRect rect(0,0,0,0);
01252     if (!m_buttonExists)
01253         return rect;
01254 
01255     rect.setRect(m_hl_button.x(), m_hl_button.y(), m_hl_button.width(),
01256                  m_hl_button.height());
01257 
01258     return rect;
01259 }
01260 
01264 bool DVDRingBuffer::DecodeSubtitles(AVSubtitle *sub, int *gotSubtitles,
01265                                     const uint8_t *spu_pkt, int buf_size)
01266 {
01267     #define GETBE16(p) (((p)[0] << 8) | (p)[1])
01268 
01269     int cmd_pos, pos, cmd, next_cmd_pos, offset1, offset2;
01270     int x1, x2, y1, y2;
01271     uint8_t alpha[4], palette[4];
01272     uint i;
01273     int date;
01274 
01275     if (!spu_pkt)
01276         return false;
01277 
01278     if (buf_size < 4)
01279         return false;
01280 
01281     bool force_subtitle_display = false;
01282     sub->rects = NULL;
01283     sub->num_rects = 0;
01284     sub->start_display_time = 0;
01285     sub->end_display_time = 0;
01286 
01287     cmd_pos = GETBE16(spu_pkt + 2);
01288     while ((cmd_pos + 4) < buf_size)
01289     {
01290         offset1 = -1;
01291         offset2 = -1;
01292         date = GETBE16(spu_pkt + cmd_pos);
01293         next_cmd_pos = GETBE16(spu_pkt + cmd_pos + 2);
01294         pos = cmd_pos + 4;
01295         x1 = x2 = y1 = y2 = 0;
01296         while (pos < buf_size)
01297         {
01298             cmd = spu_pkt[pos++];
01299             switch(cmd)
01300             {
01301                 case 0x00:
01302                     force_subtitle_display = true;
01303                 break;
01304                 case 0x01:
01305                     sub->start_display_time = (date << 10) / 90;
01306                 break;
01307                 case 0x02:
01308                     sub->end_display_time = (date << 10) / 90;
01309                 break;
01310                 case 0x03:
01311                 {
01312                     if ((buf_size - pos) < 2)
01313                         goto fail;
01314 
01315                     palette[3] = spu_pkt[pos] >> 4;
01316                     palette[2] = spu_pkt[pos] & 0x0f;
01317                     palette[1] = spu_pkt[pos + 1] >> 4;
01318                     palette[0] = spu_pkt[pos + 1] & 0x0f;
01319                     pos +=2;
01320                 }
01321                 break;
01322                 case 0x04:
01323                 {
01324                     if ((buf_size - pos) < 2)
01325                         goto fail;
01326                     alpha[3] = spu_pkt[pos] >> 4;
01327                     alpha[2] = spu_pkt[pos] & 0x0f;
01328                     alpha[1] = spu_pkt[pos + 1] >> 4;
01329                     alpha[0] = spu_pkt[pos + 1] & 0x0f;
01330                     pos +=2;
01331                 }
01332                 break;
01333                 case 0x05:
01334                 {
01335                     if ((buf_size - pos) < 6)
01336                         goto fail;
01337                     x1 = (spu_pkt[pos] << 4) | (spu_pkt[pos + 1] >> 4);
01338                     x2 = ((spu_pkt[pos + 1] & 0x0f) << 8) | spu_pkt[pos + 2];
01339                     y1 = (spu_pkt[pos + 3] << 4) | (spu_pkt[pos + 4] >> 4);
01340                     y2 = ((spu_pkt[pos + 4] & 0x0f) << 8) | spu_pkt[pos + 5];
01341                     pos +=6;
01342                 }
01343                 break;
01344                 case 0x06:
01345                 {
01346                     if ((buf_size - pos) < 4)
01347                         goto fail;
01348                     offset1 = GETBE16(spu_pkt + pos);
01349                     offset2 = GETBE16(spu_pkt + pos + 2);
01350                     pos +=4;
01351                 }
01352                 break;
01353                 case 0xff:
01354                 default:
01355                 goto the_end;
01356             }
01357         }
01358         the_end:
01359         if (offset1 >= 0)
01360         {
01361             int w, h;
01362             uint8_t *bitmap;
01363             w = x2 - x1 + 1;
01364             if (w < 0)
01365                 w = 0;
01366             h = y2 - y1 + 1;
01367             if (h < 0)
01368                 h = 0;
01369             if (w > 0 && h > 0)
01370             {
01371                 if (sub->rects != NULL)
01372                 {
01373                     for (i = 0; i < sub->num_rects; i++)
01374                     {
01375                         av_free(sub->rects[i]->pict.data[0]);
01376                         av_free(sub->rects[i]->pict.data[1]);
01377                         av_freep(&sub->rects[i]);
01378                     }
01379                     av_freep(&sub->rects);
01380                     sub->num_rects = 0;
01381                 }
01382 
01383                 bitmap = (uint8_t*) av_malloc(w * h);
01384                 sub->num_rects = (NumMenuButtons() > 0) ? 2 : 1;
01385                 sub->rects = (AVSubtitleRect **)
01386                         av_mallocz(sizeof(AVSubtitleRect*) * sub->num_rects);
01387                 for (i = 0; i < sub->num_rects; i++)
01388                 {
01389                     sub->rects[i] = (AVSubtitleRect *) av_mallocz(sizeof(AVSubtitleRect));
01390                 }
01391                 sub->rects[0]->pict.data[1] = (uint8_t*)av_mallocz(4 * 4);
01392                 decode_rle(bitmap, w * 2, w, (h + 1) / 2,
01393                             spu_pkt, offset1 * 2, buf_size);
01394                 decode_rle(bitmap + w, w * 2, w, h / 2,
01395                             spu_pkt, offset2 * 2, buf_size);
01396                 guess_palette((uint32_t*)sub->rects[0]->pict.data[1], palette, alpha);
01397                 sub->rects[0]->pict.data[0] = bitmap;
01398                 sub->rects[0]->x = x1;
01399                 sub->rects[0]->y = y1;
01400                 sub->rects[0]->w = w;
01401                 sub->rects[0]->h = h;
01402                 sub->rects[0]->type = SUBTITLE_BITMAP;
01403                 sub->rects[0]->nb_colors = 4;
01404                 sub->rects[0]->pict.linesize[0] = w;
01405                 if (NumMenuButtons() > 0)
01406                 {
01407                     sub->rects[1]->type = SUBTITLE_BITMAP;
01408                     sub->rects[1]->pict.data[1] = (uint8_t*)av_malloc(4 *4);
01409                     guess_palette((uint32_t*)sub->rects[1]->pict.data[1],
01410                                 m_button_color, m_button_alpha);
01411                 }
01412                 else
01413                     find_smallest_bounding_rectangle(sub);
01414                 *gotSubtitles = 1;
01415             }
01416         }
01417         if (next_cmd_pos == cmd_pos)
01418             break;
01419         cmd_pos = next_cmd_pos;
01420     }
01421     if (sub->num_rects > 0)
01422     {
01423         if (force_subtitle_display)
01424         {
01425             sub->forced = 1;
01426             LOG(VB_PLAYBACK, LOG_INFO, LOC + "Decoded forced subtitle");
01427         }
01428         return true;
01429     }
01430 fail:
01431     return false;
01432 }
01433 
01437 bool DVDRingBuffer::DVDButtonUpdate(bool b_mode)
01438 {
01439     if (!m_parent)
01440         return false;
01441 
01442     QSize video_disp_dim = m_parent->GetVideoSize();
01443     int videoheight = video_disp_dim.height();
01444     int videowidth = video_disp_dim.width();
01445 
01446     int32_t button;
01447     pci_t *pci;
01448     dvdnav_status_t dvdRet;
01449     dvdnav_highlight_area_t hl;
01450     dvdnav_get_current_highlight(m_dvdnav, &button);
01451     pci = dvdnav_get_current_nav_pci(m_dvdnav);
01452     dvdRet = dvdnav_get_highlight_area(pci, button, b_mode, &hl);
01453 
01454     if (dvdRet == DVDNAV_STATUS_ERR)
01455         return false;
01456 
01457     for (uint i = 0 ; i < 4 ; i++)
01458     {
01459         m_button_alpha[i] = 0xf & (hl.palette >> (4 * i ));
01460         m_button_color[i] = 0xf & (hl.palette >> (16+4 *i ));
01461     }
01462 
01463     m_hl_button.setCoords(hl.sx, hl.sy, hl.ex, hl.ey);
01464 
01465     if (((hl.sx + hl.sy) > 0) &&
01466             (hl.sx < videowidth && hl.sy < videoheight))
01467         return true;
01468 
01469     return false;
01470 }
01471 
01474 void DVDRingBuffer::ClearMenuButton(void)
01475 {
01476     if (m_buttonExists || m_dvdMenuButton.rects)
01477     {
01478         for (uint i = 0; i < m_dvdMenuButton.num_rects; i++)
01479         {
01480             AVSubtitleRect* rect = m_dvdMenuButton.rects[i];
01481             av_free(rect->pict.data[0]);
01482             av_free(rect->pict.data[1]);
01483             av_free(rect);
01484         }
01485         av_free(m_dvdMenuButton.rects);
01486         m_dvdMenuButton.rects = NULL;
01487         m_dvdMenuButton.num_rects = 0;
01488         m_buttonExists = false;
01489     }
01490 }
01491 
01495 void DVDRingBuffer::ClearMenuSPUParameters(void)
01496 {
01497     if (m_menuBuflength == 0)
01498         return;
01499 
01500     LOG(VB_PLAYBACK, LOG_INFO, LOC + "Clearing Menu SPU Packet" );
01501 
01502     ClearMenuButton();
01503 
01504     av_free(m_menuSpuPkt);
01505     m_menuBuflength = 0;
01506     m_hl_button.setRect(0, 0, 0, 0);
01507 }
01508 
01509 int DVDRingBuffer::NumMenuButtons(void) const
01510 {
01511     pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01512     int numButtons = pci->hli.hl_gi.btn_ns;
01513     if (numButtons > 0 && numButtons < 36)
01514         return numButtons;
01515     else
01516         return 0;
01517 }
01518 
01521 uint DVDRingBuffer::GetAudioLanguage(int id)
01522 {
01523     uint16_t lang = dvdnav_audio_stream_to_lang(m_dvdnav, id);
01524     LOG(VB_PLAYBACK, LOG_INFO, LOC +
01525         QString("StreamID: %1; lang: %2").arg(id).arg(lang));
01526     return ConvertLangCode(lang);
01527 }
01528 
01532 int DVDRingBuffer::GetAudioTrackNum(uint stream_id)
01533 {
01534     return dvdnav_get_audio_logical_stream(m_dvdnav, stream_id);
01535 }
01536 
01537 int DVDRingBuffer::GetAudioTrackType(uint stream_id)
01538 {
01539     AudioTrackType ret = kAudioTypeNormal;
01540     audio_attr_t attributes;
01541     int logicalStreamId = dvdnav_get_audio_logical_stream(m_dvdnav, stream_id);
01542     if (dvdnav_get_audio_attr(m_dvdnav, logicalStreamId, &attributes) >= 1)
01543     {
01544         LOG(VB_AUDIO, LOG_INFO, QString("DVD Audio Track #%1 Language "
01545                                         "Extension Code - %2")
01546                                         .arg(stream_id)
01547                                         .arg(attributes.code_extension));
01548         switch (attributes.code_extension)
01549         {
01550             case 1:
01551                 ret = kAudioTypeNormal;
01552                 break;
01553             case 2:
01554                 ret = kAudioTypeAudioDescription;
01555                 break;
01556             case 3: case 4:
01557                 ret = kAudioTypeCommentary;
01558                 break;
01559             case 0: default:
01560                 break;
01561         }
01562     }
01563 
01564     return (int)ret;
01565 }
01566 
01569 uint DVDRingBuffer::GetSubtitleLanguage(int id)
01570 {
01571     uint16_t lang = dvdnav_spu_stream_to_lang(m_dvdnav, id);
01572     LOG(VB_PLAYBACK, LOG_INFO, LOC +
01573         QString("StreamID: %1; lang: %2").arg(id).arg(lang));
01574     return ConvertLangCode(lang);
01575 }
01576 
01579 uint DVDRingBuffer::ConvertLangCode(uint16_t code)
01580 {
01581     if (code == 0)
01582         return 0;
01583 
01584     QChar str2[2];
01585     str2[0] = QChar(code >> 8);
01586     str2[1] = QChar(code & 0xff);
01587     QString str3 = iso639_str2_to_str3(QString(str2, 2));
01588 
01589     LOG(VB_PLAYBACK, LOG_INFO, LOC +
01590         QString("code: %1; iso639: %2").arg(code).arg(str3));
01591 
01592     if (!str3.isEmpty())
01593         return iso639_str3_to_key(str3);
01594     return 0;
01595 }
01596 
01600 void DVDRingBuffer::SelectDefaultButton(void)
01601 {
01602     pci_t *pci = dvdnav_get_current_nav_pci(m_dvdnav);
01603     int32_t button = pci->hli.hl_gi.fosl_btnn;
01604     if (button > 0 && !m_cellRepeated)
01605     {
01606         dvdnav_button_select(m_dvdnav,pci,button);
01607         return;
01608     }
01609     dvdnav_get_current_highlight(m_dvdnav,&button);
01610     if (button > 0 && button <= NumMenuButtons())
01611         dvdnav_button_select(m_dvdnav,pci,button);
01612     else
01613         dvdnav_button_select(m_dvdnav,pci,1);
01614 }
01615 
01620 void DVDRingBuffer::SetTrack(uint type, int trackNo)
01621 {
01622     if (type == kTrackTypeSubtitle)
01623     {
01624         m_curSubtitleTrack = trackNo;
01625         if (trackNo < 0)
01626             m_autoselectsubtitle = true;
01627         else
01628             m_autoselectsubtitle = false;
01629     }
01630     else if (type == kTrackTypeAudio)
01631     {
01632         m_curAudioTrack = trackNo;
01633         dvdnav_set_active_audio_stream(m_dvdnav, trackNo);
01634     }
01635 }
01636 
01642 int DVDRingBuffer::GetTrack(uint type)
01643 {
01644     if (type == kTrackTypeSubtitle)
01645         return m_curSubtitleTrack;
01646     else if (type == kTrackTypeAudio)
01647         return m_curAudioTrack;
01648 
01649     return 0;
01650 }
01651 
01652 uint8_t DVDRingBuffer::GetNumAudioChannels(int id)
01653 {
01654     unsigned char channels = dvdnav_audio_stream_channels(m_dvdnav, id);
01655     if (channels == 0xff)
01656         return 0;
01657     return (uint8_t)channels;
01658 }
01659 
01662 bool DVDRingBuffer::GetNameAndSerialNum(QString& _name, QString& _serial)
01663 {
01664     _name    = QString(m_dvdname);
01665     _serial    = QString(m_serialnumber);
01666     if (_name.isEmpty() && _serial.isEmpty())
01667         return false;
01668     return true;
01669 }
01670 
01676 double DVDRingBuffer::GetFrameRate(void)
01677 {
01678     double dvdfps = 0;
01679     int format = dvdnav_get_video_format(m_dvdnav);
01680 
01681     dvdfps = (format == 1)? 25.00 : 29.97;
01682     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DVD Frame Rate %1").arg(dvdfps));
01683     return dvdfps;
01684 }
01685 
01689 void DVDRingBuffer::SetDVDSpeed(void)
01690 {
01691     QMutexLocker lock(&m_seekLock);
01692     SetDVDSpeed(DVD_DRIVE_SPEED);
01693 }
01694 
01697 void DVDRingBuffer::SetDVDSpeed(int speed)
01698 {
01699     if (filename.startsWith("/"))
01700         MediaMonitor::SetCDSpeed(filename.toLocal8Bit().constData(), speed);
01701 }
01702 
01705 uint DVDRingBuffer::TitleTimeLeft(void)
01706 {
01707     return (GetTotalTimeOfTitle() - GetCurrentTime());
01708 }
01709 
01712 void DVDRingBuffer::guess_palette(uint32_t *rgba_palette,uint8_t *palette,
01713                                         uint8_t *alpha)
01714 {
01715     int i,r,g,b,y,cr,cb;
01716     uint32_t yuv;
01717 
01718     memset(rgba_palette, 0, 16);
01719 
01720     for (i=0 ; i < 4 ; i++)
01721     {
01722         yuv = m_clut[palette[i]];
01723         y = ((yuv >> 16) & 0xff);
01724         cr = ((yuv >> 8) & 0xff);
01725         cb = ((yuv >> 0) & 0xff);
01726         r  = int(y + 1.4022 * (cr - 128));
01727         b  = int(y + 1.7710 * (cb - 128));
01728         g  = int(1.7047 * y - (0.1952 * b) - (0.5647 * r)) ;
01729         if (r < 0) r = 0;
01730         if (g < 0) g = 0;
01731         if (b < 0) b = 0;
01732         if (r > 0xff) r = 0xff;
01733         if (g > 0xff) g = 0xff;
01734         if (b > 0xff) b = 0xff;
01735         rgba_palette[i] = ((alpha[i] * 17) << 24) | (r << 16 )| (g << 8) | b;
01736     }
01737 }
01738 
01742 int DVDRingBuffer::decode_rle(uint8_t *bitmap, int linesize, int w, int h,
01743                                   const uint8_t *buf, int nibble_offset, int buf_size)
01744 {
01745     unsigned int v;
01746     int x, y, len, color, nibble_end;
01747     uint8_t *d;
01748 
01749     nibble_end = buf_size * 2;
01750     x = 0;
01751     y = 0;
01752     d = bitmap;
01753     for(;;) {
01754         if (nibble_offset >= nibble_end)
01755             return -1;
01756         v = get_nibble(buf, nibble_offset++);
01757         if (v < 0x4) {
01758             v = (v << 4) | get_nibble(buf, nibble_offset++);
01759             if (v < 0x10) {
01760                 v = (v << 4) | get_nibble(buf, nibble_offset++);
01761                 if (v < 0x040) {
01762                     v = (v << 4) | get_nibble(buf, nibble_offset++);
01763                     if (v < 4) {
01764                         v |= (w - x) << 2;
01765                     }
01766                 }
01767             }
01768         }
01769         len = v >> 2;
01770         if (len > (w - x))
01771             len = (w - x);
01772         color = v & 0x03;
01773         memset(d + x, color, len);
01774         x += len;
01775         if (x >= w) {
01776             y++;
01777             if (y >= h)
01778                 break;
01779             d += linesize;
01780             x = 0;
01781             nibble_offset += (nibble_offset & 1);
01782        }
01783     }
01784     return 0;
01785 }
01786 
01789 int DVDRingBuffer::get_nibble(const uint8_t *buf, int nibble_offset)
01790 {
01791     return (buf[nibble_offset >> 1] >> ((1 - (nibble_offset & 1)) << 2)) & 0xf;
01792 }
01793 
01798 int DVDRingBuffer::is_transp(const uint8_t *buf, int pitch, int n,
01799                      const uint8_t *transp_color)
01800 {
01801     int i;
01802     for (i = 0; i < n; i++)
01803     {
01804         if (!transp_color[*buf])
01805             return 0;
01806         buf += pitch;
01807     }
01808     return 1;
01809 }
01810 
01816 int DVDRingBuffer::find_smallest_bounding_rectangle(AVSubtitle *s)
01817 {
01818     uint8_t transp_color[256];
01819     int y1, y2, x1, x2, y, w, h, i;
01820     uint8_t *bitmap;
01821 
01822     if (s->num_rects == 0 || s->rects == NULL ||
01823         s->rects[0]->w <= 0 || s->rects[0]->h <= 0)
01824     {
01825         return 0;
01826     }
01827 
01828     memset(transp_color, 0, 256);
01829     for (i = 0; i < s->rects[0]->nb_colors * 4; i+=4)
01830     {
01831         if ((s->rects[0]->pict.data[1][i] >> 24) == 0)
01832             transp_color[i] = 1;
01833     }
01834 
01835     y1 = 0;
01836     while (y1 < s->rects[0]->h &&
01837             is_transp(s->rects[0]->pict.data[0] + y1 * s->rects[0]->pict.linesize[0],
01838                     1, s->rects[0]->w, transp_color))
01839     {
01840         y1++;
01841     }
01842 
01843     if (y1 == s->rects[0]->h)
01844     {
01845         av_freep(&s->rects[0]->pict.data[0]);
01846         s->rects[0]->w = s->rects[0]->h = 0;
01847         return 0;
01848     }
01849 
01850     y2 = s->rects[0]->h - 1;
01851     while (y2 > 0 &&
01852             is_transp(s->rects[0]->pict.data[0] + y2 * s->rects[0]->pict.linesize[0], 1,
01853                     s->rects[0]->w, transp_color))
01854     {
01855         y2--;
01856     }
01857 
01858     x1 = 0;
01859     while (x1 < (s->rects[0]->w - 1) &&
01860            is_transp(s->rects[0]->pict.data[0] + x1, s->rects[0]->pict.linesize[0],
01861                     s->rects[0]->h, transp_color))
01862     {
01863         x1++;
01864     }
01865 
01866     x2 = s->rects[0]->w - 1;
01867     while (x2 > 0 &&
01868            is_transp(s->rects[0]->pict.data[0] + x2, s->rects[0]->pict.linesize[0],
01869                      s->rects[0]->h, transp_color))
01870     {
01871         x2--;
01872     }
01873 
01874     w = x2 - x1 + 1;
01875     h = y2 - y1 + 1;
01876     bitmap = (uint8_t*) av_malloc(w * h);
01877     if (!bitmap)
01878         return 1;
01879 
01880     for(y = 0; y < h; y++)
01881     {
01882         memcpy(bitmap + w * y, s->rects[0]->pict.data[0] + x1 +
01883                 (y1 + y) * s->rects[0]->pict.linesize[0], w);
01884     }
01885 
01886     av_freep(&s->rects[0]->pict.data[0]);
01887     s->rects[0]->pict.data[0] = bitmap;
01888     s->rects[0]->pict.linesize[0] = w;
01889     s->rects[0]->w = w;
01890     s->rects[0]->h = h;
01891     s->rects[0]->x += x1;
01892     s->rects[0]->y += y1;
01893     return 1;
01894 }
01895 
01896 bool DVDRingBuffer::SwitchAngle(uint angle)
01897 {
01898     if (!m_dvdnav)
01899         return false;
01900 
01901     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Switching to Angle %1...")
01902             .arg(angle));
01903     dvdnav_status_t status = dvdnav_angle_change(m_dvdnav, (int32_t)angle);
01904     if (status == DVDNAV_STATUS_OK)
01905     {
01906         m_currentAngle = angle;
01907         return true;
01908     }
01909     return false;
01910 }
01911 
01912 bool DVDRingBuffer::NewSequence(bool new_sequence)
01913 {
01914     bool result = false;
01915     if (new_sequence)
01916     {
01917         LOG(VB_PLAYBACK, LOG_INFO, LOC + "New sequence");
01918         m_newSequence = true;
01919         return result;
01920     }
01921 
01922     result = m_newSequence && m_inMenu;
01923     m_newSequence = false;
01924     if (result)
01925         LOG(VB_PLAYBACK, LOG_INFO, LOC + "Asking for still frame");
01926     return result;
01927 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends