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