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