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