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