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