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