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