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