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