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