|
MythTV
0.26-pre
|
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> ×) 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 }
1.7.6.1