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