2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "DVDInputStreamNavigator.h"
22 #include "utils/LangCodeExpander.h"
23 #include "../DVDDemuxSPU.h"
24 #include "DVDStateSerializer.h"
25 #include "settings/GUISettings.h"
27 #include "utils/log.h"
28 #include "guilib/Geometry.h"
29 #include "utils/URIUtils.h"
30 #if defined(TARGET_DARWIN)
31 #include "osx/CocoaInterface.h"
34 #define HOLDMODE_NONE 0
35 #define HOLDMODE_HELD 1 /* set internally when we wish to flush demuxer */
36 #define HOLDMODE_SKIP 2 /* set by inputstream user, when they wish to skip the held mode */
37 #define HOLDMODE_DATA 3 /* set after hold mode has been exited, and action that inited it has been executed */
39 CDVDInputStreamNavigator::CDVDInputStreamNavigator(IDVDPlayer* player) : CDVDInputStream(DVDSTREAM_TYPE_DVD)
42 m_pDVDPlayer = player;
43 m_bCheckButtons = false;
45 m_iVobUnitStart = 0LL;
47 m_iVobUnitCorrection = 0LL;
49 m_holdmode = HOLDMODE_NONE;
50 m_iTitle = m_iTitleCount = 0;
51 m_iPart = m_iPartCount = 0;
52 m_iTime = m_iTotalTime = 0;
54 m_icurrentGroupId = 0;
55 m_lastevent = DVDNAV_NOP;
57 memset(m_lastblock, 0, sizeof(m_lastblock));
60 CDVDInputStreamNavigator::~CDVDInputStreamNavigator()
65 bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& content)
67 m_icurrentGroupId = 0;
68 if (!CDVDInputStream::Open(strFile, "video/x-dvd-mpeg"))
75 // load the dvd language codes
76 // g_LangCodeExpander.LoadStandardCodes();
78 // libdvdcss fails if the file path contains VIDEO_TS.IFO or VIDEO_TS/VIDEO_TS.IFO
79 // libdvdnav is still able to play without, so strip them.
81 CStdString path = strFile;
82 if(URIUtils::GetFileName(path) == "VIDEO_TS.IFO")
83 path = URIUtils::GetParentPath(path);
84 URIUtils::RemoveSlashAtEnd(path);
85 if(URIUtils::GetFileName(path) == "VIDEO_TS")
86 path = URIUtils::GetParentPath(path);
87 URIUtils::RemoveSlashAtEnd(path);
89 #if defined(TARGET_DARWIN_OSX)
90 // if physical DVDs, libdvdnav wants "/dev/rdiskN" device name for OSX,
91 // strDVDFile will get realloc'ed and replaced IF this is a physical DVD.
92 char* strDVDFile = Cocoa_MountPoint2DeviceName(strdup(path.c_str()));
97 // open up the DVD device
98 if (m_dll.dvdnav_open(&m_dvdnav, path.c_str()) != DVDNAV_STATUS_OK)
100 CLog::Log(LOGERROR,"Error on dvdnav_open\n");
105 int region = g_guiSettings.GetInt("dvds.playerregion");
108 mask = 1 << (region-1);
111 // find out what region dvd reports itself to be from, and use that as mask if available
112 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
113 if (vm && vm->vmgi && vm->vmgi->vmgi_mat)
114 mask = ((vm->vmgi->vmgi_mat->vmg_category >> 16) & 0xff) ^ 0xff;
119 CLog::Log(LOGDEBUG, "%s - Setting region mask %02x", __FUNCTION__, mask);
120 m_dll.dvdnav_set_region_mask(m_dvdnav, mask);
122 // get default language settings
123 char language_menu[3];
124 strncpy(language_menu, g_langInfo.GetDVDMenuLanguage().c_str(), sizeof(language_menu)-1);
125 language_menu[2] = '\0';
127 char language_audio[3];
128 strncpy(language_audio, g_langInfo.GetDVDAudioLanguage().c_str(), sizeof(language_audio)-1);
129 language_audio[2] = '\0';
131 char language_subtitle[3];
132 strncpy(language_subtitle, g_langInfo.GetDVDSubtitleLanguage().c_str(), sizeof(language_subtitle)-1);
133 language_subtitle[2] = '\0';
135 // set language settings in case they are not set in xbmc's configuration
136 if (language_menu[0] == '\0') strcpy(language_menu, "en");
137 if (language_audio[0] == '\0') strcpy(language_audio, "en");
138 if (language_subtitle[0] == '\0') strcpy(language_subtitle, "en");
140 // set default language settings
141 if (m_dll.dvdnav_menu_language_select(m_dvdnav, (char*)language_menu) != DVDNAV_STATUS_OK)
143 CLog::Log(LOGERROR, "Error on setting default menu language: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
144 CLog::Log(LOGERROR, "Defaulting to \"en\"");
145 m_dll.dvdnav_menu_language_select(m_dvdnav, (char*)"en");
148 if (m_dll.dvdnav_audio_language_select(m_dvdnav, (char*)language_audio) != DVDNAV_STATUS_OK)
150 CLog::Log(LOGERROR, "Error on setting default audio language: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
151 CLog::Log(LOGERROR, "Defaulting to \"en\"");
152 m_dll.dvdnav_audio_language_select(m_dvdnav, (char*)"en");
155 if (m_dll.dvdnav_spu_language_select(m_dvdnav, (char*)language_subtitle) != DVDNAV_STATUS_OK)
157 CLog::Log(LOGERROR, "Error on setting default subtitle language: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
158 CLog::Log(LOGERROR, "Defaulting to \"en\"");
159 m_dll.dvdnav_spu_language_select(m_dvdnav, (char*)"en");
162 // set read ahead cache usage
163 if (m_dll.dvdnav_set_readahead_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
165 CLog::Log(LOGERROR,"Error on dvdnav_set_readahead_flag: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
170 // set the PGC positioning flag to have position information relatively to the
171 // whole feature instead of just relatively to the current chapter
172 if (m_dll.dvdnav_set_PGC_positioning_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
174 CLog::Log(LOGERROR,"Error on dvdnav_set_PGC_positioning_flag: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
179 // jump directly to title menu
180 if(g_guiSettings.GetBool("dvds.automenu"))
184 uint8_t* buf_ptr = buf;
186 // must startup vm and pgc
187 m_dll.dvdnav_get_next_cache_block(m_dvdnav,&buf_ptr,&event,&len);
188 m_dll.dvdnav_sector_search(m_dvdnav, 0, SEEK_SET);
190 if(m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Title) != DVDNAV_STATUS_OK)
191 CLog::Log(LOGERROR,"Error on dvdnav_menu_call(Title): %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
195 m_bCheckButtons = false;
197 m_iVobUnitStart = 0LL;
198 m_iVobUnitStop = 0LL;
199 m_iVobUnitCorrection = 0LL;
201 m_holdmode = HOLDMODE_NONE;
202 m_iTitle = m_iTitleCount = 0;
203 m_iPart = m_iPartCount = 0;
204 m_iTime = m_iTotalTime = 0;
209 void CDVDInputStreamNavigator::Close()
211 if (!m_dvdnav) return;
213 // finish off by closing the dvdnav device
214 if (m_dll.dvdnav_close(m_dvdnav) != DVDNAV_STATUS_OK)
216 CLog::Log(LOGERROR,"Error on dvdnav_close: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
220 CDVDInputStream::Close();
225 int CDVDInputStreamNavigator::Read(BYTE* buf, int buf_size)
227 if (!m_dvdnav || m_bEOF) return 0;
228 if (buf_size < DVD_VIDEO_BLOCKSIZE)
230 CLog::Log(LOGERROR, "CDVDInputStreamNavigator: buffer size is to small, %d bytes, should be 2048 bytes", buf_size);
238 navresult = ProcessBlock(buf, &iBytesRead);
239 if (navresult == NAVRESULT_HOLD) return 0; // return 0 bytes read;
240 else if (navresult == NAVRESULT_ERROR) return -1;
241 else if (navresult == NAVRESULT_DATA) return iBytesRead;
247 // not working yet, but it is the recommanded way for seeking
248 int64_t CDVDInputStreamNavigator::Seek(int64_t offset, int whence)
250 if(whence == SEEK_POSSIBLE)
256 int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)
258 if (!m_dvdnav) return -1;
262 int iNavresult = NAVRESULT_NOP;
264 // m_tempbuffer will be used for anything that isn't a normal data block
265 uint8_t* buf = m_lastblock;
268 if(m_holdmode == HOLDMODE_HELD)
269 return NAVRESULT_HOLD;
271 // the main reading function
272 if(m_holdmode == HOLDMODE_SKIP)
273 { /* we where holding data, return the data held */
274 m_holdmode = HOLDMODE_DATA;
275 result = DVDNAV_STATUS_OK;
278 result = m_dll.dvdnav_get_next_cache_block(m_dvdnav, &buf, &m_lastevent, &len);
280 if (result == DVDNAV_STATUS_ERR)
282 CLog::Log(LOGERROR,"Error getting next block: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
284 return NAVRESULT_ERROR;
289 case DVDNAV_BLOCK_OK:
291 // We have received a regular block of the currently playing MPEG stream.
292 // buf contains the data and len its length (obviously!) (which is always 2048 bytes btw)
293 m_holdmode = HOLDMODE_NONE;
294 memcpy(dest_buffer, buf, len);
296 iNavresult = NAVRESULT_DATA;
301 // Nothing to do here.
304 case DVDNAV_STILL_FRAME:
306 // We have reached a still frame. A real player application would wait
307 // the amount of time specified by the still's length while still handling
308 // user input to make menus and other interactive stills work.
309 // A length of 0xff means an indefinite still which has to be skipped
310 // indirectly by some user interaction.
311 m_holdmode = HOLDMODE_NONE;
312 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_STILL_FRAME);
314 /* if user didn't care for action, just skip it */
315 if(iNavresult == NAVRESULT_NOP)
322 // We have reached a point in DVD playback, where timing is critical.
323 // Player application with internal fifos can introduce state
324 // inconsistencies, because libdvdnav is always the fifo's length
325 // ahead in the stream compared to what the application sees.
326 // Such applications should wait until their fifos are empty
327 // when they receive this type of event.
328 if(m_holdmode == HOLDMODE_NONE)
330 CLog::Log(LOGDEBUG, " - DVDNAV_WAIT (HOLDING)");
331 m_holdmode = HOLDMODE_HELD;
332 iNavresult = NAVRESULT_HOLD;
335 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_WAIT);
337 /* if user didn't care for action, just skip it */
338 if(iNavresult == NAVRESULT_NOP)
343 case DVDNAV_SPU_CLUT_CHANGE:
344 // Player applications should pass the new colour lookup table to their
345 // SPU decoder. The CLUT is given as 16 uint32_t's in the buffer.
347 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_SPU_CLUT_CHANGE);
351 case DVDNAV_SPU_STREAM_CHANGE:
352 // Player applications should inform their SPU decoder to switch channels
354 dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)buf;
356 //libdvdnav never sets logical, why.. don't know..
357 event->logical = GetActiveSubtitleStream();
359 if(event->logical<0 && GetSubTitleStreamCount()>0)
361 /* this will not take effect in this event */
362 CLog::Log(LOGINFO, "%s - none or invalid subtitle stream selected, defaulting to first", __FUNCTION__);
363 SetActiveSubtitleStream(0);
365 m_bCheckButtons = true;
366 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_SPU_STREAM_CHANGE);
370 case DVDNAV_AUDIO_STREAM_CHANGE:
371 // Player applications should inform their audio decoder to switch channels
374 //dvdnav_get_audio_logical_stream actually does the oposite to the docs..
375 //taking a audiostream as given on dvd, it gives the physical stream that
376 //refers to in the mpeg file
378 dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)buf;
380 //wroong... stupid docs..
381 //event->logical = dvdnav_get_audio_logical_stream(m_dvdnav, event->physical);
382 //logical should actually be set to the (vm->state).AST_REG
384 event->logical = GetActiveAudioStream();
387 /* this will not take effect in this event */
388 CLog::Log(LOGINFO, "%s - none or invalid audio stream selected, defaulting to first", __FUNCTION__);
389 SetActiveAudioStream(0);
392 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_AUDIO_STREAM_CHANGE);
397 case DVDNAV_HIGHLIGHT:
399 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_HIGHLIGHT);
403 case DVDNAV_VTS_CHANGE:
404 // Some status information like video aspect and video scale permissions do
405 // not change inside a VTS. Therefore this event can be used to query such
406 // information only when necessary and update the decoding/displaying
409 if(m_holdmode == HOLDMODE_NONE)
411 CLog::Log(LOGDEBUG, " - DVDNAV_VTS_CHANGE (HOLDING)");
412 m_holdmode = HOLDMODE_HELD;
413 iNavresult = NAVRESULT_HOLD;
417 m_bInMenu = (0 == m_dll.dvdnav_is_domain_vts(m_dvdnav));
418 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_VTS_CHANGE);
423 case DVDNAV_CELL_CHANGE:
425 // Some status information like the current Title and Part numbers do not
426 // change inside a cell. Therefore this event can be used to query such
427 // information only when necessary and update the decoding/displaying
430 // this may lead to a discontinuity, but it's also the end of the
431 // vobunit, so make sure everything in demuxer is output
432 if(m_holdmode == HOLDMODE_NONE)
434 CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE (HOLDING)");
435 m_holdmode = HOLDMODE_HELD;
436 iNavresult = NAVRESULT_HOLD;
442 m_dll.dvdnav_current_title_info(m_dvdnav, &m_iTitle, &m_iPart);
443 m_dll.dvdnav_get_number_of_titles(m_dvdnav, &m_iTitleCount);
445 m_dll.dvdnav_get_number_of_parts(m_dvdnav, m_iTitle, &m_iPartCount);
448 m_dll.dvdnav_get_position(m_dvdnav, &pos, &len);
450 CLog::Log(LOGDEBUG, "%s - Cell change: Title %d, Chapter %d\n", __FUNCTION__, m_iTitle, m_iPart);
451 CLog::Log(LOGDEBUG, "%s - At position %.0f%% inside the feature\n", __FUNCTION__, 100 * (double)pos / (double)len);
452 //Get total segment time
454 dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)buf;
455 m_iCellStart = cell_change_event->cell_start; // store cell time as we need that for time later
456 m_iTime = (int) (m_iCellStart / 90);
457 m_iTotalTime = (int) (cell_change_event->pgc_length / 90);
458 m_icurrentGroupId = cell_change_event->pgN * 1000 + cell_change_event->cellN;
460 iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE);
464 case DVDNAV_NAV_PACKET:
466 // A NAV packet provides PTS discontinuity information, angle linking information and
467 // button definitions for DVD menus. Angles are handled completely inside libdvdnav.
468 // For the menus to work, the NAV packet information has to be passed to the overlay
469 // engine of the player so that it knows the dimensions of the button areas.
471 // Applications with fifos should not use these functions to retrieve NAV packets,
472 // they should implement their own NAV handling, because the packet you get from these
473 // functions will already be ahead in the stream which can cause state inconsistencies.
474 // Applications with fifos should therefore pass the NAV packet through the fifo
475 // and decoding pipeline just like any other data.
477 // Calculate current time
478 //unsigned int pos, len;
479 //m_dll.dvdnav_get_position(m_dvdnav, &pos, &len);
480 //m_iTime = (int)(((int64_t)m_iTotalTime * pos) / len);
482 pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
483 m_dll.dvdnav_get_current_nav_dsi(m_dvdnav);
487 iNavresult = NAVRESULT_NOP;
491 /* if we have any buttons or are not in vts domain we assume we are in meny */
492 m_bInMenu = pci->hli.hl_gi.hli_ss || (0 == m_dll.dvdnav_is_domain_vts(m_dvdnav));
494 /* check for any gap in the stream, this is likely a discontinuity */
495 int64_t gap = (int64_t)pci->pci_gi.vobu_s_ptm - m_iVobUnitStop;
498 /* make sure demuxer is flushed before we change any correction */
499 if(m_holdmode == HOLDMODE_NONE)
501 CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET (HOLDING)");
502 m_holdmode = HOLDMODE_HELD;
503 iNavresult = NAVRESULT_HOLD;
506 m_iVobUnitCorrection += gap;
508 CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET - DISCONTINUITY FROM:%"PRId64" TO:%"PRId64" DIFF:%"PRId64, (m_iVobUnitStop * 1000)/90, ((int64_t)pci->pci_gi.vobu_s_ptm*1000)/90, (gap*1000)/90);
511 m_iVobUnitStart = pci->pci_gi.vobu_s_ptm;
512 m_iVobUnitStop = pci->pci_gi.vobu_e_ptm;
514 m_iTime = (int) ( m_dll.dvdnav_convert_time( &(pci->pci_gi.e_eltm) ) + m_iCellStart ) / 90;
519 m_bCheckButtons = false;
522 iNavresult = m_pDVDPlayer->OnDVDNavResult((void*)pci, DVDNAV_NAV_PACKET);
526 case DVDNAV_HOP_CHANNEL:
527 // This event is issued whenever a non-seamless operation has been executed.
528 // Applications with fifos should drop the fifos content to speed up responsiveness.
530 iNavresult = m_pDVDPlayer->OnDVDNavResult(NULL, DVDNAV_HOP_CHANNEL);
536 // Playback should end here.
538 // don't read any further, it could be libdvdnav had some problems reading
539 // the disc. reading further results in a crash
542 m_pDVDPlayer->OnDVDNavResult(NULL, DVDNAV_STOP);
543 iNavresult = NAVRESULT_ERROR;
549 CLog::Log(LOGDEBUG,"CDVDInputStreamNavigator: Unknown event (%i)\n", m_lastevent);
555 // check if libdvdnav gave us some other buffer to work with
556 // probably not needed since function will check if buf
557 // is part of the internal cache, but do it for good measure
558 if( buf != m_lastblock )
559 m_dll.dvdnav_free_cache_block(m_dvdnav, buf);
564 bool CDVDInputStreamNavigator::SetActiveAudioStream(int iId)
566 int streamId = ConvertAudioStreamId_XBMCToExternal(iId);
567 CLog::Log(LOGDEBUG, "%s - id: %d, stream: %d", __FUNCTION__, iId, streamId);
572 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
578 /* make sure stream is valid, if not don't allow it */
579 if (streamId < 0 || streamId >= 8)
581 else if ( !(vm->state.pgc->audio_control[streamId] & (1<<15)) )
584 if (vm->state.domain != VTS_DOMAIN && streamId != 0)
587 vm->state.AST_REG = streamId;
591 bool CDVDInputStreamNavigator::SetActiveSubtitleStream(int iId)
593 int streamId = ConvertSubtitleStreamId_XBMCToExternal(iId);
594 CLog::Log(LOGDEBUG, "%s - id: %d, stream: %d", __FUNCTION__, iId, streamId);
599 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
605 /* make sure stream is valid, if not don't allow it */
606 if (streamId < 0 || streamId >= 32)
608 else if ( !(vm->state.pgc->subp_control[streamId] & (1<<31)) )
611 if (vm->state.domain != VTS_DOMAIN && streamId != 0)
614 /* set subtitle stream without modifying visibility */
615 vm->state.SPST_REG = streamId | (vm->state.SPST_REG & 0x40);
620 void CDVDInputStreamNavigator::ActivateButton()
624 m_dll.dvdnav_button_activate(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
628 void CDVDInputStreamNavigator::SelectButton(int iButton)
630 if (!m_dvdnav) return;
631 m_dll.dvdnav_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav), iButton);
634 int CDVDInputStreamNavigator::GetCurrentButton()
639 m_dll.dvdnav_get_current_highlight(m_dvdnav, &button);
645 void CDVDInputStreamNavigator::CheckButtons()
650 pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
651 int iCurrentButton = GetCurrentButton();
653 if( iCurrentButton > 0 && iCurrentButton < 37 )
655 btni_t* button = &(pci->hli.btnit[iCurrentButton-1]);
657 // menu buttons are always cropped overlays, so if there is no such information
658 // we assume the button is invalid
659 if ((button->x_start || button->x_end || button->y_start || button->y_end))
661 // button has info, it's valid
666 // select first valid button.
667 for (int i = 0; i < 36; i++)
669 if (pci->hli.btnit[i].x_start ||
670 pci->hli.btnit[i].x_end ||
671 pci->hli.btnit[i].y_start ||
672 pci->hli.btnit[i].y_end)
674 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator: found invalid button(%d)", iCurrentButton);
675 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator: switching to button(%d) instead", i + 1);
676 m_dll.dvdnav_button_select(m_dvdnav, pci, i + 1);
683 int CDVDInputStreamNavigator::GetTotalButtons()
685 if (!m_dvdnav) return 0;
687 pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
690 for (int i = 0; i < 36; i++)
692 if (pci->hli.btnit[i].x_start ||
693 pci->hli.btnit[i].x_end ||
694 pci->hli.btnit[i].y_start ||
695 pci->hli.btnit[i].y_end)
703 void CDVDInputStreamNavigator::OnUp()
705 if (m_dvdnav) m_dll.dvdnav_upper_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
708 void CDVDInputStreamNavigator::OnDown()
710 if (m_dvdnav) m_dll.dvdnav_lower_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
713 void CDVDInputStreamNavigator::OnLeft()
715 if (m_dvdnav) m_dll.dvdnav_left_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
718 void CDVDInputStreamNavigator::OnRight()
720 if (m_dvdnav) m_dll.dvdnav_right_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
723 bool CDVDInputStreamNavigator::OnMouseMove(const CPoint &point)
727 pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
728 return (DVDNAV_STATUS_OK == m_dll.dvdnav_mouse_select(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
733 bool CDVDInputStreamNavigator::OnMouseClick(const CPoint &point)
737 pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
738 return (DVDNAV_STATUS_OK == m_dll.dvdnav_mouse_activate(m_dvdnav, pci, (int32_t)point.x, (int32_t)point.y));
743 void CDVDInputStreamNavigator::OnMenu()
745 if (m_dvdnav) m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Escape);
748 void CDVDInputStreamNavigator::OnBack()
750 if (m_dvdnav) m_dll.dvdnav_go_up(m_dvdnav);
753 // we don't allow skipping in menu's cause it will remove menu overlays
754 void CDVDInputStreamNavigator::OnNext()
756 if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
758 m_dll.dvdnav_next_pg_search(m_dvdnav);
762 // we don't allow skipping in menu's cause it will remove menu overlays
763 void CDVDInputStreamNavigator::OnPrevious()
765 if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
767 m_dll.dvdnav_prev_pg_search(m_dvdnav);
771 void CDVDInputStreamNavigator::SkipStill()
773 if (!m_dvdnav) return ;
774 m_dll.dvdnav_still_skip(m_dvdnav);
777 void CDVDInputStreamNavigator::SkipWait()
779 if (!m_dvdnav) return ;
780 m_dll.dvdnav_wait_skip(m_dvdnav);
783 CDVDInputStream::ENextStream CDVDInputStreamNavigator::NextStream()
785 if(m_holdmode == HOLDMODE_HELD)
786 m_holdmode = HOLDMODE_SKIP;
789 return NEXTSTREAM_NONE;
790 else if(m_lastevent == DVDNAV_VTS_CHANGE)
791 return NEXTSTREAM_OPEN;
793 return NEXTSTREAM_RETRY;
796 int CDVDInputStreamNavigator::GetActiveSubtitleStream()
798 int activeStream = 0;
802 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
803 if (vm && vm->state.pgc)
805 // get the current selected audiostream, for non VTS_DOMAIN it is always stream 0
807 if (vm->state.domain == VTS_DOMAIN)
809 subpN = vm->state.SPST_REG & ~0x40;
811 /* make sure stream is valid, if not don't allow it */
812 if (subpN < 0 || subpN >= 32)
814 else if ( !(vm->state.pgc->subp_control[subpN] & (1<<31)) )
818 activeStream = ConvertSubtitleStreamId_ExternalToXBMC(subpN);
825 std::string CDVDInputStreamNavigator::GetSubtitleStreamLanguage(int iId)
827 if (!m_dvdnav) return NULL;
829 CStdString strLanguage;
831 subp_attr_t subp_attributes;
832 int streamId = ConvertSubtitleStreamId_XBMCToExternal(iId);
833 if( m_dll.dvdnav_get_stitle_info(m_dvdnav, streamId, &subp_attributes) == DVDNAV_STATUS_OK )
836 if (subp_attributes.type == DVD_SUBPICTURE_TYPE_Language ||
837 subp_attributes.type == DVD_SUBPICTURE_TYPE_NotSpecified)
839 if (!g_LangCodeExpander.Lookup(strLanguage, subp_attributes.lang_code)) strLanguage = "Unknown";
841 switch (subp_attributes.lang_extension)
843 case DVD_SUBPICTURE_LANG_EXT_NotSpecified:
844 case DVD_SUBPICTURE_LANG_EXT_NormalCaptions:
845 case DVD_SUBPICTURE_LANG_EXT_BigCaptions:
846 case DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions:
849 case DVD_SUBPICTURE_LANG_EXT_NormalCC:
850 case DVD_SUBPICTURE_LANG_EXT_BigCC:
851 case DVD_SUBPICTURE_LANG_EXT_ChildrensCC:
852 strLanguage+= " (CC)";
854 case DVD_SUBPICTURE_LANG_EXT_Forced:
855 strLanguage+= " (Forced)";
857 case DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments:
858 case DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments:
859 case DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments:
860 strLanguage+= " (Directors Comments)";
866 strLanguage = "Unknown";
873 int CDVDInputStreamNavigator::GetSubTitleStreamCount()
875 if (!m_dvdnav) return 0;
877 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
880 if (!vm->state.pgc) return 0;
882 if (vm->state.domain == VTS_DOMAIN)
885 for (int i = 0; i < 32; i++)
887 if (vm->state.pgc->subp_control[i] & (1<<31))
894 /* just for good measure say that non vts domain always has one */
899 int CDVDInputStreamNavigator::GetActiveAudioStream()
901 int activeStream = -1;
905 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
906 if (vm && vm->state.pgc)
908 // get the current selected audiostream, for non VTS_DOMAIN it is always stream 0
910 if (vm->state.domain == VTS_DOMAIN)
912 audioN = vm->state.AST_REG;
914 /* make sure stream is valid, if not don't allow it */
915 if (audioN < 0 || audioN >= 8)
917 else if ( !(vm->state.pgc->audio_control[audioN] & (1<<15)) )
921 activeStream = ConvertAudioStreamId_ExternalToXBMC(audioN);
928 std::string CDVDInputStreamNavigator::GetAudioStreamLanguage(int iId)
930 if (!m_dvdnav) return NULL;
932 CStdString strLanguage;
934 audio_attr_t audio_attributes;
935 int streamId = ConvertAudioStreamId_XBMCToExternal(iId);
936 if( m_dll.dvdnav_get_audio_info(m_dvdnav, streamId, &audio_attributes) == DVDNAV_STATUS_OK )
938 if (!g_LangCodeExpander.Lookup(strLanguage, audio_attributes.lang_code)) strLanguage = "Unknown";
940 switch( audio_attributes.lang_extension )
942 case DVD_AUDIO_LANG_EXT_VisuallyImpaired:
943 strLanguage+= " (Visually Impaired)";
945 case DVD_AUDIO_LANG_EXT_DirectorsComments1:
946 strLanguage+= " (Directors Comments)";
948 case DVD_AUDIO_LANG_EXT_DirectorsComments2:
949 strLanguage+= " (Directors Comments 2)";
951 case DVD_AUDIO_LANG_EXT_NotSpecified:
952 case DVD_AUDIO_LANG_EXT_NormalCaptions:
961 int CDVDInputStreamNavigator::GetAudioStreamCount()
963 if (!m_dvdnav) return 0;
965 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
968 if (!vm->state.pgc) return 0;
970 if (vm->state.domain == VTS_DOMAIN)
973 for (int i = 0; i < 8; i++)
975 if (vm->state.pgc->audio_control[i] & (1<<15))
982 /* just for good measure say that non vts domain always has one */
987 bool CDVDInputStreamNavigator::GetCurrentButtonInfo(CDVDOverlaySpu* pOverlayPicture, CDVDDemuxSPU* pSPU, int iButtonType)
991 dvdnav_highlight_area_t hl;
993 if (!m_dvdnav) return false;
995 int iButton = GetCurrentButton();
997 if (m_dll.dvdnav_get_button_info(m_dvdnav, alpha, color) == 0)
999 pOverlayPicture->highlight_alpha[0] = alpha[iButtonType][0];
1000 pOverlayPicture->highlight_alpha[1] = alpha[iButtonType][1];
1001 pOverlayPicture->highlight_alpha[2] = alpha[iButtonType][2];
1002 pOverlayPicture->highlight_alpha[3] = alpha[iButtonType][3];
1004 if (pSPU->m_bHasClut)
1006 for (int i = 0; i < 4; i++)
1007 for (int j = 0; j < 3; j++)
1008 pOverlayPicture->highlight_color[i][j] = pSPU->m_clut[color[iButtonType][i]][j];
1012 if (DVDNAV_STATUS_OK == m_dll.dvdnav_get_highlight_area(m_dll.dvdnav_get_current_nav_pci(m_dvdnav), iButton, iButtonType, &hl))
1014 // button cropping information
1015 pOverlayPicture->crop_i_x_start = hl.sx;
1016 pOverlayPicture->crop_i_x_end = hl.ex;
1017 pOverlayPicture->crop_i_y_start = hl.sy;
1018 pOverlayPicture->crop_i_y_end = hl.ey;
1024 int CDVDInputStreamNavigator::GetTotalTime()
1026 //We use buffers of this as they can get called from multiple threads, and could block if we are currently reading data
1027 return m_iTotalTime;
1030 int CDVDInputStreamNavigator::GetTime()
1032 //We use buffers of this as they can get called from multiple threads, and could block if we are currently reading data
1036 bool CDVDInputStreamNavigator::SeekTime(int iTimeInMsec)
1038 if( m_dll.dvdnav_time_search(m_dvdnav, iTimeInMsec * 90) == DVDNAV_STATUS_ERR )
1040 CLog::Log(LOGDEBUG, "dvdnav: dvdnav_time_search failed( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1046 bool CDVDInputStreamNavigator::SeekChapter(int iChapter)
1051 // cannot allow to return true in case of buttons (overlays) because otherwise back in DVDPlayer FlushBuffers will remove menu overlays
1052 // therefore we just skip the request in case there are buttons and return false
1053 if (IsInMenu() && GetTotalButtons() > 0)
1055 CLog::Log(LOGDEBUG, "%s - Seeking chapter is not allowed in menu set with buttons", __FUNCTION__);
1059 bool enabled = IsSubtitleStreamEnabled();
1060 int audio = GetActiveAudioStream();
1061 int subtitle = GetActiveSubtitleStream();
1063 if (iChapter == (m_iPart + 1))
1065 if (m_dll.dvdnav_next_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
1067 CLog::Log(LOGERROR, "dvdnav: dvdnav_next_pg_search( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1072 if (iChapter == (m_iPart - 1))
1074 if (m_dll.dvdnav_prev_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
1076 CLog::Log(LOGERROR, "dvdnav: dvdnav_prev_pg_search( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1081 if (m_dll.dvdnav_part_play(m_dvdnav, m_iTitle, iChapter) == DVDNAV_STATUS_ERR)
1083 CLog::Log(LOGERROR, "dvdnav: dvdnav_part_play failed( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1087 SetActiveSubtitleStream(subtitle);
1088 SetActiveAudioStream(audio);
1089 EnableSubtitleStream(enabled);
1093 float CDVDInputStreamNavigator::GetVideoAspectRatio()
1095 int iAspect = m_dll.dvdnav_get_video_aspect(m_dvdnav);
1096 int iPerm = m_dll.dvdnav_get_video_scale_permission(m_dvdnav);
1098 //The video scale permissions should give if the source is letterboxed
1099 //and such. should be able to give us info that we can zoom in automatically
1100 //not sure what to do with it currently
1102 CLog::Log(LOGINFO, "%s - Aspect wanted: %d, Scale permissions: %d", __FUNCTION__, iAspect, iPerm);
1108 return 16.0f / 9.0f;
1110 return 2.11f / 1.0f;
1111 default: //Unknown, use libmpeg2
1116 void CDVDInputStreamNavigator::EnableSubtitleStream(bool bEnable)
1121 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1126 vm->state.SPST_REG |= 0x40;
1128 vm->state.SPST_REG &= ~0x40;
1131 bool CDVDInputStreamNavigator::IsSubtitleStreamEnabled()
1136 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1141 if(vm->state.SPST_REG & 0x40)
1147 bool CDVDInputStreamNavigator::GetNavigatorState(std::string &xmlstate)
1152 dvd_state_t save_state;
1153 if( DVDNAV_STATUS_ERR == m_dll.dvdnav_get_state(m_dvdnav, &save_state) )
1155 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::GetNavigatorState - Failed to get state (%s)", m_dll.dvdnav_err_to_string(m_dvdnav));
1159 if( !CDVDStateSerializer::DVDToXMLState(xmlstate, &save_state) )
1161 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to serialize state");
1168 bool CDVDInputStreamNavigator::SetNavigatorState(std::string &xmlstate)
1173 dvd_state_t save_state;
1174 memset( &save_state, 0, sizeof( save_state ) );
1176 if( !CDVDStateSerializer::XMLToDVDState(&save_state, xmlstate) )
1178 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to deserialize state");
1182 if( DVDNAV_STATUS_ERR == m_dll.dvdnav_set_state(m_dvdnav, &save_state) )
1184 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to set state (%s), retrying after read", m_dll.dvdnav_err_to_string(m_dvdnav));
1186 /* vm won't be started until after first read, this should really be handled internally */
1187 BYTE buffer[DVD_VIDEO_BLOCKSIZE];
1188 Read(buffer,DVD_VIDEO_BLOCKSIZE);
1190 if( DVDNAV_STATUS_ERR == m_dll.dvdnav_set_state(m_dvdnav, &save_state) )
1192 CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to set state (%s)", m_dll.dvdnav_err_to_string(m_dvdnav));
1199 int CDVDInputStreamNavigator::ConvertAudioStreamId_XBMCToExternal(int id)
1204 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1208 if (vm->state.domain == VTS_DOMAIN)
1214 for (int i = 0; i < 8; i++)
1216 if (vm->state.pgc->audio_control[i] & (1<<15)) stream++;
1217 if (stream == id) return i;
1226 int CDVDInputStreamNavigator::ConvertAudioStreamId_ExternalToXBMC(int id)
1228 if (!m_dvdnav) return -1;
1229 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1232 if (!vm->state.pgc) return -1;
1233 if (id < 0) return -1;
1235 if( vm->state.domain == VTS_DOMAIN )
1237 /* VTS domain can only have limited number of streams */
1240 CLog::Log(LOGWARNING, "%s - incorrect id : %d", __FUNCTION__, id);
1244 /* make sure this is a valid id, otherwise the count below will be very wrong */
1245 if ((vm->state.pgc->audio_control[id] & (1<<15)))
1248 for (int i = 0; i <= id; i++)
1250 if (vm->state.pgc->audio_control[i] & (1<<15)) stream++;
1256 CLog::Log(LOGWARNING, "%s - non existing id %d", __FUNCTION__, id);
1263 CLog::Log(LOGWARNING, "%s - non vts domain can't have id %d", __FUNCTION__, id);
1265 // non VTS_DOMAIN, only one stream is available
1270 int CDVDInputStreamNavigator::ConvertSubtitleStreamId_XBMCToExternal(int id)
1275 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1279 if (vm->state.domain == VTS_DOMAIN)
1285 for (int i = 0; i < 32; i++)
1287 if (vm->state.pgc->subp_control[i] & (1<<31)) stream++;
1288 if (stream == id) return i;
1297 int CDVDInputStreamNavigator::ConvertSubtitleStreamId_ExternalToXBMC(int id)
1299 if (!m_dvdnav) return -1;
1300 vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1303 if (!vm->state.pgc) return -1;
1304 if (id < 0) return -1;
1306 if( vm->state.domain == VTS_DOMAIN )
1308 /* VTS domain can only have limited number of streams */
1311 CLog::Log(LOGWARNING, "%s - incorrect id : %d", __FUNCTION__, id);
1315 /* make sure this is a valid id, otherwise the count below will be very wrong */
1316 if ((vm->state.pgc->subp_control[id] & (1<<31)))
1319 for (int i = 0; i <= id; i++)
1321 if (vm->state.pgc->subp_control[i] & (1<<31)) stream++;
1327 CLog::Log(LOGWARNING, "%s - non existing id %d", __FUNCTION__, id);
1334 CLog::Log(LOGWARNING, "%s - non vts domain can't have id %d", __FUNCTION__, id);
1336 // non VTS_DOMAIN, only one stream is available