[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / dvdplayer / DVDInputStreams / DVDInputStreamNavigator.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "DVDInputStreamNavigator.h"
22 #include "utils/LangCodeExpander.h"
23 #include "../DVDDemuxSPU.h"
24 #include "DVDStateSerializer.h"
25 #include "settings/GUISettings.h"
26 #include "LangInfo.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"
32 #endif
33
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 */
38
39 CDVDInputStreamNavigator::CDVDInputStreamNavigator(IDVDPlayer* player) : CDVDInputStream(DVDSTREAM_TYPE_DVD)
40 {
41   m_dvdnav = 0;
42   m_pDVDPlayer = player;
43   m_bCheckButtons = false;
44   m_iCellStart = 0;
45   m_iVobUnitStart = 0LL;
46   m_iVobUnitStop = 0LL;
47   m_iVobUnitCorrection = 0LL;
48   m_bInMenu = false;
49   m_holdmode = HOLDMODE_NONE;
50   m_iTitle = m_iTitleCount = 0;
51   m_iPart = m_iPartCount = 0;
52   m_iTime = m_iTotalTime = 0;
53   m_bEOF = false;
54   m_icurrentGroupId = 0;
55   m_lastevent = DVDNAV_NOP;
56
57   memset(m_lastblock, 0, sizeof(m_lastblock));
58 }
59
60 CDVDInputStreamNavigator::~CDVDInputStreamNavigator()
61 {
62   Close();
63 }
64
65 bool CDVDInputStreamNavigator::Open(const char* strFile, const std::string& content)
66 {
67   m_icurrentGroupId = 0;
68   if (!CDVDInputStream::Open(strFile, "video/x-dvd-mpeg"))
69     return false;
70
71   // load libdvdnav.dll
72   if (!m_dll.Load())
73     return false;
74
75   // load the dvd language codes
76   // g_LangCodeExpander.LoadStandardCodes();
77
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.
80
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);
88
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()));
93   path = strDVDFile;
94   free(strDVDFile);
95 #endif
96
97   // open up the DVD device
98   if (m_dll.dvdnav_open(&m_dvdnav, path.c_str()) != DVDNAV_STATUS_OK)
99   {
100     CLog::Log(LOGERROR,"Error on dvdnav_open\n");
101     Close();
102     return false;
103   }
104
105   int region = g_guiSettings.GetInt("dvds.playerregion");
106   int mask = 0;
107   if(region > 0)
108     mask = 1 << (region-1);
109   else
110   {
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;
115   }
116   if(!mask)
117     mask = 0xff;
118
119   CLog::Log(LOGDEBUG, "%s - Setting region mask %02x", __FUNCTION__, mask);
120   m_dll.dvdnav_set_region_mask(m_dvdnav, mask);
121
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';
126
127   char language_audio[3];
128   strncpy(language_audio, g_langInfo.GetDVDAudioLanguage().c_str(), sizeof(language_audio)-1);
129   language_audio[2] = '\0';
130
131   char language_subtitle[3];
132   strncpy(language_subtitle, g_langInfo.GetDVDSubtitleLanguage().c_str(), sizeof(language_subtitle)-1);
133   language_subtitle[2] = '\0';
134
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");
139
140   // set default language settings
141   if (m_dll.dvdnav_menu_language_select(m_dvdnav, (char*)language_menu) != DVDNAV_STATUS_OK)
142   {
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");
146   }
147
148   if (m_dll.dvdnav_audio_language_select(m_dvdnav, (char*)language_audio) != DVDNAV_STATUS_OK)
149   {
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");
153   }
154
155   if (m_dll.dvdnav_spu_language_select(m_dvdnav, (char*)language_subtitle) != DVDNAV_STATUS_OK)
156   {
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");
160   }
161
162   // set read ahead cache usage
163   if (m_dll.dvdnav_set_readahead_flag(m_dvdnav, 1) != DVDNAV_STATUS_OK)
164   {
165     CLog::Log(LOGERROR,"Error on dvdnav_set_readahead_flag: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
166     Close();
167     return false;
168   }
169
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)
173   {
174     CLog::Log(LOGERROR,"Error on dvdnav_set_PGC_positioning_flag: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
175     Close();
176     return false;
177   }
178
179   // jump directly to title menu
180   if(g_guiSettings.GetBool("dvds.automenu"))
181   {
182     int len, event;
183     uint8_t buf[2048];
184     uint8_t* buf_ptr = buf;
185
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);
189
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));
192   }
193
194   m_bEOF = false;
195   m_bCheckButtons = false;
196   m_iCellStart = 0;
197   m_iVobUnitStart = 0LL;
198   m_iVobUnitStop = 0LL;
199   m_iVobUnitCorrection = 0LL;
200   m_bInMenu = false;
201   m_holdmode = HOLDMODE_NONE;
202   m_iTitle = m_iTitleCount = 0;
203   m_iPart = m_iPartCount = 0;
204   m_iTime = m_iTotalTime = 0;
205
206   return true;
207 }
208
209 void CDVDInputStreamNavigator::Close()
210 {
211   if (!m_dvdnav) return;
212
213   // finish off by closing the dvdnav device
214   if (m_dll.dvdnav_close(m_dvdnav) != DVDNAV_STATUS_OK)
215   {
216     CLog::Log(LOGERROR,"Error on dvdnav_close: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
217     return ;
218   }
219
220   CDVDInputStream::Close();
221   m_dvdnav = NULL;
222   m_bEOF = true;
223 }
224
225 int CDVDInputStreamNavigator::Read(BYTE* buf, int buf_size)
226 {
227   if (!m_dvdnav || m_bEOF) return 0;
228   if (buf_size < DVD_VIDEO_BLOCKSIZE)
229   {
230     CLog::Log(LOGERROR, "CDVDInputStreamNavigator: buffer size is to small, %d bytes, should be 2048 bytes", buf_size);
231     return -1;
232   }
233
234   int navresult;
235   int iBytesRead;
236
237   while(true) {
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;
242   }
243
244   return iBytesRead;
245 }
246
247 // not working yet, but it is the recommanded way for seeking
248 int64_t CDVDInputStreamNavigator::Seek(int64_t offset, int whence)
249 {
250   if(whence == SEEK_POSSIBLE)
251     return 0;
252   else
253     return -1;
254 }
255
256 int CDVDInputStreamNavigator::ProcessBlock(BYTE* dest_buffer, int* read)
257 {
258   if (!m_dvdnav) return -1;
259
260   int result;
261   int len = 2048;
262   int iNavresult = NAVRESULT_NOP;
263
264   // m_tempbuffer will be used for anything that isn't a normal data block
265   uint8_t* buf = m_lastblock;
266   iNavresult = -1;
267
268   if(m_holdmode == HOLDMODE_HELD)
269     return NAVRESULT_HOLD;
270
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;
276   }
277   else
278     result = m_dll.dvdnav_get_next_cache_block(m_dvdnav, &buf, &m_lastevent, &len);
279
280   if (result == DVDNAV_STATUS_ERR)
281   {
282     CLog::Log(LOGERROR,"Error getting next block: %s\n", m_dll.dvdnav_err_to_string(m_dvdnav));
283     m_bEOF = true;
284     return NAVRESULT_ERROR;
285   }
286
287     switch (m_lastevent)
288     {
289     case DVDNAV_BLOCK_OK:
290       {
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);
295         *read = len;
296         iNavresult = NAVRESULT_DATA;
297       }
298       break;
299
300     case DVDNAV_NOP:
301       // Nothing to do here.
302       break;
303
304     case DVDNAV_STILL_FRAME:
305       {
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);
313
314         /* if user didn't care for action, just skip it */
315         if(iNavresult == NAVRESULT_NOP)
316           SkipStill();
317       }
318       break;
319
320     case DVDNAV_WAIT:
321       {
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)
329         {
330           CLog::Log(LOGDEBUG, " - DVDNAV_WAIT (HOLDING)");
331           m_holdmode = HOLDMODE_HELD;
332           iNavresult = NAVRESULT_HOLD;
333         }
334         else
335           iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_WAIT);
336
337         /* if user didn't care for action, just skip it */
338         if(iNavresult == NAVRESULT_NOP)
339           SkipWait();
340       }
341       break;
342
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.
346       {
347         iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_SPU_CLUT_CHANGE);
348       }
349       break;
350
351     case DVDNAV_SPU_STREAM_CHANGE:
352       // Player applications should inform their SPU decoder to switch channels
353       {
354         dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)buf;
355
356         //libdvdnav never sets logical, why.. don't know..
357         event->logical = GetActiveSubtitleStream();
358
359         if(event->logical<0 && GetSubTitleStreamCount()>0)
360         {
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);
364         }
365         m_bCheckButtons = true;
366         iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_SPU_STREAM_CHANGE);
367       }
368       break;
369
370     case DVDNAV_AUDIO_STREAM_CHANGE:
371       // Player applications should inform their audio decoder to switch channels
372       {
373
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
377
378         dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)buf;
379
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
383
384         event->logical = GetActiveAudioStream();
385         if(event->logical<0)
386         {
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);
390         }
391
392         iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_AUDIO_STREAM_CHANGE);
393       }
394
395       break;
396
397     case DVDNAV_HIGHLIGHT:
398       {
399         iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_HIGHLIGHT);
400       }
401       break;
402
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
407       // accordingly.
408       {
409         if(m_holdmode == HOLDMODE_NONE)
410         {
411           CLog::Log(LOGDEBUG, " - DVDNAV_VTS_CHANGE (HOLDING)");
412           m_holdmode = HOLDMODE_HELD;
413           iNavresult = NAVRESULT_HOLD;
414         }
415         else
416         {
417           m_bInMenu   = (0 == m_dll.dvdnav_is_domain_vts(m_dvdnav));
418           iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_VTS_CHANGE);
419         }
420       }
421       break;
422
423     case DVDNAV_CELL_CHANGE:
424       {
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
428         // accordingly.
429
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)
433         {
434           CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE (HOLDING)");
435           m_holdmode = HOLDMODE_HELD;
436           iNavresult = NAVRESULT_HOLD;
437           break;
438         }
439
440         uint32_t pos, len;
441
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);
444         if(m_iTitle > 0)
445           m_dll.dvdnav_get_number_of_parts(m_dvdnav, m_iTitle, &m_iPartCount);
446         else
447           m_iPartCount = 0;
448         m_dll.dvdnav_get_position(m_dvdnav, &pos, &len);
449
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
453
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;
459
460         iNavresult = m_pDVDPlayer->OnDVDNavResult(buf, DVDNAV_CELL_CHANGE);
461       }
462       break;
463
464     case DVDNAV_NAV_PACKET:
465       {
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.
470
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.
476
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);
481
482         pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
483         m_dll.dvdnav_get_current_nav_dsi(m_dvdnav);
484
485         if(!pci)
486         {
487           iNavresult = NAVRESULT_NOP;
488           break;
489         }
490
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));
493
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;
496         if(gap)
497         {
498           /* make sure demuxer is flushed before we change any correction */
499           if(m_holdmode == HOLDMODE_NONE)
500           {
501             CLog::Log(LOGDEBUG, "DVDNAV_NAV_PACKET (HOLDING)");
502             m_holdmode = HOLDMODE_HELD;
503             iNavresult = NAVRESULT_HOLD;
504             break;
505           }
506           m_iVobUnitCorrection += gap;
507
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);
509         }
510
511         m_iVobUnitStart = pci->pci_gi.vobu_s_ptm;
512         m_iVobUnitStop = pci->pci_gi.vobu_e_ptm;
513
514         m_iTime = (int) ( m_dll.dvdnav_convert_time( &(pci->pci_gi.e_eltm) ) + m_iCellStart ) / 90;
515
516         if (m_bCheckButtons)
517         {
518           CheckButtons();
519           m_bCheckButtons = false;
520         }
521
522         iNavresult = m_pDVDPlayer->OnDVDNavResult((void*)pci, DVDNAV_NAV_PACKET);
523       }
524       break;
525
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.
529       {
530         iNavresult = m_pDVDPlayer->OnDVDNavResult(NULL, DVDNAV_HOP_CHANNEL);
531       }
532       break;
533
534     case DVDNAV_STOP:
535       {
536         // Playback should end here.
537
538         // don't read any further, it could be libdvdnav had some problems reading
539         // the disc. reading further results in a crash
540         m_bEOF = true;
541
542         m_pDVDPlayer->OnDVDNavResult(NULL, DVDNAV_STOP);
543         iNavresult = NAVRESULT_ERROR;
544       }
545       break;
546
547     default:
548       {
549         CLog::Log(LOGDEBUG,"CDVDInputStreamNavigator: Unknown event (%i)\n", m_lastevent);
550       }
551       break;
552
553     }
554
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);
560
561   return iNavresult;
562 }
563
564 bool CDVDInputStreamNavigator::SetActiveAudioStream(int iId)
565 {
566   int streamId = ConvertAudioStreamId_XBMCToExternal(iId);
567   CLog::Log(LOGDEBUG, "%s - id: %d, stream: %d", __FUNCTION__, iId, streamId);
568
569   if (!m_dvdnav)
570     return false;
571
572   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
573   if (!vm)
574     return false;
575   if (!vm->state.pgc)
576     return false;
577
578   /* make sure stream is valid, if not don't allow it */
579   if (streamId < 0 || streamId >= 8)
580     return false;
581   else if ( !(vm->state.pgc->audio_control[streamId] & (1<<15)) )
582     return false;
583
584   if (vm->state.domain != VTS_DOMAIN && streamId != 0)
585     return false;
586
587   vm->state.AST_REG = streamId;
588   return true;
589 }
590
591 bool CDVDInputStreamNavigator::SetActiveSubtitleStream(int iId)
592 {
593   int streamId = ConvertSubtitleStreamId_XBMCToExternal(iId);
594   CLog::Log(LOGDEBUG, "%s - id: %d, stream: %d", __FUNCTION__, iId, streamId);
595
596   if (!m_dvdnav)
597     return false;
598
599   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
600   if (!vm)
601     return false;
602   if (!vm->state.pgc)
603     return false;
604
605   /* make sure stream is valid, if not don't allow it */
606   if (streamId < 0 || streamId >= 32)
607     return false;
608   else if ( !(vm->state.pgc->subp_control[streamId] & (1<<31)) )
609     return false;
610
611   if (vm->state.domain != VTS_DOMAIN && streamId != 0)
612     return false;
613
614   /* set subtitle stream without modifying visibility */
615   vm->state.SPST_REG = streamId | (vm->state.SPST_REG & 0x40);
616
617   return true;
618 }
619
620 void CDVDInputStreamNavigator::ActivateButton()
621 {
622   if (m_dvdnav)
623   {
624     m_dll.dvdnav_button_activate(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
625   }
626 }
627
628 void CDVDInputStreamNavigator::SelectButton(int iButton)
629 {
630   if (!m_dvdnav) return;
631   m_dll.dvdnav_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav), iButton);
632 }
633
634 int CDVDInputStreamNavigator::GetCurrentButton()
635 {
636   int button;
637   if (m_dvdnav)
638   {
639     m_dll.dvdnav_get_current_highlight(m_dvdnav, &button);
640     return button;
641   }
642   return -1;
643 }
644
645 void CDVDInputStreamNavigator::CheckButtons()
646 {
647   if (m_dvdnav)
648   {
649
650     pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
651     int iCurrentButton = GetCurrentButton();
652
653     if( iCurrentButton > 0 && iCurrentButton < 37 )
654     {
655       btni_t* button = &(pci->hli.btnit[iCurrentButton-1]);
656
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))
660       {
661         // button has info, it's valid
662         return;
663       }
664     }
665
666     // select first valid button.
667     for (int i = 0; i < 36; i++)
668     {
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)
673       {
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);
677         break;
678       }
679     }
680   }
681 }
682
683 int CDVDInputStreamNavigator::GetTotalButtons()
684 {
685   if (!m_dvdnav) return 0;
686
687   pci_t* pci = m_dll.dvdnav_get_current_nav_pci(m_dvdnav);
688
689   int counter = 0;
690   for (int i = 0; i < 36; i++)
691   {
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)
696     {
697       counter++;
698     }
699   }
700   return counter;
701 }
702
703 void CDVDInputStreamNavigator::OnUp()
704 {
705   if (m_dvdnav) m_dll.dvdnav_upper_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
706 }
707
708 void CDVDInputStreamNavigator::OnDown()
709 {
710   if (m_dvdnav) m_dll.dvdnav_lower_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
711 }
712
713 void CDVDInputStreamNavigator::OnLeft()
714 {
715   if (m_dvdnav) m_dll.dvdnav_left_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
716 }
717
718 void CDVDInputStreamNavigator::OnRight()
719 {
720   if (m_dvdnav) m_dll.dvdnav_right_button_select(m_dvdnav, m_dll.dvdnav_get_current_nav_pci(m_dvdnav));
721 }
722
723 bool CDVDInputStreamNavigator::OnMouseMove(const CPoint &point)
724 {
725   if (m_dvdnav)
726   {
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));
729   }
730   return false;
731 }
732
733 bool CDVDInputStreamNavigator::OnMouseClick(const CPoint &point)
734 {
735   if (m_dvdnav)
736   {
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));
739   }
740   return false;
741 }
742
743 void CDVDInputStreamNavigator::OnMenu()
744 {
745   if (m_dvdnav) m_dll.dvdnav_menu_call(m_dvdnav, DVD_MENU_Escape);
746 }
747
748 void CDVDInputStreamNavigator::OnBack()
749 {
750   if (m_dvdnav) m_dll.dvdnav_go_up(m_dvdnav);
751 }
752
753 // we don't allow skipping in menu's cause it will remove menu overlays
754 void CDVDInputStreamNavigator::OnNext()
755 {
756   if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
757   {
758     m_dll.dvdnav_next_pg_search(m_dvdnav);
759   }
760 }
761
762 // we don't allow skipping in menu's cause it will remove menu overlays
763 void CDVDInputStreamNavigator::OnPrevious()
764 {
765   if (m_dvdnav && !(IsInMenu() && GetTotalButtons() > 0))
766   {
767     m_dll.dvdnav_prev_pg_search(m_dvdnav);
768   }
769 }
770
771 void CDVDInputStreamNavigator::SkipStill()
772 {
773   if (!m_dvdnav) return ;
774   m_dll.dvdnav_still_skip(m_dvdnav);
775 }
776
777 void CDVDInputStreamNavigator::SkipWait()
778 {
779   if (!m_dvdnav) return ;
780   m_dll.dvdnav_wait_skip(m_dvdnav);
781 }
782
783 CDVDInputStream::ENextStream CDVDInputStreamNavigator::NextStream()
784 {
785   if(m_holdmode == HOLDMODE_HELD)
786     m_holdmode = HOLDMODE_SKIP;
787
788   if(m_bEOF)
789     return NEXTSTREAM_NONE;
790   else if(m_lastevent == DVDNAV_VTS_CHANGE)
791     return NEXTSTREAM_OPEN;
792   else
793     return NEXTSTREAM_RETRY;
794 }
795
796 int CDVDInputStreamNavigator::GetActiveSubtitleStream()
797 {
798   int activeStream = 0;
799
800   if (m_dvdnav)
801   {
802     vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
803     if (vm && vm->state.pgc)
804     {
805       // get the current selected audiostream, for non VTS_DOMAIN it is always stream 0
806       int subpN = 0;
807       if (vm->state.domain == VTS_DOMAIN)
808       {
809         subpN = vm->state.SPST_REG & ~0x40;
810
811         /* make sure stream is valid, if not don't allow it */
812         if (subpN < 0 || subpN >= 32)
813           subpN = -1;
814         else if ( !(vm->state.pgc->subp_control[subpN] & (1<<31)) )
815           subpN = -1;
816       }
817
818       activeStream = ConvertSubtitleStreamId_ExternalToXBMC(subpN);
819     }
820   }
821
822   return activeStream;
823 }
824
825 std::string CDVDInputStreamNavigator::GetSubtitleStreamLanguage(int iId)
826 {
827   if (!m_dvdnav) return NULL;
828
829   CStdString strLanguage;
830
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 )
834   {
835
836     if (subp_attributes.type == DVD_SUBPICTURE_TYPE_Language ||
837         subp_attributes.type == DVD_SUBPICTURE_TYPE_NotSpecified)
838     {
839       if (!g_LangCodeExpander.Lookup(strLanguage, subp_attributes.lang_code)) strLanguage = "Unknown";
840
841       switch (subp_attributes.lang_extension)
842       {
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:
847           break;
848
849         case DVD_SUBPICTURE_LANG_EXT_NormalCC:
850         case DVD_SUBPICTURE_LANG_EXT_BigCC:
851         case DVD_SUBPICTURE_LANG_EXT_ChildrensCC:
852           strLanguage+= " (CC)";
853           break;
854         case DVD_SUBPICTURE_LANG_EXT_Forced:
855           strLanguage+= " (Forced)";
856           break;
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)";
861           break;
862       }
863     }
864     else
865     {
866       strLanguage = "Unknown";
867     }
868   }
869
870   return strLanguage;
871 }
872
873 int CDVDInputStreamNavigator::GetSubTitleStreamCount()
874 {
875   if (!m_dvdnav) return 0;
876
877   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
878
879   if (!vm) return 0;
880   if (!vm->state.pgc) return 0;
881
882   if (vm->state.domain == VTS_DOMAIN)
883   {
884     int streamN = 0;
885     for (int i = 0; i < 32; i++)
886     {
887       if (vm->state.pgc->subp_control[i] & (1<<31))
888         streamN++;
889     }
890     return streamN;
891   }
892   else
893   {
894     /* just for good measure say that non vts domain always has one */
895     return 1;
896   }
897 }
898
899 int CDVDInputStreamNavigator::GetActiveAudioStream()
900 {
901   int activeStream = -1;
902
903   if (m_dvdnav)
904   {
905     vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
906     if (vm && vm->state.pgc)
907     {
908       // get the current selected audiostream, for non VTS_DOMAIN it is always stream 0
909       int audioN = 0;
910       if (vm->state.domain == VTS_DOMAIN)
911       {
912         audioN = vm->state.AST_REG;
913
914         /* make sure stream is valid, if not don't allow it */
915         if (audioN < 0 || audioN >= 8)
916           audioN = -1;
917         else if ( !(vm->state.pgc->audio_control[audioN] & (1<<15)) )
918           audioN = -1;
919       }
920
921       activeStream = ConvertAudioStreamId_ExternalToXBMC(audioN);
922     }
923   }
924
925   return activeStream;
926 }
927
928 std::string CDVDInputStreamNavigator::GetAudioStreamLanguage(int iId)
929 {
930   if (!m_dvdnav) return NULL;
931
932   CStdString strLanguage;
933
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 )
937   {
938     if (!g_LangCodeExpander.Lookup(strLanguage, audio_attributes.lang_code)) strLanguage = "Unknown";
939
940     switch( audio_attributes.lang_extension )
941     {
942       case DVD_AUDIO_LANG_EXT_VisuallyImpaired:
943         strLanguage+= " (Visually Impaired)";
944         break;
945       case DVD_AUDIO_LANG_EXT_DirectorsComments1:
946         strLanguage+= " (Directors Comments)";
947         break;
948       case DVD_AUDIO_LANG_EXT_DirectorsComments2:
949         strLanguage+= " (Directors Comments 2)";
950         break;
951       case DVD_AUDIO_LANG_EXT_NotSpecified:
952       case DVD_AUDIO_LANG_EXT_NormalCaptions:
953       default:
954         break;
955     }
956   }
957
958   return strLanguage;
959 }
960
961 int CDVDInputStreamNavigator::GetAudioStreamCount()
962 {
963   if (!m_dvdnav) return 0;
964
965   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
966
967   if (!vm) return 0;
968   if (!vm->state.pgc) return 0;
969
970   if (vm->state.domain == VTS_DOMAIN)
971   {
972     int streamN = 0;
973     for (int i = 0; i < 8; i++)
974     {
975       if (vm->state.pgc->audio_control[i] & (1<<15))
976         streamN++;
977     }
978     return streamN;
979   }
980   else
981   {
982     /* just for good measure say that non vts domain always has one */
983     return 1;
984   }
985 }
986
987 bool CDVDInputStreamNavigator::GetCurrentButtonInfo(CDVDOverlaySpu* pOverlayPicture, CDVDDemuxSPU* pSPU, int iButtonType)
988 {
989   int alpha[2][4];
990   int color[2][4];
991   dvdnav_highlight_area_t hl;
992
993   if (!m_dvdnav) return false;
994
995   int iButton = GetCurrentButton();
996
997   if (m_dll.dvdnav_get_button_info(m_dvdnav, alpha, color) == 0)
998   {
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];
1003
1004     if (pSPU->m_bHasClut)
1005     {
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];
1009     }
1010   }
1011
1012   if (DVDNAV_STATUS_OK == m_dll.dvdnav_get_highlight_area(m_dll.dvdnav_get_current_nav_pci(m_dvdnav), iButton, iButtonType, &hl))
1013   {
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;
1019   }
1020
1021   return true;
1022 }
1023
1024 int CDVDInputStreamNavigator::GetTotalTime()
1025 {
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;
1028 }
1029
1030 int CDVDInputStreamNavigator::GetTime()
1031 {
1032   //We use buffers of this as they can get called from multiple threads, and could block if we are currently reading data
1033   return m_iTime;
1034 }
1035
1036 bool CDVDInputStreamNavigator::SeekTime(int iTimeInMsec)
1037 {
1038   if( m_dll.dvdnav_time_search(m_dvdnav, iTimeInMsec * 90) == DVDNAV_STATUS_ERR )
1039   {
1040     CLog::Log(LOGDEBUG, "dvdnav: dvdnav_time_search failed( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1041     return false;
1042   }
1043   return true;
1044 }
1045
1046 bool CDVDInputStreamNavigator::SeekChapter(int iChapter)
1047 {
1048   if (!m_dvdnav)
1049     return false;
1050
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)
1054   {
1055     CLog::Log(LOGDEBUG, "%s - Seeking chapter is not allowed in menu set with buttons", __FUNCTION__);
1056     return false;
1057   }
1058
1059   bool enabled = IsSubtitleStreamEnabled();
1060   int audio    = GetActiveAudioStream();
1061   int subtitle = GetActiveSubtitleStream();
1062
1063   if (iChapter == (m_iPart + 1))
1064   {
1065     if (m_dll.dvdnav_next_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
1066     {
1067       CLog::Log(LOGERROR, "dvdnav: dvdnav_next_pg_search( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1068       return false;
1069     }
1070   }
1071   else
1072   if (iChapter == (m_iPart - 1))
1073   {
1074     if (m_dll.dvdnav_prev_pg_search(m_dvdnav) == DVDNAV_STATUS_ERR)
1075     {
1076       CLog::Log(LOGERROR, "dvdnav: dvdnav_prev_pg_search( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1077       return false;
1078     }
1079   }
1080   else  
1081   if (m_dll.dvdnav_part_play(m_dvdnav, m_iTitle, iChapter) == DVDNAV_STATUS_ERR)
1082   {
1083     CLog::Log(LOGERROR, "dvdnav: dvdnav_part_play failed( %s )", m_dll.dvdnav_err_to_string(m_dvdnav));
1084     return false;
1085   }
1086
1087   SetActiveSubtitleStream(subtitle);
1088   SetActiveAudioStream(audio);
1089   EnableSubtitleStream(enabled);
1090   return true;
1091 }
1092
1093 float CDVDInputStreamNavigator::GetVideoAspectRatio()
1094 {
1095   int iAspect = m_dll.dvdnav_get_video_aspect(m_dvdnav);
1096   int iPerm = m_dll.dvdnav_get_video_scale_permission(m_dvdnav);
1097
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
1101
1102   CLog::Log(LOGINFO, "%s - Aspect wanted: %d, Scale permissions: %d", __FUNCTION__, iAspect, iPerm);
1103   switch(iAspect)
1104   {
1105     case 2: //4:3
1106       return 4.0f / 3.0f;
1107     case 3: //16:9
1108       return 16.0f / 9.0f;
1109     case 4:
1110       return 2.11f / 1.0f;
1111     default: //Unknown, use libmpeg2
1112       return 0.0f;
1113   }
1114 }
1115
1116 void CDVDInputStreamNavigator::EnableSubtitleStream(bool bEnable)
1117 {
1118   if (!m_dvdnav)
1119     return;
1120
1121   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1122   if (!vm)
1123     return;
1124
1125   if(bEnable)
1126     vm->state.SPST_REG |= 0x40;
1127   else
1128     vm->state.SPST_REG &= ~0x40;
1129 }
1130
1131 bool CDVDInputStreamNavigator::IsSubtitleStreamEnabled()
1132 {
1133   if (!m_dvdnav)
1134     return false;
1135
1136   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1137   if (!vm)
1138     return false;
1139
1140
1141   if(vm->state.SPST_REG & 0x40)
1142     return true;
1143   else
1144     return false;
1145 }
1146
1147 bool CDVDInputStreamNavigator::GetNavigatorState(std::string &xmlstate)
1148 {
1149   if( !m_dvdnav )
1150     return false;
1151
1152   dvd_state_t save_state;
1153   if( DVDNAV_STATUS_ERR == m_dll.dvdnav_get_state(m_dvdnav, &save_state) )
1154   {
1155     CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::GetNavigatorState - Failed to get state (%s)", m_dll.dvdnav_err_to_string(m_dvdnav));
1156     return false;
1157   }
1158
1159   if( !CDVDStateSerializer::DVDToXMLState(xmlstate, &save_state) )
1160   {
1161     CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to serialize state");
1162     return false;
1163   }
1164
1165   return true;
1166 }
1167
1168 bool CDVDInputStreamNavigator::SetNavigatorState(std::string &xmlstate)
1169 {
1170   if( !m_dvdnav )
1171     return false;
1172
1173   dvd_state_t save_state;
1174   memset( &save_state, 0, sizeof( save_state ) );
1175
1176   if( !CDVDStateSerializer::XMLToDVDState(&save_state, xmlstate)  )
1177   {
1178     CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to deserialize state");
1179     return false;
1180   }
1181
1182   if( DVDNAV_STATUS_ERR == m_dll.dvdnav_set_state(m_dvdnav, &save_state) )
1183   {
1184     CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to set state (%s), retrying after read", m_dll.dvdnav_err_to_string(m_dvdnav));
1185
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);
1189
1190     if( DVDNAV_STATUS_ERR == m_dll.dvdnav_set_state(m_dvdnav, &save_state) )
1191     {
1192       CLog::Log(LOGWARNING, "CDVDInputStreamNavigator::SetNavigatorState - Failed to set state (%s)", m_dll.dvdnav_err_to_string(m_dvdnav));
1193       return false;
1194     }
1195   }
1196   return true;
1197 }
1198
1199 int CDVDInputStreamNavigator::ConvertAudioStreamId_XBMCToExternal(int id)
1200 {
1201   if (!m_dvdnav)
1202     return -1;
1203
1204   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1205   if(!vm)
1206     return -1;
1207
1208   if (vm->state.domain == VTS_DOMAIN)
1209   {
1210     if(!vm->state.pgc)
1211       return -1;
1212
1213     int stream = -1;
1214     for (int i = 0; i < 8; i++)
1215     {
1216       if (vm->state.pgc->audio_control[i] & (1<<15)) stream++;
1217       if (stream == id) return i;
1218     }
1219   }
1220   else if(id == 0)
1221     return 0;
1222
1223   return -1;
1224 }
1225
1226 int CDVDInputStreamNavigator::ConvertAudioStreamId_ExternalToXBMC(int id)
1227 {
1228   if  (!m_dvdnav) return -1;
1229   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1230
1231   if (!vm) return -1;
1232   if (!vm->state.pgc) return -1;
1233   if (id < 0) return -1;
1234
1235   if( vm->state.domain == VTS_DOMAIN )
1236   {
1237     /* VTS domain can only have limited number of streams */
1238     if (id >= 8)
1239     {
1240       CLog::Log(LOGWARNING, "%s - incorrect id : %d", __FUNCTION__, id);
1241       return -1;
1242     }
1243
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)))
1246     {
1247       int stream = -1;
1248       for (int i = 0; i <= id; i++)
1249       {
1250         if (vm->state.pgc->audio_control[i] & (1<<15)) stream++;
1251       }
1252       return stream;
1253     }
1254     else
1255     {
1256       CLog::Log(LOGWARNING, "%s - non existing id %d", __FUNCTION__, id);
1257       return -1;
1258     }
1259   }
1260   else
1261   {
1262     if( id != 0 )
1263       CLog::Log(LOGWARNING, "%s - non vts domain can't have id %d", __FUNCTION__, id);
1264
1265     // non VTS_DOMAIN, only one stream is available
1266     return 0;
1267   }
1268 }
1269
1270 int CDVDInputStreamNavigator::ConvertSubtitleStreamId_XBMCToExternal(int id)
1271 {
1272   if (!m_dvdnav)
1273     return -1;
1274
1275   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1276   if(!vm)
1277     return -1;
1278
1279   if (vm->state.domain == VTS_DOMAIN)
1280   {
1281     if(!vm->state.pgc)
1282       return -1;
1283
1284     int stream = -1;
1285     for (int i = 0; i < 32; i++)
1286     {
1287       if (vm->state.pgc->subp_control[i] & (1<<31)) stream++;
1288       if (stream == id) return i;
1289     }
1290   }
1291   else if(id == 0)
1292     return 0;
1293
1294   return -1;
1295 }
1296
1297 int CDVDInputStreamNavigator::ConvertSubtitleStreamId_ExternalToXBMC(int id)
1298 {
1299   if  (!m_dvdnav) return -1;
1300   vm_t* vm = m_dll.dvdnav_get_vm(m_dvdnav);
1301
1302   if (!vm) return -1;
1303   if (!vm->state.pgc) return -1;
1304   if (id < 0) return -1;
1305
1306   if( vm->state.domain == VTS_DOMAIN )
1307   {
1308     /* VTS domain can only have limited number of streams */
1309     if (id >= 32)
1310     {
1311       CLog::Log(LOGWARNING, "%s - incorrect id : %d", __FUNCTION__, id);
1312       return -1;
1313     }
1314
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)))
1317     {
1318       int stream = -1;
1319       for (int i = 0; i <= id; i++)
1320       {
1321         if (vm->state.pgc->subp_control[i] & (1<<31)) stream++;
1322       }
1323       return stream;
1324     }
1325     else
1326     {
1327       CLog::Log(LOGWARNING, "%s - non existing id %d", __FUNCTION__, id);
1328       return -1;
1329     }
1330   }
1331   else
1332   {
1333     if( id != 0 )
1334       CLog::Log(LOGWARNING, "%s - non vts domain can't have id %d", __FUNCTION__, id);
1335
1336     // non VTS_DOMAIN, only one stream is available
1337     return 0;
1338   }
1339 }