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