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