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