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