changed: if stream hints matches when opening a new stream, don't close and reopen...
[xbmc:xbmc-antiquated.git] / XBMC / xbmc / cores / dvdplayer / DVDPlayer.cpp
1 /*
2  *      Copyright (C) 2005-2008 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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21  
22 #include "stdafx.h"
23 #include "DVDPlayer.h"
24
25 #include "DVDInputStreams/DVDInputStream.h"
26 #include "DVDInputStreams/DVDFactoryInputStream.h"
27 #include "DVDInputStreams/DVDInputStreamNavigator.h"
28 #include "DVDInputStreams/DVDInputStreamTV.h"
29
30 #include "DVDDemuxers/DVDDemux.h"
31 #include "DVDDemuxers/DVDDemuxUtils.h"
32 #include "DVDDemuxers/DVDDemuxVobsub.h"
33 #include "DVDDemuxers/DVDFactoryDemuxer.h"
34 #include "DVDDemuxers/DVDDemuxFFmpeg.h"
35
36 #include "DVDCodecs/DVDCodecs.h"
37 #include "DVDCodecs/DVDFactoryCodec.h"
38
39 #include "Util.h"
40 #include "utils/GUIInfoManager.h"
41 #include "Application.h"
42 #include "DVDPerformanceCounter.h"
43 #include "../../FileSystem/cdioSupport.h"
44 #include "../../Picture.h"
45 #include "../ffmpeg/DllSwScale.h"
46 #ifdef HAS_VIDEO_PLAYBACK
47 #include "cores/VideoRenderers/RenderManager.h"
48 #endif
49 #ifdef HAS_PERFORMANCE_SAMPLE
50 #include "../../xbmc/utils/PerformanceSample.h"
51 #else
52 #define MEASURE_FUNCTION
53 #endif
54 #include "Settings.h"
55 #include "FileItem.h"
56
57 using namespace std;
58
59 void CSelectionStreams::Clear(StreamType type, StreamSource source)
60 {
61   CSingleLock lock(m_section);
62   for(int i=m_Streams.size()-1;i>=0;i--)
63   {
64     if(type && m_Streams[i].type != type)
65       continue;
66
67     if(source && m_Streams[i].source != source)
68       continue;
69
70     m_Streams.erase(m_Streams.begin() + i);
71   }
72 }
73
74 SelectionStream& CSelectionStreams::Get(StreamType type, int index)
75 {
76   CSingleLock lock(m_section);
77   int count = -1;
78   for(int i=0;i<(int)m_Streams.size();i++)
79   {
80     if(m_Streams[i].type != type)
81       continue;
82     count++;
83     if(count == index)
84       return m_Streams[i];    
85   }
86   CLog::Log(LOGERROR, "%s - failed to get stream", __FUNCTION__);
87   return m_invalid;
88 }
89
90 int CSelectionStreams::IndexOf(StreamType type, int source, int id)
91 {
92   CSingleLock lock(m_section);
93   int count = -1;
94   for(int i=0;i<(int)m_Streams.size();i++)
95   {
96     if(type && m_Streams[i].type != type)
97       continue;
98     count++;
99     if(source && m_Streams[i].source != source)
100       continue;
101     if(id < 0)
102       continue;
103     if(m_Streams[i].id == id)
104       return count;
105   }
106   if(id < 0)
107     return count;
108   else
109     return -1;
110 }
111
112 int CSelectionStreams::IndexOf(StreamType type, CDVDPlayer& p)
113 {
114   if (p.m_pInputStream && p.m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
115   {
116     int id = -1;
117     if(type == STREAM_AUDIO)
118       id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveAudioStream();
119     else if(type == STREAM_VIDEO)
120       id = p.m_CurrentVideo.id;
121     else if(type == STREAM_SUBTITLE)
122       id = ((CDVDInputStreamNavigator*)p.m_pInputStream)->GetActiveSubtitleStream();
123
124     return IndexOf(type, STREAM_SOURCE_NAV, id);
125   }
126
127   if(type == STREAM_AUDIO)
128     return IndexOf(type, p.m_CurrentAudio.source, p.m_CurrentAudio.id);
129   else if(type == STREAM_VIDEO)
130     return IndexOf(type, p.m_CurrentVideo.source, p.m_CurrentVideo.id);
131   else if(type == STREAM_SUBTITLE)
132     return IndexOf(type, p.m_CurrentSubtitle.source, p.m_CurrentSubtitle.id);
133
134   return -1;
135 }
136
137 int CSelectionStreams::Source(StreamSource source, std::string filename)
138 {
139   CSingleLock lock(m_section);
140   int index = source - 1;
141   for(int i=0;i<(int)m_Streams.size();i++)
142   {
143     SelectionStream &s = m_Streams[i];
144     if(STREAM_SOURCE_MASK(s.source) != source)
145       continue;
146     // if it already exists, return same
147     if(s.filename == filename)
148       return s.source;
149     if(index < s.source)
150       index = s.source;
151   }
152   // return next index
153   return index + 1;
154 }
155
156 void CSelectionStreams::Update(SelectionStream& s)
157 {
158   CSingleLock lock(m_section);
159   int index = IndexOf(s.type, s.source, s.id);
160   if(index >= 0)
161     Get(s.type, index) = s;
162   else
163     m_Streams.push_back(s);
164 }
165
166 void CSelectionStreams::Update(CDVDInputStream* input, CDVDDemux* demuxer)
167 {
168   if(input && input->IsStreamType(DVDSTREAM_TYPE_DVD))
169   {    
170     CDVDInputStreamNavigator* nav = (CDVDInputStreamNavigator*)input;
171     string filename = nav->GetFileName();
172     int source = Source(STREAM_SOURCE_NAV, filename);
173
174     int count;
175     count = nav->GetAudioStreamCount();
176     for(int i=0;i<count;i++)
177     {
178       SelectionStream s;
179       s.source   = source;
180       s.type     = STREAM_AUDIO;
181       s.id       = i;
182       s.name     = nav->GetAudioStreamLanguage(i);
183       s.filename = filename;
184       Update(s);
185     }
186
187     count = nav->GetSubTitleStreamCount();
188     for(int i=0;i<count;i++)
189     {
190       SelectionStream s;
191       s.source   = source;
192       s.type     = STREAM_SUBTITLE;
193       s.id       = i;
194       s.name     = nav->GetSubtitleStreamLanguage(i);
195       s.filename = filename;
196       Update(s);
197     }
198   }
199   else if(demuxer)
200   {
201     string filename = demuxer->GetFileName();
202     int count = demuxer->GetNrOfStreams();
203     int source;
204     if(input) /* hack to know this is sub decoder */
205       source = Source(STREAM_SOURCE_DEMUX, filename);
206     else
207       source = Source(STREAM_SOURCE_DEMUX_SUB, filename);
208
209
210     for(int i=0;i<count;i++)
211     {
212       CDemuxStream* stream = demuxer->GetStream(i);
213       /* make sure stream is marked with right source */
214       stream->source = source;
215
216       SelectionStream s;
217       s.source   = source;
218       s.type     = stream->type;
219       s.id       = stream->iId;
220       s.language = stream->language;
221       s.filename = demuxer->GetFileName();
222       stream->GetStreamName(s.name);
223       if(stream->type == STREAM_AUDIO)
224       {
225         std::string type;
226         ((CDemuxStreamAudio*)stream)->GetStreamType(type);
227         if(type.length() > 0)
228         {
229           if(s.name.length() > 0)
230             s.name += " - ";
231           s.name += type;
232         }
233       }
234       Update(s);
235     }
236   }
237 }
238
239 CDVDPlayer::CDVDPlayer(IPlayerCallback& callback)
240     : IPlayer(callback),
241       CThread(),
242       m_CurrentAudio(STREAM_AUDIO),
243       m_CurrentVideo(STREAM_VIDEO),
244       m_CurrentSubtitle(STREAM_SUBTITLE),
245       m_dvdPlayerVideo(&m_clock, &m_overlayContainer),
246       m_dvdPlayerAudio(&m_clock),
247       m_dvdPlayerSubtitle(&m_overlayContainer),
248       m_messenger("player")
249 {
250   m_pDemuxer = NULL;
251   m_pSubtitleDemuxer = NULL;
252   m_pInputStream = NULL;
253
254   m_tmLastSeek = time(NULL);
255
256   m_hReadyEvent = CreateEvent(NULL, true, false, NULL);
257
258   InitializeCriticalSection(&m_critStreamSection);
259
260   memset(&m_dvd, 0, sizeof(DVDInfo));
261   m_dvd.iSelectedAudioStream = -1;
262   m_dvd.iSelectedSPUStream = -1;
263
264   m_State.Clear();
265   m_UpdateApplication = 0;
266
267   m_bAbortRequest = false;
268   m_errorCount = 0;
269   m_playSpeed = DVD_PLAYSPEED_NORMAL;
270   m_caching = false;
271   m_seeking = false;
272
273   m_pDlgCache = NULL;
274
275 #ifdef DVDDEBUG_MESSAGE_TRACKER
276   g_dvdMessageTracker.Init();
277 #endif
278 }
279
280 CDVDPlayer::~CDVDPlayer()
281 {
282   CloseFile();
283
284   CloseHandle(m_hReadyEvent);
285   DeleteCriticalSection(&m_critStreamSection);
286 #ifdef DVDDEBUG_MESSAGE_TRACKER
287   g_dvdMessageTracker.DeInit();
288 #endif
289 }
290
291 bool CDVDPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
292 {
293   try
294   {
295     if (m_pDlgCache)
296       m_pDlgCache->Close();
297
298     CStdString strHeader;
299     if (file.IsInternetStream())
300       strHeader = g_localizeStrings.Get(10214);
301
302     if(file.IsInternetStream())
303       m_pDlgCache = new CDlgCache(0, strHeader, file.GetLabel());
304     else if(!file.IsDVDFile(false, true) && !file.IsDVDImage() && !file.IsDVD())
305       m_pDlgCache = new CDlgCache(3000, strHeader, file.GetLabel());
306
307     CLog::Log(LOGNOTICE, "DVDPlayer: Opening: %s", file.m_strPath.c_str());
308
309     // if playing a file close it first
310     // this has to be changed so we won't have to close it.
311     if(ThreadHandle())
312       CloseFile();
313
314     m_bAbortRequest = false;
315     m_seeking = false;
316     SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
317
318     m_dvd.state = DVDSTATE_NORMAL;
319     m_dvd.iSelectedSPUStream = -1;
320     m_dvd.iSelectedAudioStream = -1;
321     m_State.Clear();
322     m_UpdateApplication = 0;
323     m_clock.Discontinuity(CLOCK_DISC_FULL);
324
325
326     // settings that should be set before opening the file
327     SetAVDelay(g_stSettings.m_currentVideoSettings.m_AudioDelay);
328
329     m_PlayerOptions = options;
330     m_item     = file;
331     m_content  = file.GetContentType();
332     m_filename = file.m_strPath;
333
334     ResetEvent(m_hReadyEvent);
335     Create();
336     WaitForSingleObject(m_hReadyEvent, INFINITE);
337
338     // Playback might have been stopped due to some error
339     if (m_bStop || m_bAbortRequest) 
340     {
341       if (m_pDlgCache)
342       {
343         m_pDlgCache->Close();
344         m_pDlgCache = NULL;
345       }
346       return false;
347     }
348
349    
350     return true;
351   }
352   catch(...)
353   {
354     CLog::Log(LOGERROR, "%s - Exception thrown on open", __FUNCTION__);
355     if (m_pDlgCache)
356     {
357       m_pDlgCache->Close();
358       m_pDlgCache = NULL;
359     }
360
361     return false;
362   }
363 }
364
365 bool CDVDPlayer::CloseFile()
366 {
367   CLog::Log(LOGNOTICE, "CDVDPlayer::CloseFile()");
368
369   // unpause the player
370   SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
371
372   // set the abort request so that other threads can finish up
373   m_bAbortRequest = true;
374
375   // tell demuxer to abort
376   if(m_pDemuxer)
377     m_pDemuxer->Abort();
378
379   if(m_pSubtitleDemuxer)
380     m_pSubtitleDemuxer->Abort();
381
382   CLog::Log(LOGNOTICE, "DVDPlayer: waiting for threads to exit");
383
384   // wait for the main thread to finish up
385   // since this main thread cleans up all other resources and threads
386   // we are done after the StopThread call
387   StopThread();
388
389   m_Edl.Reset();
390
391   CLog::Log(LOGNOTICE, "DVDPlayer: finished waiting");
392 #if defined(_LINUX) && defined(HAS_VIDEO_PLAYBACK)
393   g_renderManager.OnClose();
394 #endif
395   return true;
396 }
397
398 bool CDVDPlayer::IsPlaying() const
399 {
400   return !m_bStop;
401 }
402
403 void CDVDPlayer::OnStartup()
404 {
405   CThread::SetName("CDVDPlayer");
406   m_CurrentVideo.Clear();
407   m_CurrentAudio.Clear();
408   m_CurrentSubtitle.Clear();
409
410   m_messenger.Init();
411
412   g_dvdPerformanceCounter.EnableMainPerformance(ThreadHandle());
413 }
414
415 bool CDVDPlayer::OpenInputStream()
416 {
417   if(m_pInputStream)
418     SAFE_DELETE(m_pInputStream);
419
420   CLog::Log(LOGNOTICE, "Creating InputStream");
421
422   // correct the filename if needed
423   CStdString filename(m_filename);
424   if (filename.Find("dvd://") == 0 
425   ||  filename.CompareNoCase("d:\\video_ts\\video_ts.ifo") == 0
426   ||  filename.CompareNoCase("iso9660://video_ts/video_ts.ifo") == 0)
427   {
428 #ifdef _WIN32PC
429     m_filename = MEDIA_DETECT::CLibcdio::GetInstance()->GetDeviceFileName()+4;
430 #else
431     m_filename = MEDIA_DETECT::CLibcdio::GetInstance()->GetDeviceFileName();
432 #endif
433   }
434   
435   m_pInputStream = CDVDFactoryInputStream::CreateInputStream(this, m_filename, m_content);
436   if(m_pInputStream == NULL)
437   {
438     CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - unable to create input stream for [%s]", m_filename.c_str());
439     return false;
440   }
441   else
442     m_pInputStream->SetFileItem(m_item);
443
444   if (!m_pInputStream->Open(m_filename.c_str(), m_content))
445   {
446     CLog::Log(LOGERROR, "CDVDPlayer::OpenInputStream - error opening [%s]", m_filename.c_str());
447     return false;
448   }
449
450   // find any available external subtitles for non dvd files
451   if (!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) 
452   &&  !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
453   &&  !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
454   {
455     if(g_stSettings.m_currentVideoSettings.m_SubtitleOn)
456     {
457       // find any available external subtitles
458       std::vector<std::string> filenames;
459       CDVDFactorySubtitle::GetSubtitles(filenames, m_filename);
460       for(unsigned int i=0;i<filenames.size();i++)
461         AddSubtitleFile(filenames[i]);
462
463       g_stSettings.m_currentVideoSettings.m_SubtitleCached = true;
464     }
465
466     // look for any edl files
467     if (g_guiSettings.GetBool("videoplayer.editdecision") && !m_item.IsInternetStream())
468       m_Edl.ReadnCacheAny(m_filename);
469   }
470
471   return true;
472 }
473
474 bool CDVDPlayer::OpenDemuxStream()
475 {
476   if(m_pDemuxer)
477     SAFE_DELETE(m_pDemuxer);
478
479   CLog::Log(LOGNOTICE, "Creating Demuxer");
480
481   try
482   {
483     int attempts = 10;
484     while(!m_bStop && attempts-- > 0)
485     {
486       m_pDemuxer = CDVDFactoryDemuxer::CreateDemuxer(m_pInputStream);
487       if(!m_pDemuxer && m_pInputStream->NextStream())
488       {
489         CLog::Log(LOGDEBUG, "%s - New stream available from input, retry open", __FUNCTION__);
490         continue;
491       }
492       break;
493     }
494
495     if(!m_pDemuxer)
496     {
497       CLog::Log(LOGERROR, "%s - Error creating demuxer", __FUNCTION__);
498       return false;
499     }
500
501   }
502   catch(...)
503   {
504     CLog::Log(LOGERROR, "%s - Exception thrown when opeing demuxer", __FUNCTION__);
505     return false;
506   }
507
508   m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
509   m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
510   m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
511
512   return true;
513 }
514
515 void CDVDPlayer::OpenDefaultStreams()
516 {
517   int  count;
518   bool valid;
519   // open video stream
520   count = m_SelectionStreams.Count(STREAM_VIDEO);
521   valid = false;
522   for(int i = 0;i<count && !valid;i++)
523   {
524     SelectionStream& s = m_SelectionStreams.Get(STREAM_VIDEO, i);
525     if(OpenVideoStream(s.id, s.source))
526       valid = true;
527   }
528   if(!valid)
529     CloseVideoStream(true);
530
531   if(!m_PlayerOptions.video_only)
532   {
533     // open audio stream
534     count = m_SelectionStreams.Count(STREAM_AUDIO);
535     valid = false;
536     if(g_stSettings.m_currentVideoSettings.m_AudioStream >= 0 
537     && g_stSettings.m_currentVideoSettings.m_AudioStream < count)
538     {
539       SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, g_stSettings.m_currentVideoSettings.m_AudioStream);
540       if(OpenAudioStream(s.id, s.source))
541         valid = true;
542       else
543         CLog::Log(LOGWARNING, "%s - failed to restore selected audio stream (%d)", __FUNCTION__, g_stSettings.m_currentVideoSettings.m_AudioStream);
544     }
545
546     for(int i = 0; i<count && !valid; i++)
547     {
548       SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, i);
549       if(OpenAudioStream(s.id, s.source))
550         valid = true;
551     }
552     if(!valid)
553       CloseAudioStream(true);
554   }
555
556   // open subtitle stream
557   if(g_stSettings.m_currentVideoSettings.m_SubtitleOn && !m_PlayerOptions.video_only)
558   {
559     m_dvdPlayerVideo.EnableSubtitle(true);
560     count = m_SelectionStreams.Count(STREAM_SUBTITLE);
561     valid = false;
562     if(g_stSettings.m_currentVideoSettings.m_SubtitleStream >= 0 
563     && g_stSettings.m_currentVideoSettings.m_SubtitleStream < count)
564     {
565       SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, g_stSettings.m_currentVideoSettings.m_SubtitleStream);
566       if(OpenSubtitleStream(s.id, s.source))
567         valid = true;
568       else
569         CLog::Log(LOGWARNING, "%s - failed to restore selected subtitle stream (%d)", __FUNCTION__, g_stSettings.m_currentVideoSettings.m_SubtitleStream);
570     }
571
572     for(int i = 0;i<count && !valid; i++)
573     {
574       SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, i);
575       if(OpenSubtitleStream(s.id, s.source))
576         valid = true;
577     }
578     if(!valid)
579       CloseSubtitleStream(false);
580   }
581   else
582     m_dvdPlayerVideo.EnableSubtitle(false);
583
584 }
585
586 bool CDVDPlayer::ReadPacket(DemuxPacket*& packet, CDemuxStream*& stream)
587 {
588
589   // check if we should read from subtitle demuxer
590   if(m_dvdPlayerSubtitle.AcceptsData() && m_pSubtitleDemuxer )
591   {
592     if(m_pSubtitleDemuxer)
593       packet = m_pSubtitleDemuxer->Read();
594
595     if(packet)
596     {
597       if(packet->iStreamId < 0)
598         return true;
599
600       stream = m_pSubtitleDemuxer->GetStream(packet->iStreamId);
601       if (!stream)
602       {
603         CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
604         return false;
605       }
606       if(stream->source == STREAM_SOURCE_NONE)
607       {
608         m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX_SUB);
609         m_SelectionStreams.Update(NULL, m_pSubtitleDemuxer);
610       }
611       return true;
612     }
613   }
614
615   // read a data frame from stream.
616   if(m_pDemuxer)
617     packet = m_pDemuxer->Read();
618
619   if(packet)
620   {
621
622     // correct for timestamp errors, maybe should be inside demuxer
623     if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
624     {
625       CDVDInputStreamNavigator *pInput = static_cast<CDVDInputStreamNavigator*>(m_pInputStream);
626
627       if (packet->dts != DVD_NOPTS_VALUE)
628         packet->dts -= pInput->GetTimeStampCorrection();
629       if (packet->pts != DVD_NOPTS_VALUE)
630         packet->pts -= pInput->GetTimeStampCorrection();
631     }
632
633     // this groupId stuff is getting a bit messy, need to find a better way
634     // currently it is used to determine if a menu overlay is associated with a picture
635     // for dvd's we use as a group id, the current cell and the current title
636     // to be a bit more precise we alse count the number of disc's in case of a pts wrap back in the same cell / title
637     packet->iGroupId = m_pInputStream->GetCurrentGroupId();
638
639     if(packet->iStreamId < 0)
640       return true;
641
642     stream = m_pDemuxer->GetStream(packet->iStreamId);
643     if (!stream) 
644     {
645       CLog::Log(LOGERROR, "%s - Error demux packet doesn't belong to a valid stream", __FUNCTION__);
646       return false;
647     }
648     if(stream->source == STREAM_SOURCE_NONE)
649     {
650       m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_DEMUX);
651       m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
652     }
653     return true;
654   }
655   return false;
656 }
657
658 bool CDVDPlayer::IsValidStream(CCurrentStream& stream)
659 {
660   if(stream.id<0)
661     return true; // we consider non selected as valid
662
663   int source = STREAM_SOURCE_MASK(stream.source);
664   if(source == STREAM_SOURCE_TEXT)
665     return true;
666   if(source == STREAM_SOURCE_DEMUX_SUB)
667   {
668     CDemuxStream* st = m_pSubtitleDemuxer->GetStream(stream.id);
669     if(st == NULL || st->disabled)
670       return false;
671     if(st->type != stream.type)
672       return false;
673     return true;
674   }
675   if(source == STREAM_SOURCE_DEMUX)
676   {
677     CDemuxStream* st = m_pDemuxer->GetStream(stream.id);
678     if(st == NULL || st->disabled)
679       return false;
680     if(st->type != stream.type)
681       return false;
682
683     if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
684     {
685       if(stream.type == STREAM_AUDIO    && st->iPhysicalId != m_dvd.iSelectedAudioStream)
686         return false;
687       if(stream.type == STREAM_SUBTITLE && st->iPhysicalId != m_dvd.iSelectedSPUStream)
688         return false;
689     }
690
691     return true;
692   }
693
694   return false;
695 }
696
697 bool CDVDPlayer::IsBetterStream(CCurrentStream& current, CDemuxStream* stream)
698 {
699   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
700   {
701     int source_type;
702
703     source_type = STREAM_SOURCE_MASK(current.source);
704     if(source_type != STREAM_SOURCE_DEMUX 
705     && source_type != STREAM_SOURCE_NONE)
706       return false;
707
708     source_type = STREAM_SOURCE_MASK(stream->source);
709     if(source_type  != STREAM_SOURCE_DEMUX
710     || stream->type != current.type
711     || stream->iId  == current.id)
712       return false;
713
714     if(current.type == STREAM_AUDIO    && stream->iPhysicalId == m_dvd.iSelectedAudioStream)
715       return true;
716     if(current.type == STREAM_SUBTITLE && stream->iPhysicalId == m_dvd.iSelectedSPUStream)
717       return true;
718     if(current.type == STREAM_VIDEO    && current.id < 0)
719       return true;
720   }
721   else
722   {
723     if(stream->source == current.source 
724     && stream->iId    == current.id)
725       return false;
726
727     if(stream->disabled)
728       return false;
729
730     if(stream->type != current.type)
731       return false;
732
733     if(current.type == STREAM_SUBTITLE)
734       return false;
735
736     if(current.id < 0)
737       return true;
738   }
739   return false;
740 }
741
742 void CDVDPlayer::Process()
743 {
744   if (m_pDlgCache && m_pDlgCache->IsCanceled())
745     return;
746  
747   if (!OpenInputStream())
748   {
749     m_bAbortRequest = true;
750     return;
751   }
752
753   if (m_pDlgCache && m_pDlgCache->IsCanceled())
754     return;
755
756   if(m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
757   {
758     CLog::Log(LOGNOTICE, "DVDPlayer: playing a dvd with menu's");
759     m_PlayerOptions.starttime = 0;
760
761
762     if(m_PlayerOptions.state.size() > 0)
763       ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(m_PlayerOptions.state);
764     ((CDVDInputStreamNavigator*)m_pInputStream)->EnableSubtitleStream(g_stSettings.m_currentVideoSettings.m_SubtitleOn);
765
766     g_stSettings.m_currentVideoSettings.m_SubtitleCached = true;
767   }
768
769   if(!OpenDemuxStream())
770   {
771     m_bAbortRequest = true;
772     return;
773   }
774
775   if (m_pDlgCache && m_pDlgCache->IsCanceled())
776     return;
777
778   OpenDefaultStreams();
779
780   if( m_PlayerOptions.starttime > 0 )
781   {
782     double startpts = DVD_NOPTS_VALUE;
783     if(m_pDemuxer)
784     {
785       if (m_pDemuxer->SeekTime(m_PlayerOptions.starttime * 1000, false, &startpts))
786         CLog::Log(LOGDEBUG, "%s - starting demuxer from: %f", __FUNCTION__, m_PlayerOptions.starttime);
787       else
788         CLog::Log(LOGDEBUG, "%s - failed to start demuxing from %f", __FUNCTION__,  m_PlayerOptions.starttime);
789     }
790
791     if(m_pSubtitleDemuxer)
792     {
793       if(m_pSubtitleDemuxer->SeekTime(m_PlayerOptions.starttime * 1000, false, &startpts))
794         CLog::Log(LOGDEBUG, "%s - starting subtitle demuxer from: %f", __FUNCTION__, m_PlayerOptions.starttime);
795       else
796         CLog::Log(LOGDEBUG, "%s - failed to start subtitle demuxing from: %f", __FUNCTION__, m_PlayerOptions.starttime);
797     }
798   }
799
800   // allow renderer to switch to fullscreen if requested
801   m_dvdPlayerVideo.EnableFullscreen(m_PlayerOptions.fullscreen);
802
803   // make sure application know our info
804   UpdateApplication(0);
805   UpdatePlayState(0);
806
807   // we are done initializing now, set the readyevent
808   SetEvent(m_hReadyEvent);
809
810   if(m_PlayerOptions.identify == false)
811     m_callback.OnPlayBackStarted();
812
813   if (m_pDlgCache && m_pDlgCache->IsCanceled())
814     return;
815
816   if (m_pDlgCache)
817     m_pDlgCache->SetMessage(g_localizeStrings.Get(10213));
818
819   if(!m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) 
820   && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV)
821   && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_HTSP))
822     SetCaching(true);
823
824   while (!m_bAbortRequest)
825   {
826     // handle messages send to this thread, like seek or demuxer reset requests
827     HandleMessages();
828
829     if(m_bAbortRequest)
830       break;
831
832     // should we open a new input stream?
833     if(!m_pInputStream)
834     {
835       if (OpenInputStream() == false)
836         break;
837     }
838
839     // should we open a new demuxer?
840     if(!m_pDemuxer)
841     {
842       if (m_pInputStream->NextStream() == false)
843         break;
844
845       if (m_pInputStream->IsEOF())
846         break;
847
848       if (OpenDemuxStream() == false)
849         break;
850
851       OpenDefaultStreams();
852       UpdateApplication(0);
853       UpdatePlayState(0);
854     }
855
856     // handle eventual seeks due tp playspeed
857     HandlePlaySpeed();
858
859     // update player state
860     UpdatePlayState(200);
861
862     // update application with our state
863     UpdateApplication(1000);
864
865     // if the queues are full, no need to read more
866     if ((!m_dvdPlayerAudio.AcceptsData() && m_CurrentAudio.id >= 0)
867     ||  (!m_dvdPlayerVideo.AcceptsData() && m_CurrentVideo.id >= 0))
868     {
869       Sleep(10);
870       if (m_caching)
871         SetCaching(false);
872       continue;
873     }
874
875     DemuxPacket* pPacket = NULL;
876     CDemuxStream *pStream = NULL;
877     ReadPacket(pPacket, pStream);
878     if (pPacket && !pStream)
879     {
880       /* probably a empty packet, just free it and move on */
881       CDVDDemuxUtils::FreeDemuxPacket(pPacket); 
882       continue;
883     }
884
885     if (!pPacket)
886     {
887       // when paused, demuxer could be be returning empty
888       if (m_playSpeed == DVD_PLAYSPEED_PAUSE)
889         continue;
890
891       // if there is another stream available, let 
892       // player reopen demuxer
893       if(m_pInputStream->NextStream())
894       {
895         SAFE_DELETE(m_pDemuxer);
896         continue;
897       }
898
899       if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
900       {
901         CDVDInputStreamNavigator* pStream = static_cast<CDVDInputStreamNavigator*>(m_pInputStream);
902
903         // stream is holding back data until demuxer has flushed
904         if(pStream->IsHeld())
905         {
906           pStream->SkipHold();
907           continue;
908         }
909
910         // stills will be skipped
911         if(m_dvd.state == DVDSTATE_STILL)
912         {
913           if (m_dvd.iDVDStillTime > 0)
914           {
915             if (GetTickCount() >= (m_dvd.iDVDStillStartTime + m_dvd.iDVDStillTime))
916             {
917               m_dvd.iDVDStillTime = 0;
918               m_dvd.iDVDStillStartTime = 0;
919               m_dvd.state = DVDSTATE_NORMAL;
920               pStream->SkipStill();
921               continue;
922             }
923           }
924           Sleep(100);
925           continue;
926         }
927       }
928
929       // if we are caching, start playing it agian
930       if (m_caching && !m_bAbortRequest)
931         SetCaching(false);
932
933       // while players are still playing, keep going to allow seekbacks
934       if(m_dvdPlayerAudio.m_messageQueue.GetDataSize() > 0 
935       || m_dvdPlayerVideo.m_messageQueue.GetDataSize() > 0)
936       {
937         // audio must be closed to finish last data
938         if(m_dvdPlayerAudio.m_messageQueue.GetDataSize() == 0 && m_CurrentAudio.id >= 0)
939           CloseAudioStream(true);
940
941         Sleep(100);
942         continue;
943       }
944
945       if (m_pInputStream->IsEOF()) 
946         break;
947
948       // any demuxer supporting non blocking reads, should return empty packates
949       CLog::Log(LOGINFO, "%s - eof reading from demuxer", __FUNCTION__);
950
951       // ignore this for dvd's, to allow continuation on errors
952       if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
953         continue;
954
955       break;
956     }
957
958     // it's a valid data packet, reset error counter
959     m_errorCount = 0;
960
961     try
962     {
963       if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
964       {
965         // check so dvdnavigator didn't want us to close stream,
966         // we allow lingering invalid audio/subtitle streams here to let player pass vts/cell borders more cleanly
967         if (m_dvd.iSelectedAudioStream < 0 && m_CurrentAudio.id >= 0) CloseAudioStream( true );
968         if (m_dvd.iSelectedSPUStream < 0 && m_CurrentVideo.id >= 0)   CloseSubtitleStream( true );
969       }
970
971       // check so that none of our streams has become invalid
972       if (!IsValidStream(m_CurrentAudio))    CloseAudioStream(true);
973       if (!IsValidStream(m_CurrentVideo))    CloseVideoStream(true);
974       if (!IsValidStream(m_CurrentSubtitle)) CloseSubtitleStream(true);
975
976       // check if there is any better stream to use (normally for dvd's)
977       if ( !m_PlayerOptions.video_only )
978       {
979         // Do not reopen non-video streams if we're in video-only mode
980         if (IsBetterStream(m_CurrentAudio,    pStream)) OpenAudioStream(pStream->iId, pStream->source);
981         if (IsBetterStream(m_CurrentSubtitle, pStream)) OpenSubtitleStream(pStream->iId, pStream->source);
982       }
983
984       if (IsBetterStream(m_CurrentVideo,    pStream)) OpenVideoStream(pStream->iId, pStream->source);
985     }
986     catch (...)
987     {
988       CLog::Log(LOGERROR, "%s - Exception thrown when attempting to open stream", __FUNCTION__);
989       break;
990     }
991
992     // process the packet
993     ProcessPacket(pStream, pPacket);
994
995     // present the cache dialog until playback actually started
996     if (m_pDlgCache)
997     {
998       if (m_pDlgCache->IsCanceled())
999       {
1000         m_bAbortRequest = true;
1001         break;
1002       }
1003
1004       if (m_caching)
1005       {
1006         m_pDlgCache->ShowProgressBar(true);
1007         m_pDlgCache->SetPercentage(GetCacheLevel());
1008       }
1009       else if (GetTime() > 500) // movie started to play
1010       {
1011         m_pDlgCache->Close();
1012         m_pDlgCache = NULL;
1013       }
1014     }
1015
1016   }
1017
1018   // playback ended, make sure anything buffered is displayed
1019   if (m_pDlgCache)
1020   {
1021     m_pDlgCache->Close();
1022     m_pDlgCache = NULL;
1023   }
1024 }
1025
1026 void CDVDPlayer::ProcessPacket(CDemuxStream* pStream, DemuxPacket* pPacket)
1027 {
1028     /* process packet if it belongs to selected stream. for dvd's down't allow automatic opening of streams*/
1029     LockStreams();
1030
1031     try
1032     {
1033       if (pPacket->iStreamId == m_CurrentAudio.id && pStream->source == m_CurrentAudio.source && pStream->type == STREAM_AUDIO)
1034         ProcessAudioData(pStream, pPacket);
1035       else if (pPacket->iStreamId == m_CurrentVideo.id && pStream->source == m_CurrentVideo.source && pStream->type == STREAM_VIDEO)
1036         ProcessVideoData(pStream, pPacket);
1037       else if (pPacket->iStreamId == m_CurrentSubtitle.id && pStream->source == m_CurrentSubtitle.source && pStream->type == STREAM_SUBTITLE)
1038         ProcessSubData(pStream, pPacket);
1039       else
1040       {
1041         pStream->SetDiscard(AVDISCARD_ALL);
1042         CDVDDemuxUtils::FreeDemuxPacket(pPacket); // free it since we won't do anything with it
1043       }
1044     }
1045     catch(...)
1046     {
1047       CLog::Log(LOGERROR, "%s - Exception thrown when processing demux packet", __FUNCTION__);
1048     }
1049
1050     UnlockStreams();
1051 }
1052
1053 void CDVDPlayer::ProcessAudioData(CDemuxStream* pStream, DemuxPacket* pPacket)
1054 {
1055   if (m_CurrentAudio.stream != (void*)pStream)
1056   {
1057     /* check so that dmuxer hints or extra data hasn't changed */
1058     /* if they have, reopen stream */
1059
1060     if (m_CurrentAudio.hint != CDVDStreamInfo(*pStream, true))
1061     { 
1062       // we don't actually have to close audiostream here first, as 
1063       // we could send it as a stream message. only problem 
1064       // is how to notify player if a stream change failed.
1065       CloseAudioStream( true );
1066       OpenAudioStream( pPacket->iStreamId, pStream->source );
1067     }
1068
1069     m_CurrentAudio.stream = (void*)pStream;
1070   }
1071
1072   // check if we are too slow and need to recache
1073   if(CheckStartCaching(m_CurrentAudio) && m_dvdPlayerAudio.IsStalled())
1074     SetCaching(true);
1075
1076   CheckContinuity(m_CurrentAudio, pPacket);
1077   if(pPacket->dts != DVD_NOPTS_VALUE)
1078     m_CurrentAudio.dts = pPacket->dts;
1079   else if(pPacket->pts != DVD_NOPTS_VALUE)
1080     m_CurrentAudio.dts = pPacket->pts;
1081
1082   bool drop = false;
1083   if (CheckPlayerInit(m_CurrentAudio, DVDPLAYER_AUDIO))
1084     drop = true;
1085
1086   if (CheckSceneSkip(m_CurrentAudio))
1087     drop = true;
1088
1089   m_dvdPlayerAudio.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1090 }
1091
1092 void CDVDPlayer::ProcessVideoData(CDemuxStream* pStream, DemuxPacket* pPacket)
1093 {
1094   if (m_CurrentVideo.stream != (void*)pStream)
1095   {
1096     /* check so that dmuxer hints or extra data hasn't changed */
1097     /* if they have reopen stream */
1098
1099     if (m_CurrentVideo.hint != CDVDStreamInfo(*pStream, true))
1100     {
1101       CloseVideoStream(true);
1102       OpenVideoStream(pPacket->iStreamId, pStream->source);
1103     }
1104
1105     m_CurrentVideo.stream = (void*)pStream;
1106   }
1107
1108   // check if we are too slow and need to recache
1109   if(CheckStartCaching(m_CurrentVideo) && m_dvdPlayerVideo.IsStalled())
1110     SetCaching(true);
1111
1112   if( pPacket->iSize != 4) //don't check the EOF_SEQUENCE of stillframes
1113   {
1114     CheckContinuity(m_CurrentVideo, pPacket);
1115     if(pPacket->dts != DVD_NOPTS_VALUE)
1116       m_CurrentVideo.dts = pPacket->dts;
1117     else if(pPacket->pts != DVD_NOPTS_VALUE)
1118       m_CurrentVideo.dts = pPacket->pts;
1119   }
1120
1121   bool drop = false;
1122   if (CheckPlayerInit(m_CurrentVideo, DVDPLAYER_VIDEO))
1123     drop = true;
1124
1125   if (CheckSceneSkip(m_CurrentAudio))
1126     drop = true;
1127
1128   m_dvdPlayerVideo.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1129 }
1130
1131 void CDVDPlayer::ProcessSubData(CDemuxStream* pStream, DemuxPacket* pPacket)
1132 {
1133   if (m_CurrentSubtitle.stream != (void*)pStream)
1134   {
1135     /* check so that dmuxer hints or extra data hasn't changed */
1136     /* if they have reopen stream */
1137
1138     if (m_CurrentSubtitle.hint != CDVDStreamInfo(*pStream, true))
1139     {
1140         CloseSubtitleStream(true);
1141         OpenSubtitleStream(pPacket->iStreamId, pStream->source);
1142     }
1143
1144     m_CurrentSubtitle.stream = (void*)pStream;
1145   }
1146   if(pPacket->dts != DVD_NOPTS_VALUE)
1147     m_CurrentSubtitle.dts = pPacket->dts;
1148   else if(pPacket->pts != DVD_NOPTS_VALUE)
1149     m_CurrentSubtitle.dts = pPacket->pts;
1150
1151   bool drop = false;
1152   if (CheckPlayerInit(m_CurrentSubtitle, DVDPLAYER_SUBTITLE))
1153     drop = true;
1154
1155   if (CheckSceneSkip(m_CurrentAudio))
1156     drop = true;
1157
1158   m_dvdPlayerSubtitle.SendMessage(new CDVDMsgDemuxerPacket(pPacket, drop));
1159
1160   if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))    
1161     m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
1162 }
1163
1164 void CDVDPlayer::HandlePlaySpeed()
1165 {
1166   if(GetPlaySpeed() != DVD_PLAYSPEED_NORMAL && GetPlaySpeed() != DVD_PLAYSPEED_PAUSE)
1167   {
1168     if (IsInMenu())
1169     {
1170       // this can't be done in menu
1171       SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1172
1173     }
1174     else if (m_CurrentVideo.id >= 0 
1175           &&  m_CurrentVideo.inited == true
1176           &&  m_SpeedState.lastpts  != m_dvdPlayerVideo.GetCurrentPts()
1177           &&  m_SpeedState.lasttime != GetTime())
1178     {
1179       m_SpeedState.lastpts  = m_dvdPlayerVideo.GetCurrentPts();
1180       m_SpeedState.lasttime = GetTime();
1181       // check how much off clock video is when ff/rw:ing
1182       // a problem here is that seeking isn't very accurate
1183       // and since the clock will be resynced after seek
1184       // we might actually not really be playing at the wanted
1185       // speed. we'd need to have some way to not resync the clock
1186       // after a seek to remember timing. still need to handle
1187       // discontinuities somehow
1188
1189       // when seeking, give the player a headstart to make sure 
1190       // the time it takes to seek doesn't make a difference.
1191       double error;
1192       error  = m_clock.GetClock() - m_SpeedState.lastpts;
1193       error *= m_playSpeed / abs(m_playSpeed);
1194
1195       if(error > DVD_MSEC_TO_TIME(1000))
1196       {
1197         CLog::Log(LOGDEBUG, "CDVDPlayer::Process - Seeking to catch up");
1198         __int64 iTime = (__int64)(m_SpeedState.lasttime + 500.0 * m_playSpeed / DVD_PLAYSPEED_NORMAL);
1199         m_messenger.Put(new CDVDMsgPlayerSeek(iTime, (GetPlaySpeed() < 0), true, false));
1200       }
1201     }
1202   }
1203 }
1204
1205 bool CDVDPlayer::CheckStartCaching(CCurrentStream& current)
1206 {
1207   return !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) 
1208       && !m_caching && m_playSpeed == DVD_PLAYSPEED_NORMAL
1209       && current.inited;
1210 }
1211
1212 bool CDVDPlayer::CheckPlayerInit(CCurrentStream& current, unsigned int source)
1213 {
1214   if(current.startsync)
1215   {
1216     if ((current.startpts < current.dts && current.dts != DVD_NOPTS_VALUE)
1217     ||  (current.startpts == DVD_NOPTS_VALUE))
1218     {
1219       SendPlayerMessage(current.startsync, source);
1220
1221       current.startpts = DVD_NOPTS_VALUE;
1222       current.startsync = NULL;
1223     }
1224     else if((current.startpts - current.dts) > DVD_SEC_TO_TIME(20)
1225          &&  current.dts != DVD_NOPTS_VALUE)
1226     {
1227       CLog::Log(LOGDEBUG, "%s - too far to decode before finishing seek", __FUNCTION__);
1228       if(m_CurrentAudio.startpts != DVD_NOPTS_VALUE)
1229         m_CurrentAudio.startpts = current.dts;
1230       if(m_CurrentVideo.startpts != DVD_NOPTS_VALUE)
1231         m_CurrentVideo.startpts = current.dts;
1232       if(m_CurrentSubtitle.startpts != DVD_NOPTS_VALUE)
1233         m_CurrentSubtitle.startpts = current.dts;
1234     }
1235   }
1236
1237   // await start sync to be finished
1238   if(current.startsync)
1239   {
1240     CLog::Log(LOGDEBUG, "%s - dropping packet type:%d dts:%f to get to start point at %f", __FUNCTION__, source,  current.dts, current.startpts);
1241     return true;
1242   }
1243
1244
1245   //If this is the first packet after a discontinuity, send it as a resync
1246   if (current.inited == false && current.dts != DVD_NOPTS_VALUE)
1247   {
1248     current.inited   = true;
1249     current.startpts = current.dts;
1250
1251     bool setclock = false;
1252     if(m_playSpeed == DVD_PLAYSPEED_NORMAL)
1253     {
1254       if(     source == DVDPLAYER_AUDIO)
1255         setclock = !m_CurrentVideo.inited;
1256       else if(source == DVDPLAYER_VIDEO)
1257         setclock = !m_CurrentAudio.inited;
1258     }
1259     else
1260     {
1261       if(source == DVDPLAYER_VIDEO)
1262         setclock = true;
1263     }
1264
1265     double starttime = current.startpts;
1266     if(m_CurrentAudio.inited 
1267     && m_CurrentAudio.startpts != DVD_NOPTS_VALUE 
1268     && m_CurrentAudio.startpts < starttime)
1269       starttime = m_CurrentAudio.startpts;
1270     if(m_CurrentVideo.inited 
1271     && m_CurrentVideo.startpts != DVD_NOPTS_VALUE
1272     && m_CurrentVideo.startpts < starttime)
1273       starttime = m_CurrentVideo.startpts;
1274
1275     starttime = current.startpts - starttime;
1276     if(starttime > 0)
1277     {
1278       if(starttime > DVD_SEC_TO_TIME(2))
1279         CLog::Log(LOGWARNING, "CDVDPlayer::CheckPlayerInit(%d) - Ignoring too large delay of %f", source, starttime);
1280       else
1281         SendPlayerMessage(new CDVDMsgDouble(CDVDMsg::GENERAL_DELAY, starttime), source);
1282     }
1283
1284     SendPlayerMessage(new CDVDMsgGeneralResync(current.dts, setclock), source);
1285   }
1286   return false;
1287 }
1288
1289 void CDVDPlayer::CheckContinuity(CCurrentStream& current, DemuxPacket* pPacket)
1290 {
1291   if (m_playSpeed < DVD_PLAYSPEED_PAUSE)
1292     return;
1293
1294   if( pPacket->dts == DVD_NOPTS_VALUE )
1295     return;
1296
1297   if (current.type == STREAM_VIDEO
1298   && m_CurrentAudio.dts != DVD_NOPTS_VALUE 
1299   && m_CurrentVideo.dts != DVD_NOPTS_VALUE)
1300   {
1301     /* check for looping stillframes on non dvd's, dvd's will be detected by long duration check later */
1302     if( m_pInputStream && !m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1303     {
1304       /* special case for looping stillframes THX test discs*/
1305       /* only affect playback when not from dvd */
1306       if( (m_CurrentAudio.dts > m_CurrentVideo.dts + DVD_MSEC_TO_TIME(200)) 
1307       && (m_CurrentVideo.dts == pPacket->dts) )
1308       {
1309         CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - Detected looping stillframe");
1310         SyncronizePlayers(SYNCSOURCE_VIDEO);
1311         return;
1312       }
1313     }
1314
1315     /* if we haven't received video for a while, but we have been */
1316     /* getting audio much more recently, make sure video wait's  */
1317     /* this occurs especially on thx test disc */
1318     if( (pPacket->dts > m_CurrentVideo.dts + DVD_MSEC_TO_TIME(200)) 
1319      && (pPacket->dts < m_CurrentAudio.dts + DVD_MSEC_TO_TIME(50)) )
1320     {
1321       CLog::Log(LOGDEBUG, "CDVDPlayer::CheckContinuity - Potential long duration frame");
1322       SyncronizePlayers(SYNCSOURCE_VIDEO);
1323       return;
1324     }
1325   }
1326
1327   double mindts, maxdts;
1328   if(m_CurrentAudio.dts == DVD_NOPTS_VALUE)
1329     maxdts = mindts = m_CurrentVideo.dts;
1330   else if(m_CurrentVideo.dts == DVD_NOPTS_VALUE)
1331     maxdts = mindts = m_CurrentAudio.dts;
1332   else
1333   {
1334     maxdts = max(m_CurrentAudio.dts, m_CurrentVideo.dts);
1335     mindts = min(m_CurrentAudio.dts, m_CurrentVideo.dts);
1336   }
1337
1338   /* if we don't have max and min, we can't do anything more */
1339   if( mindts == DVD_NOPTS_VALUE || maxdts == DVD_NOPTS_VALUE )
1340     return;
1341
1342   /* warn if dts is moving backwords */
1343   if(current.dts != DVD_NOPTS_VALUE && pPacket->dts < current.dts)
1344     CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - wrapback of stream:%d, prev:%f, curr:%f, diff:%f"
1345                         , current.type, current.dts, pPacket->dts, pPacket->dts - current.dts);
1346
1347   /* if video player is rendering a stillframe, we need to make sure */
1348   /* audio has finished processing it's data otherwise it will be */
1349   /* displayed too early */
1350
1351   if( pPacket->dts < mindts - DVD_MSEC_TO_TIME(100) && current.inited)
1352   {
1353     CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - resyncing due to stream wrapback (%d)"
1354                         , current.type);
1355     if (m_dvdPlayerVideo.IsStalled() && m_CurrentVideo.dts != DVD_NOPTS_VALUE)
1356       SyncronizePlayers(SYNCSOURCE_VIDEO);
1357     else if (m_dvdPlayerAudio.IsStalled() && m_CurrentAudio.dts != DVD_NOPTS_VALUE)
1358       SyncronizePlayers(SYNCSOURCE_AUDIO);
1359
1360     m_CurrentAudio.inited = false;
1361     m_CurrentVideo.inited = false;
1362     m_CurrentSubtitle.inited = false;
1363   }
1364
1365   /* stream jump forward */
1366   if( pPacket->dts > maxdts + DVD_MSEC_TO_TIME(1000) && current.inited)
1367   {
1368     CLog::Log(LOGWARNING, "CDVDPlayer::CheckContinuity - stream forward jump detected (%d)"
1369                         , current.type);
1370     /* normally don't need to sync players since video player will keep playing at normal fps */
1371     /* after a discontinuity */
1372     //SyncronizePlayers(dts, pts, MSGWAIT_ALL);
1373     m_CurrentAudio.inited = false;
1374     m_CurrentVideo.inited = false;
1375     m_CurrentSubtitle.inited = false;
1376   }
1377
1378 }
1379
1380 bool CDVDPlayer::CheckSceneSkip(CCurrentStream& current)
1381 {
1382   CEdl::Cut cut;
1383
1384   if(!m_Edl.HaveCutpoints())
1385     return false;
1386
1387   if(current.dts == DVD_NOPTS_VALUE)
1388     return false;
1389
1390   if (m_Edl.InCutpoint(DVD_TIME_TO_MSEC(current.dts), &cut) && cut.CutAction == CEdl::CUT)
1391   {
1392     // check if both streams are in cut position, if they are do the seek
1393     if (m_CurrentAudio.id >= 0 && m_CurrentAudio.dts != DVD_NOPTS_VALUE && m_CurrentAudio.dts > DVD_MSEC_TO_TIME(cut.CutStart)
1394     &&  m_CurrentVideo.id >= 0 && m_CurrentVideo.dts != DVD_NOPTS_VALUE && m_CurrentVideo.dts > DVD_MSEC_TO_TIME(cut.CutStart))
1395       m_messenger.Put(new CDVDMsgPlayerSeek(cut.CutEnd+1, false, false, true)); // seek past cutpoint
1396
1397     return true;
1398   }
1399   return false;
1400 }
1401
1402
1403 void CDVDPlayer::SyncronizeDemuxer(DWORD timeout)
1404 {
1405   if(CThread::ThreadId() == GetCurrentThreadId())
1406     return;
1407   if(!m_messenger.IsInited())
1408     return;
1409
1410   CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, 0);
1411   message->Acquire();  
1412   m_messenger.Put(message);
1413   message->Wait(&m_bStop, 0);
1414   message->Release();
1415 }
1416
1417 void CDVDPlayer::SyncronizePlayers(DWORD sources, double pts)
1418 {
1419   /* if we are awaiting a start sync, we can't sync here or we could deadlock */
1420   if(m_CurrentAudio.startsync 
1421   || m_CurrentVideo.startsync
1422   || m_CurrentSubtitle.startsync)
1423   {
1424     CLog::Log(LOGDEBUG, "%s - can't sync since we are already awaiting a sync", __FUNCTION__);
1425     return;
1426   }
1427
1428   /* we need a big timeout as audio queue is about 8seconds for 2ch ac3 */
1429   const int timeout = 10*1000; // in milliseconds
1430
1431   CDVDMsgGeneralSynchronize* message = new CDVDMsgGeneralSynchronize(timeout, sources);
1432   if (m_CurrentAudio.id >= 0)
1433   {
1434     message->Acquire();
1435     m_CurrentAudio.dts = DVD_NOPTS_VALUE;
1436     m_CurrentAudio.startpts  = pts;
1437     m_CurrentAudio.startsync = message;
1438   }
1439   if (m_CurrentVideo.id >= 0)
1440   {
1441     message->Acquire();
1442     m_CurrentVideo.dts = DVD_NOPTS_VALUE;
1443     m_CurrentVideo.startpts  = pts;
1444     m_CurrentVideo.startsync = message;
1445   }
1446 /* TODO - we have to rewrite the sync class, to not require
1447           all other players waiting for subtitle, should only
1448           be the oposite way
1449   if (m_CurrentSubtitle.id >= 0)
1450   {
1451     message->Acquire();
1452     m_CurrentSubtitle.dts = DVD_NOPTS_VALUE;
1453     m_CurrentSubtitle.startpts  = pts;
1454     m_CurrentSubtitle.startsync = message;
1455   }
1456 */
1457   message->Release();
1458 }
1459
1460 void CDVDPlayer::SendPlayerMessage(CDVDMsg* pMsg, unsigned int target)
1461 {
1462   if(target == DVDPLAYER_AUDIO)
1463     m_dvdPlayerAudio.SendMessage(pMsg);
1464   if(target == DVDPLAYER_VIDEO)
1465     m_dvdPlayerVideo.SendMessage(pMsg);
1466   if(target == DVDPLAYER_SUBTITLE)
1467     m_dvdPlayerSubtitle.SendMessage(pMsg);
1468 }
1469
1470 void CDVDPlayer::OnExit()
1471 {
1472   g_dvdPerformanceCounter.DisableMainPerformance();
1473
1474   if (m_pDlgCache)
1475     m_pDlgCache->SetMessage(g_localizeStrings.Get(10212));
1476
1477   try
1478   {
1479     CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit()");
1480
1481     // set event to inform openfile something went wrong in case openfile is still waiting for this event
1482     SetEvent(m_hReadyEvent);
1483     SetCaching(false);
1484
1485     // close each stream
1486     if (!m_bAbortRequest) CLog::Log(LOGNOTICE, "DVDPlayer: eof, waiting for queues to empty");
1487     if (m_CurrentAudio.id >= 0)
1488     {
1489       CLog::Log(LOGNOTICE, "DVDPlayer: closing audio stream");
1490       CloseAudioStream(!m_bAbortRequest);
1491     }
1492     if (m_CurrentVideo.id >= 0)
1493     {
1494       CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
1495       CloseVideoStream(!m_bAbortRequest);
1496     }
1497     if (m_CurrentSubtitle.id >= 0)
1498     {
1499       CLog::Log(LOGNOTICE, "DVDPlayer: closing video stream");
1500       CloseSubtitleStream(!m_bAbortRequest);
1501     }
1502     // destroy the demuxer
1503     if (m_pDemuxer)
1504     {
1505       CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting demuxer");
1506       delete m_pDemuxer;
1507     }
1508     m_pDemuxer = NULL;
1509
1510     if (m_pSubtitleDemuxer)
1511     {
1512       CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting subtitle demuxer");
1513       delete m_pSubtitleDemuxer;
1514     }
1515     m_pSubtitleDemuxer = NULL;
1516
1517     // destroy the inputstream
1518     if (m_pInputStream)
1519     {
1520       CLog::Log(LOGNOTICE, "CDVDPlayer::OnExit() deleting input stream");
1521       delete m_pInputStream;
1522     }
1523     m_pInputStream = NULL;
1524
1525     // clean up all selection streams
1526     m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NONE);
1527
1528     m_messenger.End();
1529
1530   }
1531   catch (...)
1532   {
1533     CLog::Log(LOGERROR, "%s - Exception thrown when trying to close down player, memory leak will follow", __FUNCTION__);
1534     m_pInputStream = NULL;
1535     m_pDemuxer = NULL;   
1536   }
1537   // set event to inform openfile something went wrong in case openfile is still waiting for this event
1538   SetEvent(m_hReadyEvent);
1539   if (m_pDlgCache)
1540   {
1541     m_pDlgCache->Close();
1542     m_pDlgCache = NULL;
1543   }
1544
1545   m_bStop = true;
1546   // if we didn't stop playing, advance to the next item in xbmc's playlist
1547   if(m_PlayerOptions.identify == false)
1548   {
1549     if (m_bAbortRequest)
1550       m_callback.OnPlayBackStopped();
1551     else
1552       m_callback.OnPlayBackEnded();
1553   }
1554 }
1555
1556 void CDVDPlayer::HandleMessages()
1557 {
1558   CDVDMsg* pMsg;
1559
1560   MsgQueueReturnCode ret = m_messenger.Get(&pMsg, 0);
1561
1562   while (ret == MSGQ_OK)
1563   {
1564     LockStreams();
1565
1566     try
1567     {
1568       if (pMsg->IsType(CDVDMsg::PLAYER_SEEK))
1569       {
1570         CPlayerSeek m_pause(this);
1571
1572         CDVDMsgPlayerSeek &msg(*((CDVDMsgPlayerSeek*)pMsg));
1573         double start = DVD_NOPTS_VALUE;
1574
1575         CLog::Log(LOGDEBUG, "demuxer seek to: %d", msg.GetTime());
1576         if (m_pDemuxer && m_pDemuxer->SeekTime(msg.GetTime(), msg.GetBackward(), &start))
1577         {
1578           CLog::Log(LOGDEBUG, "demuxer seek to: %d, success", msg.GetTime());
1579           if(m_pSubtitleDemuxer)
1580           {
1581             if(!m_pSubtitleDemuxer->SeekTime(msg.GetTime(), msg.GetBackward()))
1582               CLog::Log(LOGDEBUG, "failed to seek subtitle demuxer: %d, success", msg.GetTime());
1583           }
1584           FlushBuffers(!msg.GetFlush());
1585           if(msg.GetAccurate())
1586             SyncronizePlayers(SYNCSOURCE_ALL, start);
1587           else
1588             SyncronizePlayers(SYNCSOURCE_ALL, DVD_NOPTS_VALUE);
1589         }
1590         else
1591           CLog::Log(LOGWARNING, "error while seeking");
1592
1593         // set flag to indicate we have finished a seeking request
1594         g_infoManager.m_performingSeek = false;
1595       }
1596       else if (pMsg->IsType(CDVDMsg::PLAYER_SEEK_CHAPTER))
1597       {
1598         CPlayerSeek m_pause(this);
1599
1600         CDVDMsgPlayerSeekChapter &msg(*((CDVDMsgPlayerSeekChapter*)pMsg));
1601         double start = DVD_NOPTS_VALUE;
1602         
1603         // This should always be the case. 
1604         if(m_pDemuxer && m_pDemuxer->SeekChapter(msg.GetChapter(), &start))
1605         {
1606           FlushBuffers(false);
1607           SyncronizePlayers(SYNCSOURCE_ALL, start);
1608         }
1609       }
1610       else if (pMsg->IsType(CDVDMsg::DEMUXER_RESET))
1611       {
1612           m_CurrentAudio.stream = NULL;
1613           m_CurrentVideo.stream = NULL;
1614           m_CurrentSubtitle.stream = NULL;
1615
1616           // we need to reset the demuxer, probably because the streams have changed
1617           if(m_pDemuxer)
1618             m_pDemuxer->Reset();
1619           if(m_pSubtitleDemuxer)
1620             m_pSubtitleDemuxer->Reset();
1621       }
1622       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_AUDIOSTREAM))
1623       {
1624         CDVDMsgPlayerSetAudioStream* pMsg2 = (CDVDMsgPlayerSetAudioStream*)pMsg;        
1625
1626         SelectionStream& st = m_SelectionStreams.Get(STREAM_AUDIO, pMsg2->GetStreamId());
1627         if(st.source != STREAM_SOURCE_NONE)
1628         {
1629           if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1630           {
1631             CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
1632             if(pStream->SetActiveAudioStream(st.id))
1633             {
1634               m_dvd.iSelectedAudioStream = -1;
1635               CloseAudioStream(false);
1636             }
1637           }
1638           else
1639           {
1640             CloseAudioStream(false);
1641             OpenAudioStream(st.id, st.source);
1642           }
1643         }
1644       }
1645       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM))
1646       {
1647         CDVDMsgPlayerSetSubtitleStream* pMsg2 = (CDVDMsgPlayerSetSubtitleStream*)pMsg;
1648
1649         SelectionStream& st = m_SelectionStreams.Get(STREAM_SUBTITLE, pMsg2->GetStreamId());
1650         if(st.source != STREAM_SOURCE_NONE)
1651         {
1652           if(st.source == STREAM_SOURCE_NAV && m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1653           {
1654             CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
1655             if(pStream->SetActiveSubtitleStream(st.id))
1656             {
1657               m_dvd.iSelectedSPUStream = -1;
1658               CloseSubtitleStream(false);
1659             }
1660           }
1661           else
1662           {
1663             CloseSubtitleStream(false);
1664             OpenSubtitleStream(st.id, st.source);
1665           }
1666         }
1667       }
1668       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE))
1669       {
1670         CDVDMsgBool* pValue = (CDVDMsgBool*)pMsg;
1671
1672         m_dvdPlayerVideo.EnableSubtitle(pValue->m_value);
1673
1674         if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1675           static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->EnableSubtitleStream(pValue->m_value);
1676       }
1677       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_STATE))
1678       {
1679         CPlayerSeek m_pause(this);
1680
1681         CDVDMsgPlayerSetState* pMsgPlayerSetState = (CDVDMsgPlayerSetState*)pMsg;
1682
1683         if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
1684         {
1685           std::string s = pMsgPlayerSetState->GetState();
1686           ((CDVDInputStreamNavigator*)m_pInputStream)->SetNavigatorState(s);
1687           m_dvd.state = DVDSTATE_NORMAL;
1688           m_dvd.iDVDStillStartTime = 0;
1689           m_dvd.iDVDStillTime = 0;
1690         }
1691       }
1692       else if (pMsg->IsType(CDVDMsg::PLAYER_SET_RECORD))
1693       {
1694         if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_TV))
1695           static_cast<CDVDInputStreamTV*>(m_pInputStream)->Record(*(CDVDMsgBool*)pMsg);
1696       }
1697       else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH))
1698       {
1699         FlushBuffers(false);
1700       }
1701       else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED))
1702       {
1703         int speed = static_cast<CDVDMsgInt*>(pMsg)->m_value;
1704         
1705         // correct our current clock, as it would start going wrong otherwise
1706         if(m_State.timestamp > 0)
1707         {
1708           double offset;
1709           offset  = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
1710           offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
1711           if(offset >  1000) offset =  1000;
1712           if(offset < -1000) offset = -1000;
1713           m_State.time     += DVD_TIME_TO_MSEC(offset);
1714           m_State.timestamp =  CDVDClock::GetAbsoluteClock();
1715         }
1716
1717         // if playspeed is different then DVD_PLAYSPEED_NORMAL or DVD_PLAYSPEED_PAUSE
1718         // audioplayer, stops outputing audio to audiorendere, but still tries to
1719         // sleep an correct amount for each packet
1720         // videoplayer just plays faster after the clock speed has been increased
1721         // 1. disable audio
1722         // 2. skip frames and adjust their pts or the clock
1723         m_playSpeed = speed;
1724         m_caching = false;
1725         m_clock.SetSpeed(speed);
1726         m_dvdPlayerAudio.SetSpeed(speed);
1727         m_dvdPlayerVideo.SetSpeed(speed);
1728
1729         // TODO - we really shouldn't pause demuxer 
1730         //        until our buffers are somewhat filled
1731         if(m_pDemuxer)
1732           m_pDemuxer->SetSpeed(speed);
1733       } 
1734       else if (pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT) || pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_PREV))
1735       {
1736         CPlayerSeek m_pause(this);
1737
1738         CDVDInputStream::IChannel* input = dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream);
1739         if(input)
1740         {
1741           bool result;
1742           if(pMsg->IsType(CDVDMsg::PLAYER_CHANNEL_NEXT))
1743             result = input->NextChannel();
1744           else
1745             result = input->PrevChannel();
1746
1747           if(result)
1748           {
1749             FlushBuffers(false);
1750             CloseVideoStream(false);
1751             CloseAudioStream(false);
1752             CloseSubtitleStream(false);
1753             SAFE_DELETE(m_pDemuxer);
1754           }
1755         }
1756       }
1757       else if (pMsg->IsType(CDVDMsg::GENERAL_GUI_ACTION))
1758         OnAction(((CDVDMsgType<CAction>*)pMsg)->m_value);
1759     }
1760     catch (...)
1761     {
1762       CLog::Log(LOGERROR, "%s - Exception thrown when handling message", __FUNCTION__);
1763     }
1764     
1765     UnlockStreams();
1766     
1767     pMsg->Release();
1768     ret = m_messenger.Get(&pMsg, 0);
1769   }
1770 }
1771
1772 void CDVDPlayer::SetCaching(bool enabled)
1773 {
1774   if(m_caching == enabled)
1775     return;
1776
1777   if(enabled)
1778   {
1779     m_clock.SetSpeed(DVD_PLAYSPEED_PAUSE);
1780     m_dvdPlayerAudio.SetSpeed(DVD_PLAYSPEED_PAUSE);
1781     m_dvdPlayerVideo.SetSpeed(DVD_PLAYSPEED_PAUSE);
1782     m_caching = true;
1783   }
1784   else
1785   {
1786     m_clock.SetSpeed(m_playSpeed);
1787     m_dvdPlayerAudio.SetSpeed(m_playSpeed);
1788     m_dvdPlayerVideo.SetSpeed(m_playSpeed);
1789     m_caching = false;
1790
1791     if (m_pDlgCache)
1792     {
1793       m_pDlgCache->Close();
1794       m_pDlgCache = NULL;
1795     }
1796   }
1797 }
1798
1799 void CDVDPlayer::SetPlaySpeed(int speed)
1800 {
1801   m_messenger.Put(new CDVDMsgInt(CDVDMsg::PLAYER_SETSPEED, speed));
1802   m_dvdPlayerAudio.SetSpeed(speed);
1803   m_dvdPlayerVideo.SetSpeed(speed);
1804   SyncronizeDemuxer(100);
1805 }
1806
1807 void CDVDPlayer::Pause()
1808 {
1809   if(m_playSpeed != DVD_PLAYSPEED_PAUSE && m_caching)
1810   {
1811     SetCaching(false);
1812     return;
1813   }
1814
1815   // return to normal speed if it was paused before, pause otherwise
1816   if (m_playSpeed == DVD_PLAYSPEED_PAUSE) 
1817     SetPlaySpeed(DVD_PLAYSPEED_NORMAL);
1818   else 
1819     SetPlaySpeed(DVD_PLAYSPEED_PAUSE);
1820 }
1821
1822 bool CDVDPlayer::IsPaused() const
1823 {
1824   return (m_playSpeed == DVD_PLAYSPEED_PAUSE) || m_caching;
1825 }
1826
1827 bool CDVDPlayer::HasVideo() const
1828 {
1829   if (m_pInputStream)
1830   {
1831     if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) || m_CurrentVideo.id >= 0) return true;
1832   }
1833   return false;
1834 }
1835
1836 bool CDVDPlayer::HasAudio() const
1837 {
1838   return (m_CurrentAudio.id >= 0);
1839 }
1840
1841 bool CDVDPlayer::CanSeek()
1842 {
1843   return GetTotalTime() > 0;
1844 }
1845
1846 void CDVDPlayer::Seek(bool bPlus, bool bLargeStep)
1847 {
1848 #if 0
1849   // sadly this doesn't work for now, audio player must
1850   // drop packets at the same rate as we play frames
1851   if( m_playSpeed == DVD_PLAYSPEED_PAUSE && bPlus && !bLargeStep)
1852   {
1853     m_dvdPlayerVideo.StepFrame();
1854     return;
1855   }
1856 #endif
1857   if (bLargeStep && GetChapterCount() > 1)
1858   {
1859     if(bPlus)
1860       SeekChapter(GetChapter() + 1);
1861     else
1862       SeekChapter(GetChapter() - 1);
1863     return;
1864   }
1865
1866   __int64 seek;
1867   if (g_advancedSettings.m_videoUseTimeSeeking && GetTotalTime() > 2*g_advancedSettings.m_videoTimeSeekForwardBig)
1868   {
1869     if (bLargeStep)
1870       seek = bPlus ? g_advancedSettings.m_videoTimeSeekForwardBig : g_advancedSettings.m_videoTimeSeekBackwardBig;
1871     else
1872       seek = bPlus ? g_advancedSettings.m_videoTimeSeekForward : g_advancedSettings.m_videoTimeSeekBackward;
1873     seek *= 1000;
1874     seek += GetTime();
1875   }
1876   else
1877   {
1878     float percent;
1879     if (bLargeStep)
1880       percent = bPlus ? g_advancedSettings.m_videoPercentSeekForwardBig : g_advancedSettings.m_videoPercentSeekBackwardBig;
1881     else
1882       percent = bPlus ? g_advancedSettings.m_videoPercentSeekForward : g_advancedSettings.m_videoPercentSeekBackward;
1883     seek = (__int64)(GetTotalTimeInMsec()*(GetPercentage()+percent)/100);
1884   }
1885
1886   m_messenger.Put(new CDVDMsgPlayerSeek((int)seek, !bPlus, true, false));
1887   SyncronizeDemuxer(100);
1888   m_tmLastSeek = time(NULL);
1889 }
1890
1891 bool CDVDPlayer::SeekScene(bool bPlus)
1892 {
1893   __int64 iScenemarker;
1894   if( m_Edl.HaveScenes() && m_Edl.SeekScene(bPlus,&iScenemarker) )
1895   {
1896     m_messenger.Put(new CDVDMsgPlayerSeek((int)iScenemarker, false, false, true)); 
1897     SyncronizeDemuxer(100);
1898     return true;
1899   }
1900   return false;
1901 }
1902
1903 void CDVDPlayer::ToggleFrameDrop()
1904 {
1905   m_dvdPlayerVideo.EnableFrameDrop( !m_dvdPlayerVideo.IsFrameDropEnabled() );
1906 }
1907
1908 void CDVDPlayer::GetAudioInfo(CStdString& strAudioInfo)
1909 {
1910   CSingleLock lock(m_StateSection);
1911   strAudioInfo.Format("D( %s ), P( %s )", m_State.demux_audio.c_str()
1912                                         , m_dvdPlayerAudio.GetPlayerInfo().c_str());
1913 }
1914
1915 void CDVDPlayer::GetVideoInfo(CStdString& strVideoInfo)
1916 {
1917   CSingleLock lock(m_StateSection);
1918   strVideoInfo.Format("D( %s ), P( %s )", m_State.demux_video.c_str()
1919                                         , m_dvdPlayerVideo.GetPlayerInfo().c_str());
1920 }
1921
1922 void CDVDPlayer::GetGeneralInfo(CStdString& strGeneralInfo)
1923 {
1924   if (!m_bStop)
1925   {
1926     double dDelay = (double)m_dvdPlayerVideo.GetDelay() / DVD_TIME_BASE;
1927
1928     double apts = m_dvdPlayerAudio.GetCurrentPts();
1929     double vpts = m_dvdPlayerVideo.GetCurrentPts();
1930     double dDiff = 0;
1931     char cEdlStatus;
1932
1933     if( apts != DVD_NOPTS_VALUE && vpts != DVD_NOPTS_VALUE )
1934       dDiff = (apts - vpts) / DVD_TIME_BASE;
1935
1936     int iFramesDropped = m_dvdPlayerVideo.GetNrOfDroppedFrames();
1937     cEdlStatus = m_Edl.GetEdlStatus();
1938
1939     strGeneralInfo.Format("DVD Player ad:%6.3f, a/v:%6.3f, dropped:%d, cpu: %i%%. edl: %c source bitrate: %4.2f MBit/s", dDelay, dDiff, iFramesDropped, (int)(CThread::GetRelativeUsage()*100), cEdlStatus, (double)GetSourceBitrate() / (1024.0*1024.0));
1940   }
1941 }
1942
1943 void CDVDPlayer::SeekPercentage(float iPercent)
1944 {
1945   __int64 iTotalTime = GetTotalTimeInMsec();
1946
1947   if (!iTotalTime)
1948     return;
1949
1950   SeekTime((__int64)(iTotalTime * iPercent / 100));
1951   m_tmLastSeek = time(NULL);
1952 }
1953
1954 float CDVDPlayer::GetPercentage()
1955 {
1956   __int64 iTotalTime = GetTotalTimeInMsec();
1957
1958   if (!iTotalTime)
1959     return 0.0f;
1960
1961   return GetTime() * 100 / (float)iTotalTime;
1962 }
1963
1964 void CDVDPlayer::SetAVDelay(float fValue)
1965 {
1966   m_dvdPlayerVideo.SetDelay( (fValue * DVD_TIME_BASE) ) ;
1967 }
1968
1969 float CDVDPlayer::GetAVDelay()
1970 {
1971   return m_dvdPlayerVideo.GetDelay() / (float)DVD_TIME_BASE;
1972 }
1973
1974 void CDVDPlayer::SetSubTitleDelay(float fValue)
1975 {
1976   m_dvdPlayerVideo.SetSubtitleDelay(-fValue * DVD_TIME_BASE);
1977 }
1978
1979 float CDVDPlayer::GetSubTitleDelay()
1980 {
1981   return -m_dvdPlayerVideo.GetSubtitleDelay() / DVD_TIME_BASE;
1982 }
1983
1984 // priority: 1: libdvdnav, 2: external subtitles, 3: muxed subtitles
1985 int CDVDPlayer::GetSubtitleCount()
1986 {
1987   LockStreams();
1988   m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
1989   UnlockStreams();
1990   return m_SelectionStreams.Count(STREAM_SUBTITLE);
1991 }
1992
1993 int CDVDPlayer::GetSubtitle()
1994 {
1995   return m_SelectionStreams.IndexOf(STREAM_SUBTITLE, *this);
1996 }
1997
1998 void CDVDPlayer::GetSubtitleName(int iStream, CStdString &strStreamName)
1999 {
2000   strStreamName = "";
2001   SelectionStream& s = m_SelectionStreams.Get(STREAM_SUBTITLE, iStream);
2002   if(s.name.length() > 0)
2003     strStreamName = s.name;
2004   else
2005     strStreamName = "Unknown";
2006
2007   if(s.type == STREAM_NONE)
2008     strStreamName += "(Invalid)";
2009 }
2010
2011 void CDVDPlayer::SetSubtitle(int iStream)
2012 {
2013   m_messenger.Put(new CDVDMsgPlayerSetSubtitleStream(iStream));
2014 }
2015
2016 bool CDVDPlayer::GetSubtitleVisible()
2017 {
2018   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2019   {
2020     CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2021     if(pStream->IsInMenu())
2022       return g_stSettings.m_currentVideoSettings.m_SubtitleOn;
2023     else
2024       return pStream->IsSubtitleStreamEnabled();
2025   }
2026
2027   return m_dvdPlayerVideo.IsSubtitleEnabled();
2028 }
2029
2030 void CDVDPlayer::SetSubtitleVisible(bool bVisible)
2031 {
2032   g_stSettings.m_currentVideoSettings.m_SubtitleOn = bVisible;
2033   m_messenger.Put(new CDVDMsgBool(CDVDMsg::PLAYER_SET_SUBTITLESTREAM_VISIBLE, bVisible));
2034 }
2035
2036 int CDVDPlayer::GetAudioStreamCount()
2037 {
2038   LockStreams();
2039   m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2040   UnlockStreams();
2041   return m_SelectionStreams.Count(STREAM_AUDIO);
2042 }
2043
2044 int CDVDPlayer::GetAudioStream()
2045 {
2046   return m_SelectionStreams.IndexOf(STREAM_AUDIO, *this);
2047 }
2048
2049 void CDVDPlayer::GetAudioStreamName(int iStream, CStdString& strStreamName)
2050 {
2051   strStreamName = "";
2052   SelectionStream& s = m_SelectionStreams.Get(STREAM_AUDIO, iStream);
2053   if(s.name.length() > 0)
2054     strStreamName += s.name;
2055   else
2056     strStreamName += "Unknown";
2057
2058   if(s.type == STREAM_NONE)
2059     strStreamName += " (Invalid)";
2060 }
2061
2062 void CDVDPlayer::SetAudioStream(int iStream)
2063 {
2064   m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
2065   SyncronizeDemuxer(100);
2066 }
2067
2068 void CDVDPlayer::SeekTime(__int64 iTime)
2069 {
2070   m_messenger.Put(new CDVDMsgPlayerSeek((int)iTime, true, true, true));
2071   SyncronizeDemuxer(100);
2072   m_tmLastSeek = time(NULL);
2073 }
2074
2075 // return the time in milliseconds
2076 __int64 CDVDPlayer::GetTime()
2077 {
2078   CSingleLock lock(m_StateSection);
2079   double offset = 0;
2080   if(m_State.timestamp > 0)
2081   {
2082     offset  = CDVDClock::GetAbsoluteClock() - m_State.timestamp;
2083     offset *= m_playSpeed / DVD_PLAYSPEED_NORMAL;
2084     if(offset >  1000) offset =  1000;
2085     if(offset < -1000) offset = -1000;
2086   }
2087   return m_State.time + DVD_TIME_TO_MSEC(offset);
2088 }
2089
2090 // return length in msec
2091 __int64 CDVDPlayer::GetTotalTimeInMsec()
2092 {
2093   CSingleLock lock(m_StateSection);
2094   return m_State.time_total;
2095 }
2096
2097 // return length in seconds.. this should be changed to return in milleseconds throughout xbmc
2098 int CDVDPlayer::GetTotalTime()
2099 {
2100   return (int)(GetTotalTimeInMsec() / 1000);
2101 }
2102
2103 void CDVDPlayer::ToFFRW(int iSpeed)
2104 {
2105   // can't rewind in menu as seeking isn't possible
2106   // forward is fine
2107   if (iSpeed < 0 && IsInMenu()) return;
2108   SetPlaySpeed(iSpeed * DVD_PLAYSPEED_NORMAL);
2109 }
2110
2111 bool CDVDPlayer::OpenAudioStream(int iStream, int source)
2112 {
2113   CLog::Log(LOGNOTICE, "Opening audio stream: %i source: %i", iStream, source);
2114
2115   if (!m_pDemuxer)
2116     return false;
2117   
2118   CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2119   if (!pStream || pStream->disabled)
2120     return false;
2121
2122   if( m_CurrentAudio.id < 0 &&  m_CurrentVideo.id >= 0 )
2123   {
2124     // up until now we wheren't playing audio, but we did play video
2125     // this will change what is used to sync the dvdclock.
2126     // since the new audio data doesn't have to have any relation
2127     // to the current video data in the packet que, we have to
2128     // wait for it to empty
2129
2130     // this happens if a new cell has audio data, but previous didn't
2131     // and both have video data
2132
2133     SyncronizePlayers(SYNCSOURCE_AUDIO);
2134   }
2135
2136   CDVDStreamInfo hint(*pStream, true);
2137
2138   if(m_CurrentAudio.id    < 0 
2139   || m_CurrentAudio.hint != hint)
2140   {
2141     if(m_CurrentAudio.id >= 0)
2142     {
2143       CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
2144       CloseAudioStream(true);
2145     }
2146
2147     if (!m_dvdPlayerAudio.OpenStream( hint ))
2148     {
2149       /* mark stream as disabled, to disallaw further attempts*/
2150       CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2151       pStream->disabled = true;
2152       pStream->SetDiscard(AVDISCARD_ALL);
2153       return false;
2154     }
2155   }
2156   else
2157     m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2158
2159   /* store information about stream */
2160   m_CurrentAudio.id = iStream;
2161   m_CurrentAudio.source = source;
2162   m_CurrentAudio.hint = hint;
2163   m_CurrentAudio.stream = (void*)pStream;
2164
2165   /* audio normally won't consume full cpu, so let it have prio */
2166   m_dvdPlayerAudio.SetPriority(GetThreadPriority(*this)+1);
2167
2168   return true;
2169 }
2170
2171 bool CDVDPlayer::OpenVideoStream(int iStream, int source)
2172 {
2173   CLog::Log(LOGNOTICE, "Opening video stream: %i source: %i", iStream, source);
2174
2175   if (!m_pDemuxer)
2176     return false;
2177   
2178   CDemuxStream* pStream = m_pDemuxer->GetStream(iStream);
2179   if(!pStream && pStream->disabled)
2180     return false;
2181   pStream->SetDiscard(AVDISCARD_NONE);
2182
2183   CDVDStreamInfo hint(*pStream, true);
2184
2185   if( m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD) )
2186   {
2187     /* set aspect ratio as requested by navigator for dvd's */
2188     float aspect = static_cast<CDVDInputStreamNavigator*>(m_pInputStream)->GetVideoAspectRatio();
2189     if(aspect != 0.0)
2190       hint.aspect = aspect;
2191   }
2192
2193   if(m_CurrentVideo.id    < 0 
2194   || m_CurrentVideo.hint != hint)
2195   {
2196     if(m_CurrentVideo.id >= 0)
2197     {
2198       CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
2199       CloseVideoStream(true);
2200     }
2201
2202     if (!m_dvdPlayerVideo.OpenStream(hint))
2203     {
2204       /* mark stream as disabled, to disallaw further attempts */
2205       CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2206       pStream->disabled = true;
2207       pStream->SetDiscard(AVDISCARD_ALL);
2208       return false;
2209     }
2210   }
2211   else
2212     m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2213
2214   /* store information about stream */
2215   m_CurrentVideo.id = iStream;
2216   m_CurrentVideo.source = source;
2217   m_CurrentVideo.hint = hint;
2218   m_CurrentVideo.stream = (void*)pStream;
2219
2220   /* use same priority for video thread as demuxing thread, as */
2221   /* otherwise demuxer will starve if video consumes the full cpu */
2222   m_dvdPlayerVideo.SetPriority(GetThreadPriority(*this));
2223   return true;
2224
2225 }
2226
2227 bool CDVDPlayer::OpenSubtitleStream(int iStream, int source)
2228 {
2229   CLog::Log(LOGNOTICE, "Opening Subtitle stream: %i source: %i", iStream, source);
2230
2231   CDemuxStream* pStream = NULL;
2232   std::string filename;
2233   CDVDStreamInfo hint;
2234
2235   if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_DEMUX_SUB)
2236   {
2237     int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2238     if(index < 0)
2239       return false;
2240     SelectionStream st = m_SelectionStreams.Get(STREAM_SUBTITLE, index);
2241
2242     if(!m_pSubtitleDemuxer || m_pSubtitleDemuxer->GetFileName() != st.filename)
2243     {
2244       CLog::Log(LOGNOTICE, "Opening Subtitle file: %s", st.filename.c_str());
2245       auto_ptr<CDVDDemuxVobsub> demux(new CDVDDemuxVobsub());
2246       if(!demux->Open(st.filename))      
2247         return false;
2248       m_pSubtitleDemuxer = demux.release();
2249     }
2250
2251     pStream = m_pSubtitleDemuxer->GetStream(iStream);
2252     if(!pStream || pStream->disabled)
2253       return false;
2254     pStream->SetDiscard(AVDISCARD_NONE);
2255     double pts = m_dvdPlayerVideo.GetCurrentPts();
2256     if(pts == DVD_NOPTS_VALUE)
2257       pts = m_CurrentVideo.dts;
2258     if(pts != DVD_NOPTS_VALUE)
2259       m_pSubtitleDemuxer->SeekTime((int)(1000.0 * pts / (double)DVD_TIME_BASE));
2260
2261     hint.Assign(*pStream, true);
2262   }
2263   else if(STREAM_SOURCE_MASK(source) == STREAM_SOURCE_TEXT)
2264   {
2265     int index = m_SelectionStreams.IndexOf(STREAM_SUBTITLE, source, iStream);
2266     if(index < 0)
2267       return false;
2268     filename = m_SelectionStreams.Get(STREAM_SUBTITLE, index).filename;
2269
2270     hint.Clear();
2271     hint.fpsscale = m_CurrentVideo.hint.fpsscale;
2272     hint.fpsrate  = m_CurrentVideo.hint.fpsrate;
2273   }
2274   else
2275   {
2276     if(!m_pDemuxer)
2277       return false;
2278     pStream = m_pDemuxer->GetStream(iStream);
2279     if(!pStream || pStream->disabled)
2280       return false;
2281     pStream->SetDiscard(AVDISCARD_NONE);
2282
2283     hint.Assign(*pStream, true);
2284
2285     if(m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2286       filename = "dvd";
2287   }
2288
2289   if(m_CurrentSubtitle.id    < 0 
2290   || m_CurrentSubtitle.hint != hint) 
2291   {
2292     if(m_CurrentSubtitle.id >= 0)
2293     {
2294       CLog::Log(LOGDEBUG, " - codecs hints have changed, must close previous stream");
2295       CloseSubtitleStream(false);
2296     }
2297
2298     if(!m_dvdPlayerSubtitle.OpenStream(hint, filename))
2299     {
2300       CLog::Log(LOGWARNING, "%s - Unsupported stream %d. Stream disabled.", __FUNCTION__, iStream);
2301       if(pStream)
2302       {
2303         pStream->disabled = true;
2304         pStream->SetDiscard(AVDISCARD_ALL);
2305       }
2306       return false;
2307     }
2308   }
2309   else
2310     m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2311
2312   m_CurrentSubtitle.id     = iStream;
2313   m_CurrentSubtitle.source = source;
2314   m_CurrentSubtitle.hint   = hint;
2315   m_CurrentSubtitle.stream = (void*)pStream;
2316
2317   return true;
2318 }
2319
2320 bool CDVDPlayer::CloseAudioStream(bool bWaitForBuffers)
2321 {
2322   if (m_CurrentAudio.id < 0)
2323     return false;
2324
2325   CLog::Log(LOGNOTICE, "Closing audio stream");
2326
2327   m_dvdPlayerAudio.CloseStream(bWaitForBuffers);
2328
2329   m_CurrentAudio.Clear();
2330   return true;
2331 }
2332
2333 bool CDVDPlayer::CloseVideoStream(bool bWaitForBuffers)
2334 {
2335   if (m_CurrentVideo.id < 0) 
2336     return false;
2337
2338   CLog::Log(LOGNOTICE, "Closing video stream");  
2339
2340   m_dvdPlayerVideo.CloseStream(bWaitForBuffers);
2341
2342   m_CurrentVideo.Clear();
2343   return true;
2344 }
2345
2346 bool CDVDPlayer::CloseSubtitleStream(bool bKeepOverlays)
2347 {
2348   if (m_CurrentSubtitle.id < 0) 
2349     return false;
2350
2351   CLog::Log(LOGNOTICE, "Closing subtitle stream");
2352
2353   m_dvdPlayerSubtitle.CloseStream(!bKeepOverlays);
2354
2355   m_CurrentSubtitle.Clear();
2356   return true;
2357 }
2358
2359 void CDVDPlayer::FlushBuffers(bool queued)
2360 {
2361   if(queued) 
2362   {
2363     m_dvdPlayerAudio.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2364     m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2365     m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
2366     m_dvdPlayerSubtitle.SendMessage(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2367     SyncronizePlayers(SYNCSOURCE_ALL);
2368   } 
2369   else
2370   {
2371     m_dvdPlayerAudio.Flush();
2372     m_dvdPlayerVideo.Flush();
2373     m_dvdPlayerSubtitle.Flush();
2374
2375     // clear subtitle and menu overlays
2376     m_overlayContainer.Clear();
2377   }
2378   m_CurrentAudio.inited = false;
2379   m_CurrentVideo.inited = false;
2380   m_CurrentSubtitle.inited = false;  
2381 }
2382
2383 // since we call ffmpeg functions to decode, this is being called in the same thread as ::Process() is
2384 int CDVDPlayer::OnDVDNavResult(void* pData, int iMessage)
2385 {
2386   if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2387   {
2388     CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2389
2390     switch (iMessage)
2391     {
2392     case DVDNAV_STILL_FRAME:
2393       {
2394         //CLog::Log(LOGDEBUG, "DVDNAV_STILL_FRAME");
2395
2396         dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)pData;
2397         // should wait the specified time here while we let the player running
2398         // after that call dvdnav_still_skip(m_dvdnav);
2399
2400         if (m_dvd.state != DVDSTATE_STILL)
2401         {
2402           // else notify the player we have received a still frame
2403
2404           if(still_event->length < 0xff)
2405             m_dvd.iDVDStillTime = still_event->length * 1000;
2406           else
2407             m_dvd.iDVDStillTime = 0;
2408
2409           m_dvd.iDVDStillStartTime = GetTickCount();
2410
2411           /* adjust for the output delay in the video queue */
2412           DWORD time = 0;
2413           if( m_CurrentVideo.stream && m_dvd.iDVDStillTime > 0 )
2414           {
2415             time = (DWORD)(m_dvdPlayerVideo.GetOutputDelay() / ( DVD_TIME_BASE / 1000 ));
2416             if( time < 10000 && time > 0 ) 
2417               m_dvd.iDVDStillTime += time;
2418           }
2419           m_dvd.state = DVDSTATE_STILL;
2420           CLog::Log(LOGDEBUG,
2421                     "DVDNAV_STILL_FRAME - waiting %i sec, with delay of %d sec",
2422                     still_event->length, time / 1000);
2423         }
2424         return NAVRESULT_HOLD;
2425       }
2426       break;
2427     case DVDNAV_SPU_CLUT_CHANGE:
2428       {
2429         CLog::Log(LOGDEBUG, "DVDNAV_SPU_CLUT_CHANGE");
2430         m_dvdPlayerSubtitle.SendMessage(new CDVDMsgSubtitleClutChange((BYTE*)pData));
2431       }
2432       break;
2433     case DVDNAV_SPU_STREAM_CHANGE:
2434       {
2435         CLog::Log(LOGDEBUG, "DVDNAV_SPU_STREAM_CHANGE");
2436
2437         dvdnav_spu_stream_change_event_t* event = (dvdnav_spu_stream_change_event_t*)pData;
2438
2439         int iStream = event->physical_wide;
2440         bool visible = !(iStream & 0x80);
2441
2442         m_dvdPlayerVideo.EnableSubtitle(visible);
2443
2444         if (iStream >= 0)
2445           m_dvd.iSelectedSPUStream = (iStream & ~0x80);
2446         else
2447           m_dvd.iSelectedSPUStream = -1;
2448
2449         m_CurrentSubtitle.stream = NULL;
2450       }
2451       break;
2452     case DVDNAV_AUDIO_STREAM_CHANGE:
2453       {
2454         CLog::Log(LOGDEBUG, "DVDNAV_AUDIO_STREAM_CHANGE");
2455
2456         // This should be the correct way i think, however we don't have any streams right now
2457         // since the demuxer hasn't started so it doesn't change. not sure how to do this.
2458         dvdnav_audio_stream_change_event_t* event = (dvdnav_audio_stream_change_event_t*)pData;
2459
2460         // Tell system what audiostream should be opened by default
2461         if (event->logical >= 0)
2462           m_dvd.iSelectedAudioStream = event->physical;
2463         else
2464           m_dvd.iSelectedAudioStream = -1;
2465
2466         m_CurrentAudio.stream = NULL;
2467       }
2468       break;
2469     case DVDNAV_HIGHLIGHT:
2470       {
2471         //dvdnav_highlight_event_t* pInfo = (dvdnav_highlight_event_t*)pData;
2472         int iButton = pStream->GetCurrentButton();
2473         CLog::Log(LOGDEBUG, "DVDNAV_HIGHLIGHT: Highlight button %d\n", iButton);        
2474         m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_NORMAL);
2475       }
2476       break;
2477     case DVDNAV_VTS_CHANGE:
2478       {
2479         //dvdnav_vts_change_event_t* vts_change_event = (dvdnav_vts_change_event_t*)pData;
2480         CLog::Log(LOGDEBUG, "DVDNAV_VTS_CHANGE");
2481
2482         //Make sure we clear all the old overlays here, or else old forced items are left.
2483         m_overlayContainer.Clear();
2484
2485         // reset the demuxer, this also imples closing the video and the audio system
2486         // this is a bit tricky cause it's the demuxer that's is making this call in the end
2487         // so we send a message to indicate the main loop that the demuxer needs a reset
2488         // this also means the libdvdnav may not return any data packets after this command
2489         m_messenger.Put(new CDVDMsgDemuxerReset());
2490
2491         //Force an aspect ratio that is set in the dvdheaders if available
2492         m_CurrentVideo.hint.aspect = pStream->GetVideoAspectRatio();
2493         if( m_dvdPlayerAudio.m_messageQueue.IsInited() )
2494           m_dvdPlayerVideo.SendMessage(new CDVDMsgDouble(CDVDMsg::VIDEO_SET_ASPECT, m_CurrentVideo.hint.aspect));
2495
2496         m_SelectionStreams.Clear(STREAM_NONE, STREAM_SOURCE_NAV);
2497         m_SelectionStreams.Update(m_pInputStream, m_pDemuxer);
2498
2499         // we must hold here once more, otherwise the demuxer message
2500         // will be executed after demuxer have filled with data
2501         return NAVRESULT_HOLD;
2502       }
2503       break;
2504     case DVDNAV_CELL_CHANGE:
2505       {
2506         //dvdnav_cell_change_event_t* cell_change_event = (dvdnav_cell_change_event_t*)pData;
2507         CLog::Log(LOGDEBUG, "DVDNAV_CELL_CHANGE");
2508
2509         m_dvd.state = DVDSTATE_NORMAL;        
2510         
2511         if( m_dvdPlayerVideo.m_messageQueue.IsInited() )
2512           m_dvdPlayerVideo.SendMessage(new CDVDMsg(CDVDMsg::VIDEO_NOSKIP));
2513       }
2514       break;
2515     case DVDNAV_NAV_PACKET:
2516       {
2517           //pci_t* pci = (pci_t*)pData;
2518
2519           // this should be possible to use to make sure we get
2520           // seamless transitions over these boundaries
2521           // if we remember the old vobunits boundaries
2522           // when a packet comes out of demuxer that has
2523           // pts values outside that boundary, it belongs
2524           // to the new vobunit, wich has new timestamps
2525       }
2526       break;
2527     case DVDNAV_HOP_CHANNEL:
2528       {
2529         // This event is issued whenever a non-seamless operation has been executed.
2530         // Applications with fifos should drop the fifos content to speed up responsiveness.
2531         CLog::Log(LOGDEBUG, "DVDNAV_HOP_CHANNEL");
2532         m_messenger.Put(new CDVDMsg(CDVDMsg::GENERAL_FLUSH));
2533         return NAVRESULT_ERROR;
2534       }
2535       break;
2536     case DVDNAV_STOP:
2537       {
2538         CLog::Log(LOGDEBUG, "DVDNAV_STOP");
2539         m_dvd.state = DVDSTATE_NORMAL;
2540       }
2541       break;
2542     default:
2543     {}
2544       break;
2545     }
2546   }
2547   return NAVRESULT_NOP;
2548 }
2549
2550 bool CDVDPlayer::OnAction(const CAction &action)
2551 {
2552 #define THREAD_ACTION(action) \
2553   do { \
2554     if(GetCurrentThreadId() != CThread::ThreadId()) { \
2555       m_messenger.Put(new CDVDMsgType<CAction>(CDVDMsg::GENERAL_GUI_ACTION, action)); \
2556       return true; \
2557     } \
2558   } while(false)
2559
2560   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2561   {
2562     CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2563
2564
2565     if( m_dvd.state == DVDSTATE_STILL && m_dvd.iDVDStillTime != 0 && pStream->GetTotalButtons() == 0 )
2566     {
2567       switch(action.wID)
2568       {
2569         case ACTION_NEXT_ITEM:
2570         case ACTION_MOVE_RIGHT:
2571         case ACTION_MOVE_UP:
2572         case ACTION_SELECT_ITEM:
2573           {
2574             THREAD_ACTION(action);
2575             /* this will force us out of the stillframe */
2576             CLog::Log(LOGDEBUG, "%s - User asked to exit stillframe", __FUNCTION__);
2577             m_dvd.iDVDStillStartTime = 0;
2578             m_dvd.iDVDStillTime = 1;            
2579           }
2580           return true;
2581       }
2582     }
2583
2584
2585     switch (action.wID)
2586     {
2587     case ACTION_PREV_ITEM:  // SKIP-:
2588       {
2589         THREAD_ACTION(action);
2590         CLog::Log(LOGDEBUG, " - pushed prev");
2591         pStream->OnPrevious();
2592         g_infoManager.SetDisplayAfterSeek();
2593         return true;
2594       }
2595       break;
2596     case ACTION_NEXT_ITEM:  // SKIP+:
2597       {
2598         THREAD_ACTION(action);
2599         CLog::Log(LOGDEBUG, " - pushed next");
2600         pStream->OnNext();
2601         g_infoManager.SetDisplayAfterSeek();
2602         return true;
2603       }
2604       break;
2605     case ACTION_SHOW_VIDEOMENU:   // start button
2606       {
2607         THREAD_ACTION(action);
2608         CLog::Log(LOGDEBUG, " - go to menu");
2609         pStream->OnMenu();
2610         // send a message to everyone that we've gone to the menu
2611         CGUIMessage msg(GUI_MSG_VIDEO_MENU_STARTED, 0, 0);
2612         g_graphicsContext.SendMessage(msg);
2613         return true;
2614       }
2615       break;
2616     }
2617
2618     if (pStream->IsInMenu())
2619     {
2620       switch (action.wID)
2621       {
2622       case ACTION_PREVIOUS_MENU:
2623         {
2624           THREAD_ACTION(action);
2625           CLog::Log(LOGDEBUG, " - menu back");
2626           pStream->OnBack();
2627         }
2628         break;
2629       case ACTION_MOVE_LEFT:
2630         {
2631           THREAD_ACTION(action);
2632           CLog::Log(LOGDEBUG, " - move left");
2633           pStream->OnLeft();
2634         }
2635         break;
2636       case ACTION_MOVE_RIGHT:
2637         {
2638           THREAD_ACTION(action);
2639           CLog::Log(LOGDEBUG, " - move right");
2640           pStream->OnRight();
2641         }
2642         break;
2643       case ACTION_MOVE_UP:
2644         {
2645           THREAD_ACTION(action);
2646           CLog::Log(LOGDEBUG, " - move up");
2647           pStream->OnUp();
2648         }
2649         break;
2650       case ACTION_MOVE_DOWN:
2651         {
2652           THREAD_ACTION(action);
2653           CLog::Log(LOGDEBUG, " - move down");
2654           pStream->OnDown();
2655         }
2656         break;
2657
2658       case ACTION_MOUSE:
2659         {
2660           // check the action
2661           CAction action2 = action;
2662           action2.m_dwButtonCode = g_Mouse.bClick[MOUSE_LEFT_BUTTON] ? 1 : 0;
2663           action2.fAmount1 = g_Mouse.GetLocation().x;
2664           action2.fAmount2 = g_Mouse.GetLocation().y;
2665
2666           RECT rs, rd;
2667           GetVideoRect(rs, rd);
2668           if (action2.fAmount1 < rd.left || action2.fAmount1 > rd.right ||
2669               action2.fAmount2 < rd.top || action2.fAmount2 > rd.bottom)
2670             return false; // out of bounds
2671           THREAD_ACTION(action2);
2672           // convert to video coords...
2673           CPoint pt(action2.fAmount1, action2.fAmount2);
2674           pt -= CPoint(rd.left, rd.top);
2675           pt.x *= (float)(rs.right - rs.left) / (rd.right - rd.left);
2676           pt.y *= (float)(rs.bottom - rs.top) / (rd.bottom - rd.top);
2677           pt += CPoint(rs.left, rs.top);
2678           if (action2.m_dwButtonCode)
2679             return pStream->OnMouseClick(pt);
2680           return pStream->OnMouseMove(pt);
2681         }
2682         break;
2683       case ACTION_SHOW_OSD:
2684       case ACTION_SELECT_ITEM:
2685         {
2686           THREAD_ACTION(action);
2687           CLog::Log(LOGDEBUG, " - button select");
2688           // show button pushed overlay
2689           m_dvdPlayerSubtitle.UpdateOverlayInfo((CDVDInputStreamNavigator*)m_pInputStream, LIBDVDNAV_BUTTON_CLICKED);
2690
2691           pStream->ActivateButton();
2692         }
2693         break;
2694       case REMOTE_0:
2695       case REMOTE_1:
2696       case REMOTE_2:
2697       case REMOTE_3:
2698       case REMOTE_4:
2699       case REMOTE_5:
2700       case REMOTE_6:
2701       case REMOTE_7:
2702       case REMOTE_8:
2703       case REMOTE_9:
2704         {
2705           THREAD_ACTION(action);
2706           // Offset from key codes back to button number
2707           int button = action.wID - REMOTE_0;
2708           CLog::Log(LOGDEBUG, " - button pressed %d", button);
2709           pStream->SelectButton(button);
2710         }
2711        break;
2712       default:
2713         return false;
2714         break;
2715       }
2716       return true; // message is handled
2717     }
2718   }
2719
2720   if (dynamic_cast<CDVDInputStream::IChannel*>(m_pInputStream))
2721   {
2722     switch (action.wID)
2723     {
2724       case ACTION_NEXT_ITEM:
2725       case ACTION_PAGE_UP:
2726         m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_NEXT));
2727         g_infoManager.SetDisplayAfterSeek();
2728         return true;
2729       break;
2730
2731       case ACTION_PREV_ITEM:
2732       case ACTION_PAGE_DOWN:
2733         m_messenger.Put(new CDVDMsg(CDVDMsg::PLAYER_CHANNEL_PREV));
2734         g_infoManager.SetDisplayAfterSeek();
2735         return true;
2736       break;
2737     }
2738   }
2739
2740   switch (action.wID)
2741   {
2742     case ACTION_NEXT_ITEM:
2743     case ACTION_PAGE_UP:
2744       if(GetChapterCount() > 0) 
2745       {
2746         m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()+1));
2747         g_infoManager.SetDisplayAfterSeek();
2748         return true;
2749       }
2750       else
2751         break;
2752     case ACTION_PREV_ITEM:
2753     case ACTION_PAGE_DOWN:
2754       if(GetChapterCount() > 0) 
2755       {
2756         m_messenger.Put(new CDVDMsgPlayerSeekChapter(GetChapter()-1));
2757         g_infoManager.SetDisplayAfterSeek();
2758         return true;
2759       }
2760       else
2761         break;
2762   }
2763
2764   // return false to inform the caller we didn't handle the message
2765   return false;
2766 }
2767
2768 bool CDVDPlayer::IsInMenu() const
2769 {
2770   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2771   {
2772     CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2773     if( m_dvd.state == DVDSTATE_STILL )
2774       return true;
2775     else
2776       return pStream->IsInMenu();
2777   }
2778   return false;
2779 }
2780
2781 bool CDVDPlayer::HasMenu()
2782 {
2783   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2784     return true;
2785   else
2786     return false;
2787 }
2788
2789 bool CDVDPlayer::GetCurrentSubtitle(CStdString& strSubtitle)
2790 {
2791   double pts = m_clock.GetClock();
2792
2793   if (m_pInputStream && m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2794     return false;
2795
2796   return m_dvdPlayerSubtitle.GetCurrentSubtitle(strSubtitle, pts - m_dvdPlayerVideo.GetSubtitleDelay());
2797 }
2798
2799 CStdString CDVDPlayer::GetPlayerState()
2800 {
2801   if (!m_pInputStream) return "";
2802
2803   if (m_pInputStream->IsStreamType(DVDSTREAM_TYPE_DVD))
2804   {
2805     CDVDInputStreamNavigator* pStream = (CDVDInputStreamNavigator*)m_pInputStream;
2806
2807     std::string buffer;
2808     if( pStream->GetNavigatorState(buffer) ) return buffer;
2809   }
2810
2811   return "";
2812 }
2813
2814 bool CDVDPlayer::SetPlayerState(CStdString state)
2815 {
2816   m_messenger.Put(new CDVDMsgPlayerSetState(state));
2817   return true;
2818 }
2819
2820 int CDVDPlayer::GetChapterCount()
2821 {
2822   CSingleLock lock(m_StateSection);
2823   return m_State.chapter_count;
2824 }
2825
2826 int CDVDPlayer::GetChapter()
2827 {
2828   CSingleLock lock(m_StateSection);
2829   return m_State.chapter;
2830 }
2831
2832 void CDVDPlayer::GetChapterName(CStdString& strChapterName)
2833 {
2834   CSingleLock lock(m_StateSection);
2835   strChapterName = m_State.chapter_name;
2836 }
2837
2838 int CDVDPlayer::SeekChapter(int iChapter)
2839 {
2840   if (GetChapterCount() > 0)
2841   {
2842     if (iChapter < 0)
2843       iChapter = 0;
2844     if (iChapter > GetChapterCount())
2845       return 0;
2846       
2847     // Seek to the chapter.
2848     m_messenger.Put(new CDVDMsgPlayerSeekChapter(iChapter));
2849     SyncronizeDemuxer(100);
2850     m_tmLastSeek = time(NULL);
2851   }
2852   else
2853   {
2854     // Do a regular big jump.
2855     if (iChapter > GetChapter())
2856       Seek(true, true);
2857     else
2858       Seek(false, true);