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