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